blob: b3e78d0888370b7baacbdcde428cf3dee082fb79 [file] [log] [blame]
Colin Cross436b7652018-03-15 16:24:10 -07001// 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")
Dan Willemsen304cfec2019-05-28 14:49:06 -070032 zipPrefix = flag.String("zip-prefix", "", "optional prefix within the zip file to extract, stripping the prefix")
Zi Wang6a66e3d2023-06-13 10:40:52 -070033 filter multiFlag
Colin Cross436b7652018-03-15 16:24:10 -070034)
35
Zi Wang6a66e3d2023-06-13 10:40:52 -070036func init() {
37 flag.Var(&filter, "f", "optional filter pattern")
38}
39
Colin Cross436b7652018-03-15 16:24:10 -070040func must(err error) {
41 if err != nil {
42 log.Fatal(err)
43 }
44}
45
46func writeFile(filename string, in io.Reader, perm os.FileMode) error {
47 out, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
48 if err != nil {
49 return err
50 }
51 _, err = io.Copy(out, in)
52 if err != nil {
53 out.Close()
54 return err
55 }
56
57 return out.Close()
58}
59
Jiyong Parkdc48afd2020-11-18 15:52:07 +090060func writeSymlink(filename string, in io.Reader) error {
61 b, err := ioutil.ReadAll(in)
62 if err != nil {
63 return err
64 }
65 dest := string(b)
66 err = os.Symlink(dest, filename)
67 return err
68}
69
Colin Cross436b7652018-03-15 16:24:10 -070070func main() {
71 flag.Usage = func() {
72 fmt.Fprintln(os.Stderr, "usage: zipsync -d <output dir> [-l <output file>] [-f <pattern>] [zip]...")
73 flag.PrintDefaults()
74 }
75
76 flag.Parse()
77
78 if *outputDir == "" {
79 flag.Usage()
80 os.Exit(1)
81 }
82
83 inputs := flag.Args()
84
85 // For now, just wipe the output directory and replace its contents with the zip files
86 // Eventually this could only modify the directory contents as necessary to bring it up
87 // to date with the zip files.
88 must(os.RemoveAll(*outputDir))
89
90 must(os.MkdirAll(*outputDir, 0777))
91
92 var files []string
93 seen := make(map[string]string)
94
Dan Willemsen304cfec2019-05-28 14:49:06 -070095 if *zipPrefix != "" {
96 *zipPrefix = filepath.Clean(*zipPrefix) + "/"
97 }
98
Colin Cross436b7652018-03-15 16:24:10 -070099 for _, input := range inputs {
100 reader, err := zip.OpenReader(input)
101 if err != nil {
102 log.Fatal(err)
103 }
104 defer reader.Close()
105
106 for _, f := range reader.File {
Dan Willemsen304cfec2019-05-28 14:49:06 -0700107 name := f.Name
108 if *zipPrefix != "" {
109 if !strings.HasPrefix(name, *zipPrefix) {
110 continue
111 }
112 name = strings.TrimPrefix(name, *zipPrefix)
113 }
Zi Wang6a66e3d2023-06-13 10:40:52 -0700114
115 if filter != nil {
116 if match, err := filter.Match(filepath.Base(name)); err != nil {
Colin Cross436b7652018-03-15 16:24:10 -0700117 log.Fatal(err)
118 } else if !match {
119 continue
120 }
121 }
Zi Wang6a66e3d2023-06-13 10:40:52 -0700122
Dan Willemsen304cfec2019-05-28 14:49:06 -0700123 if filepath.IsAbs(name) {
124 log.Fatalf("%q in %q is an absolute path", name, input)
Colin Cross436b7652018-03-15 16:24:10 -0700125 }
126
Dan Willemsen304cfec2019-05-28 14:49:06 -0700127 if prev, exists := seen[name]; exists {
128 log.Fatalf("%q found in both %q and %q", name, prev, input)
Colin Cross436b7652018-03-15 16:24:10 -0700129 }
Dan Willemsen304cfec2019-05-28 14:49:06 -0700130 seen[name] = input
Colin Cross436b7652018-03-15 16:24:10 -0700131
Dan Willemsen304cfec2019-05-28 14:49:06 -0700132 filename := filepath.Join(*outputDir, name)
Colin Cross436b7652018-03-15 16:24:10 -0700133 if f.FileInfo().IsDir() {
Jaewoong Junga86983d2020-04-03 18:35:38 -0700134 must(os.MkdirAll(filename, 0777))
Colin Cross436b7652018-03-15 16:24:10 -0700135 } else {
136 must(os.MkdirAll(filepath.Dir(filename), 0777))
137 in, err := f.Open()
138 if err != nil {
139 log.Fatal(err)
140 }
Jiyong Parkdc48afd2020-11-18 15:52:07 +0900141 if f.FileInfo().Mode()&os.ModeSymlink != 0 {
142 must(writeSymlink(filename, in))
143 } else {
144 must(writeFile(filename, in, f.FileInfo().Mode()))
145 }
Colin Cross436b7652018-03-15 16:24:10 -0700146 in.Close()
147 files = append(files, filename)
148 }
149 }
150 }
151
152 if *outputFile != "" {
Colin Cross628d55d2018-03-22 12:38:05 -0700153 data := strings.Join(files, "\n")
154 if len(files) > 0 {
155 data += "\n"
156 }
Colin Cross436b7652018-03-15 16:24:10 -0700157 must(ioutil.WriteFile(*outputFile, []byte(data), 0666))
158 }
159}
Zi Wang6a66e3d2023-06-13 10:40:52 -0700160
161type multiFlag []string
162
163func (m *multiFlag) String() string {
164 return strings.Join(*m, " ")
165}
166
167func (m *multiFlag) Set(s string) error {
168 *m = append(*m, s)
169 return nil
170}
171
172func (m *multiFlag) Match(s string) (bool, error) {
173 if m == nil {
174 return false, nil
175 }
176 for _, f := range *m {
177 if match, err := filepath.Match(f, s); err != nil {
178 return false, err
179 } else if match {
180 return true, nil
181 }
182 }
183 return false, nil
184}