sam.c (12513B)
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 = get9root(); 156 fprint(io, "#!/bin/sh\n%s/bin/samsave '%s' $* <<'---%s'\n", root, buf, buf); 157 addr.r.p1 = 0, addr.r.p2 = f->b.nc; 158 writeio(f); 159 fprint(io, "\n---%s\n", (char *)buf); 160 } 161 } 162 163 void 164 panic(char *s) 165 { 166 int wasd; 167 168 if(!panicking++ && !setjmp(mainloop)){ 169 wasd = downloaded; 170 downloaded = 0; 171 dprint("sam: panic: %s: %r\n", s); 172 if(wasd) 173 fprint(2, "sam: panic: %s: %r\n", s); 174 rescue(); 175 abort(); 176 } 177 } 178 179 void 180 hiccough(char *s) 181 { 182 File *f; 183 int i; 184 185 if(rescuing) 186 exits("rescue"); 187 if(s) 188 dprint("%s\n", s); 189 resetcmd(); 190 resetxec(); 191 resetsys(); 192 if(io > 0) 193 close(io); 194 195 /* 196 * back out any logged changes & restore old sequences 197 */ 198 for(i=0; i<file.nused; i++){ 199 f = file.filepptr[i]; 200 if(f==cmd) 201 continue; 202 if(f->seq==seq){ 203 bufdelete(&f->epsilon, 0, f->epsilon.nc); 204 f->seq = f->prevseq; 205 f->dot.r = f->prevdot; 206 f->mark = f->prevmark; 207 state(f, f->prevmod ? Dirty: Clean); 208 } 209 } 210 211 update(); 212 if (curfile) { 213 if (curfile->unread) 214 curfile->unread = FALSE; 215 else if (downloaded) 216 outTs(Hcurrent, curfile->tag); 217 } 218 longjmp(mainloop, 1); 219 } 220 221 void 222 intr(void) 223 { 224 error(Eintr); 225 } 226 227 void 228 trytoclose(File *f) 229 { 230 char *t; 231 char buf[256]; 232 233 if(f == cmd) /* possible? */ 234 return; 235 if(f->deleted) 236 return; 237 if(fileisdirty(f) && !f->closeok){ 238 f->closeok = TRUE; 239 if(f->name.s[0]){ 240 t = Strtoc(&f->name); 241 strncpy(buf, t, sizeof buf-1); 242 free(t); 243 }else 244 strcpy(buf, "nameless file"); 245 error_s(Emodified, buf); 246 } 247 f->deleted = TRUE; 248 } 249 250 void 251 trytoquit(void) 252 { 253 int c; 254 File *f; 255 256 if(!quitok){ 257 for(c = 0; c<file.nused; c++){ 258 f = file.filepptr[c]; 259 if(f!=cmd && fileisdirty(f)){ 260 quitok = TRUE; 261 eof = FALSE; 262 error(Echanges); 263 } 264 } 265 } 266 } 267 268 void 269 load(File *f) 270 { 271 Address saveaddr; 272 273 Strduplstr(&genstr, &f->name); 274 filename(f); 275 if(f->name.s[0]){ 276 saveaddr = addr; 277 edit(f, 'I'); 278 addr = saveaddr; 279 }else{ 280 f->unread = 0; 281 f->cleanseq = f->seq; 282 } 283 284 fileupdate(f, TRUE, TRUE); 285 } 286 287 void 288 cmdupdate(void) 289 { 290 if(cmd && cmd->seq!=0){ 291 fileupdate(cmd, FALSE, downloaded); 292 cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->b.nc; 293 telldot(cmd); 294 } 295 } 296 297 void 298 delete(File *f) 299 { 300 if(downloaded && f->rasp) 301 outTs(Hclose, f->tag); 302 delfile(f); 303 if(f == curfile) 304 current(0); 305 } 306 307 void 308 update(void) 309 { 310 int i, anymod; 311 File *f; 312 313 settempfile(); 314 for(anymod = i=0; i<tempfile.nused; i++){ 315 f = tempfile.filepptr[i]; 316 if(f==cmd) /* cmd gets done in main() */ 317 continue; 318 if(f->deleted) { 319 delete(f); 320 continue; 321 } 322 if(f->seq==seq && fileupdate(f, FALSE, downloaded)) 323 anymod++; 324 if(f->rasp) 325 telldot(f); 326 } 327 if(anymod) 328 seq++; 329 } 330 331 File * 332 current(File *f) 333 { 334 return curfile = f; 335 } 336 337 void 338 edit(File *f, int cmd) 339 { 340 int empty = TRUE; 341 Posn p; 342 int nulls; 343 344 if(cmd == 'r') 345 logdelete(f, addr.r.p1, addr.r.p2); 346 if(cmd=='e' || cmd=='I'){ 347 logdelete(f, (Posn)0, f->b.nc); 348 addr.r.p2 = f->b.nc; 349 }else if(f->b.nc!=0 || (f->name.s[0] && Strcmp(&genstr, &f->name)!=0)) 350 empty = FALSE; 351 if((io = open(genc, OREAD))<0) { 352 if (curfile && curfile->unread) 353 curfile->unread = FALSE; 354 error_r(Eopen, genc); 355 } 356 p = readio(f, &nulls, empty, TRUE); 357 closeio((cmd=='e' || cmd=='I')? -1 : p); 358 if(cmd == 'r') 359 f->ndot.r.p1 = addr.r.p2, f->ndot.r.p2 = addr.r.p2+p; 360 else 361 f->ndot.r.p1 = f->ndot.r.p2 = 0; 362 f->closeok = empty; 363 if (quitok) 364 quitok = empty; 365 else 366 quitok = FALSE; 367 state(f, empty && !nulls? Clean : Dirty); 368 if(empty && !nulls) 369 f->cleanseq = f->seq; 370 if(cmd == 'e') 371 filename(f); 372 } 373 374 int 375 getname(File *f, String *s, int save) 376 { 377 int c, i; 378 379 Strzero(&genstr); 380 if(genc){ 381 free(genc); 382 genc = 0; 383 } 384 if(s==0 || (c = s->s[0])==0){ /* no name provided */ 385 if(f) 386 Strduplstr(&genstr, &f->name); 387 goto Return; 388 } 389 if(c!=' ' && c!='\t') 390 error(Eblank); 391 for(i=0; (c=s->s[i])==' ' || c=='\t'; i++) 392 ; 393 while(s->s[i] > ' ') 394 Straddc(&genstr, s->s[i++]); 395 if(s->s[i]) 396 error(Enewline); 397 fixname(&genstr); 398 if(f && (save || f->name.s[0]==0)){ 399 logsetname(f, &genstr); 400 if(Strcmp(&f->name, &genstr)){ 401 quitok = f->closeok = FALSE; 402 f->qidpath = 0; 403 f->mtime = 0; 404 state(f, Dirty); /* if it's 'e', fix later */ 405 } 406 } 407 Return: 408 genc = Strtoc(&genstr); 409 i = genstr.n; 410 if(i && genstr.s[i-1]==0) 411 i--; 412 return i; /* strlen(name) */ 413 } 414 415 void 416 filename(File *f) 417 { 418 if(genc) 419 free(genc); 420 genc = Strtoc(&genstr); 421 dprint("%c%c%c %s\n", " '"[f->mod], 422 "-+"[f->rasp!=0], " ."[f==curfile], genc); 423 } 424 425 void 426 undostep(File *f, int isundo) 427 { 428 uint p1, p2; 429 int mod; 430 431 mod = f->mod; 432 fileundo(f, isundo, 1, &p1, &p2, TRUE); 433 f->ndot = f->dot; 434 if(f->mod){ 435 f->closeok = 0; 436 quitok = 0; 437 }else 438 f->closeok = 1; 439 440 if(f->mod != mod){ 441 f->mod = mod; 442 if(mod) 443 mod = Clean; 444 else 445 mod = Dirty; 446 state(f, mod); 447 } 448 } 449 450 int 451 undo(int isundo) 452 { 453 File *f; 454 int i; 455 Mod max; 456 457 max = undoseq(curfile, isundo); 458 if(max == 0) 459 return 0; 460 settempfile(); 461 for(i = 0; i<tempfile.nused; i++){ 462 f = tempfile.filepptr[i]; 463 if(f!=cmd && undoseq(f, isundo)==max) 464 undostep(f, isundo); 465 } 466 return 1; 467 } 468 469 int 470 readcmd(String *s) 471 { 472 int retcode; 473 474 if(flist != 0) 475 fileclose(flist); 476 flist = fileopen(); 477 478 addr.r.p1 = 0, addr.r.p2 = flist->b.nc; 479 retcode = plan9(flist, '<', s, FALSE); 480 fileupdate(flist, FALSE, FALSE); 481 flist->seq = 0; 482 if (flist->b.nc > BLOCKSIZE) 483 error(Etoolong); 484 Strzero(&genstr); 485 Strinsure(&genstr, flist->b.nc); 486 bufread(&flist->b, (Posn)0, genbuf, flist->b.nc); 487 memmove(genstr.s, genbuf, flist->b.nc*RUNESIZE); 488 genstr.n = flist->b.nc; 489 Straddc(&genstr, '\0'); 490 return retcode; 491 } 492 493 void 494 getcurwd(void) 495 { 496 String *t; 497 char buf[256]; 498 499 buf[0] = 0; 500 getwd(buf, sizeof(buf)); 501 t = tmpcstr(buf); 502 Strduplstr(&curwd, t); 503 freetmpstr(t); 504 if(curwd.n == 0) 505 warn(Wpwd); 506 else if(curwd.s[curwd.n-1] != '/') 507 Straddc(&curwd, '/'); 508 } 509 510 void 511 cd(String *str) 512 { 513 int i, fd; 514 char *s; 515 File *f; 516 String owd; 517 518 getcurwd(); 519 if(getname((File *)0, str, FALSE)) 520 s = genc; 521 else 522 s = home; 523 if(chdir(s)) 524 syserror("chdir"); 525 fd = open("/dev/wdir", OWRITE); 526 if(fd > 0) 527 write(fd, s, strlen(s)); 528 dprint("!\n"); 529 Strinit(&owd); 530 Strduplstr(&owd, &curwd); 531 getcurwd(); 532 settempfile(); 533 /* 534 * Two passes so that if we have open 535 * /a/foo.c and /b/foo.c and cd from /b to /a, 536 * we don't ever have two foo.c simultaneously. 537 */ 538 for(i=0; i<tempfile.nused; i++){ 539 f = tempfile.filepptr[i]; 540 if(f!=cmd && f->name.s[0]!='/' && f->name.s[0]!=0){ 541 Strinsert(&f->name, &owd, (Posn)0); 542 fixname(&f->name); 543 sortname(f); 544 } 545 } 546 for(i=0; i<tempfile.nused; i++){ 547 f = tempfile.filepptr[i]; 548 if(f != cmd && Strispre(&curwd, &f->name)){ 549 fixname(&f->name); 550 sortname(f); 551 } 552 } 553 Strclose(&owd); 554 } 555 556 int 557 loadflist(String *s) 558 { 559 int c, i; 560 561 c = s->s[0]; 562 for(i = 0; s->s[i]==' ' || s->s[i]=='\t'; i++) 563 ; 564 if((c==' ' || c=='\t') && s->s[i]!='\n'){ 565 if(s->s[i]=='<'){ 566 Strdelete(s, 0L, (long)i+1); 567 readcmd(s); 568 }else{ 569 Strzero(&genstr); 570 while((c = s->s[i++]) && c!='\n') 571 Straddc(&genstr, c); 572 Straddc(&genstr, '\0'); 573 } 574 }else{ 575 if(c != '\n') 576 error(Eblank); 577 Strdupl(&genstr, empty); 578 } 579 if(genc) 580 free(genc); 581 genc = Strtoc(&genstr); 582 return genstr.s[0]; 583 } 584 585 File * 586 readflist(int readall, int delete) 587 { 588 Posn i; 589 int c; 590 File *f; 591 String t; 592 593 Strinit(&t); 594 for(i=0,f=0; f==0 || readall || delete; i++){ /* ++ skips blank */ 595 Strdelete(&genstr, (Posn)0, i); 596 for(i=0; (c = genstr.s[i])==' ' || c=='\t' || c=='\n'; i++) 597 ; 598 if(i >= genstr.n) 599 break; 600 Strdelete(&genstr, (Posn)0, i); 601 for(i=0; (c=genstr.s[i]) && c!=' ' && c!='\t' && c!='\n'; i++) 602 ; 603 604 if(i == 0) 605 break; 606 genstr.s[i] = 0; 607 Strduplstr(&t, tmprstr(genstr.s, i+1)); 608 fixname(&t); 609 f = lookfile(&t); 610 if(delete){ 611 if(f == 0) 612 warn_S(Wfile, &t); 613 else 614 trytoclose(f); 615 }else if(f==0 && readall) 616 logsetname(f = newfile(), &t); 617 } 618 Strclose(&t); 619 return f; 620 } 621 622 File * 623 tofile(String *s) 624 { 625 File *f; 626 627 if(s->s[0] != ' ') 628 error(Eblank); 629 if(loadflist(s) == 0){ 630 f = lookfile(&genstr); /* empty string ==> nameless file */ 631 if(f == 0) 632 error_s(Emenu, genc); 633 }else if((f=readflist(FALSE, FALSE)) == 0) 634 error_s(Emenu, genc); 635 return current(f); 636 } 637 638 File * 639 getfile(String *s) 640 { 641 File *f; 642 643 if(loadflist(s) == 0) 644 logsetname(f = newfile(), &genstr); 645 else if((f=readflist(TRUE, FALSE)) == 0) 646 error(Eblank); 647 return current(f); 648 } 649 650 void 651 closefiles(File *f, String *s) 652 { 653 if(s->s[0] == 0){ 654 if(f == 0) 655 error(Enofile); 656 trytoclose(f); 657 return; 658 } 659 if(s->s[0] != ' ') 660 error(Eblank); 661 if(loadflist(s) == 0) 662 error(Enewline); 663 readflist(FALSE, TRUE); 664 } 665 666 void 667 copy(File *f, Address addr2) 668 { 669 Posn p; 670 int ni; 671 for(p=addr.r.p1; p<addr.r.p2; p+=ni){ 672 ni = addr.r.p2-p; 673 if(ni > BLOCKSIZE) 674 ni = BLOCKSIZE; 675 bufread(&f->b, p, genbuf, ni); 676 loginsert(addr2.f, addr2.r.p2, tmprstr(genbuf, ni)->s, ni); 677 } 678 addr2.f->ndot.r.p2 = addr2.r.p2+(f->dot.r.p2-f->dot.r.p1); 679 addr2.f->ndot.r.p1 = addr2.r.p2; 680 } 681 682 void 683 move(File *f, Address addr2) 684 { 685 if(addr.r.p2 <= addr2.r.p2){ 686 logdelete(f, addr.r.p1, addr.r.p2); 687 copy(f, addr2); 688 }else if(addr.r.p1 >= addr2.r.p2){ 689 copy(f, addr2); 690 logdelete(f, addr.r.p1, addr.r.p2); 691 }else 692 error(Eoverlap); 693 } 694 695 Posn 696 nlcount(File *f, Posn p0, Posn p1) 697 { 698 Posn nl = 0; 699 700 while(p0 < p1) 701 if(filereadc(f, p0++)=='\n') 702 nl++; 703 return nl; 704 } 705 706 void 707 printposn(File *f, int charsonly) 708 { 709 Posn l1, l2; 710 711 if(!charsonly){ 712 l1 = 1+nlcount(f, (Posn)0, addr.r.p1); 713 l2 = l1+nlcount(f, addr.r.p1, addr.r.p2); 714 /* check if addr ends with '\n' */ 715 if(addr.r.p2>0 && addr.r.p2>addr.r.p1 && filereadc(f, addr.r.p2-1)=='\n') 716 --l2; 717 dprint("%lud", l1); 718 if(l2 != l1) 719 dprint(",%lud", l2); 720 dprint("; "); 721 } 722 dprint("#%lud", addr.r.p1); 723 if(addr.r.p2 != addr.r.p1) 724 dprint(",#%lud", addr.r.p2); 725 dprint("\n"); 726 } 727 728 void 729 settempfile(void) 730 { 731 if(tempfile.nalloc < file.nused){ 732 if(tempfile.filepptr) 733 free(tempfile.filepptr); 734 tempfile.filepptr = emalloc(sizeof(File*)*file.nused); 735 tempfile.nalloc = file.nused; 736 } 737 memmove(tempfile.filepptr, file.filepptr, sizeof(File*)*file.nused); 738 tempfile.nused = file.nused; 739 }