diff --git a/Android.bp b/Android.bp index e1a66213a..523bec16e 100644 --- a/Android.bp +++ b/Android.bp @@ -10,6 +10,8 @@ // 2) Build again // +subdirs = ["third_party/zip"] + bootstrap_go_binary { name: "soong_build", deps: [ @@ -194,6 +196,14 @@ bootstrap_go_package { pluginFor: ["soong_build"], } +blueprint_go_binary { + name: "zip2zip", + deps: ["android-archive-zip"], + srcs: [ + "cmd/zip2zip/zip2zip.go", + ], +} + blueprint_go_binary { name: "soong_jar", srcs: [ diff --git a/cmd/zip2zip/zip2zip.go b/cmd/zip2zip/zip2zip.go new file mode 100644 index 000000000..8e7523ffc --- /dev/null +++ b/cmd/zip2zip/zip2zip.go @@ -0,0 +1,128 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "flag" + "fmt" + "os" + "path/filepath" + "strings" + + "android/soong/third_party/zip" +) + +var ( + input = flag.String("i", "", "zip file to read from") + output = flag.String("o", "", "output file") +) + +func usage() { + fmt.Fprintln(os.Stderr, "usage: zip2zip -i zipfile -o zipfile [filespec]...") + flag.PrintDefaults() + fmt.Fprintln(os.Stderr, " filespec:") + fmt.Fprintln(os.Stderr, " ") + fmt.Fprintln(os.Stderr, " :") + fmt.Fprintln(os.Stderr, " :/") + fmt.Fprintln(os.Stderr, "") + fmt.Fprintln(os.Stderr, "Files will be copied with their existing compression from the input zipfile to") + fmt.Fprintln(os.Stderr, "the output zipfile, in the order of filespec arguments") + os.Exit(2) +} + +func main() { + flag.Parse() + + if flag.NArg() == 0 || *input == "" || *output == "" { + usage() + } + + reader, err := zip.OpenReader(*input) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(3) + } + defer reader.Close() + + output, err := os.Create(*output) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(4) + } + defer output.Close() + + writer := zip.NewWriter(output) + defer func() { + err := writer.Close() + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(5) + } + }() + + for _, arg := range flag.Args() { + var input string + var output string + + // Reserve escaping for future implementation, so make sure no + // one is using \ and expecting a certain behavior. + if strings.Contains(arg, "\\") { + fmt.Fprintln(os.Stderr, "\\ characters are not currently supported") + os.Exit(6) + } + + args := strings.SplitN(arg, ":", 2) + input = args[0] + if len(args) == 2 { + output = args[1] + } + + if strings.IndexAny(input, "*?[") >= 0 { + for _, file := range reader.File { + if match, err := filepath.Match(input, file.Name); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(7) + } else if match { + var newFileName string + if output == "" { + newFileName = file.Name + } else { + _, name := filepath.Split(file.Name) + newFileName = filepath.Join(output, name) + } + err = writer.CopyFrom(file, newFileName) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(8) + } + } + } + } else { + if output == "" { + output = input + } + for _, file := range reader.File { + if input == file.Name { + err = writer.CopyFrom(file, output) + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(8) + } + break + } + } + } + } +} diff --git a/third_party/zip/Android.bp b/third_party/zip/Android.bp new file mode 100644 index 000000000..044e6f8ec --- /dev/null +++ b/third_party/zip/Android.bp @@ -0,0 +1,31 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +bootstrap_go_package { + name: "android-archive-zip", + pkgPath: "android/soong/third_party/zip", + srcs: [ + "reader.go", + "register.go", + "struct.go", + "writer.go", + + "android.go", + ], + testSrcs: [ + "reader_test.go", + "writer_test.go", + "zip_test.go", + ], +} diff --git a/third_party/zip/android.go b/third_party/zip/android.go new file mode 100644 index 000000000..7a3a2213b --- /dev/null +++ b/third_party/zip/android.go @@ -0,0 +1,70 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package zip + +import ( + "io" +) + +func (w *Writer) CopyFrom(orig *File, newName string) error { + if w.last != nil && !w.last.closed { + if err := w.last.close(); err != nil { + return err + } + w.last = nil + } + + fileHeader := orig.FileHeader + fileHeader.Name = newName + fh := &fileHeader + fh.Flags |= 0x8 + + h := &header{ + FileHeader: fh, + offset: uint64(w.cw.count), + } + w.dir = append(w.dir, h) + + if err := writeHeader(w.cw, fh); err != nil { + return err + } + + // Copy data + dataOffset, err := orig.DataOffset() + if err != nil { + return err + } + io.Copy(w.cw, io.NewSectionReader(orig.zipr, dataOffset, int64(orig.CompressedSize64))) + + // Write data descriptor. + var buf []byte + if fh.isZip64() { + buf = make([]byte, dataDescriptor64Len) + } else { + buf = make([]byte, dataDescriptorLen) + } + b := writeBuf(buf) + b.uint32(dataDescriptorSignature) + b.uint32(fh.CRC32) + if fh.isZip64() { + b.uint64(fh.CompressedSize64) + b.uint64(fh.UncompressedSize64) + } else { + b.uint32(fh.CompressedSize) + b.uint32(fh.UncompressedSize) + } + _, err = w.cw.Write(buf) + return err +}