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