#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;
}
|