...
1 package quotedprintable
2
3 import "io"
4
5 const lineMaxLen = 76
6
7
8 type Writer struct {
9
10
11 Binary bool
12
13 w io.Writer
14 i int
15 line [78]byte
16 cr bool
17 }
18
19
20 func NewWriter(w io.Writer) *Writer {
21 return &Writer{w: w}
22 }
23
24
25
26
27 func (w *Writer) Write(p []byte) (n int, err error) {
28 for i, b := range p {
29 switch {
30
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
62
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
72 func (w *Writer) write(p []byte) error {
73 for _, b := range p {
74 if b == '\n' || b == '\r' {
75
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
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 }
167
View as plain text