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