9fans.net/go@v0.0.7/cmd/samterm/mesg.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"fmt"
     7  	"os"
     8  	"unicode/utf8"
     9  
    10  	"9fans.net/go/plumb"
    11  )
    12  
    13  const HSIZE = 3 /* Type + short count */
    14  var h Header
    15  var indata = make([]byte, 0, DATASIZE)
    16  var outdata [DATASIZE]byte
    17  var outcount int
    18  var hversion int
    19  var hostfd [2]*os.File
    20  var exiting int
    21  
    22  var rcv_state int = 0
    23  var rcv_count int = 0
    24  var rcv_errs int = 0
    25  
    26  func rcv() {
    27  	if protodebug {
    28  		print("rcv in\n")
    29  	}
    30  	for c := rcvchar(); c >= 0; c = rcvchar() {
    31  		if protodebug {
    32  			print(".")
    33  		}
    34  		switch rcv_state {
    35  		case 0:
    36  			h.typ = Hmesg(c)
    37  			rcv_state++
    38  
    39  		case 1:
    40  			h.count0 = byte(c)
    41  			rcv_state++
    42  
    43  		case 2:
    44  			h.count1 = byte(c)
    45  			rcv_count = int(h.count0) | int(h.count1)<<8
    46  			if rcv_count > DATASIZE {
    47  				rcv_errs++
    48  				if rcv_errs < 5 {
    49  					dumperrmsg(rcv_count, h.typ, int(h.count0), c)
    50  					rcv_state = 0
    51  					continue
    52  				}
    53  				fmt.Fprintf(os.Stderr, "type %d count %d\n", h.typ, rcv_count)
    54  				panic("count>DATASIZE")
    55  			}
    56  			indata = indata[:0]
    57  			if rcv_count == 0 {
    58  				inmesg(h.typ, 0)
    59  				rcv_count = 0
    60  				rcv_state = 0
    61  				continue
    62  			}
    63  			rcv_state++
    64  
    65  		case 3:
    66  			indata = append(indata, byte(c))
    67  			if len(indata) == rcv_count {
    68  				inmesg(h.typ, rcv_count)
    69  				rcv_count = 0
    70  				rcv_state = 0
    71  				continue
    72  			}
    73  		}
    74  		if protodebug {
    75  			print(":")
    76  		}
    77  	}
    78  
    79  	if protodebug {
    80  		print("rcv out\n")
    81  	}
    82  }
    83  
    84  func whichtext(tg int) *Text {
    85  	for i := range tag {
    86  		if tag[i] == tg {
    87  			return text[i]
    88  		}
    89  	}
    90  	println("TEXT")
    91  	for i := range tag {
    92  		println(tag[i], text[i], string(name[i]))
    93  	}
    94  	panic("whichtext")
    95  	// return nil
    96  }
    97  
    98  func inmesg(typ Hmesg, count int) {
    99  	m := inshort(0)
   100  	l := inlong(2)
   101  	switch typ {
   102  	case -1:
   103  		panic("rcv error")
   104  		// fallthrough
   105  	default:
   106  		fmt.Fprintf(os.Stderr, "type %d\n", typ)
   107  		panic("rcv unknown")
   108  		// fallthrough
   109  
   110  	case Hversion:
   111  		hversion = m
   112  
   113  	case Hbindname:
   114  		l := invlong(2) /* for 64-bit pointers */
   115  		i := whichmenu(m)
   116  		if i < 0 {
   117  			break
   118  		}
   119  		/* in case of a race, a bindname may already have occurred */
   120  		old := textByID[l]
   121  		t := whichtext(m)
   122  		if t == nil {
   123  			t = old
   124  		} else { /* let the old one win; clean up the new one */
   125  			for old.nwin > 0 {
   126  				closeup(&old.l[old.front])
   127  			}
   128  		}
   129  		text[i] = t
   130  		text[i].tag = m
   131  
   132  	case Hcurrent:
   133  		if whichmenu(m) < 0 {
   134  			break
   135  		}
   136  		t := whichtext(m)
   137  		isCmd := which != nil && which.text == &cmd && m != cmd.tag
   138  		if t == nil {
   139  			t = sweeptext(false, m)
   140  			if t == nil {
   141  				break
   142  			}
   143  		}
   144  		if t.l[t.front].textfn == nil {
   145  			panic("Hcurrent")
   146  		}
   147  		lp := &t.l[t.front]
   148  		if isCmd {
   149  			flupfront(lp)
   150  			flborder(lp, false)
   151  			work = lp
   152  		} else {
   153  			current(lp)
   154  		}
   155  
   156  	case Hmovname:
   157  		m := whichmenu(m)
   158  		if m < 0 {
   159  			break
   160  		}
   161  		t := text[m]
   162  		l := tag[m]
   163  		i := name[m][0]
   164  		text[m] = nil /* suppress panic in menudel */
   165  		menudel(m)
   166  		if t == &cmd {
   167  			m = 0
   168  		} else {
   169  			if len(text) > 0 && text[0] == &cmd {
   170  				m = 1
   171  			} else {
   172  				m = 0
   173  			}
   174  			for ; m < len(name); m++ {
   175  				if bytes.Compare(indata[2:], name[m][1:]) < 0 {
   176  					break
   177  				}
   178  			}
   179  		}
   180  		menuins(m, indata[2:], t, i, int(l))
   181  
   182  	case Hgrow:
   183  		if whichmenu(m) >= 0 {
   184  			hgrow(m, l, inlong(6), 1)
   185  		}
   186  
   187  	case Hnewname:
   188  		menuins(0, nil, nil, ' ', m)
   189  
   190  	case Hcheck0:
   191  		i := whichmenu(m)
   192  		if i >= 0 {
   193  			t := text[i]
   194  			if t != nil {
   195  				t.lock++
   196  			}
   197  			outTs(Tcheck, m)
   198  		}
   199  
   200  	case Hcheck:
   201  		i := whichmenu(m)
   202  		if i >= 0 {
   203  			t := text[i]
   204  			if t != nil && t.lock != 0 {
   205  				t.lock--
   206  			}
   207  			hcheck(m)
   208  		}
   209  
   210  	case Hunlock:
   211  		clrlock()
   212  
   213  	case Hdata:
   214  		if whichmenu(m) >= 0 {
   215  			l += hdata(m, l, indata[6:])
   216  		}
   217  		goto Checkscroll
   218  
   219  	case Horigin:
   220  		if whichmenu(m) >= 0 {
   221  			horigin(m, l)
   222  		}
   223  
   224  	case Hunlockfile:
   225  		if whichmenu(m) >= 0 {
   226  			t := whichtext(m)
   227  			if t.lock != 0 {
   228  				t.lock--
   229  				l = -1
   230  				goto Checkscroll
   231  			}
   232  		}
   233  
   234  	case Hsetdot:
   235  		if whichmenu(m) >= 0 {
   236  			hsetdot(m, l, inlong(6))
   237  		}
   238  
   239  	case Hgrowdata:
   240  		if whichmenu(m) < 0 {
   241  			break
   242  		}
   243  		hgrow(m, l, inlong(6), 0)
   244  		whichtext(m).lock++ /* fake the request */
   245  		l += hdata(m, l, indata[10:])
   246  		goto Checkscroll
   247  
   248  	case Hmoveto:
   249  		if whichmenu(m) >= 0 {
   250  			hmoveto(m, l)
   251  		}
   252  
   253  	case Hclean:
   254  		m := whichmenu(m)
   255  		if m >= 0 {
   256  			name[m][0] = ' '
   257  		}
   258  
   259  	case Hdirty:
   260  		m := whichmenu(m)
   261  		if m >= 0 {
   262  			name[m][0] = '\''
   263  		}
   264  
   265  	case Hdelname:
   266  		m := whichmenu(m)
   267  		if m >= 0 {
   268  			menudel(m)
   269  		}
   270  
   271  	case Hcut:
   272  		if whichmenu(m) >= 0 {
   273  			hcut(m, l, inlong(6))
   274  		}
   275  
   276  	case Hclose:
   277  		if whichmenu(m) < 0 {
   278  			break
   279  		}
   280  		t := whichtext(m)
   281  		if t == nil {
   282  			break
   283  		}
   284  		l := t.nwin
   285  		for i := 0; l > 0 && i < NL; i++ {
   286  			lp := &t.l[i]
   287  			if lp.textfn != nil {
   288  				closeup(lp)
   289  				l--
   290  			}
   291  		}
   292  
   293  	case Hsetpat:
   294  		setpat(indata)
   295  
   296  	case Hsetsnarf:
   297  		hsetsnarf(m)
   298  
   299  	case Hsnarflen:
   300  		snarflen = inlong(0)
   301  
   302  	case Hack:
   303  		outT0(Tack)
   304  
   305  	case Hexit:
   306  		exiting = 1
   307  		outT0(Texit)
   308  		os.Exit(0)
   309  
   310  	case Hplumb:
   311  		hplumb(m)
   312  	}
   313  	return
   314  
   315  Checkscroll:
   316  	if m == cmd.tag {
   317  		for i := 0; i < NL; i++ {
   318  			lp := &cmd.l[i]
   319  			if lp.textfn != nil {
   320  				p := int(l)
   321  				if p < 0 {
   322  					p = lp.p1
   323  				}
   324  				center(lp, p)
   325  			}
   326  		}
   327  	}
   328  }
   329  
   330  func setlock() {
   331  	hostlock++
   332  	cursor = &lockarrow
   333  	display.SwitchCursor(cursor)
   334  }
   335  
   336  func clrlock() {
   337  	hasunlocked = true
   338  	if hostlock > 0 {
   339  		hostlock--
   340  	}
   341  	if hostlock == 0 {
   342  		cursor = nil
   343  		display.SwitchCursor(cursor)
   344  	}
   345  }
   346  
   347  func startfile(t *Text) {
   348  	outTsv(Tstartfile, t.tag, t.id) /* for 64-bit pointers */
   349  	setlock()
   350  }
   351  
   352  func startnewfile(typ Tmesg, t *Text) {
   353  	t.tag = Untagged
   354  	outTv(typ, t.id) /* for 64-bit pointers */
   355  }
   356  
   357  func inshort(n int) int {
   358  	return int(binary.LittleEndian.Uint16(indata[n : n+2]))
   359  }
   360  
   361  func inlong(n int) int {
   362  	return int(binary.LittleEndian.Uint32(indata[n : n+4]))
   363  }
   364  
   365  func invlong(n int) int64 {
   366  	return int64(binary.LittleEndian.Uint64(indata[n : n+8]))
   367  }
   368  
   369  func outT0(typ Tmesg) {
   370  	outstart(typ)
   371  	outsend()
   372  }
   373  
   374  func outTl(typ Tmesg, l int) {
   375  	outstart(typ)
   376  	outlong(l)
   377  	outsend()
   378  }
   379  
   380  func outTs(typ Tmesg, s int) {
   381  	outstart(typ)
   382  	outshort(s)
   383  	outsend()
   384  }
   385  
   386  func outTss(typ Tmesg, s1 int, s2 int) {
   387  	outstart(typ)
   388  	outshort(s1)
   389  	outshort(s2)
   390  	outsend()
   391  }
   392  
   393  func outTsll(typ Tmesg, s1 int, l1 int, l2 int) {
   394  	outstart(typ)
   395  	outshort(s1)
   396  	outlong(l1)
   397  	outlong(l2)
   398  	outsend()
   399  }
   400  
   401  func outTsl(typ Tmesg, s1 int, l1 int) {
   402  	outstart(typ)
   403  	outshort(s1)
   404  	outlong(l1)
   405  	outsend()
   406  }
   407  
   408  func outTsv(typ Tmesg, s1 int, v1 int64) {
   409  	outstart(typ)
   410  	outshort(s1)
   411  	outvlong(v1)
   412  	outsend()
   413  }
   414  
   415  func outTv(typ Tmesg, v1 int64) {
   416  	outstart(typ)
   417  	outvlong(v1)
   418  	outsend()
   419  }
   420  
   421  func outTslS(typ Tmesg, s1 int, l1 int, s []rune) {
   422  	outstart(typ)
   423  	outshort(s1)
   424  	outlong(l1)
   425  	outrunes(s)
   426  	outsend()
   427  }
   428  
   429  func outTsls(typ Tmesg, s1 int, l1 int, s2 int) {
   430  	outstart(typ)
   431  	outshort(s1)
   432  	outlong(l1)
   433  	outshort(s2)
   434  	outsend()
   435  }
   436  
   437  func outstart(typ Tmesg) {
   438  	outdata[0] = byte(typ)
   439  	outcount = 0
   440  }
   441  
   442  func outrunes(s []rune) {
   443  	for _, r := range s {
   444  		outcount += utf8.EncodeRune(outdata[HSIZE+outcount:HSIZE+outcount+utf8.UTFMax], r)
   445  	}
   446  }
   447  
   448  func outshort(s int) {
   449  	binary.LittleEndian.PutUint16(outdata[HSIZE+outcount:HSIZE+outcount+2], uint16(s))
   450  	outcount += 2
   451  }
   452  
   453  func outlong(l int) {
   454  	binary.LittleEndian.PutUint32(outdata[HSIZE+outcount:HSIZE+outcount+4], uint32(l))
   455  	outcount += 4
   456  }
   457  
   458  func outvlong(v int64) {
   459  	binary.LittleEndian.PutUint64(outdata[HSIZE+outcount:HSIZE+outcount+8], uint64(v))
   460  	outcount += 8
   461  }
   462  
   463  func outsend() {
   464  	if outcount > DATASIZE-HSIZE {
   465  		panic("outcount>sizeof outdata")
   466  	}
   467  	outdata[1] = uint8(outcount)
   468  	outdata[2] = uint8(outcount >> 8)
   469  	if n, err := hostfd[1].Write(outdata[:outcount+HSIZE]); n != int(outcount+HSIZE) {
   470  		panic("write error: " + err.Error())
   471  	}
   472  }
   473  
   474  func hsetdot(m int, p0 int, p1 int) {
   475  	t := whichtext(m)
   476  	l := &t.l[t.front]
   477  
   478  	flushtyping(true)
   479  	flsetselect(l, p0, p1)
   480  }
   481  
   482  func horigin(m int, p0 int) {
   483  	t := whichtext(m)
   484  	l := &t.l[t.front]
   485  	if !flprepare(l) {
   486  		l.origin = p0
   487  		return
   488  	}
   489  	a := p0 - l.origin
   490  	if a >= 0 && a < l.f.NumChars {
   491  		l.f.Delete(0, a)
   492  	} else if a < 0 && -a < l.f.NumChars {
   493  		rp := rload(&t.rasp, p0, l.origin)
   494  		l.f.Insert(rp, 0)
   495  	} else {
   496  		l.f.Delete(0, l.f.NumChars)
   497  	}
   498  	l.origin = p0
   499  	scrdraw(l, t.rasp.nrunes)
   500  	if l.visible == Some {
   501  		flrefresh(l, l.entire, 0)
   502  	}
   503  	hcheck(m)
   504  }
   505  
   506  func hmoveto(m int, p0 int) {
   507  	t := whichtext(m)
   508  	l := &t.l[t.front]
   509  
   510  	if p0 < l.origin || p0-l.origin > l.f.NumChars*9/10 {
   511  		outTsll(Torigin, m, p0, 2)
   512  	}
   513  }
   514  
   515  func hcheck(m int) {
   516  	reqd := false
   517  	if m == Untagged {
   518  		return
   519  	}
   520  	t := whichtext(m)
   521  	if t == nil { /* possible in a half-built window */
   522  		return
   523  	}
   524  	for i := 0; i < NL; i++ {
   525  		l := &t.l[i]
   526  		if l.textfn == nil || !flprepare(l) {
   527  			/* BUG: don't need this if BUG below is fixed */
   528  			// TODO(rsc): What BUG?
   529  			continue
   530  		}
   531  		a := t.l[i].origin
   532  		n := rcontig(&t.rasp, a, a+l.f.NumChars, true)
   533  		if n < l.f.NumChars { /* text missing in middle of screen */
   534  			a += n
   535  		} else { /* text missing at end of screen? */
   536  		Again:
   537  			if l.f.LastLineFull {
   538  				goto Checksel /* all's well */
   539  			}
   540  			a = t.l[i].origin + l.f.NumChars
   541  			n = t.rasp.nrunes - a
   542  			if n == 0 {
   543  				goto Checksel
   544  			}
   545  			if n > TBLOCKSIZE {
   546  				n = TBLOCKSIZE
   547  			}
   548  			n = rcontig(&t.rasp, a, a+n, true)
   549  			if n > 0 {
   550  				rp := rload(&t.rasp, a, a+n)
   551  				nl := l.f.NumChars
   552  				flinsert(l, rp, l.origin+nl)
   553  				if nl == l.f.NumChars { /* made no progress */
   554  					goto Checksel
   555  				}
   556  				goto Again
   557  			}
   558  		}
   559  		if !reqd {
   560  			n = rcontig(&t.rasp, a, a+TBLOCKSIZE, false)
   561  			if n <= 0 {
   562  				panic("hcheck request==0")
   563  			}
   564  			outTsls(Trequest, m, a, int(n))
   565  			outTs(Tcheck, m)
   566  			t.lock++ /* for the Trequest */
   567  			t.lock++ /* for the Tcheck */
   568  			reqd = true
   569  		}
   570  	Checksel:
   571  		flsetselect(l, l.p0, l.p1)
   572  	}
   573  }
   574  
   575  func flnewlyvisible(l *Flayer) {
   576  	hcheck(l.text.tag)
   577  }
   578  
   579  func hsetsnarf(nc int) {
   580  	display.SwitchCursor(&deadmouse)
   581  	osnarf := make([]byte, nc)
   582  	for i := range osnarf {
   583  		osnarf[i] = byte(getch())
   584  	}
   585  	nsnarf := snarfswap(osnarf)
   586  	if nsnarf != nil {
   587  		if len(nsnarf) > SNARFSIZE {
   588  			nsnarf = []byte("<snarf too long>")
   589  		}
   590  		snarflen = len(nsnarf)
   591  		outTs(Tsetsnarf, len(nsnarf))
   592  		if len(nsnarf) > 0 {
   593  			if n, err := hostfd[1].Write(nsnarf); n != len(nsnarf) {
   594  				panic("snarf write error: " + err.Error())
   595  			}
   596  		}
   597  	} else {
   598  		outTs(Tsetsnarf, 0)
   599  	}
   600  	display.SwitchCursor(cursor)
   601  }
   602  
   603  func hplumb(nc int) {
   604  	s := make([]byte, nc)
   605  	for i := range s {
   606  		s[i] = byte(getch())
   607  	}
   608  	if plumbfd != nil {
   609  		m := new(plumb.Message)
   610  		if err := m.Recv(bytes.NewReader(s)); err == nil {
   611  			m.Send(plumbfd)
   612  		}
   613  	}
   614  }
   615  
   616  func hgrow(m int, a int, new int, req int) {
   617  	if new <= 0 {
   618  		panic("hgrow")
   619  	}
   620  	t := whichtext(m)
   621  	rresize(&t.rasp, a, 0, new)
   622  	for i := 0; i < NL; i++ {
   623  		l := &t.l[i]
   624  		if l.textfn == nil {
   625  			continue
   626  		}
   627  		o := l.origin
   628  		b := a - o - rmissing(&t.rasp, o, a)
   629  		if a < o {
   630  			l.origin += new
   631  		}
   632  		if a < l.p0 {
   633  			l.p0 += new
   634  		}
   635  		if a < l.p1 {
   636  			l.p1 += new
   637  		}
   638  		/* must prevent b temporarily becoming unsigned */
   639  		if req == 0 || a < o || (b > 0 && b > l.f.NumChars) || (l.f.NumChars == 0 && a-o > 0) {
   640  			continue
   641  		}
   642  		if new > TBLOCKSIZE {
   643  			new = TBLOCKSIZE
   644  		}
   645  		outTsls(Trequest, m, a, new)
   646  		t.lock++
   647  		req = 0
   648  	}
   649  }
   650  
   651  func hdata1(t *Text, a int, rp []rune) int {
   652  	for i := 0; i < NL; i++ {
   653  		l := &t.l[i]
   654  		if l.textfn == nil {
   655  			continue
   656  		}
   657  		o := l.origin
   658  		b := a - o - rmissing(&t.rasp, o, a)
   659  		/* must prevent b temporarily becoming unsigned */
   660  		if a < o || (b > 0 && b > l.f.NumChars) {
   661  			continue
   662  		}
   663  		flinsert(l, rp, o+b)
   664  	}
   665  	rdata(&t.rasp, a, a+len(rp), rp)
   666  	rclean(&t.rasp)
   667  	return len(rp)
   668  }
   669  
   670  func hdata(m int, a int, s []byte) int {
   671  	t := whichtext(m)
   672  	if t.lock != 0 {
   673  		t.lock--
   674  	}
   675  	if len(s) == 0 {
   676  		return 0
   677  	}
   678  	r := []rune(string(s))
   679  	return hdata1(t, a, r)
   680  }
   681  
   682  func hdatarune(m int, a int, rp []rune) int {
   683  	t := whichtext(m)
   684  	if t.lock != 0 {
   685  		t.lock--
   686  	}
   687  	if len(rp) == 0 {
   688  		return 0
   689  	}
   690  	return hdata1(t, a, rp)
   691  }
   692  
   693  func hcut(m, a, old int) {
   694  	t := whichtext(m)
   695  	if t.lock != 0 {
   696  		t.lock--
   697  	}
   698  	for i := 0; i < NL; i++ {
   699  		l := &t.l[i]
   700  		if l.textfn == nil {
   701  			continue
   702  		}
   703  		o := l.origin
   704  		b := a - o - rmissing(&t.rasp, o, a)
   705  		/* must prevent b temporarily becoming unsigned */
   706  		if (b < 0 || b < l.f.NumChars) && a+old >= o {
   707  			p := o
   708  			if b >= 0 {
   709  				p += b
   710  			}
   711  			fldelete(l, p, a+old-rmissing(&t.rasp, o, a+old))
   712  		}
   713  		if a+old < o {
   714  			l.origin -= old
   715  		} else if a <= o {
   716  			l.origin = a
   717  		}
   718  		if a+old < l.p0 {
   719  			l.p0 -= old
   720  		} else if a <= l.p0 {
   721  			l.p0 = a
   722  		}
   723  		if a+old < l.p1 {
   724  			l.p1 -= old
   725  		} else if a <= l.p1 {
   726  			l.p1 = a
   727  		}
   728  	}
   729  	rresize(&t.rasp, a, old, 0)
   730  	rclean(&t.rasp)
   731  }