sam.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
       ---
       sam.c (12567B)
       ---
            1 #include "sam.h"
            2 
            3 Rune        genbuf[BLOCKSIZE];
            4 int        io;
            5 int        panicking;
            6 int        rescuing;
            7 String        genstr;
            8 String        rhs;
            9 String        curwd;
           10 String        cmdstr;
           11 Rune        empty[] = { 0 };
           12 char        *genc;
           13 File        *curfile;
           14 File        *flist;
           15 File        *cmd;
           16 jmp_buf        mainloop;
           17 List        tempfile = { 'p' };
           18 int        quitok = TRUE;
           19 int        downloaded;
           20 int        dflag;
           21 int        Rflag;
           22 char        *machine;
           23 char        *home;
           24 int        bpipeok;
           25 int        termlocked;
           26 char        *samterm = SAMTERM;
           27 char        *rsamname = RSAM;
           28 File        *lastfile;
           29 Disk        *disk;
           30 long        seq;
           31 
           32 char *winsize;
           33 
           34 Rune        baddir[] = { '<', 'b', 'a', 'd', 'd', 'i', 'r', '>', '\n'};
           35 
           36 void        usage(void);
           37 
           38 extern int notify(void(*)(void*,char*));
           39 
           40 void
           41 main(int _argc, char **_argv)
           42 {
           43         volatile int i, argc;
           44         char **volatile argv;
           45         String *t;
           46         char *termargs[10], **ap;
           47         
           48         argc = _argc;
           49         argv = _argv;
           50         ap = termargs;
           51         *ap++ = "samterm";
           52         ARGBEGIN{
           53         case 'd':
           54                 dflag++;
           55                 break;
           56         case 'r':
           57                 machine = EARGF(usage());
           58                 break;
           59         case 'R':
           60                 Rflag++;
           61                 break;
           62         case 't':
           63                 samterm = EARGF(usage());
           64                 break;
           65         case 's':
           66                 rsamname = EARGF(usage());
           67                 break;
           68         default:
           69                 dprint("sam: unknown flag %c\n", ARGC());
           70                 usage();
           71         /* options for samterm */
           72         case 'a':
           73                 *ap++ = "-a";
           74                 break;
           75         case 'W':
           76                 *ap++ = "-W";
           77                 *ap++ = EARGF(usage());
           78                 break;
           79         }ARGEND
           80         *ap = nil;
           81 
           82         Strinit(&cmdstr);
           83         Strinit0(&lastpat);
           84         Strinit0(&lastregexp);
           85         Strinit0(&genstr);
           86         Strinit0(&rhs);
           87         Strinit0(&curwd);
           88         Strinit0(&plan9cmd);
           89         home = getenv(HOME);
           90         disk = diskinit();
           91         if(home == 0)
           92                 home = "/";
           93         if(!dflag)
           94                 startup(machine, Rflag, termargs, (char**)argv);
           95         notify(notifyf);
           96         getcurwd();
           97         if(argc>0){
           98                 for(i=0; i<argc; i++){
           99                         if(!setjmp(mainloop)){
          100                                 t = tmpcstr(argv[i]);
          101                                 Straddc(t, '\0');
          102                                 Strduplstr(&genstr, t);
          103                                 freetmpstr(t);
          104                                 fixname(&genstr);
          105                                 logsetname(newfile(), &genstr);
          106                         }
          107                 }
          108         }else if(!downloaded)
          109                 newfile();
          110         seq++;
          111         if(file.nused)
          112                 current(file.filepptr[0]);
          113         setjmp(mainloop);
          114         cmdloop();
          115         trytoquit();        /* if we already q'ed, quitok will be TRUE */
          116         exits(0);
          117 }
          118 
          119 void
          120 usage(void)
          121 {
          122         dprint("usage: sam [-d] [-t samterm] [-s sam name] [-r machine] [file ...]\n");
          123         exits("usage");
          124 }
          125 
          126 void
          127 rescue(void)
          128 {
          129         int i, nblank = 0;
          130         File *f;
          131         char *c;
          132         char buf[256];
          133         char *root;
          134 
          135         if(rescuing++)
          136                 return;
          137         io = -1;
          138         for(i=0; i<file.nused; i++){
          139                 f = file.filepptr[i];
          140                 if(f==cmd || f->b.nc==0 || !fileisdirty(f))
          141                         continue;
          142                 if(io == -1){
          143                         sprint(buf, "%s/sam.save", home);
          144                         io = create(buf, 1, 0777);
          145                         if(io<0)
          146                                 return;
          147                 }
          148                 if(f->name.s[0]){
          149                         c = Strtoc(&f->name);
          150                         strncpy(buf, c, sizeof buf-1);
          151                         buf[sizeof buf-1] = 0;
          152                         free(c);
          153                 }else
          154                         sprint(buf, "nameless.%d", nblank++);
          155                 root = getenv("PLAN9");
          156                 if(root == nil)
          157                         root = "/usr/local/plan9";
          158                 fprint(io, "#!/bin/sh\n%s/bin/samsave '%s' $* <<'---%s'\n", root, buf, buf);
          159                 addr.r.p1 = 0, addr.r.p2 = f->b.nc;
          160                 writeio(f);
          161                 fprint(io, "\n---%s\n", (char *)buf);
          162         }
          163 }
          164 
          165 void
          166 panic(char *s)
          167 {
          168         int wasd;
          169 
          170         if(!panicking++ && !setjmp(mainloop)){
          171                 wasd = downloaded;
          172                 downloaded = 0;
          173                 dprint("sam: panic: %s: %r\n", s);
          174                 if(wasd)
          175                         fprint(2, "sam: panic: %s: %r\n", s);
          176                 rescue();
          177                 abort();
          178         }
          179 }
          180 
          181 void
          182 hiccough(char *s)
          183 {
          184         File *f;
          185         int i;
          186 
          187         if(rescuing)
          188                 exits("rescue");
          189         if(s)
          190                 dprint("%s\n", s);
          191         resetcmd();
          192         resetxec();
          193         resetsys();
          194         if(io > 0)
          195                 close(io);
          196 
          197         /*
          198          * back out any logged changes & restore old sequences
          199          */
          200         for(i=0; i<file.nused; i++){
          201                 f = file.filepptr[i];
          202                 if(f==cmd)
          203                         continue;
          204                 if(f->seq==seq){
          205                         bufdelete(&f->epsilon, 0, f->epsilon.nc);
          206                         f->seq = f->prevseq;
          207                         f->dot.r = f->prevdot;
          208                         f->mark = f->prevmark;
          209                         state(f, f->prevmod ? Dirty: Clean);
          210                 }
          211         }
          212 
          213         update();
          214         if (curfile) {
          215                 if (curfile->unread)
          216                         curfile->unread = FALSE;
          217                 else if (downloaded)
          218                         outTs(Hcurrent, curfile->tag);
          219         }
          220         longjmp(mainloop, 1);
          221 }
          222 
          223 void
          224 intr(void)
          225 {
          226         error(Eintr);
          227 }
          228 
          229 void
          230 trytoclose(File *f)
          231 {
          232         char *t;
          233         char buf[256];
          234 
          235         if(f == cmd)        /* possible? */
          236                 return;
          237         if(f->deleted)
          238                 return;
          239         if(fileisdirty(f) && !f->closeok){
          240                 f->closeok = TRUE;
          241                 if(f->name.s[0]){
          242                         t = Strtoc(&f->name);
          243                         strncpy(buf, t, sizeof buf-1);
          244                         free(t);
          245                 }else
          246                         strcpy(buf, "nameless file");
          247                 error_s(Emodified, buf);
          248         }
          249         f->deleted = TRUE;
          250 }
          251 
          252 void
          253 trytoquit(void)
          254 {
          255         int c;
          256         File *f;
          257 
          258         if(!quitok){
          259                 for(c = 0; c<file.nused; c++){
          260                         f = file.filepptr[c];
          261                         if(f!=cmd && fileisdirty(f)){
          262                                 quitok = TRUE;
          263                                 eof = FALSE;
          264                                 error(Echanges);
          265                         }
          266                 }
          267         }
          268 }
          269 
          270 void
          271 load(File *f)
          272 {
          273         Address saveaddr;
          274 
          275         Strduplstr(&genstr, &f->name);
          276         filename(f);
          277         if(f->name.s[0]){
          278                 saveaddr = addr;
          279                 edit(f, 'I');
          280                 addr = saveaddr;
          281         }else{
          282                 f->unread = 0;
          283                 f->cleanseq = f->seq;
          284         }
          285 
          286         fileupdate(f, TRUE, TRUE);
          287 }
          288 
          289 void
          290 cmdupdate(void)
          291 {
          292         if(cmd && cmd->seq!=0){
          293                 fileupdate(cmd, FALSE, downloaded);
          294                 cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->b.nc;
          295                 telldot(cmd);
          296         }
          297 }
          298 
          299 void
          300 delete(File *f)
          301 {
          302         if(downloaded && f->rasp)
          303                 outTs(Hclose, f->tag);
          304         delfile(f);
          305         if(f == curfile)
          306                 current(0);
          307 }
          308 
          309 void
          310 update(void)
          311 {
          312         int i, anymod;
          313         File *f;
          314 
          315         settempfile();
          316         for(anymod = i=0; i<tempfile.nused; i++){
          317                 f = tempfile.filepptr[i];
          318                 if(f==cmd)        /* cmd gets done in main() */
          319                         continue;
          320                 if(f->deleted) {
          321                         delete(f);
          322                         continue;
          323                 }
          324                 if(f->seq==seq && fileupdate(f, FALSE, downloaded))
          325                         anymod++;
          326                 if(f->rasp)
          327                         telldot(f);
          328         }
          329         if(anymod)
          330                 seq++;
          331 }
          332 
          333 File *
          334 current(File *f)
          335 {
          336         return curfile = f;
          337 }
          338 
          339 void
          340 edit(File *f, int cmd)
          341 {
          342         int empty = TRUE;
          343         Posn p;
          344         int nulls;
          345 
          346         if(cmd == 'r')
          347                 logdelete(f, addr.r.p1, addr.r.p2);
          348         if(cmd=='e' || cmd=='I'){
          349                 logdelete(f, (Posn)0, f->b.nc);
          350                 addr.r.p2 = f->b.nc;
          351         }else if(f->b.nc!=0 || (f->name.s[0] && Strcmp(&genstr, &f->name)!=0))
          352                 empty = FALSE;
          353         if((io = open(genc, OREAD))<0) {
          354                 if (curfile && curfile->unread)
          355                         curfile->unread = FALSE;
          356                 error_r(Eopen, genc);
          357         }
          358         p = readio(f, &nulls, empty, TRUE);
          359         closeio((cmd=='e' || cmd=='I')? -1 : p);
          360         if(cmd == 'r')
          361                 f->ndot.r.p1 = addr.r.p2, f->ndot.r.p2 = addr.r.p2+p;
          362         else
          363                 f->ndot.r.p1 = f->ndot.r.p2 = 0;
          364         f->closeok = empty;
          365         if (quitok)
          366                 quitok = empty;
          367         else
          368                 quitok = FALSE;
          369         state(f, empty && !nulls? Clean : Dirty);
          370         if(empty && !nulls)
          371                 f->cleanseq = f->seq;
          372         if(cmd == 'e')
          373                 filename(f);
          374 }
          375 
          376 int
          377 getname(File *f, String *s, int save)
          378 {
          379         int c, i;
          380 
          381         Strzero(&genstr);
          382         if(genc){
          383                 free(genc);
          384                 genc = 0;
          385         }
          386         if(s==0 || (c = s->s[0])==0){                /* no name provided */
          387                 if(f)
          388                         Strduplstr(&genstr, &f->name);
          389                 goto Return;
          390         }
          391         if(c!=' ' && c!='\t')
          392                 error(Eblank);
          393         for(i=0; (c=s->s[i])==' ' || c=='\t'; i++)
          394                 ;
          395         while(s->s[i] > ' ')
          396                 Straddc(&genstr, s->s[i++]);
          397         if(s->s[i])
          398                 error(Enewline);
          399         fixname(&genstr);
          400         if(f && (save || f->name.s[0]==0)){
          401                 logsetname(f, &genstr);
          402                 if(Strcmp(&f->name, &genstr)){
          403                         quitok = f->closeok = FALSE;
          404                         f->qidpath = 0;
          405                         f->mtime = 0;
          406                         state(f, Dirty); /* if it's 'e', fix later */
          407                 }
          408         }
          409     Return:
          410         genc = Strtoc(&genstr);
          411         i = genstr.n;
          412         if(i && genstr.s[i-1]==0)
          413                 i--;
          414         return i;        /* strlen(name) */
          415 }
          416 
          417 void
          418 filename(File *f)
          419 {
          420         if(genc)
          421                 free(genc);
          422         genc = Strtoc(&genstr);
          423         dprint("%c%c%c %s\n", " '"[f->mod],
          424                 "-+"[f->rasp!=0], " ."[f==curfile], genc);
          425 }
          426 
          427 void
          428 undostep(File *f, int isundo)
          429 {
          430         uint p1, p2;
          431         int mod;
          432 
          433         mod = f->mod;
          434         fileundo(f, isundo, 1, &p1, &p2, TRUE);
          435         f->ndot = f->dot;
          436         if(f->mod){
          437                 f->closeok = 0;
          438                 quitok = 0;
          439         }else
          440                 f->closeok = 1;
          441 
          442         if(f->mod != mod){
          443                 f->mod = mod;
          444                 if(mod)
          445                         mod = Clean;
          446                 else
          447                         mod = Dirty;
          448                 state(f, mod);
          449         }
          450 }
          451 
          452 int
          453 undo(int isundo)
          454 {
          455         File *f;
          456         int i;
          457         Mod max;
          458 
          459         max = undoseq(curfile, isundo);
          460         if(max == 0)
          461                 return 0;
          462         settempfile();
          463         for(i = 0; i<tempfile.nused; i++){
          464                 f = tempfile.filepptr[i];
          465                 if(f!=cmd && undoseq(f, isundo)==max)
          466                         undostep(f, isundo);
          467         }
          468         return 1;
          469 }
          470 
          471 int
          472 readcmd(String *s)
          473 {
          474         int retcode;
          475 
          476         if(flist != 0)
          477                 fileclose(flist);
          478         flist = fileopen();
          479 
          480         addr.r.p1 = 0, addr.r.p2 = flist->b.nc;
          481         retcode = plan9(flist, '<', s, FALSE);
          482         fileupdate(flist, FALSE, FALSE);
          483         flist->seq = 0;
          484         if (flist->b.nc > BLOCKSIZE)
          485                 error(Etoolong);
          486         Strzero(&genstr);
          487         Strinsure(&genstr, flist->b.nc);
          488         bufread(&flist->b, (Posn)0, genbuf, flist->b.nc);
          489         memmove(genstr.s, genbuf, flist->b.nc*RUNESIZE);
          490         genstr.n = flist->b.nc;
          491         Straddc(&genstr, '\0');
          492         return retcode;
          493 }
          494 
          495 void
          496 getcurwd(void)
          497 {
          498         String *t;
          499         char buf[256];
          500 
          501         buf[0] = 0;
          502         getwd(buf, sizeof(buf));
          503         t = tmpcstr(buf);
          504         Strduplstr(&curwd, t);
          505         freetmpstr(t);
          506         if(curwd.n == 0)
          507                 warn(Wpwd);
          508         else if(curwd.s[curwd.n-1] != '/')
          509                 Straddc(&curwd, '/');
          510 }
          511 
          512 void
          513 cd(String *str)
          514 {
          515         int i, fd;
          516         char *s;
          517         File *f;
          518         String owd;
          519 
          520         getcurwd();
          521         if(getname((File *)0, str, FALSE))
          522                 s = genc;
          523         else
          524                 s = home;
          525         if(chdir(s))
          526                 syserror("chdir");
          527         fd = open("/dev/wdir", OWRITE);
          528         if(fd > 0)
          529                 write(fd, s, strlen(s));
          530         dprint("!\n");
          531         Strinit(&owd);
          532         Strduplstr(&owd, &curwd);
          533         getcurwd();
          534         settempfile();
          535         /*
          536          * Two passes so that if we have open
          537          * /a/foo.c and /b/foo.c and cd from /b to /a,
          538          * we don't ever have two foo.c simultaneously.
          539          */
          540         for(i=0; i<tempfile.nused; i++){
          541                 f = tempfile.filepptr[i];
          542                 if(f!=cmd && f->name.s[0]!='/' && f->name.s[0]!=0){
          543                         Strinsert(&f->name, &owd, (Posn)0);
          544                         fixname(&f->name);
          545                         sortname(f);
          546                 }
          547         }
          548         for(i=0; i<tempfile.nused; i++){
          549                 f = tempfile.filepptr[i];
          550                 if(f != cmd && Strispre(&curwd, &f->name)){
          551                         fixname(&f->name);
          552                         sortname(f);
          553                 }
          554         }
          555         Strclose(&owd);
          556 }
          557 
          558 int
          559 loadflist(String *s)
          560 {
          561         int c, i;
          562 
          563         c = s->s[0];
          564         for(i = 0; s->s[i]==' ' || s->s[i]=='\t'; i++)
          565                 ;
          566         if((c==' ' || c=='\t') && s->s[i]!='\n'){
          567                 if(s->s[i]=='<'){
          568                         Strdelete(s, 0L, (long)i+1);
          569                         readcmd(s);
          570                 }else{
          571                         Strzero(&genstr);
          572                         while((c = s->s[i++]) && c!='\n')
          573                                 Straddc(&genstr, c);
          574                         Straddc(&genstr, '\0');
          575                 }
          576         }else{
          577                 if(c != '\n')
          578                         error(Eblank);
          579                 Strdupl(&genstr, empty);
          580         }
          581         if(genc)
          582                 free(genc);
          583         genc = Strtoc(&genstr);
          584         return genstr.s[0];
          585 }
          586 
          587 File *
          588 readflist(int readall, int delete)
          589 {
          590         Posn i;
          591         int c;
          592         File *f;
          593         String t;
          594 
          595         Strinit(&t);
          596         for(i=0,f=0; f==0 || readall || delete; i++){        /* ++ skips blank */
          597                 Strdelete(&genstr, (Posn)0, i);
          598                 for(i=0; (c = genstr.s[i])==' ' || c=='\t' || c=='\n'; i++)
          599                         ;
          600                 if(i >= genstr.n)
          601                         break;
          602                 Strdelete(&genstr, (Posn)0, i);
          603                 for(i=0; (c=genstr.s[i]) && c!=' ' && c!='\t' && c!='\n'; i++)
          604                         ;
          605 
          606                 if(i == 0)
          607                         break;
          608                 genstr.s[i] = 0;
          609                 Strduplstr(&t, tmprstr(genstr.s, i+1));
          610                 fixname(&t);
          611                 f = lookfile(&t);
          612                 if(delete){
          613                         if(f == 0)
          614                                 warn_S(Wfile, &t);
          615                         else
          616                                 trytoclose(f);
          617                 }else if(f==0 && readall)
          618                         logsetname(f = newfile(), &t);
          619         }
          620         Strclose(&t);
          621         return f;
          622 }
          623 
          624 File *
          625 tofile(String *s)
          626 {
          627         File *f;
          628 
          629         if(s->s[0] != ' ')
          630                 error(Eblank);
          631         if(loadflist(s) == 0){
          632                 f = lookfile(&genstr);        /* empty string ==> nameless file */
          633                 if(f == 0)
          634                         error_s(Emenu, genc);
          635         }else if((f=readflist(FALSE, FALSE)) == 0)
          636                 error_s(Emenu, genc);
          637         return current(f);
          638 }
          639 
          640 File *
          641 getfile(String *s)
          642 {
          643         File *f;
          644 
          645         if(loadflist(s) == 0)
          646                 logsetname(f = newfile(), &genstr);
          647         else if((f=readflist(TRUE, FALSE)) == 0)
          648                 error(Eblank);
          649         return current(f);
          650 }
          651 
          652 void
          653 closefiles(File *f, String *s)
          654 {
          655         if(s->s[0] == 0){
          656                 if(f == 0)
          657                         error(Enofile);
          658                 trytoclose(f);
          659                 return;
          660         }
          661         if(s->s[0] != ' ')
          662                 error(Eblank);
          663         if(loadflist(s) == 0)
          664                 error(Enewline);
          665         readflist(FALSE, TRUE);
          666 }
          667 
          668 void
          669 copy(File *f, Address addr2)
          670 {
          671         Posn p;
          672         int ni;
          673         for(p=addr.r.p1; p<addr.r.p2; p+=ni){
          674                 ni = addr.r.p2-p;
          675                 if(ni > BLOCKSIZE)
          676                         ni = BLOCKSIZE;
          677                 bufread(&f->b, p, genbuf, ni);
          678                 loginsert(addr2.f, addr2.r.p2, tmprstr(genbuf, ni)->s, ni);
          679         }
          680         addr2.f->ndot.r.p2 = addr2.r.p2+(f->dot.r.p2-f->dot.r.p1);
          681         addr2.f->ndot.r.p1 = addr2.r.p2;
          682 }
          683 
          684 void
          685 move(File *f, Address addr2)
          686 {
          687         if(addr.r.p2 <= addr2.r.p2){
          688                 logdelete(f, addr.r.p1, addr.r.p2);
          689                 copy(f, addr2);
          690         }else if(addr.r.p1 >= addr2.r.p2){
          691                 copy(f, addr2);
          692                 logdelete(f, addr.r.p1, addr.r.p2);
          693         }else
          694                 error(Eoverlap);
          695 }
          696 
          697 Posn
          698 nlcount(File *f, Posn p0, Posn p1)
          699 {
          700         Posn nl = 0;
          701 
          702         while(p0 < p1)
          703                 if(filereadc(f, p0++)=='\n')
          704                         nl++;
          705         return nl;
          706 }
          707 
          708 void
          709 printposn(File *f, int charsonly)
          710 {
          711         Posn l1, l2;
          712 
          713         if(!charsonly){
          714                 l1 = 1+nlcount(f, (Posn)0, addr.r.p1);
          715                 l2 = l1+nlcount(f, addr.r.p1, addr.r.p2);
          716                 /* check if addr ends with '\n' */
          717                 if(addr.r.p2>0 && addr.r.p2>addr.r.p1 && filereadc(f, addr.r.p2-1)=='\n')
          718                         --l2;
          719                 dprint("%lud", l1);
          720                 if(l2 != l1)
          721                         dprint(",%lud", l2);
          722                 dprint("; ");
          723         }
          724         dprint("#%lud", addr.r.p1);
          725         if(addr.r.p2 != addr.r.p1)
          726                 dprint(",#%lud", addr.r.p2);
          727         dprint("\n");
          728 }
          729 
          730 void
          731 settempfile(void)
          732 {
          733         if(tempfile.nalloc < file.nused){
          734                 if(tempfile.filepptr)
          735                         free(tempfile.filepptr);
          736                 tempfile.filepptr = emalloc(sizeof(File*)*file.nused);
          737                 tempfile.nalloc = file.nused;
          738         }
          739         memmove(tempfile.filepptr, file.filepptr, sizeof(File*)*file.nused);
          740         tempfile.nused = file.nused;
          741 }