Пример #1
0
func main() {
	c := cmd.AppCtx()
	opts.NewFlag("D", "debug", &c.Debug)
	cmd.UnixIO("err")
	args := opts.Parse()
	if len(args) == 1 && ismode(args[0]) {
		args = append([]string{"mode"}, args...)
	}
	if len(args) == 0 || len(args)%2 != 0 {
		cmd.Warn("wrong number of arguments")
		opts.Usage()
	}
	nd := chd(args...)

	in := cmd.In("in")
	var err error
	for m := range in {
		switch d := m.(type) {
		case zx.Dir:
			cmd.Dprintf("got %T %s\n", d, d["Upath"])
			if cerr := ch(d, nd); cerr != nil {
				cmd.Warn("%s", cerr)
				err = cerr
			}
		default:
			// ignored
			cmd.Dprintf("got %T\n", m)
		}
	}
	if err != nil {
		cmd.Exit(err)
	}
}
Пример #2
0
func ignored(year, day string) bool {
	if lastyear != "" && strings.Split(year, ".")[0] > lastyear {
		cmd.Dprintf("ignored %s\n", year)
		return true
	}
	if day != "" && lastday != "" && strings.Split(day, ".")[0] > lastday {
		cmd.Dprintf("ignored %s %s\n", year, day)
		return true
	}
	return false
}
Пример #3
0
func main() {
	cmd.UnixIO("err")
	c := cmd.AppCtx()
	opts.NewFlag("D", "debug", &c.Debug)
	opts.NewFlag("u", "unix IO", &ux)
	opts.NewFlag("g", "get contents", &gflag)
	if cmd.Args()[0] == "gf" {
		gflag = true
	}
	args := opts.Parse()
	if ux {
		cmd.UnixIO()
	}
	if len(args) == 0 {
		args = append(args, ".,1")
	}

	var dc <-chan face{}
	if !gflag {
		dc = cmd.Dirs(args...)
	} else {
		dc = cmd.Files(args...)
	}

	out := cmd.Out("out")
	var err error
	for m := range dc {
		cmd.Dprintf("got %T\n", m)
		switch m := m.(type) {
		case error:
			err = m
			cmd.Warn("%s", m)
			if !ux {
				m := fmt.Errorf("%s: %s", cmd.Args()[0], m)
				if ok := out <- m; !ok {
					close(dc, cerror(out))
				}
			}
		case zx.Dir:
			if !ux {
				if ok := out <- m; !ok {
					close(dc, cerror(out))
				}
			} else {
				printf("%s\n", m.Fmt())
			}
		case []byte:
			if ok := out <- m; !ok {
				close(dc, cerror(out))
			}
		}
	}
	if err := cerror(dc); err != nil {
		if !ux {
			out <- fmt.Errorf("%s: %s", cmd.Args()[0], err)
		}
		cmd.Exit(err)
	}
	cmd.Exit(err)
}
Пример #4
0
func edit(t *ink.Txt) {
	in := t.Events()
	for ev := range in {
		cmd.Warn("got text: %v", ev.Args)
		switch ev.Args[0] {
		case "start":
			continue
			// Example: keep only a single view
			vs := t.Views()
			for _, v := range vs {
				if v != ev.Src {
					t.CloseView(v)
				}
			}
			// Example: do some edits from the program.
			go edits(t)
		case "tag":
			if len(ev.Args) == 1 || ev.Args[1] != "Del" {
				continue
			}
			t.Close()
		case "end":
			// Example: delete the text when all views are gone
			vs := t.Views()
			cmd.Dprintf("views %v\n", t.Views())
			if len(vs) == 0 {
				t.Close()
				return
			}
		}
	}
}
Пример #5
0
func mv1(src, dst zx.Dir) error {
	cmd.VWarn("%s %s", src["Upath"], dst["Upath"])
	cmd.Dprintf("mv1: %s %s %s %s\n", src.SAddr(), src["Rpath"], dst.SAddr(), dst["Rpath"])
	if dry {
		return nil
	}
	return cmd.Move(src["path"], dst["path"])
}
Пример #6
0
func TestBib2ref(t *testing.T) {
	c := cmd.AppCtx()
	c.Debug = testing.Verbose()
	lnc := b2s(cmd.ByteLines(cmd.Get("/zx/lib/bib/zx.bib", 0, -1)))
	rc := bib2ref(lnc)
	for ln := range rc {
		cmd.Dprintf("%s", ln)
	}
}
Пример #7
0
func runFiles(fn func(nm string, c chan []byte, dc chan bool)) {
	var lnc chan []byte
	var dc chan bool
	in := cmd.Lines(cmd.In("in"))
	out := cmd.Out("out")
	nm := "in"
	for m := range in {
		cmd.Dprintf("got %T\n", m)
		switch m := m.(type) {
		case zx.Dir:
			if pflag {
				p := m["Upath"]
				if p == "" {
					p = m["path"]
				}
				nm = p
			}
			if dc != nil {
				close(lnc)
				<-dc
			}
			dc = nil
			lnc = nil
			if ok := out <- m; !ok {
				close(in, cerror(out))
			}
		case []byte:
			if dc == nil {
				lnc = make(chan []byte)
				dc = make(chan bool, 1)
				go fn(nm, lnc, dc)
			}
			if ok := lnc <- m; !ok {
				close(in, cerror(lnc))
				close(out, cerror(lnc))
			}
		default:
			if dc != nil {
				close(lnc)
				<-dc
			}
			dc = nil
			lnc = nil
			if ok := out <- m; !ok {
				close(lnc, cerror(out))
				close(in, cerror(out))
			}
		}
	}
	if dc != nil {
		close(lnc, cerror(in))
		<-dc
	}
	cmd.Exit(cerror(in))
}
Пример #8
0
// Run print lines in the current app context.
func main() {
	c := cmd.AppCtx()
	cmd.UnixIO("err")
	opts.NewFlag("D", "debug", &c.Debug)
	opts.NewFlag("1", "collect all files (not one msg per file)", &one)
	ux := false
	opts.NewFlag("u", "use unix out", &ux)
	args := opts.Parse()
	if ux {
		cmd.UnixIO("out")
	}
	if len(args) != 0 {
		cmd.SetIn("in", cmd.Files(args...))
	}
	buf := &bytes.Buffer{}
	in := cmd.In("in")
	out := cmd.Out("out")
	for m := range in {
		switch m := m.(type) {
		case []byte:
			buf.Write(m)
		case zx.Dir:
			if !one && buf.Len() > 0 {
				if ok := out <- buf.Bytes(); !ok {
					close(in, cerror(out))
					break
				}
				buf = &bytes.Buffer{}
			}
			if !one && !ux {
				if ok := out <- m; !ok {
					close(in, cerror(out))
					break
				}
			}
		case error:
			cmd.Warn("%s", m)
		default:
			cmd.Dprintf("ignored %T\n", m)
			if !ux {
				if ok := out <- m; !ok {
					close(in, cerror(out))
					break
				}
			}
		}
	}
	if buf.Len() > 0 {
		out <- buf.Bytes()
	}
	if err := cerror(in); err != nil {
		cmd.Fatal(err)
	}
}
Пример #9
0
func TestBibLoad(t *testing.T) {
	BibTexOk = true
	c := cmd.AppCtx()
	c.Debug = testing.Verbose()
	b, err := Load("/zx/lib/bib")
	if err != nil {
		t.Fatalf("load: %s", err)
	}
	if testing.Verbose() {
		b.WriteTo(os.Stdout)
	}

	cmd.Dprintf("cite plan 9 networks:\n")
	refs := b.Cites("VisageFS")
	for _, r := range refs {
		cmd.Dprintf("got:\n%s\n", strings.Join(r.Reference(), "\n"))
	}
	if len(refs) == 0 {
		t.Fatalf("did not find visage in bib")
	}
}
Пример #10
0
func TestLoad(t *testing.T) {
	BibTexOk = false
	c := cmd.AppCtx()
	c.Debug = testing.Verbose()
	b, err := Load("/zx/lib/bib")
	if err != nil {
		t.Fatalf("load: %s", err)
	}
	if testing.Verbose() {
		b.WriteTo(os.Stdout)
	}

	cmd.Dprintf("cite plan 9 networks:\n")
	refs := b.Cites("plan", "9", "networks")
	for _, r := range refs {
		cmd.Dprintf("got:\n%s\n", strings.Join(r.Reference(), "\n"))
	}
	if len(refs) != 2 {
		t.Fatalf("wrong count for plan 9 networks at lsub bib")
	}
}
Пример #11
0
// Run cols in the current app context.
func main() {
	c := cmd.AppCtx()
	opts.NewFlag("D", "debug", &c.Debug)
	opts.NewFlag("w", "wid: set max line width", &wid)
	opts.NewFlag("n", "ncols: set number of columns", &ncols)
	opts.NewFlag("u", "use unix output", &ux)
	cmd.UnixIO("err")
	args := opts.Parse()
	if ux {
		cmd.UnixIO("out")
	}
	if len(args) != 0 {
		cmd.SetIn("in", cmd.Files(args...))
	}
	in := cmd.In("in")
	for m := range in {
		switch m := m.(type) {
		default:
			// ignored & forwarded
			cmd.Dprintf("got %T\n", m)
			continue
		case zx.Dir:
			cmd.Dprintf("got %T %s\n", m, m["Upath"])
			add(strings.TrimSpace(m["name"]))
		case error:
			if m != nil {
				cmd.Warn("%s", m)
			}
		case []byte:
			cmd.Dprintf("got %T [%d]\n", m, len(m))
			words := strings.Fields(strings.TrimSpace(string(m)))
			add(words...)
		}
	}
	col()
	if err := cerror(in); err != nil {
		cmd.Fatal("in %s", err)
	}
}
Пример #12
0
func replre(s string, re *sre.ReProg, to string, glob bool) string {
	rfrom := []rune(s)
	rto := []rune(to)
	nrefs := 0
	for i := 0; i < len(rto)-1; i++ {
		if nb := rto[i+1]; rto[i] == '\\' {
			if nb >= '0' && nb <= '9' {
				nb -= '0'
				rto[i] = nb
				nrefs++
				n := copy(rto[i+1:], rto[i+2:])
				rto = rto[:i+1+n]
			} else if nb == '\\' {
				n := copy(rto[i+1:], rto[i+2:])
				rto = rto[:i+1+n]
			}
		}
	}
	st := 0
	for {
		cmd.Dprintf("re match [%d:%d]\n", st, len(rfrom))
		rg := re.ExecRunes(rfrom, st, len(rfrom))
		if len(rg) == 0 {
			break
		}
		r0 := rg[0]
		var ns []rune
		ns = append(ns, rfrom[:r0.P0]...)
		if nrefs == 0 {
			ns = append(ns, rto...)
		} else {
			for _, r := range rto {
				if r > 10 {
					ns = append(ns, r)
					continue
				}
				if r < rune(len(rg)) {
					t := rfrom[rg[r].P0:rg[r].P1]
					ns = append(ns, t...)
				}
			}
		}
		st = len(ns)
		ns = append(ns, rfrom[r0.P1:]...)
		rfrom = ns
		if !glob {
			break
		}
	}
	return string(rfrom)
}
Пример #13
0
func kq(dir, wcmd string) {
	w, err := fswatch.New()
	if err != nil {
		cmd.Fatal("kqueue: %s", err)
	}

	cmd.Dprintf("(re)read %s\n", dir)
	if err := w.Add(dir); err != nil {
		cmd.Fatal("kqueue: %s", err)
	}
	if err != nil {
		cmd.Fatal("watch: %s", err)
	}
	var pc chan string
	if once {
		w.Once()
	}
	pc = w.Changes()
	for p := range pc {
		if wcmd == "" {
			cmd.Warn("%s", p)
			continue
		}
		cln := strings.Replace(wcmd, "%", p, -1)
		cmd.Dprintf("run %s\n", cln)
		out, err := exec.Command("sh", "-c", cln).CombinedOutput()
		cmd.Out("out") <- out
		if err != nil {
			cmd.Warn("run: %s", err)
		}
	}
	if err := cerror(pc); err != nil {
		cmd.Fatal(err)
	}
	cmd.Exit(nil)
}
Пример #14
0
// Example of how to update the text from the API while the user edits it,
// again, mostly for testing.
func edits(t *ink.Txt) {
	time.Sleep(5 * time.Second)
	t.Ins([]rune("ZZZ\n"), 3)
	time.Sleep(1 * time.Second)
	rs := t.Del(3, 4)
	cmd.Dprintf("did del %s\n", string(rs))
	time.Sleep(1 * time.Second)
	x := t.GetText()
	x.Ins([]rune("XXX\n"), 2)
	x.Ins([]rune("XXX\n"), x.Len())
	t.SetMark("xx", 30)
	t.PutText()
	for i := 0; i < 30; i++ {
		time.Sleep(time.Second)
		t.MarkIns("xx", []rune(fmt.Sprintf("--%d--\n", i)))
	}
}
Пример #15
0
func b2s(in <-chan []byte) <-chan string {
	rc := make(chan string)
	go func() {
		for x := range in {
			if ok := rc <- string(x); !ok {
				close(in, cerror(rc))
			}
		}
		close(rc, cerror(in))
	}()
	return rc
}

