9fans.net/go@v0.0.7/cmd/sam/mesg.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "fmt" 7 "os" 8 "path/filepath" 9 "strings" 10 11 "9fans.net/go/plumb" 12 ) 13 14 // #include "sam.h" 15 var h Header 16 var indata = make([]byte, DATASIZE) 17 var inp []uint8 18 19 var outdata [2*DATASIZE + 3]uint8 /* room for overflow message */ 20 var outmsg = outdata[:0] // messages completed but not sent 21 var outp []uint8 // message being created in spare capacity of outmsg 22 var cmdpt Posn 23 var cmdptadv Posn 24 var snarfbuf Buffer 25 var waitack bool 26 var outbuffered bool 27 var tversion int 28 29 /* 30 // #ifdef DEBUG 31 var hname = [26]string{ 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 var tname = [23]string{ 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 var journal_fd int = 0 87 88 func journal(out int, s string) { 89 90 if journal_fd <= 0 { 91 // journal_fd = create("/tmp/sam.out", 1, 0666) 92 } 93 var tmp8 unknown 94 if out != 0 { 95 tmp8 = "out: " 96 } else { 97 tmp8 = "in: " 98 } 99 fmt.Fprintf(journal_fd, "%s%s\n", tmp8, s) 100 } 101 102 func journaln(out int, n int) { 103 var buf [32]int8 104 snprint(buf, sizeof(buf), "%ld", n) 105 // journal(out, buf) 106 } 107 108 func journalv(out int, v int64) { 109 var buf [32]int8 110 snprint(buf, sizeof(buf), "%lld", v) 111 // journal(out, buf) 112 } 113 114 // #else 115 // #define // journal(a, b) 116 // #define journaln(a, b) 117 // #endif 118 */ 119 120 func journal(out int, s string) {} 121 func journaln(out, n int) {} 122 123 var rcvchar_nleft int = 0 124 var rcvchar_buf [64]uint8 125 var rcvchar_i int 126 127 func rcvchar() int { 128 if rcvchar_nleft <= 0 { 129 n, err := os.Stdin.Read(rcvchar_buf[:]) 130 if err != nil || n <= 0 { 131 return -1 132 } 133 rcvchar_nleft = n 134 rcvchar_i = 0 135 } 136 c := rcvchar_buf[rcvchar_i] 137 rcvchar_nleft-- 138 rcvchar_i++ 139 return int(c) 140 } 141 142 var rcv_state int = 0 143 var rcv_count int = 0 144 var rcv_i int = 0 145 146 func rcv() bool { 147 for c := rcvchar(); c >= 0; c = rcvchar() { 148 switch rcv_state { 149 case 0: 150 h.typ = Tmesg(c) 151 rcv_state++ 152 153 case 1: 154 h.count0 = uint8(c) 155 rcv_state++ 156 157 case 2: 158 h.count1 = uint8(c) 159 rcv_count = int(h.count0) | int(h.count1)<<8 160 if rcv_count > DATASIZE { 161 panic_("count>DATASIZE") 162 } 163 indata = indata[:0] 164 if rcv_count == 0 { 165 rcv_count = 0 166 rcv_state = 0 167 return inmesg(h.typ) 168 } 169 rcv_state++ 170 171 case 3: 172 indata = append(indata, byte(c)) 173 if len(indata) == rcv_count { 174 rcv_count = 0 175 rcv_state = 0 176 return inmesg(h.typ) 177 } 178 } 179 } 180 return false 181 } 182 183 func whichfile(tag int) *File { 184 for _, f := range file { 185 if f.tag == tag { 186 return f 187 } 188 } 189 hiccough("") 190 return nil 191 } 192 193 func inmesg(type_ Tmesg) bool { 194 debug("inmesg %v %x\n", type_, indata) 195 if type_ > TMAX { 196 panic_("inmesg") 197 } 198 199 // journal(0, tname[type_]) 200 201 inp = indata 202 var buf [1025]rune 203 var i int 204 var m int 205 var s int 206 var l int 207 var l1 int 208 var v int64 209 var f *File 210 var p0 Posn 211 var p1 Posn 212 var p Posn 213 var r Range 214 var str *String 215 switch type_ { 216 case -1: 217 panic_("rcv error") 218 fallthrough 219 220 default: 221 fmt.Fprintf(os.Stderr, "unknown type %d\n", type_) 222 panic_("rcv unknown") 223 fallthrough 224 225 case Tversion: 226 tversion = inshort() 227 // journaln(0, tversion) 228 229 case Tstartcmdfile: 230 v = invlong() /* for 64-bit pointers */ 231 // journaln(0, v) 232 Strdupl(&genstr, samname) 233 cmd = newfile() 234 cmd.unread = false 235 outTsv(Hbindname, cmd.tag, v) 236 outTs(Hcurrent, cmd.tag) 237 logsetname(cmd, &genstr) 238 cmd.rasp = new(PosnList) 239 cmd.mod = false 240 if len(cmdstr.s) != 0 { 241 loginsert(cmd, 0, cmdstr.s) 242 Strdelete(&cmdstr, 0, Posn(len(cmdstr.s))) 243 } 244 fileupdate(cmd, false, true) 245 outT0(Hunlock) 246 /* go through whichfile to check the tag */ 247 248 case Tcheck: 249 outTs(Hcheck, whichfile(inshort()).tag) 250 251 case Trequest: 252 f = whichfile(inshort()) 253 p0 = inlong() 254 p1 = p0 + inshort() 255 // journaln(0, p0) 256 // journaln(0, p1-p0) 257 if f.unread { 258 panic_("Trequest: unread") 259 } 260 if p1 > f.b.nc { 261 p1 = f.b.nc 262 } 263 if p0 > f.b.nc { /* can happen e.g. scrolling during command */ 264 p0 = f.b.nc 265 } 266 if p0 == p1 { 267 i = 0 268 r.p2 = p0 269 r.p1 = r.p2 270 } else { 271 r = rdata(f.rasp, p0, p1-p0) 272 i = r.p2 - r.p1 273 bufread(&f.b, r.p1, buf[:i]) 274 } 275 outTslS(Hdata, f.tag, r.p1, tmprstr(buf[:i])) 276 277 case Torigin: 278 s = inshort() 279 l = inlong() 280 l1 = inlong() 281 // journaln(0, l1) 282 lookorigin(whichfile(s), l, l1) 283 284 case Tstartfile: 285 termlocked++ 286 f = whichfile(inshort()) 287 if f.rasp == nil { /* this might be a duplicate message */ 288 f.rasp = new(PosnList) 289 } 290 current(f) 291 outTsv(Hbindname, f.tag, invlong()) /* for 64-bit pointers */ 292 outTs(Hcurrent, f.tag) 293 // journaln(0, f.tag) 294 if f.unread { 295 load(f) 296 } else { 297 if f.b.nc > 0 { 298 rgrow(f.rasp, 0, f.b.nc) 299 outTsll(Hgrow, f.tag, 0, f.b.nc) 300 } 301 outTs(Hcheck0, f.tag) 302 moveto(f, f.dot.r) 303 } 304 305 case Tworkfile: 306 i = inshort() 307 f = whichfile(i) 308 current(f) 309 f.dot.r.p1 = inlong() 310 f.dot.r.p2 = inlong() 311 f.tdot = f.dot.r 312 // journaln(0, i) 313 // journaln(0, f.dot.r.p1) 314 // journaln(0, f.dot.r.p2) 315 316 case Ttype: 317 f = whichfile(inshort()) 318 p0 = inlong() 319 // journaln(0, p0) 320 // journal(0, (string)(inp)) 321 str = tmpcstr((string)(inp)) 322 i = len(str.s) 323 debug("Ttype %s %d %q\n", f.name, p0, str) 324 loginsert(f, p0, str.s) 325 if fileupdate(f, false, false) { 326 seq++ 327 } 328 if f == cmd && p0 == f.b.nc-i && i > 0 && str.s[i-1] == '\n' { 329 freetmpstr(str) 330 termlocked++ 331 termcommand() 332 } else { 333 freetmpstr(str) 334 } /* terminal knows this already */ 335 f.dot.r.p2 = p0 + i 336 f.dot.r.p1 = f.dot.r.p2 337 f.tdot = f.dot.r 338 339 case Tcut: 340 f = whichfile(inshort()) 341 p0 = inlong() 342 p1 = inlong() 343 // journaln(0, p0) 344 // journaln(0, p1) 345 logdelete(f, p0, p1) 346 if fileupdate(f, false, false) { 347 seq++ 348 } 349 f.dot.r.p2 = p0 350 f.dot.r.p1 = f.dot.r.p2 351 f.tdot = f.dot.r /* terminal knows the value of dot already */ 352 353 case Tpaste: 354 f = whichfile(inshort()) 355 p0 = inlong() 356 // journaln(0, p0) 357 for l = 0; l < snarfbuf.nc; l += m { 358 m = snarfbuf.nc - l 359 if m > BLOCKSIZE { 360 m = BLOCKSIZE 361 } 362 bufread(&snarfbuf, l, genbuf[:m]) 363 loginsert(f, p0, tmprstr(genbuf[:m]).s) // TODO(rsc): had ", m" 364 } 365 if fileupdate(f, false, true) { 366 seq++ 367 } 368 f.dot.r.p1 = p0 369 f.dot.r.p2 = p0 + snarfbuf.nc 370 f.tdot.p1 = -1 /* force telldot to tell (arguably a BUG) */ 371 telldot(f) 372 outTs(Hunlockfile, f.tag) 373 374 case Tsnarf: 375 i = inshort() 376 p0 = inlong() 377 p1 = inlong() 378 snarf(whichfile(i), p0, p1, &snarfbuf, 0) 379 380 case Tstartnewfile: 381 v = invlong() 382 Strdupl(&genstr, empty) 383 f = newfile() 384 f.rasp = new(PosnList) 385 outTsv(Hbindname, f.tag, v) 386 logsetname(f, &genstr) 387 outTs(Hcurrent, f.tag) 388 current(f) 389 load(f) 390 391 case Twrite: 392 termlocked++ 393 i = inshort() 394 // journaln(0, i) 395 f = whichfile(i) 396 addr.r.p1 = 0 397 addr.r.p2 = f.b.nc 398 if len(f.name.s) == 0 { 399 error_(Enoname) 400 } 401 Strduplstr(&genstr, &f.name) 402 writef(f) 403 404 case Tclose: 405 termlocked++ 406 i = inshort() 407 // journaln(0, i) 408 f = whichfile(i) 409 current(f) 410 trytoclose(f) 411 /* if trytoclose fails, will error out */ 412 delete(f) 413 414 case Tlook: 415 f = whichfile(inshort()) 416 termlocked++ 417 p0 = inlong() 418 p1 = inlong() 419 // journaln(0, p0) 420 // journaln(0, p1) 421 setgenstr(f, p0, p1) 422 for l = 0; l < len(genstr.s); l++ { 423 i := genstr.s[l] 424 if strings.ContainsRune(".*+?(|)\\[]^$", i) { 425 str = tmpcstr("\\") 426 Strinsert(&genstr, str, l) 427 l++ 428 freetmpstr(str) 429 } 430 } 431 nextmatch(f, &genstr, p1, 1) 432 moveto(f, sel.p[0]) 433 434 case Tsearch: 435 termlocked++ 436 if curfile == nil { 437 error_(Enofile) 438 } 439 if len(lastpat.s) == 0 { 440 panic_("Tsearch") 441 } 442 nextmatch(curfile, &lastpat, curfile.dot.r.p2, 1) 443 moveto(curfile, sel.p[0]) 444 445 case Tsend: 446 termlocked++ 447 inshort() /* ignored */ 448 p0 = inlong() 449 p1 = inlong() 450 setgenstr(cmd, p0, p1) 451 bufreset(&snarfbuf) 452 bufinsert(&snarfbuf, Posn(0), genstr.s) 453 outTl(Hsnarflen, len(genstr.s)) 454 if len(genstr.s) > 0 && genstr.s[len(genstr.s)-1] != '\n' { 455 Straddc(&genstr, '\n') 456 } 457 loginsert(cmd, cmd.b.nc, genstr.s) 458 fileupdate(cmd, false, true) 459 cmd.dot.r.p2 = cmd.b.nc 460 cmd.dot.r.p1 = cmd.dot.r.p2 461 telldot(cmd) 462 termcommand() 463 464 case Tdclick: 465 f = whichfile(inshort()) 466 p1 = inlong() 467 doubleclick(f, p1) 468 f.tdot.p2 = p1 469 f.tdot.p1 = f.tdot.p2 470 telldot(f) 471 outTs(Hunlockfile, f.tag) 472 473 case Tstartsnarf: 474 if snarfbuf.nc <= 0 { /* nothing to export */ 475 outTs(Hsetsnarf, 0) 476 break 477 } 478 m = snarfbuf.nc 479 if m > SNARFSIZE { 480 m = SNARFSIZE 481 dprint("?warning: snarf buffer truncated\n") 482 } 483 rp := make([]rune, m) 484 bufread(&snarfbuf, 0, rp) 485 c := []byte(string(rp)) // TODO(rsc) 486 // free(rp) 487 outTs(Hsetsnarf, len(c)) 488 os.Stdout.Write(c) 489 // free(c) 490 491 case Tsetsnarf: 492 m = inshort() 493 if m > SNARFSIZE { 494 error_(Etoolong) 495 } 496 c := make([]byte, m) 497 for i := 0; i < m; i++ { 498 c[i] = byte(rcvchar()) 499 } 500 str := []rune(string(c)) // TODO(rsc) 501 // free(c) 502 bufreset(&snarfbuf) 503 bufinsert(&snarfbuf, Posn(0), str) 504 // freetmpstr(str) 505 outT0(Hunlock) 506 507 case Tack: 508 waitack = false 509 510 case Tplumb: 511 f = whichfile(inshort()) 512 p0 = inlong() 513 p1 = inlong() 514 pm := new(plumb.Message) 515 pm.Src = "sam" 516 /* construct current directory */ 517 c := string(f.name.s) 518 if len(c) > 0 && c[0] == '/' { 519 pm.Dir = c 520 } else { 521 wd, _ := os.Getwd() 522 pm.Dir = filepath.Join(wd, c) 523 } 524 if i := strings.LastIndex(pm.Dir, "/"); i >= 0 { 525 pm.Dir = pm.Dir[:i] 526 } 527 pm.Type = "text" 528 if p1 > p0 { 529 pm.Attr = nil 530 } else { 531 p = p0 532 for p0 > 0 { 533 p0-- 534 } 535 for p1 < f.b.nc { 536 p1++ 537 } 538 pm.Attr = &plumb.Attribute{Name: "click", Value: fmt.Sprint(p - p0)} 539 } 540 if p0 == p1 || p1-p0 >= BLOCKSIZE { 541 // plumbfree(pm) 542 break 543 } 544 setgenstr(f, p0, p1) 545 pm.Data = []byte(string(genstr.s)) 546 var enc bytes.Buffer 547 pm.Send(&enc) 548 outTs(Hplumb, enc.Len()) 549 os.Stdout.Write(enc.Bytes()) 550 // free(enc) 551 // plumbfree(pm) 552 553 case Texit: 554 os.Exit(0) 555 } 556 return true 557 } 558 559 func snarf(f *File, p1 Posn, p2 Posn, buf *Buffer, emptyok int) { 560 if emptyok == 0 && p1 == p2 { 561 return 562 } 563 bufreset(buf) 564 /* Stage through genbuf to avoid compaction problems (vestigial) */ 565 if p2 > f.b.nc { 566 fmt.Fprintf(os.Stderr, "bad snarf addr p1=%d p2=%d f->b.nc=%d\n", p1, p2, f.b.nc) /*ZZZ should never happen, can remove */ 567 p2 = f.b.nc 568 } 569 var n int 570 for l := p1; l < p2; l += n { 571 n = p2 - l 572 if n > BLOCKSIZE { 573 n = BLOCKSIZE 574 } 575 bufread(&f.b, l, genbuf[:n]) 576 bufinsert(buf, buf.nc, tmprstr(genbuf[:n]).s) // TODO was ,n 577 } 578 } 579 580 func inshort() int { 581 n := binary.LittleEndian.Uint16(inp) 582 inp = inp[2:] 583 return int(n) 584 } 585 586 func inlong() int { 587 n := binary.LittleEndian.Uint32(inp) 588 inp = inp[4:] 589 return int(n) 590 } 591 592 func invlong() int64 { 593 v := binary.LittleEndian.Uint64(inp) 594 inp = inp[8:] 595 return int64(v) 596 } 597 598 func setgenstr(f *File, p0 Posn, p1 Posn) { 599 if p0 != p1 { 600 if p1-p0 >= TBLOCKSIZE { 601 error_(Etoolong) 602 } 603 Strinsure(&genstr, p1-p0) 604 bufread(&f.b, p0, genbuf[:p1-p0]) 605 copy(genstr.s, genbuf[:]) 606 } else { 607 if snarfbuf.nc == 0 { 608 error_(Eempty) 609 } 610 if snarfbuf.nc > TBLOCKSIZE { 611 error_(Etoolong) 612 } 613 bufread(&snarfbuf, Posn(0), genbuf[:snarfbuf.nc]) 614 Strinsure(&genstr, snarfbuf.nc) 615 copy(genstr.s, genbuf[:]) 616 } 617 } 618 619 func outT0(type_ Hmesg) { 620 outstart(type_) 621 outsend() 622 } 623 624 func outTl(type_ Hmesg, l int) { 625 outstart(type_) 626 outlong(l) 627 outsend() 628 } 629 630 func outTs(type_ Hmesg, s int) { 631 outstart(type_) 632 // journaln(1, s) 633 outshort(s) 634 outsend() 635 } 636 637 func outS(s *String) { 638 c := []byte(string(s.s)) // TODO(rsc) 639 outcopy(c) 640 // journaln(1, len(c)) 641 // if len(c) > 99 { c = c[:99] } 642 // journal(1, c) 643 // free(c) 644 } 645 646 func outTsS(type_ Hmesg, s1 int, s *String) { 647 outstart(type_) 648 outshort(s1) 649 outS(s) 650 outsend() 651 } 652 653 func outTslS(type_ Hmesg, s1 int, l1 Posn, s *String) { 654 outstart(type_) 655 outshort(s1) 656 // journaln(1, s1) 657 outlong(l1) 658 // journaln(1, l1) 659 outS(s) 660 outsend() 661 } 662 663 func outTS(type_ Hmesg, s *String) { 664 outstart(type_) 665 outS(s) 666 outsend() 667 } 668 669 func outTsllS(type_ Hmesg, s1 int, l1 Posn, l2 Posn, s *String) { 670 outstart(type_) 671 outshort(s1) 672 outlong(l1) 673 outlong(l2) 674 // journaln(1, l1) 675 // journaln(1, l2) 676 outS(s) 677 outsend() 678 } 679 680 func outTsll(type_ Hmesg, s int, l1 Posn, l2 Posn) { 681 outstart(type_) 682 outshort(s) 683 outlong(l1) 684 outlong(l2) 685 // journaln(1, l1) 686 // journaln(1, l2) 687 outsend() 688 } 689 690 func outTsl(type_ Hmesg, s int, l Posn) { 691 outstart(type_) 692 outshort(s) 693 outlong(l) 694 // journaln(1, l) 695 outsend() 696 } 697 698 func outTsv(type_ Hmesg, s int, v int64) { 699 outstart(type_) 700 outshort(s) 701 outvlong(v) 702 // journaln(1, v) 703 outsend() 704 } 705 706 func outstart(typ Hmesg) { 707 // journal(1, hname[type_]) 708 outp = outmsg[len(outmsg):len(outmsg)] 709 outp = append(outp, byte(typ), 0, 0) 710 } 711 712 func outcopy(data []byte) { 713 outp = append(outp, data...) 714 } 715 716 func outshort(s int) { 717 outp = append(outp, byte(s), byte(s>>8)) 718 } 719 720 func outlong(l int) { 721 outp = append(outp, byte(l), byte(l>>8), byte(l>>16), byte(l>>24)) 722 } 723 724 func outvlong(v int64) { 725 outp = append(outp, byte(v), byte(v>>8), byte(v>>16), byte(v>>24), 726 byte(v>>32), byte(v>>40), byte(v>>48), byte(v>>56)) 727 } 728 729 func outsend() { 730 if len(outp) >= cap(outmsg)-len(outmsg) { 731 panic_("outsend") 732 } 733 outcount := len(outp) 734 outcount -= 3 735 outp[1] = byte(outcount) 736 outp[2] = byte(outcount >> 8) 737 outmsg = outmsg[:len(outmsg)+len(outp)] 738 if !outbuffered { 739 if nw, err := os.Stdout.Write(outmsg); err != nil || nw != len(outmsg) { 740 rescue() 741 } 742 outmsg = outdata[:0] 743 return 744 } 745 } 746 747 func needoutflush() bool { 748 return len(outmsg) >= DATASIZE 749 } 750 751 func outflush() { 752 if len(outmsg) == 0 { 753 return 754 } 755 outbuffered = false 756 /* flow control */ 757 outT0(Hack) 758 waitack = true 759 for { 760 if !rcv() { 761 rescue() 762 os.Exit(1) 763 } 764 if !waitack { 765 break 766 } 767 } 768 outmsg = outdata[:0] 769 outbuffered = true 770 }