xref: /plan9/sys/src/cmd/upas/common/appendfiletombox.c (revision 707451d03a6d58b744a5cc141769992c27007ce0)
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