func (b *Bib) loadBib(fn string) error {
	lnc := b2s(cmd.ByteLines(cmd.Get(fn, 0, zx.All)))
	ln := <-lnc
	if !strings.Contains(ln, "bib2ref ok") {
		close(lnc, "not for me")
		return nil
	}
	cmd.Dprintf("add file %s\n", fn)
	return b.loadLines(bib2ref(lnc))
}

func bib2ref(lnc <-chan string) chan string {
	rc := make(chan string)
	go parseBib(lnc, rc)
	return rc
}

func parseBib(lnc <-chan string, outc chan<- string) {
	for r, err := parse1Bib(lnc); err != io.EOF; r, err = parse1Bib(lnc) {
		for _, ln := range r {
			outc <- ln
		}
		outc <- "\n"
	}
	close(outc, cerror(lnc))
}

// trim space and no final ,
func cleanLn(ln string) string {
	ln = strings.TrimSpace(ln)
	if len(ln) > 0 && ln[len(ln)-1] == ',' {
		ln = ln[:len(ln)-1]
	}
	return ln
}

func parse1Bib(lnc <-chan string) ([]string, error) {
	for {
		ln, ok := <-lnc
		if !ok {
			return nil, io.EOF
		}
		ln = cleanLn(ln)
		if ln == "" {
			continue
		}
		if ln[0] != '@' {
			continue
		}
		out := []string{}
		brace := strings.IndexRune(ln, '{')
		if brace < 0 {
			continue
		}
		ln = ln[brace+1:]
		toks := strings.Fields(ln)
		if len(toks) > 0 {
			out = append(out, "%K "+toks[0]+"\n")
		}
		for fld, err := parseField(lnc); err != io.EOF && fld != nil; fld, err = parseField(lnc) {
			out = append(out, fld...)
		}
		return out, nil
	}
}

