Subversion Repositories planix.SVN

Rev

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

#include "sam.h"

/*
 * Structure of Undo list:
 *      The Undo structure follows any associated data, so the list
 *      can be read backwards: read the structure, then read whatever
 *      data is associated (insert string, file name) and precedes it.
 *      The structure includes the previous value of the modify bit
 *      and a sequence number; successive Undo structures with the
 *      same sequence number represent simultaneous changes.
 */

typedef struct Undo Undo;
typedef struct Merge Merge;

struct Undo
{
        short   type;           /* Delete, Insert, Filename, Dot, Mark */
        short   mod;            /* modify bit */
        uint    seq;            /* sequence number */
        uint    p0;             /* location of change (unused in f) */
        uint    n;              /* # runes in string or file name */
};

struct Merge
{
        File    *f;
        uint    seq;            /* of logged change */
        uint    p0;             /* location of change (unused in f) */
        uint    n;              /* # runes to delete */
        uint    nbuf;           /* # runes to insert */
        Rune    buf[RBUFSIZE];
};

enum
{
        Maxmerge = 50,
        Undosize = sizeof(Undo)/sizeof(Rune),
};

static Merge    merge;

File*
fileopen(void)
{
        File *f;

        f = emalloc(sizeof(File));
        f->dot.f = f;
        f->ndot.f = f;
        f->seq = 0;
        f->mod = FALSE;
        f->unread = TRUE;
        Strinit0(&f->name);
        return f;
}

int
fileisdirty(File *f)
{
        return f->seq != f->cleanseq;
}

static void
wrinsert(Buffer *delta, int seq, int mod, uint p0, Rune *s, uint ns)
{
        Undo u;

        u.type = Insert;
        u.mod = mod;
        u.seq = seq;
        u.p0 = p0;
        u.n = ns;
        bufinsert(delta, delta->nc, s, ns);
        bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
}

static void
wrdelete(Buffer *delta, int seq, int mod, uint p0, uint p1)
{
        Undo u;

        u.type = Delete;
        u.mod = mod;
        u.seq = seq;
        u.p0 = p0;
        u.n = p1 - p0;
        bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
}

void
flushmerge(void)
{
        File *f;

        f = merge.f;
        if(f == nil)
                return;
        if(merge.seq != f->seq)
                panic("flushmerge seq mismatch");
        if(merge.n != 0)
                wrdelete(&f->epsilon, f->seq, TRUE, merge.p0, merge.p0+merge.n);
        if(merge.nbuf != 0)
                wrinsert(&f->epsilon, f->seq, TRUE, merge.p0+merge.n, merge.buf, merge.nbuf);
        merge.f = nil;
        merge.n = 0;
        merge.nbuf = 0;
}

void
mergeextend(File *f, uint p0)
{
        uint mp0n;

        mp0n = merge.p0+merge.n;
        if(mp0n != p0){
                bufread(f, mp0n, merge.buf+merge.nbuf, p0-mp0n);
                merge.nbuf += p0-mp0n;
                merge.n = p0-merge.p0;
        }
}

/*
 * like fileundelete, but get the data from arguments
 */
void
loginsert(File *f, uint p0, Rune *s, uint ns)
{
        if(f->rescuing)
                return;
        if(ns == 0)
                return;
        if(ns>STRSIZE)
                panic("loginsert");
        if(f->seq < seq)
                filemark(f);
        if(p0 < f->hiposn)
                error(Esequence);

        if(merge.f != f
        || p0-(merge.p0+merge.n)>Maxmerge                       /* too far */
        || merge.nbuf+((p0+ns)-(merge.p0+merge.n))>=RBUFSIZE)   /* too long */
                flushmerge();

        if(ns>=RBUFSIZE){
                if(!(merge.n == 0 && merge.nbuf == 0 && merge.f == nil))
                        panic("loginsert bad merge state");
                wrinsert(&f->epsilon, f->seq, TRUE, p0, s, ns);
        }else{
                if(merge.f != f){
                        merge.f = f;
                        merge.p0 = p0;
                        merge.seq = f->seq;
                }
                mergeextend(f, p0);

                /* append string to merge */
                runemove(merge.buf+merge.nbuf, s, ns);
                merge.nbuf += ns;
        }

        f->hiposn = p0;
        if(!f->unread && !f->mod)
                state(f, Dirty);
}

void
logdelete(File *f, uint p0, uint p1)
{
        if(f->rescuing)
                return;
        if(p0 == p1)
                return;
        if(f->seq < seq)
                filemark(f);
        if(p0 < f->hiposn)
                error(Esequence);

        if(merge.f != f
        || p0-(merge.p0+merge.n)>Maxmerge                       /* too far */
        || merge.nbuf+(p0-(merge.p0+merge.n))>=RBUFSIZE){       /* too long */
                flushmerge();
                merge.f = f;
                merge.p0 = p0;
                merge.seq = f->seq;
        }

        mergeextend(f, p0);

        /* add to deletion */
        merge.n = p1-merge.p0;

        f->hiposn = p1;
        if(!f->unread && !f->mod)
                state(f, Dirty);
}

