Logo Search packages:      
Sourcecode: sam version File versions  Download package

file.c

/* Copyright (c) 1992 AT&T - All rights reserved. */
#include "sam.h"
/*
 * Files are splayed out a factor of NDISC to reduce indirect block access
 */
Discdesc    *files[NDISC];
Discdesc    *transcripts[NDISC];
Buffer            *undobuf;
static String     *ftempstr(Rune*, int);
int         fcount;
File        *lastfile;

void  puthdr_csl(Buffer*, char, short, Posn);
void  puthdr_cs(Buffer*, char, short);
void  puthdr_M(Buffer*, Posn, Range, Range, Mod, short);
void  puthdr_cll(Buffer*, char, Posn, Posn);
void  Fflush(File*);

enum{
      SKIP=50,          /* max dist between file changes folded together */
      MAXCACHE=STRSIZE  /* max length of cache. must be < 32K-BLOCKSIZE */
};

void
Fstart(void)
{
      undobuf = Bopen(Dstart());
      snarfbuf = Bopen(Dstart());
      plan9buf = Bopen(Dstart());
}

void
Fmark(File *f, Mod m)
{
      Buffer *t = f->transcript;
      Posn p;

      if(f->state == Readerr)
            return;
      if(f->state == Unread)  /* this is implicit 'e' of a file */
            return;
      p = m==0? -1 : f->markp;
      f->markp = t->nrunes;
      puthdr_M(t, p, f->dot.r, f->mark, f->mod, f->state);
      f->ndot = f->dot;
      f->marked = TRUE;
      f->mod = m;
      f->hiposn = -1;
      /* Safety first */
      f->cp1 = f->cp2 = 0;
}

File *
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]);
      if(++fcount == NDISC)
            fcount = 0;
      f->nrunes = 0;
      f->markp = 0;
      f->mod = 0;
      f->dot.f = f;
      f->ndot.f = f;
      f->dev = ~0;
      f->qid = ~0;
      Strinit0(&f->name);
      Strinit(&f->cache);
      f->state = Unread;
      Fmark(f, (Mod)0);
      return f;
}

void
Fclose(File *f)
{
      if(f == lastfile)
            lastfile = 0;
      Bterm(f->buf);
      Bterm(f->transcript);
      Strclose(&f->name);
      Strclose(&f->cache);
      if(f->rasp)
            listfree(f->rasp);
      free(f);
}

void
Finsert(File *f, String *str, Posn p1)
{
      Buffer *t = f->transcript;

      if(f->state == Readerr)
            return;
      if(str->n == 0)
            return;
      if(str->n<0 || str->n>STRSIZE)
            panic("Finsert");
      if(f->mod < modnum)
            Fmark(f, modnum);
      if(p1 < f->hiposn)
            error(Esequence);
      if(str->n >= BLOCKSIZE){      /* don't bother with the cache */
            Fflush(f);
            puthdr_csl(t, 'i', str->n, p1);
            Binsert(t, str, t->nrunes);
      }else{      /* insert into the cache instead of the transcript */
            if(f->cp2==0 && f->cp1==0 && f->cache.n==0)     /* empty cache */
                  f->cp1 = f->cp2 = p1;
            if(p1-f->cp2>SKIP || f->cache.n+str->n>MAXCACHE-SKIP){
                  Fflush(f);
                  f->cp1 = f->cp2 = p1;
            }
            if(f->cp2 != p1){ /* grab the piece in between */
                  Rune buf[SKIP];
                  String s;
                  Fchars(f, buf, f->cp2, p1);
                  s.s = buf;
                  s.n = p1-f->cp2;
                  Strinsert(&f->cache, &s, f->cache.n);
                  f->cp2 = p1;
            }
            Strinsert(&f->cache, str, f->cache.n);
      }
      if(f != cmd)
            quitok = FALSE;
      f->closeok = FALSE;
      if(f->state == Clean)
            state(f, Dirty);
      f->hiposn = p1;
}

void
Fdelete(File *f, Posn p1, Posn p2)
{
      if(f->state == Readerr)
            return;
      if(p1==p2)
            return;
      if(f->mod<modnum)
            Fmark(f, modnum);
      if(p1<f->hiposn)
            error(Esequence);
      if(p1-f->cp2>SKIP)
            Fflush(f);
      if(f->cp2==0 && f->cp1==0 && f->cache.n==0)     /* empty cache */
            f->cp1 = f->cp2 = p1;
      if(f->cp2 != p1){ /* grab the piece in between */
            if(f->cache.n+(p1-f->cp2)>MAXCACHE){
                  Fflush(f);
                  f->cp1 = f->cp2 = p1;
            }else{
                  Rune buf[SKIP];
                  String s;
                  Fchars(f, buf, f->cp2, p1);
                  s.s = buf;
                  s.n = p1-f->cp2;
                  Strinsert(&f->cache, &s, f->cache.n);
            }
      }
      f->cp2 = p2;
      if(f!=cmd)
            quitok = FALSE;
      f->closeok = FALSE;
      if(f->state==Clean)
            state(f, Dirty);
      f->hiposn = p2;
}