var keys = map[string]string{
	"author":       "A",
	"title":        "T",
	"note":         "O",
	"booktitle":    "B",
	"series":       "S",
	"year":         "D",
	"location":     "C",
	"address":      "C",
	"pages":        "P",
	"url":          "O",
	"publisher":    "I",
	"key":          "K",
	"volume":       "V",
	"journal":      "J",
	"keywords":     "K",
	"organization": "I",
}

func parseField(lnc <-chan string) ([]string, error) {
	for {
		ln, ok := <-lnc
		if !ok {
			return nil, io.EOF
		}
		ln = cleanLn(ln)
		if ln == "" {
			return nil, nil
		}
		if ln[0] == '%' {
			continue
		}
		toks := strings.SplitN(ln, "=", 2)
		if len(toks) != 2 {
			continue
		}
		fname := strings.ToLower(strings.TrimSpace(toks[0]))
		if k := keys[fname]; k != "" {
			val := parseValue(k, toks[1])
			for i, v := range val {
				val[i] = fmt.Sprintf("%%%s %s\n", k, strings.TrimSpace(v))
			}
			return val, nil
		}
	}
}

func parseValue(key, ln string) []string {
	ln = strings.TrimSpace(ln)
	if len(ln) == 0 {
		return nil
	}
	if ln[0] == '"' {
		ln = ln[1:]
		if len(ln) > 0 && ln[len(ln)-1] == '"' {
			ln = ln[:len(ln)-1]
		}
	} else if len(ln) > 0 && ln[0] == '{' {
		ln = ln[1:]
		if len(ln) > 0 && ln[len(ln)-1] == '}' {
			ln = ln[:len(ln)-1]
		}
	}
	nln := ""
	incaps := 0
	for _, r := range ln {
		switch r {
		case '\\', '\'':
			continue
		case '{':
			incaps++
		case '}':
			incaps--
		default:
			if incaps <= 0 || key == "A" {
				nln += string(r)
			} else {
				nln += strings.ToUpper(string(r))
			}
		}
	}
	ln = nln
	if key == "A" {
		return strings.Split(ln, " and ")
	}
	if key == "K" {
		return strings.Split(ln, ",")
	}
	return []string{strings.TrimSpace(ln)}
}
Пример #16
0
func lns(nm string, in chan []byte, donec chan bool) {
	last := []string{}
	nln := 0
	cmd.Dprintf("nhd %d ntl %d nfrom %d\n", nhd, ntl, nfrom)
	var err error
	for m := range in {
		s := string(m)
		lout := false
		nln++
		if all {
			if pflag {
				_, err = cmd.Printf("%s:%-5d %s", nm, nln, s)
			} else if nflag {
				_, err = cmd.Printf("%-5d %s", nln, s)
			} else {
				_, err = cmd.Printf("%s", s)
			}
			if err != nil {
				close(in, err)
			}
			continue
		}
		if ntl == 0 && nfrom == 0 && nhd > 0 && nln > nhd {
			close(in)
			close(donec)
			return
		}
		for _, a := range addrs {
			cmd.Dprintf("tl match %d of ? in %s\n", nln, a)
			if a.Matches(nln, 0) {
				lout = true
				if pflag {
					_, err = cmd.Printf("%s:%-5d %s", nm, nln, s)
				} else if nflag {
					_, err = cmd.Printf("%-5d %s", nln, s)
				} else {
					_, err = cmd.Printf("%s", s)
				}
				if err != nil {
					close(in, err)
				}
				break
			}
		}
		if nln >= nfrom || ntl > 0 {
			if lout {
				s = "" /*already there */
			}
			if nln >= nfrom || ntl > 0 && len(last) < ntl {
				last = append(last, s)
			} else {
				copy(last, last[1:])
				last[len(last)-1] = s
			}
		}

	}

	if !all && (ntl > 0 || nfrom > 0) {
		// if len(last) == 3 and nln is 10
		// last[0] is -3 or 10-2
		// last[1] is -2 or 10-1
		// last[2] is -1 or 10
		for i := 0; i < len(last); i++ {
			for _, a := range addrs {
				if a.P0 > 0 && a.P1 > 0 { // done already
					continue
				}
				cmd.Dprintf("tl match %d of %d in %s\n", nln-len(last)+1+i, nln, a)
				if a.Matches(nln-len(last)+1+i, nln) && last[i] != "" {
					if pflag {
						_, err = cmd.Printf("%s:%-5d %s",
							nm, nln-len(last)+1+i, last[i])
					} else if nflag {
						_, err = cmd.Printf("%-5d %s", nln-len(last)+1+i, last[i])
					} else {
						_, err = cmd.Printf("%s", last[i])
					}
					if err != nil {
						close(donec, err)
						return
					}
					last[i] = "" /* because if empty it still contains \n */
					break
				}
			}
		}
	}
	close(donec)
}
Пример #17
0
// Run rem in the current app context.
func main() {
	c := cmd.AppCtx()
	cmd.UnixIO("err")
	opts.NewFlag("D", "debug", &c.Debug)
	opts.NewFlag("v", "verbose; print the calls made in the order they are made.", &c.Verb)
	opts.NewFlag("a", "remove all", &aflag)
	opts.NewFlag("f", "quiet, called 'force' in unix", &fflag)
	opts.NewFlag("n", "dry run; report removes but do not do them", &dry)
	args := opts.Parse()
	if len(args) != 0 {
		cmd.SetIn("in", cmd.Dirs(args...))
	}
	c.Verb = c.Verb || dry
	in := cmd.In("in")
	var err error
	for m := range in {
		switch d := m.(type) {
		case zx.Dir:
			cmd.Dprintf("got %T %s\n", d, d["upath"])
			dirs = append(dirs, d)
		case error:
			cmd.Warn("%s", d)
		default:
			// ignored
			cmd.Dprintf("ignored %T\n", m)
		}
	}
	if aflag {
		for i := 0; i < len(dirs); i++ {
			if dirs[i] == nil {
				continue
			}
			pi := dirs[i]["path"]
			for j := 1; j < len(dirs); j++ {
				if i == j || dirs[j] == nil {
					continue
				}
				pj := dirs[j]["path"]
				if zx.HasPrefix(pj, pi) {
					dirs[j] = nil
				}
			}
		}
	}
	for i := len(dirs) - 1; i >= 0; i-- {
		if dirs[i] == nil {
			continue
		}
		if cerr := rmf(dirs[i]); cerr != nil {
			if !fflag {
				cmd.Warn("%s", cerr)
				err = cerr
			}
		}
	}
	if err == nil {
		err = cerror(in)
		if err != nil {
			cmd.Fatal(err)
		}
	}
	cmd.Exit(err)
}
Пример #18
0
func find(dump, dpref, rel string, dc chan<- zx.Dir, ufile zx.Dir) {
	droot := fpath.Join(dump, dpref)
	years, err := cmd.GetDir(droot)
	if err != nil {
		cmd.Warn("%s", err)
		return
	}
	for i := len(years) - 1; i >= 0; i-- {
		year := years[i]["name"]
		if ignored(year, "") {
			continue
		}
		ypath := years[i]["path"]
		days, err := cmd.GetDir(ypath)
		if err != nil {
			cmd.Warn("%s: %s", ypath, err)
			continue
		}
		lastsz, lastmt, lastm := "", "", ""
		for j := len(days) - 1; j >= 0; j-- {
			day := days[j]["name"]
			if ignored(year, day) {
				continue
			}
			fpath := fpath.Join(days[j]["path"], rel)
			d, err := cmd.Stat(fpath)
			if err != nil {
				if !force {
					cmd.Dprintf("find: %s", err)
					return
				}
				continue
			}
			newm, newsz, newmt := d["mode"], d["size"], d["mtime"]
			if newsz == lastsz && newmt == lastmt && newm == lastm {
				continue
			}
			lastm, lastsz, lastmt = newm, newsz, newmt
			d["upath"] = ufile["path"]
			d["uupath"] = ufile["upath"]
			if ok := dc <- d; !ok {
				return
			}
			if !all {
				return
			}
		}
	}
}

