Switch to simple buffers. - sam - An updated version of the sam text editor.
 (HTM) git clone git://vernunftzentrum.de/sam.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
 (DIR) commit b74192453a1bd53ded5dca89283ff6f27be028d2
 (DIR) parent c278f2b4d6f063d41632760c4e59addd653af041
 (HTM) Author: Rob King <jking@deadpixi.com>
       Date:   Mon,  3 Oct 2016 22:53:59 -0500
       
       Switch to simple buffers.
       
       Diffstat:
         sam/Makefile                        |       2 +-
         sam/buffer.c                        |     267 ++++++++++++++-----------------
         sam/disc.c                          |     335 -------------------------------
         sam/file.c                          |      18 +++++-------------
         sam/sam.h                           |      62 ++-----------------------------
       
       5 files changed, 133 insertions(+), 551 deletions(-)
       ---
 (DIR) diff --git a/sam/Makefile b/sam/Makefile
       @@ -33,7 +33,7 @@ CFLAGS=$(STANDARDS) $(INCS) $(INCLUDES) -DUSE64BITS=$(USE64BITS) -DRXPATH='"$(RX
        LIB=../libframe/libframe.a ../libXg/libXg.a
        CC?=c99
        
       -OBJ=sam.o address.o buffer.o cmd.o disc.o error.o file.o io.o \
       +OBJ=sam.o address.o buffer.o cmd.o error.o file.o io.o \
                list.o mesg.o moveto.o multi.o rasp.o regexp.o shell.o \
                string.o sys.o unix.o xec.o
        
 (DIR) diff --git a/sam/buffer.c b/sam/buffer.c
       @@ -1,176 +1,155 @@
       -/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
       +/* Copyright 2016 Rob King -- See LICENSE for details */
       +
        #include "sam.h"
        
       -int incache(Buffer*, Posn, Posn);
       +#define BUFFER_MIN 65535
       +#define GAPSIZE(b) ((b)->ge - (b)->gs)
        
       -Buffer *
       -Bopen(Discdesc *dd)
       +typedef size_t pos_t;
       +typedef struct Gapbuffer Gapbuffer;
       +struct Gapbuffer{
       +    size_t size;
       +    pos_t gs;
       +    pos_t ge;
       +    wchar_t *buf;
       +};
       +
       +static void
       +movegap(Gapbuffer *b, pos_t p)
        {
       -    Buffer *b;
       +    if (p == b->gs)
       +        return;
       +    else if (p < b->gs){
       +        size_t d = b->gs - p;
       +        b->gs -= d;
       +        b->ge -= d;
       +        wmemmove(b->buf + b->ge, b->buf + b->gs, d);
       +    } else{
       +        size_t d = p - b->gs;
       +        b->gs += d;
       +        b->ge += d;
       +        wmemmove(b->buf + b->gs - d, b->buf + b->ge - d, d);
       +    }
       +}
        
       -    b = emalloc(sizeof(Buffer));
       -    b->disc = Dopen(dd);
       -    Strinit(&b->cache);
       -    return b;
       +static void
       +ensuregap(Gapbuffer *b, size_t l)
       +{
       +    size_t ns = b->size + l + BUFFER_MIN;
       +    size_t es = b->size - b->ge;
       +
       +    if (GAPSIZE(b) >= l)
       +        return;
       +
       +    b->buf = realloc(b->buf, ns * RUNESIZE);
       +    if (!b->buf)
       +        panic("out of memory");
       +
       +    wmemmove(b->buf + (ns - es), b->buf + b->ge, es);
       +    b->ge = ns - es;
       +    b->size = ns;
        }
        
       -void
       -Bterm(Buffer *b)
       +static void
       +deletebuffer(Gapbuffer *b, pos_t p, size_t l)
        {
       -    Dclose(b->disc);
       -    Strclose(&b->cache);
       -    free(b);
       +    movegap(b, p);
       +    b->ge += l;
        }
        
       -int
       -Bread(Buffer *b, wchar_t *addr, int n, Posn p0)
       +static size_t
       +readbuffer(Gapbuffer *b, pos_t p, size_t l, wchar_t *c)
        {
       -    int m;
       -
       -    if(b->c2>b->disc->nrunes || b->c1>b->disc->nrunes)
       -        panic("bread cache");
       -    if(p0 < 0)
       -        panic("Bread p0<0");
       -    if(p0+n > b->nrunes){
       -        n = b->nrunes-p0;
       -        if(n < 0)
       -            panic("Bread<0");
       -    }
       -    if(!incache(b, p0, p0+n)){
       -        Bflush(b);
       -        if(n>=BLOCKSIZE/2)
       -            return Dread(b->disc, addr, n, p0);
       -        else{
       -            Posn minp;
       -            if(b->nrunes-p0>BLOCKSIZE/2)
       -                m = BLOCKSIZE/2;
       -            else
       -                m = b->nrunes-p0;
       -            if(m<n)
       -                m = n;
       -            minp = p0-BLOCKSIZE/2;
       -            if(minp<0)
       -                minp = 0;
       -            m += p0-minp;
       -            Strinsure(&b->cache, m);
       -            if(Dread(b->disc, b->cache.s, m, minp)!=m)
       -                panic("Bread");
       -            b->cache.n = m;
       -            b->c1 = minp;
       -            b->c2 = minp+m;
       -            b->dirty = false;
       -        }
       +    size_t r = 0;
       +
       +    if (p < b->gs){
       +        size_t d = b->gs - p;
       +        size_t t = l > d ? d : l;
       +
       +        wmemcpy(c, b->buf + p, t);
       +        c += t;
       +        l -= t;
       +        r += t;
       +
       +        wmemcpy(c, b->buf + b->ge, l);
       +        r += l;
       +    } else{
       +        p += GAPSIZE(b);
       +
       +        wmemcpy(c, b->buf + p, l);
       +        r = l;
            }
       -    memmove(addr, &b->cache.s[p0-b->c1], n*RUNESIZE);
       -    return n;
       +
       +    return r;
        }
        
       -void
       -Binsert(Buffer *b, String *s, Posn p0)
       +static void
       +insertbuffer(Gapbuffer *b, pos_t p, const wchar_t *s, size_t l)
        {
       -    if(b->c2>b->disc->nrunes || b->c1>b->disc->nrunes)
       -        panic("binsert cache");
       -    if(p0<0)
       -        panic("Binsert p0<0");
       -    if(s->n == 0)
       -        return;
       -    if(incache(b, p0, p0) && b->cache.n+s->n<=STRSIZE){
       -        Strinsert(&b->cache, s, p0-b->c1);
       -        b->dirty = true;
       -        if(b->cache.n > BLOCKSIZE*2){
       -            b->nrunes += s->n;
       -            Bflush(b);
       -            /* try to leave some cache around p0 */
       -            if(p0 >= b->c1+BLOCKSIZE){
       -                /* first BLOCKSIZE can go */
       -                Strdelete(&b->cache, 0, BLOCKSIZE);
       -                b->c1 += BLOCKSIZE;
       -            }else if(p0 <= b->c2-BLOCKSIZE){
       -                /* last BLOCKSIZE can go */
       -                b->cache.n -= BLOCKSIZE;
       -                b->c2 -= BLOCKSIZE;
       -            }else{
       -                /* too hard; negate the cache and pick up next time */
       -                Strzero(&b->cache);
       -                b->c1 = b->c2 = 0;
       -            }
       -            return;
       -        }
       -    }else{
       -        Bflush(b);
       -        if(s->n >= BLOCKSIZE/2){
       -            b->cache.n = 0;
       -            b->c1 = b->c2 = 0;
       -            Dinsert(b->disc, s->s, s->n, p0);
       -        }else{
       -            int m;
       -            Posn minp;
       -            if(b->nrunes-p0 > BLOCKSIZE/2)
       -                m = BLOCKSIZE/2;
       -            else
       -                m = b->nrunes-p0;
       -            minp = p0-BLOCKSIZE/2;
       -            if(minp < 0)
       -                minp = 0;
       -            m += p0-minp;
       -            Strinsure(&b->cache, m);
       -            if(Dread(b->disc, b->cache.s, m, minp)!=m)
       -                panic("Bread");
       -            b->cache.n = m;
       -            b->c1 = minp;
       -            b->c2 = minp+m;
       -            Strinsert(&b->cache, s, p0-b->c1);
       -            b->dirty = true;
       -        }
       -    }
       -    b->nrunes += s->n;
       +    ensuregap(b, l);
       +    movegap(b, p);
       +    wmemcpy(b->buf + b->gs, s, l);
       +    b->gs += l;
        }
        
       -void
       -Bdelete(Buffer *b, Posn p1, Posn p2)
       +Buffer *
       +Bopen(void)
        {
       -    if(p1<0 || p2<0)
       -        panic("Bdelete p<0");
       -    if(b->c2>b->disc->nrunes || b->c1>b->disc->nrunes)
       -        panic("bdelete cache");
       -    if(p1 == p2)
       -        return;
       -    if(incache(b, p1, p2)){
       -        Strdelete(&b->cache, p1-b->c1, p2-b->c1);
       -        b->dirty = true;
       -    }else{
       -        Bflush(b);
       -        Ddelete(b->disc, p1, p2);
       -        b->cache.n = 0;
       -        b->c1 = b->c2 = 0;
       -    }
       -    b->nrunes -= p2-p1;
       +    Buffer *b = calloc(1, sizeof(Buffer));
       +    if (!b)
       +        panic("out of memory");
       +
       +    b->gb = calloc(1, sizeof(Gapbuffer));
       +    if (!b->gb)
       +        panic("out of memory");
       +
       +    b->gb->buf = calloc(1, BUFFER_MIN * RUNESIZE);
       +    if (!b->gb->buf)
       +        panic("out of memory");
       +
       +    b->gb->size = BUFFER_MIN;
       +    b->gb->gs = 0;
       +    b->gb->ge = BUFFER_MIN;
       +
       +    return b;
        }
        
        void
       -Bflush(Buffer *b)
       +Bterm(Buffer *b)
        {
       -    if(b->dirty){
       -        Dreplace(b->disc, b->c1, b->c2, b->cache.s, b->cache.n);
       -        b->c2 = b->c1+b->cache.n;
       -        b->dirty = false;
       -        if(b->nrunes != b->disc->nrunes)
       -            panic("Bflush");
       +    if (b){
       +        free(b->gb->buf);
       +        free(b->gb);
       +        free(b);
            }
        }
        
       +int
       +Bread(Buffer *b, wchar_t *c, int l, Posn p)
       +{
       +    if (p + l > b->nrunes)
       +        l = b->nrunes - p;
       +
       +    if (l == 0)
       +        return 0;
       +
       +    size_t r = readbuffer(b->gb, p, l, c);
       +    return (int)r;
       +}
       +
        void
       -Bclean(Buffer *b)
       +Binsert(Buffer *b, String *s, Posn p)
        {
       -    if(b->dirty){
       -        Bflush(b);
       -        b->c1 = b->c2 = 0;
       -        Strzero(&b->cache);
       +    if (s->n > 0){
       +        insertbuffer(b->gb, (size_t)p, s->s, s->n);
       +        b->nrunes += s->n;
            }
        }
        
       -int
       -incache(Buffer *b, Posn p1, Posn p2)
       +void
       +Bdelete(Buffer *b, Posn p1, Posn p2)
        {
       -    return b->c1<=p1 && p2<=b->c1+b->cache.n;
       +    size_t l = p2 - p1;
       +    deletebuffer(b->gb, p1, l);
       +    b->nrunes -= l;
        }
 (DIR) diff --git a/sam/disc.c b/sam/disc.c
       @@ -1,335 +0,0 @@
       -/* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
       -#include "sam.h"
       -
       -#define BLOCKFILL   (BLOCKSIZE/2)
       -
       -static Discdesc desc[NBUFFILES];
       -
       -void        bkalloc(Disc*, int);
       -void        bkfree(Disc*, int);
       -void        bkwrite(Disc*, wchar_t*, int, int, int);
       -void        bkread(Disc*, wchar_t*, int, int, int);
       -
       -
       -Discdesc *
       -Dstart(void)
       -{
       -    int i, fd;
       -    Discdesc *dd;
       -
       -    for(i=0, dd=desc; dd->fd; i++, dd++)
       -        if(i == NBUFFILES-1)
       -            panic("too many buffer files");
       -    fd = newtmp();
       -    if(fd < 0)
       -        panic("can't create buffer file");
       -    dd->fd = fd;
       -    return dd;
       -}
       -
       -Disc *
       -Dopen(Discdesc *dd)
       -{
       -    Disc *d;
       -
       -    d = emalloc(sizeof(Disc));
       -    d->desc = dd;
       -    return d;
       -}
       -
       -void
       -Dclose(Disc *d)
       -{
       -    int i;
       -
       -    for(i=d->block.nused; --i>=0; ) /* backwards because bkfree() stacks */
       -        bkfree(d, i);
       -    free(d->block.listptr);
       -    free(d);
       -}
       -
       -int
       -Dread(Disc *d, wchar_t *addr, int n, Posn p1)
       -{
       -    int i, nb, nr;
       -    Posn p = 0, p2 = p1+n;
       -
       -    for(i=0; i<d->block.nused; i++){
       -        if((p+=d->block.blkptr[i].nrunes) > p1){
       -            p -= d->block.blkptr[i].nrunes;
       -            goto out;
       -        }
       -    }
       -    if(p == p1)
       -        return 0;   /* eof */
       -    return -1;      /* past eof */
       -
       -    out:
       -    n = 0;
       -    if(p != p1){    /* trailing partial block */
       -        nb = d->block.blkptr[i].nrunes;
       -        if(p2 > p+nb)
       -            nr = nb-(p1-p);
       -        else
       -            nr = p2-p1;
       -        bkread(d, addr, nr, i, p1-p);
       -        /* advance to next block */
       -        p += nb;
       -        addr += nr;
       -        n += nr;
       -        i++;
       -    }
       -    /* whole blocks */
       -    while(p<p2 && (nb = d->block.blkptr[i].nrunes)<=p2-p){
       -        if(i >= d->block.nused)
       -            return n;   /* eof */
       -        bkread(d, addr, nb, i, 0);
       -        p += nb;
       -        addr += nb;
       -        n += nb;
       -        i++;
       -    }
       -    if(p < p2){ /* any initial partial block left? */
       -        nr = p2-p;
       -        nb = d->block.blkptr[i].nrunes;
       -        if(nr>nb)
       -            nr = nb;        /* eof */
       -        /* just read in the part that survives */
       -        bkread(d, addr, nr, i, 0);
       -        n += nr;
       -    }
       -    return n;
       -}
       -
       -void
       -Dinsert(Disc *d, wchar_t *addr, int n, Posn p0) /* if addr null, just make space */
       -{
       -    int i, nb, ni;
       -    Posn p = 0;
       -    wchar_t hold[BLOCKSIZE];
       -    int nhold;
       -
       -    for(i=0; i<d->block.nused; i++){
       -        if((p+=d->block.blkptr[i].nrunes) >= p0){
       -            p -= d->block.blkptr[i].nrunes;
       -            goto out;
       -        }
       -    }
       -    if(p != p0)
       -        panic("Dinsert");   /* beyond eof */
       -
       -    out:
       -    d->nrunes += n;
       -    nhold = 0;
       -    if(i<d->block.nused && (nb=d->block.blkptr[i].nrunes)>p0-p){
       -        nhold = nb-(p0-p);
       -        bkread(d, hold, nhold, i, p0-p);
       -        d->block.blkptr[i].nrunes -= nhold; /* no write necessary */
       -    }
       -    /* insertion point is now at end of block i (which may not exist) */
       -    while(n > 0){
       -        if(i < d->block.nused
       -        && (nb=d->block.blkptr[i].nrunes) < BLOCKFILL){
       -            /* fill this block */
       -            if(nb+n > BLOCKSIZE)
       -                ni = BLOCKFILL-nb;
       -            else
       -                ni = n;
       -            if(addr)
       -                bkwrite(d, addr, ni, i, nb);
       -            nb += ni;
       -        }else{  /* make new block */
       -            if(i < d->block.nused)
       -                i++;    /* put after this block, if it exists */
       -            bkalloc(d, i);
       -            if(n > BLOCKSIZE)
       -                ni = BLOCKFILL;
       -            else
       -                ni = n;
       -            if(addr)
       -                bkwrite(d, addr, ni, i, 0);
       -            nb = ni;
       -        }
       -        d->block.blkptr[i].nrunes = nb;
       -        if(addr)
       -            addr += ni;
       -        n -= ni;
       -    }
       -    if(nhold){
       -        if(i < d->block.nused
       -        && (nb=d->block.blkptr[i].nrunes)+nhold < BLOCKSIZE){
       -            /* fill this block */
       -            bkwrite(d, hold, nhold, i, nb);
       -            nb += nhold;
       -        }else{  /* make new block */
       -            if(i < d->block.nused)
       -                i++;    /* put after this block, if it exists */
       -            bkalloc(d, i);
       -            bkwrite(d, hold, nhold, i, 0);
       -            nb = nhold;
       -        }
       -        d->block.blkptr[i].nrunes = nb;
       -    }
       -}
       -
       -void
       -Ddelete(Disc *d, Posn p1, Posn p2)
       -{
       -    int i, nb, nd;
       -    Posn p = 0;
       -    wchar_t buf[BLOCKSIZE];
       -
       -    for(i = 0; i<d->block.nused; i++){
       -        if((p+=d->block.blkptr[i].nrunes) > p1){
       -            p -= d->block.blkptr[i].nrunes;
       -            goto out;
       -        }
       -    }
       -    if(p1!=d->nrunes || p2!=p1)
       -        panic("Ddelete");
       -    return; /* beyond eof */
       -
       -    out:
       -    d->nrunes -= p2-p1;
       -    if(p != p1){    /* throw away partial block */
       -        nb = d->block.blkptr[i].nrunes;
       -        bkread(d, buf, nb, i, 0);
       -        if(p2 >= p+nb)
       -            nd = nb-(p1-p);
       -        else{
       -            nd = p2-p1;
       -            memmove(buf+(p1-p), buf+(p1-p)+nd, RUNESIZE*(nb-((p1-p)+nd)));
       -        }
       -        nb -= nd;
       -        bkwrite(d, buf, nb, i, 0);
       -        d->block.blkptr[i].nrunes = nb;
       -        p2 -= nd;
       -        /* advance to next block */
       -        p += nb;
       -        i++;
       -    }
       -    /* throw away whole blocks */
       -    while(p<p2 && (nb = d->block.blkptr[i].nrunes)<=p2-p){
       -        if(i >= d->block.nused)
       -            panic("Ddelete 2");
       -        bkfree(d, i);
       -        p2 -= nb;
       -    }
       -    if(p >= p2) /* any initial partial block left to delete? */
       -        return; /* no */
       -    nd = p2-p;
       -    nb = d->block.blkptr[i].nrunes;
       -    /* just read in the part that survives */
       -    bkread(d, buf, nb-=nd, i, nd);
       -    /* a little block merging */
       -    if(nb<BLOCKSIZE/2 && i>0 && (nd = d->block.blkptr[i-1].nrunes)<BLOCKSIZE/2){
       -        memmove(buf+nd, buf, RUNESIZE*nb);
       -        bkread(d, buf, nd, --i, 0);
       -        bkfree(d, i);
       -        nb += nd;
       -    }
       -    bkwrite(d, buf, nb, i, 0);
       -    d->block.blkptr[i].nrunes = nb;
       -}
       -
       -void
       -Dreplace(Disc *d, Posn p1, Posn p2, wchar_t *addr, int n)
       -{
       -    int i, nb, nr;
       -    Posn p = 0;
       -    wchar_t buf[BLOCKSIZE];
       -
       -    if(p2-p1 > n)
       -        Ddelete(d, p1+n, p2);
       -    else if(p2-p1 < n)
       -        Dinsert(d, 0, n-(p2-p1), p2);
       -    if(n == 0)
       -        return;
       -    p2 = p1+n;
       -    /* they're now conformal; replace in place */
       -    for(i=0; i<d->block.nused; i++){
       -        if((p+=d->block.blkptr[i].nrunes) > p1){
       -            p -= d->block.blkptr[i].nrunes;
       -            goto out;
       -        }
       -    }
       -    panic("Dreplace");
       -
       -    out:
       -    if(p != p1){    /* trailing partial block */
       -        nb = d->block.blkptr[i].nrunes;
       -        bkread(d, buf, nb, i, 0);
       -        if(p2 > p+nb)
       -            nr = nb-(p1-p);
       -        else
       -            nr = p2-p1;
       -        memmove(buf+p1-p, addr, RUNESIZE*nr);
       -        bkwrite(d, buf, nb, i, 0);
       -        /* advance to next block */
       -        p += nb;
       -        addr += nr;
       -        i++;
       -    }
       -    /* whole blocks */
       -    while(p<p2 && (nb = d->block.blkptr[i].nrunes)<=p2-p){
       -        if(i >= d->block.nused)
       -            panic("Dreplace 2");
       -        bkwrite(d, addr, nb, i, 0);
       -        p += nb;
       -        addr += nb;
       -        i++;
       -    }
       -    if(p < p2){ /* any initial partial block left? */
       -        nr = p2-p;
       -        nb = d->block.blkptr[i].nrunes;
       -        /* just read in the part that survives */
       -        bkread(d, buf+nr, nb-nr, i, nr);
       -        memmove(buf, addr, RUNESIZE*nr);
       -        bkwrite(d, buf, nb, i, 0);
       -    }
       -}
       -
       -void
       -bkread(Disc *d, wchar_t *loc, int n, int bk, int off)
       -{
       -    Seek(d->desc->fd, RUNESIZE*(BLOCKSIZE*d->block.blkptr[bk].bnum+off), 0);
       -    Read(d->desc->fd, loc, n*RUNESIZE);
       -}
       -
       -void
       -bkwrite(Disc *d, wchar_t *loc, int n, int bk, int off)
       -{
       -    Seek(d->desc->fd, RUNESIZE*(BLOCKSIZE*d->block.blkptr[bk].bnum+off), 0);
       -    Write(d->desc->fd, loc, n*RUNESIZE);
       -        /*
       -         * sleazy hack to avoid silly SGI kernel bug
       -         *  fill partial block with garbage
       -         */
       -    if (off+n >= d->block.blkptr[bk].nrunes && off+n < BLOCKSIZE)
       -        Write(d->desc->fd, genbuf, (BLOCKSIZE-(off+n))*RUNESIZE);
       -}
       -
       -void
       -bkalloc(Disc *d, int n)
       -{
       -    Discdesc *dd = d->desc;
       -    uint64_t bnum;
       -
       -    if(dd->free.nused)
       -        bnum = dd->free.longptr[--dd->free.nused];
       -    else
       -        bnum = dd->nbk++;
       -    if(bnum >= 1<<(8*(sizeof(((Block*)0)->bnum))))
       -        error(Etmpovfl);
       -    inslist(&d->block, n, 0L);
       -    d->block.blkptr[n].bnum = bnum;
       -}
       -
       -void
       -bkfree(Disc *d, int n)
       -{
       -    Discdesc *dd = d->desc;
       -
       -    inslist(&dd->free, dd->free.nused, d->block.blkptr[n].bnum);
       -    dellist(&d->block, n);
       -}
 (DIR) diff --git a/sam/file.c b/sam/file.c
       @@ -3,8 +3,6 @@
        /*
         * Files are splayed out a factor of NDISC to reduce indirect block access
         */
       -Discdesc    *files[NDISC];
       -Discdesc    *transcripts[NDISC];
        Buffer      *undobuf;
        static String   *ftempstr(wchar_t*, int);
        int     fcount;
       @@ -24,9 +22,9 @@ enum{
        void
        Fstart(void)
        {
       -    undobuf = Bopen(Dstart());
       -    snarfbuf = Bopen(Dstart());
       -    plan9buf = Bopen(Dstart());
       +    undobuf = Bopen();
       +    snarfbuf = Bopen();
       +    plan9buf = Bopen();
        }
        
        void
       @@ -56,12 +54,8 @@ Fopen(void)
            File *f;
        
            f = emalloc(sizeof(File));
       -    if(files[fcount] == 0){
       -        files[fcount] = Dstart();
       -        transcripts[fcount] = Dstart();
       -    }
       -    f->buf = Bopen(files[fcount]);
       -    f->transcript = Bopen(transcripts[fcount]);
       +    f->buf = Bopen();
       +    f->transcript = Bopen();
            if(++fcount == NDISC)
                fcount = 0;
            f->nrunes = 0;
       @@ -232,8 +226,6 @@ Fupdate(File *f, int mktrans, int toterm)
        
            if(f->state == Readerr)
                return false;
       -    if(lastfile && f!=lastfile)
       -        Bclean(lastfile->transcript);   /* save memory when multifile */
            lastfile = f;
            Fflush(f);
            if(f->marked)
 (DIR) diff --git a/sam/sam.h b/sam/sam.h
       @@ -23,8 +23,6 @@ typedef uint16_t      Mod;        /* modification number */
        typedef struct Address  Address;
        typedef struct Block    Block;
        typedef struct Buffer   Buffer;
       -typedef struct Disc Disc;
       -typedef struct Discdesc Discdesc;
        typedef struct File File;
        typedef struct List List;
        typedef struct Mark Mark;
       @@ -79,47 +77,6 @@ struct List /* code depends on a int64_t being able to hold a pointer */
        #define filepptr    g.filep
        #define listval     g.listv
        
       -/*
       - * Block must fit in a int64_t because the list routines manage arrays of
       - * blocks.  Two problems: some machines (e.g. Cray) can't pull this off
       - * -- on them, use bitfields -- and the uint16_t bnum limits temp file sizes
       - * to about 200 megabytes.  Advantages: small, simple code and small
       - * memory overhead.  If you really want to edit huge files, making BLOCKSIZE
       - * bigger is the easiest way.
       -*
       -* The necessary conditions are even stronger:
       -*      sizeof(struct Block)==sizeof(int64_t)
       -*   && the first 32 bits must hold bnum and nrunes.
       -* When sizeof(uint16_t)+sizeof(int16_t) < sizeof(int64_t),
       -* add padding at the beginning on a little endian and at
       -* the end on a big endian, as shown below for the DEC Alpha.
       - */
       -struct Block
       -{
       -#if USE64BITS == 1
       -    char    pad[sizeof(int64_t)-sizeof(uint16_t)-sizeof(int16_t)];
       -#endif
       -    uint16_t  bnum;       /* absolute number on disk */
       -    int16_t   nrunes;     /* runes stored in this block */
       -#if USE64BITS == 2
       -    char    pad[sizeof(int64_t)-sizeof(uint16_t)-sizeof(int16_t)];
       -#endif
       -};
       -
       -struct Discdesc
       -{
       -    int fd;     /* plan 9 file descriptor of temp file */
       -    uint64_t   nbk;        /* high water mark */
       -    List    free;       /* array of free block indices */
       -};
       -
       -struct Disc
       -{
       -    Discdesc *desc;     /* descriptor of temp file */
       -    Posn    nrunes;     /* runes on disc file */
       -    List    block;      /* list of used block indices */
       -};
       -
        struct String
        {
            int16_t   n;
       @@ -127,14 +84,11 @@ struct String
            wchar_t    *s;
        };
        
       +struct Gapbuffer;
        struct Buffer
        {
       -    Disc    *disc;      /* disc storage */
       -    Posn    nrunes;     /* total length of buffer */
       -    String  cache;      /* in-core storage for efficiency */
       -    Posn    c1, c2;     /* cache start and end positions in disc */
       -                /* note: if dirty, cache is really c1, c1+cache.n */
       -    int dirty;      /* cache dirty */
       +    Posn nrunes;
       +    struct Gapbuffer *gb;
        };
        
        #define NGETC   128
       @@ -209,19 +163,12 @@ union Hdr
        #define Fgetc(f)  ((--(f)->ngetc<0)? Fgetcload(f, (f)->getcp) : (f)->getcbuf[(f)->getcp++, (f)->getci++])
        #define Fbgetc(f) (((f)->getci<=0)? Fbgetcload(f, (f)->getcp) : (f)->getcbuf[--(f)->getcp, --(f)->getci])
        
       -void    Bclean(Buffer*);
        void    Bterm(Buffer*);
        void    Bdelete(Buffer*, Posn, Posn);
        void    Bflush(Buffer*);
        void    Binsert(Buffer*, String*, Posn);
       -Buffer  *Bopen(Discdesc*);
       +Buffer  *Bopen(void);
        int Bread(Buffer*, wchar_t*, int, Posn);
       -void    Dclose(Disc*);
       -void    Ddelete(Disc*, Posn, Posn);
       -void    Dinsert(Disc*, wchar_t*, int, Posn);
       -Disc    *Dopen(Discdesc*);
       -int Dread(Disc*, wchar_t*, int, Posn);
       -void    Dreplace(Disc*, Posn, Posn, wchar_t*, int);
        int Fbgetcload(File*, Posn);
        int Fbgetcset(File*, Posn);
        int64_t    Fchars(File*, wchar_t*, Posn, Posn);
       @@ -334,7 +281,6 @@ void    warn_S(Warn, String*);
        int whichmenu(File*);
        void    writef(File*);
        Posn    writeio(File*);
       -Discdesc *Dstart(void);
        
        extern wchar_t samname[];  /* compiler dependent */
        extern wchar_t *left[];