/*
 * like fileunsetname, but get the data from arguments
 */
void
logsetname(File *f, String *s)
{
        Undo u;
        Buffer *delta;

        if(f->rescuing)
                return;

        if(f->unread){  /* This is setting initial file name */
                filesetname(f, s);
                return;
        }

        if(f->seq < seq)
                filemark(f);

        /* undo a file name change by restoring old name */
        delta = &f->epsilon;
        u.type = Filename;
        u.mod = TRUE;
        u.seq = f->seq;
        u.p0 = 0;       /* unused */
        u.n = s->n;
        if(s->n)
                bufinsert(delta, delta->nc, s->s, s->n);
        bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
        if(!f->unread && !f->mod)
                state(f, Dirty);
}

#ifdef NOTEXT
File*
fileaddtext(File *f, Text *t)
{
        if(f == nil){
                f = emalloc(sizeof(File));
                f->unread = TRUE;
        }
        f->text = realloc(f->text, (f->ntext+1)*sizeof(Text*));
        f->text[f->ntext++] = t;
        f->curtext = t;
        return f;
}

void
filedeltext(File *f, Text *t)
{
        int i;

        for(i=0; i<f->ntext; i++)
                if(f->text[i] == t)
                        goto Found;
        panic("can't find text in filedeltext");

    Found:
        f->ntext--;
        if(f->ntext == 0){
                fileclose(f);
                return;
        }
        memmove(f->text+i, f->text+i+1, (f->ntext-i)*sizeof(Text*));
        if(f->curtext == t)
                f->curtext = f->text[0];
}
#endif

void
fileuninsert(File *f, Buffer *delta, uint p0, uint ns)
{
        Undo u;

        /* undo an insertion by deleting */
        u.type = Delete;
        u.mod = f->mod;
        u.seq = f->seq;
        u.p0 = p0;
        u.n = ns;
        bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
}

void
fileundelete(File *f, Buffer *delta, uint p0, uint p1)
{
        Undo u;
        Rune *buf;
        uint i, n;

        /* undo a deletion by inserting */
        u.type = Insert;
        u.mod = f->mod;
        u.seq = f->seq;
        u.p0 = p0;
        u.n = p1-p0;
        buf = fbufalloc();
        for(i=p0; i<p1; i+=n){
                n = p1 - i;
                if(n > RBUFSIZE)
                        n = RBUFSIZE;
                bufread(f, i, buf, n);
                bufinsert(delta, delta->nc, buf, n);
        }
        fbuffree(buf);
        bufinsert(delta, delta->nc, (Rune*)&u, Undosize);

}

int
filereadc(File *f, uint q)
{
        Rune r;

        if(q >= f->nc)
                return -1;
        bufread(f, q, &r, 1);
        return r;
}

void
filesetname(File *f, String *s)
{
        if(!f->unread)  /* This is setting initial file name */
                fileunsetname(f, &f->delta);
        Strduplstr(&f->name, s);
        sortname(f);
        f->unread = TRUE;
}

void
fileunsetname(File *f, Buffer *delta)
{
        String s;
        Undo u;

        /* undo a file name change by restoring old name */
        u.type = Filename;
        u.mod = f->mod;
        u.seq = f->seq;
        u.p0 = 0;       /* unused */
        Strinit(&s);
        Strduplstr(&s, &f->name);
        fullname(&s);
        u.n = s.n;
        if(s.n)
                bufinsert(delta, delta->nc, s.s, s.n);
        bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
        Strclose(&s);
}

void
fileunsetdot(File *f, Buffer *delta, Range dot)
{
        Undo u;

        u.type = Dot;
        u.mod = f->mod;
        u.seq = f->seq;
        u.p0 = dot.p1;
        u.n = dot.p2 - dot.p1;
        bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
}

void
fileunsetmark(File *f, Buffer *delta, Range mark)
{
        Undo u;

        u.type = Mark;
        u.mod = f->mod;
        u.seq = f->seq;
        u.p0 = mark.p1;
        u.n = mark.p2 - mark.p1;
        bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
}

uint
fileload(File *f, uint p0, int fd, int *nulls)
{
        if(f->seq > 0)
                panic("undo in file.load unimplemented");
        return bufload(f, p0, fd, nulls);
}

int
fileupdate(File *f, int notrans, int toterm)
{
        uint p1, p2;
        int mod;

        if(f->rescuing)
                return FALSE;

        flushmerge();

        /*
         * fix the modification bit
         * subtle point: don't save it away in the log.
         *
         * if another change is made, the correct f->mod
         * state is saved  in the undo log by filemark
         * when setting the dot and mark.
         *
         * if the change is undone, the correct state is
         * saved from f in the fileun... routines.
         */
        mod = f->mod;
        f->mod = f->prevmod;
        if(f == cmd)
                notrans = TRUE;
        else{
                fileunsetdot(f, &f->delta, f->prevdot);
                fileunsetmark(f, &f->delta, f->prevmark);
        }
        f->dot = f->ndot;
        fileundo(f, FALSE, !notrans, &p1, &p2, toterm);
        f->mod = mod;

        if(f->delta.nc == 0)
                f->seq = 0;

        if(f == cmd)
                return FALSE;

        if(f->mod){
                f->closeok = 0;
                quitok = 0;
        }else
                f->closeok = 1;
        return TRUE;
}

