Merge from mainline (167278:168000).
[official-gcc/graphite-test-results.git] / libgo / go / archive / zip / reader.go
blob579ba16029494a35aff3ec3bede977e08b5b112f
1 // Copyright 2010 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 /*
6 The zip package provides support for reading ZIP archives.
8 See: http://www.pkware.com/documents/casestudies/APPNOTE.TXT
10 This package does not support ZIP64 or disk spanning.
12 package zip
14 import (
15 "bufio"
16 "bytes"
17 "compress/flate"
18 "hash"
19 "hash/crc32"
20 "encoding/binary"
21 "io"
22 "os"
25 var (
26 FormatError = os.NewError("not a valid zip file")
27 UnsupportedMethod = os.NewError("unsupported compression algorithm")
28 ChecksumError = os.NewError("checksum error")
31 type Reader struct {
32 r io.ReaderAt
33 File []*File
34 Comment string
37 type File struct {
38 FileHeader
39 zipr io.ReaderAt
40 zipsize int64
41 headerOffset uint32
42 bodyOffset int64
45 // OpenReader will open the Zip file specified by name and return a Reader.
46 func OpenReader(name string) (*Reader, os.Error) {
47 f, err := os.Open(name, os.O_RDONLY, 0644)
48 if err != nil {
49 return nil, err
51 fi, err := f.Stat()
52 if err != nil {
53 return nil, err
55 return NewReader(f, fi.Size)
58 // NewReader returns a new Reader reading from r, which is assumed to
59 // have the given size in bytes.
60 func NewReader(r io.ReaderAt, size int64) (*Reader, os.Error) {
61 end, err := readDirectoryEnd(r, size)
62 if err != nil {
63 return nil, err
65 z := &Reader{
66 r: r,
67 File: make([]*File, end.directoryRecords),
68 Comment: end.comment,
70 rs := io.NewSectionReader(r, 0, size)
71 if _, err = rs.Seek(int64(end.directoryOffset), 0); err != nil {
72 return nil, err
74 buf := bufio.NewReader(rs)
75 for i := range z.File {
76 z.File[i] = &File{zipr: r, zipsize: size}
77 if err := readDirectoryHeader(z.File[i], buf); err != nil {
78 return nil, err
81 return z, nil
84 // Open returns a ReadCloser that provides access to the File's contents.
85 func (f *File) Open() (rc io.ReadCloser, err os.Error) {
86 off := int64(f.headerOffset)
87 if f.bodyOffset == 0 {
88 r := io.NewSectionReader(f.zipr, off, f.zipsize-off)
89 if err = readFileHeader(f, r); err != nil {
90 return
92 if f.bodyOffset, err = r.Seek(0, 1); err != nil {
93 return
96 r := io.NewSectionReader(f.zipr, off+f.bodyOffset, int64(f.CompressedSize))
97 switch f.Method {
98 case 0: // store (no compression)
99 rc = nopCloser{r}
100 case 8: // DEFLATE
101 rc = flate.NewReader(r)
102 default:
103 err = UnsupportedMethod
105 if rc != nil {
106 rc = &checksumReader{rc, crc32.NewIEEE(), f.CRC32}
108 return
111 type checksumReader struct {
112 rc io.ReadCloser
113 hash hash.Hash32
114 sum uint32
117 func (r *checksumReader) Read(b []byte) (n int, err os.Error) {
118 n, err = r.rc.Read(b)
119 r.hash.Write(b[:n])
120 if err != os.EOF {
121 return
123 if r.hash.Sum32() != r.sum {
124 err = ChecksumError
126 return
129 func (r *checksumReader) Close() os.Error { return r.rc.Close() }
131 type nopCloser struct {
132 io.Reader
135 func (f nopCloser) Close() os.Error { return nil }
137 func readFileHeader(f *File, r io.Reader) (err os.Error) {
138 defer func() {
139 if rerr, ok := recover().(os.Error); ok {
140 err = rerr
143 var (
144 signature uint32
145 filenameLength uint16
146 extraLength uint16
148 read(r, &signature)
149 if signature != fileHeaderSignature {
150 return FormatError
152 read(r, &f.ReaderVersion)
153 read(r, &f.Flags)
154 read(r, &f.Method)
155 read(r, &f.ModifiedTime)
156 read(r, &f.ModifiedDate)
157 read(r, &f.CRC32)
158 read(r, &f.CompressedSize)
159 read(r, &f.UncompressedSize)
160 read(r, &filenameLength)
161 read(r, &extraLength)
162 f.Name = string(readByteSlice(r, filenameLength))
163 f.Extra = readByteSlice(r, extraLength)
164 return
167 func readDirectoryHeader(f *File, r io.Reader) (err os.Error) {
168 defer func() {
169 if rerr, ok := recover().(os.Error); ok {
170 err = rerr
173 var (
174 signature uint32
175 filenameLength uint16
176 extraLength uint16
177 commentLength uint16
178 startDiskNumber uint16 // unused
179 internalAttributes uint16 // unused
180 externalAttributes uint32 // unused
182 read(r, &signature)
183 if signature != directoryHeaderSignature {
184 return FormatError
186 read(r, &f.CreatorVersion)
187 read(r, &f.ReaderVersion)
188 read(r, &f.Flags)
189 read(r, &f.Method)
190 read(r, &f.ModifiedTime)
191 read(r, &f.ModifiedDate)
192 read(r, &f.CRC32)
193 read(r, &f.CompressedSize)
194 read(r, &f.UncompressedSize)
195 read(r, &filenameLength)
196 read(r, &extraLength)
197 read(r, &commentLength)
198 read(r, &startDiskNumber)
199 read(r, &internalAttributes)
200 read(r, &externalAttributes)
201 read(r, &f.headerOffset)
202 f.Name = string(readByteSlice(r, filenameLength))
203 f.Extra = readByteSlice(r, extraLength)
204 f.Comment = string(readByteSlice(r, commentLength))
205 return
208 func readDirectoryEnd(r io.ReaderAt, size int64) (d *directoryEnd, err os.Error) {
209 // look for directoryEndSignature in the last 1k, then in the last 65k
210 var b []byte
211 for i, bLen := range []int64{1024, 65 * 1024} {
212 if bLen > size {
213 bLen = size
215 b = make([]byte, int(bLen))
216 if _, err := r.ReadAt(b, size-bLen); err != nil && err != os.EOF {
217 return nil, err
219 if p := findSignatureInBlock(b); p >= 0 {
220 b = b[p:]
221 break
223 if i == 1 || bLen == size {
224 return nil, FormatError
228 // read header into struct
229 defer func() {
230 if rerr, ok := recover().(os.Error); ok {
231 err = rerr
232 d = nil
235 br := bytes.NewBuffer(b[4:]) // skip over signature
236 d = new(directoryEnd)
237 read(br, &d.diskNbr)
238 read(br, &d.dirDiskNbr)
239 read(br, &d.dirRecordsThisDisk)
240 read(br, &d.directoryRecords)
241 read(br, &d.directorySize)
242 read(br, &d.directoryOffset)
243 read(br, &d.commentLen)
244 d.comment = string(readByteSlice(br, d.commentLen))
245 return d, nil
248 func findSignatureInBlock(b []byte) int {
249 const minSize = 4 + 2 + 2 + 2 + 2 + 4 + 4 + 2 // fixed part of header
250 for i := len(b) - minSize; i >= 0; i-- {
251 // defined from directoryEndSignature in struct.go
252 if b[i] == 'P' && b[i+1] == 'K' && b[i+2] == 0x05 && b[i+3] == 0x06 {
253 // n is length of comment
254 n := int(b[i+minSize-2]) | int(b[i+minSize-1])<<8
255 if n+minSize+i == len(b) {
256 return i
260 return -1
263 func read(r io.Reader, data interface{}) {
264 if err := binary.Read(r, binary.LittleEndian, data); err != nil {
265 panic(err)
269 func readByteSlice(r io.Reader, l uint16) []byte {
270 b := make([]byte, l)
271 if l == 0 {
272 return b
274 if _, err := io.ReadFull(r, b); err != nil {
275 panic(err)
277 return b