#include "common.h" enum { Buffersize = 64*1024, }; typedef struct Inbuf Inbuf; struct Inbuf { char buf[Buffersize]; char *wp; char *rp; int eof; int in; int out; int last; ulong bytes; }; static Inbuf* allocinbuf(int in, int out) { Inbuf *b; b = mallocz(sizeof(Inbuf), 1); if(b == nil) sysfatal("reading mailbox: %r"); b->rp = b->wp = b->buf; b->in = in; b->out = out; return b; } /* should only be called at start of file or when b->rp[-1] == '\n' */ static int fill(Inbuf *b, int addspace) { int i, n; if(b->eof && b->wp - b->rp == 0) return 0; n = b->rp - b->buf; if(n > 0){ i = write(b->out, b->buf, n); if(i != n) return -1; b->last = b->buf[n-1]; b->bytes += n; } if(addspace){ if(write(b->out, " ", 1) != 1) return -1; b->last = ' '; b->bytes++; } n = b->wp - b->rp; memmove(b->buf, b->rp, n); b->rp = b->buf; b->wp = b->rp + n; i = read(b->in, b->buf+n, sizeof(b->buf)-n); if(i < 0) return -1; b->wp += i; return b->wp - b->rp; } enum { Fromlen = sizeof "From " - 1, }; /* code to escape ' '*From' ' at the beginning of a line */ int appendfiletombox(int in, int out) { int addspace, n, sol; char *p; Inbuf *b; seek(out, 0, 2); b = allocinbuf(in, out); addspace = 0; sol = 1; for(;;){ if(b->wp - b->rp < Fromlen){ /* * not enough unread bytes in buffer to match "From ", * so get some more. We must only inject a space at * the start of a line (one that begins with "From "). */ if (b->rp == b->buf || b->rp[-1] == '\n') { n = fill(b, addspace); addspace = 0; } else n = fill(b, 0); if(n < 0) goto error; if(n == 0) break; if(n < Fromlen){ /* still can't match? */ b->rp = b->wp; continue; } } /* state machine looking for ' '*From' ' */ if(!sol){ p = memchr(b->rp, '\n', b->wp - b->rp); if(p == nil) b->rp = b->wp; else{ b->rp = p+1; sol = 1; } continue; } else { if(*b->rp == ' ' || strncmp(b->rp, "From ", Fromlen) != 0){ b->rp++; continue; } addspace = 1; sol = 0; } } /* mailbox entries always terminate with two newlines */ n = b->last == '\n' ? 1 : 2; if(write(out, "\n\n", n) != n) goto error; n += b->bytes; free(b); return n; error: free(b); return -1; } int appendfiletofile(int in, int out) { int n; Inbuf *b; seek(out, 0, 2); b = allocinbuf(in, out); for(;;){ n = fill(b, 0); if(n < 0) goto error; if(n == 0) break; b->rp = b->wp; } n = b->bytes; free(b); return n; error: free(b); return -1; }