]> Cypherpunks.ru repositories - gostls13.git/blob - misc/nacl/mkzip.go
aaf37f120fccd6d8d2f81c26efec2253c21e6cf9
[gostls13.git] / misc / nacl / mkzip.go
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.
4
5 // Mkzip creates a zip file from a 'proto' file describing the contents.
6 //
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.
14 package main
15
16 import (
17         "archive/zip"
18         "bufio"
19         "flag"
20         "fmt"
21         "io"
22         "io/ioutil"
23         "log"
24         "os"
25         "path"
26         "path/filepath"
27         "strings"
28 )
29
30 func usage() {
31         fmt.Fprintf(os.Stderr, "usage: mkzip [-r root] src.proto out.zip\n")
32         os.Exit(2)
33 }
34
35 func sysfatal(format string, args ...interface{}) {
36         fmt.Fprintf(os.Stderr, "mkzip: %s\n", fmt.Sprintf(format, args...))
37         os.Exit(2)
38 }
39
40 var (
41         root      = flag.String("r", ".", "interpret source paths relative to this directory")
42         gopackage = flag.String("p", "", "write Go source file in this package")
43 )
44
45 type stack struct {
46         name  string
47         src   string
48         depth int
49 }
50
51 func main() {
52         log.SetFlags(0)
53         flag.Usage = usage
54         flag.Parse()
55
56         args := flag.Args()
57         if len(args) != 2 {
58                 usage()
59         }
60
61         rf, err := os.Open(args[0])
62         if err != nil {
63                 sysfatal("%v", err)
64         }
65         r := bufio.NewScanner(rf)
66
67         zf, err := os.Create(args[1])
68         if err != nil {
69                 sysfatal("%v", err)
70         }
71
72         var w io.Writer = zf
73         if *gopackage != "" {
74                 fmt.Fprintf(zf, `package %s
75 import "sync"
76 func init() {
77         var once sync.Once
78         fsinit = func() {
79                 once.Do(func() {
80                         unzip("`, *gopackage)
81                 gw := &goWriter{b: bufio.NewWriter(w)}
82                 defer func() {
83                         if err := gw.Close(); err != nil {
84                                 sysfatal("finishing Go output: %v", err)
85                         }
86                 }()
87                 w = gw
88         }
89         z := zip.NewWriter(w)
90
91         lineno := 0
92
93         addfile := func(info os.FileInfo, dst string, src string) {
94                 zh, err := zip.FileInfoHeader(info)
95                 if err != nil {
96                         sysfatal("%s:%d: %s: %v", args[0], lineno, src, err)
97                 }
98                 zh.Name = dst
99                 zh.Method = zip.Deflate
100                 if info.IsDir() && !strings.HasSuffix(dst, "/") {
101                         zh.Name += "/"
102                 }
103                 w, err := z.CreateHeader(zh)
104                 if err != nil {
105                         sysfatal("%s:%d: %s: %v", args[0], lineno, src, err)
106                 }
107                 if info.IsDir() {
108                         return
109                 }
110                 r, err := os.Open(src)
111                 if err != nil {
112                         sysfatal("%s:%d: %s: %v", args[0], lineno, src, err)
113                 }
114                 defer r.Close()
115                 if _, err := io.Copy(w, r); err != nil {
116                         sysfatal("%s:%d: %s: %v", args[0], lineno, src, err)
117                 }
118         }
119
120         var stk []stack
121
122         for r.Scan() {
123                 line := r.Text()
124                 lineno++
125                 s := strings.TrimLeft(line, "\t")
126                 prefix, line := line[:len(line)-len(s)], s
127                 if i := strings.Index(line, "#"); i >= 0 {
128                         line = line[:i]
129                 }
130                 f := strings.Fields(line)
131                 if len(f) == 0 {
132                         continue
133                 }
134                 if strings.HasPrefix(line, " ") {
135                         sysfatal("%s:%d: must use tabs for indentation", args[0], lineno)
136                 }
137                 depth := len(prefix)
138                 for len(stk) > 0 && depth <= stk[len(stk)-1].depth {
139                         stk = stk[:len(stk)-1]
140                 }
141                 parent := ""
142                 psrc := *root
143                 if len(stk) > 0 {
144                         parent = stk[len(stk)-1].name
145                         psrc = stk[len(stk)-1].src
146                 }
147                 if strings.Contains(f[0], "/") {
148                         sysfatal("%s:%d: destination name cannot contain slash", args[0], lineno)
149                 }
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, "=")
154                         if i < 0 {
155                                 sysfatal("%s:%d: malformed attribute %q", args[0], lineno, attr)
156                         }
157                         key, val := attr[:i], attr[i+1:]
158                         switch key {
159                         case "src":
160                                 src = val
161                         default:
162                                 sysfatal("%s:%d: unknown attribute %q", args[0], lineno, attr)
163                         }
164                 }
165
166                 stk = append(stk, stack{name: name, src: src, depth: depth})
167
168                 if f[0] == "*" || f[0] == "+" {
169                         if f[0] == "*" {
170                                 dir, err := ioutil.ReadDir(psrc)
171                                 if err != nil {
172                                         sysfatal("%s:%d: %v", args[0], lineno, err)
173                                 }
174                                 for _, d := range dir {
175                                         addfile(d, path.Join(parent, d.Name()), filepath.Join(psrc, d.Name()))
176                                 }
177                         } else {
178                                 err := filepath.Walk(psrc, func(src string, info os.FileInfo, err error) error {
179                                         if err != nil {
180                                                 return err
181                                         }
182                                         if src == psrc {
183                                                 return nil
184                                         }
185                                         if psrc == "." {
186                                                 psrc = ""
187                                         }
188                                         name := path.Join(parent, filepath.ToSlash(src[len(psrc):]))
189                                         addfile(info, name, src)
190                                         return nil
191                                 })
192                                 if err != nil {
193                                         sysfatal("%s:%d: %v", args[0], lineno, err)
194                                 }
195                         }
196                         continue
197                 }
198
199                 fi, err := os.Stat(src)
200                 if err != nil {
201                         sysfatal("%s:%d: %v", args[0], lineno, err)
202                 }
203                 addfile(fi, name, src)
204         }
205
206         if err := z.Close(); err != nil {
207                 sysfatal("finishing zip file: %v", err)
208         }
209 }
210
211 type goWriter struct {
212         b *bufio.Writer
213 }
214
215 func (w *goWriter) Write(b []byte) (int, error) {
216         for _, c := range b {
217                 fmt.Fprintf(w.b, "\\x%02x", c)
218         }
219         return len(b), nil
220 }
221
222 func (w *goWriter) Close() error {
223         fmt.Fprintf(w.b, "\")\n\t\t})\n\t}\n}")
224         w.b.Flush()
225         return nil
226 }