diff options
Diffstat (limited to 'misc/nacl/mkzip.go')
-rw-r--r-- | misc/nacl/mkzip.go | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/misc/nacl/mkzip.go b/misc/nacl/mkzip.go new file mode 100644 index 000000000..7b2de7d47 --- /dev/null +++ b/misc/nacl/mkzip.go @@ -0,0 +1,220 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Mkzip creates a zip file from a 'proto' file describing the contents. +// +// The proto file is inspired by the Plan 9 mkfs prototype file format. +// It describes a file tree, one directory per line, with leading tab +// indentation marking the tree structure. Each line contains a leading +// name field giving the name of the file to copy into the zip file, +// and then a sequence of optional key=value attributes to control +// the copy. The only known attribute is src=foo, meaning copy the +// actual data for the file (or directory) from an alternate location. +package main + +import ( + "archive/zip" + "bufio" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "path" + "path/filepath" + "strings" +) + +func usage() { + fmt.Fprintf(os.Stderr, "usage: mkzip [-r root] src.proto out.zip\n") + os.Exit(2) +} + +func sysfatal(format string, args ...interface{}) { + fmt.Fprintf(os.Stderr, "mkzip: %s\n", fmt.Sprintf(format, args...)) + os.Exit(2) +} + +var ( + root = flag.String("r", ".", "interpret source paths relative to this directory") + gopackage = flag.String("p", "", "write Go source file in this package") +) + +type stack struct { + name string + src string + depth int +} + +func main() { + log.SetFlags(0) + flag.Usage = usage + flag.Parse() + + args := flag.Args() + if len(args) != 2 { + usage() + } + + rf, err := os.Open(args[0]) + if err != nil { + sysfatal("%v", err) + } + r := bufio.NewScanner(rf) + + zf, err := os.Create(args[1]) + if err != nil { + sysfatal("%v", err) + } + + var w io.Writer = zf + if *gopackage != "" { + fmt.Fprintf(zf, "package %s\n\nfunc init() {\n\tunzip(\"", *gopackage) + gw := &goWriter{b: bufio.NewWriter(w)} + defer func() { + if err := gw.Close(); err != nil { + sysfatal("finishing Go output: %v", err) + } + }() + w = gw + } + z := zip.NewWriter(w) + + lineno := 0 + + addfile := func(info os.FileInfo, dst string, src string) { + zh, err := zip.FileInfoHeader(info) + if err != nil { + sysfatal("%s:%d: %s: %v", args[0], lineno, src, err) + } + zh.Name = dst + zh.Method = zip.Deflate + if info.IsDir() && !strings.HasSuffix(dst, "/") { + zh.Name += "/" + } + w, err := z.CreateHeader(zh) + if err != nil { + sysfatal("%s:%d: %s: %v", args[0], lineno, src, err) + } + if info.IsDir() { + return + } + r, err := os.Open(src) + if err != nil { + sysfatal("%s:%d: %s: %v", args[0], lineno, src, err) + } + defer r.Close() + if _, err := io.Copy(w, r); err != nil { + sysfatal("%s:%d: %s: %v", args[0], lineno, src, err) + } + } + + var stk []stack + + for r.Scan() { + line := r.Text() + lineno++ + s := strings.TrimLeft(line, "\t") + prefix, line := line[:len(line)-len(s)], s + if i := strings.Index(line, "#"); i >= 0 { + line = line[:i] + } + f := strings.Fields(line) + if len(f) == 0 { + continue + } + if strings.HasPrefix(line, " ") { + sysfatal("%s:%d: must use tabs for indentation", args[0], lineno) + } + depth := len(prefix) + for len(stk) > 0 && depth <= stk[len(stk)-1].depth { + stk = stk[:len(stk)-1] + } + parent := "" + psrc := *root + if len(stk) > 0 { + parent = stk[len(stk)-1].name + psrc = stk[len(stk)-1].src + } + if strings.Contains(f[0], "/") { + sysfatal("%s:%d: destination name cannot contain slash", args[0], lineno) + } + name := path.Join(parent, f[0]) + src := filepath.Join(psrc, f[0]) + for _, attr := range f[1:] { + i := strings.Index(attr, "=") + if i < 0 { + sysfatal("%s:%d: malformed attribute %q", args[0], lineno, attr) + } + key, val := attr[:i], attr[i+1:] + switch key { + case "src": + src = val + default: + sysfatal("%s:%d: unknown attribute %q", args[0], lineno, attr) + } + } + + stk = append(stk, stack{name: name, src: src, depth: depth}) + + if f[0] == "*" || f[0] == "+" { + if f[0] == "*" { + dir, err := ioutil.ReadDir(psrc) + if err != nil { + sysfatal("%s:%d: %v", args[0], lineno, err) + } + for _, d := range dir { + addfile(d, path.Join(parent, d.Name()), filepath.Join(psrc, d.Name())) + } + } else { + err := filepath.Walk(psrc, func(src string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if src == psrc { + return nil + } + if psrc == "." { + psrc = "" + } + name := path.Join(parent, filepath.ToSlash(src[len(psrc):])) + addfile(info, name, src) + return nil + }) + if err != nil { + sysfatal("%s:%d: %v", args[0], lineno, err) + } + } + continue + } + + fi, err := os.Stat(src) + if err != nil { + sysfatal("%s:%d: %v", args[0], lineno, err) + } + addfile(fi, name, src) + } + + if err := z.Close(); err != nil { + sysfatal("finishing zip file: %v", err) + } +} + +type goWriter struct { + b *bufio.Writer +} + +func (w *goWriter) Write(b []byte) (int, error) { + for _, c := range b { + fmt.Fprintf(w.b, "\\x%02x", c) + } + return len(b), nil +} + +func (w *goWriter) Close() error { + fmt.Fprintf(w.b, "\")\n}\n") + w.b.Flush() + return nil +} |