Francisco J Ballesteroszxfs put fix1449f3310 years agoHistory
454 lines · 8.5 KB · Go
RawDownload
/*	diffs command*/package main import (	"bytes"	"clive/cmd"	"clive/cmd/opt"	"clive/zx"	"errors"	"fmt") struct hash {	lines  map[string]int	hlines []string} struct file {	name  string	lines []int} const (	oEq = iota	oAdd	oDel) struct rep {	what, i, j int} struct xFiles {	rpath  string	f      [2]file	h      hash	prefix []int	suffix []int	lcs    [][]int	repc   chan rep} var (	opts         = opt.New("file [file]")	lflag, qflag bool 	errDiffs = errors.New("diffs")) func (f file) String() string {	return fmt.Sprintf("%s[%d]", f.name, len(f.lines))} func prefix(ln1, ln2 []int) ([]int, []int, []int) {	ni := len(ln1)	nj := len(ln2)	i := 0	for i < ni && i < nj {		if ln1[i] != ln2[i] {			break		}		i++	}	return ln1[:i], ln1[i:], ln2[i:]} func suffix(ln1, ln2 []int) ([]int, []int, []int) {	ni := len(ln1)	nj := len(ln2)	i := 0	for i < ni && i < nj {		if ln1[ni-i-1] != ln2[nj-i-1] {			break		}		i++	}	return ln1[ni-i:], ln1[:ni-i], ln2[:nj-i]} func (x *xFiles) addLine(ln []byte, fno int) {	switch fno {	case 0:		return	case 1, 2:		fno--		s := string(ln)		if x.h.lines == nil {			x.h.lines = map[string]int{}		}		id, ok := x.h.lines[s]		if !ok {			id = len(x.h.hlines)			x.h.lines[s] = id			x.h.hlines = append(x.h.hlines, s)		}		x.f[fno].lines = append(x.f[fno].lines, id)	}} func (x *xFiles) report(i, j int) {	ln1 := x.f[0].lines	ln2 := x.f[1].lines	if i > 0 && j > 0 && ln1[i-1] == ln2[j-1] {		x.report(i-1, j-1)		x.repc <- rep{oEq, i - 1, j - 1}	} else if j > 0 && (i == 0 || x.lcs[i][j-1] >= x.lcs[i-1][j]) {		x.report(i, j-1)		x.repc <- rep{oAdd, i, j - 1}	} else if i > 0 && (j == 0 || x.lcs[i][j-1] < x.lcs[i-1][j]) {		x.report(i-1, j)		x.repc <- rep{oDel, i - 1, j}	}} func (x *xFiles) showreport() error {	var err, oerr error	if lflag {		for _, ln := range x.prefix {			if _, oerr = cmd.Printf("  %s", x.h.hlines[ln]); oerr != nil {				err = oerr			}		}		cmd.Dprintf("---\n")	}	f1 := x.f[0]	f2 := x.f[1]	a1 := zx.Addr{Name: f1.name}	a2 := zx.Addr{Name: f2.name}	var n1, n2 int	buf := &bytes.Buffer{}	out := cmd.Out("out")	var r rep	for r = range x.repc {		switch r.what {		case oEq:			if buf.Len() > 0 {				if a1.Ln0 == 0 {					a1.Ln0 = r.i + 1 + len(x.prefix)					n1 = 1				}				if a2.Ln0 == 0 {					a2.Ln0 = r.j + 1 + len(x.prefix)					n2 = 1				}				a1.Ln1 = a1.Ln0 + n1 - 1				a2.Ln1 = a2.Ln0 + n2 - 1				out <- []byte(fmt.Sprintf("#diff\t%s\t%s\n", a1, a2)) 				if ok := out <- buf.Bytes(); !ok {					err = cerror(out)				}				buf = &bytes.Buffer{}			}			a1 = zx.Addr{Name: f1.name}			a2 = zx.Addr{Name: f2.name}			n1, n2 = 0, 0			if lflag {				_, oerr = cmd.Printf("  %s", x.h.hlines[f1.lines[r.i]])			}		case oAdd:			if a2.Ln0 == 0 {				a2.Ln0 = r.j + 1 + len(x.prefix)				n2++			} else {				n2++			}			fmt.Fprintf(buf, "+ %s", x.h.hlines[f2.lines[r.j]])			err = errDiffs		case oDel:			if a1.Ln0 == 0 {				a1.Ln0 = r.i + 1 + len(x.prefix)				n1++			} else {				n1++			}			fmt.Fprintf(buf, "- %s", x.h.hlines[f1.lines[r.i]])			err = errDiffs		}	}	if buf.Len() > 0 {		if a1.Ln0 == 0 {			a1.Ln0 = r.i + len(x.prefix)			n1 = 1		}		if a2.Ln0 == 0 {			a2.Ln0 = r.j + len(x.prefix)			n2 = 1		}		a1.Ln1 = a1.Ln0 + n1 - 1		a2.Ln1 = a2.Ln0 + n2 - 1		cmd.Printf("#diff\t%s\t%s\n", a1, a2)		if ok := out <- buf.Bytes(); !ok {			return err		}	}	if lflag {		cmd.Dprintf("---\n")		for _, ln := range x.suffix {			if _, err = cmd.Printf("  %s", x.h.hlines[ln]); err != nil {				return err			}		}	}	return err} func (x *xFiles) diff() error {	f1, f2 := x.f[0], x.f[1]	x.prefix, f1.lines, f2.lines = prefix(f1.lines, f2.lines)	x.suffix, f1.lines, f2.lines = suffix(f1.lines, f2.lines)	x.f[0], x.f[1] = f1, f2	if len(f1.lines) == 0 && len(f2.lines) == 0 {		return nil	}	if _, err := cmd.Printf("#diffs\t%s\t%s\n", f1.name, f2.name); err != nil {		return err	}	if qflag {		return errDiffs	}	ni := len(f1.lines) + 1	nj := len(f2.lines) + 1	x.lcs = make([][]int, ni)	for i := 0; i < ni; i++ {		x.lcs[i] = make([]int, nj)	}	for i := 1; i < ni; i++ {		for j := 1; j < nj; j++ {			if f1.lines[i-1] == f2.lines[j-1] {				x.lcs[i][j] = x.lcs[i-1][j-1] + 1			} else if x.lcs[i][j-1] > x.lcs[i-1][j] {				x.lcs[i][j] = x.lcs[i][j-1]			} else {				x.lcs[i][j] = x.lcs[i-1][j]			}		}	}	x.repc = make(chan rep)	rdonec := make(chan bool)	go func() {		x.report(ni-1, nj-1)		close(x.repc)		rdonec <- true	}()	err := x.showreport()	<-rdonec	return err} func (x *xFiles) getFile(in <-chan face{}, fno int) zx.Dir {	cmd.Dprintf("getfile %d\n", fno)	for m := range in {		if e, ok := m.(error); ok {			cmd.Warn("%s", e)			continue		}		if d, ok := m.(zx.Dir); ok {			return d		}		if b, ok := m.([]byte); ok {			x.addLine(b, fno)		}	}	if err := cerror(in); err != nil {		cmd.Warn("%s", err)	}	return nil} func (x *xFiles) getFiles(i1, i2 <-chan face{}) (zx.Dir, zx.Dir) {	cmd.Dprintf("getfiles\n")	doselect {	case m, ok := <-i1:		if !ok {			if err := cerror(i1); err != nil {				cmd.Warn("%s", err)			}			return nil, x.getFile(i2, 2)		}		if e, ok := m.(error); ok {			cmd.Warn("%s", e)			continue		}		if d, ok := m.(zx.Dir); ok {			return d, x.getFile(i2, 2)		}		if b, ok := m.([]byte); ok {			x.addLine(b, 1)		}	case m, ok := <-i2:		if !ok {			if err := cerror(i2); err != nil {				cmd.Warn("%s", err)			}			return x.getFile(i1, 1), nil		}		if e, ok := m.(error); ok {			cmd.Warn("%s", e)			continue		}		if d, ok := m.(zx.Dir); ok {			return x.getFile(i1, 1), d		}		if b, ok := m.([]byte); ok {			x.addLine(b, 2)		}	}} func (x *xFiles) getDir(in <-chan face{}) zx.Dir {	for m := range in {		if e, ok := m.(error); ok {			cmd.Warn("%s", e)			continue		}		if d, ok := m.(zx.Dir); ok {			return d		}	}	if err := cerror(in); err != nil {		cmd.Warn("%s", err)	}	return nil} func (x *xFiles) getDirs(i1, i2 <-chan face{}) (zx.Dir, zx.Dir) {	cmd.Dprintf("getdirs\n")	var d1, d2 zx.Dir	doselect {	case m1, ok := <-i1:		if !ok {			if err := cerror(i1); err != nil {				cmd.Warn("%s", err)			}			cmd.Dprintf("getdir 2\n")			return nil, x.getDir(i2)		}		if e, ok := m1.(error); ok {			cmd.Warn("%s", e)			continue		}		d1, ok = m1.(zx.Dir)		if !ok {			continue		}		cmd.Dprintf("getdir 2\n")		return d1, x.getDir(i2)	case m2, ok := <-i2:		if !ok {			if err := cerror(i2); err != nil {				cmd.Warn("%s", err)			}			cmd.Dprintf("getdir 1\n")			return x.getDir(i1), nil		}		if e, ok := m2.(error); ok {			cmd.Warn("%s", e)			continue		}		d2, ok = m2.(zx.Dir)		if !ok {			continue		}		cmd.Dprintf("getdir 1\n")		return x.getDir(i1), d2	}} // Run diffs in the current app context.func main() {	cmd.UnixIO("err")	c := cmd.AppCtx()	opts.NewFlag("D", "debug", &c.Debug)	opts.NewFlag("l", "long output", &lflag)	opts.NewFlag("q", "quiet output (report which files differ)", &qflag)	ux := false	opts.NewFlag("u", "use unix out", &ux)	args := opts.Parse()	if ux {		cmd.UnixIO("out")	}	var i1, i2 <-chan face{}	switch len(args) {	case 1:		i1 = cmd.In("in")		i2 = cmd.Files(args[0])	case 2:		i1 = cmd.Files(args[0])		i2 = cmd.Files(args[1])	default:		cmd.Warn("wrong number of arguments")		opts.Usage()	}	i1 = cmd.Lines(i1)	i2 = cmd.Lines(i2) 	var sts error	x := &xFiles{}	d1, d2 := x.getDirs(i1, i2)	for d1 != nil || d2 != nil {		cmd.Dprintf("loop d1 %s d2 %s\n", d1["Rpath"], d2["Rpath"])		switch cmp := zx.PathCmp(d1["Rpath"], d2["Rpath"]); {		case d2 == nil || (d1 != nil && cmp < 0):			d1["path"] = d1["Upath"]			_, err := cmd.Printf("#only %s\n", d1.Fmt())			if err != nil {				close(i1, err)				close(i2, err)				cmd.Fatal(err)			}			d1 = x.getDir(i1)			sts = errDiffs			continue		case d1 == nil || (d2 != nil && cmp > 0):			d2["path"] = d2["Upath"]			_, err := cmd.Printf("#only %s\n", d2.Fmt())			if err != nil {				close(i1, err)				close(i2, err)				cmd.Fatal(err)			}			d2 = x.getDir(i2)			sts = errDiffs			continue		default:			if d1["type"] != d2["type"] {				d1["path"] = d1["Upath"]				cmd.Printf("#only %s\n", d1.Fmt())				d2["path"] = d2["Upath"]				cmd.Printf("#only %s\n", d2.Fmt())				sts = errDiffs			}		}		o1, o2 := d1, d2		x.rpath = d1["Rpath"]		x.f[0] = file{name: d1["Upath"]}		x.f[1] = file{name: d2["Upath"]}		cmd.Dprintf("getfiles %s %s\n", x.f[0], x.f[1])		d1, d2 = x.getFiles(i1, i2)		if o1["type"] == "-" && o2["type"] == "-" {			cmd.Dprintf("run diff %s %s\n", x.f[0], x.f[1])			if err := x.diff(); err != nil {				sts = err			}			x = &xFiles{}		}	}	if sts != errDiffs {		cmd.Fatal(sts)	}	cmd.Exit(sts)}