func report(dc chan zx.Dir, donec chan bool) {
	last := ""
	for d := range dc {
		if last == "" {
			last = d["Upath"]
			if last == "" {
				last = d["path"]
			}
		}
		p := d["path"]
		cmd.Dprintf("found '%s'\n", p)
		var err error
		switch {
		case xcmd != "":
			_, err = cmd.Printf("%s %s %s\n", xcmd, p, last)
		case dflag:
			dcmd := fmt.Sprintf(`9 diff -n %s %s`, p, last)
			_, err = cmd.Printf("%s\n", dcmd)
			if err != nil {
				cmd.Warn("diff: %s", err)
				continue
			}
		case lflag:
			_, err = cmd.Printf("%s\n", d.Fmt())
		case cflag:
			_, err = cmd.Printf("cp %s %s\n", p, d["Upath"])
		default:
			_, err = cmd.Printf("%s\n", d["path"])
		}
		if err != nil {
			close(dc, err)
		}
		last = p
	}
	close(donec, cerror(dc))
}

func hist(in <-chan face{}) error {
	dc := make(chan zx.Dir)
	ec := make(chan bool)
	go report(dc, ec)
	var sts error
	for m := range in {
		switch m := m.(type) {
		case zx.Dir:
			cmd.Dprintf("got %T %s\n", m, m["path"])
			file := m["path"]
			if m["upath"] == "" {
				m["upath"] = m["path"]
			}
			ddir := dump
			dpref := ""
			rel := ""
			switch {
			case zx.HasPrefix(file, "/zx"):
				if ddir == "" {
					ddir = "/dump"
				}
				dpref = "/zx"
				rel = zx.Suffix(file, "/zx")
			case zx.HasPrefix(file, "/u/gosrc/src/clive"):
				if ddir == "" {
					ddir = "/u/dump"
				}
				dpref = "clive"
				rel = zx.Suffix(file, "/u/gosrc/src/clive")
			case zx.HasPrefix(file, "/u"):
				if ddir == "" {
					ddir = "/u/dump"
				}
				els := zx.Elems(file)
				if len(els) < 3 {
					cmd.Warn("%s: too few path elements", m["upath"])
					sts = errNoDump
					continue
				}
				dpref = els[1]
				rel = zx.Path(els[2:]...)
			default:
				cmd.Warn("%s: %s", m["upath"], errNoDump)
				sts = errNoDump
				continue
			}
			find(ddir, dpref, rel, dc, m.Dup())
		default:
			cmd.Dprintf("got %T\n", m)

		}
	}
	close(dc, cerror(in))
	<-ec
	if sts == nil {
		sts = cerror(ec)
	}
	if sts == nil {
		sts = cerror(in)
	}
	return sts
}

