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