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