9fans.net/go@v0.0.7/cmd/acme/internal/dump/rows1.go (about)

     1  // #include <u.h>
     2  // #include <libc.h>
     3  // #include <draw.h>
     4  // #include <thread.h>
     5  // #include <cursor.h>
     6  // #include <mouse.h>
     7  // #include <keyboard.h>
     8  // #include <frame.h>
     9  // #include <fcall.h>
    10  // #include <bio.h>
    11  // #include <plumb.h>
    12  // #include <libsec.h>
    13  // #include "dat.h"
    14  // #include "fns.h"
    15  
    16  package dump
    17  
    18  import (
    19  	"bufio"
    20  	"fmt"
    21  	"io/ioutil"
    22  	"os"
    23  	"strconv"
    24  	"unicode/utf8"
    25  
    26  	"9fans.net/go/cmd/acme/internal/adraw"
    27  	"9fans.net/go/cmd/acme/internal/alog"
    28  	"9fans.net/go/cmd/acme/internal/bufs"
    29  	"9fans.net/go/cmd/acme/internal/fileload"
    30  	"9fans.net/go/cmd/acme/internal/ui"
    31  	"9fans.net/go/cmd/acme/internal/util"
    32  	"9fans.net/go/cmd/acme/internal/wind"
    33  	"9fans.net/go/draw"
    34  )
    35  
    36  var Get = func(*wind.Text) {}
    37  var Run = func(string, []rune) {}
    38  var Home = ""
    39  var OnNewWindow = func(*wind.Window) {}
    40  
    41  func Dump(row *wind.Row, file *string) {
    42  	if len(row.Col) == 0 {
    43  		return
    44  	}
    45  	// defer fbuffree(buf)
    46  	if file == nil {
    47  		if Home == "" {
    48  			alog.Printf("can't find file for dump: $home not defined\n")
    49  			return
    50  		}
    51  		s := fmt.Sprintf("%s/acme.dump", Home)
    52  		file = &s
    53  	}
    54  	f, err := os.Create(*file)
    55  	if err != nil {
    56  		alog.Printf("can't open %s: %v\n", *file, err)
    57  		return
    58  	}
    59  	b := bufio.NewWriter(f)
    60  	r := bufs.AllocRunes()
    61  	fmt.Fprintf(b, "%s\n", ui.Wdir)
    62  	fmt.Fprintf(b, "%s\n", adraw.FontNames[0])
    63  	fmt.Fprintf(b, "%s\n", adraw.FontNames[1])
    64  	var i int
    65  	var c *wind.Column
    66  	for i = 0; i < len(row.Col); i++ {
    67  		c = row.Col[i]
    68  		fmt.Fprintf(b, "%11.7f", 100.0*float64(c.R.Min.X-row.R.Min.X)/float64(row.R.Dx()))
    69  		if i == len(row.Col)-1 {
    70  			b.WriteByte('\n')
    71  		} else {
    72  			b.WriteByte(' ')
    73  		}
    74  	}
    75  	dumpid := make(map[*wind.File]int)
    76  	m := util.Min(bufs.RuneLen, row.Tag.Len())
    77  	row.Tag.File.Read(0, r[:m])
    78  	n := 0
    79  	for n < m && r[n] != '\n' {
    80  		n++
    81  	}
    82  	fmt.Fprintf(b, "w %s\n", string(r[:n]))
    83  	for i = 0; i < len(row.Col); i++ {
    84  		c = row.Col[i]
    85  		m = util.Min(bufs.RuneLen, c.Tag.Len())
    86  		c.Tag.File.Read(0, r[:m])
    87  		n = 0
    88  		for n < m && r[n] != '\n' {
    89  			n++
    90  		}
    91  		fmt.Fprintf(b, "c%11d %s\n", i, string(r[:n]))
    92  	}
    93  	for i, c := range row.Col {
    94  	Windows:
    95  		for j, w := range c.W {
    96  			wind.Wincommit(w, &w.Tag)
    97  			t := &w.Body
    98  			// windows owned by others get special treatment
    99  			if w.External {
   100  				if w.Dumpstr == "" {
   101  					continue
   102  				}
   103  			}
   104  			// zeroxes of external windows are tossed
   105  			if len(t.File.Text) > 1 {
   106  				for n = 0; n < len(t.File.Text); n++ {
   107  					w1 := t.File.Text[n].W
   108  					if w == w1 {
   109  						continue
   110  					}
   111  					if w1.External {
   112  						continue Windows
   113  					}
   114  				}
   115  			}
   116  			fontname := ""
   117  			if t.Reffont.F != adraw.Font {
   118  				fontname = t.Reffont.F.Name
   119  			}
   120  			var a string
   121  			if len(t.File.Name()) != 0 {
   122  				a = string(t.File.Name())
   123  			}
   124  			var dumped bool
   125  			if dumpid[t.File] != 0 {
   126  				dumped = false
   127  				fmt.Fprintf(b, "x%11d %11d %11d %11d %11.7f %s\n", i, dumpid[t.File], w.Body.Q0, w.Body.Q1, 100.0*float64(w.R.Min.Y-c.R.Min.Y)/float64(c.R.Dy()), fontname)
   128  			} else if w.Dumpstr != "" {
   129  				dumped = false
   130  				fmt.Fprintf(b, "e%11d %11d %11d %11d %11.7f %s\n", i, 0, 0, 0, 100.0*float64(w.R.Min.Y-c.R.Min.Y)/float64(c.R.Dy()), fontname)
   131  			} else if (!w.Dirty && exists(a)) || w.IsDir {
   132  				dumped = false
   133  				dumpid[t.File] = w.ID
   134  				fmt.Fprintf(b, "f%11d %11d %11d %11d %11.7f %s\n", i, w.ID, w.Body.Q0, w.Body.Q1, 100.0*float64(w.R.Min.Y-c.R.Min.Y)/float64(c.R.Dy()), fontname)
   135  			} else {
   136  				dumped = true
   137  				dumpid[t.File] = w.ID
   138  				fmt.Fprintf(b, "F%11d %11d %11d %11d %11.7f %11d %s\n", i, j, w.Body.Q0, w.Body.Q1, 100.0*float64(w.R.Min.Y-c.R.Min.Y)/float64(c.R.Dy()), w.Body.Len(), fontname)
   139  			}
   140  			b.WriteString(wind.Winctlprint(w, false))
   141  			m = util.Min(bufs.RuneLen, w.Tag.Len())
   142  			w.Tag.File.Read(0, r[:m])
   143  			if !containsRune(r[:m], '|') {
   144  				alog.Printf("dump: window %d has no | in tag %q!", w.ID, string(r[:m]))
   145  			}
   146  			n = 0
   147  			for n < m {
   148  				start := n
   149  				for n < m && r[n] != '\n' {
   150  					n++
   151  				}
   152  				fmt.Fprintf(b, "%s", string(r[start:n]))
   153  				if n < m {
   154  					b.WriteByte(0xff) // \n in tag becomes 0xff byte (invalid UTF)
   155  					n++
   156  				}
   157  			}
   158  			fmt.Fprintf(b, "\n")
   159  			if dumped {
   160  				q0 := 0
   161  				q1 := t.Len()
   162  				for q0 < q1 {
   163  					n = q1 - q0
   164  					if n > bufs.Len/utf8.UTFMax {
   165  						n = bufs.Len / utf8.UTFMax
   166  					}
   167  					t.File.Read(q0, r[:n])
   168  					fmt.Fprintf(b, "%s", string(r[:n]))
   169  					q0 += n
   170  				}
   171  			}
   172  			if w.Dumpstr != "" {
   173  				if w.Dumpdir != "" {
   174  					fmt.Fprintf(b, "%s\n%s\n", w.Dumpdir, w.Dumpstr)
   175  				} else {
   176  					fmt.Fprintf(b, "\n%s\n", w.Dumpstr)
   177  				}
   178  			}
   179  		}
   180  	}
   181  	b.Flush() // TODO(rsc): err check
   182  	f.Close() // TODO(rsc): err check
   183  	bufs.FreeRunes(r)
   184  }
   185  
   186  func containsRune(r []rune, c rune) bool {
   187  	for _, rc := range r {
   188  		if rc == c {
   189  			return true
   190  		}
   191  	}
   192  	return false
   193  }
   194  
   195  func exists(file string) bool {
   196  	_, err := os.Stat(file)
   197  	return err == nil
   198  }
   199  
   200  func rdline(b *bufio.Reader, linep *int) (string, error) {
   201  	l, err := b.ReadString('\n')
   202  	if err == nil {
   203  		(*linep)++
   204  	}
   205  	return l, err
   206  }
   207  
   208  /*
   209   * Get font names from load file so we don't load fonts we won't use
   210   */
   211  func LoadFonts(file string) {
   212  	f, err := os.Open(file)
   213  	if err != nil {
   214  		return
   215  	}
   216  	defer f.Close()
   217  	b := bufio.NewReader(f)
   218  	// current directory
   219  	_, err = b.ReadString('\n')
   220  	if err != nil {
   221  		return
   222  	}
   223  	// global fonts
   224  	for i := 0; i < 2; i++ {
   225  		l, err := b.ReadString('\n')
   226  		if err != nil {
   227  			return
   228  		}
   229  		l = l[:len(l)-1]
   230  		if l != "" && l != adraw.FontNames[i] {
   231  			adraw.FontNames[i] = l
   232  		}
   233  	}
   234  }
   235  
   236  func Load(row *wind.Row, file *string, initing bool) bool {
   237  	if file == nil {
   238  		if Home == "" {
   239  			alog.Printf("can't find file for load: $home not defined\n")
   240  			return false
   241  		}
   242  		s := fmt.Sprintf("%s/acme.dump", Home)
   243  		file = &s
   244  	}
   245  	f, err := os.Open(*file)
   246  	if err != nil {
   247  		alog.Printf("can't open load file %s: %v\n", *file, err)
   248  		return false
   249  	}
   250  	defer f.Close()
   251  	b := bufio.NewReader(f)
   252  
   253  	// current directory
   254  	line := 0
   255  	bad := func() bool {
   256  		alog.Printf("bad load file %s:%d\n", *file, line)
   257  		return false
   258  	}
   259  	l, err := rdline(b, &line)
   260  	if err != nil {
   261  		return bad()
   262  	}
   263  	l = l[:len(l)-1]
   264  	if err := os.Chdir(l); err != nil {
   265  		alog.Printf("can't chdir %s\n", l)
   266  		return bad()
   267  	}
   268  
   269  	// global fonts
   270  	var i int
   271  	for i = 0; i < 2; i++ {
   272  		l, err := rdline(b, &line)
   273  		if err != nil {
   274  			return bad()
   275  		}
   276  		l = l[:len(l)-1]
   277  		if l != "" && l != adraw.FontNames[i] {
   278  			adraw.FindFont(i != 0, true, i == 0 && initing, l)
   279  		}
   280  	}
   281  	if initing && len(row.Col) == 0 {
   282  		wind.RowInit(row, adraw.Display.ScreenImage.Clipr)
   283  	}
   284  	l, err = rdline(b, &line)
   285  	if err != nil {
   286  		return bad()
   287  	}
   288  	j := len(l) / 12
   289  	if j <= 0 || j > 10 {
   290  		return bad()
   291  	}
   292  	var percent float64
   293  	for i = 0; i < j; i++ {
   294  		percent = atof(l[i*12 : (i+1)*12])
   295  		if percent < 0 || percent >= 100 {
   296  			return bad()
   297  		}
   298  		x := row.R.Min.X + int(percent*float64(row.R.Dx())/100+0.5)
   299  		if i < len(row.Col) {
   300  			if i == 0 {
   301  				continue
   302  			}
   303  			c1 := row.Col[i-1]
   304  			c2 := row.Col[i]
   305  			r1 := c1.R
   306  			r2 := c2.R
   307  			if x < adraw.Border() {
   308  				x = adraw.Border()
   309  			}
   310  			r1.Max.X = x - adraw.Border()
   311  			r2.Min.X = x
   312  			if r1.Dx() < 50 || r2.Dx() < 50 {
   313  				continue
   314  			}
   315  			adraw.Display.ScreenImage.Draw(draw.Rpt(r1.Min, r2.Max), adraw.Display.White, nil, draw.ZP)
   316  			wind.Colresize(c1, r1)
   317  			wind.Colresize(c2, r2)
   318  			r2.Min.X = x - adraw.Border()
   319  			r2.Max.X = x
   320  			adraw.Display.ScreenImage.Draw(r2, adraw.Display.Black, nil, draw.ZP)
   321  		}
   322  		if i >= len(row.Col) {
   323  			wind.RowAdd(row, nil, x)
   324  		}
   325  	}
   326  	var n int
   327  	var ns int
   328  	var r []rune
   329  	hdrdone := false
   330  	byDumpID := make(map[int]*wind.Window)
   331  	for {
   332  		l, err = rdline(b, &line)
   333  		if err != nil {
   334  			break
   335  		}
   336  		if !hdrdone {
   337  			switch l[0] {
   338  			case 'c':
   339  				l = l[:len(l)-1]
   340  				i = atoi(l[1:12])
   341  				r = []rune(l[1*12:])
   342  				ns = -1
   343  				for n = 0; n < len(r); n++ {
   344  					if r[n] == '/' {
   345  						ns = n
   346  					}
   347  					if r[n] == ' ' {
   348  						break
   349  					}
   350  				}
   351  				wind.Textdelete(&row.Col[i].Tag, 0, row.Col[i].Tag.Len(), true)
   352  				wind.Textinsert(&row.Col[i].Tag, 0, r[n+1:], true)
   353  				continue
   354  			case 'w':
   355  				l = l[:len(l)-1]
   356  				r = []rune(l[2:])
   357  				ns = -1
   358  				for n = 0; n < len(r); n++ {
   359  					if r[n] == '/' {
   360  						ns = n
   361  					}
   362  					if r[n] == ' ' {
   363  						break
   364  					}
   365  				}
   366  				wind.Textdelete(&row.Tag, 0, row.Tag.Len(), true)
   367  				wind.Textinsert(&row.Tag, 0, r, true)
   368  				continue
   369  			}
   370  			hdrdone = true
   371  		}
   372  		dumpid := 0
   373  		var fontname string
   374  		var ndumped int
   375  		switch l[0] {
   376  		case 'e':
   377  			if len(l) < 1+5*12+1 {
   378  				return bad()
   379  			}
   380  			l, err = rdline(b, &line) // ctl line; ignored
   381  			if err != nil {
   382  				return bad()
   383  			}
   384  			l, err = rdline(b, &line) // directory
   385  			if err != nil {
   386  				return bad()
   387  			}
   388  			l = l[:len(l)-1]
   389  			if len(l) == 0 {
   390  				if Home == "" {
   391  					r = []rune("./")
   392  				} else {
   393  					r = []rune(Home + "/")
   394  				}
   395  			} else {
   396  				r = []rune(l)
   397  			}
   398  			l, err = rdline(b, &line) // command
   399  			if err != nil {
   400  				return bad()
   401  			}
   402  			Run(l, r)
   403  			continue
   404  		case 'f':
   405  			if len(l) < 1+5*12+1 {
   406  				return bad()
   407  			}
   408  			l = l[:len(l)-1]
   409  			fontname = l[1+5*12:]
   410  			ndumped = -1
   411  		case 'F':
   412  			if len(l) < 1+6*12+1 {
   413  				return bad()
   414  			}
   415  			l = l[:len(l)-1]
   416  			fontname = l[1+6*12:]
   417  			ndumped = atoi(l[1+5*12+1:])
   418  		case 'x':
   419  			if len(l) < 1+5*12+1 {
   420  				return bad()
   421  			}
   422  			l = l[:len(l)-1]
   423  			fontname = l[1+5*12:]
   424  			ndumped = -1
   425  			dumpid = atoi(l[1+1*12:])
   426  		default:
   427  			return bad()
   428  		}
   429  		var fontr []rune
   430  		if fontname != "" {
   431  			fontr = []rune(fontname)
   432  		}
   433  		i = atoi(l[1+0*12:])
   434  		j = atoi(l[1+1*12:])
   435  		q0 := atoi(l[1+2*12:])
   436  		q1 := atoi(l[1+3*12:])
   437  		percent = atof(l[1+4*12:])
   438  		if i < 0 || i > 10 {
   439  			return bad()
   440  		}
   441  		if i > len(row.Col) {
   442  			i = len(row.Col)
   443  		}
   444  		c := row.Col[i]
   445  		y := c.R.Min.Y + int((percent*float64(c.R.Dy()))/100+0.5)
   446  		if y < c.R.Min.Y || y >= c.R.Max.Y {
   447  			y = -1
   448  		}
   449  		var w *wind.Window
   450  		if dumpid == 0 {
   451  			w = wind.Coladd(c, nil, nil, y)
   452  		} else {
   453  			w = wind.Coladd(c, nil, byDumpID[dumpid], y)
   454  		}
   455  		if w == nil {
   456  			continue
   457  		}
   458  		byDumpID[j] = w
   459  		l, err = rdline(b, &line)
   460  		if err != nil {
   461  			return bad()
   462  		}
   463  		l = l[:len(l)-1]
   464  		// convert 0xff in multiline tag back to \n
   465  		lb := []byte(l)
   466  		for i = 0; i < len(lb); i++ {
   467  			if lb[i] == 0xff {
   468  				lb[i] = '\n'
   469  			}
   470  		}
   471  		l = string(lb)
   472  		r = []rune(l[5*12:])
   473  		ns = -1
   474  		for n = 0; n < len(r); n++ {
   475  			if r[n] == '/' {
   476  				ns = n
   477  			}
   478  			if r[n] == ' ' {
   479  				break
   480  			}
   481  		}
   482  		if dumpid == 0 {
   483  			wind.Winsetname(w, r[:n])
   484  		}
   485  		for ; n < len(r); n++ {
   486  			if r[n] == '|' {
   487  				break
   488  			}
   489  		}
   490  		wind.Wincleartatg(w)
   491  		if n < len(r) {
   492  			wind.Textinsert(&w.Tag, w.Tag.Len(), r[n+1:], true)
   493  		} else {
   494  			alog.Printf("load: found window tag with no | character (tag: %q)", string(r))
   495  		}
   496  		if ndumped >= 0 {
   497  			// simplest thing is to put it in a file and load that
   498  			f, err := ioutil.TempFile("", fmt.Sprintf("acme.%d.*", os.Getpid()))
   499  			if err != nil {
   500  				alog.Printf("can't create temp file: %v\n", err)
   501  				return bad()
   502  			}
   503  			defer f.Close()
   504  			bout := bufio.NewWriter(f)
   505  			for n = 0; n < ndumped; n++ {
   506  				ch, _, err := b.ReadRune()
   507  				if err != nil {
   508  					return bad()
   509  				}
   510  				bout.WriteRune(ch)
   511  			}
   512  			if err := bout.Flush(); err != nil {
   513  				return bad()
   514  			}
   515  			tmp := f.Name()
   516  			if err := f.Close(); err != nil {
   517  				return bad()
   518  			}
   519  			fileload.Textload(&w.Body, 0, tmp, true)
   520  			os.Remove(tmp)
   521  			w.Body.File.SetMod(true)
   522  			for n = 0; n < len(w.Body.File.Text); n++ {
   523  				w.Body.File.Text[n].W.Dirty = true
   524  			}
   525  			wind.Winsettag(w)
   526  		} else if dumpid == 0 && r[ns+1] != '+' && r[ns+1] != '-' {
   527  			Get(&w.Body)
   528  		}
   529  		if fontr != nil {
   530  			fmt.Fprintf(os.Stderr, "FONTR %q\n", fontr)
   531  			ui.Fontx(&w.Body, nil, nil, false, false, fontr)
   532  		}
   533  		if q0 > w.Body.Len() || q1 > w.Body.Len() || q0 > q1 {
   534  			q1 = 0
   535  			q0 = q1
   536  		}
   537  		wind.Textshow(&w.Body, q0, q1, true)
   538  		w.Maxlines = util.Min(w.Body.Fr.NumLines, util.Max(w.Maxlines, w.Body.Fr.MaxLines))
   539  		OnNewWindow(w)
   540  	}
   541  	return true
   542  }
   543  
   544  func atoi(s string) int {
   545  	for s != "" && s[0] == ' ' {
   546  		s = s[1:]
   547  	}
   548  	v := 0
   549  	for i := 0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ {
   550  		v = v*10 + int(s[i]-'0')
   551  	}
   552  	return v
   553  }
   554  
   555  func atof(s string) float64 {
   556  	for s != "" && s[0] == ' ' {
   557  		s = s[1:]
   558  	}
   559  	i := 0
   560  	for i < len(s) && ('0' <= s[i] && s[i] <= '9' || s[i] == '.') {
   561  		i++
   562  	}
   563  	f, err := strconv.ParseFloat(s[:i], 64)
   564  	if err != nil {
   565  		f = 0
   566  	}
   567  	return f
   568  }