1 #include "common.h"
2
3 enum {
4 Buffersize = 64*1024,
5 };
6
7 typedef struct Inbuf Inbuf;
8 struct Inbuf
9 {
10 char buf[Buffersize];
11 char *wp;
12 char *rp;
13 int eof;
14 int in;
15 int out;
16 int last;
17 ulong bytes;
18 };
19
20 static Inbuf*
allocinbuf(int in,int out)21 allocinbuf(int in, int out)
22 {
23 Inbuf *b;
24
25 b = mallocz(sizeof(Inbuf), 1);
26 if(b == nil)
27 sysfatal("reading mailbox: %r");
28 b->rp = b->wp = b->buf;
29 b->in = in;
30 b->out = out;
31 return b;
32 }
33
34 /* should only be called at start of file or when b->rp[-1] == '\n' */
35 static int
fill(Inbuf * b,int addspace)36 fill(Inbuf *b, int addspace)
37 {
38 int i, n;
39
40 if(b->eof && b->wp - b->rp == 0)
41 return 0;
42
43 n = b->rp - b->buf;
44 if(n > 0){
45 i = write(b->out, b->buf, n);
46 if(i != n)
47 return -1;
48 b->last = b->buf[n-1];
49 b->bytes += n;
50 }
51 if(addspace){
52 if(write(b->out, " ", 1) != 1)
53 return -1;
54 b->last = ' ';
55 b->bytes++;
56 }
57
58 n = b->wp - b->rp;
59 memmove(b->buf, b->rp, n);
60 b->rp = b->buf;
61 b->wp = b->rp + n;
62
63 i = read(b->in, b->buf+n, sizeof(b->buf)-n);
64 if(i < 0)
65 return -1;
66 b->wp += i;
67
68 return b->wp - b->rp;
69 }
70
71 enum { Fromlen = sizeof "From " - 1, };
72
73 /* code to escape ' '*From' ' at the beginning of a line */
74 int
appendfiletombox(int in,int out)75 appendfiletombox(int in, int out)
76 {
77 int addspace, n, sol;
78 char *p;
79 Inbuf *b;
80
81 seek(out, 0, 2);
82
83 b = allocinbuf(in, out);
84 addspace = 0;
85 sol = 1;
86
87 for(;;){
88 if(b->wp - b->rp < Fromlen){
89 /*
90 * not enough unread bytes in buffer to match "From ",
91 * so get some more. We must only inject a space at
92 * the start of a line (one that begins with "From ").
93 */
94 if (b->rp == b->buf || b->rp[-1] == '\n') {
95 n = fill(b, addspace);
96 addspace = 0;
97 } else
98 n = fill(b, 0);
99 if(n < 0)
100 goto error;
101 if(n == 0)
102 break;
103 if(n < Fromlen){ /* still can't match? */
104 b->rp = b->wp;
105 continue;
106 }
107 }
108
109 /* state machine looking for ' '*From' ' */
110 if(!sol){
111 p = memchr(b->rp, '\n', b->wp - b->rp);
112 if(p == nil)
113 b->rp = b->wp;
114 else{
115 b->rp = p+1;
116 sol = 1;
117 }
118 continue;
119 } else {
120 if(*b->rp == ' ' || strncmp(b->rp, "From ", Fromlen) != 0){
121 b->rp++;
122 continue;
123 }
124 addspace = 1;
125 sol = 0;
126 }
127 }
128
129 /* mailbox entries always terminate with two newlines */
130 n = b->last == '\n' ? 1 : 2;
131 if(write(out, "\n\n", n) != n)
132 goto error;
133 n += b->bytes;
134 free(b);
135 return n;
136 error:
137 free(b);
138 return -1;
139 }
140
141 int
appendfiletofile(int in,int out)142 appendfiletofile(int in, int out)
143 {
144 int n;
145 Inbuf *b;
146
147 seek(out, 0, 2);
148
149 b = allocinbuf(in, out);
150 for(;;){
151 n = fill(b, 0);
152 if(n < 0)
153 goto error;
154 if(n == 0)
155 break;
156 b->rp = b->wp;
157 }
158 n = b->bytes;
159 free(b);
160 return n;
161 error:
162 free(b);
163 return -1;
164 }
165