long
prevseq(Buffer *b)
{
        Undo u;
        uint up;

        up = b->nc;
        if(up == 0)
                return 0;
        up -= Undosize;
        bufread(b, up, (Rune*)&u, Undosize);
        return u.seq;
}

long
undoseq(File *f, int isundo)
{
        if(isundo)
                return f->seq;

        return prevseq(&f->epsilon);
}

void
fileundo(File *f, int isundo, int canredo, uint *q0p, uint *q1p, int flag)
{
        Undo u;
        Rune *buf;
        uint i, n, up;
        uint stop;
        Buffer *delta, *epsilon;

        if(isundo){
                /* undo; reverse delta onto epsilon, seq decreases */
                delta = &f->delta;
                epsilon = &f->epsilon;
                stop = f->seq;
        }else{
                /* redo; reverse epsilon onto delta, seq increases */
                delta = &f->epsilon;
                epsilon = &f->delta;
                stop = 0;       /* don't know yet */
        }

        raspstart(f);
        while(delta->nc > 0){
                /* rasp and buffer are in sync; sync with wire if needed */
                if(needoutflush())
                        raspflush(f);
                up = delta->nc-Undosize;
                bufread(delta, up, (Rune*)&u, Undosize);
                if(isundo){
                        if(u.seq < stop){
                                f->seq = u.seq;
                                raspdone(f, flag);
                                return;
                        }
                }else{
                        if(stop == 0)
                                stop = u.seq;
                        if(u.seq > stop){
                                raspdone(f, flag);
                                return;
                        }
                }
                switch(u.type){
                default:
                        panic("undo unknown u.type");
                        break;

                case Delete:
                        f->seq = u.seq;
                        if(canredo)
                                fileundelete(f, epsilon, u.p0, u.p0+u.n);
                        f->mod = u.mod;
                        bufdelete(f, u.p0, u.p0+u.n);
                        raspdelete(f, u.p0, u.p0+u.n, flag);
                        *q0p = u.p0;
                        *q1p = u.p0;
                        break;

                case Insert:
                        f->seq = u.seq;
                        if(canredo)
                                fileuninsert(f, epsilon, u.p0, u.n);
                        f->mod = u.mod;
                        up -= u.n;
                        buf = fbufalloc();
                        for(i=0; i<u.n; i+=n){
                                n = u.n - i;
                                if(n > RBUFSIZE)
                                        n = RBUFSIZE;
                                bufread(delta, up+i, buf, n);
                                bufinsert(f, u.p0+i, buf, n);
                                raspinsert(f, u.p0+i, buf, n, flag);
                        }
                        fbuffree(buf);
                        *q0p = u.p0;
                        *q1p = u.p0+u.n;
                        break;

                case Filename:
                        f->seq = u.seq;
                        if(canredo)
                                fileunsetname(f, epsilon);
                        f->mod = u.mod;
                        up -= u.n;

                        Strinsure(&f->name, u.n+1);
                        bufread(delta, up, f->name.s, u.n);
                        f->name.s[u.n] = 0;
                        f->name.n = u.n;
                        fixname(&f->name);
                        sortname(f);
                        break;
                case Dot:
                        f->seq = u.seq;
                        if(canredo)
                                fileunsetdot(f, epsilon, f->dot.r);
                        f->mod = u.mod;
                        f->dot.r.p1 = u.p0;
                        f->dot.r.p2 = u.p0 + u.n;
                        break;
                case Mark:
                        f->seq = u.seq;
                        if(canredo)
                                fileunsetmark(f, epsilon, f->mark);
                        f->mod = u.mod;
                        f->mark.p1 = u.p0;
                        f->mark.p2 = u.p0 + u.n;
                        break;
                }
                bufdelete(delta, up, delta->nc);
        }
        if(isundo)
                f->seq = 0;
        raspdone(f, flag);
}

void
filereset(File *f)
{
        bufreset(&f->delta);
        bufreset(&f->epsilon);
        f->seq = 0;
}

void
fileclose(File *f)
{
        Strclose(&f->name);
        bufclose(f);
        bufclose(&f->delta);
        bufclose(&f->epsilon);
        if(f->rasp)
                listfree(f->rasp);
        free(f);
}

void
filemark(File *f)
{

        if(f->unread)
                return;
        if(f->epsilon.nc)
                bufdelete(&f->epsilon, 0, f->epsilon.nc);

        if(f != cmd){
                f->prevdot = f->dot.r;
                f->prevmark = f->mark;
                f->prevseq = f->seq;
                f->prevmod = f->mod;
        }

        f->ndot = f->dot;
        f->seq = seq;
        f->hiposn = 0;
}