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