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  }