Signed-off-by: Sebastien Binet <s@sbinet.org>
---
cmd/kpath-find/main.go | 80 +++++++++++++++++++++++++++
cmd/kpath-find/main_test.go | 105 ++++++++++++++++++++++++++++++++++++
2 files changed, 185 insertions(+)
create mode 100644 cmd/kpath-find/main.go
create mode 100644 cmd/kpath-find/main_test.go
diff --git a/cmd/kpath-find/main.go b/cmd/kpath-find/main.go
new file mode 100644
index 0000000..4fdb083
--- /dev/null
+++ b/cmd/kpath-find/main.go
@@ -0,0 +1,80 @@
+// Copyright ©2021 The star-tex Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Command kpath-find finds files in a TeX Directory Structure.
+package main
+
+import (
+ "flag"
+ "fmt"
+ "log"
+ "os"
+ "path/filepath"
+
+ "git.sr.ht/~sbinet/star-tex/kpath"
+)
+
+func main() {
+ log.SetPrefix("kpath-find: ")
+ log.SetFlags(0)
+
+ var (
+ texmf = flag.String("texmf", "", "path to TEXMF distribution")
+ all = flag.Bool("all", false, "display all matches")
+ )
+
+ flag.Parse()
+
+ if flag.NArg() <= 0 {
+ flag.Usage()
+ log.Fatalf("missing input file name")
+ }
+
+ if *texmf == "" {
+ loop:
+ for _, env := range []string{"TEXMF", "TEXMFROOT", "TEXMFDIST", "TEXMFHOME"} {
+ v := os.Getenv(env)
+ if v != "" {
+ *texmf = v
+ break loop
+ }
+ }
+ if *texmf == "" {
+ *texmf = "/usr/share/texmf-dist"
+ }
+ }
+
+ dbname := filepath.Join(*texmf, "ls-R")
+ xmain(flag.Arg(0), dbname, *all)
+}
+
+func xmain(name, dbname string, all bool) {
+ fnames, err := process(name, dbname, all)
+ if err != nil {
+ log.Fatal(err)
+ }
+ for _, fname := range fnames {
+ fmt.Printf("%s\n", fname)
+ }
+}
+
+func process(name, dbname string, all bool) ([]string, error) {
+ db, err := os.Open(dbname)
+ if err != nil {
+ return nil, fmt.Errorf("could not open texmf db %q: %w", dbname, err)
+ }
+ defer db.Close()
+
+ ctx, err := kpath.NewFromDB(db)
+ if err != nil {
+ return nil, fmt.Errorf("could not create kpath context: %w", err)
+ }
+
+ if all {
+ return ctx.FindAll(name)
+ }
+
+ fname, err := ctx.Find(name)
+ return []string{fname}, err
+}
diff --git a/cmd/kpath-find/main_test.go b/cmd/kpath-find/main_test.go
new file mode 100644
index 0000000..951fb21
--- /dev/null
+++ b/cmd/kpath-find/main_test.go
@@ -0,0 +1,105 @@
+// Copyright ©2021 The star-tex Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "reflect"
+ "testing"
+)
+
+func TestProcess(t *testing.T) {
+ dir, err := os.MkdirTemp("", "kpath-find-")
+ if err != nil {
+ t.Fatalf("could not create tmp dir: %+v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ dbname := filepath.Join(dir, "ls-R")
+ err = os.WriteFile(dbname, []byte(`%%
+./:
+./dir1:
+file1.tex
+
+./dir2:
+file2.tex
+
+./dir3:
+file2.tex
+`), 0755)
+ if err != nil {
+ t.Fatalf("could not create texmf db: %+v", err)
+ }
+
+ for _, tc := range []struct {
+ name string
+ dbname string
+ all bool
+ want []string
+ err error
+ }{
+ {
+ name: "file1.tex",
+ dbname: dbname,
+ want: []string{filepath.Join(dir, "/dir1/file1.tex")},
+ },
+ {
+ name: "file2.tex",
+ dbname: dbname,
+ all: true,
+ want: []string{
+ filepath.Join(dir, "/dir2/file2.tex"),
+ filepath.Join(dir, "/dir3/file2.tex"),
+ },
+ },
+ {
+ name: "file2.tex",
+ dbname: dbname,
+ err: fmt.Errorf(`kpath: too many hits for file "file2.tex" (n=2)`),
+ },
+ {
+ name: "file1.tex",
+ dbname: dir,
+ err: fmt.Errorf(
+ `could not create kpath context: `+
+ `kpath: could not parse db file: `+
+ `could not scan db file: read %s: is a directory`,
+ dir,
+ ),
+ },
+ {
+ name: "file1.tex",
+ dbname: filepath.Join(dir, "not-there"),
+ err: fmt.Errorf(
+ `could not open texmf db %[1]q: `+
+ `open %[1]s: no such file or directory`,
+ filepath.Join(dir, "not-there"),
+ ),
+ },
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ got, err := process(tc.name, tc.dbname, tc.all)
+ switch {
+ case err == nil && tc.err == nil:
+ // ok.
+ case err != nil && tc.err != nil:
+ if got, want := err.Error(), tc.err.Error(); got != want {
+ t.Fatalf("invalid error:\ngot= %s\nwant=%s\n", got, want)
+ }
+ return
+ case err != nil && tc.err == nil:
+ t.Fatalf("could not run kpath-find: %+v", err)
+ case err == nil && tc.err != nil:
+ t.Fatalf("missing error. expected: %+v", tc.err)
+ }
+
+ if !reflect.DeepEqual(got, tc.want) {
+ t.Fatalf("invalid file named:\ngot= %q\nwant=%q", got, tc.want)
+ }
+ })
+ }
+}
--
2.30.1