blob: 48c36ccc29499e3a6a981dc5cfb2489efc695406 [file] [log] [blame]
Dan Willemsen3bf1a082016-08-03 00:35:25 -07001// Copyright 2016 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 "flag"
19 "fmt"
Dan Willemsen82218f22017-06-19 16:35:00 -070020 "log"
Dan Willemsen3bf1a082016-08-03 00:35:25 -070021 "os"
22 "path/filepath"
Dan Willemsen82218f22017-06-19 16:35:00 -070023 "sort"
Dan Willemsen3bf1a082016-08-03 00:35:25 -070024 "strings"
Dan Willemsen82218f22017-06-19 16:35:00 -070025 "time"
Dan Willemsen3bf1a082016-08-03 00:35:25 -070026
27 "android/soong/third_party/zip"
28)
29
30var (
Dan Willemsen82218f22017-06-19 16:35:00 -070031 input = flag.String("i", "", "zip file to read from")
32 output = flag.String("o", "", "output file")
33 sortGlobs = flag.Bool("s", false, "sort matches from each glob (defaults to the order from the input zip file)")
34 setTime = flag.Bool("t", false, "set timestamps to 2009-01-01 00:00:00")
35
36 staticTime = time.Date(2009, 1, 1, 0, 0, 0, 0, time.UTC)
Dan Willemsen3bf1a082016-08-03 00:35:25 -070037)
38
Dan Willemsen3bf1a082016-08-03 00:35:25 -070039func main() {
Dan Willemsen82218f22017-06-19 16:35:00 -070040 flag.Usage = func() {
41 fmt.Fprintln(os.Stderr, "usage: zip2zip -i zipfile -o zipfile [-s] [-t] [filespec]...")
42 flag.PrintDefaults()
43 fmt.Fprintln(os.Stderr, " filespec:")
44 fmt.Fprintln(os.Stderr, " <name>")
45 fmt.Fprintln(os.Stderr, " <in_name>:<out_name>")
46 fmt.Fprintln(os.Stderr, " <glob>:<out_dir>/")
47 fmt.Fprintln(os.Stderr, "")
48 fmt.Fprintln(os.Stderr, "<glob> uses the rules at https://golang.org/pkg/path/filepath/#Match")
49 fmt.Fprintln(os.Stderr, "As a special exception, '**' is supported to specify all files in the input zip")
50 fmt.Fprintln(os.Stderr, "")
51 fmt.Fprintln(os.Stderr, "Files will be copied with their existing compression from the input zipfile to")
52 fmt.Fprintln(os.Stderr, "the output zipfile, in the order of filespec arguments")
53 }
54
Dan Willemsen3bf1a082016-08-03 00:35:25 -070055 flag.Parse()
56
57 if flag.NArg() == 0 || *input == "" || *output == "" {
Dan Willemsen82218f22017-06-19 16:35:00 -070058 flag.Usage()
59 os.Exit(1)
Dan Willemsen3bf1a082016-08-03 00:35:25 -070060 }
61
Dan Willemsen82218f22017-06-19 16:35:00 -070062 log.SetFlags(log.Lshortfile)
63
Dan Willemsen3bf1a082016-08-03 00:35:25 -070064 reader, err := zip.OpenReader(*input)
65 if err != nil {
Dan Willemsen82218f22017-06-19 16:35:00 -070066 log.Fatal(err)
Dan Willemsen3bf1a082016-08-03 00:35:25 -070067 }
68 defer reader.Close()
69
70 output, err := os.Create(*output)
71 if err != nil {
Dan Willemsen82218f22017-06-19 16:35:00 -070072 log.Fatal(err)
Dan Willemsen3bf1a082016-08-03 00:35:25 -070073 }
74 defer output.Close()
75
76 writer := zip.NewWriter(output)
77 defer func() {
78 err := writer.Close()
79 if err != nil {
Dan Willemsen82218f22017-06-19 16:35:00 -070080 log.Fatal(err)
Dan Willemsen3bf1a082016-08-03 00:35:25 -070081 }
82 }()
83
Dan Willemsen82218f22017-06-19 16:35:00 -070084 if err := zip2zip(&reader.Reader, writer, *sortGlobs, *setTime, flag.Args()); err != nil {
85 log.Fatal(err)
86 }
87}
88
89func zip2zip(reader *zip.Reader, writer *zip.Writer, sortGlobs, setTime bool, args []string) error {
90 for _, arg := range args {
Dan Willemsen3bf1a082016-08-03 00:35:25 -070091 var input string
92 var output string
93
94 // Reserve escaping for future implementation, so make sure no
95 // one is using \ and expecting a certain behavior.
96 if strings.Contains(arg, "\\") {
Dan Willemsen82218f22017-06-19 16:35:00 -070097 return fmt.Errorf("\\ characters are not currently supported")
Dan Willemsen3bf1a082016-08-03 00:35:25 -070098 }
99
100 args := strings.SplitN(arg, ":", 2)
101 input = args[0]
102 if len(args) == 2 {
103 output = args[1]
104 }
105
Dan Willemsen82218f22017-06-19 16:35:00 -0700106 type pair struct {
107 *zip.File
108 newName string
109 }
110
111 matches := []pair{}
Dan Willemsen3bf1a082016-08-03 00:35:25 -0700112 if strings.IndexAny(input, "*?[") >= 0 {
Dan Willemsen82218f22017-06-19 16:35:00 -0700113 matchAll := input == "**"
114 if !matchAll && strings.Contains(input, "**") {
115 return fmt.Errorf("** is only supported on its own, not with other characters")
116 }
117
Dan Willemsen3bf1a082016-08-03 00:35:25 -0700118 for _, file := range reader.File {
Dan Willemsen82218f22017-06-19 16:35:00 -0700119 match := matchAll
120
121 if !match {
122 var err error
123 match, err = filepath.Match(input, file.Name)
Dan Willemsen3bf1a082016-08-03 00:35:25 -0700124 if err != nil {
Dan Willemsen82218f22017-06-19 16:35:00 -0700125 return err
Dan Willemsen3bf1a082016-08-03 00:35:25 -0700126 }
127 }
Dan Willemsen82218f22017-06-19 16:35:00 -0700128
129 if match {
130 var newName string
131 if output == "" {
132 newName = file.Name
133 } else {
134 _, name := filepath.Split(file.Name)
135 newName = filepath.Join(output, name)
136 }
137 matches = append(matches, pair{file, newName})
138 }
139 }
140
141 if sortGlobs {
142 sort.SliceStable(matches, func(i, j int) bool {
143 return matches[i].newName < matches[j].newName
144 })
Dan Willemsen3bf1a082016-08-03 00:35:25 -0700145 }
146 } else {
147 if output == "" {
148 output = input
149 }
150 for _, file := range reader.File {
151 if input == file.Name {
Dan Willemsen82218f22017-06-19 16:35:00 -0700152 matches = append(matches, pair{file, output})
Dan Willemsen3bf1a082016-08-03 00:35:25 -0700153 break
154 }
155 }
156 }
Dan Willemsen82218f22017-06-19 16:35:00 -0700157
158 for _, match := range matches {
159 if setTime {
160 match.File.SetModTime(staticTime)
161 }
162 if err := writer.CopyFrom(match.File, match.newName); err != nil {
163 return err
164 }
165 }
Dan Willemsen3bf1a082016-08-03 00:35:25 -0700166 }
Dan Willemsen82218f22017-06-19 16:35:00 -0700167
168 return nil
Dan Willemsen3bf1a082016-08-03 00:35:25 -0700169}