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  }