1// Copyright 2018 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package main 16 17import ( 18 "archive/zip" 19 "flag" 20 "fmt" 21 "io" 22 "io/ioutil" 23 "log" 24 "os" 25 "path/filepath" 26 "strings" 27) 28 29var ( 30 outputDir = flag.String("d", "", "output dir") 31 outputFile = flag.String("l", "", "output list file") 32 filter = flag.String("f", "", "optional filter pattern") 33) 34 35func must(err error) { 36 if err != nil { 37 log.Fatal(err) 38 } 39} 40 41func writeFile(filename string, in io.Reader, perm os.FileMode) error { 42 out, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) 43 if err != nil { 44 return err 45 } 46 _, err = io.Copy(out, in) 47 if err != nil { 48 out.Close() 49 return err 50 } 51 52 return out.Close() 53} 54 55func main() { 56 flag.Usage = func() { 57 fmt.Fprintln(os.Stderr, "usage: zipsync -d <output dir> [-l <output file>] [-f <pattern>] [zip]...") 58 flag.PrintDefaults() 59 } 60 61 flag.Parse() 62 63 if *outputDir == "" { 64 flag.Usage() 65 os.Exit(1) 66 } 67 68 inputs := flag.Args() 69 70 // For now, just wipe the output directory and replace its contents with the zip files 71 // Eventually this could only modify the directory contents as necessary to bring it up 72 // to date with the zip files. 73 must(os.RemoveAll(*outputDir)) 74 75 must(os.MkdirAll(*outputDir, 0777)) 76 77 var files []string 78 seen := make(map[string]string) 79 80 for _, input := range inputs { 81 reader, err := zip.OpenReader(input) 82 if err != nil { 83 log.Fatal(err) 84 } 85 defer reader.Close() 86 87 for _, f := range reader.File { 88 if *filter != "" { 89 if match, err := filepath.Match(*filter, filepath.Base(f.Name)); err != nil { 90 log.Fatal(err) 91 } else if !match { 92 continue 93 } 94 } 95 if filepath.IsAbs(f.Name) { 96 log.Fatalf("%q in %q is an absolute path", f.Name, input) 97 } 98 99 if prev, exists := seen[f.Name]; exists { 100 log.Fatalf("%q found in both %q and %q", f.Name, prev, input) 101 } 102 seen[f.Name] = input 103 104 filename := filepath.Join(*outputDir, f.Name) 105 if f.FileInfo().IsDir() { 106 must(os.MkdirAll(filename, f.FileInfo().Mode())) 107 } else { 108 must(os.MkdirAll(filepath.Dir(filename), 0777)) 109 in, err := f.Open() 110 if err != nil { 111 log.Fatal(err) 112 } 113 must(writeFile(filename, in, f.FileInfo().Mode())) 114 in.Close() 115 files = append(files, filename) 116 } 117 } 118 } 119 120 if *outputFile != "" { 121 data := strings.Join(files, "\n") 122 if len(files) > 0 { 123 data += "\n" 124 } 125 must(ioutil.WriteFile(*outputFile, []byte(data), 0666)) 126 } 127} 128