Home | History | Annotate | Download | only in nacl
      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 }
    227