| // 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 bindata |
| |
| import ( |
| "bufio" |
| "fmt" |
| "os" |
| "path/filepath" |
| "regexp" |
| "sort" |
| "strings" |
| "unicode" |
| ) |
| |
| // Translate reads assets from an input directory, converts them |
| // to Go code and writes new files to the output specified |
| // in the given configuration. |
| func Translate(c *Config) error { |
| var toc []Asset |
| |
| // Ensure our configuration has sane values. |
| err := c.validate() |
| if err != nil { |
| return err |
| } |
| |
| var knownFuncs = make(map[string]int) |
| var visitedPaths = make(map[string]bool) |
| // Locate all the assets. |
| for _, input := range c.Input { |
| err = findFiles(input.Path, c.Prefix, input.Recursive, &toc, c.Ignore, knownFuncs, visitedPaths) |
| if err != nil { |
| return err |
| } |
| } |
| |
| // Create output file. |
| fd, err := os.Create(c.Output) |
| if err != nil { |
| return err |
| } |
| |
| defer fd.Close() |
| |
| // Create a buffered writer for better performance. |
| bfd := bufio.NewWriter(fd) |
| defer bfd.Flush() |
| |
| // Write the header. This makes e.g. Github ignore diffs in generated files. |
| if _, err = fmt.Fprint(bfd, "// Code generated by go-bindata.\n"); err != nil { |
| return err |
| } |
| if _, err = fmt.Fprint(bfd, "// sources:\n"); err != nil { |
| return err |
| } |
| |
| wd, err := os.Getwd() |
| if err != nil { |
| return err |
| } |
| |
| for _, asset := range toc { |
| relative, _ := filepath.Rel(wd, asset.Path) |
| if _, err = fmt.Fprintf(bfd, "// %s\n", filepath.ToSlash(relative)); err != nil { |
| return err |
| } |
| } |
| if _, err = fmt.Fprint(bfd, "// DO NOT EDIT!\n\n"); err != nil { |
| return err |
| } |
| |
| // Write build tags, if applicable. |
| if len(c.Tags) > 0 { |
| if _, err = fmt.Fprintf(bfd, "// +build %s\n\n", c.Tags); err != nil { |
| return err |
| } |
| } |
| |
| // Write package declaration. |
| _, err = fmt.Fprintf(bfd, "package %s\n\n", c.Package) |
| if err != nil { |
| return err |
| } |
| |
| // Write assets. |
| if c.Debug || c.Dev { |
| err = writeDebug(bfd, c, toc) |
| } else { |
| err = writeRelease(bfd, c, toc) |
| } |
| |
| if err != nil { |
| return err |
| } |
| |
| // Write table of contents |
| if err := writeTOC(bfd, toc); err != nil { |
| return err |
| } |
| // Write hierarchical tree of assets |
| if err := writeTOCTree(bfd, toc); err != nil { |
| return err |
| } |
| |
| // Write restore procedure |
| return writeRestore(bfd) |
| } |
| |
| // Implement sort.Interface for []os.FileInfo based on Name() |
| type ByName []os.FileInfo |
| |
| func (v ByName) Len() int { return len(v) } |
| func (v ByName) Swap(i, j int) { v[i], v[j] = v[j], v[i] } |
| func (v ByName) Less(i, j int) bool { return v[i].Name() < v[j].Name() } |
| |
| // findFiles recursively finds all the file paths in the given directory tree. |
| // They are added to the given map as keys. Values will be safe function names |
| // for each file, which will be used when generating the output code. |
| func findFiles(dir, prefix string, recursive bool, toc *[]Asset, ignore []*regexp.Regexp, knownFuncs map[string]int, visitedPaths map[string]bool) error { |
| dirpath := dir |
| if len(prefix) > 0 { |
| dirpath, _ = filepath.Abs(dirpath) |
| prefix, _ = filepath.Abs(prefix) |
| prefix = filepath.ToSlash(prefix) |
| } |
| |
| fi, err := os.Stat(dirpath) |
| if err != nil { |
| return err |
| } |
| |
| var list []os.FileInfo |
| |
| if !fi.IsDir() { |
| dirpath = filepath.Dir(dirpath) |
| list = []os.FileInfo{fi} |
| } else { |
| visitedPaths[dirpath] = true |
| fd, err := os.Open(dirpath) |
| if err != nil { |
| return err |
| } |
| |
| defer fd.Close() |
| |
| list, err = fd.Readdir(0) |
| if err != nil { |
| return err |
| } |
| |
| // Sort to make output stable between invocations |
| sort.Sort(ByName(list)) |
| } |
| |
| for _, file := range list { |
| var asset Asset |
| asset.Path = filepath.Join(dirpath, file.Name()) |
| asset.Name = filepath.ToSlash(asset.Path) |
| |
| ignoring := false |
| for _, re := range ignore { |
| if re.MatchString(asset.Path) { |
| ignoring = true |
| break |
| } |
| } |
| if ignoring { |
| continue |
| } |
| |
| if file.IsDir() { |
| if recursive { |
| recursivePath := filepath.Join(dir, file.Name()) |
| visitedPaths[asset.Path] = true |
| findFiles(recursivePath, prefix, recursive, toc, ignore, knownFuncs, visitedPaths) |
| } |
| continue |
| } else if file.Mode()&os.ModeSymlink == os.ModeSymlink { |
| var linkPath string |
| if linkPath, err = os.Readlink(asset.Path); err != nil { |
| return err |
| } |
| if !filepath.IsAbs(linkPath) { |
| if linkPath, err = filepath.Abs(dirpath + "/" + linkPath); err != nil { |
| return err |
| } |
| } |
| if _, ok := visitedPaths[linkPath]; !ok { |
| visitedPaths[linkPath] = true |
| findFiles(asset.Path, prefix, recursive, toc, ignore, knownFuncs, visitedPaths) |
| } |
| continue |
| } |
| |
| if strings.HasPrefix(asset.Name, prefix) { |
| asset.Name = asset.Name[len(prefix):] |
| } else { |
| asset.Name = filepath.Join(dir, file.Name()) |
| } |
| |
| // If we have a leading slash, get rid of it. |
| if len(asset.Name) > 0 && asset.Name[0] == '/' { |
| asset.Name = asset.Name[1:] |
| } |
| |
| // This shouldn't happen. |
| if len(asset.Name) == 0 { |
| return fmt.Errorf("Invalid file: %v", asset.Path) |
| } |
| |
| asset.Func = safeFunctionName(asset.Name, knownFuncs) |
| asset.Path, _ = filepath.Abs(asset.Path) |
| *toc = append(*toc, asset) |
| } |
| |
| return nil |
| } |
| |
| var regFuncName = regexp.MustCompile(`[^a-zA-Z0-9_]`) |
| |
| // safeFunctionName converts the given name into a name |
| // which qualifies as a valid function identifier. It |
| // also compares against a known list of functions to |
| // prevent conflict based on name translation. |
| func safeFunctionName(name string, knownFuncs map[string]int) string { |
| var inBytes, outBytes []byte |
| var toUpper bool |
| |
| name = strings.ToLower(name) |
| inBytes = []byte(name) |
| |
| for i := 0; i < len(inBytes); i++ { |
| if regFuncName.Match([]byte{inBytes[i]}) { |
| toUpper = true |
| } else if toUpper { |
| outBytes = append(outBytes, []byte(strings.ToUpper(string(inBytes[i])))...) |
| toUpper = false |
| } else { |
| outBytes = append(outBytes, inBytes[i]) |
| } |
| } |
| |
| name = string(outBytes) |
| |
| // Identifier can't start with a digit. |
| if unicode.IsDigit(rune(name[0])) { |
| name = "_" + name |
| } |
| |
| if num, ok := knownFuncs[name]; ok { |
| knownFuncs[name] = num + 1 |
| name = fmt.Sprintf("%s%d", name, num) |
| } else { |
| knownFuncs[name] = 2 |
| } |
| |
| return name |
| } |