github.com/yanyiwu/go@v0.0.0-20150106053140-03d6637dbb7f/misc/nacl/mkzip.go (about)

     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  }