187 lines
4.5 KiB
Go
187 lines
4.5 KiB
Go
// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
|
|
// license. Its contents can be found at:
|
|
// http://creativecommons.org/publicdomain/zero/1.0/
|
|
|
|
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
"unicode"
|
|
)
|
|
|
|
var (
|
|
pipe = false
|
|
in = ""
|
|
out = flag.String("out", "", "Optional path to the output directory.")
|
|
pkgname = flag.String("pkg", "main", "Name of the package to generate.")
|
|
funcname = flag.String("func", "", "Optional name of the function to generate.")
|
|
prefix = flag.String("prefix", "", "Optional path prefix to strip off map keys and function names.")
|
|
uncompressed = flag.Bool("uncompressed", false, "The specified resource will /not/ be GZIP compressed when this flag is specified. This alters the generated output code.")
|
|
nomemcopy = flag.Bool("nomemcopy", false, "Use a .rodata hack to get rid of unnecessary memcopies. Refer to the documentation to see what implications this carries.")
|
|
toc = flag.Bool("toc", false, "Generate a table of contents for this and other files. The input filepath becomes the map key. This option is only useable in non-pipe mode.")
|
|
version = flag.Bool("version", false, "Display version information.")
|
|
regFuncName = regexp.MustCompile(`[^a-zA-Z0-9_]`)
|
|
)
|
|
|
|
func main() {
|
|
parseArgs()
|
|
|
|
if pipe {
|
|
translate(os.Stdin, os.Stdout, *pkgname, *funcname, *uncompressed, *nomemcopy)
|
|
return
|
|
}
|
|
|
|
fs, err := os.Open(in)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "[e] %s\n", err)
|
|
return
|
|
}
|
|
|
|
defer fs.Close()
|
|
|
|
fd, err := os.Create(*out)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "[e] %s\n", err)
|
|
return
|
|
}
|
|
|
|
defer fd.Close()
|
|
|
|
// Translate binary to Go code.
|
|
translate(fs, fd, *pkgname, *funcname, *uncompressed, *nomemcopy)
|
|
|
|
// Append the TOC init function to the end of the output file and
|
|
// write the `bindata-toc.go` file, if applicable.
|
|
if *toc {
|
|
err := createTOC(*out, *pkgname)
|
|
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "[e] %s\n", err)
|
|
return
|
|
}
|
|
|
|
writeTOCInit(fd, in, *prefix, *funcname)
|
|
}
|
|
}
|
|
|
|
// parseArgs processes and verifies commandline arguments.
|
|
func parseArgs() {
|
|
flag.Usage = func() {
|
|
fmt.Printf("Usage: %s [options] <filename>\n\n", os.Args[0])
|
|
flag.PrintDefaults()
|
|
}
|
|
flag.Parse()
|
|
|
|
if *version {
|
|
fmt.Printf("%s\n", Version())
|
|
os.Exit(0)
|
|
}
|
|
|
|
pipe = flag.NArg() == 0
|
|
|
|
if !pipe {
|
|
*prefix, _ = filepath.Abs(filepath.Clean(*prefix))
|
|
in, _ = filepath.Abs(filepath.Clean(flag.Args()[0]))
|
|
*out = safeFilename(*out, in)
|
|
}
|
|
|
|
if len(*pkgname) == 0 {
|
|
fmt.Fprintln(os.Stderr, "[w] No package name specified. Using 'main'.")
|
|
*pkgname = "main"
|
|
} else {
|
|
if unicode.IsDigit(rune((*pkgname)[0])) {
|
|
// Identifier can't start with a digit.
|
|
*pkgname = "_" + *pkgname
|
|
}
|
|
}
|
|
|
|
if len(*funcname) == 0 {
|
|
if pipe {
|
|
// Can't infer from input file name in this mode.
|
|
fmt.Fprintln(os.Stderr, "[e] No function name specified.")
|
|
os.Exit(1)
|
|
}
|
|
|
|
*funcname = safeFuncname(in, *prefix)
|
|
fmt.Fprintf(os.Stderr, "[w] No function name specified. Using %s.\n", *funcname)
|
|
}
|
|
}
|
|
|
|
// safeFilename creates a safe output filename from the given
|
|
// output base directory and input filename.
|
|
func safeFilename(out, in string) string {
|
|
dir, in := filepath.Split(in)
|
|
|
|
if len(out) == 0 {
|
|
out = dir
|
|
} else {
|
|
out, _ = filepath.Abs(filepath.Clean(out))
|
|
}
|
|
|
|
// Ensure output directory exists while we're here.
|
|
stat, err := os.Lstat(out)
|
|
if err != nil {
|
|
os.MkdirAll(out, 0755)
|
|
} else if !stat.IsDir() {
|
|
fmt.Fprintf(os.Stderr, "Output path is an existing file.\n")
|
|
os.Exit(1)
|
|
}
|
|
|
|
filename := path.Join(out, in+".go")
|
|
_, err = os.Lstat(filename)
|
|
|
|
if err == nil {
|
|
// File already exists. Pad name with a sequential number until we
|
|
// find a name that is available.
|
|
count := 0
|
|
|
|
for {
|
|
filename = path.Join(out, fmt.Sprintf("%s.%d.go", in, count))
|
|
_, err = os.Lstat(filename)
|
|
|
|
if err != nil {
|
|
break
|
|
}
|
|
|
|
count++
|
|
}
|
|
}
|
|
|
|
return filename
|
|
}
|
|
|
|
// safeFuncname creates a safe function name from the input path.
|
|
func safeFuncname(in, prefix string) string {
|
|
name := strings.Replace(in, prefix, "", 1)
|
|
|
|
if len(name) == 0 {
|
|
name = in
|
|
}
|
|
|
|
name = strings.ToLower(name)
|
|
name = regFuncName.ReplaceAllString(name, "_")
|
|
|
|
if unicode.IsDigit(rune(name[0])) {
|
|
// Identifier can't start with a digit.
|
|
name = "_" + name
|
|
}
|
|
|
|
// Get rid of "__" instances for niceness.
|
|
for strings.Index(name, "__") > -1 {
|
|
name = strings.Replace(name, "__", "_", -1)
|
|
}
|
|
|
|
// Leading underscore is silly.
|
|
if name[0] == '_' {
|
|
name = name[1:]
|
|
}
|
|
|
|
return name
|
|
}
|