Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

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