github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/gopkg.in/alexcesaro/quotedprintable.v3/writer.go (about) 1 package quotedprintable 2 3 import "io" 4 5 const lineMaxLen = 76 6 7 // A Writer is a quoted-printable writer that implements io.WriteCloser. 8 type Writer struct { 9 // Binary mode treats the writer's input as pure binary and processes end of 10 // line bytes as binary data. 11 Binary bool 12 13 w io.Writer 14 i int 15 line [78]byte 16 cr bool 17 } 18 19 // NewWriter returns a new Writer that writes to w. 20 func NewWriter(w io.Writer) *Writer { 21 return &Writer{w: w} 22 } 23 24 // Write encodes p using quoted-printable encoding and writes it to the 25 // underlying io.Writer. It limits line length to 76 characters. The encoded 26 // bytes are not necessarily flushed until the Writer is closed. 27 func (w *Writer) Write(p []byte) (n int, err error) { 28 for i, b := range p { 29 switch { 30 // Simple writes are done in batch. 31 case b >= '!' && b <= '~' && b != '=': 32 continue 33 case isWhitespace(b) || !w.Binary && (b == '\n' || b == '\r'): 34 continue 35 } 36 37 if i > n { 38 if err := w.write(p[n:i]); err != nil { 39 return n, err 40 } 41 n = i 42 } 43 44 if err := w.encode(b); err != nil { 45 return n, err 46 } 47 n++ 48 } 49 50 if n == len(p) { 51 return n, nil 52 } 53 54 if err := w.write(p[n:]); err != nil { 55 return n, err 56 } 57 58 return len(p), nil 59 } 60 61 // Close closes the Writer, flushing any unwritten data to the underlying 62 // io.Writer, but does not close the underlying io.Writer. 63 func (w *Writer) Close() error { 64 if err := w.checkLastByte(); err != nil { 65 return err 66 } 67 68 return w.flush() 69 } 70 71 // write limits text encoded in quoted-printable to 76 characters per line. 72 func (w *Writer) write(p []byte) error { 73 for _, b := range p { 74 if b == '\n' || b == '\r' { 75 // If the previous byte was \r, the CRLF has already been inserted. 76 if w.cr && b == '\n' { 77 w.cr = false 78 continue 79 } 80 81 if b == '\r' { 82 w.cr = true 83 } 84 85 if err := w.checkLastByte(); err != nil { 86 return err 87 } 88 if err := w.insertCRLF(); err != nil { 89 return err 90 } 91 continue 92 } 93 94 if w.i == lineMaxLen-1 { 95 if err := w.insertSoftLineBreak(); err != nil { 96 return err 97 } 98 } 99 100 w.line[w.i] = b 101 w.i++ 102 w.cr = false 103 } 104 105 return nil 106 } 107 108 func (w *Writer) encode(b byte) error { 109 if lineMaxLen-1-w.i < 3 { 110 if err := w.insertSoftLineBreak(); err != nil { 111 return err 112 } 113 } 114 115 w.line[w.i] = '=' 116 w.line[w.i+1] = upperhex[b>>4] 117 w.line[w.i+2] = upperhex[b&0x0f] 118 w.i += 3 119 120 return nil 121 } 122 123 // checkLastByte encodes the last buffered byte if it is a space or a tab. 124 func (w *Writer) checkLastByte() error { 125 if w.i == 0 { 126 return nil 127 } 128 129 b := w.line[w.i-1] 130 if isWhitespace(b) { 131 w.i-- 132 if err := w.encode(b); err != nil { 133 return err 134 } 135 } 136 137 return nil 138 } 139 140 func (w *Writer) insertSoftLineBreak() error { 141 w.line[w.i] = '=' 142 w.i++ 143 144 return w.insertCRLF() 145 } 146 147 func (w *Writer) insertCRLF() error { 148 w.line[w.i] = '\r' 149 w.line[w.i+1] = '\n' 150 w.i += 2 151 152 return w.flush() 153 } 154 155 func (w *Writer) flush() error { 156 if _, err := w.w.Write(w.line[:w.i]); err != nil { 157 return err 158 } 159 160 w.i = 0 161 return nil 162 } 163 164 func isWhitespace(b byte) bool { 165 return b == ' ' || b == '\t' 166 }