1 #include "sam.h" 2 3 Header h; 4 uchar indata[DATASIZE]; 5 uchar outdata[2*DATASIZE+3]; /* room for overflow message */ 6 uchar *inp; 7 uchar *outp; 8 uchar *outmsg = outdata; 9 Posn cmdpt; 10 Posn cmdptadv; 11 Buffer snarfbuf; 12 int waitack; 13 int noflush; 14 int tversion; 15 16 int inshort(void); 17 long inlong(void); 18 vlong invlong(void); 19 int inmesg(Tmesg); 20 21 void outshort(int); 22 void outlong(long); 23 void outvlong(vlong); 24 void outcopy(int, void*); 25 void outsend(void); 26 void outstart(Hmesg); 27 28 void setgenstr(File*, Posn, Posn); 29 30 #ifdef DEBUG 31 char *hname[] = { 32 [Hversion] "Hversion", 33 [Hbindname] "Hbindname", 34 [Hcurrent] "Hcurrent", 35 [Hnewname] "Hnewname", 36 [Hmovname] "Hmovname", 37 [Hgrow] "Hgrow", 38 [Hcheck0] "Hcheck0", 39 [Hcheck] "Hcheck", 40 [Hunlock] "Hunlock", 41 [Hdata] "Hdata", 42 [Horigin] "Horigin", 43 [Hunlockfile] "Hunlockfile", 44 [Hsetdot] "Hsetdot", 45 [Hgrowdata] "Hgrowdata", 46 [Hmoveto] "Hmoveto", 47 [Hclean] "Hclean", 48 [Hdirty] "Hdirty", 49 [Hcut] "Hcut", 50 [Hsetpat] "Hsetpat", 51 [Hdelname] "Hdelname", 52 [Hclose] "Hclose", 53 [Hsetsnarf] "Hsetsnarf", 54 [Hsnarflen] "Hsnarflen", 55 [Hack] "Hack", 56 [Hexit] "Hexit", 57 [Hplumb] "Hplumb", 58 }; 59 60 char *tname[] = { 61 [Tversion] "Tversion", 62 [Tstartcmdfile] "Tstartcmdfile", 63 [Tcheck] "Tcheck", 64 [Trequest] "Trequest", 65 [Torigin] "Torigin", 66 [Tstartfile] "Tstartfile", 67 [Tworkfile] "Tworkfile", 68 [Ttype] "Ttype", 69 [Tcut] "Tcut", 70 [Tpaste] "Tpaste", 71 [Tsnarf] "Tsnarf", 72 [Tstartnewfile] "Tstartnewfile", 73 [Twrite] "Twrite", 74 [Tclose] "Tclose", 75 [Tlook] "Tlook", 76 [Tsearch] "Tsearch", 77 [Tsend] "Tsend", 78 [Tdclick] "Tdclick", 79 [Tstartsnarf] "Tstartsnarf", 80 [Tsetsnarf] "Tsetsnarf", 81 [Tack] "Tack", 82 [Texit] "Texit", 83 [Tplumb] "Tplumb", 84 }; 85 86 void 87 journal(int out, char *s) 88 { 89 static int fd = 0; 90 91 if(fd <= 0) 92 fd = create("/tmp/sam.out", 1, 0666L); 93 fprint(fd, "%s%s\n", out? "out: " : "in: ", s); 94 } 95 96 void 97 journaln(int out, long n) 98 { 99 char buf[32]; 100 101 snprint(buf, sizeof(buf), "%ld", n); 102 journal(out, buf); 103 } 104 105 void 106 journalv(int out, vlong v) 107 { 108 char buf[32]; 109 110 sprint(buf, sizeof(buf), "%lld", v); 111 journal(out, buf); 112 } 113 #else 114 #define journal(a, b) 115 #define journaln(a, b) 116 #define journalv(a, b) 117 #endif 118 119 int 120 rcvchar(void){ 121 static uchar buf[64]; 122 static i, nleft = 0; 123 124 if(nleft <= 0){ 125 nleft = read(0, (char *)buf, sizeof buf); 126 if(nleft <= 0) 127 return -1; 128 i = 0; 129 } 130 --nleft; 131 return buf[i++]; 132 } 133 134 int 135 rcv(void){ 136 int c; 137 static state = 0; 138 static count = 0; 139 static i = 0; 140 141 while((c=rcvchar()) != -1) 142 switch(state){ 143 case 0: 144 h.type = c; 145 state++; 146 break; 147 148 case 1: 149 h.count0 = c; 150 state++; 151 break; 152 153 case 2: 154 h.count1 = c; 155 count = h.count0|(h.count1<<8); 156 i = 0; 157 if(count > DATASIZE) 158 panic("count>DATASIZE"); 159 if(count == 0) 160 goto zerocount; 161 state++; 162 break; 163 164 case 3: 165 indata[i++] = c; 166 if(i == count){ 167 zerocount: 168 indata[i] = 0; 169 state = count = 0; 170 return inmesg(h.type); 171 } 172 break; 173 } 174 return 0; 175 } 176 177 File * 178 whichfile(int tag) 179 { 180 int i; 181 182 for(i = 0; i<file.nused; i++) 183 if(file.filepptr[i]->tag==tag) 184 return file.filepptr[i]; 185 hiccough((char *)0); 186 return 0; 187 } 188 189 int 190 inmesg(Tmesg type) 191 { 192 Rune buf[1025]; 193 char cbuf[64]; 194 int i, m; 195 short s; 196 long l, l1; 197 vlong v; 198 File *f; 199 Posn p0, p1, p; 200 Range r; 201 String *str; 202 char *c, *wdir; 203 Rune *rp; 204 Plumbmsg *pm; 205 206 if(type > TMAX) 207 panic("inmesg"); 208 209 journal(0, tname[type]); 210 211 inp = indata; 212 switch(type){ 213 case -1: 214 panic("rcv error"); 215 216 default: 217 fprint(2, "unknown type %d\n", type); 218 panic("rcv unknown"); 219 220 case Tversion: 221 tversion = inshort(); 222 journaln(0, tversion); 223 break; 224 225 case Tstartcmdfile: 226 v = invlong(); /* for 64-bit pointers */ 227 journalv(0, v); 228 Strdupl(&genstr, samname); 229 cmd = newfile(); 230 cmd->unread = 0; 231 outTsv(Hbindname, cmd->tag, v); 232 outTs(Hcurrent, cmd->tag); 233 logsetname(cmd, &genstr); 234 cmd->rasp = listalloc('P'); 235 cmd->mod = 0; 236 if(cmdstr.n){ 237 loginsert(cmd, 0L, cmdstr.s, cmdstr.n); 238 Strdelete(&cmdstr, 0L, (Posn)cmdstr.n); 239 } 240 fileupdate(cmd, FALSE, TRUE); 241 outT0(Hunlock); 242 break; 243 244 case Tcheck: 245 /* go through whichfile to check the tag */ 246 outTs(Hcheck, whichfile(inshort())->tag); 247 break; 248 249 case Trequest: 250 f = whichfile(inshort()); 251 p0 = inlong(); 252 p1 = p0+inshort(); 253 journaln(0, p0); 254 journaln(0, p1-p0); 255 if(f->unread) 256 panic("Trequest: unread"); 257 if(p1>f->nc) 258 p1 = f->nc; 259 if(p0>f->nc) /* can happen e.g. scrolling during command */ 260 p0 = f->nc; 261 if(p0 == p1){ 262 i = 0; 263 r.p1 = r.p2 = p0; 264 }else{ 265 r = rdata(f->rasp, p0, p1-p0); 266 i = r.p2-r.p1; 267 bufread(f, r.p1, buf, i); 268 } 269 buf[i]=0; 270 outTslS(Hdata, f->tag, r.p1, tmprstr(buf, i+1)); 271 break; 272 273 case Torigin: 274 s = inshort(); 275 l = inlong(); 276 l1 = inlong(); 277 journaln(0, l1); 278 lookorigin(whichfile(s), l, l1); 279 break; 280 281 case Tstartfile: 282 termlocked++; 283 f = whichfile(inshort()); 284 if(!f->rasp) /* this might be a duplicate message */ 285 f->rasp = listalloc('P'); 286 current(f); 287 outTsv(Hbindname, f->tag, invlong()); /* for 64-bit pointers */ 288 outTs(Hcurrent, f->tag); 289 journaln(0, f->tag); 290 if(f->unread) 291 load(f); 292 else{ 293 if(f->nc>0){ 294 rgrow(f->rasp, 0L, f->nc); 295 outTsll(Hgrow, f->tag, 0L, f->nc); 296 } 297 outTs(Hcheck0, f->tag); 298 moveto(f, f->dot.r); 299 } 300 break; 301 302 case Tworkfile: 303 i = inshort(); 304 f = whichfile(i); 305 current(f); 306 f->dot.r.p1 = inlong(); 307 f->dot.r.p2 = inlong(); 308 f->tdot = f->dot.r; 309 journaln(0, i); 310 journaln(0, f->dot.r.p1); 311 journaln(0, f->dot.r.p2); 312 break; 313 314 case Ttype: 315 f = whichfile(inshort()); 316 p0 = inlong(); 317 journaln(0, p0); 318 journal(0, (char*)inp); 319 str = tmpcstr((char*)inp); 320 i = str->n; 321 loginsert(f, p0, str->s, str->n); 322 if(fileupdate(f, FALSE, FALSE)) 323 seq++; 324 if(f==cmd && p0==f->nc-i && i>0 && str->s[i-1]=='\n'){ 325 freetmpstr(str); 326 termlocked++; 327 termcommand(); 328 }else 329 freetmpstr(str); 330 f->dot.r.p1 = f->dot.r.p2 = p0+i; /* terminal knows this already */ 331 f->tdot = f->dot.r; 332 break; 333 334 case Tcut: 335 f = whichfile(inshort()); 336 p0 = inlong(); 337 p1 = inlong(); 338 journaln(0, p0); 339 journaln(0, p1); 340 logdelete(f, p0, p1); 341 if(fileupdate(f, FALSE, FALSE)) 342 seq++; 343 f->dot.r.p1 = f->dot.r.p2 = p0; 344 f->tdot = f->dot.r; /* terminal knows the value of dot already */ 345 break; 346 347 case Tpaste: 348 f = whichfile(inshort()); 349 p0 = inlong(); 350 journaln(0, p0); 351 for(l=0; l<snarfbuf.nc; l+=m){ 352 m = snarfbuf.nc-l; 353 if(m>BLOCKSIZE) 354 m = BLOCKSIZE; 355 bufread(&snarfbuf, l, genbuf, m); 356 loginsert(f, p0, tmprstr(genbuf, m)->s, m); 357 } 358 if(fileupdate(f, FALSE, TRUE)) 359 seq++; 360 f->dot.r.p1 = p0; 361 f->dot.r.p2 = p0+snarfbuf.nc; 362 f->tdot.p1 = -1; /* force telldot to tell (arguably a BUG) */ 363 telldot(f); 364 outTs(Hunlockfile, f->tag); 365 break; 366 367 case Tsnarf: 368 i = inshort(); 369 p0 = inlong(); 370 p1 = inlong(); 371 snarf(whichfile(i), p0, p1, &snarfbuf, 0); 372 break; 373 374 case Tstartnewfile: 375 v = invlong(); 376 Strdupl(&genstr, empty); 377 f = newfile(); 378 f->rasp = listalloc('P'); 379 outTsv(Hbindname, f->tag, v); 380 logsetname(f, &genstr); 381 outTs(Hcurrent, f->tag); 382 current(f); 383 load(f); 384 break; 385 386 case Twrite: 387 termlocked++; 388 i = inshort(); 389 journaln(0, i); 390 f = whichfile(i); 391 addr.r.p1 = 0; 392 addr.r.p2 = f->nc; 393 if(f->name.s[0] == 0) 394 error(Enoname); 395 Strduplstr(&genstr, &f->name); 396 writef(f); 397 break; 398 399 case Tclose: 400 termlocked++; 401 i = inshort(); 402 journaln(0, i); 403 f = whichfile(i); 404 current(f); 405 trytoclose(f); 406 /* if trytoclose fails, will error out */ 407 delete(f); 408 break; 409 410 case Tlook: 411 f = whichfile(inshort()); 412 termlocked++; 413 p0 = inlong(); 414 p1 = inlong(); 415 journaln(0, p0); 416 journaln(0, p1); 417 setgenstr(f, p0, p1); 418 for(l = 0; l<genstr.n; l++){ 419 i = genstr.s[l]; 420 if(utfrune(".*+?(|)\\[]^$", i)){ 421 str = tmpcstr("\\"); 422 Strinsert(&genstr, str, l++); 423 freetmpstr(str); 424 } 425 } 426 Straddc(&genstr, '\0'); 427 nextmatch(f, &genstr, p1, 1); 428 moveto(f, sel.p[0]); 429 break; 430 431 case Tsearch: 432 termlocked++; 433 if(curfile == 0) 434 error(Enofile); 435 if(lastpat.s[0] == 0) 436 panic("Tsearch"); 437 nextmatch(curfile, &lastpat, curfile->dot.r.p2, 1); 438 moveto(curfile, sel.p[0]); 439 break; 440 441 case Tsend: 442 termlocked++; 443 inshort(); /* ignored */ 444 p0 = inlong(); 445 p1 = inlong(); 446 setgenstr(cmd, p0, p1); 447 bufreset(&snarfbuf); 448 bufinsert(&snarfbuf, (Posn)0, genstr.s, genstr.n); 449 outTl(Hsnarflen, genstr.n); 450 if(genstr.s[genstr.n-1] != '\n') 451 Straddc(&genstr, '\n'); 452 loginsert(cmd, cmd->nc, genstr.s, genstr.n); 453 fileupdate(cmd, FALSE, TRUE); 454 cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->nc; 455 telldot(cmd); 456 termcommand(); 457 break; 458 459 case Tdclick: 460 f = whichfile(inshort()); 461 p1 = inlong(); 462 doubleclick(f, p1); 463 f->tdot.p1 = f->tdot.p2 = p1; 464 telldot(f); 465 outTs(Hunlockfile, f->tag); 466 break; 467 468 case Tstartsnarf: 469 if (snarfbuf.nc <= 0) { /* nothing to export */ 470 outTs(Hsetsnarf, 0); 471 break; 472 } 473 c = 0; 474 i = 0; 475 m = snarfbuf.nc; 476 if(m > SNARFSIZE) { 477 m = SNARFSIZE; 478 dprint("?warning: snarf buffer truncated\n"); 479 } 480 rp = malloc(m*sizeof(Rune)); 481 if(rp){ 482 bufread(&snarfbuf, 0, rp, m); 483 c = Strtoc(tmprstr(rp, m)); 484 free(rp); 485 i = strlen(c); 486 } 487 outTs(Hsetsnarf, i); 488 if(c){ 489 Write(1, c, i); 490 free(c); 491 } else 492 dprint("snarf buffer too long\n"); 493 break; 494 495 case Tsetsnarf: 496 m = inshort(); 497 if(m > SNARFSIZE) 498 error(Etoolong); 499 c = malloc(m+1); 500 if(c){ 501 for(i=0; i<m; i++) 502 c[i] = rcvchar(); 503 c[m] = 0; 504 str = tmpcstr(c); 505 free(c); 506 bufreset(&snarfbuf); 507 bufinsert(&snarfbuf, (Posn)0, str->s, str->n); 508 freetmpstr(str); 509 outT0(Hunlock); 510 } 511 break; 512 513 case Tack: 514 waitack = 0; 515 break; 516 517 case Tplumb: 518 f = whichfile(inshort()); 519 p0 = inlong(); 520 p1 = inlong(); 521 pm = emalloc(sizeof(Plumbmsg)); 522 pm->src = strdup("sam"); 523 pm->dst = 0; 524 /* construct current directory */ 525 c = Strtoc(&f->name); 526 if(c[0] == '/') 527 pm->wdir = c; 528 else{ 529 wdir = emalloc(1024); 530 getwd(wdir, 1024); 531 pm->wdir = emalloc(1024); 532 snprint(pm->wdir, 1024, "%s/%s", wdir, c); 533 cleanname(pm->wdir); 534 free(wdir); 535 free(c); 536 } 537 c = strrchr(pm->wdir, '/'); 538 if(c) 539 *c = '\0'; 540 pm->type = strdup("text"); 541 if(p1 > p0) 542 pm->attr = nil; 543 else{ 544 p = p0; 545 while(p0>0 && (i=filereadc(f, p0 - 1))!=' ' && i!='\t' && i!='\n') 546 p0--; 547 while(p1<f->nc && (i=filereadc(f, p1))!=' ' && i!='\t' && i!='\n') 548 p1++; 549 sprint(cbuf, "click=%ld", p-p0); 550 pm->attr = plumbunpackattr(cbuf); 551 } 552 if(p0==p1 || p1-p0>=BLOCKSIZE){ 553 plumbfree(pm); 554 break; 555 } 556 setgenstr(f, p0, p1); 557 pm->data = Strtoc(&genstr); 558 pm->ndata = strlen(pm->data); 559 c = plumbpack(pm, &i); 560 if(c != 0){ 561 outTs(Hplumb, i); 562 Write(1, c, i); 563 free(c); 564 } 565 plumbfree(pm); 566 break; 567 568 case Texit: 569 exits(0); 570 } 571 return TRUE; 572 } 573 574 void 575 snarf(File *f, Posn p1, Posn p2, Buffer *buf, int emptyok) 576 { 577 Posn l; 578 int i; 579 580 if(!emptyok && p1==p2) 581 return; 582 bufreset(buf); 583 /* Stage through genbuf to avoid compaction problems (vestigial) */ 584 if(p2 > f->nc){ 585 fprint(2, "bad snarf addr p1=%ld p2=%ld f->nc=%d\n", p1, p2, f->nc); /*ZZZ should never happen, can remove */ 586 p2 = f->nc; 587 } 588 for(l=p1; l<p2; l+=i){ 589 i = p2-l>BLOCKSIZE? BLOCKSIZE : p2-l; 590 bufread(f, l, genbuf, i); 591 bufinsert(buf, buf->nc, tmprstr(genbuf, i)->s, i); 592 } 593 } 594 595 int 596 inshort(void) 597 { 598 ushort n; 599 600 n = inp[0] | (inp[1]<<8); 601 inp += 2; 602 return n; 603 } 604 605 long 606 inlong(void) 607 { 608 ulong n; 609 610 n = inp[0] | (inp[1]<<8) | (inp[2]<<16) | (inp[3]<<24); 611 inp += 4; 612 return n; 613 } 614 615 vlong 616 invlong(void) 617 { 618 vlong v; 619 620 v = (inp[7]<<24) | (inp[6]<<16) | (inp[5]<<8) | inp[4]; 621 v = (v<<16) | (inp[3]<<8) | inp[2]; 622 v = (v<<16) | (inp[1]<<8) | inp[0]; 623 inp += 8; 624 return v; 625 } 626 627 void 628 setgenstr(File *f, Posn p0, Posn p1) 629 { 630 if(p0 != p1){ 631 if(p1-p0 >= TBLOCKSIZE) 632 error(Etoolong); 633 Strinsure(&genstr, p1-p0); 634 bufread(f, p0, genbuf, p1-p0); 635 memmove(genstr.s, genbuf, RUNESIZE*(p1-p0)); 636 genstr.n = p1-p0; 637 }else{ 638 if(snarfbuf.nc == 0) 639 error(Eempty); 640 if(snarfbuf.nc > TBLOCKSIZE) 641 error(Etoolong); 642 bufread(&snarfbuf, (Posn)0, genbuf, snarfbuf.nc); 643 Strinsure(&genstr, snarfbuf.nc); 644 memmove(genstr.s, genbuf, RUNESIZE*snarfbuf.nc); 645 genstr.n = snarfbuf.nc; 646 } 647 } 648 649 void 650 outT0(Hmesg type) 651 { 652 outstart(type); 653 outsend(); 654 } 655 656 void 657 outTl(Hmesg type, long l) 658 { 659 outstart(type); 660 outlong(l); 661 outsend(); 662 } 663 664 void 665 outTs(Hmesg type, int s) 666 { 667 outstart(type); 668 journaln(1, s); 669 outshort(s); 670 outsend(); 671 } 672 673 void 674 outS(String *s) 675 { 676 char *c; 677 int i; 678 679 c = Strtoc(s); 680 i = strlen(c); 681 outcopy(i, c); 682 if(i > 99) 683 c[99] = 0; 684 journaln(1, i); 685 journal(1, c); 686 free(c); 687 } 688 689 void 690 outTsS(Hmesg type, int s1, String *s) 691 { 692 outstart(type); 693 outshort(s1); 694 outS(s); 695 outsend(); 696 } 697 698 void 699 outTslS(Hmesg type, int s1, Posn l1, String *s) 700 { 701 outstart(type); 702 outshort(s1); 703 journaln(1, s1); 704 outlong(l1); 705 journaln(1, l1); 706 outS(s); 707 outsend(); 708 } 709 710 void 711 outTS(Hmesg type, String *s) 712 { 713 outstart(type); 714 outS(s); 715 outsend(); 716 } 717 718 void 719 outTsllS(Hmesg type, int s1, Posn l1, Posn l2, String *s) 720 { 721 outstart(type); 722 outshort(s1); 723 outlong(l1); 724 outlong(l2); 725 journaln(1, l1); 726 journaln(1, l2); 727 outS(s); 728 outsend(); 729 } 730 731 void 732 outTsll(Hmesg type, int s, Posn l1, Posn l2) 733 { 734 outstart(type); 735 outshort(s); 736 outlong(l1); 737 outlong(l2); 738 journaln(1, l1); 739 journaln(1, l2); 740 outsend(); 741 } 742 743 void 744 outTsl(Hmesg type, int s, Posn l) 745 { 746 outstart(type); 747 outshort(s); 748 outlong(l); 749 journaln(1, l); 750 outsend(); 751 } 752 753 void 754 outTsv(Hmesg type, int s, vlong v) 755 { 756 outstart(type); 757 outshort(s); 758 outvlong(v); 759 journalv(1, v); 760 outsend(); 761 } 762 763 void 764 outstart(Hmesg type) 765 { 766 journal(1, hname[type]); 767 outmsg[0] = type; 768 outp = outmsg+3; 769 } 770 771 void 772 outcopy(int count, void *data) 773 { 774 memmove(outp, data, count); 775 outp += count; 776 } 777 778 void 779 outshort(int s) 780 { 781 *outp++ = s; 782 *outp++ = s>>8; 783 } 784 785 void 786 outlong(long l) 787 { 788 *outp++ = l; 789 *outp++ = l>>8; 790 *outp++ = l>>16; 791 *outp++ = l>>24; 792 } 793 794 void 795 outvlong(vlong v) 796 { 797 int i; 798 799 for(i = 0; i < 8; i++){ 800 *outp++ = v; 801 v >>= 8; 802 } 803 } 804 805 void 806 outsend(void) 807 { 808 int outcount; 809 810 outcount = outp-outmsg; 811 outcount -= 3; 812 outmsg[1] = outcount; 813 outmsg[2] = outcount>>8; 814 outmsg = outp; 815 if(!noflush){ 816 outcount = outmsg-outdata; 817 if (write(1, (char*) outdata, outcount) != outcount) 818 rescue(); 819 outmsg = outdata; 820 return; 821 } 822 if(outmsg < outdata+DATASIZE) 823 return; 824 outflush(); 825 } 826 827 void 828 outflush(void) 829 { 830 if(outmsg == outdata) 831 return; 832 noflush = 0; 833 outT0(Hack); 834 waitack = 1; 835 do 836 if(rcv() == 0){ 837 rescue(); 838 exits("eof"); 839 } 840 while(waitack); 841 outmsg = outdata; 842 noflush = 1; 843 } 844