Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(5)

Side by Side Diff: src/pkg/archive/zip/reader.go

Issue 2125042: code review 2125042: archive/zip: new package for reading ZIP files (Closed)
Patch Set: code review 2125042: archive/zip: new package for reading ZIP files Created 7 years, 4 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 /*
6 The zip package provides support for reading ZIP archives as described here:·
7 http://www.pkware.com/documents/casestudies/APPNOTE.TXT
8
9 This package does not support ZIP64 or disk spanning.
10 */
11 package zip
12
13 import (
14 "compress/flate"
15 "encoding/binary"
16 "io"
17 "os"
18 )
19
20 var (
21 ShortReadError = os.NewError("Short read")
22 WrongSignatureError = os.NewError("Wrong signature for this header")
23 NotFoundError = os.NewError("The signature could not be found")
24 UnsupportedMethod = os.NewError("This file uses an unsupported compres sion method")
25 )
26
27 type Reader struct {
28 directoryEnd
rsc1 2010/09/08 14:55:50 Does this need to be in the struct?
adg 2010/09/09 10:47:44 No, removed.
29 r io.ReaderAt
30 Files []*File
rsc1 2010/09/08 14:55:50 we're not entirely consistent about this but most
adg 2010/09/09 10:47:44 Done.
31 Comment string
32 }
33
34 // File represents a single file within a Zip archive.
35 type File struct {
36 directoryHeader
rsc1 2010/09/08 14:55:50 you have to expose this if you are trying to expos
adg 2010/09/09 10:47:44 Done.
37 r *io.SectionReader
38 header *fileHeader
39 bodyOffset int64
40 Filename string
rsc1 2010/09/08 14:55:50 It's already in type File. s/Filename/Name/
adg 2010/09/09 10:47:44 Done.
41 }
42
43 // ReadFile will open the Zip file specified by name and return a Reader.
44 func ReadFile(name string) (r *Reader, err os.Error) {
rsc1 2010/09/08 14:55:50 s/ReadFile/OpenReader/?
adg 2010/09/09 10:47:44 Done.
45 f, err := os.Open(name, os.O_RDONLY, 0644)
46 if err != nil {
47 return
48 }
49 fi, err := f.Stat()
50 if err != nil {
51 return
52 }
53 return NewReader(f, fi.Size)
54 }
55
56 // NewReader takes a ReaderAt and stream length,·
rsc1 2010/09/08 14:55:50 You can drop the "takes" notes: it's in the code.
adg 2010/09/09 10:47:44 Done.
57 // reads the Zip file metadata,
58 // and returns a *Reader with its Files element populated
59 // with the Zip file's contents.
60 func NewReader(r io.ReaderAt, length int64) (z *Reader, err os.Error) {
rsc1 2010/09/08 14:55:50 s/length/size/ to match os.FileInfo
adg 2010/09/09 10:47:44 Done.
61 rs := io.NewSectionReader(r, 0, length)
62 offs, err := findDirectoryEndOffset(rs)
rsc1 2010/09/08 14:55:50 s/offs/off/ offs sounds plural
adg 2010/09/09 10:47:44 Done.
63 if err != nil {
64 return
65 }
66 if _, err = rs.Seek(offs, 0); err != nil {
67 return
68 }
69 end, err := readDirectoryEnd(rs)
70 if err != nil {
71 return
72 }
73 z = &Reader{
74 directoryEnd: *end,
75 r: r,
76 Files: make([]*File, end.directoryRecords),
77 Comment: end.Comment,
78 }
79 if _, err = rs.Seek(int64(z.directoryOffset), 0); err != nil {
80 return
81 }
82 for i := range z.Files {
83 dh, err := readDirectoryHeader(rs)
84 if err != nil {
85 return nil, err
rsc1 2010/09/08 14:55:50 I don't like using named return values in anythin
adg 2010/09/09 10:47:44 Done.
86 }
87 z.Files[i] = &File{
88 directoryHeader: *dh,
89 Filename: dh.Filename,
90 }
91 }
92 for i, f := range z.Files {
93 offs := int64(f.fileHeaderOffset)
rsc1 2010/09/08 14:55:50 s/offs/off/
adg 2010/09/09 10:47:44 Done.
94 var max int64
95 if i == len(z.Files)-1 {
96 max = int64(z.directoryOffset)
97 } else {
98 max = int64(z.Files[i+1].fileHeaderOffset)
99 }
100 max -= offs
101 f.r = io.NewSectionReader(r, offs, max)
102 }
103 return
104 }
105
106 // Open returns a ReadCloser that provides access to the File's contents.
107 func (f *File) Open() (rc io.ReadCloser, err os.Error) {
108 if f.header == nil {
109 if _, err = f.r.Seek(0, 0); err != nil {
110 return
111 }
112 if f.header, err = readFileHeader(f.r); err != nil {
113 return
114 }
115 if f.bodyOffset, err = f.r.Seek(0, 1); err != nil {
116 return
117 }
118 }
119 reader := io.NewSectionReader(f.r, f.bodyOffset, int64(f.header.Compress edSize))
120 switch f.Method {
121 case 0: // store (no compression)
122 rc = nopCloser{reader}
123 case 8: // DEFLATE
124 rc = flate.NewReader(reader)
125 default:
126 err = UnsupportedMethod
127 }
128 return
129 }
130
131 type nopCloser struct {
132 io.Reader
133 }
134
135 func (f nopCloser) Close() os.Error { return nil }
136
137 func readFileHeader(r io.Reader) (f *fileHeader, err os.Error) {
138 defer func() {
139 if rerr, ok := recover().(os.Error); rerr != nil && ok {
rsc1 2010/09/08 14:55:50 rerr != nil is implied by ok
adg 2010/09/09 10:47:44 Done.
140 err = rerr
141 }
142 }()
143 f = new(fileHeader)
144 read(r, &f.signature)
145 if f.signature != fileHeaderSignature {
146 return nil, WrongSignatureError
147 }
148 read(r, &f.ReaderVersion)
149 read(r, &f.flags)
150 read(r, &f.Method)
151 read(r, &f.modifiedTime)
152 read(r, &f.modifiedDate)
153 read(r, &f.CRC32)
154 read(r, &f.CompressedSize)
155 read(r, &f.UncompressedSize)
156 read(r, &f.filenameLength)
157 read(r, &f.extraLength)
158 f.Filename = string(readByteSlice(r, f.filenameLength))
159 f.Extra = readByteSlice(r, f.extraLength)
160 return
161 }
162
163 func readDirectoryHeader(r io.Reader) (d *directoryHeader, err os.Error) {
164 defer func() {
165 if rerr, ok := recover().(os.Error); rerr != nil && ok {
rsc1 2010/09/08 14:55:50 same
adg 2010/09/09 10:47:44 Done.
166 err = rerr
167 }
168 }()
169 d = new(directoryHeader)
170 read(r, &d.signature)
171 if d.signature != directoryHeaderSignature {
172 return nil, WrongSignatureError
173 }
174 read(r, &d.CreatorVersion)
175 read(r, &d.ReaderVersion)
176 read(r, &d.flags)
177 read(r, &d.Method)
178 read(r, &d.modifiedTime)
179 read(r, &d.modifiedDate)
180 read(r, &d.CRC32)
181 read(r, &d.CompressedSize)
182 read(r, &d.UncompressedSize)
183 read(r, &d.filenameLength)
184 read(r, &d.extraLength)
185 read(r, &d.commentLength)
186 read(r, &d.startDiskNumber)
187 read(r, &d.internalAttributes)
188 read(r, &d.externalAttributes)
189 read(r, &d.fileHeaderOffset)
190 d.Filename = string(readByteSlice(r, d.filenameLength))
191 d.Extra = readByteSlice(r, d.extraLength)
192 d.Commment = string(readByteSlice(r, d.commentLength))
193 return
194 }
195
196 func readDirectoryEnd(r io.Reader) (d *directoryEnd, err os.Error) {
197 defer func() {
198 if rerr, ok := recover().(os.Error); rerr != nil && ok {
199 err = rerr
200 }
201 }()
202 d = new(directoryEnd)
203 read(r, &d.signature)
204 if d.signature != directoryEndSignature {
205 return nil, WrongSignatureError
206 }
207 read(r, &d.diskNbr)
208 read(r, &d.dirDiskNbr)
209 read(r, &d.dirRecordsThisDisk)
210 read(r, &d.directoryRecords)
211 read(r, &d.directorySize)
212 read(r, &d.directoryOffset)
213 read(r, &d.commentLen)
214 d.Comment = string(readByteSlice(r, d.commentLen))
215 return
216 }
217
218 func findDirectoryEndOffset(rs io.ReadSeeker) (offset int64, err os.Error) {
219 const minRecordSize = 4 + 2 + 2 + 2 + 2 + 4 + 4 + 2
220 offset, err = rs.Seek(-minRecordSize, 2) // relative to end-of-file
221 if err != nil {
222 return
223 }
224 for ; offset >= 0; offset-- {
225 rs.Seek(offset, 0)
226 // TODO: this is very inefficient, but it works.
227 // Better to write the little-endian representation of
228 // directoryEndSignature to a []byte and then
229 // seek backwards comparing subslices of the read buffer.
230 var sig uint32
231 err = binary.Read(rs, binary.LittleEndian, &sig)
232 if err != nil {
233 return 0, err
234 }
235 if sig == directoryEndSignature {
236 return
237 }
238 }
239 return 0, NotFoundError
240 }
241
242 func read(r io.Reader, data interface{}) {
243 if err := binary.Read(r, binary.LittleEndian, data); err != nil {
244 panic(err)
245 }
246 }
247
248 func readByteSlice(r io.Reader, l uint16) []byte {
249 b := make([]byte, l)
250 if l == 0 {
251 return b
252 }
253 n, err := r.Read(b)
254 if err != nil {
255 panic(err)
256 }
257 if n != int(l) {
258 panic(ShortReadError)
259 }
260 return b
261 }
OLDNEW

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld 204d58d