// Copyright 2014-2017 Ulrich Kunitz. 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 ( "flag" "fmt" "io" "io/ioutil" "log" "os" "path/filepath" "strings" "text/template" ) const catUsageString = `xb cat [options] :... This xb command puts the contents of the files given as relative paths to the GOPATH variable as string constants into a go file. -h prints this message and exits -p package name (default main) -o file name of output ` func catUsage(w io.Writer) { fmt.Fprint(w, catUsageString) } type gopath struct { p []string i int } func newGopath() *gopath { p := strings.Split(os.Getenv("GOPATH"), ":") return &gopath{p: p} } type cpair struct { id string path string } func (p cpair) Read() (s string, err error) { var r io.ReadCloser if p.path == "-" { r = os.Stdin } else { if r, err = os.Open(p.path); err != nil { return } } defer func() { err = r.Close() }() b, err := ioutil.ReadAll(r) if err != nil { return } s = string(b) return } func verifyPath(path string) error { fi, err := os.Stat(path) if err != nil { return err } if !fi.Mode().IsRegular() { return fmt.Errorf("%s is not a regular file", path) } return nil } func (gp *gopath) find(arg string) (p cpair, err error) { t := strings.SplitN(arg, ":", 2) switch len(t) { case 0: err = fmt.Errorf("empty argument not supported") return case 1: gp.i++ p = cpair{fmt.Sprintf("gocat%d", gp.i), t[0]} case 2: p = cpair{t[0], t[1]} } if p.path == "-" { return } // substitute first ~ by $HOME p.path = strings.Replace(p.path, "~", os.Getenv("HOME"), 1) paths := make([]string, 0, len(gp.p)+1) if filepath.IsAbs(p.path) { paths = append(paths, filepath.Clean(p.path)) } else { for _, q := range gp.p { u := filepath.Join(q, "src", p.path) paths = append(paths, filepath.Clean(u)) } u := filepath.Join(".", p.path) paths = append(paths, filepath.Clean(u)) } for _, u := range paths { if err = verifyPath(u); err != nil { if os.IsNotExist(err) { continue } return } p.path = u return } err = fmt.Errorf("file %s not found", p.path) return } // Gofile is used with the template gofileTmpl. type Gofile struct { Pkg string Cmap map[string]string } var gofileTmpl = `package {{.Pkg}} {{range $k, $v := .Cmap}}const {{$k}} = ` + "`{{$v}}`\n{{end}}" func cat() { var err error cmdName := filepath.Base(os.Args[0]) log.SetPrefix(fmt.Sprintf("%s: ", cmdName)) log.SetFlags(0) flag.CommandLine = flag.NewFlagSet(cmdName, flag.ExitOnError) flag.Usage = func() { catUsage(os.Stderr); os.Exit(1) } help := flag.Bool("h", false, "") pkg := flag.String("p", "main", "") out := flag.String("o", "", "") flag.Parse() if *help { catUsage(os.Stdout) os.Exit(0) } if *pkg == "" { log.Fatal("option -p must not be empty") } w := os.Stdout if *out != "" { if w, err = os.Create(*out); err != nil { log.Fatal(err) } } gp := newGopath() gofile := Gofile{ Pkg: *pkg, Cmap: make(map[string]string, len(flag.Args())), } for _, arg := range flag.Args() { p, err := gp.find(arg) if err != nil { log.Print(err) continue } s, err := p.Read() if err != nil { log.Print(err) continue } gofile.Cmap[p.id] = s } tmpl, err := template.New("gofile").Parse(gofileTmpl) if err != nil { log.Panicf("goFileTmpl error %s", err) } if err = tmpl.Execute(w, gofile); err != nil { log.Fatal(err) } os.Exit(0) }