file.c - 9base - revived minimalist port of Plan 9 userland to Unix
 (HTM) git clone git://git.suckless.org/9base
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       file.c (10709B)
       ---
            1 #include "sam.h"
            2 
            3 /*
            4  * Structure of Undo list:
            5  *         The Undo structure follows any associated data, so the list
            6  *        can be read backwards: read the structure, then read whatever
            7  *        data is associated (insert string, file name) and precedes it.
            8  *        The structure includes the previous value of the modify bit
            9  *        and a sequence number; successive Undo structures with the
           10  *        same sequence number represent simultaneous changes.
           11  */
           12 
           13 typedef struct Undo Undo;
           14 typedef struct Merge Merge;
           15 
           16 struct Undo
           17 {
           18         short        type;                /* Delete, Insert, Filename, Dot, Mark */
           19         short        mod;                /* modify bit */
           20         uint        seq;                /* sequence number */
           21         uint        p0;                /* location of change (unused in f) */
           22         uint        n;                /* # runes in string or file name */
           23 };
           24 
           25 struct Merge
           26 {
           27         File        *f;
           28         uint        seq;                /* of logged change */
           29         uint        p0;                /* location of change (unused in f) */
           30         uint        n;                /* # runes to delete */
           31         uint        nbuf;                /* # runes to insert */
           32         Rune        buf[RBUFSIZE];
           33 };
           34 
           35 enum
           36 {
           37         Maxmerge = 50,
           38         Undosize = sizeof(Undo)/sizeof(Rune)
           39 };
           40 
           41 static Merge        merge;
           42 
           43 File*
           44 fileopen(void)
           45 {
           46         File *f;
           47 
           48         f = emalloc(sizeof(File));
           49         f->dot.f = f;
           50         f->ndot.f = f;
           51         f->seq = 0;
           52         f->mod = FALSE;
           53         f->unread = TRUE;
           54         Strinit0(&f->name);
           55         return f;
           56 }
           57 
           58 int
           59 fileisdirty(File *f)
           60 {
           61         return f->seq != f->cleanseq;
           62 }
           63 
           64 static void
           65 wrinsert(Buffer *delta, int seq, int mod, uint p0, Rune *s, uint ns)
           66 {
           67         Undo u;
           68 
           69         u.type = Insert;
           70         u.mod = mod;
           71         u.seq = seq;
           72         u.p0 = p0;
           73         u.n = ns;
           74         bufinsert(delta, delta->nc, s, ns);
           75         bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
           76 }
           77 
           78 static void
           79 wrdelete(Buffer *delta, int seq, int mod, uint p0, uint p1)
           80 {
           81         Undo u;
           82 
           83         u.type = Delete;
           84         u.mod = mod;
           85         u.seq = seq;
           86         u.p0 = p0;
           87         u.n = p1 - p0;
           88         bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
           89 }
           90 
           91 void
           92 flushmerge(void)
           93 {
           94         File *f;
           95 
           96         f = merge.f;
           97         if(f == nil)
           98                 return;
           99         if(merge.seq != f->seq)
          100                 panic("flushmerge seq mismatch");
          101         if(merge.n != 0)
          102                 wrdelete(&f->epsilon, f->seq, TRUE, merge.p0, merge.p0+merge.n);
          103         if(merge.nbuf != 0)
          104                 wrinsert(&f->epsilon, f->seq, TRUE, merge.p0+merge.n, merge.buf, merge.nbuf);
          105         merge.f = nil;
          106         merge.n = 0;
          107         merge.nbuf = 0;
          108 }
          109 
          110 void
          111 mergeextend(File *f, uint p0)
          112 {
          113         uint mp0n;
          114 
          115         mp0n = merge.p0+merge.n;
          116         if(mp0n != p0){
          117                 bufread(&f->b, mp0n, merge.buf+merge.nbuf, p0-mp0n);
          118                 merge.nbuf += p0-mp0n;
          119                 merge.n = p0-merge.p0;
          120         }
          121 }
          122 
          123 /*
          124  * like fileundelete, but get the data from arguments
          125  */
          126 void
          127 loginsert(File *f, uint p0, Rune *s, uint ns)
          128 {
          129         if(f->rescuing)
          130                 return;
          131         if(ns == 0)
          132                 return;
          133         if(ns<0 || ns>STRSIZE)
          134                 panic("loginsert");
          135         if(f->seq < seq)
          136                 filemark(f);
          137         if(p0 < f->hiposn)
          138                 error(Esequence);
          139 
          140         if(merge.f != f
          141         || p0-(merge.p0+merge.n)>Maxmerge                        /* too far */
          142         || merge.nbuf+((p0+ns)-(merge.p0+merge.n))>=RBUFSIZE)        /* too long */
          143                 flushmerge();
          144 
          145         if(ns>=RBUFSIZE){
          146                 if(!(merge.n == 0 && merge.nbuf == 0 && merge.f == nil))
          147                         panic("loginsert bad merge state");
          148                 wrinsert(&f->epsilon, f->seq, TRUE, p0, s, ns);
          149         }else{
          150                 if(merge.f != f){
          151                         merge.f = f;
          152                         merge.p0 = p0;
          153                         merge.seq = f->seq;
          154                 }
          155                 mergeextend(f, p0);
          156 
          157                 /* append string to merge */
          158                 runemove(merge.buf+merge.nbuf, s, ns);
          159                 merge.nbuf += ns;
          160         }
          161 
          162         f->hiposn = p0;
          163         if(!f->unread && !f->mod)
          164                 state(f, Dirty);
          165 }
          166 
          167 void
          168 logdelete(File *f, uint p0, uint p1)
          169 {
          170         if(f->rescuing)
          171                 return;
          172         if(p0 == p1)
          173                 return;
          174         if(f->seq < seq)
          175                 filemark(f);
          176         if(p0 < f->hiposn)
          177                 error(Esequence);
          178 
          179         if(merge.f != f
          180         || p0-(merge.p0+merge.n)>Maxmerge                        /* too far */
          181         || merge.nbuf+(p0-(merge.p0+merge.n))>=RBUFSIZE){        /* too long */
          182                 flushmerge();
          183                 merge.f = f;
          184                 merge.p0 = p0;
          185                 merge.seq = f->seq;
          186         }
          187 
          188         mergeextend(f, p0);
          189 
          190         /* add to deletion */
          191         merge.n = p1-merge.p0;
          192 
          193         f->hiposn = p1;
          194         if(!f->unread && !f->mod)
          195                 state(f, Dirty);
          196 }
          197 
          198 /*
          199  * like fileunsetname, but get the data from arguments
          200  */
          201 void
          202 logsetname(File *f, String *s)
          203 {
          204         Undo u;
          205         Buffer *delta;
          206 
          207         if(f->rescuing)
          208                 return;
          209 
          210         if(f->unread){        /* This is setting initial file name */
          211                 filesetname(f, s);
          212                 return;
          213         }
          214 
          215         if(f->seq < seq)
          216                 filemark(f);
          217 
          218         /* undo a file name change by restoring old name */
          219         delta = &f->epsilon;
          220         u.type = Filename;
          221         u.mod = TRUE;
          222         u.seq = f->seq;
          223         u.p0 = 0;        /* unused */
          224         u.n = s->n;
          225         if(s->n)
          226                 bufinsert(delta, delta->nc, s->s, s->n);
          227         bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
          228         if(!f->unread && !f->mod)
          229                 state(f, Dirty);
          230 }
          231 
          232 #ifdef NOTEXT
          233 File*
          234 fileaddtext(File *f, Text *t)
          235 {
          236         if(f == nil){
          237                 f = emalloc(sizeof(File));
          238                 f->unread = TRUE;
          239         }
          240         f->text = realloc(f->text, (f->ntext+1)*sizeof(Text*));
          241         f->text[f->ntext++] = t;
          242         f->curtext = t;
          243         return f;
          244 }
          245 
          246 void
          247 filedeltext(File *f, Text *t)
          248 {
          249         int i;
          250 
          251         for(i=0; i<f->ntext; i++)
          252                 if(f->text[i] == t)
          253                         goto Found;
          254         panic("can't find text in filedeltext");
          255 
          256     Found:
          257         f->ntext--;
          258         if(f->ntext == 0){
          259                 fileclose(f);
          260                 return;
          261         }
          262         memmove(f->text+i, f->text+i+1, (f->ntext-i)*sizeof(Text*));
          263         if(f->curtext == t)
          264                 f->curtext = f->text[0];
          265 }
          266 #endif
          267 
          268 void
          269 fileuninsert(File *f, Buffer *delta, uint p0, uint ns)
          270 {
          271         Undo u;
          272 
          273         /* undo an insertion by deleting */
          274         u.type = Delete;
          275         u.mod = f->mod;
          276         u.seq = f->seq;
          277         u.p0 = p0;
          278         u.n = ns;
          279         bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
          280 }
          281 
          282 void
          283 fileundelete(File *f, Buffer *delta, uint p0, uint p1)
          284 {
          285         Undo u;
          286         Rune *buf;
          287         uint i, n;
          288 
          289         /* undo a deletion by inserting */
          290         u.type = Insert;
          291         u.mod = f->mod;
          292         u.seq = f->seq;
          293         u.p0 = p0;
          294         u.n = p1-p0;
          295         buf = fbufalloc();
          296         for(i=p0; i<p1; i+=n){
          297                 n = p1 - i;
          298                 if(n > RBUFSIZE)
          299                         n = RBUFSIZE;
          300                 bufread(&f->b, i, buf, n);
          301                 bufinsert(delta, delta->nc, buf, n);
          302         }
          303         fbuffree(buf);
          304         bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
          305 
          306 }
          307 
          308 int
          309 filereadc(File *f, uint q)
          310 {
          311         Rune r;
          312 
          313         if(q >= f->b.nc)
          314                 return -1;
          315         bufread(&f->b, q, &r, 1);
          316         return r;
          317 }
          318 
          319 void
          320 filesetname(File *f, String *s)
          321 {
          322         if(!f->unread)        /* This is setting initial file name */
          323                 fileunsetname(f, &f->delta);
          324         Strduplstr(&f->name, s);
          325         sortname(f);
          326         f->unread = TRUE;
          327 }
          328 
          329 void
          330 fileunsetname(File *f, Buffer *delta)
          331 {
          332         String s;
          333         Undo u;
          334 
          335         /* undo a file name change by restoring old name */
          336         u.type = Filename;
          337         u.mod = f->mod;
          338         u.seq = f->seq;
          339         u.p0 = 0;        /* unused */
          340         Strinit(&s);
          341         Strduplstr(&s, &f->name);
          342         fullname(&s);
          343         u.n = s.n;
          344         if(s.n)
          345                 bufinsert(delta, delta->nc, s.s, s.n);
          346         bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
          347         Strclose(&s);
          348 }
          349 
          350 void
          351 fileunsetdot(File *f, Buffer *delta, Range dot)
          352 {
          353         Undo u;
          354 
          355         u.type = Dot;
          356         u.mod = f->mod;
          357         u.seq = f->seq;
          358         u.p0 = dot.p1;
          359         u.n = dot.p2 - dot.p1;
          360         bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
          361 }
          362 
          363 void
          364 fileunsetmark(File *f, Buffer *delta, Range mark)
          365 {
          366         Undo u;
          367 
          368         u.type = Mark;
          369         u.mod = f->mod;
          370         u.seq = f->seq;
          371         u.p0 = mark.p1;
          372         u.n = mark.p2 - mark.p1;
          373         bufinsert(delta, delta->nc, (Rune*)&u, Undosize);
          374 }
          375 
          376 uint
          377 fileload(File *f, uint p0, int fd, int *nulls)
          378 {
          379         if(f->seq > 0)
          380                 panic("undo in file.load unimplemented");
          381         return bufload(&f->b, p0, fd, nulls);
          382 }
          383 
          384 int
          385 fileupdate(File *f, int notrans, int toterm)
          386 {
          387         uint p1, p2;
          388         int mod;
          389 
          390         if(f->rescuing)
          391                 return FALSE;
          392 
          393         flushmerge();
          394 
          395         /*
          396          * fix the modification bit
          397          * subtle point: don't save it away in the log.
          398          *
          399          * if another change is made, the correct f->mod
          400          * state is saved  in the undo log by filemark
          401          * when setting the dot and mark.
          402          *
          403          * if the change is undone, the correct state is
          404          * saved from f in the fileun... routines.
          405          */
          406         mod = f->mod;
          407         f->mod = f->prevmod;
          408         if(f == cmd)
          409                 notrans = TRUE;
          410         else{
          411                 fileunsetdot(f, &f->delta, f->prevdot);
          412                 fileunsetmark(f, &f->delta, f->prevmark);
          413         }
          414         f->dot = f->ndot;
          415         fileundo(f, FALSE, !notrans, &p1, &p2, toterm);
          416         f->mod = mod;
          417 
          418         if(f->delta.nc == 0)
          419                 f->seq = 0;
          420 
          421         if(f == cmd)
          422                 return FALSE;
          423 
          424         if(f->mod){
          425                 f->closeok = 0;
          426                 quitok = 0;
          427         }else
          428                 f->closeok = 1;
          429         return TRUE;
          430 }
          431 
          432 long
          433 prevseq(Buffer *b)
          434 {
          435         Undo u;
          436         uint up;
          437 
          438         up = b->nc;
          439         if(up == 0)
          440                 return 0;
          441         up -= Undosize;
          442         bufread(b, up, (Rune*)&u, Undosize);
          443         return u.seq;
          444 }
          445 
          446 long
          447 undoseq(File *f, int isundo)
          448 {
          449         if(isundo)
          450                 return f->seq;
          451 
          452         return prevseq(&f->epsilon);
          453 }
          454 
          455 void
          456 fileundo(File *f, int isundo, int canredo, uint *q0p, uint *q1p, int flag)
          457 {
          458         Undo u;
          459         Rune *buf;
          460         uint i, n, up;
          461         uint stop;
          462         Buffer *delta, *epsilon;
          463 
          464         if(isundo){
          465                 /* undo; reverse delta onto epsilon, seq decreases */
          466                 delta = &f->delta;
          467                 epsilon = &f->epsilon;
          468                 stop = f->seq;
          469         }else{
          470                 /* redo; reverse epsilon onto delta, seq increases */
          471                 delta = &f->epsilon;
          472                 epsilon = &f->delta;
          473                 stop = 0;        /* don't know yet */
          474         }
          475 
          476         raspstart(f);
          477         while(delta->nc > 0){
          478                 /* rasp and buffer are in sync; sync with wire if needed */
          479                 if(needoutflush())
          480                         raspflush(f);
          481                 up = delta->nc-Undosize;
          482                 bufread(delta, up, (Rune*)&u, Undosize);
          483                 if(isundo){
          484                         if(u.seq < stop){
          485                                 f->seq = u.seq;
          486                                 raspdone(f, flag);
          487                                 return;
          488                         }
          489                 }else{
          490                         if(stop == 0)
          491                                 stop = u.seq;
          492                         if(u.seq > stop){
          493                                 raspdone(f, flag);
          494                                 return;
          495                         }
          496                 }
          497                 switch(u.type){
          498                 default:
          499                         panic("undo unknown u.type");
          500                         break;
          501 
          502                 case Delete:
          503                         f->seq = u.seq;
          504                         if(canredo)
          505                                 fileundelete(f, epsilon, u.p0, u.p0+u.n);
          506                         f->mod = u.mod;
          507                         bufdelete(&f->b, u.p0, u.p0+u.n);
          508                         raspdelete(f, u.p0, u.p0+u.n, flag);
          509                         *q0p = u.p0;
          510                         *q1p = u.p0;
          511                         break;
          512 
          513                 case Insert:
          514                         f->seq = u.seq;
          515                         if(canredo)
          516                                 fileuninsert(f, epsilon, u.p0, u.n);
          517                         f->mod = u.mod;
          518                         up -= u.n;
          519                         buf = fbufalloc();
          520                         for(i=0; i<u.n; i+=n){
          521                                 n = u.n - i;
          522                                 if(n > RBUFSIZE)
          523                                         n = RBUFSIZE;
          524                                 bufread(delta, up+i, buf, n);
          525                                 bufinsert(&f->b, u.p0+i, buf, n);
          526                                 raspinsert(f, u.p0+i, buf, n, flag);
          527                         }
          528                         fbuffree(buf);
          529                         *q0p = u.p0;
          530                         *q1p = u.p0+u.n;
          531                         break;
          532 
          533                 case Filename:
          534                         f->seq = u.seq;
          535                         if(canredo)
          536                                 fileunsetname(f, epsilon);
          537                         f->mod = u.mod;
          538                         up -= u.n;
          539 
          540                         Strinsure(&f->name, u.n+1);
          541                         bufread(delta, up, f->name.s, u.n);
          542                         f->name.s[u.n] = 0;
          543                         f->name.n = u.n;
          544                         fixname(&f->name);
          545                         sortname(f);
          546                         break;
          547                 case Dot:
          548                         f->seq = u.seq;
          549                         if(canredo)
          550                                 fileunsetdot(f, epsilon, f->dot.r);
          551                         f->mod = u.mod;
          552                         f->dot.r.p1 = u.p0;
          553                         f->dot.r.p2 = u.p0 + u.n;
          554                         break;
          555                 case Mark:
          556                         f->seq = u.seq;
          557                         if(canredo)
          558                                 fileunsetmark(f, epsilon, f->mark);
          559                         f->mod = u.mod;
          560                         f->mark.p1 = u.p0;
          561                         f->mark.p2 = u.p0 + u.n;
          562                         break;
          563                 }
          564                 bufdelete(delta, up, delta->nc);
          565         }
          566         if(isundo)
          567                 f->seq = 0;
          568         raspdone(f, flag);
          569 }
          570 
          571 void
          572 filereset(File *f)
          573 {
          574         bufreset(&f->delta);
          575         bufreset(&f->epsilon);
          576         f->seq = 0;
          577 }
          578 
          579 void
          580 fileclose(File *f)
          581 {
          582         Strclose(&f->name);
          583         bufclose(&f->b);
          584         bufclose(&f->delta);
          585         bufclose(&f->epsilon);
          586         if(f->rasp)
          587                 listfree(f->rasp);
          588         free(f);
          589 }
          590 
          591 void
          592 filemark(File *f)
          593 {
          594 
          595         if(f->unread)
          596                 return;
          597         if(f->epsilon.nc)
          598                 bufdelete(&f->epsilon, 0, f->epsilon.nc);
          599 
          600         if(f != cmd){
          601                 f->prevdot = f->dot.r;
          602                 f->prevmark = f->mark;
          603                 f->prevseq = f->seq;
          604                 f->prevmod = f->mod;
          605         }
          606 
          607         f->ndot = f->dot;
          608         f->seq = seq;
          609         f->hiposn = 0;
          610 }