// 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 ( "bytes" "compress/gzip" "fmt" "io" "io/ioutil" "os" "unicode/utf8" ) // writeRelease writes the release code file. func writeRelease(w io.Writer, c *Config, toc []Asset) error { err := writeReleaseHeader(w, c) if err != nil { return err } for i := range toc { err = writeReleaseAsset(w, c, &toc[i]) if err != nil { return err } } return nil } // writeReleaseHeader writes output file headers. // This targets release builds. func writeReleaseHeader(w io.Writer, c *Config) error { var err error if c.NoCompress { if c.NoMemCopy { err = header_uncompressed_nomemcopy(w) } else { err = header_uncompressed_memcopy(w) } } else { if c.NoMemCopy { err = header_compressed_nomemcopy(w) } else { err = header_compressed_memcopy(w) } } if err != nil { return err } return header_release_common(w) } // writeReleaseAsset write a release entry for the given asset. // A release entry is a function which embeds and returns // the file's byte content. func writeReleaseAsset(w io.Writer, c *Config, asset *Asset) error { fd, err := os.Open(asset.Path) if err != nil { return err } defer fd.Close() if c.NoCompress { if c.NoMemCopy { err = uncompressed_nomemcopy(w, asset, fd) } else { err = uncompressed_memcopy(w, asset, fd) } } else { if c.NoMemCopy { err = compressed_nomemcopy(w, asset, fd) } else { err = compressed_memcopy(w, asset, fd) } } if err != nil { return err } return asset_release_common(w, c, asset) } // sanitize prepares a valid UTF-8 string as a raw string constant. // Based on https://code.google.com/p/go/source/browse/godoc/static/makestatic.go?repo=tools func sanitize(b []byte) []byte { // Replace ` with `+"`"+` b = bytes.Replace(b, []byte("`"), []byte("`+\"`\"+`"), -1) // Replace BOM with `+"\xEF\xBB\xBF"+` // (A BOM is valid UTF-8 but not permitted in Go source files. // I wouldn't bother handling this, but for some insane reason // jquery.js has a BOM somewhere in the middle.) return bytes.Replace(b, []byte("\xEF\xBB\xBF"), []byte("`+\"\\xEF\\xBB\\xBF\"+`"), -1) } func header_compressed_nomemcopy(w io.Writer) error { _, err := fmt.Fprintf(w, `import ( "bytes" "compress/gzip" "fmt" "io" "io/ioutil" "os" "path/filepath" "strings" "time" ) func bindataRead(data, name string) ([]byte, error) { gz, err := gzip.NewReader(strings.NewReader(data)) if err != nil { return nil, fmt.Errorf("Read %%q: %%v", name, err) } var buf bytes.Buffer _, err = io.Copy(&buf, gz) clErr := gz.Close() if err != nil { return nil, fmt.Errorf("Read %%q: %%v", name, err) } if clErr != nil { return nil, err } return buf.Bytes(), nil } `) return err } func header_compressed_memcopy(w io.Writer) error { _, err := fmt.Fprintf(w, `import ( "bytes" "compress/gzip" "fmt" "io" "io/ioutil" "os" "path/filepath" "strings" "time" ) func bindataRead(data []byte, name string) ([]byte, error) { gz, err := gzip.NewReader(bytes.NewBuffer(data)) if err != nil { return nil, fmt.Errorf("Read %%q: %%v", name, err) } var buf bytes.Buffer _, err = io.Copy(&buf, gz) clErr := gz.Close() if err != nil { return nil, fmt.Errorf("Read %%q: %%v", name, err) } if clErr != nil { return nil, err } return buf.Bytes(), nil } `) return err } func header_uncompressed_nomemcopy(w io.Writer) error { _, err := fmt.Fprintf(w, `import ( "fmt" "io/ioutil" "os" "path/filepath" "reflect" "strings" "time" "unsafe" ) func bindataRead(data, name string) ([]byte, error) { var empty [0]byte sx := (*reflect.StringHeader)(unsafe.Pointer(&data)) b := empty[:] bx := (*reflect.SliceHeader)(unsafe.Pointer(&b)) bx.Data = sx.Data bx.Len = len(data) bx.Cap = bx.Len return b, nil } `) return err } func header_uncompressed_memcopy(w io.Writer) error { _, err := fmt.Fprintf(w, `import ( "fmt" "io/ioutil" "os" "path/filepath" "strings" "time" ) `) return err } func header_release_common(w io.Writer) error { _, err := fmt.Fprintf(w, `type asset struct { bytes []byte info os.FileInfo } type bindataFileInfo struct { name string size int64 mode os.FileMode modTime time.Time } func (fi bindataFileInfo) Name() string { return fi.name } func (fi bindataFileInfo) Size() int64 { return fi.size } func (fi bindataFileInfo) Mode() os.FileMode { return fi.mode } func (fi bindataFileInfo) ModTime() time.Time { return fi.modTime } func (fi bindataFileInfo) IsDir() bool { return false } func (fi bindataFileInfo) Sys() interface{} { return nil } `) return err } func compressed_nomemcopy(w io.Writer, asset *Asset, r io.Reader) error { _, err := fmt.Fprintf(w, `var _%s = "`, asset.Func) if err != nil { return err } gz := gzip.NewWriter(&StringWriter{Writer: w}) _, err = io.Copy(gz, r) gz.Close() if err != nil { return err } _, err = fmt.Fprintf(w, `" func %sBytes() ([]byte, error) { return bindataRead( _%s, %q, ) } `, asset.Func, asset.Func, asset.Name) return err } func compressed_memcopy(w io.Writer, asset *Asset, r io.Reader) error { _, err := fmt.Fprintf(w, `var _%s = []byte("`, asset.Func) if err != nil { return err } gz := gzip.NewWriter(&StringWriter{Writer: w}) _, err = io.Copy(gz, r) gz.Close() if err != nil { return err } _, err = fmt.Fprintf(w, `") func %sBytes() ([]byte, error) { return bindataRead( _%s, %q, ) } `, asset.Func, asset.Func, asset.Name) return err } func uncompressed_nomemcopy(w io.Writer, asset *Asset, r io.Reader) error { _, err := fmt.Fprintf(w, `var _%s = "`, asset.Func) if err != nil { return err } _, err = io.Copy(&StringWriter{Writer: w}, r) if err != nil { return err } _, err = fmt.Fprintf(w, `" func %sBytes() ([]byte, error) { return bindataRead( _%s, %q, ) } `, asset.Func, asset.Func, asset.Name) return err } func uncompressed_memcopy(w io.Writer, asset *Asset, r io.Reader) error { _, err := fmt.Fprintf(w, `var _%s = []byte(`, asset.Func) if err != nil { return err } b, err := ioutil.ReadAll(r) if err != nil { return err } if utf8.Valid(b) && !bytes.Contains(b, []byte{0}) { fmt.Fprintf(w, "`%s`", sanitize(b)) } else { fmt.Fprintf(w, "%+q", b) } _, err = fmt.Fprintf(w, `) func %sBytes() ([]byte, error) { return _%s, nil } `, asset.Func, asset.Func) return err } func asset_release_common(w io.Writer, c *Config, asset *Asset) error { fi, err := os.Stat(asset.Path) if err != nil { return err } mode := uint(fi.Mode()) modTime := fi.ModTime().Unix() size := fi.Size() if c.NoMetadata { mode = 0 modTime = 0 size = 0 } if c.Mode > 0 { mode = uint(os.ModePerm) & c.Mode } if c.ModTime > 0 { modTime = c.ModTime } _, err = fmt.Fprintf(w, `func %s() (*asset, error) { bytes, err := %sBytes() if err != nil { return nil, err } info := bindataFileInfo{name: %q, size: %d, mode: os.FileMode(%d), modTime: time.Unix(%d, 0)} a := &asset{bytes: bytes, info: info} return a, nil } `, asset.Func, asset.Func, asset.Name, size, mode, modTime) return err }