1 // Copyright 2014 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Mkzip creates a zip file from a 'proto' file describing the contents.
7 // The proto file is inspired by the Plan 9 mkfs prototype file format.
8 // It describes a file tree, one directory per line, with leading tab
9 // indentation marking the tree structure. Each line contains a leading
10 // name field giving the name of the file to copy into the zip file,
11 // and then a sequence of optional key=value attributes to control
12 // the copy. The only known attribute is src=foo, meaning copy the
13 // actual data for the file (or directory) from an alternate location.
31 fmt.Fprintf(os.Stderr, "usage: mkzip [-r root] src.proto out.zip\n")
35 func sysfatal(format string, args ...interface{}) {
36 fmt.Fprintf(os.Stderr, "mkzip: %s\n", fmt.Sprintf(format, args...))
41 root = flag.String("r", ".", "interpret source paths relative to this directory")
42 gopackage = flag.String("p", "", "write Go source file in this package")
61 rf, err := os.Open(args[0])
65 r := bufio.NewScanner(rf)
67 zf, err := os.Create(args[1])
74 fmt.Fprintf(zf, `package %s
81 gw := &goWriter{b: bufio.NewWriter(w)}
83 if err := gw.Close(); err != nil {
84 sysfatal("finishing Go output: %v", err)
93 addfile := func(info os.FileInfo, dst string, src string) {
94 zh, err := zip.FileInfoHeader(info)
96 sysfatal("%s:%d: %s: %v", args[0], lineno, src, err)
99 zh.Method = zip.Deflate
100 if info.IsDir() && !strings.HasSuffix(dst, "/") {
103 w, err := z.CreateHeader(zh)
105 sysfatal("%s:%d: %s: %v", args[0], lineno, src, err)
110 r, err := os.Open(src)
112 sysfatal("%s:%d: %s: %v", args[0], lineno, src, err)
115 if _, err := io.Copy(w, r); err != nil {
116 sysfatal("%s:%d: %s: %v", args[0], lineno, src, err)
125 s := strings.TrimLeft(line, "\t")
126 prefix, line := line[:len(line)-len(s)], s
127 if i := strings.Index(line, "#"); i >= 0 {
130 f := strings.Fields(line)
134 if strings.HasPrefix(line, " ") {
135 sysfatal("%s:%d: must use tabs for indentation", args[0], lineno)
138 for len(stk) > 0 && depth <= stk[len(stk)-1].depth {
139 stk = stk[:len(stk)-1]
144 parent = stk[len(stk)-1].name
145 psrc = stk[len(stk)-1].src
147 if strings.Contains(f[0], "/") {
148 sysfatal("%s:%d: destination name cannot contain slash", args[0], lineno)
150 name := path.Join(parent, f[0])
151 src := filepath.Join(psrc, f[0])
152 for _, attr := range f[1:] {
153 i := strings.Index(attr, "=")
155 sysfatal("%s:%d: malformed attribute %q", args[0], lineno, attr)
157 key, val := attr[:i], attr[i+1:]
162 sysfatal("%s:%d: unknown attribute %q", args[0], lineno, attr)
166 stk = append(stk, stack{name: name, src: src, depth: depth})
168 if f[0] == "*" || f[0] == "+" {
170 dir, err := ioutil.ReadDir(psrc)
172 sysfatal("%s:%d: %v", args[0], lineno, err)
174 for _, d := range dir {
175 addfile(d, path.Join(parent, d.Name()), filepath.Join(psrc, d.Name()))
178 err := filepath.Walk(psrc, func(src string, info os.FileInfo, err error) error {
188 name := path.Join(parent, filepath.ToSlash(src[len(psrc):]))
189 addfile(info, name, src)
193 sysfatal("%s:%d: %v", args[0], lineno, err)
199 fi, err := os.Stat(src)
201 sysfatal("%s:%d: %v", args[0], lineno, err)
203 addfile(fi, name, src)
206 if err := z.Close(); err != nil {
207 sysfatal("finishing zip file: %v", err)
211 type goWriter struct {
215 func (w *goWriter) Write(b []byte) (int, error) {
216 for _, c := range b {
217 fmt.Fprintf(w.b, "\\x%02x", c)
222 func (w *goWriter) Close() error {
223 fmt.Fprintf(w.b, "\")\n\t\t})\n\t}\n}")