2011-06-17 16:01:49 +00:00
// 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/
2011-06-17 15:44:59 +00:00
package main
import (
"flag"
"fmt"
2011-12-07 12:49:06 +00:00
"os"
2011-06-17 15:44:59 +00:00
"path"
2013-07-25 20:46:12 +00:00
"path/filepath"
"regexp"
2011-12-07 12:49:06 +00:00
"strings"
2011-06-27 16:45:39 +00:00
"unicode"
2011-06-17 15:44:59 +00:00
)
2012-06-22 12:12:15 +00:00
var (
pipe = false
2013-07-25 20:46:12 +00:00
in = ""
2013-08-01 10:10:59 +00:00
out = flag . String ( "out" , "" , "Optional path and name of the output file." )
2013-07-25 20:46:12 +00:00
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." )
2013-09-19 15:27:54 +00:00
tags = flag . String ( "tags" , "" , "Optional build tags" )
2013-07-25 20:46:12 +00:00
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_] ` )
2011-06-17 15:44:59 +00:00
)
func main ( ) {
2012-06-22 12:12:15 +00:00
parseArgs ( )
if pipe {
translate ( os . Stdin , os . Stdout , * pkgname , * funcname , * uncompressed , * nomemcopy )
2013-07-25 20:46:12 +00:00
return
}
2012-06-22 12:12:15 +00:00
2013-07-25 20:46:12 +00:00
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 ( )
2013-09-19 15:27:54 +00:00
if * tags != "" {
fmt . Fprintf ( fd , "// +build %s\n\n" , * tags )
}
2013-07-25 20:46:12 +00:00
// 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 {
2013-08-01 10:10:59 +00:00
dir , _ := filepath . Split ( * out )
err := createTOC ( dir , * pkgname )
2012-06-22 12:12:15 +00:00
if err != nil {
fmt . Fprintf ( os . Stderr , "[e] %s\n" , err )
return
}
2013-07-25 20:46:12 +00:00
writeTOCInit ( fd , in , * prefix , * funcname )
2013-01-26 20:06:46 +00:00
}
2012-06-22 12:12:15 +00:00
}
2011-06-17 15:44:59 +00:00
2012-06-22 12:12:15 +00:00
// parseArgs processes and verifies commandline arguments.
func parseArgs ( ) {
2013-07-25 20:46:12 +00:00
flag . Usage = func ( ) {
fmt . Printf ( "Usage: %s [options] <filename>\n\n" , os . Args [ 0 ] )
flag . PrintDefaults ( )
}
2011-06-17 15:44:59 +00:00
flag . Parse ( )
if * version {
2013-07-25 20:46:12 +00:00
fmt . Printf ( "%s\n" , Version ( ) )
2012-06-22 12:12:15 +00:00
os . Exit ( 0 )
2011-06-17 15:44:59 +00:00
}
2013-07-25 20:46:12 +00:00
pipe = flag . NArg ( ) == 0
2011-06-17 15:44:59 +00:00
2013-07-25 21:58:17 +00:00
if ! pipe {
2013-07-25 20:46:12 +00:00
* prefix , _ = filepath . Abs ( filepath . Clean ( * prefix ) )
in , _ = filepath . Abs ( filepath . Clean ( flag . Args ( ) [ 0 ] ) )
2013-07-25 21:58:17 +00:00
* out = safeFilename ( * out , in )
2011-06-17 15:44:59 +00:00
}
if len ( * pkgname ) == 0 {
fmt . Fprintln ( os . Stderr , "[w] No package name specified. Using 'main'." )
* pkgname = "main"
2011-06-27 16:45:39 +00:00
} else {
2011-10-26 09:59:30 +00:00
if unicode . IsDigit ( rune ( ( * pkgname ) [ 0 ] ) ) {
2011-06-27 16:45:39 +00:00
// Identifier can't start with a digit.
* pkgname = "_" + * pkgname
}
2011-06-17 15:44:59 +00:00
}
if len ( * funcname ) == 0 {
2011-06-17 16:52:40 +00:00
if pipe {
// Can't infer from input file name in this mode.
fmt . Fprintln ( os . Stderr , "[e] No function name specified." )
2012-06-22 12:12:15 +00:00
os . Exit ( 1 )
2012-06-07 21:34:34 +00:00
}
2012-06-07 21:28:16 +00:00
2013-07-25 20:46:12 +00:00
* funcname = safeFuncname ( in , * prefix )
fmt . Fprintf ( os . Stderr , "[w] No function name specified. Using %s.\n" , * funcname )
}
}
2012-06-07 21:34:34 +00:00
2013-07-25 21:58:17 +00:00
// safeFilename creates a safe output filename from the given
2013-08-01 10:10:59 +00:00
// output and input paths.
2013-07-25 21:58:17 +00:00
func safeFilename ( out , in string ) string {
2013-08-01 10:10:59 +00:00
var filename string
2013-07-25 21:58:17 +00:00
if len ( out ) == 0 {
2013-08-01 10:10:59 +00:00
filename = in + ".go"
2013-07-26 21:47:30 +00:00
2013-08-01 10:10:59 +00:00
_ , 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
2013-07-25 21:58:17 +00:00
2013-08-01 10:10:59 +00:00
for {
filename = path . Join ( out , fmt . Sprintf ( "%s.%d.go" , in , count ) )
_ , err = os . Lstat ( filename )
2013-07-25 21:58:17 +00:00
2013-08-01 10:10:59 +00:00
if err != nil {
break
}
2013-07-25 21:58:17 +00:00
2013-08-01 10:10:59 +00:00
count ++
2013-07-25 21:58:17 +00:00
}
}
2013-08-01 10:10:59 +00:00
} else {
filename , _ = filepath . Abs ( filepath . Clean ( out ) )
}
// Ensure output directory exists while we're here.
dir , _ := filepath . Split ( filename )
_ , err := os . Lstat ( dir )
if err != nil {
os . MkdirAll ( dir , 0755 )
2013-07-25 21:58:17 +00:00
}
return filename
}
2013-07-25 20:46:12 +00:00
// safeFuncname creates a safe function name from the input path.
func safeFuncname ( in , prefix string ) string {
name := strings . Replace ( in , prefix , "" , 1 )
2012-06-07 21:34:34 +00:00
2013-07-25 20:46:12 +00:00
if len ( name ) == 0 {
name = in
2011-06-17 15:44:59 +00:00
}
2013-07-25 20:46:12 +00:00
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
2011-06-17 15:44:59 +00:00
}