Rev 6727 | Rev 6764 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
6725 | siemargl | 1 | /* |
2 | Kolibri OS port for gcc 5.4 |
||
3 | |||
4 | Started by Siemargl @Nov 2016 |
||
5 | Borrowed code parts from other unzip ports |
||
6 | |||
7 | howto make: |
||
8 | go in unzip60 directory (below this file) and |
||
9 | >make -f kolibri\makefile.gcc |
||
10 | |||
11 | Contains: |
||
12 | version() |
||
13 | mapattr() |
||
6745 | siemargl | 14 | mapname() |
6725 | siemargl | 15 | checkdir() |
6745 | siemargl | 16 | close_outfile() |
17 | get_extattribs() |
||
18 | do_wild() |
||
6725 | siemargl | 19 | |
6745 | siemargl | 20 | todo |
21 | russian filenames in arh |
||
22 | datetime restore |
||
23 | overwrite request not in stderr |
||
24 | |||
25 | too many open files error EMFILE when DEBUG - error in newlib open. fixed |
||
26 | -d dir error. Use -ddir or -d dir/ |
||
27 | |||
28 | release, sizing removing old compression methods or zlib |
||
6725 | siemargl | 29 | */ |
30 | |||
31 | #define FATTR FS_HIDDEN+FS_SYSTEM+FS_SUBDIR |
||
32 | |||
33 | |||
34 | |||
35 | #define UNZIP_INTERNAL |
||
36 | #include "unzip.h" |
||
37 | |||
38 | // Siemargl fork of Kolibri system API |
||
39 | #include "kos32sys1.h" |
||
40 | |||
41 | /********************************************************************************************************************/ |
||
42 | /*** Function version() */ |
||
43 | /********************************************************************************************************************/ |
||
44 | |||
45 | void version(__G) |
||
46 | __GDEF |
||
47 | { |
||
48 | sprintf((char *)slide, LoadFarString(CompiledWith), |
||
49 | #if defined(__TINYC__) |
||
50 | "TinyC", "", |
||
51 | #elif defined(__GNUC__) |
||
52 | "GNU C ", __VERSION__, |
||
53 | #else |
||
54 | "(unknown compiler) ","", |
||
55 | #endif |
||
56 | "KolibriOS ", |
||
57 | |||
58 | #ifdef __POWERPC__ |
||
59 | "(PowerPC)", |
||
60 | #else |
||
61 | # ifdef __INTEL__ |
||
62 | "(x86)", |
||
63 | # else |
||
64 | "(unknown)", /* someday we may have other architectures... */ |
||
65 | # endif |
||
66 | #endif |
||
67 | |||
68 | #ifdef __DATE__ |
||
69 | " on ", __DATE__ |
||
70 | #else |
||
71 | "", "" |
||
72 | #endif |
||
73 | ); |
||
74 | |||
75 | (*G.message)((zvoid *)&G, slide, (ulg)strlen((char *)slide), 0); |
||
76 | |||
77 | } /* end function version() */ |
||
78 | |||
79 | |||
80 | /********************************************************************************************************************/ |
||
81 | /*** Function mapattr() */ |
||
82 | /********************************************************************************************************************/ |
||
83 | |||
84 | /* Identical to MS-DOS, OS/2 versions. However, NT has a lot of extra |
||
85 | * permission stuff, so this function should probably be extended in the |
||
86 | * future. */ |
||
87 | |||
88 | int mapattr(__G) |
||
89 | __GDEF |
||
90 | { |
||
91 | /* set archive bit for file entries (file is not backed up): */ |
||
92 | G.pInfo->file_attr = ((unsigned)G.crec.external_file_attributes | |
||
93 | (G.crec.external_file_attributes & FS_SUBDIR ? |
||
94 | |||
95 | return 0; |
||
96 | |||
97 | } /* end function mapattr() */ |
||
98 | |||
99 | |||
100 | /********************************************************************************************************************/ |
||
6727 | siemargl | 101 | /*** Function mapname() */ |
6725 | siemargl | 102 | /********************************************************************************************************************/ |
103 | |||
104 | int mapname(__G__ renamed) |
||
105 | __GDEF |
||
106 | int renamed; |
||
107 | /* |
||
108 | * returns: |
||
109 | * MPN_OK - no problem detected |
||
110 | * MPN_INF_TRUNC - caution (truncated filename) |
||
111 | * MPN_INF_SKIP - info "skip entry" (dir doesn't exist) |
||
112 | * MPN_ERR_SKIP - error -> skip entry |
||
113 | * MPN_ERR_TOOLONG - error -> path is too long |
||
114 | * MPN_NOMEM - error (memory allocation failed) -> skip entry |
||
115 | * [also MPN_VOL_LABEL, MPN_CREATED_DIR] |
||
116 | */ |
||
117 | { |
||
118 | char pathcomp[FILNAMSIZ]; /* path-component buffer */ |
||
119 | char *pp, *cp=(char *)NULL; /* character pointers */ |
||
120 | char *lastsemi=(char *)NULL; /* pointer to last semi-colon in pathcomp */ |
||
121 | #ifdef ACORN_FTYPE_NFS |
||
122 | char *lastcomma=(char *)NULL; /* pointer to last comma in pathcomp */ |
||
123 | RO_extra_block *ef_spark; /* pointer Acorn FTYPE ef block */ |
||
124 | #endif |
||
125 | int killed_ddot = FALSE; /* is set when skipping "../" pathcomp */ |
||
126 | int error = MPN_OK; |
||
127 | register unsigned workch; /* hold the character being tested */ |
||
128 | |||
129 | |||
130 | /*--------------------------------------------------------------------------- |
||
131 | Initialize various pointers and counters and stuff. |
||
132 | ---------------------------------------------------------------------------*/ |
||
133 | |||
134 | if (G.pInfo->vollabel) |
||
135 | return MPN_VOL_LABEL; /* can't set disk volume labels in Unix */ |
||
136 | |||
137 | /* can create path as long as not just freshening, or if user told us */ |
||
138 | G.create_dirs = (!uO.fflag || renamed); |
||
139 | |||
140 | G.created_dir = FALSE; /* not yet */ |
||
141 | |||
142 | /* user gave full pathname: don't prepend rootpath */ |
||
143 | G.renamed_fullpath = (renamed && (*G.filename == '/')); |
||
144 | |||
145 | if (checkdir(__G__ (char *)NULL, INIT) == MPN_NOMEM) |
||
146 | return MPN_NOMEM; /* initialize path buffer, unless no memory */ |
||
147 | |||
148 | *pathcomp = '\0'; /* initialize translation buffer */ |
||
149 | pp = pathcomp; /* point to translation buffer */ |
||
150 | if (uO.jflag) /* junking directories */ |
||
151 | cp = (char *)strrchr(G.filename, '/'); |
||
152 | if (cp == (char *)NULL) /* no '/' or not junking dirs */ |
||
153 | cp = G.filename; /* point to internal zipfile-member pathname */ |
||
154 | else |
||
155 | ++cp; /* point to start of last component of path */ |
||
156 | |||
157 | /*--------------------------------------------------------------------------- |
||
158 | Begin main loop through characters in filename. |
||
159 | ---------------------------------------------------------------------------*/ |
||
160 | |||
161 | while ((workch = (uch)*cp++) != 0) { |
||
162 | |||
163 | switch (workch) { |
||
164 | case '/': /* can assume -j flag not given */ |
||
165 | *pp = '\0'; |
||
166 | if (strcmp(pathcomp, ".") == 0) { |
||
167 | /* don't bother appending "./" to the path */ |
||
168 | *pathcomp = '\0'; |
||
169 | } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) { |
||
170 | /* "../" dir traversal detected, skip over it */ |
||
171 | *pathcomp = '\0'; |
||
172 | killed_ddot = TRUE; /* set "show message" flag */ |
||
173 | } |
||
174 | /* when path component is not empty, append it now */ |
||
175 | if (*pathcomp != '\0' && |
||
176 | ((error = checkdir(__G__ pathcomp, APPEND_DIR)) |
||
177 | & MPN_MASK) > MPN_INF_TRUNC) |
||
178 | return error; |
||
179 | pp = pathcomp; /* reset conversion buffer for next piece */ |
||
180 | lastsemi = (char *)NULL; /* leave direct. semi-colons alone */ |
||
181 | break; |
||
182 | |||
183 | #ifdef __CYGWIN__ /* Cygwin runs on Win32, apply FAT/NTFS filename rules */ |
||
184 | case ':': /* drive spec not stored, so no colon allowed */ |
||
185 | case '\\': /* '\\' may come as normal filename char (not */ |
||
186 | case '<': /* dir sep char!) from unix-like file system */ |
||
187 | case '>': /* no redirection symbols allowed either */ |
||
188 | case '|': /* no pipe signs allowed */ |
||
189 | case '"': /* no double quotes allowed */ |
||
190 | case '?': /* no wildcards allowed */ |
||
191 | case '*': |
||
192 | *pp++ = '_'; /* these rules apply equally to FAT and NTFS */ |
||
193 | break; |
||
194 | #endif |
||
195 | |||
196 | case ';': /* VMS version (or DEC-20 attrib?) */ |
||
197 | lastsemi = pp; |
||
198 | *pp++ = ';'; /* keep for now; remove VMS ";##" */ |
||
199 | break; /* later, if requested */ |
||
200 | |||
201 | #ifdef ACORN_FTYPE_NFS |
||
202 | case ',': /* NFS filetype extension */ |
||
203 | lastcomma = pp; |
||
204 | *pp++ = ','; /* keep for now; may need to remove */ |
||
205 | break; /* later, if requested */ |
||
206 | #endif |
||
207 | |||
208 | #ifdef MTS |
||
209 | case ' ': /* change spaces to underscore under */ |
||
210 | *pp++ = '_'; /* MTS; leave as spaces under Unix */ |
||
211 | break; |
||
212 | #endif |
||
213 | |||
214 | default: |
||
215 | /* disable control character filter when requested, |
||
216 | * else allow 8-bit characters (e.g. UTF-8) in filenames: |
||
217 | */ |
||
6727 | siemargl | 218 | if ((isprint(workch) || (128 <= workch && workch <= 254))) |
219 | *pp++ = (char)workch; |
||
6725 | siemargl | 220 | /*kos |
221 | if (uO.cflxflag || |
||
222 | (isprint(workch) || (128 <= workch && workch <= 254))) |
||
223 | *pp++ = (char)workch; |
||
224 | */ |
||
225 | } /* end switch */ |
||
226 | |||
227 | } /* end while loop */ |
||
228 | |||
229 | /* Show warning when stripping insecure "parent dir" path components */ |
||
230 | if (killed_ddot && QCOND2) { |
||
231 | Info(slide, 0, ((char *)slide, |
||
232 | "warning: skipped \"../\" path component(s) in %s\n", |
||
233 | FnFilter1(G.filename))); |
||
234 | if (!(error & ~MPN_MASK)) |
||
235 | error = (error & MPN_MASK) | PK_WARN; |
||
236 | } |
||
237 | |||
238 | /*--------------------------------------------------------------------------- |
||
239 | Report if directory was created (and no file to create: filename ended |
||
240 | in '/'), check name to be sure it exists, and combine path and name be- |
||
241 | fore exiting. |
||
242 | ---------------------------------------------------------------------------*/ |
||
243 | |||
244 | if (G.filename[strlen(G.filename) - 1] == '/') { |
||
245 | checkdir(__G__ G.filename, GETPATH); |
||
246 | if (G.created_dir) { |
||
247 | if (QCOND2) { |
||
248 | Info(slide, 0, ((char *)slide, " creating: %s\n", |
||
249 | FnFilter1(G.filename))); |
||
250 | } |
||
251 | #ifndef NO_CHMOD |
||
252 | /* Filter out security-relevant attributes bits. */ |
||
253 | G.pInfo->file_attr = filtattr(__G__ G.pInfo->file_attr); |
||
254 | /* When extracting non-UNIX directories or when extracting |
||
255 | * without UID/GID restoration or SGID preservation, any |
||
256 | * SGID flag inherited from the parent directory should be |
||
257 | * maintained to allow files extracted into this new folder |
||
258 | * to inherit the GID setting from the parent directory. |
||
259 | */ |
||
260 | if (G.pInfo->hostnum != UNIX_ || !(uO.X_flag || uO.K_flag)) { |
||
261 | /* preserve SGID bit when inherited from parent dir */ |
||
262 | if (!SSTAT(G.filename, &G.statbuf)) { |
||
263 | G.pInfo->file_attr |= G.statbuf.st_mode & S_ISGID; |
||
264 | } else { |
||
265 | perror("Could not read directory attributes"); |
||
266 | } |
||
267 | } |
||
268 | |||
269 | /* set approx. dir perms (make sure can still read/write in dir) */ |
||
270 | if (chmod(G.filename, G.pInfo->file_attr | 0700)) |
||
271 | perror("chmod (directory attributes) error"); |
||
272 | #endif |
||
273 | /* set dir time (note trailing '/') */ |
||
274 | return (error & ~MPN_MASK) | MPN_CREATED_DIR; |
||
275 | } |
||
276 | /* dir existed already; don't look for data to extract */ |
||
277 | return (error & ~MPN_MASK) | MPN_INF_SKIP; |
||
278 | } |
||
279 | |||
280 | *pp = '\0'; /* done with pathcomp: terminate it */ |
||
281 | |||
282 | /* if not saving them, remove VMS version numbers (appended ";###") */ |
||
283 | if (!uO.V_flag && lastsemi) { |
||
284 | pp = lastsemi + 1; |
||
285 | while (isdigit((uch)(*pp))) |
||
286 | ++pp; |
||
287 | if (*pp == '\0') /* only digits between ';' and end: nuke */ |
||
288 | *lastsemi = '\0'; |
||
289 | } |
||
290 | |||
291 | /* On UNIX (and compatible systems), "." and ".." are reserved for |
||
292 | * directory navigation and cannot be used as regular file names. |
||
293 | * These reserved one-dot and two-dot names are mapped to "_" and "__". |
||
294 | */ |
||
295 | if (strcmp(pathcomp, ".") == 0) |
||
296 | *pathcomp = '_'; |
||
297 | else if (strcmp(pathcomp, "..") == 0) |
||
298 | strcpy(pathcomp, "__"); |
||
299 | |||
300 | #ifdef ACORN_FTYPE_NFS |
||
301 | /* translate Acorn filetype information if asked to do so */ |
||
302 | if (uO.acorn_nfs_ext && |
||
303 | (ef_spark = (RO_extra_block *) |
||
304 | getRISCOSexfield(G.extra_field, G.lrec.extra_field_length)) |
||
305 | != (RO_extra_block *)NULL) |
||
306 | { |
||
307 | /* file *must* have a RISC OS extra field */ |
||
308 | long ft = (long)makelong(ef_spark->loadaddr); |
||
309 | /*32-bit*/ |
||
310 | if (lastcomma) { |
||
311 | pp = lastcomma + 1; |
||
312 | while (isxdigit((uch)(*pp))) ++pp; |
||
313 | if (pp == lastcomma+4 && *pp == '\0') *lastcomma='\0'; /* nuke */ |
||
314 | } |
||
315 | if ((ft & 1<<31)==0) ft=0x000FFD00; |
||
316 | sprintf(pathcomp+strlen(pathcomp), ",%03x", (int)(ft>>8) & 0xFFF); |
||
317 | } |
||
318 | #endif /* ACORN_FTYPE_NFS */ |
||
319 | |||
320 | if (*pathcomp == '\0') { |
||
321 | Info(slide, 1, ((char *)slide, "mapname: conversion of %s failed\n", |
||
322 | FnFilter1(G.filename))); |
||
323 | return (error & ~MPN_MASK) | MPN_ERR_SKIP; |
||
324 | } |
||
325 | |||
326 | checkdir(__G__ pathcomp, APPEND_NAME); /* returns 1 if truncated: care? */ |
||
327 | checkdir(__G__ G.filename, GETPATH); |
||
328 | |||
329 | return error; |
||
330 | |||
331 | } /* end function mapname() */ |
||
332 | |||
333 | |||
334 | |||
335 | |||
336 | #if 0 /*========== NOTES ==========*/ |
||
337 | |||
338 | extract-to dir: a:path/ |
||
339 | buildpath: path1/path2/ ... (NULL-terminated) |
||
340 | pathcomp: filename |
||
341 | |||
342 | mapname(): |
||
343 | loop over chars in zipfile member name |
||
344 | checkdir(path component, COMPONENT | CREATEDIR) --> map as required? |
||
345 | (d:/tmp/unzip/) (disk:[tmp.unzip.) |
||
346 | (d:/tmp/unzip/jj/) (disk:[tmp.unzip.jj.) |
||
347 | (d:/tmp/unzip/jj/temp/) (disk:[tmp.unzip.jj.temp.) |
||
348 | finally add filename itself and check for existence? (could use with rename) |
||
349 | (d:/tmp/unzip/jj/temp/msg.outdir) (disk:[tmp.unzip.jj.temp]msg.outdir) |
||
350 | checkdir(name, GETPATH) --> copy path to name and free space |
||
351 | |||
352 | #endif /* 0 */ |
||
353 | |||
354 | |||
355 | |||
356 | |||
6727 | siemargl | 357 | /********************************************************************************************************************/ |
358 | /*** Function checkdir() */ |
||
359 | /********************************************************************************************************************/ |
||
6725 | siemargl | 360 | |
6727 | siemargl | 361 | |
6725 | siemargl | 362 | int checkdir(__G__ pathcomp, flag) |
363 | __GDEF |
||
364 | char *pathcomp; |
||
365 | int flag; |
||
366 | /* |
||
367 | * returns: |
||
368 | * MPN_OK - no problem detected |
||
369 | * MPN_INF_TRUNC - (on APPEND_NAME) truncated filename |
||
370 | * MPN_INF_SKIP - path doesn't exist, not allowed to create |
||
371 | * MPN_ERR_SKIP - path doesn't exist, tried to create and failed; or path |
||
372 | * exists and is not a directory, but is supposed to be |
||
373 | * MPN_ERR_TOOLONG - path is too long |
||
374 | * MPN_NOMEM - can't allocate memory for filename buffers |
||
375 | */ |
||
376 | { |
||
377 | /* static int rootlen = 0; */ /* length of rootpath */ |
||
378 | /* static char *rootpath; */ /* user's "extract-to" directory */ |
||
379 | /* static char *buildpath; */ /* full path (so far) to extracted file */ |
||
380 | /* static char *end; */ /* pointer to end of buildpath ('\0') */ |
||
381 | |||
382 | # define FN_MASK 7 |
||
383 | # define FUNCTION (flag & FN_MASK) |
||
384 | |||
385 | |||
386 | |||
387 | /*--------------------------------------------------------------------------- |
||
388 | APPEND_DIR: append the path component to the path being built and check |
||
389 | for its existence. If doesn't exist and we are creating directories, do |
||
390 | so for this one; else signal success or error as appropriate. |
||
391 | ---------------------------------------------------------------------------*/ |
||
392 | |||
393 | if (FUNCTION == APPEND_DIR) { |
||
394 | int too_long = FALSE; |
||
395 | #ifdef SHORT_NAMES |
||
396 | char *old_end = end; |
||
397 | #endif |
||
398 | |||
399 | Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp))); |
||
400 | while ((*G.end = *pathcomp++) != '\0') |
||
401 | ++G.end; |
||
402 | #ifdef SHORT_NAMES /* path components restricted to 14 chars, typically */ |
||
403 | if ((G.end-old_end) > FILENAME_MAX) /* GRR: proper constant? */ |
||
404 | *(G.end = old_end + FILENAME_MAX) = '\0'; |
||
405 | #endif |
||
406 | |||
407 | /* GRR: could do better check, see if overrunning buffer as we go: |
||
408 | * check end-buildpath after each append, set warning variable if |
||
409 | * within 20 of FILNAMSIZ; then if var set, do careful check when |
||
410 | * appending. Clear variable when begin new path. */ |
||
411 | |||
412 | /* next check: need to append '/', at least one-char name, '\0' */ |
||
413 | if ((G.end-G.buildpath) > FILNAMSIZ-3) |
||
414 | too_long = TRUE; /* check if extracting dir? */ |
||
415 | if (SSTAT(G.buildpath, &G.statbuf)) { /* path doesn't exist */ |
||
416 | if (!G.create_dirs) { /* told not to create (freshening) */ |
||
417 | free(G.buildpath); |
||
418 | return MPN_INF_SKIP; /* path doesn't exist: nothing to do */ |
||
419 | } |
||
420 | if (too_long) { |
||
421 | Info(slide, 1, ((char *)slide, |
||
422 | "checkdir error: path too long: %s\n", |
||
423 | FnFilter1(G.buildpath))); |
||
424 | free(G.buildpath); |
||
425 | /* no room for filenames: fatal */ |
||
426 | return MPN_ERR_TOOLONG; |
||
427 | } |
||
428 | if (mkdir(G.buildpath, 0777) == -1) { /* create the directory */ |
||
429 | Info(slide, 1, ((char *)slide, |
||
430 | "checkdir error: cannot create %s\n\ |
||
431 | %s\n\ |
||
432 | unable to process %s.\n", |
||
433 | FnFilter2(G.buildpath), |
||
434 | strerror(errno), |
||
435 | FnFilter1(G.filename))); |
||
436 | free(G.buildpath); |
||
437 | /* path didn't exist, tried to create, failed */ |
||
438 | return MPN_ERR_SKIP; |
||
439 | } |
||
440 | G.created_dir = TRUE; |
||
441 | } else if (!S_ISDIR(G.statbuf.st_mode)) { |
||
442 | Info(slide, 1, ((char *)slide, |
||
443 | "checkdir error: %s exists but is not directory\n\ |
||
444 | unable to process %s.\n", |
||
445 | FnFilter2(G.buildpath), FnFilter1(G.filename))); |
||
446 | free(G.buildpath); |
||
447 | /* path existed but wasn't dir */ |
||
448 | return MPN_ERR_SKIP; |
||
449 | } |
||
450 | if (too_long) { |
||
451 | Info(slide, 1, ((char *)slide, |
||
452 | "checkdir error: path too long: %s\n", FnFilter1(G.buildpath))); |
||
453 | free(G.buildpath); |
||
454 | /* no room for filenames: fatal */ |
||
455 | return MPN_ERR_TOOLONG; |
||
456 | } |
||
457 | *G.end++ = '/'; |
||
458 | *G.end = '\0'; |
||
459 | Trace((stderr, "buildpath now = [%s]\n", FnFilter1(G.buildpath))); |
||
460 | return MPN_OK; |
||
461 | |||
462 | } /* end if (FUNCTION == APPEND_DIR) */ |
||
463 | |||
464 | /*--------------------------------------------------------------------------- |
||
465 | GETPATH: copy full path to the string pointed at by pathcomp, and free |
||
466 | G.buildpath. |
||
467 | ---------------------------------------------------------------------------*/ |
||
468 | |||
469 | if (FUNCTION == GETPATH) { |
||
470 | strcpy(pathcomp, G.buildpath); |
||
471 | Trace((stderr, "getting and freeing path [%s]\n", |
||
472 | FnFilter1(pathcomp))); |
||
473 | free(G.buildpath); |
||
474 | G.buildpath = G.end = (char *)NULL; |
||
475 | return MPN_OK; |
||
476 | } |
||
477 | |||
478 | /*--------------------------------------------------------------------------- |
||
479 | APPEND_NAME: assume the path component is the filename; append it and |
||
480 | return without checking for existence. |
||
481 | ---------------------------------------------------------------------------*/ |
||
482 | |||
483 | if (FUNCTION == APPEND_NAME) { |
||
484 | #ifdef SHORT_NAMES |
||
485 | char *old_end = end; |
||
486 | #endif |
||
487 | |||
488 | Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp))); |
||
489 | while ((*G.end = *pathcomp++) != '\0') { |
||
490 | ++G.end; |
||
491 | #ifdef SHORT_NAMES /* truncate name at 14 characters, typically */ |
||
492 | if ((G.end-old_end) > FILENAME_MAX) /* GRR: proper constant? */ |
||
493 | *(G.end = old_end + FILENAME_MAX) = '\0'; |
||
494 | #endif |
||
495 | if ((G.end-G.buildpath) >= FILNAMSIZ) { |
||
496 | *--G.end = '\0'; |
||
497 | Info(slide, 0x201, ((char *)slide, |
||
498 | "checkdir warning: path too long; truncating\n\ |
||
499 | %s\n -> %s\n", |
||
500 | FnFilter1(G.filename), FnFilter2(G.buildpath))); |
||
501 | return MPN_INF_TRUNC; /* filename truncated */ |
||
502 | } |
||
503 | } |
||
504 | Trace((stderr, "buildpath now = [%s]\n", FnFilter1(G.buildpath))); |
||
505 | /* could check for existence here, prompt for new name... */ |
||
506 | return MPN_OK; |
||
507 | } |
||
508 | |||
509 | /*--------------------------------------------------------------------------- |
||
510 | INIT: allocate and initialize buffer space for the file currently being |
||
511 | extracted. If file was renamed with an absolute path, don't prepend the |
||
512 | extract-to path. |
||
513 | ---------------------------------------------------------------------------*/ |
||
514 | |||
515 | /* GRR: for VMS and TOPS-20, add up to 13 to strlen */ |
||
516 | |||
517 | if (FUNCTION == INIT) { |
||
518 | Trace((stderr, "initializing buildpath to ")); |
||
519 | #ifdef ACORN_FTYPE_NFS |
||
520 | if ((G.buildpath = (char *)malloc(strlen(G.filename)+G.rootlen+ |
||
521 | (uO.acorn_nfs_ext ? 5 : 1))) |
||
522 | #else |
||
523 | if ((G.buildpath = (char *)malloc(strlen(G.filename)+G.rootlen+1)) |
||
524 | #endif |
||
525 | == (char *)NULL) |
||
526 | return MPN_NOMEM; |
||
527 | if ((G.rootlen > 0) && !G.renamed_fullpath) { |
||
528 | strcpy(G.buildpath, G.rootpath); |
||
529 | G.end = G.buildpath + G.rootlen; |
||
530 | } else { |
||
531 | *G.buildpath = '\0'; |
||
532 | G.end = G.buildpath; |
||
533 | } |
||
534 | Trace((stderr, "[%s]\n", FnFilter1(G.buildpath))); |
||
535 | return MPN_OK; |
||
536 | } |
||
537 | |||
538 | /*--------------------------------------------------------------------------- |
||
539 | ROOT: if appropriate, store the path in rootpath and create it if |
||
540 | necessary; else assume it's a zipfile member and return. This path |
||
541 | segment gets used in extracting all members from every zipfile specified |
||
542 | on the command line. |
||
543 | ---------------------------------------------------------------------------*/ |
||
544 | |||
545 | #if (!defined(SFX) || defined(SFX_EXDIR)) |
||
546 | if (FUNCTION == ROOT) { |
||
547 | Trace((stderr, "initializing root path to [%s]\n", |
||
548 | FnFilter1(pathcomp))); |
||
549 | if (pathcomp == (char *)NULL) { |
||
550 | G.rootlen = 0; |
||
551 | return MPN_OK; |
||
552 | } |
||
553 | if (G.rootlen > 0) /* rootpath was already set, nothing to do */ |
||
554 | return MPN_OK; |
||
555 | if ((G.rootlen = strlen(pathcomp)) > 0) { |
||
556 | char *tmproot; |
||
557 | |||
558 | if ((tmproot = (char *)malloc(G.rootlen+2)) == (char *)NULL) { |
||
559 | G.rootlen = 0; |
||
560 | return MPN_NOMEM; |
||
561 | } |
||
562 | strcpy(tmproot, pathcomp); |
||
563 | if (tmproot[G.rootlen-1] == '/') { |
||
564 | tmproot[--G.rootlen] = '\0'; |
||
565 | } |
||
566 | if (G.rootlen > 0 && (SSTAT(tmproot, &G.statbuf) || |
||
567 | !S_ISDIR(G.statbuf.st_mode))) |
||
568 | { /* path does not exist */ |
||
569 | if (!G.create_dirs /* || iswild(tmproot) */ ) { |
||
570 | free(tmproot); |
||
571 | G.rootlen = 0; |
||
572 | /* skip (or treat as stored file) */ |
||
573 | return MPN_INF_SKIP; |
||
574 | } |
||
575 | /* create the directory (could add loop here scanning tmproot |
||
576 | * to create more than one level, but why really necessary?) */ |
||
577 | if (mkdir(tmproot, 0777) == -1) { |
||
578 | Info(slide, 1, ((char *)slide, |
||
579 | "checkdir: cannot create extraction directory: %s\n\ |
||
580 | %s\n", |
||
581 | FnFilter1(tmproot), strerror(errno))); |
||
582 | free(tmproot); |
||
583 | G.rootlen = 0; |
||
584 | /* path didn't exist, tried to create, and failed: */ |
||
585 | /* file exists, or 2+ subdir levels required */ |
||
586 | return MPN_ERR_SKIP; |
||
587 | } |
||
588 | } |
||
589 | tmproot[G.rootlen++] = '/'; |
||
590 | tmproot[G.rootlen] = '\0'; |
||
591 | if ((G.rootpath = (char *)realloc(tmproot, G.rootlen+1)) == NULL) { |
||
592 | free(tmproot); |
||
593 | G.rootlen = 0; |
||
594 | return MPN_NOMEM; |
||
595 | } |
||
596 | Trace((stderr, "rootpath now = [%s]\n", FnFilter1(G.rootpath))); |
||
597 | } |
||
598 | return MPN_OK; |
||
599 | } |
||
600 | #endif /* !SFX || SFX_EXDIR */ |
||
601 | |||
602 | /*--------------------------------------------------------------------------- |
||
603 | END: free rootpath, immediately prior to program exit. |
||
604 | ---------------------------------------------------------------------------*/ |
||
605 | |||
606 | if (FUNCTION == END) { |
||
607 | Trace((stderr, "freeing rootpath\n")); |
||
608 | if (G.rootlen > 0) { |
||
609 | free(G.rootpath); |
||
610 | G.rootlen = 0; |
||
611 | } |
||
612 | return MPN_OK; |
||
613 | } |
||
614 | |||
615 | return MPN_INVALID; /* should never reach */ |
||
616 | |||
617 | } /* end function checkdir() */ |
||
618 | |||
619 | static int get_extattribs OF((__GPRO__ iztimes *pzt, ulg z_uidgid[2])); |
||
620 | |||
621 | static int get_extattribs(__G__ pzt, z_uidgid) |
||
622 | __GDEF |
||
623 | iztimes *pzt; |
||
624 | ulg z_uidgid[2]; |
||
625 | { |
||
626 | /*--------------------------------------------------------------------------- |
||
627 | Convert from MSDOS-format local time and date to Unix-format 32-bit GMT |
||
628 | time: adjust base year from 1980 to 1970, do usual conversions from |
||
629 | yy/mm/dd hh:mm:ss to elapsed seconds, and account for timezone and day- |
||
630 | light savings time differences. If we have a Unix extra field, however, |
||
631 | we're laughing: both mtime and atime are ours. On the other hand, we |
||
632 | then have to check for restoration of UID/GID. |
||
633 | ---------------------------------------------------------------------------*/ |
||
634 | int have_uidgid_flg; |
||
635 | unsigned eb_izux_flg; |
||
636 | |||
637 | eb_izux_flg = 0; // kos32 |
||
638 | /* |
||
639 | (G.extra_field ? ef_scan_for_izux(G.extra_field, |
||
640 | G.lrec.extra_field_length, 0, G.lrec.last_mod_dos_datetime, |
||
641 | #ifdef IZ_CHECK_TZ |
||
642 | (G.tz_is_valid ? pzt : NULL), |
||
643 | #else |
||
644 | pzt, |
||
645 | #endif |
||
646 | z_uidgid) : 0); |
||
647 | */ |
||
648 | if (eb_izux_flg & EB_UT_FL_MTIME) { |
||
649 | TTrace((stderr, "\nget_extattribs: Unix e.f. modif. time = %ld\n", |
||
650 | pzt->mtime)); |
||
651 | } else { |
||
652 | pzt->mtime = dos_to_unix_time(G.lrec.last_mod_dos_datetime); |
||
653 | } |
||
654 | if (eb_izux_flg & EB_UT_FL_ATIME) { |
||
655 | TTrace((stderr, "get_extattribs: Unix e.f. access time = %ld\n", |
||
656 | pzt->atime)); |
||
657 | } else { |
||
658 | pzt->atime = pzt->mtime; |
||
659 | TTrace((stderr, "\nget_extattribs: modification/access times = %ld\n", |
||
660 | pzt->mtime)); |
||
661 | } |
||
662 | |||
663 | /* if -X option was specified and we have UID/GID info, restore it */ |
||
664 | have_uidgid_flg = |
||
665 | #ifdef RESTORE_UIDGID |
||
666 | (uO.X_flag && (eb_izux_flg & EB_UX2_VALID)); |
||
667 | #else |
||
668 | 0; |
||
669 | #endif |
||
670 | return have_uidgid_flg; |
||
671 | } |
||
672 | |||
673 | /****************************/ |
||
674 | /* Function close_outfile() */ |
||
675 | /****************************/ |
||
676 | |||
677 | void close_outfile(__G) /* GRR: change to return PK-style warning level */ |
||
678 | __GDEF |
||
679 | { |
||
680 | union { |
||
681 | iztimes t3; /* mtime, atime, ctime */ |
||
682 | ztimbuf t2; /* modtime, actime */ |
||
683 | } zt; |
||
684 | ulg z_uidgid[2]; |
||
685 | int have_uidgid_flg; |
||
686 | |||
687 | have_uidgid_flg = get_extattribs(__G__ &(zt.t3), z_uidgid); |
||
688 | |||
689 | /*--------------------------------------------------------------------------- |
||
690 | If symbolic links are supported, allocate storage for a symlink control |
||
691 | structure, put the uncompressed "data" and other required info in it, and |
||
692 | add the structure to the "deferred symlinks" chain. Since we know it's a |
||
693 | symbolic link to start with, we shouldn't have to worry about overflowing |
||
694 | unsigned ints with unsigned longs. |
||
695 | ---------------------------------------------------------------------------*/ |
||
696 | |||
697 | #ifdef SYMLINKS |
||
698 | if (G.symlnk) { |
||
699 | extent ucsize = (extent)G.lrec.ucsize; |
||
700 | # ifdef SET_SYMLINK_ATTRIBS |
||
701 | extent attribsize = sizeof(unsigned) + |
||
702 | (have_uidgid_flg ? sizeof(z_uidgid) : 0); |
||
703 | # else |
||
704 | extent attribsize = 0; |
||
705 | # endif |
||
706 | /* size of the symlink entry is the sum of |
||
707 | * (struct size (includes 1st '\0') + 1 additional trailing '\0'), |
||
708 | * system specific attribute data size (might be 0), |
||
709 | * and the lengths of name and link target. |
||
710 | */ |
||
711 | extent slnk_entrysize = (sizeof(slinkentry) + 1) + attribsize + |
||
712 | ucsize + strlen(G.filename); |
||
713 | slinkentry *slnk_entry; |
||
714 | |||
715 | if (slnk_entrysize < ucsize) { |
||
716 | Info(slide, 0x201, ((char *)slide, |
||
717 | "warning: symbolic link (%s) failed: mem alloc overflow\n", |
||
718 | FnFilter1(G.filename))); |
||
719 | fclose(G.outfile); |
||
720 | return; |
||
721 | } |
||
722 | |||
723 | if ((slnk_entry = (slinkentry *)malloc(slnk_entrysize)) == NULL) { |
||
724 | Info(slide, 0x201, ((char *)slide, |
||
725 | "warning: symbolic link (%s) failed: no mem\n", |
||
726 | FnFilter1(G.filename))); |
||
727 | fclose(G.outfile); |
||
728 | return; |
||
729 | } |
||
730 | slnk_entry->next = NULL; |
||
731 | slnk_entry->targetlen = ucsize; |
||
732 | slnk_entry->attriblen = attribsize; |
||
733 | # ifdef SET_SYMLINK_ATTRIBS |
||
734 | memcpy(slnk_entry->buf, &(G.pInfo->file_attr), |
||
735 | sizeof(unsigned)); |
||
736 | if (have_uidgid_flg) |
||
737 | memcpy(slnk_entry->buf + 4, z_uidgid, sizeof(z_uidgid)); |
||
738 | # endif |
||
739 | slnk_entry->target = slnk_entry->buf + slnk_entry->attriblen; |
||
740 | slnk_entry->fname = slnk_entry->target + ucsize + 1; |
||
741 | strcpy(slnk_entry->fname, G.filename); |
||
742 | |||
743 | /* move back to the start of the file to re-read the "link data" */ |
||
744 | rewind(G.outfile); |
||
745 | |||
746 | if (fread(slnk_entry->target, 1, ucsize, G.outfile) != ucsize) |
||
747 | { |
||
748 | Info(slide, 0x201, ((char *)slide, |
||
749 | "warning: symbolic link (%s) failed\n", |
||
750 | FnFilter1(G.filename))); |
||
751 | free(slnk_entry); |
||
752 | fclose(G.outfile); |
||
753 | return; |
||
754 | } |
||
755 | fclose(G.outfile); /* close "link" file for good... */ |
||
756 | slnk_entry->target[ucsize] = '\0'; |
||
757 | if (QCOND2) |
||
758 | Info(slide, 0, ((char *)slide, "-> %s ", |
||
759 | FnFilter1(slnk_entry->target))); |
||
760 | /* add this symlink record to the list of deferred symlinks */ |
||
761 | if (G.slink_last != NULL) |
||
762 | G.slink_last->next = slnk_entry; |
||
763 | else |
||
764 | G.slink_head = slnk_entry; |
||
765 | G.slink_last = slnk_entry; |
||
766 | return; |
||
767 | } |
||
768 | #endif /* SYMLINKS */ |
||
769 | |||
770 | #ifdef QLZIP |
||
771 | if (G.extra_field) { |
||
772 | static void qlfix OF((__GPRO__ uch *ef_ptr, unsigned ef_len)); |
||
773 | |||
774 | qlfix(__G__ G.extra_field, G.lrec.extra_field_length); |
||
775 | } |
||
776 | #endif |
||
777 | |||
778 | #if (defined(NO_FCHOWN)) |
||
779 | fclose(G.outfile); |
||
6745 | siemargl | 780 | Trace((stdout, "File (%s) closed\n", FnFilter1(G.filename))); |
6725 | siemargl | 781 | #endif |
782 | |||
783 | // kos. add set file datetime ? |
||
784 | #ifndef KOS32 |
||
785 | /* if -X option was specified and we have UID/GID info, restore it */ |
||
786 | if (have_uidgid_flg |
||
787 | /* check that both uid and gid values fit into their data sizes */ |
||
788 | && ((ulg)(uid_t)(z_uidgid[0]) == z_uidgid[0]) |
||
789 | && ((ulg)(gid_t)(z_uidgid[1]) == z_uidgid[1])) { |
||
790 | TTrace((stderr, "close_outfile: restoring Unix UID/GID info\n")); |
||
791 | #if (defined(NO_FCHOWN)) |
||
792 | if (chown(G.filename, (uid_t)z_uidgid[0], (gid_t)z_uidgid[1])) |
||
793 | #else |
||
794 | if (fchown(fileno(G.outfile), (uid_t)z_uidgid[0], (gid_t)z_uidgid[1])) |
||
795 | #endif |
||
796 | { |
||
797 | if (uO.qflag) |
||
798 | Info(slide, 0x201, ((char *)slide, CannotSetItemUidGid, |
||
799 | z_uidgid[0], z_uidgid[1], FnFilter1(G.filename), |
||
800 | strerror(errno))); |
||
801 | else |
||
802 | Info(slide, 0x201, ((char *)slide, CannotSetUidGid, |
||
803 | z_uidgid[0], z_uidgid[1], strerror(errno))); |
||
804 | } |
||
805 | } |
||
806 | |||
807 | #if (!defined(NO_FCHOWN) && defined(NO_FCHMOD)) |
||
808 | fclose(G.outfile); |
||
809 | #endif |
||
810 | |||
811 | #if (!defined(NO_FCHOWN) && !defined(NO_FCHMOD)) |
||
812 | /*--------------------------------------------------------------------------- |
||
813 | Change the file permissions from default ones to those stored in the |
||
814 | zipfile. |
||
815 | ---------------------------------------------------------------------------*/ |
||
816 | |||
817 | if (fchmod(fileno(G.outfile), filtattr(__G__ G.pInfo->file_attr))) |
||
818 | perror("fchmod (file attributes) error"); |
||
819 | |||
820 | fclose(G.outfile); |
||
821 | #endif /* !NO_FCHOWN && !NO_FCHMOD */ |
||
822 | |||
823 | /* skip restoring time stamps on user's request */ |
||
824 | if (uO.D_flag <= 1) { |
||
825 | /* set the file's access and modification times */ |
||
826 | if (utime(G.filename, &(zt.t2))) { |
||
827 | if (uO.qflag) |
||
828 | Info(slide, 0x201, ((char *)slide, CannotSetItemTimestamps, |
||
829 | FnFilter1(G.filename), strerror(errno))); |
||
830 | else |
||
831 | Info(slide, 0x201, ((char *)slide, CannotSetTimestamps, |
||
832 | strerror(errno))); |
||
833 | } |
||
834 | } |
||
835 | |||
836 | #if (defined(NO_FCHOWN) || defined(NO_FCHMOD)) |
||
837 | /*--------------------------------------------------------------------------- |
||
838 | Change the file permissions from default ones to those stored in the |
||
839 | zipfile. |
||
840 | ---------------------------------------------------------------------------*/ |
||
841 | |||
842 | #ifndef NO_CHMOD |
||
843 | if (chmod(G.filename, filtattr(__G__ G.pInfo->file_attr))) |
||
844 | perror("chmod (file attributes) error"); |
||
845 | #endif |
||
846 | #endif /* NO_FCHOWN || NO_FCHMOD */ |
||
847 | |||
848 | #endif // 0 |
||
849 | |||
850 | } /* end function close_outfile() */ |
||
851 | |||
852 | |||
853 | /**********************/ |
||
854 | /* Function do_wild() */ /* for porting: dir separator; match(ignore_case) */ |
||
855 | /**********************/ |
||
856 | |||
857 | char *do_wild(__G__ wildspec) |
||
858 | __GDEF |
||
859 | ZCONST char *wildspec; /* only used first time on a given dir */ |
||
860 | { |
||
861 | /* these statics are now declared in SYSTEM_SPECIFIC_GLOBALS in unxcfg.h: |
||
862 | static DIR *wild_dir = (DIR *)NULL; |
||
863 | static ZCONST char *wildname; |
||
864 | static char *dirname, matchname[FILNAMSIZ]; |
||
865 | static int notfirstcall=FALSE, have_dirname, dirnamelen; |
||
866 | */ |
||
867 | struct dirent *file; |
||
868 | |||
869 | /* Even when we're just returning wildspec, we *always* do so in |
||
870 | * matchname[]--calling routine is allowed to append four characters |
||
871 | * to the returned string, and wildspec may be a pointer to argv[]. |
||
872 | */ |
||
873 | if (!G.notfirstcall) { /* first call: must initialize everything */ |
||
874 | G.notfirstcall = TRUE; |
||
875 | |||
876 | if (!iswild(wildspec)) { |
||
877 | strncpy(G.matchname, wildspec, FILNAMSIZ); |
||
878 | G.matchname[FILNAMSIZ-1] = '\0'; |
||
879 | G.have_dirname = FALSE; |
||
880 | G.wild_dir = NULL; |
||
881 | return G.matchname; |
||
882 | } |
||
883 | |||
884 | /* break the wildspec into a directory part and a wildcard filename */ |
||
885 | if ((G.wildname = (ZCONST char *)strrchr(wildspec, '/')) == NULL) { |
||
886 | G.dirname = "."; |
||
887 | G.dirnamelen = 1; |
||
888 | G.have_dirname = FALSE; |
||
889 | G.wildname = wildspec; |
||
890 | } else { |
||
891 | ++G.wildname; /* point at character after '/' */ |
||
892 | G.dirnamelen = G.wildname - wildspec; |
||
893 | if ((G.dirname = (char *)malloc(G.dirnamelen+1)) == (char *)NULL) { |
||
894 | Info(slide, 0x201, ((char *)slide, |
||
895 | "warning: cannot allocate wildcard buffers\n")); |
||
896 | strncpy(G.matchname, wildspec, FILNAMSIZ); |
||
897 | G.matchname[FILNAMSIZ-1] = '\0'; |
||
898 | return G.matchname; /* but maybe filespec was not a wildcard */ |
||
899 | } |
||
900 | strncpy(G.dirname, wildspec, G.dirnamelen); |
||
901 | G.dirname[G.dirnamelen] = '\0'; /* terminate for strcpy below */ |
||
902 | G.have_dirname = TRUE; |
||
903 | } |
||
904 | |||
905 | if ((G.wild_dir = (zvoid *)opendir(G.dirname)) != (zvoid *)NULL) { |
||
906 | while ((file = readdir((DIR *)G.wild_dir)) != |
||
907 | (struct dirent *)NULL) { |
||
908 | Trace((stderr, "do_wild: readdir returns %s\n", |
||
909 | FnFilter1(file->d_name))); |
||
910 | if (file->d_name[0] == '.' && G.wildname[0] != '.') |
||
911 | continue; /* Unix: '*' and '?' do not match leading dot */ |
||
912 | if (match(file->d_name, G.wildname, 0 WISEP) &&/*0=case sens.*/ |
||
913 | /* skip "." and ".." directory entries */ |
||
914 | strcmp(file->d_name, ".") && strcmp(file->d_name, "..")) { |
||
915 | Trace((stderr, "do_wild: match() succeeds\n")); |
||
916 | if (G.have_dirname) { |
||
917 | strcpy(G.matchname, G.dirname); |
||
918 | strcpy(G.matchname+G.dirnamelen, file->d_name); |
||
919 | } else |
||
920 | strcpy(G.matchname, file->d_name); |
||
921 | return G.matchname; |
||
922 | } |
||
923 | } |
||
924 | /* if we get to here directory is exhausted, so close it */ |
||
925 | closedir((DIR *)G.wild_dir); |
||
926 | G.wild_dir = (zvoid *)NULL; |
||
927 | } |
||
928 | Trace((stderr, "do_wild: opendir(%s) returns NULL\n", |
||
929 | FnFilter1(G.dirname))); |
||
930 | |||
931 | /* return the raw wildspec in case that works (e.g., directory not |
||
932 | * searchable, but filespec was not wild and file is readable) */ |
||
933 | strncpy(G.matchname, wildspec, FILNAMSIZ); |
||
934 | G.matchname[FILNAMSIZ-1] = '\0'; |
||
935 | return G.matchname; |
||
936 | } |
||
937 | |||
938 | /* last time through, might have failed opendir but returned raw wildspec */ |
||
939 | if ((DIR *)G.wild_dir == (DIR *)NULL) { |
||
940 | G.notfirstcall = FALSE; /* nothing left--reset for new wildspec */ |
||
941 | if (G.have_dirname) |
||
942 | free(G.dirname); |
||
943 | return (char *)NULL; |
||
944 | } |
||
945 | |||
946 | /* If we've gotten this far, we've read and matched at least one entry |
||
947 | * successfully (in a previous call), so dirname has been copied into |
||
948 | * matchname already. |
||
949 | */ |
||
950 | while ((file = readdir((DIR *)G.wild_dir)) != (struct dirent *)NULL) { |
||
951 | Trace((stderr, "do_wild: readdir returns %s\n", |
||
952 | FnFilter1(file->d_name))); |
||
953 | if (file->d_name[0] == '.' && G.wildname[0] != '.') |
||
954 | continue; /* Unix: '*' and '?' do not match leading dot */ |
||
955 | if (match(file->d_name, G.wildname, 0 WISEP)) { /* 0 == case sens. */ |
||
956 | Trace((stderr, "do_wild: match() succeeds\n")); |
||
957 | if (G.have_dirname) { |
||
958 | /* strcpy(G.matchname, G.dirname); */ |
||
959 | strcpy(G.matchname+G.dirnamelen, file->d_name); |
||
960 | } else |
||
961 | strcpy(G.matchname, file->d_name); |
||
962 | return G.matchname; |
||
963 | } |
||
964 | } |
||
965 | |||
966 | closedir((DIR *)G.wild_dir); /* at least one entry read; nothing left */ |
||
967 | G.wild_dir = (zvoid *)NULL; |
||
968 | G.notfirstcall = FALSE; /* reset for new wildspec */ |
||
969 | if (G.have_dirname) |
||
970 | free(G.dirname); |
||
971 | return (char *)NULL; |
||
972 | |||
973 | } /* end function do_wild() */=>>31)==0)><31)==0)>=>=>=>=>':> |
||
974 |