2 * Support for a subset of PKZIP format v4.5:
5 * - only Store and Deflate
6 * - ZIP64 format (long long sizes and offsets) [TODO]
8 * TODO for Metro compliance: compare file names by unescaping %XX
9 * and then converting to upper-case NFC.
15 typedef struct sa_zipent_s sa_zipent
;
32 static inline unsigned int read2(fz_stream
*f
)
34 unsigned char a
= fz_readbyte(f
);
35 unsigned char b
= fz_readbyte(f
);
39 static inline unsigned int read4(fz_stream
*f
)
41 unsigned char a
= fz_readbyte(f
);
42 unsigned char b
= fz_readbyte(f
);
43 unsigned char c
= fz_readbyte(f
);
44 unsigned char d
= fz_readbyte(f
);
45 return (d
<< 24) | (c
<< 16) | (b
<< 8) | a
;
48 static fz_error
*readzipdir(sa_zip
*zip
, int startoffset
)
51 unsigned csize
, usize
;
52 unsigned namesize
, metasize
, comsize
;
56 fz_seek(zip
->file
, startoffset
, 0);
58 for (i
= 0; i
< zip
->len
; i
++)
60 sign
= read4(zip
->file
);
61 if (sign
!= 0x02014b50)
62 return fz_throw("ioerror: unknown zip signature");
64 (void) read2(zip
->file
); /* version made by */
65 (void) read2(zip
->file
); /* version to extract */
66 (void) read2(zip
->file
); /* general */
67 (void) read2(zip
->file
); /* method */
68 (void) read2(zip
->file
); /* last mod file time */
69 (void) read2(zip
->file
); /* last mod file date */
70 (void) read4(zip
->file
); /* crc-32 */
71 csize
= read4(zip
->file
);
72 usize
= read4(zip
->file
);
73 namesize
= read2(zip
->file
);
74 metasize
= read2(zip
->file
);
75 comsize
= read2(zip
->file
);
76 (void) read2(zip
->file
); /* disk number start */
77 (void) read2(zip
->file
); /* int file atts */
78 (void) read4(zip
->file
); /* ext file atts */
79 offset
= read4(zip
->file
);
81 zip
->table
[i
].offset
= offset
;
82 zip
->table
[i
].csize
= csize
;
83 zip
->table
[i
].usize
= usize
;
85 zip
->table
[i
].name
= fz_malloc(namesize
+ 1);
86 if (!zip
->table
[i
].name
)
88 fz_read(zip
->file
, zip
->table
[i
].name
, namesize
);
89 zip
->table
[i
].name
[namesize
] = 0;
91 fz_seek(zip
->file
, metasize
, 1);
92 fz_seek(zip
->file
, comsize
, 1);
98 static fz_error
*readzipendofdir(sa_zip
*zip
, int startoffset
)
104 fz_seek(zip
->file
, startoffset
, 0);
106 sign
= read4(zip
->file
);
107 if (sign
!= 0x06054b50)
108 return fz_throw("ioerror: unknown zip signature");
110 (void) read2(zip
->file
); /* this disk */
111 (void) read2(zip
->file
); /* start disk */
112 (void) read2(zip
->file
); /* ents in this disk */
113 count
= read2(zip
->file
); /* ents in central directory */
114 (void) read4(zip
->file
); /* size of central directory */
115 offset
= read4(zip
->file
); /* offset to central directory */
118 zip
->table
= fz_malloc(zip
->len
* sizeof(sa_zipent
));
122 memset(zip
->table
, 0, zip
->len
* sizeof(sa_zipent
));
124 return readzipdir(zip
, offset
);
127 static fz_error
*findzipendofdir(sa_zip
*zip
)
130 int back
, maxback
, filesize
;
133 filesize
= fz_seek(zip
->file
, 0, 2);
135 return fz_ioerror(zip
->file
);
137 maxback
= MIN(filesize
, 0xFFFF + sizeof buf
);
138 back
= MIN(maxback
, sizeof buf
);
140 while (back
< maxback
)
142 fz_seek(zip
->file
, filesize
- back
, 0);
143 n
= fz_read(zip
->file
, buf
, sizeof buf
);
145 return fz_ioerror(zip
->file
);
147 for (i
= n
- 4; i
> 0; i
--)
148 if (!memcmp(buf
+ i
, "\120\113\5\6", 4))
149 return readzipendofdir(zip
, filesize
- back
+ i
);
151 back
+= sizeof buf
- 4;
154 return fz_throw("ioerror: could not find central directory in zip");
158 * Open a ZIP archive for reading.
159 * Load the table of contents.
162 sa_openzip(sa_zip
**zipp
, char *filename
)
167 zip
= *zipp
= fz_malloc(sizeof(sa_zip
));
175 error
= fz_openrfile(&zip
->file
, filename
);
179 return findzipendofdir(zip
);
183 * Free the table of contents and close the underlying file.
186 sa_closezip(sa_zip
*zip
)
191 fz_dropstream(zip
->file
);
193 for (i
= 0; i
< zip
->len
; i
++)
194 if (zip
->table
[i
].name
)
195 fz_free(zip
->table
[i
].name
);
201 * Print a table of contents of the zip archive
204 sa_debugzip(sa_zip
*zip
)
208 for (i
= 0; i
< zip
->len
; i
++)
210 printf("%8u ", zip
->table
[i
].usize
);
211 if (zip
->table
[i
].usize
)
212 printf("%3d%% ", zip
->table
[i
].csize
* 100 / zip
->table
[i
].usize
);
215 printf("%s\n", zip
->table
[i
].name
);
220 sa_accesszipentry(sa_zip
*zip
, char *name
)
223 for (i
= 0; i
< zip
->len
; i
++)
224 if (!sa_strcmp(name
, zip
->table
[i
].name
))
230 * Seek and push decoding filter to read an individual file in the zip archive.
232 static fz_error
*reallyopenzipentry(fz_stream
**stmp
, sa_zip
*zip
, int idx
)
237 unsigned sign
, version
, general
, method
;
238 unsigned csize
, usize
;
239 unsigned namesize
, metasize
;
242 t
= fz_seek(zip
->file
, zip
->table
[idx
].offset
, 0);
244 return fz_ioerror(zip
->file
);
246 sign
= read4(zip
->file
);
247 if (sign
!= 0x04034b50)
248 return fz_throw("ioerror: unknown zip signature");
250 version
= read2(zip
->file
);
251 general
= read2(zip
->file
);
252 method
= read2(zip
->file
);
253 (void) read2(zip
->file
); /* time */
254 (void) read2(zip
->file
); /* date */
255 (void) read4(zip
->file
); /* crc-32 */
256 csize
= read4(zip
->file
);
257 usize
= read4(zip
->file
);
258 namesize
= read2(zip
->file
);
259 metasize
= read2(zip
->file
);
261 if ((version
& 0xff) > 45)
262 return fz_throw("ioerror: unsupported zip version");
264 if (general
& 0x0001)
265 return fz_throw("ioerror: encrypted zip entry");
267 t
= fz_seek(zip
->file
, namesize
+ metasize
, 1);
269 return fz_ioerror(zip
->file
);
274 error
= fz_newnullfilter(&filter
, csize
);
280 error
= fz_packobj(&obj
, "<</ZIP true>>");
283 error
= fz_newflated(&filter
, obj
);
290 return fz_throw("ioerror: unsupported compression method");
294 error
= fz_openrfilter(stmp
, filter
, zip
->file
);
295 fz_dropfilter(filter
);
303 sa_openzipentry(fz_stream
**stmp
, sa_zip
*zip
, char *name
)
307 for (i
= 0; i
< zip
->len
; i
++)
308 if (!sa_strcmp(name
, zip
->table
[i
].name
))
309 return reallyopenzipentry(stmp
, zip
, i
);
311 return fz_throw("ioerror: file not found in zip: '%s'", name
);