void
Fflush(File *f)
{
      Buffer *t = f->transcript;
      Posn p1 = f->cp1, p2 = f->cp2;

      if(f->state == Readerr)
            return;
      if(p1 != p2)
            puthdr_cll(t, 'd', p1, p2);
      if(f->cache.n){
            puthdr_csl(t, 'i', f->cache.n, p2);
            Binsert(t, &f->cache, t->nrunes);
            Strzero(&f->cache);
      }
      f->cp1 = f->cp2 = 0;
}

void
Fsetname(File *f, String *s)
{
      Buffer *t = f->transcript;

      if(f->state == Readerr)
            return;
      if(f->state == Unread){ /* This is setting initial file name */
            Strduplstr(&f->name, s);
            sortname(f);
      }else{
            if(f->mod < modnum)
                  Fmark(f, modnum);
            puthdr_cs(t, 'f', s->n);
            Binsert(t, s, t->nrunes);
      }
}

/*
 * The heart of it all. Fupdate will run along the transcript list, executing
 * the commands and converting them into their inverses for a later undo pass.
 * The pass runs top to bottom, so addresses in the transcript are tracked
 * (by the var. delta) so they stay valid during the operation.  This causes
 * all operations to appear to happen simultaneously, which is why the addresses
 * passed to Fdelete and Finsert never take into account other changes occurring
 * in this command (and is why things are done this way).
 */
int
Fupdate(File *f, int mktrans, int toterm)
{
      Buffer *t = f->transcript;
      Buffer *u = undobuf;
      int n, ni;
      Posn p0, p1, p2, p, deltadot = 0, deltamark = 0, delta = 0;
      int changes = FALSE;
      union Hdr buf;
      Rune tmp[BLOCKSIZE+1];  /* +1 for NUL in 'f' case */

      if(f->state == Readerr)
            return FALSE;
      if(lastfile && f!=lastfile)
            Bclean(lastfile->transcript); /* save memory when multifile */
      lastfile = f;
      Fflush(f);
      if(f->marked)
            p0 = f->markp+sizeof(Mark)/RUNESIZE;
      else
            p0 = 0;
      f->dot = f->ndot;
      while((n=Bread(t, (Rune*)&buf, sizeof buf/RUNESIZE, p0)) > 0){
            switch(buf.cs.c){
            default:
                  panic("unknown in Fupdate");
            case 'd':
                  p1 = buf.cll.l;
                  p2 = buf.cll.l1;
                  p0 += sizeof(struct _cll)/RUNESIZE;
                  if(p2 <= f->dot.r.p1)
                        deltadot -= p2-p1;
                  if(p2 <= f->mark.p1)
                        deltamark -= p2-p1;
                  p1 += delta, p2+=delta;
                  delta -= p2-p1;
                  if(!mktrans)
                        for(p = p1; p<p2; p+=ni){
                              if(p2-p>BLOCKSIZE)
                                    ni = BLOCKSIZE;
                              else
                                    ni = p2-p;
                              puthdr_csl(u, 'i', ni, p1);
                              Bread(f->buf, tmp, ni, p);
                              Binsert(u, ftempstr(tmp, ni), u->nrunes);
                        }
                  f->nrunes -= p2-p1;
                  Bdelete(f->buf, p1, p2);
                  changes = TRUE;
                  break;

            case 'f':
                  n = buf.cs.s;
                  p0 += sizeof(struct _cs)/RUNESIZE;
                  Strinsure(&genstr, n+1);
                  Bread(t, tmp, n, p0);
                  tmp[n] = 0;
                  p0 += n;
                  Strdupl(&genstr, tmp);
                  if(!mktrans){
                        puthdr_cs(u, 'f', f->name.n);
                        Binsert(u, &f->name, u->nrunes);
                  }
                  Strduplstr(&f->name, &genstr);
                  sortname(f);
                  changes = TRUE;
                  break;

            case 'i':
                  n = buf.csl.s;
                  p1 = buf.csl.l;
                  p0 += sizeof(struct _csl)/RUNESIZE;
                  if(p1 < f->dot.r.p1)
                        deltadot += n;
                  if(p1 < f->mark.p1)
                        deltamark += n;
                  p1 += delta;
                  delta += n;
                  if(!mktrans)
                        puthdr_cll(u, 'd', p1, p1+n);
                  changes = TRUE;
                  f->nrunes += n;
                  while(n > 0){
                        if(n > BLOCKSIZE)
                              ni = BLOCKSIZE;
                        else
                              ni = n;
                        Bread(t, tmp, ni, p0);
                        Binsert(f->buf, ftempstr(tmp, ni), p1);
                        n -= ni;
                        p1 += ni;
                        p0 += ni;
                  }
                  break;
            }
      }
      toterminal(f, toterm);
      f->dot.r.p1 += deltadot;
      f->dot.r.p2 += deltadot;
      if(f->dot.r.p1 > f->nrunes)
            f->dot.r.p1 = f->nrunes;
      if(f->dot.r.p2 > f->nrunes)
            f->dot.r.p2 = f->nrunes;
      f->mark.p1 += deltamark;
      f->mark.p2 += deltamark;
      if(f->mark.p1 > f->nrunes)
            f->mark.p1 = f->nrunes;
      if(f->mark.p2 > f->nrunes)
            f->mark.p2 = f->nrunes;
      if(n < 0)
            panic("Fupdate read");
      if(f == cmd)
            f->mod = 0; /* can't undo command file */
      if(p0 > f->markp+sizeof(Posn)/RUNESIZE){  /* for undo, this throws away the undo transcript */
            if(f->mod > 0){   /* can't undo the dawn of time */
                  Bdelete(t, f->markp+sizeof(Mark)/RUNESIZE, t->nrunes);
                  /* copy the undo list back into the transcript */
                  for(p = 0; p<u->nrunes; p+=ni){
                        if(u->nrunes-p>BLOCKSIZE)
                              ni = BLOCKSIZE;
                        else
                              ni = u->nrunes-p;
                        Bread(u, tmp, ni, p);
                        Binsert(t, ftempstr(tmp, ni), t->nrunes);
                  }
            }
            Bdelete(u, (Posn)0, u->nrunes);
      }
      return f==cmd? FALSE : changes;
}

