139 lines
3.5 KiB
Go
139 lines
3.5 KiB
Go
package rice
|
|
|
|
import (
|
|
"archive/zip"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/daaku/go.zipexe"
|
|
"github.com/kardianos/osext"
|
|
)
|
|
|
|
// appendedBox defines an appended box
|
|
type appendedBox struct {
|
|
Name string // box name
|
|
Files map[string]*appendedFile // appended files (*zip.File) by full path
|
|
}
|
|
|
|
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 := osext.Executable()
|
|
if err != nil {
|
|
return // not appended or cant find self executable
|
|
}
|
|
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),
|
|
}
|
|
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),
|
|
//++ TODO: use zip modtime when that is set correctly: af.zipFile.ModTime()
|
|
time: time.Now(),
|
|
}
|
|
} 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
|
|
}
|