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 }