Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
6725 | siemargl | 1 | /* |
2 | Copyright (c) 1990-2008 Info-ZIP. All rights reserved. |
||
3 | |||
4 | See the accompanying file LICENSE, version 2000-Apr-09 or later |
||
5 | (the contents of which are also included in unzip.h) for terms of use. |
||
6 | If, for some reason, all these files are missing, the Info-ZIP license |
||
7 | also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html |
||
8 | */ |
||
9 | /*--------------------------------------------------------------------------- |
||
10 | |||
11 | atari.c |
||
12 | |||
13 | Atari-specific routines for use with Info-ZIP's UnZip 5.1 and later. |
||
14 | |||
15 | Contains: readdir() |
||
16 | do_wild() <-- generic enough to put in fileio.c? |
||
17 | mapattr() |
||
18 | mapname() |
||
19 | checkdir() |
||
20 | mkdir() |
||
21 | close_outfile() |
||
22 | stamp_file() [optional feature] |
||
23 | version() |
||
24 | |||
25 | Due to the amazing MiNT library being very, very close to BSD unix's |
||
26 | library, I'm using the unix.c as a base for this. Note: If you're not |
||
27 | going to compile this with the MiNT libraries (for GNU C, Turbo C, Pure C, |
||
28 | Lattice C, or Heat & Serve C), you're going to be in for some nasty work. |
||
29 | Most of the modifications in this file were made by Chris Herborth |
||
30 | (cherborth@semprini.waterloo-rdp.on.ca) and /should/ be marked with [cjh]. |
||
31 | |||
32 | ---------------------------------------------------------------------------*/ |
||
33 | |||
34 | |||
35 | #define UNZIP_INTERNAL |
||
36 | #include "unzip.h" |
||
37 | #include |
||
38 | |||
39 | static int created_dir; /* used in mapname(), checkdir() */ |
||
40 | static int renamed_fullpath; /* ditto */ |
||
41 | |||
42 | |||
43 | #ifndef SFX |
||
44 | |||
45 | /**********************/ |
||
46 | /* Function do_wild() */ /* for porting: dir separator; match(ignore_case) */ |
||
47 | /**********************/ |
||
48 | |||
49 | char *do_wild(__G__ wildspec) |
||
50 | __GDEF |
||
51 | ZCONST char *wildspec; /* only used first time on a given dir */ |
||
52 | { |
||
53 | static DIR *wild_dir = (DIR *)NULL; |
||
54 | static ZCONST char *wildname; |
||
55 | static char *dirname, matchname[FILNAMSIZ]; |
||
56 | static int notfirstcall=FALSE, have_dirname, dirnamelen; |
||
57 | struct dirent *file; |
||
58 | |||
59 | /* Even when we're just returning wildspec, we *always* do so in |
||
60 | * matchname[]--calling routine is allowed to append four characters |
||
61 | * to the returned string, and wildspec may be a pointer to argv[]. |
||
62 | */ |
||
63 | if (!notfirstcall) { /* first call: must initialize everything */ |
||
64 | notfirstcall = TRUE; |
||
65 | |||
66 | if (!iswild(wildspec)) { |
||
67 | strncpy(matchname, wildspec, FILNAMSIZ); |
||
68 | matchname[FILNAMSIZ-1] = '\0'; |
||
69 | have_dirname = FALSE; |
||
70 | dir = NULL; |
||
71 | return matchname; |
||
72 | } |
||
73 | |||
74 | /* break the wildspec into a directory part and a wildcard filename */ |
||
75 | if ((wildname = strrchr(wildspec, '/')) == (ZCONST char *)NULL) { |
||
76 | dirname = "."; |
||
77 | dirnamelen = 1; |
||
78 | have_dirname = FALSE; |
||
79 | wildname = wildspec; |
||
80 | } else { |
||
81 | ++wildname; /* point at character after '/' */ |
||
82 | dirnamelen = wildname - wildspec; |
||
83 | if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) { |
||
84 | Info(slide, 0x201, ((char *)slide, |
||
85 | "warning: cannot allocate wildcard buffers\n")); |
||
86 | strncpy(matchname, wildspec, FILNAMSIZ); |
||
87 | matchname[FILNAMSIZ-1] = '\0'; |
||
88 | return matchname; /* but maybe filespec was not a wildcard */ |
||
89 | } |
||
90 | strncpy(dirname, wildspec, dirnamelen); |
||
91 | dirname[dirnamelen] = '\0'; /* terminate for strcpy below */ |
||
92 | have_dirname = TRUE; |
||
93 | } |
||
94 | |||
95 | if ((wild_dir = opendir(dirname)) != (DIR *)NULL) { |
||
96 | while ((file = readdir(wild_dir)) != (struct dirent *)NULL) { |
||
97 | Trace((stderr, "do_wild: readdir returns %s\n", file->d_name)); |
||
98 | if (file->d_name[0] == '.' && wildname[0] != '.') |
||
99 | continue; /* Unix: '*' and '?' do not match leading dot */ |
||
100 | /* Need something here for TOS filesystem? [cjh] */ |
||
101 | if (match(file->d_name, wildname, 0 WISEP) && /* 0=case sens.*/ |
||
102 | /* skip "." and ".." directory entries */ |
||
103 | strcmp(file->d_name, ".") && strcmp(file->d_name, "..")) { |
||
104 | Trace((stderr, "do_wild: match() succeeds\n")); |
||
105 | if (have_dirname) { |
||
106 | strcpy(matchname, dirname); |
||
107 | strcpy(matchname+dirnamelen, file->d_name); |
||
108 | } else |
||
109 | strcpy(matchname, file->d_name); |
||
110 | return matchname; |
||
111 | } |
||
112 | } |
||
113 | /* if we get to here directory is exhausted, so close it */ |
||
114 | closedir(wild_dir); |
||
115 | wild_dir = (DIR *)NULL; |
||
116 | } |
||
117 | |||
118 | /* return the raw wildspec in case that works (e.g., directory not |
||
119 | * searchable, but filespec was not wild and file is readable) */ |
||
120 | strncpy(matchname, wildspec, FILNAMSIZ); |
||
121 | matchname[FILNAMSIZ-1] = '\0'; |
||
122 | return matchname; |
||
123 | } |
||
124 | |||
125 | /* last time through, might have failed opendir but returned raw wildspec */ |
||
126 | if (wild_dir == (DIR *)NULL) { |
||
127 | notfirstcall = FALSE; /* nothing left to try--reset for new wildspec */ |
||
128 | if (have_dirname) |
||
129 | free(dirname); |
||
130 | return (char *)NULL; |
||
131 | } |
||
132 | |||
133 | /* If we've gotten this far, we've read and matched at least one entry |
||
134 | * successfully (in a previous call), so dirname has been copied into |
||
135 | * matchname already. |
||
136 | */ |
||
137 | while ((file = readdir(wild_dir)) != (struct dirent *)NULL) { |
||
138 | /* May need special TOS handling here. [cjh] */ |
||
139 | Trace((stderr, "do_wild: readdir returns %s\n", file->d_name)); |
||
140 | if (file->d_name[0] == '.' && wildname[0] != '.') |
||
141 | continue; /* Unix: '*' and '?' do not match leading dot */ |
||
142 | if (match(file->d_name, wildname, 0 WISEP)) { /* 0 = case sens. */ |
||
143 | Trace((stderr, "do_wild: match() succeeds\n")); |
||
144 | if (have_dirname) { |
||
145 | /* strcpy(matchname, dirname); */ |
||
146 | strcpy(matchname+dirnamelen, file->d_name); |
||
147 | } else |
||
148 | strcpy(matchname, file->d_name); |
||
149 | return matchname; |
||
150 | } |
||
151 | } |
||
152 | |||
153 | closedir(wild_dir); /* have read at least one entry; nothing left */ |
||
154 | wild_dir = (DIR *)NULL; |
||
155 | notfirstcall = FALSE; /* reset for new wildspec */ |
||
156 | if (have_dirname) |
||
157 | free(dirname); |
||
158 | return (char *)NULL; |
||
159 | |||
160 | } /* end function do_wild() */ |
||
161 | |||
162 | #endif /* !SFX */ |
||
163 | |||
164 | |||
165 | |||
166 | |||
167 | |||
168 | /**********************/ |
||
169 | /* Function mapattr() */ |
||
170 | /**********************/ |
||
171 | |||
172 | int mapattr(__G) |
||
173 | __GDEF |
||
174 | { |
||
175 | int r; |
||
176 | ulg tmp = G.crec.external_file_attributes; |
||
177 | |||
178 | G.pInfo->file_attr = 0; |
||
179 | /* initialized to 0 for check in "default" branch below... */ |
||
180 | |||
181 | switch (G.pInfo->hostnum) { |
||
182 | case AMIGA_: |
||
183 | tmp = (unsigned)(tmp>>17 & 7); /* Amiga RWE bits */ |
||
184 | G.pInfo->file_attr = (unsigned)(tmp<<6 | tmp<<3 | tmp); |
||
185 | break; |
||
186 | case THEOS_: |
||
187 | tmp &= 0xF1FFFFFFL; |
||
188 | if ((tmp & 0xF0000000L) != 0x40000000L) |
||
189 | tmp &= 0x01FFFFFFL; /* not a dir, mask all ftype bits */ |
||
190 | else |
||
191 | tmp &= 0x41FFFFFFL; /* leave directory bit as set */ |
||
192 | /* fall through! */ |
||
193 | case UNIX_: |
||
194 | case VMS_: |
||
195 | case ACORN_: |
||
196 | case ATARI_: |
||
197 | case ATHEOS_: |
||
198 | case BEOS_: |
||
199 | case QDOS_: |
||
200 | case TANDEM_: |
||
201 | r = FALSE; |
||
202 | G.pInfo->file_attr = (unsigned)(tmp >> 16); |
||
203 | if (G.pInfo->file_attr == 0 && G.extra_field) { |
||
204 | /* Some (non-Info-ZIP) implementations of Zip for Unix and |
||
205 | * VMS (and probably others ??) leave 0 in the upper 16-bit |
||
206 | * part of the external_file_attributes field. Instead, they |
||
207 | * store file permission attributes in some extra field. |
||
208 | * As a work-around, we search for the presence of one of |
||
209 | * these extra fields and fall back to the MSDOS compatible |
||
210 | * part of external_file_attributes if one of the known |
||
211 | * e.f. types has been detected. |
||
212 | * Later, we might implement extraction of the permission |
||
213 | * bits from the VMS extra field. But for now, the work-around |
||
214 | * should be sufficient to provide "readable" extracted files. |
||
215 | * (For ASI Unix e.f., an experimental remap of the e.f. |
||
216 | * mode value IS already provided!) |
||
217 | */ |
||
218 | ush ebID; |
||
219 | unsigned ebLen; |
||
220 | uch *ef = G.extra_field; |
||
221 | unsigned ef_len = G.crec.extra_field_length; |
||
222 | |||
223 | while (!r && ef_len >= EB_HEADSIZE) { |
||
224 | ebID = makeword(ef); |
||
225 | ebLen = (unsigned)makeword(ef+EB_LEN); |
||
226 | if (ebLen > (ef_len - EB_HEADSIZE)) |
||
227 | /* discoverd some e.f. inconsistency! */ |
||
228 | break; |
||
229 | switch (ebID) { |
||
230 | case EF_ASIUNIX: |
||
231 | if (ebLen >= (EB_ASI_MODE+2)) { |
||
232 | G.pInfo->file_attr = |
||
233 | (unsigned)makeword(ef+(EB_HEADSIZE+EB_ASI_MODE)); |
||
234 | /* force stop of loop: */ |
||
235 | ef_len = (ebLen + EB_HEADSIZE); |
||
236 | break; |
||
237 | } |
||
238 | /* else: fall through! */ |
||
239 | case EF_PKVMS: |
||
240 | /* "found nondecypherable e.f. with perm. attr" */ |
||
241 | r = TRUE; |
||
242 | default: |
||
243 | break; |
||
244 | } |
||
245 | ef_len -= (ebLen + EB_HEADSIZE); |
||
246 | ef += (ebLen + EB_HEADSIZE); |
||
247 | } |
||
248 | } |
||
249 | if (!r) { |
||
250 | #ifdef SYMLINKS |
||
251 | /* Check if the file is a (POSIX-compatible) symbolic link. |
||
252 | * We restrict symlink support to those "made-by" hosts that |
||
253 | * are known to support symbolic links. |
||
254 | */ |
||
255 | G.pInfo->symlink = S_ISLNK(G.pInfo->file_attr) && |
||
256 | SYMLINK_HOST(G.pInfo->hostnum); |
||
257 | #endif |
||
258 | return 0; |
||
259 | } |
||
260 | /* fall through! */ |
||
261 | /* all remaining cases: expand MSDOS read-only bit into write perms */ |
||
262 | case FS_FAT_: |
||
263 | /* PKWARE's PKZip for Unix marks entries as FS_FAT_, but stores the |
||
264 | * Unix attributes in the upper 16 bits of the external attributes |
||
265 | * field, just like Info-ZIP's Zip for Unix. We try to use that |
||
266 | * value, after a check for consistency with the MSDOS attribute |
||
267 | * bits (see below). |
||
268 | */ |
||
269 | G.pInfo->file_attr = (unsigned)(tmp >> 16); |
||
270 | /* fall through! */ |
||
271 | case FS_HPFS_: |
||
272 | case FS_NTFS_: |
||
273 | case MAC_: |
||
274 | case TOPS20_: |
||
275 | default: |
||
276 | /* Ensure that DOS subdir bit is set when the entry's name ends |
||
277 | * in a '/'. Some third-party Zip programs fail to set the subdir |
||
278 | * bit for directory entries. |
||
279 | */ |
||
280 | if ((tmp & 0x10) == 0) { |
||
281 | extent fnlen = strlen(G.filename); |
||
282 | if (fnlen > 0 && G.filename[fnlen-1] == '/') |
||
283 | tmp |= 0x10; |
||
284 | } |
||
285 | /* read-only bit --> write perms; subdir bit --> dir exec bit */ |
||
286 | tmp = !(tmp & 1) << 1 | (tmp & 0x10) >> 4; |
||
287 | if ((G.pInfo->file_attr & 0700) == (unsigned)(0400 | tmp<<6)) { |
||
288 | /* keep previous G.pInfo->file_attr setting, when its "owner" |
||
289 | * part appears to be consistent with DOS attribute flags! |
||
290 | */ |
||
291 | #ifdef SYMLINKS |
||
292 | /* Entries "made by FS_FAT_" could have been zipped on a |
||
293 | * system that supports POSIX-style symbolic links. |
||
294 | */ |
||
295 | G.pInfo->symlink = S_ISLNK(G.pInfo->file_attr) && |
||
296 | (G.pInfo->hostnum == FS_FAT_); |
||
297 | #endif |
||
298 | return 0; |
||
299 | } |
||
300 | G.pInfo->file_attr = (unsigned)(0444 | tmp<<6 | tmp<<3 | tmp); |
||
301 | break; |
||
302 | } /* end switch (host-OS-created-by) */ |
||
303 | |||
304 | /* for originating systems with no concept of "group," "other," "system": */ |
||
305 | umask( (int)(tmp=umask(0)) ); /* apply mask to expanded r/w(/x) perms */ |
||
306 | G.pInfo->file_attr &= ~tmp; |
||
307 | |||
308 | return 0; |
||
309 | |||
310 | } /* end function mapattr() */ |
||
311 | |||
312 | |||
313 | |||
314 | |||
315 | |||
316 | /************************/ |
||
317 | /* Function mapname() */ |
||
318 | /************************/ |
||
319 | |||
320 | int mapname(__G__ renamed) |
||
321 | __GDEF |
||
322 | int renamed; |
||
323 | /* |
||
324 | * returns: |
||
325 | * MPN_OK - no problem detected |
||
326 | * MPN_INF_TRUNC - caution (truncated filename) |
||
327 | * MPN_INF_SKIP - info "skip entry" (dir doesn't exist) |
||
328 | * MPN_ERR_SKIP - error -> skip entry |
||
329 | * MPN_ERR_TOOLONG - error -> path is too long |
||
330 | * MPN_NOMEM - error (memory allocation failed) -> skip entry |
||
331 | * [also MPN_VOL_LABEL, MPN_CREATED_DIR] |
||
332 | */ |
||
333 | { |
||
334 | char pathcomp[FILNAMSIZ]; /* path-component buffer */ |
||
335 | char *pp, *cp=(char *)NULL; /* character pointers */ |
||
336 | char *lastsemi=(char *)NULL; /* pointer to last semi-colon in pathcomp */ |
||
337 | #ifdef ACORN_FTYPE_NFS |
||
338 | char *lastcomma=(char *)NULL; /* pointer to last comma in pathcomp */ |
||
339 | RO_extra_block *ef_spark; /* pointer Acorn FTYPE ef block */ |
||
340 | #endif |
||
341 | int killed_ddot = FALSE; /* is set when skipping "../" pathcomp */ |
||
342 | int error = MPN_OK; |
||
343 | register unsigned workch; /* hold the character being tested */ |
||
344 | |||
345 | |||
346 | /*--------------------------------------------------------------------------- |
||
347 | Initialize various pointers and counters and stuff. |
||
348 | ---------------------------------------------------------------------------*/ |
||
349 | |||
350 | if (G.pInfo->vollabel) |
||
351 | return MPN_VOL_LABEL; /* can't set disk volume labels on Atari */ |
||
352 | |||
353 | /* can create path as long as not just freshening, or if user told us */ |
||
354 | G.create_dirs = (!uO.fflag || renamed); |
||
355 | |||
356 | created_dir = FALSE; /* not yet */ |
||
357 | |||
358 | /* user gave full pathname: don't prepend rootpath */ |
||
359 | renamed_fullpath = (renamed && (*G.filename == '/')); |
||
360 | |||
361 | if (checkdir(__G__ (char *)NULL, INIT) == MPN_NOMEM) |
||
362 | return MPN_NOMEM; /* initialize path buffer, unless no memory */ |
||
363 | |||
364 | *pathcomp = '\0'; /* initialize translation buffer */ |
||
365 | pp = pathcomp; /* point to translation buffer */ |
||
366 | if (uO.jflag) /* junking directories */ |
||
367 | cp = (char *)strrchr(G.filename, '/'); |
||
368 | if (cp == (char *)NULL) /* no '/' or not junking dirs */ |
||
369 | cp = G.filename; /* point to internal zipfile-member pathname */ |
||
370 | else |
||
371 | ++cp; /* point to start of last component of path */ |
||
372 | |||
373 | /*--------------------------------------------------------------------------- |
||
374 | Begin main loop through characters in filename. |
||
375 | ---------------------------------------------------------------------------*/ |
||
376 | |||
377 | while ((workch = (uch)*cp++) != 0) { |
||
378 | |||
379 | switch (workch) { |
||
380 | case '/': /* can assume -j flag not given */ |
||
381 | *pp = '\0'; |
||
382 | if (strcmp(pathcomp, ".") == 0) { |
||
383 | /* don't bother appending "./" to the path */ |
||
384 | *pathcomp = '\0'; |
||
385 | } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) { |
||
386 | /* "../" dir traversal detected, skip over it */ |
||
387 | *pathcomp = '\0'; |
||
388 | killed_ddot = TRUE; /* set "show message" flag */ |
||
389 | } |
||
390 | /* when path component is not empty, append it now */ |
||
391 | if (*pathcomp != '\0' && |
||
392 | ((error = checkdir(__G__ pathcomp, APPEND_DIR)) |
||
393 | & MPN_MASK) > MPN_INF_TRUNC) |
||
394 | return error; |
||
395 | pp = pathcomp; /* reset conversion buffer for next piece */ |
||
396 | lastsemi = (char *)NULL; /* leave direct. semi-colons alone */ |
||
397 | break; |
||
398 | |||
399 | case ';': /* VMS version (or DEC-20 attrib?) */ |
||
400 | lastsemi = pp; |
||
401 | *pp++ = ';'; /* keep for now; remove VMS ";##" */ |
||
402 | break; /* later, if requested */ |
||
403 | |||
404 | #ifdef ACORN_FTYPE_NFS |
||
405 | case ',': /* NFS filetype extension */ |
||
406 | lastcomma = pp; |
||
407 | *pp++ = ','; /* keep for now; may need to remove */ |
||
408 | break; /* later, if requested */ |
||
409 | #endif |
||
410 | |||
411 | #ifdef MTS |
||
412 | case ' ': /* change spaces to underscore under */ |
||
413 | *pp++ = '_'; /* MTS; leave as spaces under Unix */ |
||
414 | break; |
||
415 | #endif |
||
416 | |||
417 | default: |
||
418 | /* allow European characters in filenames: */ |
||
419 | if (isprint(workch) || (128 <= workch && workch <= 254)) |
||
420 | *pp++ = (char)workch; |
||
421 | } /* end switch */ |
||
422 | |||
423 | } /* end while loop */ |
||
424 | |||
425 | /* Show warning when stripping insecure "parent dir" path components */ |
||
426 | if (killed_ddot && QCOND2) { |
||
427 | Info(slide, 0, ((char *)slide, |
||
428 | "warning: skipped \"../\" path component(s) in %s\n", |
||
429 | FnFilter1(G.filename))); |
||
430 | if (!(error & ~MPN_MASK)) |
||
431 | error = (error & MPN_MASK) | PK_WARN; |
||
432 | } |
||
433 | |||
434 | /*--------------------------------------------------------------------------- |
||
435 | Report if directory was created (and no file to create: filename ended |
||
436 | in '/'), check name to be sure it exists, and combine path and name be- |
||
437 | fore exiting. |
||
438 | ---------------------------------------------------------------------------*/ |
||
439 | |||
440 | if (G.filename[strlen(G.filename) - 1] == '/') { |
||
441 | checkdir(__G__ G.filename, GETPATH); |
||
442 | if (created_dir) { |
||
443 | if (QCOND2) { |
||
444 | Info(slide, 0, ((char *)slide, " creating: %s\n", |
||
445 | FnFilter1(G.filename))); |
||
446 | } |
||
447 | /* set dir time (note trailing '/') */ |
||
448 | return (error & ~MPN_MASK) | MPN_CREATED_DIR; |
||
449 | } |
||
450 | /* dir existed already; don't look for data to extract */ |
||
451 | return (error & ~MPN_MASK) | MPN_INF_SKIP; |
||
452 | } |
||
453 | |||
454 | *pp = '\0'; /* done with pathcomp: terminate it */ |
||
455 | |||
456 | /* if not saving them, remove VMS version numbers (appended ";###") */ |
||
457 | if (!uO.V_flag && lastsemi) { |
||
458 | pp = lastsemi + 1; |
||
459 | while (isdigit((uch)(*pp))) |
||
460 | ++pp; |
||
461 | if (*pp == '\0') /* only digits between ';' and end: nuke */ |
||
462 | *lastsemi = '\0'; |
||
463 | } |
||
464 | |||
465 | /* On UNIX (and compatible systems), "." and ".." are reserved for |
||
466 | * directory navigation and cannot be used as regular file names. |
||
467 | * These reserved one-dot and two-dot names are mapped to "_" and "__". |
||
468 | */ |
||
469 | if (strcmp(pathcomp, ".") == 0) |
||
470 | *pathcomp = '_'; |
||
471 | else if (strcmp(pathcomp, "..") == 0) |
||
472 | strcpy(pathcomp, "__"); |
||
473 | |||
474 | #ifdef ACORN_FTYPE_NFS |
||
475 | /* translate Acorn filetype information if asked to do so */ |
||
476 | if (uO.acorn_nfs_ext && |
||
477 | (ef_spark = (RO_extra_block *) |
||
478 | getRISCOSexfield(G.extra_field, G.lrec.extra_field_length)) |
||
479 | != (RO_extra_block *)NULL) |
||
480 | { |
||
481 | /* file *must* have a RISC OS extra field */ |
||
482 | long ft = (long)makelong(ef_spark->loadaddr); |
||
483 | /*32-bit*/ |
||
484 | if (lastcomma) { |
||
485 | pp = lastcomma + 1; |
||
486 | while (isxdigit((uch)(*pp))) ++pp; |
||
487 | if (pp == lastcomma+4 && *pp == '\0') *lastcomma='\0'; /* nuke */ |
||
488 | } |
||
489 | if ((ft & 1<<31)==0) ft=0x000FFD00; |
||
490 | sprintf(pathcomp+strlen(pathcomp), ",%03x", (int)(ft>>8) & 0xFFF); |
||
491 | } |
||
492 | #endif /* ACORN_FTYPE_NFS */ |
||
493 | |||
494 | if (*pathcomp == '\0') { |
||
495 | Info(slide, 1, ((char *)slide, "mapname: conversion of %s failed\n", |
||
496 | FnFilter1(G.filename))); |
||
497 | return (error & ~MPN_MASK) | MPN_ERR_SKIP; |
||
498 | } |
||
499 | |||
500 | checkdir(__G__ pathcomp, APPEND_NAME); /* returns 1 if truncated: care? */ |
||
501 | checkdir(__G__ G.filename, GETPATH); |
||
502 | |||
503 | return error; |
||
504 | |||
505 | } /* end function mapname() */ |
||
506 | |||
507 | |||
508 | |||
509 | |||
510 | #if 0 /*========== NOTES ==========*/ |
||
511 | |||
512 | extract-to dir: a:path/ |
||
513 | buildpath: path1/path2/ ... (NULL-terminated) |
||
514 | pathcomp: filename |
||
515 | |||
516 | mapname(): |
||
517 | loop over chars in zipfile member name |
||
518 | checkdir(path component, COMPONENT | CREATEDIR) --> map as required? |
||
519 | (d:/tmp/unzip/) (disk:[tmp.unzip.) |
||
520 | (d:/tmp/unzip/jj/) (disk:[tmp.unzip.jj.) |
||
521 | (d:/tmp/unzip/jj/temp/) (disk:[tmp.unzip.jj.temp.) |
||
522 | finally add filename itself and check for existence? (could use with rename) |
||
523 | (d:/tmp/unzip/jj/temp/msg.outdir) (disk:[tmp.unzip.jj.temp]msg.outdir) |
||
524 | checkdir(name, GETPATH) --> copy path to name and free space |
||
525 | |||
526 | #endif /* 0 */ |
||
527 | |||
528 | |||
529 | |||
530 | |||
531 | /***********************/ |
||
532 | /* Function checkdir() */ |
||
533 | /***********************/ |
||
534 | |||
535 | int checkdir(__G__ pathcomp, flag) |
||
536 | __GDEF |
||
537 | char *pathcomp; |
||
538 | int flag; |
||
539 | /* |
||
540 | * returns: |
||
541 | * MPN_OK - no problem detected |
||
542 | * MPN_INF_TRUNC - (on APPEND_NAME) truncated filename |
||
543 | * MPN_INF_SKIP - path doesn't exist, not allowed to create |
||
544 | * MPN_ERR_SKIP - path doesn't exist, tried to create and failed; or path |
||
545 | * exists and is not a directory, but is supposed to be |
||
546 | * MPN_ERR_TOOLONG - path is too long |
||
547 | * MPN_NOMEM - can't allocate memory for filename buffers |
||
548 | */ |
||
549 | { |
||
550 | static int rootlen = 0; /* length of rootpath */ |
||
551 | static char *rootpath; /* user's "extract-to" directory */ |
||
552 | static char *buildpath; /* full path (so far) to extracted file */ |
||
553 | static char *end; /* pointer to end of buildpath ('\0') */ |
||
554 | |||
555 | # define FN_MASK 7 |
||
556 | # define FUNCTION (flag & FN_MASK) |
||
557 | |||
558 | |||
559 | /*--------------------------------------------------------------------------- |
||
560 | APPEND_DIR: append the path component to the path being built and check |
||
561 | for its existence. If doesn't exist and we are creating directories, do |
||
562 | so for this one; else signal success or error as appropriate. |
||
563 | ---------------------------------------------------------------------------*/ |
||
564 | |||
565 | if (FUNCTION == APPEND_DIR) { |
||
566 | int too_long = FALSE; |
||
567 | /* SHORT_NAMES required for TOS, but it has to co-exist for minix fs... [cjh] */ |
||
568 | #ifdef SHORT_NAMES |
||
569 | char *old_end = end; |
||
570 | #endif |
||
571 | |||
572 | Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp))); |
||
573 | while ((*end = *pathcomp++) != '\0') |
||
574 | ++end; |
||
575 | /* SHORT_NAMES required for TOS, but it has to co-exist for minix fs... [cjh] */ |
||
576 | #ifdef SHORT_NAMES /* path components restricted to 14 chars, typically */ |
||
577 | if ((end-old_end) > FILENAME_MAX) /* GRR: proper constant? */ |
||
578 | *(end = old_end + FILENAME_MAX) = '\0'; |
||
579 | #endif |
||
580 | |||
581 | /* GRR: could do better check, see if overrunning buffer as we go: |
||
582 | * check end-buildpath after each append, set warning variable if |
||
583 | * within 20 of FILNAMSIZ; then if var set, do careful check when |
||
584 | * appending. Clear variable when begin new path. */ |
||
585 | |||
586 | if ((end-buildpath) > FILNAMSIZ-3) /* need '/', one-char name, '\0' */ |
||
587 | too_long = TRUE; /* check if extracting directory? */ |
||
588 | if (stat(buildpath, &G.statbuf)) { /* path doesn't exist */ |
||
589 | if (!G.create_dirs) { /* told not to create (freshening) */ |
||
590 | free(buildpath); |
||
591 | return MPN_INF_SKIP; /* path doesn't exist: nothing to do */ |
||
592 | } |
||
593 | if (too_long) { |
||
594 | Info(slide, 1, ((char *)slide, |
||
595 | "checkdir error: path too long: %s\n", |
||
596 | FnFilter1(buildpath))); |
||
597 | free(buildpath); |
||
598 | /* no room for filenames: fatal */ |
||
599 | return MPN_ERR_TOOLONG; |
||
600 | } |
||
601 | if (mkdir(buildpath, 0777) == -1) { /* create the directory */ |
||
602 | Info(slide, 1, ((char *)slide, |
||
603 | "checkdir error: cannot create %s\n\ |
||
604 | unable to process %s.\n", |
||
605 | FnFilter2(buildpath), FnFilter1(G.filename))); |
||
606 | free(buildpath); |
||
607 | /* path didn't exist, tried to create, failed */ |
||
608 | return MPN_ERR_SKIP; |
||
609 | } |
||
610 | created_dir = TRUE; |
||
611 | } else if (!S_ISDIR(G.statbuf.st_mode)) { |
||
612 | Info(slide, 1, ((char *)slide, |
||
613 | "checkdir error: %s exists but is not directory\n\ |
||
614 | unable to process %s.\n", |
||
615 | FnFilter2(buildpath), FnFilter(G.filename))); |
||
616 | free(buildpath); |
||
617 | /* path existed but wasn't dir */ |
||
618 | return MPN_ERR_SKIP; |
||
619 | } |
||
620 | if (too_long) { |
||
621 | Info(slide, 1, ((char *)slide, |
||
622 | "checkdir error: path too long: %s\n", FnFilter1(buildpath))); |
||
623 | free(buildpath); |
||
624 | /* no room for filenames: fatal */ |
||
625 | return MPN_ERR_TOOLONG; |
||
626 | } |
||
627 | *end++ = '/'; |
||
628 | *end = '\0'; |
||
629 | Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath))); |
||
630 | return MPN_OK; |
||
631 | |||
632 | } /* end if (FUNCTION == APPEND_DIR) */ |
||
633 | |||
634 | /*--------------------------------------------------------------------------- |
||
635 | GETPATH: copy full path to the string pointed at by pathcomp, and free |
||
636 | buildpath. |
||
637 | ---------------------------------------------------------------------------*/ |
||
638 | |||
639 | if (FUNCTION == GETPATH) { |
||
640 | strcpy(pathcomp, buildpath); |
||
641 | Trace((stderr, "getting and freeing path [%s]\n", |
||
642 | FnFilter1(pathcomp))); |
||
643 | free(buildpath); |
||
644 | buildpath = end = (char *)NULL; |
||
645 | return MPN_OK; |
||
646 | } |
||
647 | |||
648 | /*--------------------------------------------------------------------------- |
||
649 | APPEND_NAME: assume the path component is the filename; append it and |
||
650 | return without checking for existence. |
||
651 | ---------------------------------------------------------------------------*/ |
||
652 | |||
653 | if (FUNCTION == APPEND_NAME) { |
||
654 | /* SHORT_NAMES required for TOS, but it has to co-exist for minix fs... [cjh] */ |
||
655 | #ifdef SHORT_NAMES |
||
656 | char *old_end = end; |
||
657 | #endif |
||
658 | |||
659 | Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp))); |
||
660 | while ((*end = *pathcomp++) != '\0') { |
||
661 | ++end; |
||
662 | /* SHORT_NAMES required for TOS, but it has to co-exist for minix fs... [cjh] */ |
||
663 | #ifdef SHORT_NAMES /* truncate name at 14 characters, typically */ |
||
664 | if ((end-old_end) > FILENAME_MAX) /* GRR: proper constant? */ |
||
665 | *(end = old_end + FILENAME_MAX) = '\0'; |
||
666 | #endif |
||
667 | if ((end-buildpath) >= FILNAMSIZ) { |
||
668 | *--end = '\0'; |
||
669 | Info(slide, 0x201, ((char *)slide, |
||
670 | "checkdir warning: path too long; truncating\n\ |
||
671 | %s\n -> %s\n", |
||
672 | FnFilter1(G.filename), FnFilter2(buildpath))); |
||
673 | return MPN_INF_TRUNC; /* filename truncated */ |
||
674 | } |
||
675 | } |
||
676 | Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath))); |
||
677 | /* could check for existence here, prompt for new name... */ |
||
678 | return MPN_OK; |
||
679 | } |
||
680 | |||
681 | /*--------------------------------------------------------------------------- |
||
682 | INIT: allocate and initialize buffer space for the file currently being |
||
683 | extracted. If file was renamed with an absolute path, don't prepend the |
||
684 | extract-to path. |
||
685 | ---------------------------------------------------------------------------*/ |
||
686 | |||
687 | /* GRR: for VMS and TOPS-20, add up to 13 to strlen */ |
||
688 | |||
689 | if (FUNCTION == INIT) { |
||
690 | Trace((stderr, "initializing buildpath to ")); |
||
691 | #ifdef ACORN_FTYPE_NFS |
||
692 | if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+ |
||
693 | (uO.acorn_nfs_ext ? 5 : 1))) |
||
694 | #else |
||
695 | if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+1)) |
||
696 | #endif |
||
697 | == (char *)NULL) |
||
698 | return MPN_NOMEM; |
||
699 | if ((rootlen > 0) && !renamed_fullpath) { |
||
700 | strcpy(buildpath, rootpath); |
||
701 | end = buildpath + rootlen; |
||
702 | } else { |
||
703 | *buildpath = '\0'; |
||
704 | end = buildpath; |
||
705 | } |
||
706 | Trace((stderr, "[%s]\n", FnFilter1(buildpath))); |
||
707 | return MPN_OK; |
||
708 | } |
||
709 | |||
710 | /*--------------------------------------------------------------------------- |
||
711 | ROOT: if appropriate, store the path in rootpath and create it if |
||
712 | necessary; else assume it's a zipfile member and return. This path |
||
713 | segment gets used in extracting all members from every zipfile specified |
||
714 | on the command line. |
||
715 | ---------------------------------------------------------------------------*/ |
||
716 | |||
717 | #if (!defined(SFX) || defined(SFX_EXDIR)) |
||
718 | if (FUNCTION == ROOT) { |
||
719 | Trace((stderr, "initializing root path to [%s]\n", pathcomp)); |
||
720 | if (pathcomp == (char *)NULL) { |
||
721 | rootlen = 0; |
||
722 | return MPN_OK; |
||
723 | } |
||
724 | if (rootlen > 0) /* rootpath was already set, nothing to do */ |
||
725 | return MPN_OK; |
||
726 | if ((rootlen = strlen(pathcomp)) > 0) { |
||
727 | char *tmproot; |
||
728 | |||
729 | if ((tmproot = (char *)malloc(rootlen+2)) == (char *)NULL) { |
||
730 | rootlen = 0; |
||
731 | return MPN_NOMEM; |
||
732 | } |
||
733 | strcpy(tmproot, pathcomp); |
||
734 | if (tmproot[rootlen-1] == '/') { |
||
735 | tmproot[--rootlen] = '\0'; |
||
736 | } |
||
737 | if (rootlen > 0 && (stat(tmproot, &G.statbuf) || |
||
738 | !S_ISDIR(G.statbuf.st_mode))) |
||
739 | { /* path does not exist */ |
||
740 | if (!G.create_dirs /* || iswild(tmproot) */ ) { |
||
741 | free(tmproot); |
||
742 | rootlen = 0; |
||
743 | /* skip (or treat as stored file) */ |
||
744 | return MPN_INF_SKIP; |
||
745 | } |
||
746 | /* create the directory (could add loop here scanning tmproot |
||
747 | * to create more than one level, but why really necessary?) */ |
||
748 | if (mkdir(tmproot, 0777) == -1) { |
||
749 | Info(slide, 1, ((char *)slide, |
||
750 | "checkdir: cannot create extraction directory: %s\n", |
||
751 | FnFilter1(tmproot))); |
||
752 | free(tmproot); |
||
753 | rootlen = 0; |
||
754 | /* path didn't exist, tried to create, and failed: */ |
||
755 | /* file exists, or 2+ subdir levels required */ |
||
756 | return MPN_ERR_SKIP; |
||
757 | } |
||
758 | } |
||
759 | tmproot[rootlen++] = '/'; |
||
760 | tmproot[rootlen] = '\0'; |
||
761 | if ((rootpath = (char *)realloc(tmproot, rootlen+1)) == NULL) { |
||
762 | free(tmproot); |
||
763 | rootlen = 0; |
||
764 | return MPN_NOMEM; |
||
765 | } |
||
766 | Trace((stderr, "rootpath now = [%s]\n", FnFilter1(rootpath))); |
||
767 | } |
||
768 | return MPN_OK; |
||
769 | } |
||
770 | #endif /* !SFX || SFX_EXDIR */ |
||
771 | |||
772 | /*--------------------------------------------------------------------------- |
||
773 | END: free rootpath, immediately prior to program exit. |
||
774 | ---------------------------------------------------------------------------*/ |
||
775 | |||
776 | if (FUNCTION == END) { |
||
777 | Trace((stderr, "freeing rootpath\n")); |
||
778 | if (rootlen > 0) { |
||
779 | free(rootpath); |
||
780 | rootlen = 0; |
||
781 | } |
||
782 | return MPN_OK; |
||
783 | } |
||
784 | |||
785 | return MPN_INVALID; /* should never reach */ |
||
786 | |||
787 | } /* end function checkdir() */ |
||
788 | |||
789 | |||
790 | |||
791 | |||
792 | |||
793 | /****************************/ |
||
794 | /* Function close_outfile() */ |
||
795 | /****************************/ |
||
796 | |||
797 | void close_outfile(__G) /* GRR: change to return PK-style warning level */ |
||
798 | __GDEF |
||
799 | { |
||
800 | #ifdef USE_EF_UT_TIME |
||
801 | unsigned eb_izux_flg; |
||
802 | iztimes zt; |
||
803 | #endif |
||
804 | ztimbuf tp; |
||
805 | |||
806 | /*--------------------------------------------------------------------------- |
||
807 | If symbolic links are supported, allocate storage for a symlink control |
||
808 | structure, put the uncompressed "data" and other required info in it, and |
||
809 | add the structure to the "deferred symlinks" chain. Since we know it's a |
||
810 | symbolic link to start with, we shouldn't have to worry about overflowing |
||
811 | unsigned ints with unsigned longs. |
||
812 | ---------------------------------------------------------------------------*/ |
||
813 | |||
814 | /* symlinks allowed on minix filesystems [cjh] |
||
815 | * Hopefully this will work properly... We won't bother to try if |
||
816 | * MiNT isn't present; the symlink should fail if we're on a TOS |
||
817 | * filesystem. |
||
818 | * BUG: should we copy the original file to the "symlink" if the |
||
819 | * link fails? |
||
820 | */ |
||
821 | if (G.symlnk) { |
||
822 | extent ucsize = (extent)G.lrec.ucsize; |
||
823 | /* size of the symlink entry is the sum of |
||
824 | * (struct size (includes 1st '\0') + 1 additional trailing '\0'), |
||
825 | * system specific attribute data size (might be 0), |
||
826 | * and the lengths of name and link target. |
||
827 | */ |
||
828 | extent slnk_entrysize = (sizeof(slinkentry) + 1) + |
||
829 | ucsize + strlen(G.filename); |
||
830 | slinkentry *slnk_entry; |
||
831 | |||
832 | if (slnk_entrysize < ucsize) { |
||
833 | Info(slide, 0x201, ((char *)slide, |
||
834 | "warning: symbolic link (%s) failed: mem alloc overflow\n", |
||
835 | FnFilter1(G.filename))); |
||
836 | fclose(G.outfile); |
||
837 | return; |
||
838 | } |
||
839 | |||
840 | if ((slnk_entry = (slinkentry *)malloc(slnk_entrysize)) == NULL) { |
||
841 | Info(slide, 0x201, ((char *)slide, |
||
842 | "warning: symbolic link (%s) failed: no mem\n", |
||
843 | FnFilter1(G.filename))); |
||
844 | fclose(G.outfile); |
||
845 | return; |
||
846 | } |
||
847 | slnk_entry->next = NULL; |
||
848 | slnk_entry->targetlen = ucsize; |
||
849 | slnk_entry->attriblen = 0; /* don't set attributes for symlinks */ |
||
850 | slnk_entry->target = slnk_entry->buf; |
||
851 | slnk_entry->fname = slnk_entry->target + ucsize + 1; |
||
852 | strcpy(slnk_entry->fname, G.filename); |
||
853 | |||
854 | /* move back to the start of the file to re-read the "link data" */ |
||
855 | rewind(G.outfile); |
||
856 | |||
857 | if (fread(slnk_entry->target, 1, ucsize, G.outfile) != ucsize) |
||
858 | { |
||
859 | Info(slide, 0x201, ((char *)slide, |
||
860 | "warning: symbolic link (%s) failed\n", |
||
861 | FnFilter1(G.filename))); |
||
862 | free(slnk_entry); |
||
863 | fclose(G.outfile); |
||
864 | return; |
||
865 | } |
||
866 | fclose(G.outfile); /* close "link" file for good... */ |
||
867 | slnk_entry->target[ucsize] = '\0'; |
||
868 | if (QCOND2) |
||
869 | Info(slide, 0, ((char *)slide, "-> %s ", |
||
870 | FnFilter1(slnk_entry->target))); |
||
871 | /* add this symlink record to the list of deferred symlinks */ |
||
872 | if (G.slink_last != NULL) |
||
873 | G.slink_last->next = slnk_entry; |
||
874 | else |
||
875 | G.slink_head = slnk_entry; |
||
876 | G.slink_last = slnk_entry; |
||
877 | return; |
||
878 | } |
||
879 | |||
880 | fclose(G.outfile); |
||
881 | |||
882 | /*--------------------------------------------------------------------------- |
||
883 | Convert from MSDOS-format local time and date to Unix-format 32-bit GMT |
||
884 | time: adjust base year from 1980 to 1970, do usual conversions from |
||
885 | yy/mm/dd hh:mm:ss to elapsed seconds, and account for timezone and day- |
||
886 | light savings time differences. |
||
887 | ---------------------------------------------------------------------------*/ |
||
888 | |||
889 | /* skip restoring time stamps on user's request */ |
||
890 | if (uO.D_flag <= 1) { |
||
891 | #ifdef USE_EF_UT_TIME |
||
892 | eb_izux_flg = (G.extra_field |
||
893 | #ifdef IZ_CHECK_TZ |
||
894 | && G.tz_is_valid |
||
895 | #endif |
||
896 | ? ef_scan_for_izux(G.extra_field, |
||
897 | G.lrec.extra_field_length, 0, |
||
898 | G.lrec.last_mod_dos_datetime, &zt, NULL) |
||
899 | : 0); |
||
900 | if (eb_izux_flg & EB_UT_FL_MTIME) { |
||
901 | tp.modtime = zt.mtime; |
||
902 | TTrace((stderr, |
||
903 | "\nclose_outfile: Unix e.f. modif. time = %ld\n", |
||
904 | tp.modtime)); |
||
905 | } else { |
||
906 | tp.modtime = dos_to_unix_time(G.lrec.last_mod_dos_datetime); |
||
907 | } |
||
908 | if (eb_izux_flg & EB_UT_FL_ATIME) { |
||
909 | tp.actime = zt.atime; |
||
910 | TTrace((stderr, |
||
911 | "close_outfile: Unix e.f. access time = %ld\n", |
||
912 | tp.actime)); |
||
913 | } else { |
||
914 | tp.actime = tp.modtime; |
||
915 | TTrace((stderr, |
||
916 | "\nclose_outfile: modification/access times = %ld\n", |
||
917 | tp.modtime)); |
||
918 | } |
||
919 | #else /* !USE_EF_UT_TIME */ |
||
920 | tp.actime = tp.modtime |
||
921 | = dos_to_unix_time(G.lrec.last_mod_dos_datetime); |
||
922 | |||
923 | TTrace((stderr, "\nclose_outfile: modification/access times = %ld\n", |
||
924 | tp.modtime)); |
||
925 | #endif /* ?USE_EF_UT_TIME */ |
||
926 | |||
927 | /* set the file's access and modification times */ |
||
928 | if (utime(G.filename, &tp)) |
||
929 | Info(slide, 0x201, ((char *)slide, |
||
930 | "warning: cannot set the time for %s\n", |
||
931 | FnFilter1(G.filename))); |
||
932 | } |
||
933 | |||
934 | /*--------------------------------------------------------------------------- |
||
935 | Change the file permissions from default ones to those stored in the |
||
936 | zipfile. |
||
937 | ---------------------------------------------------------------------------*/ |
||
938 | |||
939 | #ifndef NO_CHMOD |
||
940 | if (chmod(G.filename, 0xffff & G.pInfo->file_attr)) |
||
941 | perror("chmod (file attributes) error"); |
||
942 | #endif |
||
943 | |||
944 | } /* end function close_outfile() */ |
||
945 | |||
946 | |||
947 | |||
948 | |||
949 | #ifdef TIMESTAMP |
||
950 | |||
951 | /***************************/ |
||
952 | /* Function stamp_file() */ |
||
953 | /***************************/ |
||
954 | |||
955 | int stamp_file(fname, modtime) |
||
956 | ZCONST char *fname; |
||
957 | time_t modtime; |
||
958 | { |
||
959 | ztimbuf tp; |
||
960 | |||
961 | tp.modtime = tp.actime = modtime; |
||
962 | return (utime(fname, &tp)); |
||
963 | |||
964 | } /* end function stamp_file() */ |
||
965 | |||
966 | #endif /* TIMESTAMP */ |
||
967 | |||
968 | |||
969 | |||
970 | |||
971 | #ifndef SFX |
||
972 | |||
973 | /************************/ |
||
974 | /* Function version() */ |
||
975 | /************************/ |
||
976 | |||
977 | void version(__G) |
||
978 | __GDEF |
||
979 | { |
||
980 | #ifdef __TURBOC__ |
||
981 | char buf[40]; |
||
982 | #endif |
||
983 | |||
984 | sprintf((char *)slide, LoadFarString(CompiledWith), |
||
985 | |||
986 | #ifdef __GNUC__ |
||
987 | "gcc ", __VERSION__, |
||
988 | #else |
||
989 | # if 0 |
||
990 | "cc ", (sprintf(buf, " version %d", _RELEASE), buf), |
||
991 | # else |
||
992 | # ifdef __TURBOC__ |
||
993 | "Turbo C", (sprintf(buf, " (0x%04x = %d)", __TURBOC__, __TURBOC__), buf), |
||
994 | # else |
||
995 | "unknown compiler", "", |
||
996 | # endif |
||
997 | # endif |
||
998 | #endif |
||
999 | |||
1000 | #ifdef __MINT__ |
||
1001 | "Atari TOS/MiNT", |
||
1002 | #else |
||
1003 | "Atari TOS", |
||
1004 | #endif |
||
1005 | |||
1006 | " (Atari ST/TT/Falcon030)", |
||
1007 | |||
1008 | #ifdef __DATE__ |
||
1009 | " on ", __DATE__ |
||
1010 | #else |
||
1011 | "", "" |
||
1012 | #endif |
||
1013 | ); |
||
1014 | |||
1015 | (*G.message)((zvoid *)&G, slide, (ulg)strlen((char *)slide), 0); |
||
1016 | |||
1017 | } /* end function version() */ |
||
1018 | |||
1019 | #endif /* !SFX */=>>31)==0)><31)==0)>=>=>3><3>6><6>6))><6))>><>3><3>6><6>--> |