// Run cnt in the current app context.
func main() {
	c := cmd.AppCtx()
	cmd.UnixIO("err")
	opts.NewFlag("D", "debug", &c.Debug)
	opts.NewFlag("f", "force search past file removals", &force)
	opts.NewFlag("l", "produce a long listing (or print just the name)", &lflag)
	opts.NewFlag("c", "copy the file from the dump", &cflag)
	opts.NewFlag("d", "print file differences", &dflag)
	opts.NewFlag("x", "cmd: print lines to execute this command between versions", &xcmd)
	opts.NewFlag("a", "list all copies that differ, not just the last one.", &all)
	opts.NewFlag("p", "dumpdir: path to dump (default is /dump or /u/dump)", &dump)
	t := time.Now()
	when := t
	opts.NewFlag("w", "date: backward search start time (default is now)", &when)
	ux := false
	opts.NewFlag("u", "unix IO", &ux)
	args := opts.Parse()
	if (all && cflag) || (force && !all) {
		cmd.Warn("incompatible flags")
		opts.Usage()
	}
	if ux {
		cmd.UnixIO("out")
	}
	lastyear = ""
	lastday = ""
	if !t.Equal(when) {
		y := when.Year()
		m := when.Month()
		d := when.Day()
		if y == 0 {
			y = t.Year()
		}
		lastyear = fmt.Sprintf("%04d", y)
		lastday = fmt.Sprintf("%02d%02d", m, d)
	}
	if len(args) != 0 {
		cmd.SetIn("in", cmd.Dirs(args...))
	}
	in := cmd.In("in")
	if err := hist(in); err != nil {
		cmd.Fatal(err)
	}
}