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