1*333d2b36SAndroid Build Coastguard Worker// Copyright 2016 Google Inc. All rights reserved. 2*333d2b36SAndroid Build Coastguard Worker// 3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*333d2b36SAndroid Build Coastguard Worker// 7*333d2b36SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*333d2b36SAndroid Build Coastguard Worker// 9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*333d2b36SAndroid Build Coastguard Worker// limitations under the License. 14*333d2b36SAndroid Build Coastguard Worker 15*333d2b36SAndroid Build Coastguard Workerpackage main 16*333d2b36SAndroid Build Coastguard Worker 17*333d2b36SAndroid Build Coastguard Workerimport ( 18*333d2b36SAndroid Build Coastguard Worker "flag" 19*333d2b36SAndroid Build Coastguard Worker "fmt" 20*333d2b36SAndroid Build Coastguard Worker "io" 21*333d2b36SAndroid Build Coastguard Worker "log" 22*333d2b36SAndroid Build Coastguard Worker "os" 23*333d2b36SAndroid Build Coastguard Worker "path/filepath" 24*333d2b36SAndroid Build Coastguard Worker "sort" 25*333d2b36SAndroid Build Coastguard Worker "strings" 26*333d2b36SAndroid Build Coastguard Worker "time" 27*333d2b36SAndroid Build Coastguard Worker 28*333d2b36SAndroid Build Coastguard Worker "github.com/google/blueprint/pathtools" 29*333d2b36SAndroid Build Coastguard Worker 30*333d2b36SAndroid Build Coastguard Worker "android/soong/jar" 31*333d2b36SAndroid Build Coastguard Worker "android/soong/third_party/zip" 32*333d2b36SAndroid Build Coastguard Worker) 33*333d2b36SAndroid Build Coastguard Worker 34*333d2b36SAndroid Build Coastguard Workervar ( 35*333d2b36SAndroid Build Coastguard Worker input = flag.String("i", "", "zip file to read from") 36*333d2b36SAndroid Build Coastguard Worker output = flag.String("o", "", "output file") 37*333d2b36SAndroid Build Coastguard Worker sortGlobs = flag.Bool("s", false, "sort matches from each glob (defaults to the order from the input zip file)") 38*333d2b36SAndroid Build Coastguard Worker sortJava = flag.Bool("j", false, "sort using jar ordering within each glob (META-INF/MANIFEST.MF first)") 39*333d2b36SAndroid Build Coastguard Worker setTime = flag.Bool("t", false, "set timestamps to 2009-01-01 00:00:00") 40*333d2b36SAndroid Build Coastguard Worker 41*333d2b36SAndroid Build Coastguard Worker staticTime = time.Date(2009, 1, 1, 0, 0, 0, 0, time.UTC) 42*333d2b36SAndroid Build Coastguard Worker 43*333d2b36SAndroid Build Coastguard Worker excludes multiFlag 44*333d2b36SAndroid Build Coastguard Worker includes multiFlag 45*333d2b36SAndroid Build Coastguard Worker uncompress multiFlag 46*333d2b36SAndroid Build Coastguard Worker) 47*333d2b36SAndroid Build Coastguard Worker 48*333d2b36SAndroid Build Coastguard Workerfunc init() { 49*333d2b36SAndroid Build Coastguard Worker flag.Var(&excludes, "x", "exclude a filespec from the output") 50*333d2b36SAndroid Build Coastguard Worker flag.Var(&includes, "X", "include a filespec in the output that was previously excluded") 51*333d2b36SAndroid Build Coastguard Worker flag.Var(&uncompress, "0", "convert a filespec to uncompressed in the output") 52*333d2b36SAndroid Build Coastguard Worker} 53*333d2b36SAndroid Build Coastguard Worker 54*333d2b36SAndroid Build Coastguard Workerfunc main() { 55*333d2b36SAndroid Build Coastguard Worker flag.Usage = func() { 56*333d2b36SAndroid Build Coastguard Worker fmt.Fprintln(os.Stderr, "usage: zip2zip -i zipfile -o zipfile [-s|-j] [-t] [filespec]...") 57*333d2b36SAndroid Build Coastguard Worker flag.PrintDefaults() 58*333d2b36SAndroid Build Coastguard Worker fmt.Fprintln(os.Stderr, " filespec:") 59*333d2b36SAndroid Build Coastguard Worker fmt.Fprintln(os.Stderr, " <name>") 60*333d2b36SAndroid Build Coastguard Worker fmt.Fprintln(os.Stderr, " <in_name>:<out_name>") 61*333d2b36SAndroid Build Coastguard Worker fmt.Fprintln(os.Stderr, " <glob>[:<out_dir>]") 62*333d2b36SAndroid Build Coastguard Worker fmt.Fprintln(os.Stderr, "") 63*333d2b36SAndroid Build Coastguard Worker fmt.Fprintln(os.Stderr, "<glob> uses the rules at https://godoc.org/github.com/google/blueprint/pathtools/#Match") 64*333d2b36SAndroid Build Coastguard Worker fmt.Fprintln(os.Stderr, "") 65*333d2b36SAndroid Build Coastguard Worker fmt.Fprintln(os.Stderr, "Files will be copied with their existing compression from the input zipfile to") 66*333d2b36SAndroid Build Coastguard Worker fmt.Fprintln(os.Stderr, "the output zipfile, in the order of filespec arguments.") 67*333d2b36SAndroid Build Coastguard Worker fmt.Fprintln(os.Stderr, "") 68*333d2b36SAndroid Build Coastguard Worker fmt.Fprintln(os.Stderr, "If no filepsec is provided all files and directories are copied.") 69*333d2b36SAndroid Build Coastguard Worker } 70*333d2b36SAndroid Build Coastguard Worker 71*333d2b36SAndroid Build Coastguard Worker flag.Parse() 72*333d2b36SAndroid Build Coastguard Worker 73*333d2b36SAndroid Build Coastguard Worker if *input == "" || *output == "" { 74*333d2b36SAndroid Build Coastguard Worker flag.Usage() 75*333d2b36SAndroid Build Coastguard Worker os.Exit(1) 76*333d2b36SAndroid Build Coastguard Worker } 77*333d2b36SAndroid Build Coastguard Worker 78*333d2b36SAndroid Build Coastguard Worker log.SetFlags(log.Lshortfile) 79*333d2b36SAndroid Build Coastguard Worker 80*333d2b36SAndroid Build Coastguard Worker reader, err := zip.OpenReader(*input) 81*333d2b36SAndroid Build Coastguard Worker if err != nil { 82*333d2b36SAndroid Build Coastguard Worker log.Fatal(err) 83*333d2b36SAndroid Build Coastguard Worker } 84*333d2b36SAndroid Build Coastguard Worker defer reader.Close() 85*333d2b36SAndroid Build Coastguard Worker 86*333d2b36SAndroid Build Coastguard Worker output, err := os.Create(*output) 87*333d2b36SAndroid Build Coastguard Worker if err != nil { 88*333d2b36SAndroid Build Coastguard Worker log.Fatal(err) 89*333d2b36SAndroid Build Coastguard Worker } 90*333d2b36SAndroid Build Coastguard Worker defer output.Close() 91*333d2b36SAndroid Build Coastguard Worker 92*333d2b36SAndroid Build Coastguard Worker writer := zip.NewWriter(output) 93*333d2b36SAndroid Build Coastguard Worker defer func() { 94*333d2b36SAndroid Build Coastguard Worker err := writer.Close() 95*333d2b36SAndroid Build Coastguard Worker if err != nil { 96*333d2b36SAndroid Build Coastguard Worker log.Fatal(err) 97*333d2b36SAndroid Build Coastguard Worker } 98*333d2b36SAndroid Build Coastguard Worker }() 99*333d2b36SAndroid Build Coastguard Worker 100*333d2b36SAndroid Build Coastguard Worker if err := zip2zip(&reader.Reader, writer, *sortGlobs, *sortJava, *setTime, 101*333d2b36SAndroid Build Coastguard Worker flag.Args(), excludes, includes, uncompress); err != nil { 102*333d2b36SAndroid Build Coastguard Worker 103*333d2b36SAndroid Build Coastguard Worker log.Fatal(err) 104*333d2b36SAndroid Build Coastguard Worker } 105*333d2b36SAndroid Build Coastguard Worker} 106*333d2b36SAndroid Build Coastguard Worker 107*333d2b36SAndroid Build Coastguard Workertype pair struct { 108*333d2b36SAndroid Build Coastguard Worker *zip.File 109*333d2b36SAndroid Build Coastguard Worker newName string 110*333d2b36SAndroid Build Coastguard Worker uncompress bool 111*333d2b36SAndroid Build Coastguard Worker} 112*333d2b36SAndroid Build Coastguard Worker 113*333d2b36SAndroid Build Coastguard Workerfunc zip2zip(reader *zip.Reader, writer *zip.Writer, sortOutput, sortJava, setTime bool, 114*333d2b36SAndroid Build Coastguard Worker args []string, excludes, includes multiFlag, uncompresses []string) error { 115*333d2b36SAndroid Build Coastguard Worker 116*333d2b36SAndroid Build Coastguard Worker matches := []pair{} 117*333d2b36SAndroid Build Coastguard Worker 118*333d2b36SAndroid Build Coastguard Worker sortMatches := func(matches []pair) { 119*333d2b36SAndroid Build Coastguard Worker if sortJava { 120*333d2b36SAndroid Build Coastguard Worker sort.SliceStable(matches, func(i, j int) bool { 121*333d2b36SAndroid Build Coastguard Worker return jar.EntryNamesLess(matches[i].newName, matches[j].newName) 122*333d2b36SAndroid Build Coastguard Worker }) 123*333d2b36SAndroid Build Coastguard Worker } else if sortOutput { 124*333d2b36SAndroid Build Coastguard Worker sort.SliceStable(matches, func(i, j int) bool { 125*333d2b36SAndroid Build Coastguard Worker return matches[i].newName < matches[j].newName 126*333d2b36SAndroid Build Coastguard Worker }) 127*333d2b36SAndroid Build Coastguard Worker } 128*333d2b36SAndroid Build Coastguard Worker } 129*333d2b36SAndroid Build Coastguard Worker 130*333d2b36SAndroid Build Coastguard Worker for _, arg := range args { 131*333d2b36SAndroid Build Coastguard Worker input, output := includeSplit(arg) 132*333d2b36SAndroid Build Coastguard Worker 133*333d2b36SAndroid Build Coastguard Worker var includeMatches []pair 134*333d2b36SAndroid Build Coastguard Worker 135*333d2b36SAndroid Build Coastguard Worker for _, file := range reader.File { 136*333d2b36SAndroid Build Coastguard Worker var newName string 137*333d2b36SAndroid Build Coastguard Worker if match, err := pathtools.Match(input, file.Name); err != nil { 138*333d2b36SAndroid Build Coastguard Worker return err 139*333d2b36SAndroid Build Coastguard Worker } else if match { 140*333d2b36SAndroid Build Coastguard Worker if output == "" { 141*333d2b36SAndroid Build Coastguard Worker newName = file.Name 142*333d2b36SAndroid Build Coastguard Worker } else { 143*333d2b36SAndroid Build Coastguard Worker if pathtools.IsGlob(input) { 144*333d2b36SAndroid Build Coastguard Worker // If the input is a glob then the output is a directory. 145*333d2b36SAndroid Build Coastguard Worker rel, err := filepath.Rel(constantPartOfPattern(input), file.Name) 146*333d2b36SAndroid Build Coastguard Worker if err != nil { 147*333d2b36SAndroid Build Coastguard Worker return err 148*333d2b36SAndroid Build Coastguard Worker } else if strings.HasPrefix("../", rel) { 149*333d2b36SAndroid Build Coastguard Worker return fmt.Errorf("globbed path %q was not in %q", file.Name, constantPartOfPattern(input)) 150*333d2b36SAndroid Build Coastguard Worker } 151*333d2b36SAndroid Build Coastguard Worker newName = filepath.Join(output, rel) 152*333d2b36SAndroid Build Coastguard Worker } else { 153*333d2b36SAndroid Build Coastguard Worker // Otherwise it is a file. 154*333d2b36SAndroid Build Coastguard Worker newName = output 155*333d2b36SAndroid Build Coastguard Worker } 156*333d2b36SAndroid Build Coastguard Worker } 157*333d2b36SAndroid Build Coastguard Worker includeMatches = append(includeMatches, pair{file, newName, false}) 158*333d2b36SAndroid Build Coastguard Worker } 159*333d2b36SAndroid Build Coastguard Worker } 160*333d2b36SAndroid Build Coastguard Worker 161*333d2b36SAndroid Build Coastguard Worker sortMatches(includeMatches) 162*333d2b36SAndroid Build Coastguard Worker matches = append(matches, includeMatches...) 163*333d2b36SAndroid Build Coastguard Worker } 164*333d2b36SAndroid Build Coastguard Worker 165*333d2b36SAndroid Build Coastguard Worker if len(args) == 0 { 166*333d2b36SAndroid Build Coastguard Worker // implicitly match everything 167*333d2b36SAndroid Build Coastguard Worker for _, file := range reader.File { 168*333d2b36SAndroid Build Coastguard Worker matches = append(matches, pair{file, file.Name, false}) 169*333d2b36SAndroid Build Coastguard Worker } 170*333d2b36SAndroid Build Coastguard Worker sortMatches(matches) 171*333d2b36SAndroid Build Coastguard Worker } 172*333d2b36SAndroid Build Coastguard Worker 173*333d2b36SAndroid Build Coastguard Worker var matchesAfterExcludes []pair 174*333d2b36SAndroid Build Coastguard Worker seen := make(map[string]*zip.File) 175*333d2b36SAndroid Build Coastguard Worker 176*333d2b36SAndroid Build Coastguard Worker for _, match := range matches { 177*333d2b36SAndroid Build Coastguard Worker // Filter out matches whose original file name matches an exclude filter, unless it also matches an 178*333d2b36SAndroid Build Coastguard Worker // include filter 179*333d2b36SAndroid Build Coastguard Worker if exclude, err := excludes.Match(match.File.Name); err != nil { 180*333d2b36SAndroid Build Coastguard Worker return err 181*333d2b36SAndroid Build Coastguard Worker } else if exclude { 182*333d2b36SAndroid Build Coastguard Worker if include, err := includes.Match(match.File.Name); err != nil { 183*333d2b36SAndroid Build Coastguard Worker return err 184*333d2b36SAndroid Build Coastguard Worker } else if !include { 185*333d2b36SAndroid Build Coastguard Worker continue 186*333d2b36SAndroid Build Coastguard Worker } 187*333d2b36SAndroid Build Coastguard Worker } 188*333d2b36SAndroid Build Coastguard Worker 189*333d2b36SAndroid Build Coastguard Worker // Check for duplicate output names, ignoring ones that come from the same input zip entry. 190*333d2b36SAndroid Build Coastguard Worker if prev, exists := seen[match.newName]; exists { 191*333d2b36SAndroid Build Coastguard Worker if prev != match.File { 192*333d2b36SAndroid Build Coastguard Worker return fmt.Errorf("multiple entries for %q with different contents", match.newName) 193*333d2b36SAndroid Build Coastguard Worker } 194*333d2b36SAndroid Build Coastguard Worker continue 195*333d2b36SAndroid Build Coastguard Worker } 196*333d2b36SAndroid Build Coastguard Worker seen[match.newName] = match.File 197*333d2b36SAndroid Build Coastguard Worker 198*333d2b36SAndroid Build Coastguard Worker for _, u := range uncompresses { 199*333d2b36SAndroid Build Coastguard Worker if uncompressMatch, err := pathtools.Match(u, match.newName); err != nil { 200*333d2b36SAndroid Build Coastguard Worker return err 201*333d2b36SAndroid Build Coastguard Worker } else if uncompressMatch { 202*333d2b36SAndroid Build Coastguard Worker match.uncompress = true 203*333d2b36SAndroid Build Coastguard Worker break 204*333d2b36SAndroid Build Coastguard Worker } 205*333d2b36SAndroid Build Coastguard Worker } 206*333d2b36SAndroid Build Coastguard Worker 207*333d2b36SAndroid Build Coastguard Worker matchesAfterExcludes = append(matchesAfterExcludes, match) 208*333d2b36SAndroid Build Coastguard Worker } 209*333d2b36SAndroid Build Coastguard Worker 210*333d2b36SAndroid Build Coastguard Worker for _, match := range matchesAfterExcludes { 211*333d2b36SAndroid Build Coastguard Worker if setTime { 212*333d2b36SAndroid Build Coastguard Worker match.File.SetModTime(staticTime) 213*333d2b36SAndroid Build Coastguard Worker } 214*333d2b36SAndroid Build Coastguard Worker if match.uncompress && match.File.FileHeader.Method != zip.Store { 215*333d2b36SAndroid Build Coastguard Worker fh := match.File.FileHeader 216*333d2b36SAndroid Build Coastguard Worker fh.Name = match.newName 217*333d2b36SAndroid Build Coastguard Worker fh.Method = zip.Store 218*333d2b36SAndroid Build Coastguard Worker fh.CompressedSize64 = fh.UncompressedSize64 219*333d2b36SAndroid Build Coastguard Worker 220*333d2b36SAndroid Build Coastguard Worker zw, err := writer.CreateHeaderAndroid(&fh) 221*333d2b36SAndroid Build Coastguard Worker if err != nil { 222*333d2b36SAndroid Build Coastguard Worker return err 223*333d2b36SAndroid Build Coastguard Worker } 224*333d2b36SAndroid Build Coastguard Worker 225*333d2b36SAndroid Build Coastguard Worker zr, err := match.File.Open() 226*333d2b36SAndroid Build Coastguard Worker if err != nil { 227*333d2b36SAndroid Build Coastguard Worker return err 228*333d2b36SAndroid Build Coastguard Worker } 229*333d2b36SAndroid Build Coastguard Worker 230*333d2b36SAndroid Build Coastguard Worker _, err = io.Copy(zw, zr) 231*333d2b36SAndroid Build Coastguard Worker zr.Close() 232*333d2b36SAndroid Build Coastguard Worker if err != nil { 233*333d2b36SAndroid Build Coastguard Worker return err 234*333d2b36SAndroid Build Coastguard Worker } 235*333d2b36SAndroid Build Coastguard Worker } else { 236*333d2b36SAndroid Build Coastguard Worker err := writer.CopyFrom(match.File, match.newName) 237*333d2b36SAndroid Build Coastguard Worker if err != nil { 238*333d2b36SAndroid Build Coastguard Worker return err 239*333d2b36SAndroid Build Coastguard Worker } 240*333d2b36SAndroid Build Coastguard Worker } 241*333d2b36SAndroid Build Coastguard Worker } 242*333d2b36SAndroid Build Coastguard Worker 243*333d2b36SAndroid Build Coastguard Worker return nil 244*333d2b36SAndroid Build Coastguard Worker} 245*333d2b36SAndroid Build Coastguard Worker 246*333d2b36SAndroid Build Coastguard Workerfunc includeSplit(s string) (string, string) { 247*333d2b36SAndroid Build Coastguard Worker split := strings.SplitN(s, ":", 2) 248*333d2b36SAndroid Build Coastguard Worker if len(split) == 2 { 249*333d2b36SAndroid Build Coastguard Worker return split[0], split[1] 250*333d2b36SAndroid Build Coastguard Worker } else { 251*333d2b36SAndroid Build Coastguard Worker return split[0], "" 252*333d2b36SAndroid Build Coastguard Worker } 253*333d2b36SAndroid Build Coastguard Worker} 254*333d2b36SAndroid Build Coastguard Worker 255*333d2b36SAndroid Build Coastguard Workertype multiFlag []string 256*333d2b36SAndroid Build Coastguard Worker 257*333d2b36SAndroid Build Coastguard Workerfunc (m *multiFlag) String() string { 258*333d2b36SAndroid Build Coastguard Worker return strings.Join(*m, " ") 259*333d2b36SAndroid Build Coastguard Worker} 260*333d2b36SAndroid Build Coastguard Worker 261*333d2b36SAndroid Build Coastguard Workerfunc (m *multiFlag) Set(s string) error { 262*333d2b36SAndroid Build Coastguard Worker *m = append(*m, s) 263*333d2b36SAndroid Build Coastguard Worker return nil 264*333d2b36SAndroid Build Coastguard Worker} 265*333d2b36SAndroid Build Coastguard Worker 266*333d2b36SAndroid Build Coastguard Workerfunc (m *multiFlag) Match(s string) (bool, error) { 267*333d2b36SAndroid Build Coastguard Worker if m == nil { 268*333d2b36SAndroid Build Coastguard Worker return false, nil 269*333d2b36SAndroid Build Coastguard Worker } 270*333d2b36SAndroid Build Coastguard Worker for _, f := range *m { 271*333d2b36SAndroid Build Coastguard Worker if match, err := pathtools.Match(f, s); err != nil { 272*333d2b36SAndroid Build Coastguard Worker return false, err 273*333d2b36SAndroid Build Coastguard Worker } else if match { 274*333d2b36SAndroid Build Coastguard Worker return true, nil 275*333d2b36SAndroid Build Coastguard Worker } 276*333d2b36SAndroid Build Coastguard Worker } 277*333d2b36SAndroid Build Coastguard Worker return false, nil 278*333d2b36SAndroid Build Coastguard Worker} 279*333d2b36SAndroid Build Coastguard Worker 280*333d2b36SAndroid Build Coastguard Workerfunc constantPartOfPattern(pattern string) string { 281*333d2b36SAndroid Build Coastguard Worker ret := "" 282*333d2b36SAndroid Build Coastguard Worker for pattern != "" { 283*333d2b36SAndroid Build Coastguard Worker var first string 284*333d2b36SAndroid Build Coastguard Worker first, pattern = splitFirst(pattern) 285*333d2b36SAndroid Build Coastguard Worker if pathtools.IsGlob(first) { 286*333d2b36SAndroid Build Coastguard Worker return ret 287*333d2b36SAndroid Build Coastguard Worker } 288*333d2b36SAndroid Build Coastguard Worker ret = filepath.Join(ret, first) 289*333d2b36SAndroid Build Coastguard Worker } 290*333d2b36SAndroid Build Coastguard Worker return ret 291*333d2b36SAndroid Build Coastguard Worker} 292*333d2b36SAndroid Build Coastguard Worker 293*333d2b36SAndroid Build Coastguard Workerfunc splitFirst(path string) (string, string) { 294*333d2b36SAndroid Build Coastguard Worker i := strings.IndexRune(path, filepath.Separator) 295*333d2b36SAndroid Build Coastguard Worker if i < 0 { 296*333d2b36SAndroid Build Coastguard Worker return path, "" 297*333d2b36SAndroid Build Coastguard Worker } 298*333d2b36SAndroid Build Coastguard Worker return path[:i], path[i+1:] 299*333d2b36SAndroid Build Coastguard Worker} 300