Contents of /tags/mkinitrd-6_2_1/busybox/archival/unzip.c
Parent Directory | Revision Log
Revision 999 -
(show annotations)
(download)
Sun May 30 12:16:23 2010 UTC (14 years, 4 months ago) by niro
File MIME type: text/plain
File size: 19781 byte(s)
Sun May 30 12:16:23 2010 UTC (14 years, 4 months ago) by niro
File MIME type: text/plain
File size: 19781 byte(s)
tagged 'mkinitrd-6_2_1'
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * Mini unzip implementation for busybox |
4 | * |
5 | * Copyright (C) 2004 by Ed Clark |
6 | * |
7 | * Loosely based on original busybox unzip applet by Laurence Anderson. |
8 | * All options and features should work in this version. |
9 | * |
10 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. |
11 | */ |
12 | |
13 | /* For reference see |
14 | * http://www.pkware.com/company/standards/appnote/ |
15 | * http://www.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip |
16 | */ |
17 | |
18 | /* TODO |
19 | * Zip64 + other methods |
20 | */ |
21 | |
22 | #include "libbb.h" |
23 | #include "unarchive.h" |
24 | |
25 | enum { |
26 | #if BB_BIG_ENDIAN |
27 | ZIP_FILEHEADER_MAGIC = 0x504b0304, |
28 | ZIP_CDS_MAGIC = 0x504b0102, |
29 | ZIP_CDE_MAGIC = 0x504b0506, |
30 | ZIP_DD_MAGIC = 0x504b0708, |
31 | #else |
32 | ZIP_FILEHEADER_MAGIC = 0x04034b50, |
33 | ZIP_CDS_MAGIC = 0x02014b50, |
34 | ZIP_CDE_MAGIC = 0x06054b50, |
35 | ZIP_DD_MAGIC = 0x08074b50, |
36 | #endif |
37 | }; |
38 | |
39 | #define ZIP_HEADER_LEN 26 |
40 | |
41 | typedef union { |
42 | uint8_t raw[ZIP_HEADER_LEN]; |
43 | struct { |
44 | uint16_t version; /* 0-1 */ |
45 | uint16_t flags; /* 2-3 */ |
46 | uint16_t method; /* 4-5 */ |
47 | uint16_t modtime; /* 6-7 */ |
48 | uint16_t moddate; /* 8-9 */ |
49 | uint32_t crc32 PACKED; /* 10-13 */ |
50 | uint32_t cmpsize PACKED; /* 14-17 */ |
51 | uint32_t ucmpsize PACKED; /* 18-21 */ |
52 | uint16_t filename_len; /* 22-23 */ |
53 | uint16_t extra_len; /* 24-25 */ |
54 | } formatted PACKED; |
55 | } zip_header_t; /* PACKED - gcc 4.2.1 doesn't like it (spews warning) */ |
56 | |
57 | /* Check the offset of the last element, not the length. This leniency |
58 | * allows for poor packing, whereby the overall struct may be too long, |
59 | * even though the elements are all in the right place. |
60 | */ |
61 | struct BUG_zip_header_must_be_26_bytes { |
62 | char BUG_zip_header_must_be_26_bytes[ |
63 | offsetof(zip_header_t, formatted.extra_len) + 2 |
64 | == ZIP_HEADER_LEN ? 1 : -1]; |
65 | }; |
66 | |
67 | #define FIX_ENDIANNESS_ZIP(zip_header) do { \ |
68 | (zip_header).formatted.version = SWAP_LE16((zip_header).formatted.version ); \ |
69 | (zip_header).formatted.flags = SWAP_LE16((zip_header).formatted.flags ); \ |
70 | (zip_header).formatted.method = SWAP_LE16((zip_header).formatted.method ); \ |
71 | (zip_header).formatted.modtime = SWAP_LE16((zip_header).formatted.modtime ); \ |
72 | (zip_header).formatted.moddate = SWAP_LE16((zip_header).formatted.moddate ); \ |
73 | (zip_header).formatted.crc32 = SWAP_LE32((zip_header).formatted.crc32 ); \ |
74 | (zip_header).formatted.cmpsize = SWAP_LE32((zip_header).formatted.cmpsize ); \ |
75 | (zip_header).formatted.ucmpsize = SWAP_LE32((zip_header).formatted.ucmpsize ); \ |
76 | (zip_header).formatted.filename_len = SWAP_LE16((zip_header).formatted.filename_len); \ |
77 | (zip_header).formatted.extra_len = SWAP_LE16((zip_header).formatted.extra_len ); \ |
78 | } while (0) |
79 | |
80 | #define CDS_HEADER_LEN 42 |
81 | |
82 | typedef union { |
83 | uint8_t raw[CDS_HEADER_LEN]; |
84 | struct { |
85 | /* uint32_t signature; 50 4b 01 02 */ |
86 | uint16_t version_made_by; /* 0-1 */ |
87 | uint16_t version_needed; /* 2-3 */ |
88 | uint16_t cds_flags; /* 4-5 */ |
89 | uint16_t method; /* 6-7 */ |
90 | uint16_t mtime; /* 8-9 */ |
91 | uint16_t mdate; /* 10-11 */ |
92 | uint32_t crc32; /* 12-15 */ |
93 | uint32_t cmpsize; /* 16-19 */ |
94 | uint32_t ucmpsize; /* 20-23 */ |
95 | uint16_t file_name_length; /* 24-25 */ |
96 | uint16_t extra_field_length; /* 26-27 */ |
97 | uint16_t file_comment_length; /* 28-29 */ |
98 | uint16_t disk_number_start; /* 30-31 */ |
99 | uint16_t internal_file_attributes; /* 32-33 */ |
100 | uint32_t external_file_attributes PACKED; /* 34-37 */ |
101 | uint32_t relative_offset_of_local_header PACKED; /* 38-41 */ |
102 | } formatted PACKED; |
103 | } cds_header_t; |
104 | |
105 | struct BUG_cds_header_must_be_42_bytes { |
106 | char BUG_cds_header_must_be_42_bytes[ |
107 | offsetof(cds_header_t, formatted.relative_offset_of_local_header) + 4 |
108 | == CDS_HEADER_LEN ? 1 : -1]; |
109 | }; |
110 | |
111 | #define FIX_ENDIANNESS_CDS(cds_header) do { \ |
112 | (cds_header).formatted.crc32 = SWAP_LE32((cds_header).formatted.crc32 ); \ |
113 | (cds_header).formatted.cmpsize = SWAP_LE32((cds_header).formatted.cmpsize ); \ |
114 | (cds_header).formatted.ucmpsize = SWAP_LE32((cds_header).formatted.ucmpsize ); \ |
115 | (cds_header).formatted.file_name_length = SWAP_LE16((cds_header).formatted.file_name_length); \ |
116 | (cds_header).formatted.extra_field_length = SWAP_LE16((cds_header).formatted.extra_field_length); \ |
117 | (cds_header).formatted.file_comment_length = SWAP_LE16((cds_header).formatted.file_comment_length); \ |
118 | } while (0) |
119 | |
120 | #define CDE_HEADER_LEN 16 |
121 | |
122 | typedef union { |
123 | uint8_t raw[CDE_HEADER_LEN]; |
124 | struct { |
125 | /* uint32_t signature; 50 4b 05 06 */ |
126 | uint16_t this_disk_no; |
127 | uint16_t disk_with_cds_no; |
128 | uint16_t cds_entries_on_this_disk; |
129 | uint16_t cds_entries_total; |
130 | uint32_t cds_size; |
131 | uint32_t cds_offset; |
132 | /* uint16_t file_comment_length; */ |
133 | /* .ZIP file comment (variable size) */ |
134 | } formatted PACKED; |
135 | } cde_header_t; |
136 | |
137 | struct BUG_cde_header_must_be_16_bytes { |
138 | char BUG_cde_header_must_be_16_bytes[ |
139 | sizeof(cde_header_t) == CDE_HEADER_LEN ? 1 : -1]; |
140 | }; |
141 | |
142 | #define FIX_ENDIANNESS_CDE(cde_header) do { \ |
143 | (cde_header).formatted.cds_offset = SWAP_LE32((cde_header).formatted.cds_offset); \ |
144 | } while (0) |
145 | |
146 | enum { zip_fd = 3 }; |
147 | |
148 | |
149 | #if ENABLE_DESKTOP |
150 | /* NB: does not preserve file position! */ |
151 | static uint32_t find_cds_offset(void) |
152 | { |
153 | unsigned char buf[1024]; |
154 | cde_header_t cde_header; |
155 | unsigned char *p; |
156 | off_t end; |
157 | |
158 | end = xlseek(zip_fd, 0, SEEK_END); |
159 | if (end < 1024) |
160 | end = 1024; |
161 | end -= 1024; |
162 | xlseek(zip_fd, end, SEEK_SET); |
163 | full_read(zip_fd, buf, 1024); |
164 | |
165 | p = buf; |
166 | while (p <= buf + 1024 - CDE_HEADER_LEN - 4) { |
167 | if (*p != 'P') { |
168 | p++; |
169 | continue; |
170 | } |
171 | if (*++p != 'K') |
172 | continue; |
173 | if (*++p != 5) |
174 | continue; |
175 | if (*++p != 6) |
176 | continue; |
177 | /* we found CDE! */ |
178 | memcpy(cde_header.raw, p + 1, CDE_HEADER_LEN); |
179 | FIX_ENDIANNESS_CDE(cde_header); |
180 | return cde_header.formatted.cds_offset; |
181 | } |
182 | bb_error_msg_and_die("can't find file table"); |
183 | }; |
184 | |
185 | static uint32_t read_next_cds(int count_m1, uint32_t cds_offset, cds_header_t *cds_ptr) |
186 | { |
187 | off_t org; |
188 | |
189 | org = xlseek(zip_fd, 0, SEEK_CUR); |
190 | |
191 | if (!cds_offset) |
192 | cds_offset = find_cds_offset(); |
193 | |
194 | while (count_m1-- >= 0) { |
195 | xlseek(zip_fd, cds_offset + 4, SEEK_SET); |
196 | xread(zip_fd, cds_ptr->raw, CDS_HEADER_LEN); |
197 | FIX_ENDIANNESS_CDS(*cds_ptr); |
198 | cds_offset += 4 + CDS_HEADER_LEN |
199 | + cds_ptr->formatted.file_name_length |
200 | + cds_ptr->formatted.extra_field_length |
201 | + cds_ptr->formatted.file_comment_length; |
202 | } |
203 | |
204 | xlseek(zip_fd, org, SEEK_SET); |
205 | return cds_offset; |
206 | }; |
207 | #endif |
208 | |
209 | static void unzip_skip(off_t skip) |
210 | { |
211 | if (lseek(zip_fd, skip, SEEK_CUR) == (off_t)-1) |
212 | bb_copyfd_exact_size(zip_fd, -1, skip); |
213 | } |
214 | |
215 | static void unzip_create_leading_dirs(const char *fn) |
216 | { |
217 | /* Create all leading directories */ |
218 | char *name = xstrdup(fn); |
219 | if (bb_make_directory(dirname(name), 0777, FILEUTILS_RECUR)) { |
220 | bb_error_msg_and_die("exiting"); /* bb_make_directory is noisy */ |
221 | } |
222 | free(name); |
223 | } |
224 | |
225 | static void unzip_extract(zip_header_t *zip_header, int dst_fd) |
226 | { |
227 | if (zip_header->formatted.method == 0) { |
228 | /* Method 0 - stored (not compressed) */ |
229 | off_t size = zip_header->formatted.ucmpsize; |
230 | if (size) |
231 | bb_copyfd_exact_size(zip_fd, dst_fd, size); |
232 | } else { |
233 | /* Method 8 - inflate */ |
234 | inflate_unzip_result res; |
235 | if (inflate_unzip(&res, zip_header->formatted.cmpsize, zip_fd, dst_fd) < 0) |
236 | bb_error_msg_and_die("inflate error"); |
237 | /* Validate decompression - crc */ |
238 | if (zip_header->formatted.crc32 != (res.crc ^ 0xffffffffL)) { |
239 | bb_error_msg_and_die("crc error"); |
240 | } |
241 | /* Validate decompression - size */ |
242 | if (zip_header->formatted.ucmpsize != res.bytes_out) { |
243 | /* Don't die. Who knows, maybe len calculation |
244 | * was botched somewhere. After all, crc matched! */ |
245 | bb_error_msg("bad length"); |
246 | } |
247 | } |
248 | } |
249 | |
250 | int unzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
251 | int unzip_main(int argc, char **argv) |
252 | { |
253 | enum { O_PROMPT, O_NEVER, O_ALWAYS }; |
254 | |
255 | zip_header_t zip_header; |
256 | smallint quiet = 0; |
257 | IF_NOT_DESKTOP(const) smallint verbose = 0; |
258 | smallint listing = 0; |
259 | smallint overwrite = O_PROMPT; |
260 | #if ENABLE_DESKTOP |
261 | uint32_t cds_offset; |
262 | unsigned cds_entries; |
263 | #endif |
264 | unsigned long total_usize; |
265 | unsigned long total_size; |
266 | unsigned total_entries; |
267 | int dst_fd = -1; |
268 | char *src_fn = NULL; |
269 | char *dst_fn = NULL; |
270 | llist_t *zaccept = NULL; |
271 | llist_t *zreject = NULL; |
272 | char *base_dir = NULL; |
273 | int i, opt; |
274 | int opt_range = 0; |
275 | char key_buf[80]; |
276 | struct stat stat_buf; |
277 | |
278 | /* -q, -l and -v: UnZip 5.52 of 28 February 2005, by Info-ZIP: |
279 | * |
280 | * # /usr/bin/unzip -qq -v decompress_unlzma.i.zip |
281 | * 204372 Defl:N 35278 83% 09-06-09 14:23 0d056252 decompress_unlzma.i |
282 | * # /usr/bin/unzip -q -v decompress_unlzma.i.zip |
283 | * Length Method Size Ratio Date Time CRC-32 Name |
284 | * -------- ------ ------- ----- ---- ---- ------ ---- |
285 | * 204372 Defl:N 35278 83% 09-06-09 14:23 0d056252 decompress_unlzma.i |
286 | * -------- ------- --- ------- |
287 | * 204372 35278 83% 1 file |
288 | * # /usr/bin/unzip -v decompress_unlzma.i.zip |
289 | * Archive: decompress_unlzma.i.zip |
290 | * Length Method Size Ratio Date Time CRC-32 Name |
291 | * -------- ------ ------- ----- ---- ---- ------ ---- |
292 | * 204372 Defl:N 35278 83% 09-06-09 14:23 0d056252 decompress_unlzma.i |
293 | * -------- ------- --- ------- |
294 | * 204372 35278 83% 1 file |
295 | * # unzip -v decompress_unlzma.i.zip |
296 | * Archive: decompress_unlzma.i.zip |
297 | * Length Date Time Name |
298 | * -------- ---- ---- ---- |
299 | * 204372 09-06-09 14:23 decompress_unlzma.i |
300 | * -------- ------- |
301 | * 204372 1 files |
302 | * # /usr/bin/unzip -l -qq decompress_unlzma.i.zip |
303 | * 204372 09-06-09 14:23 decompress_unlzma.i |
304 | * # /usr/bin/unzip -l -q decompress_unlzma.i.zip |
305 | * Length Date Time Name |
306 | * -------- ---- ---- ---- |
307 | * 204372 09-06-09 14:23 decompress_unlzma.i |
308 | * -------- ------- |
309 | * 204372 1 file |
310 | * # /usr/bin/unzip -l decompress_unlzma.i.zip |
311 | * Archive: decompress_unlzma.i.zip |
312 | * Length Date Time Name |
313 | * -------- ---- ---- ---- |
314 | * 204372 09-06-09 14:23 decompress_unlzma.i |
315 | * -------- ------- |
316 | * 204372 1 file |
317 | */ |
318 | |
319 | /* '-' makes getopt return 1 for non-options */ |
320 | while ((opt = getopt(argc, argv, "-d:lnopqxv")) != -1) { |
321 | switch (opt_range) { |
322 | case 0: /* Options */ |
323 | switch (opt) { |
324 | case 'l': /* List */ |
325 | listing = 1; |
326 | break; |
327 | |
328 | case 'n': /* Never overwrite existing files */ |
329 | overwrite = O_NEVER; |
330 | break; |
331 | |
332 | case 'o': /* Always overwrite existing files */ |
333 | overwrite = O_ALWAYS; |
334 | break; |
335 | |
336 | case 'p': /* Extract files to stdout and fall through to set verbosity */ |
337 | dst_fd = STDOUT_FILENO; |
338 | |
339 | case 'q': /* Be quiet */ |
340 | quiet++; |
341 | break; |
342 | |
343 | case 'v': /* Verbose list */ |
344 | IF_DESKTOP(verbose++;) |
345 | listing = 1; |
346 | break; |
347 | |
348 | case 1: /* The zip file */ |
349 | /* +5: space for ".zip" and NUL */ |
350 | src_fn = xmalloc(strlen(optarg) + 5); |
351 | strcpy(src_fn, optarg); |
352 | opt_range++; |
353 | break; |
354 | |
355 | default: |
356 | bb_show_usage(); |
357 | |
358 | } |
359 | break; |
360 | |
361 | case 1: /* Include files */ |
362 | if (opt == 1) { |
363 | llist_add_to(&zaccept, optarg); |
364 | break; |
365 | } |
366 | if (opt == 'd') { |
367 | base_dir = optarg; |
368 | opt_range += 2; |
369 | break; |
370 | } |
371 | if (opt == 'x') { |
372 | opt_range++; |
373 | break; |
374 | } |
375 | bb_show_usage(); |
376 | |
377 | case 2 : /* Exclude files */ |
378 | if (opt == 1) { |
379 | llist_add_to(&zreject, optarg); |
380 | break; |
381 | } |
382 | if (opt == 'd') { /* Extract to base directory */ |
383 | base_dir = optarg; |
384 | opt_range++; |
385 | break; |
386 | } |
387 | /* fall through */ |
388 | |
389 | default: |
390 | bb_show_usage(); |
391 | } |
392 | } |
393 | |
394 | if (src_fn == NULL) { |
395 | bb_show_usage(); |
396 | } |
397 | |
398 | /* Open input file */ |
399 | if (LONE_DASH(src_fn)) { |
400 | xdup2(STDIN_FILENO, zip_fd); |
401 | /* Cannot use prompt mode since zip data is arriving on STDIN */ |
402 | if (overwrite == O_PROMPT) |
403 | overwrite = O_NEVER; |
404 | } else { |
405 | static const char extn[][5] = {"", ".zip", ".ZIP"}; |
406 | int orig_src_fn_len = strlen(src_fn); |
407 | int src_fd = -1; |
408 | |
409 | for (i = 0; (i < 3) && (src_fd == -1); i++) { |
410 | strcpy(src_fn + orig_src_fn_len, extn[i]); |
411 | src_fd = open(src_fn, O_RDONLY); |
412 | } |
413 | if (src_fd == -1) { |
414 | src_fn[orig_src_fn_len] = '\0'; |
415 | bb_error_msg_and_die("can't open %s, %s.zip, %s.ZIP", src_fn, src_fn, src_fn); |
416 | } |
417 | xmove_fd(src_fd, zip_fd); |
418 | } |
419 | |
420 | /* Change dir if necessary */ |
421 | if (base_dir) |
422 | xchdir(base_dir); |
423 | |
424 | if (quiet <= 1) { /* not -qq */ |
425 | if (quiet == 0) |
426 | printf("Archive: %s\n", src_fn); |
427 | if (listing) { |
428 | puts(verbose ? |
429 | " Length Method Size Ratio Date Time CRC-32 Name\n" |
430 | "-------- ------ ------- ----- ---- ---- ------ ----" |
431 | : |
432 | " Length Date Time Name\n" |
433 | " -------- ---- ---- ----" |
434 | ); |
435 | } |
436 | } |
437 | |
438 | total_usize = 0; |
439 | total_size = 0; |
440 | total_entries = 0; |
441 | #if ENABLE_DESKTOP |
442 | cds_entries = 0; |
443 | cds_offset = 0; |
444 | #endif |
445 | while (1) { |
446 | uint32_t magic; |
447 | |
448 | /* Check magic number */ |
449 | xread(zip_fd, &magic, 4); |
450 | /* Central directory? It's at the end, so exit */ |
451 | if (magic == ZIP_CDS_MAGIC) |
452 | break; |
453 | #if ENABLE_DESKTOP |
454 | /* Data descriptor? It was a streaming file, go on */ |
455 | if (magic == ZIP_DD_MAGIC) { |
456 | /* skip over duplicate crc32, cmpsize and ucmpsize */ |
457 | unzip_skip(3 * 4); |
458 | continue; |
459 | } |
460 | #endif |
461 | if (magic != ZIP_FILEHEADER_MAGIC) |
462 | bb_error_msg_and_die("invalid zip magic %08X", (int)magic); |
463 | |
464 | /* Read the file header */ |
465 | xread(zip_fd, zip_header.raw, ZIP_HEADER_LEN); |
466 | FIX_ENDIANNESS_ZIP(zip_header); |
467 | if ((zip_header.formatted.method != 0) && (zip_header.formatted.method != 8)) { |
468 | bb_error_msg_and_die("unsupported method %d", zip_header.formatted.method); |
469 | } |
470 | #if !ENABLE_DESKTOP |
471 | if (zip_header.formatted.flags & 0x0009) { |
472 | bb_error_msg_and_die("zip flags 1 and 8 are not supported"); |
473 | } |
474 | #else |
475 | if (zip_header.formatted.flags & 0x0001) { |
476 | /* 0x0001 - encrypted */ |
477 | bb_error_msg_and_die("zip flag 1 (encryption) is not supported"); |
478 | } |
479 | if (zip_header.formatted.flags & 0x0008) { |
480 | cds_header_t cds_header; |
481 | /* 0x0008 - streaming. [u]cmpsize can be reliably gotten |
482 | * only from Central Directory. See unzip_doc.txt */ |
483 | cds_offset = read_next_cds(total_entries - cds_entries, cds_offset, &cds_header); |
484 | cds_entries = total_entries + 1; |
485 | zip_header.formatted.crc32 = cds_header.formatted.crc32; |
486 | zip_header.formatted.cmpsize = cds_header.formatted.cmpsize; |
487 | zip_header.formatted.ucmpsize = cds_header.formatted.ucmpsize; |
488 | } |
489 | #endif |
490 | |
491 | /* Read filename */ |
492 | free(dst_fn); |
493 | dst_fn = xzalloc(zip_header.formatted.filename_len + 1); |
494 | xread(zip_fd, dst_fn, zip_header.formatted.filename_len); |
495 | |
496 | /* Skip extra header bytes */ |
497 | unzip_skip(zip_header.formatted.extra_len); |
498 | |
499 | /* Filter zip entries */ |
500 | if (find_list_entry(zreject, dst_fn) |
501 | || (zaccept && !find_list_entry(zaccept, dst_fn)) |
502 | ) { /* Skip entry */ |
503 | i = 'n'; |
504 | |
505 | } else { /* Extract entry */ |
506 | if (listing) { /* List entry */ |
507 | unsigned dostime = zip_header.formatted.modtime | (zip_header.formatted.moddate << 16); |
508 | if (!verbose) { |
509 | // " Length Date Time Name\n" |
510 | // " -------- ---- ---- ----" |
511 | printf( "%9u %02u-%02u-%02u %02u:%02u %s\n", |
512 | (unsigned)zip_header.formatted.ucmpsize, |
513 | (dostime & 0x01e00000) >> 21, |
514 | (dostime & 0x001f0000) >> 16, |
515 | (((dostime & 0xfe000000) >> 25) + 1980) % 100, |
516 | (dostime & 0x0000f800) >> 11, |
517 | (dostime & 0x000007e0) >> 5, |
518 | dst_fn); |
519 | total_usize += zip_header.formatted.ucmpsize; |
520 | } else { |
521 | unsigned long percents = zip_header.formatted.ucmpsize - zip_header.formatted.cmpsize; |
522 | percents = percents * 100; |
523 | if (zip_header.formatted.ucmpsize) |
524 | percents /= zip_header.formatted.ucmpsize; |
525 | // " Length Method Size Ratio Date Time CRC-32 Name\n" |
526 | // "-------- ------ ------- ----- ---- ---- ------ ----" |
527 | printf( "%8u Defl:N" "%9u%4u%% %02u-%02u-%02u %02u:%02u %08x %s\n", |
528 | (unsigned)zip_header.formatted.ucmpsize, |
529 | (unsigned)zip_header.formatted.cmpsize, |
530 | (unsigned)percents, |
531 | (dostime & 0x01e00000) >> 21, |
532 | (dostime & 0x001f0000) >> 16, |
533 | (((dostime & 0xfe000000) >> 25) + 1980) % 100, |
534 | (dostime & 0x0000f800) >> 11, |
535 | (dostime & 0x000007e0) >> 5, |
536 | zip_header.formatted.crc32, |
537 | dst_fn); |
538 | total_usize += zip_header.formatted.ucmpsize; |
539 | total_size += zip_header.formatted.cmpsize; |
540 | } |
541 | i = 'n'; |
542 | } else if (dst_fd == STDOUT_FILENO) { /* Extracting to STDOUT */ |
543 | i = -1; |
544 | } else if (last_char_is(dst_fn, '/')) { /* Extract directory */ |
545 | if (stat(dst_fn, &stat_buf) == -1) { |
546 | if (errno != ENOENT) { |
547 | bb_perror_msg_and_die("can't stat '%s'", dst_fn); |
548 | } |
549 | if (!quiet) { |
550 | printf(" creating: %s\n", dst_fn); |
551 | } |
552 | unzip_create_leading_dirs(dst_fn); |
553 | if (bb_make_directory(dst_fn, 0777, 0)) { |
554 | bb_error_msg_and_die("exiting"); |
555 | } |
556 | } else { |
557 | if (!S_ISDIR(stat_buf.st_mode)) { |
558 | bb_error_msg_and_die("'%s' exists but is not directory", dst_fn); |
559 | } |
560 | } |
561 | i = 'n'; |
562 | |
563 | } else { /* Extract file */ |
564 | check_file: |
565 | if (stat(dst_fn, &stat_buf) == -1) { /* File does not exist */ |
566 | if (errno != ENOENT) { |
567 | bb_perror_msg_and_die("can't stat '%s'", dst_fn); |
568 | } |
569 | i = 'y'; |
570 | } else { /* File already exists */ |
571 | if (overwrite == O_NEVER) { |
572 | i = 'n'; |
573 | } else if (S_ISREG(stat_buf.st_mode)) { /* File is regular file */ |
574 | if (overwrite == O_ALWAYS) { |
575 | i = 'y'; |
576 | } else { |
577 | printf("replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", dst_fn); |
578 | if (!fgets(key_buf, sizeof(key_buf), stdin)) { |
579 | bb_perror_msg_and_die("can't read input"); |
580 | } |
581 | i = key_buf[0]; |
582 | } |
583 | } else { /* File is not regular file */ |
584 | bb_error_msg_and_die("'%s' exists but is not regular file", dst_fn); |
585 | } |
586 | } |
587 | } |
588 | } |
589 | |
590 | switch (i) { |
591 | case 'A': |
592 | overwrite = O_ALWAYS; |
593 | case 'y': /* Open file and fall into unzip */ |
594 | unzip_create_leading_dirs(dst_fn); |
595 | dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC); |
596 | case -1: /* Unzip */ |
597 | if (!quiet) { |
598 | printf(" inflating: %s\n", dst_fn); |
599 | } |
600 | unzip_extract(&zip_header, dst_fd); |
601 | if (dst_fd != STDOUT_FILENO) { |
602 | /* closing STDOUT is potentially bad for future business */ |
603 | close(dst_fd); |
604 | } |
605 | break; |
606 | |
607 | case 'N': |
608 | overwrite = O_NEVER; |
609 | case 'n': |
610 | /* Skip entry data */ |
611 | unzip_skip(zip_header.formatted.cmpsize); |
612 | break; |
613 | |
614 | case 'r': |
615 | /* Prompt for new name */ |
616 | printf("new name: "); |
617 | if (!fgets(key_buf, sizeof(key_buf), stdin)) { |
618 | bb_perror_msg_and_die("can't read input"); |
619 | } |
620 | free(dst_fn); |
621 | dst_fn = xstrdup(key_buf); |
622 | chomp(dst_fn); |
623 | goto check_file; |
624 | |
625 | default: |
626 | printf("error: invalid response [%c]\n", (char)i); |
627 | goto check_file; |
628 | } |
629 | |
630 | total_entries++; |
631 | } |
632 | |
633 | if (listing && quiet <= 1) { |
634 | if (!verbose) { |
635 | // " Length Date Time Name\n" |
636 | // " -------- ---- ---- ----" |
637 | printf( " -------- -------\n" |
638 | "%9lu" " %u files\n", |
639 | total_usize, total_entries); |
640 | } else { |
641 | unsigned long percents = total_usize - total_size; |
642 | percents = percents * 100; |
643 | if (total_usize) |
644 | percents /= total_usize; |
645 | // " Length Method Size Ratio Date Time CRC-32 Name\n" |
646 | // "-------- ------ ------- ----- ---- ---- ------ ----" |
647 | printf( "-------- ------- --- -------\n" |
648 | "%8lu" "%17lu%4u%% %u files\n", |
649 | total_usize, total_size, (unsigned)percents, |
650 | total_entries); |
651 | } |
652 | } |
653 | |
654 | return 0; |
655 | } |