void
puthdr_csl(Buffer *b, char c, short s, Posn p)
{
      struct _csl buf;

      if(p < 0)
            panic("puthdr_csP");
      buf.c = c;
      buf.s = s;
      buf.l = p;
      Binsert(b, ftempstr((Rune*)&buf, sizeof buf/RUNESIZE), b->nrunes);
}

void
puthdr_cs(Buffer *b, char c, short s)
{
      struct _cs buf;

      buf.c = c;
      buf.s = s;
      Binsert(b, ftempstr((Rune*)&buf, sizeof buf/RUNESIZE), b->nrunes);
}

void
puthdr_M(Buffer *b, Posn p, Range dot, Range mk, Mod m, short s1)
{
      Mark mark;
      static first = 1;

      if(!first && p<0)
            panic("puthdr_M");
      mark.p = p;
      mark.dot = dot;
      mark.mark = mk;
      mark.m = m;
      mark.s1 = s1;
      Binsert(b, ftempstr((Rune *)&mark, sizeof mark/RUNESIZE), b->nrunes);
}

void
puthdr_cll(Buffer *b, char c, Posn p1, Posn p2)
{
      struct _cll buf;

      if(p1<0 || p2<0)
            panic("puthdr_cll");
      buf.c = c;
      buf.l = p1;
      buf.l1 = p2;
      Binsert(b, ftempstr((Rune*)&buf, sizeof buf/RUNESIZE), b->nrunes);
}

long
Fchars(File *f, Rune *addr, Posn p1, Posn p2)
{
      return Bread(f->buf, addr, p2-p1, p1);
}

int
Fgetcset(File *f, Posn p)
{
      if(p<0 || p>f->nrunes)
            panic("Fgetcset out of range");
      if((f->ngetc = Fchars(f, f->getcbuf, p, p+NGETC))<0)
            panic("Fgetcset Bread fail");
      f->getcp = p;
      f->getci = 0;
      return f->ngetc;
}

int
Fbgetcset(File *f, Posn p)
{
      if(p<0 || p>f->nrunes)
            panic("Fbgetcset out of range");
      if((f->ngetc = Fchars(f, f->getcbuf, p<NGETC? (Posn)0 : p-NGETC, p))<0)
            panic("Fbgetcset Bread fail");
      f->getcp = p;
      f->getci = f->ngetc;
      return f->ngetc;
}

int
Fgetcload(File *f, Posn p)
{
      if(Fgetcset(f, p)){
            --f->ngetc;
            f->getcp++;
            return f->getcbuf[f->getci++];
      }
      return -1;
}

int
Fbgetcload(File *f, Posn p)
{
      if(Fbgetcset(f, p)){
            --f->getcp;
            return f->getcbuf[--f->getci];
      }
      return -1;
}

static String*
ftempstr(Rune *s, int n)
{
      static String p;

      p.s = s;
      p.n = n;
      p.size = n;
      return &p;
}

Generated by  Doxygen 1.6.0   Back to index