1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
package rice
import (
"archive/zip"
"log"
"os"
"path/filepath"
"strings"
"time"
"github.com/daaku/go.zipexe"
)
// appendedBox defines an appended box
type appendedBox struct {
Name string // box name
Files map[string]*appendedFile // appended files (*zip.File) by full path
Time time.Time
}
type appendedFile struct {
zipFile *zip.File
dir bool
dirInfo *appendedDirInfo
children []*appendedFile
content []byte
}
// appendedBoxes is a public register of appendes boxes
var appendedBoxes = make(map[string]*appendedBox)
func init() {
// find if exec is appended
thisFile, err := os.Executable()
if err != nil {
return // not appended or cant find self executable
}
thisFile, err = filepath.EvalSymlinks(thisFile)
if err != nil {
return
}
closer, rd, err := zipexe.OpenCloser(thisFile)
if err != nil {
return // not appended
}
defer closer.Close()
for _, f := range rd.File {
// get box and file name from f.Name
fileParts := strings.SplitN(strings.TrimLeft(filepath.ToSlash(f.Name), "/"), "/", 2)
boxName := fileParts[0]
var fileName string
if len(fileParts) > 1 {
fileName = fileParts[1]
}
// find box or create new one if doesn't exist
box := appendedBoxes[boxName]
if box == nil {
box = &appendedBox{
Name: boxName,
Files: make(map[string]*appendedFile),
Time: f.ModTime(),
}
appendedBoxes[boxName] = box
}
// create and add file to box
af := &appendedFile{
zipFile: f,
}
if f.Comment == "dir" {
af.dir = true
af.dirInfo = &appendedDirInfo{
name: filepath.Base(af.zipFile.Name),
time: af.zipFile.ModTime(),
}
} else {
// this is a file, we need it's contents so we can create a bytes.Reader when the file is opened
// make a new byteslice
af.content = make([]byte, af.zipFile.FileInfo().Size())
// ignore reading empty files from zip (empty file still is a valid file to be read though!)
if len(af.content) > 0 {
// open io.ReadCloser
rc, err := af.zipFile.Open()
if err != nil {
af.content = nil // this will cause an error when the file is being opened or seeked (which is good)
// TODO: it's quite blunt to just log this stuff. but this is in init, so rice.Debug can't be changed yet..
log.Printf("error opening appended file %s: %v", af.zipFile.Name, err)
} else {
_, err = rc.Read(af.content)
rc.Close()
if err != nil {
af.content = nil // this will cause an error when the file is being opened or seeked (which is good)
// TODO: it's quite blunt to just log this stuff. but this is in init, so rice.Debug can't be changed yet..
log.Printf("error reading data for appended file %s: %v", af.zipFile.Name, err)
}
}
}
}
// add appendedFile to box file list
box.Files[fileName] = af
// add to parent dir (if any)
dirName := filepath.Dir(fileName)
if dirName == "." {
dirName = ""
}
if fileName != "" { // don't make box root dir a child of itself
if dir := box.Files[dirName]; dir != nil {
dir.children = append(dir.children, af)
}
}
}
}
// implements os.FileInfo.
// used for Readdir()
type appendedDirInfo struct {
name string
time time.Time
}
func (adi *appendedDirInfo) Name() string {
return adi.name
}
func (adi *appendedDirInfo) Size() int64 {
return 0
}
func (adi *appendedDirInfo) Mode() os.FileMode {
return os.ModeDir
}
func (adi *appendedDirInfo) ModTime() time.Time {
return adi.time
}
func (adi *appendedDirInfo) IsDir() bool {
return true
}
func (adi *appendedDirInfo) Sys() interface{} {
return nil
}
|