Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
6725 | siemargl | 1 | /* |
2 | Copyright (c) 1990-2007 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 | flexos.c |
||
12 | |||
13 | FlexOS-specific routines for use with Info-ZIP's UnZip 5.2 and later. |
||
14 | |||
15 | Based upon the MSDOS version of this file (msdos/msdos.c) |
||
16 | |||
17 | Contains: do_wild() |
||
18 | mapattr() |
||
19 | mapname() |
||
20 | map2fat() |
||
21 | checkdir() |
||
22 | close_outfile() |
||
23 | dateformat() |
||
24 | version() |
||
25 | _wildarg() |
||
26 | |||
27 | ---------------------------------------------------------------------------*/ |
||
28 | |||
29 | |||
30 | |||
31 | #define UNZIP_INTERNAL |
||
32 | #include "unzip.h" |
||
33 | |||
34 | #include |
||
35 | |||
36 | /* The following should really be a static declaration, but the compiler |
||
37 | complains (crappy compiler can't cope with a static forward declaration). |
||
38 | */ |
||
39 | extern void map2fat OF((char *pathcomp, char *last_dot)); |
||
40 | |||
41 | static int created_dir; /* used by mapname(), checkdir() */ |
||
42 | static int renamed_fullpath; /* ditto */ |
||
43 | |||
44 | /*****************************/ |
||
45 | /* Strings used in flexos.c */ |
||
46 | /*****************************/ |
||
47 | |||
48 | #ifndef SFX |
||
49 | static ZCONST char Far CantAllocateWildcard[] = |
||
50 | "warning: cannot allocate wildcard buffers\n"; |
||
51 | #endif |
||
52 | static ZCONST char Far WarnDirTraversSkip[] = |
||
53 | "warning: skipped \"../\" path component(s) in %s\n"; |
||
54 | static ZCONST char Far Creating[] = " creating: %s\n"; |
||
55 | static ZCONST char Far ConversionFailed[] = |
||
56 | "mapname: conversion of %s failed\n"; |
||
57 | static ZCONST char Far PathTooLong[] = "checkdir error: path too long: %s\n"; |
||
58 | static ZCONST char Far CantCreateDir[] = "checkdir error: cannot create %s\n\ |
||
59 | unable to process %s.\n"; |
||
60 | static ZCONST char Far DirIsntDirectory[] = |
||
61 | "checkdir error: %s exists but is not directory\n\ |
||
62 | unable to process %s.\n"; |
||
63 | static ZCONST char Far PathTooLongTrunc[] = |
||
64 | "checkdir warning: path too long; truncating\n %s\n\ |
||
65 | -> %s\n"; |
||
66 | #if (!defined(SFX) || defined(SFX_EXDIR)) |
||
67 | static ZCONST char Far CantCreateExtractDir[] = |
||
68 | "checkdir: cannot create extraction directory: %s\n"; |
||
69 | #endif |
||
70 | |||
71 | #include |
||
72 | |||
73 | #ifndef SFX |
||
74 | |||
75 | /************************/ |
||
76 | /* Function do_wild() */ /* identical to OS/2 version */ |
||
77 | /************************/ |
||
78 | |||
79 | char *do_wild(__G__ wildspec) |
||
80 | __GDEF |
||
81 | ZCONST char *wildspec; /* only used first time on a given dir */ |
||
82 | { |
||
83 | static DIR *wild_dir = (DIR *)NULL; |
||
84 | static ZCONST char *wildname; |
||
85 | static char *dirname, matchname[FILNAMSIZ]; |
||
86 | static int notfirstcall=FALSE, have_dirname, dirnamelen; |
||
87 | char *fnamestart; |
||
88 | struct dirent *file; |
||
89 | |||
90 | /* Even when we're just returning wildspec, we *always* do so in |
||
91 | * matchname[]--calling routine is allowed to append four characters |
||
92 | * to the returned string, and wildspec may be a pointer to argv[]. |
||
93 | */ |
||
94 | if (!notfirstcall) { /* first call: must initialize everything */ |
||
95 | notfirstcall = TRUE; |
||
96 | |||
97 | if (!iswild(wildspec)) { |
||
98 | strncpy(matchname, wildspec, FILNAMSIZ); |
||
99 | matchname[FILNAMSIZ-1] = '\0'; |
||
100 | have_dirname = FALSE; |
||
101 | dir = NULL; |
||
102 | return matchname; |
||
103 | } |
||
104 | |||
105 | /* break the wildspec into a directory part and a wildcard filename */ |
||
106 | if ((wildname = strrchr(wildspec, '/')) == (ZCONST char *)NULL && |
||
107 | (wildname = strrchr(wildspec, ':')) == (ZCONST char *)NULL) { |
||
108 | dirname = "."; |
||
109 | dirnamelen = 1; |
||
110 | have_dirname = FALSE; |
||
111 | wildname = wildspec; |
||
112 | } else { |
||
113 | ++wildname; /* point at character after '/' or ':' */ |
||
114 | dirnamelen = (int)(wildname - wildspec); |
||
115 | if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) { |
||
116 | Info(slide, 1, ((char *)slide, |
||
117 | LoadFarString(CantAllocateWildcard))); |
||
118 | strncpy(matchname, wildspec, FILNAMSIZ); |
||
119 | matchname[FILNAMSIZ-1] = '\0'; |
||
120 | return matchname; /* but maybe filespec was not a wildcard */ |
||
121 | } |
||
122 | /* GRR: cannot strip trailing char for opendir since might be "d:/" or "d:" |
||
123 | * (would have to check for "./" at end--let opendir handle it instead) */ |
||
124 | strncpy(dirname, wildspec, dirnamelen); |
||
125 | dirname[dirnamelen] = '\0'; /* terminate for strcpy below */ |
||
126 | have_dirname = TRUE; |
||
127 | } |
||
128 | Trace((stderr, "do_wild: dirname = [%s]\n", FnFilter1(dirname))); |
||
129 | |||
130 | if ((wild_dir = opendir(dirname)) != (DIR *)NULL) { |
||
131 | if (have_dirname) { |
||
132 | strcpy(matchname, dirname); |
||
133 | fnamestart = matchname + dirnamelen; |
||
134 | } else |
||
135 | fnamestart = matchname; |
||
136 | while ((file = readdir(wild_dir)) != (struct dirent *)NULL) { |
||
137 | Trace((stderr, "do_wild: readdir returns %s\n", |
||
138 | FnFilter1(file->d_name))); |
||
139 | strcpy(fnamestart, file->d_name); |
||
140 | if (strrchr(fnamestart, '.') == (char *)NULL) |
||
141 | strcat(fnamestart, "."); |
||
142 | if (match(fnamestart, wildname, 1 WISEP) && /* 1=ignore case */ |
||
143 | /* skip "." and ".." directory entries */ |
||
144 | strcmp(fnamestart, ".") && strcmp(fnamestart, "..")) { |
||
145 | Trace((stderr, "do_wild: match() succeeds\n")); |
||
146 | /* remove trailing dot */ |
||
147 | fnamestart += strlen(fnamestart) - 1; |
||
148 | if (*fnamestart == '.') |
||
149 | *fnamestart = '\0'; |
||
150 | return matchname; |
||
151 | } |
||
152 | } |
||
153 | /* if we get to here directory is exhausted, so close it */ |
||
154 | closedir(wild_dir); |
||
155 | wild_dir = (DIR *)NULL; |
||
156 | } |
||
157 | #ifdef DEBUG |
||
158 | else { |
||
159 | Trace((stderr, "do_wild: opendir(%s) returns NULL\n", |
||
160 | FnFilter1(dirname))); |
||
161 | } |
||
162 | #endif /* DEBUG */ |
||
163 | |||
164 | /* return the raw wildspec in case that works (e.g., directory not |
||
165 | * searchable, but filespec was not wild and file is readable) */ |
||
166 | strncpy(matchname, wildspec, FILNAMSIZ); |
||
167 | matchname[FILNAMSIZ-1] = '\0'; |
||
168 | return matchname; |
||
169 | } |
||
170 | |||
171 | /* last time through, might have failed opendir but returned raw wildspec */ |
||
172 | if (wild_dir == (DIR *)NULL) { |
||
173 | notfirstcall = FALSE; /* nothing left to try--reset for new wildspec */ |
||
174 | if (have_dirname) |
||
175 | free(dirname); |
||
176 | return (char *)NULL; |
||
177 | } |
||
178 | |||
179 | /* If we've gotten this far, we've read and matched at least one entry |
||
180 | * successfully (in a previous call), so dirname has been copied into |
||
181 | * matchname already. |
||
182 | */ |
||
183 | if (have_dirname) { |
||
184 | /* strcpy(matchname, dirname); */ |
||
185 | fnamestart = matchname + dirnamelen; |
||
186 | } else |
||
187 | fnamestart = matchname; |
||
188 | while ((file = readdir(wild_dir)) != (struct dirent *)NULL) { |
||
189 | Trace((stderr, "do_wild: readdir returns %s\n", |
||
190 | FnFilter1(file->d_name))); |
||
191 | strcpy(fnamestart, file->d_name); |
||
192 | if (strrchr(fnamestart, '.') == (char *)NULL) |
||
193 | strcat(fnamestart, "."); |
||
194 | if (match(fnamestart, wildname, 1 WISEP)) { /* 1 == ignore case */ |
||
195 | Trace((stderr, "do_wild: match() succeeds\n")); |
||
196 | /* remove trailing dot */ |
||
197 | fnamestart += strlen(fnamestart) - 1; |
||
198 | if (*fnamestart == '.') |
||
199 | *fnamestart = '\0'; |
||
200 | return matchname; |
||
201 | } |
||
202 | } |
||
203 | |||
204 | closedir(wild_dir); /* have read at least one entry; nothing left */ |
||
205 | wild_dir = (DIR *)NULL; |
||
206 | notfirstcall = FALSE; /* reset for new wildspec */ |
||
207 | if (have_dirname) |
||
208 | free(dirname); |
||
209 | return (char *)NULL; |
||
210 | |||
211 | } /* end function do_wild() */ |
||
212 | |||
213 | #endif /* !SFX */ |
||
214 | |||
215 | |||
216 | |||
217 | /**********************/ |
||
218 | /* Function mapattr() */ |
||
219 | /**********************/ |
||
220 | |||
221 | int mapattr(__G) |
||
222 | __GDEF |
||
223 | { |
||
224 | /* set archive bit (file is not backed up): */ |
||
225 | G.pInfo->file_attr = (unsigned)(G.crec.external_file_attributes & 7) | 32; |
||
226 | return 0; |
||
227 | } |
||
228 | |||
229 | |||
230 | |||
231 | /**********************/ |
||
232 | /* Function mapname() */ |
||
233 | /**********************/ |
||
234 | |||
235 | int mapname(__G__ renamed) |
||
236 | __GDEF |
||
237 | int renamed; |
||
238 | /* |
||
239 | * returns: |
||
240 | * MPN_OK - no problem detected |
||
241 | * MPN_INF_TRUNC - caution (truncated filename) |
||
242 | * MPN_INF_SKIP - info "skip entry" (dir doesn't exist) |
||
243 | * MPN_ERR_SKIP - error -> skip entry |
||
244 | * MPN_ERR_TOOLONG - error -> path is too long |
||
245 | * MPN_NOMEM - error (memory allocation failed) -> skip entry |
||
246 | * [also MPN_VOL_LABEL, MPN_CREATED_DIR] |
||
247 | */ |
||
248 | { |
||
249 | char pathcomp[FILNAMSIZ]; /* path-component buffer */ |
||
250 | char *pp, *cp=(char *)NULL; /* character pointers */ |
||
251 | char *lastsemi=(char *)NULL; /* pointer to last semi-colon in pathcomp */ |
||
252 | char *last_dot=(char *)NULL; /* last dot not converted to underscore */ |
||
253 | int dotname = FALSE; /* path component begins with dot? */ |
||
254 | int killed_ddot = FALSE; /* is set when skipping "../" pathcomp */ |
||
255 | int error = MPN_OK; |
||
256 | register unsigned workch; /* hold the character being tested */ |
||
257 | |||
258 | |||
259 | if (G.pInfo->vollabel) |
||
260 | return MPN_VOL_LABEL; /* Cannot set disk volume labels in FlexOS */ |
||
261 | |||
262 | /*--------------------------------------------------------------------------- |
||
263 | Initialize various pointers and counters and stuff. |
||
264 | ---------------------------------------------------------------------------*/ |
||
265 | |||
266 | /* can create path as long as not just freshening, or if user told us */ |
||
267 | G.create_dirs = (!uO.fflag || renamed); |
||
268 | |||
269 | created_dir = FALSE; /* not yet */ |
||
270 | renamed_fullpath = FALSE; |
||
271 | |||
272 | if (renamed) { |
||
273 | cp = G.filename - 1; /* point to beginning of renamed name... */ |
||
274 | while (*++cp) |
||
275 | if (*cp == '\\') /* convert backslashes to forward */ |
||
276 | *cp = '/'; |
||
277 | cp = G.filename; |
||
278 | /* use temporary rootpath if user gave full pathname */ |
||
279 | if (G.filename[0] == '/') { |
||
280 | renamed_fullpath = TRUE; |
||
281 | pathcomp[0] = '/'; /* copy the '/' and terminate */ |
||
282 | pathcomp[1] = '\0'; |
||
283 | ++cp; |
||
284 | } else if (isalpha((uch)G.filename[0]) && G.filename[1] == ':') { |
||
285 | renamed_fullpath = TRUE; |
||
286 | pp = pathcomp; |
||
287 | *pp++ = *cp++; /* copy the "d:" (+ '/', possibly) */ |
||
288 | *pp++ = *cp++; |
||
289 | if (*cp == '/') |
||
290 | *pp++ = *cp++; /* otherwise add "./"? */ |
||
291 | *pp = '\0'; |
||
292 | } |
||
293 | } |
||
294 | |||
295 | /* pathcomp is ignored unless renamed_fullpath is TRUE: */ |
||
296 | if ((error = checkdir(__G__ pathcomp, INIT)) != 0) /* initialize path buf */ |
||
297 | return error; /* ...unless no mem or vol label on hard disk */ |
||
298 | |||
299 | *pathcomp = '\0'; /* initialize translation buffer */ |
||
300 | pp = pathcomp; /* point to translation buffer */ |
||
301 | if (!renamed) { /* cp already set if renamed */ |
||
302 | if (uO.jflag) /* junking directories */ |
||
303 | cp = (char *)strrchr(G.filename, '/'); |
||
304 | if (cp == (char *)NULL) /* no '/' or not junking dirs */ |
||
305 | cp = G.filename; /* point to internal zipfile-member pathname */ |
||
306 | else |
||
307 | ++cp; /* point to start of last component of path */ |
||
308 | } |
||
309 | |||
310 | /*--------------------------------------------------------------------------- |
||
311 | Begin main loop through characters in filename. |
||
312 | ---------------------------------------------------------------------------*/ |
||
313 | |||
314 | while ((workch = (uch)*cp++) != 0) { |
||
315 | |||
316 | switch (workch) { |
||
317 | case '/': /* can assume -j flag not given */ |
||
318 | *pp = '\0'; |
||
319 | map2fat(pathcomp, last_dot); /* 8.3 truncation (in place) */ |
||
320 | last_dot = (char *)NULL; |
||
321 | if (strcmp(pathcomp, ".") == 0) { |
||
322 | /* don't bother appending "./" to the path */ |
||
323 | *pathcomp = '\0'; |
||
324 | } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) { |
||
325 | /* "../" dir traversal detected, skip over it */ |
||
326 | *pathcomp = '\0'; |
||
327 | killed_ddot = TRUE; /* set "show message" flag */ |
||
328 | } |
||
329 | /* when path component is not empty, append it now */ |
||
330 | if (*pathcomp != '\0' && |
||
331 | ((error = checkdir(__G__ pathcomp, APPEND_DIR)) |
||
332 | & MPN_MASK) > MPN_INF_TRUNC) |
||
333 | return error; |
||
334 | pp = pathcomp; /* reset conversion buffer for next piece */ |
||
335 | lastsemi = (char *)NULL; /* leave direct. semi-colons alone */ |
||
336 | break; |
||
337 | |||
338 | case '.': |
||
339 | if (pp == pathcomp) { /* nothing appended yet... */ |
||
340 | if (*cp == '.' && cp[1] == '/') { /* "../" */ |
||
341 | *pp++ = '.'; /* add first dot, unchanged... */ |
||
342 | ++cp; /* skip second dot, since it will */ |
||
343 | } else { /* be "added" at end of if-block */ |
||
344 | *pp++ = '_'; /* FAT doesn't allow null filename */ |
||
345 | dotname = TRUE; /* bodies, so map .exrc -> _.exrc */ |
||
346 | } /* (extra '_' now, "dot" below) */ |
||
347 | } else if (dotname) { /* found a second dot, but still */ |
||
348 | dotname = FALSE; /* have extra leading underscore: */ |
||
349 | *pp = '\0'; /* remove it by shifting chars */ |
||
350 | pp = pathcomp + 1; /* left one space (e.g., .p1.p2: */ |
||
351 | while (pp[1]) { /* __p1 -> _p1_p2 -> _p1.p2 when */ |
||
352 | *pp = pp[1]; /* finished) [opt.: since first */ |
||
353 | ++pp; /* two chars are same, can start */ |
||
354 | } /* shifting at second position] */ |
||
355 | } |
||
356 | last_dot = pp; /* point at last dot so far... */ |
||
357 | *pp++ = '_'; /* convert dot to underscore for now */ |
||
358 | break; |
||
359 | |||
360 | /* drive names are not stored in zipfile, so no colons allowed; |
||
361 | * no brackets or most other punctuation either (all of which |
||
362 | * can appear in Unix-created archives; backslash is particularly |
||
363 | * bad unless all necessary directories exist) */ |
||
364 | case '[': /* these punctuation characters forbidden */ |
||
365 | case ']': /* only on plain FAT file systems */ |
||
366 | case '+': |
||
367 | case ',': |
||
368 | case '=': |
||
369 | case ':': /* special shell characters of command.com */ |
||
370 | case '\\': /* (device and directory limiters, wildcard */ |
||
371 | case '"': /* characters, stdin/stdout redirection and */ |
||
372 | case '<': /* pipe indicators and the quote sign) are */ |
||
373 | case '>': /* never allowed in filenames on (V)FAT */ |
||
374 | case '|': |
||
375 | case '*': |
||
376 | case '?': |
||
377 | *pp++ = '_'; |
||
378 | break; |
||
379 | |||
380 | case ';': /* start of VMS version? */ |
||
381 | lastsemi = pp; |
||
382 | break; |
||
383 | |||
384 | case ' ': /* change spaces to underscores */ |
||
385 | if (uO.sflag) /* only if requested */ |
||
386 | *pp++ = '_'; |
||
387 | else |
||
388 | *pp++ = (char)workch; |
||
389 | break; |
||
390 | |||
391 | default: |
||
392 | /* allow ASCII 255 and European characters in filenames: */ |
||
393 | if (isprint(workch) || workch >= 127) |
||
394 | *pp++ = (char)workch; |
||
395 | |||
396 | } /* end switch */ |
||
397 | } /* end while loop */ |
||
398 | |||
399 | /* Show warning when stripping insecure "parent dir" path components */ |
||
400 | if (killed_ddot && QCOND2) { |
||
401 | Info(slide, 0, ((char *)slide, LoadFarString(WarnDirTraversSkip), |
||
402 | FnFilter1(G.filename))); |
||
403 | if (!(error & ~MPN_MASK)) |
||
404 | error = (error & MPN_MASK) | PK_WARN; |
||
405 | } |
||
406 | |||
407 | /*--------------------------------------------------------------------------- |
||
408 | Report if directory was created (and no file to create: filename ended |
||
409 | in '/'), check name to be sure it exists, and combine path and name be- |
||
410 | fore exiting. |
||
411 | ---------------------------------------------------------------------------*/ |
||
412 | |||
413 | if (G.filename[strlen(G.filename) - 1] == '/') { |
||
414 | checkdir(__G__ G.filename, GETPATH); |
||
415 | if (created_dir) { |
||
416 | if (QCOND2) { |
||
417 | Info(slide, 0, ((char *)slide, LoadFarString(Creating), |
||
418 | FnFilter1(G.filename))); |
||
419 | } |
||
420 | /* set dir time (note trailing '/') */ |
||
421 | return (error & ~MPN_MASK) | MPN_CREATED_DIR; |
||
422 | } |
||
423 | /* dir existed already; don't look for data to extract */ |
||
424 | return (error & ~MPN_MASK) | MPN_INF_SKIP; |
||
425 | } |
||
426 | |||
427 | *pp = '\0'; /* done with pathcomp: terminate it */ |
||
428 | |||
429 | /* if not saving them, remove VMS version numbers (appended ";###") */ |
||
430 | if (!uO.V_flag && lastsemi) { |
||
431 | pp = lastsemi; /* semi-colon was omitted: expect all #'s */ |
||
432 | while (isdigit((uch)(*pp))) |
||
433 | ++pp; |
||
434 | if (*pp == '\0') /* only digits between ';' and end: nuke */ |
||
435 | *lastsemi = '\0'; |
||
436 | } |
||
437 | |||
438 | map2fat(pathcomp, last_dot); /* 8.3 truncation (in place) */ |
||
439 | |||
440 | if (*pathcomp == '\0') { |
||
441 | Info(slide, 1, ((char *)slide, LoadFarString(ConversionFailed), |
||
442 | FnFilter1(G.filename))); |
||
443 | return (error & ~MPN_MASK) | MPN_ERR_SKIP; |
||
444 | } |
||
445 | |||
446 | checkdir(__G__ pathcomp, APPEND_NAME); /* returns 1 if truncated: care? */ |
||
447 | checkdir(__G__ G.filename, GETPATH); |
||
448 | |||
449 | return error; |
||
450 | |||
451 | } /* end function mapname() */ |
||
452 | |||
453 | |||
454 | |||
455 | |||
456 | /**********************/ |
||
457 | /* Function map2fat() */ |
||
458 | /**********************/ |
||
459 | |||
460 | static void map2fat(pathcomp, last_dot) |
||
461 | char *pathcomp, *last_dot; |
||
462 | { |
||
463 | char *pEnd = pathcomp + strlen(pathcomp); |
||
464 | |||
465 | /*--------------------------------------------------------------------------- |
||
466 | Case 1: filename has no dot, so figure out if we should add one. Note |
||
467 | that the algorithm does not try to get too fancy: if there are no dots |
||
468 | already, the name either gets truncated at 8 characters or the last un- |
||
469 | derscore is converted to a dot (only if more characters are saved that |
||
470 | way). In no case is a dot inserted between existing characters. |
||
471 | |||
472 | GRR: have problem if filename is volume label?? |
||
473 | |||
474 | ---------------------------------------------------------------------------*/ |
||
475 | |||
476 | /* pEnd = pathcomp + strlen(pathcomp); */ |
||
477 | if (last_dot == (char *)NULL) { /* no dots: check for underscores... */ |
||
478 | char *plu = strrchr(pathcomp, '_'); /* pointer to last underscore */ |
||
479 | |||
480 | if (plu == (char *)NULL) { /* no dots, no underscores: truncate at */ |
||
481 | if (pEnd > pathcomp+8) /* 8 chars (could insert '.' and keep 11) */ |
||
482 | *(pEnd = pathcomp+8) = '\0'; |
||
483 | } else if (MIN(plu - pathcomp, 8) + MIN(pEnd - plu - 1, 3) > 8) { |
||
484 | last_dot = plu; /* be lazy: drop through to next if-block */ |
||
485 | } else if ((pEnd - pathcomp) > 8) /* more fits into just basename */ |
||
486 | pathcomp[8] = '\0'; /* than if convert last underscore to dot */ |
||
487 | /* else whole thing fits into 8 chars or less: no change */ |
||
488 | } |
||
489 | |||
490 | /*--------------------------------------------------------------------------- |
||
491 | Case 2: filename has dot in it, so truncate first half at 8 chars (shift |
||
492 | extension if necessary) and second half at three. |
||
493 | ---------------------------------------------------------------------------*/ |
||
494 | |||
495 | if (last_dot != (char *)NULL) { /* one dot (or two, in the case of */ |
||
496 | *last_dot = '.'; /* "..") is OK: put it back in */ |
||
497 | |||
498 | if ((last_dot - pathcomp) > 8) { |
||
499 | char *p=last_dot, *q=pathcomp+8; |
||
500 | int i; |
||
501 | |||
502 | for (i = 0; (i < 4) && *p; ++i) /* too many chars in basename: */ |
||
503 | *q++ = *p++; /* shift extension left and */ |
||
504 | *q = '\0'; /* truncate/terminate it */ |
||
505 | } else if ((pEnd - last_dot) > 4) |
||
506 | last_dot[4] = '\0'; /* too many chars in extension */ |
||
507 | /* else filename is fine as is: no change */ |
||
508 | } |
||
509 | } /* end function map2fat() */ |
||
510 | |||
511 | |||
512 | |||
513 | |||
514 | /***********************/ |
||
515 | /* Function checkdir() */ |
||
516 | /***********************/ |
||
517 | |||
518 | int checkdir(__G__ pathcomp, flag) |
||
519 | __GDEF |
||
520 | char *pathcomp; |
||
521 | int flag; |
||
522 | /* |
||
523 | * returns: |
||
524 | * MPN_OK - no problem detected |
||
525 | * MPN_INF_TRUNC - (on APPEND_NAME) truncated filename |
||
526 | * MPN_INF_SKIP - path doesn't exist, not allowed to create |
||
527 | * MPN_ERR_SKIP - path doesn't exist, tried to create and failed; or path |
||
528 | * exists and is not a directory, but is supposed to be |
||
529 | * MPN_ERR_TOOLONG - path is too long |
||
530 | * MPN_NOMEM - can't allocate memory for filename buffers |
||
531 | */ |
||
532 | { |
||
533 | static int rootlen = 0; /* length of rootpath */ |
||
534 | static char *rootpath; /* user's "extract-to" directory */ |
||
535 | static char *buildpath; /* full path (so far) to extracted file */ |
||
536 | static char *end; /* pointer to end of buildpath ('\0') */ |
||
537 | |||
538 | # define FN_MASK 7 |
||
539 | # define FUNCTION (flag & FN_MASK) |
||
540 | |||
541 | |||
542 | /*--------------------------------------------------------------------------- |
||
543 | APPEND_DIR: append the path component to the path being built and check |
||
544 | for its existence. If doesn't exist and we are creating directories, do |
||
545 | so for this one; else signal success or error as appropriate. |
||
546 | ---------------------------------------------------------------------------*/ |
||
547 | |||
548 | if (FUNCTION == APPEND_DIR) { |
||
549 | int too_long = FALSE; |
||
550 | |||
551 | Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp))); |
||
552 | while ((*end = *pathcomp++) != '\0') |
||
553 | ++end; |
||
554 | |||
555 | /* GRR: could do better check, see if overrunning buffer as we go: |
||
556 | * check end-buildpath after each append, set warning variable if |
||
557 | * within 20 of FILNAMSIZ; then if var set, do careful check when |
||
558 | * appending. Clear variable when begin new path. */ |
||
559 | |||
560 | if ((end-buildpath) > FILNAMSIZ-3) /* need '/', one-char name, '\0' */ |
||
561 | too_long = TRUE; /* check if extracting directory? */ |
||
562 | if (stat(buildpath, &G.statbuf)) /* path doesn't exist */ |
||
563 | { |
||
564 | if (!G.create_dirs) { /* told not to create (freshening) */ |
||
565 | free(buildpath); |
||
566 | return MPN_INF_SKIP; /* path doesn't exist: nothing to do */ |
||
567 | } |
||
568 | if (too_long) { |
||
569 | Info(slide, 1, ((char *)slide, LoadFarString(PathTooLong), |
||
570 | FnFilter1(buildpath))); |
||
571 | free(buildpath); |
||
572 | /* no room for filenames: fatal */ |
||
573 | return MPN_ERR_TOOLONG; |
||
574 | } |
||
575 | if (mkdir(buildpath, 0777) == -1) { /* create the directory */ |
||
576 | Info(slide, 1, ((char *)slide, LoadFarString(CantCreateDir), |
||
577 | FnFilter2(buildpath), FnFilter1(G.filename))); |
||
578 | free(buildpath); |
||
579 | /* path didn't exist, tried to create, failed */ |
||
580 | return MPN_ERR_SKIP; |
||
581 | } |
||
582 | created_dir = TRUE; |
||
583 | } else if (!S_ISDIR(G.statbuf.st_mode)) { |
||
584 | Info(slide, 1, ((char *)slide, LoadFarString(DirIsntDirectory), |
||
585 | FnFilter2(buildpath), FnFilter1(G.filename))); |
||
586 | free(buildpath); |
||
587 | /* path existed but wasn't dir */ |
||
588 | return MPN_ERR_SKIP; |
||
589 | } |
||
590 | if (too_long) { |
||
591 | Info(slide, 1, ((char *)slide, LoadFarString(PathTooLong), |
||
592 | FnFilter1(buildpath))); |
||
593 | free(buildpath); |
||
594 | /* no room for filenames: fatal */ |
||
595 | return MPN_ERR_TOOLONG; |
||
596 | } |
||
597 | *end++ = '/'; |
||
598 | *end = '\0'; |
||
599 | Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath))); |
||
600 | return MPN_OK; |
||
601 | |||
602 | } /* end if (FUNCTION == APPEND_DIR) */ |
||
603 | |||
604 | /*--------------------------------------------------------------------------- |
||
605 | GETPATH: copy full path to the string pointed at by pathcomp, and free |
||
606 | buildpath. |
||
607 | ---------------------------------------------------------------------------*/ |
||
608 | |||
609 | if (FUNCTION == GETPATH) { |
||
610 | strcpy(pathcomp, buildpath); |
||
611 | Trace((stderr, "getting and freeing path [%s]\n", |
||
612 | FnFilter1(pathcomp))); |
||
613 | free(buildpath); |
||
614 | buildpath = end = (char *)NULL; |
||
615 | return MPN_OK; |
||
616 | } |
||
617 | |||
618 | /*--------------------------------------------------------------------------- |
||
619 | APPEND_NAME: assume the path component is the filename; append it and |
||
620 | return without checking for existence. |
||
621 | ---------------------------------------------------------------------------*/ |
||
622 | |||
623 | if (FUNCTION == APPEND_NAME) { |
||
624 | Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp))); |
||
625 | while ((*end = *pathcomp++) != '\0') { |
||
626 | ++end; |
||
627 | if ((end-buildpath) >= FILNAMSIZ) { |
||
628 | *--end = '\0'; |
||
629 | Info(slide, 1, ((char *)slide, LoadFarString(PathTooLongTrunc), |
||
630 | FnFilter1(G.filename), FnFilter2(buildpath))); |
||
631 | return MPN_INF_TRUNC; /* filename truncated */ |
||
632 | } |
||
633 | } |
||
634 | Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath))); |
||
635 | /* could check for existence here, prompt for new name... */ |
||
636 | return MPN_OK; |
||
637 | } |
||
638 | |||
639 | /*--------------------------------------------------------------------------- |
||
640 | INIT: allocate and initialize buffer space for the file currently being |
||
641 | extracted. If file was renamed with an absolute path, don't prepend the |
||
642 | extract-to path. |
||
643 | ---------------------------------------------------------------------------*/ |
||
644 | |||
645 | if (FUNCTION == INIT) { |
||
646 | Trace((stderr, "initializing buildpath to ")); |
||
647 | /* allocate space for full filename, root path, and maybe "./" */ |
||
648 | if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+3)) == |
||
649 | (char *)NULL) |
||
650 | return MPN_NOMEM; |
||
651 | if (renamed_fullpath) { /* pathcomp = valid data */ |
||
652 | end = buildpath; |
||
653 | while ((*end = *pathcomp++) != '\0') |
||
654 | ++end; |
||
655 | } else if (rootlen > 0) { |
||
656 | strcpy(buildpath, rootpath); |
||
657 | end = buildpath + rootlen; |
||
658 | } else { |
||
659 | *buildpath = '\0'; |
||
660 | end = buildpath; |
||
661 | } |
||
662 | Trace((stderr, "[%s]\n", FnFilter1(buildpath))); |
||
663 | return MPN_OK; |
||
664 | } |
||
665 | |||
666 | /*--------------------------------------------------------------------------- |
||
667 | ROOT: if appropriate, store the path in rootpath and create it if neces- |
||
668 | sary; else assume it's a zipfile member and return. This path segment |
||
669 | gets used in extracting all members from every zipfile specified on the |
||
670 | command line. Note that under FlexOS, if a candidate extract-to |
||
671 | directory specification includes a drive letter (leading "x:"), it is |
||
672 | treated just as if it had a trailing '/'--that is, one directory level |
||
673 | will be created if the path doesn't exist, unless this is otherwise pro- |
||
674 | hibited (e.g., freshening). |
||
675 | ---------------------------------------------------------------------------*/ |
||
676 | |||
677 | #if (!defined(SFX) || defined(SFX_EXDIR)) |
||
678 | if (FUNCTION == ROOT) { |
||
679 | Trace((stderr, "initializing root path to [%s]\n", |
||
680 | FnFilter1(pathcomp))); |
||
681 | if (pathcomp == (char *)NULL) { |
||
682 | rootlen = 0; |
||
683 | return MPN_OK; |
||
684 | } |
||
685 | if (rootlen > 0) /* rootpath was already set, nothing to do */ |
||
686 | return MPN_OK; |
||
687 | if ((rootlen = strlen(pathcomp)) > 0) { |
||
688 | int had_trailing_pathsep=FALSE, add_dot=FALSE; |
||
689 | char *tmproot; |
||
690 | |||
691 | if ((tmproot = (char *)malloc(rootlen+3)) == (char *)NULL) { |
||
692 | rootlen = 0; |
||
693 | return MPN_NOMEM; |
||
694 | } |
||
695 | strcpy(tmproot, pathcomp); |
||
696 | if (tmproot[rootlen-1] == '/' || tmproot[rootlen-1] == '\\') { |
||
697 | tmproot[--rootlen] = '\0'; |
||
698 | had_trailing_pathsep = TRUE; |
||
699 | } |
||
700 | if (tmproot[rootlen-1] == ':') { |
||
701 | if (!had_trailing_pathsep) /* i.e., original wasn't "xxx:/" */ |
||
702 | add_dot = TRUE; /* relative path: add '.' before '/' */ |
||
703 | } else if (rootlen > 0) && /* need not check "xxx:." and "xxx:/" */ |
||
704 | (SSTAT(tmproot, &G.statbuf) || |
||
705 | !S_ISDIR(G.statbuf.st_mode)) |
||
706 | { |
||
707 | /* path does not exist */ |
||
708 | if (!G.create_dirs /* || iswild(tmproot) */ ) { |
||
709 | free(tmproot); |
||
710 | rootlen = 0; |
||
711 | /* treat as stored file */ |
||
712 | return MPN_INF_SKIP; |
||
713 | } |
||
714 | /* GRR: scan for wildcard characters? OS-dependent... |
||
715 | * if find any, return MPN_INF_SKIP: treat as stored file(s) */ |
||
716 | /* create directory (could add loop here scanning tmproot |
||
717 | * to create more than one level, but really necessary?) */ |
||
718 | if (mkdir(tmproot, 0777) == -1) { |
||
719 | Info(slide, 1, ((char *)slide, |
||
720 | LoadFarString(CantCreateExtractDir), |
||
721 | FnFilter1(tmproot))); |
||
722 | free(tmproot); |
||
723 | rootlen = 0; |
||
724 | /* path didn't exist, tried to create, and failed: */ |
||
725 | /* file exists, or 2+ subdir levels required */ |
||
726 | return MPN_ERR_SKIP; |
||
727 | } |
||
728 | } |
||
729 | if (add_dot) /* had just "x:", make "x:." */ |
||
730 | tmproot[rootlen++] = '.'; |
||
731 | tmproot[rootlen++] = '/'; |
||
732 | tmproot[rootlen] = '\0'; |
||
733 | if ((rootpath = (char *)realloc(tmproot, rootlen+1)) == NULL) { |
||
734 | free(tmproot); |
||
735 | rootlen = 0; |
||
736 | return MPN_NOMEM; |
||
737 | } |
||
738 | Trace((stderr, "rootpath now = [%s]\n", FnFilter1(rootpath))); |
||
739 | } |
||
740 | return MPN_OK; |
||
741 | } |
||
742 | #endif /* !SFX || SFX_EXDIR */ |
||
743 | |||
744 | /*--------------------------------------------------------------------------- |
||
745 | END: free rootpath, immediately prior to program exit. |
||
746 | ---------------------------------------------------------------------------*/ |
||
747 | |||
748 | if (FUNCTION == END) { |
||
749 | Trace((stderr, "freeing rootpath\n")); |
||
750 | if (rootlen > 0) { |
||
751 | free(rootpath); |
||
752 | rootlen = 0; |
||
753 | } |
||
754 | return MPN_OK; |
||
755 | } |
||
756 | |||
757 | return MPN_INVALID; /* should never reach */ |
||
758 | |||
759 | } /* end function checkdir() */ |
||
760 | |||
761 | |||
762 | |||
763 | |||
764 | /****************************/ |
||
765 | /* Function close_outfile() */ |
||
766 | /****************************/ |
||
767 | |||
768 | void close_outfile(__G) |
||
769 | __GDEF |
||
770 | /* |
||
771 | * FlexOS VERSION |
||
772 | * |
||
773 | * Set the output file date/time stamp according to information from the |
||
774 | * zipfile directory record for this member, then close the file and set |
||
775 | * its permissions (archive, hidden, read-only, system). Aside from closing |
||
776 | * the file, this routine is optional (but most compilers support it). |
||
777 | */ |
||
778 | { |
||
779 | DISKFILE df; |
||
780 | LONG fnum; |
||
781 | |||
782 | struct { /* date and time words */ |
||
783 | union { /* DOS file modification time word */ |
||
784 | ush ztime; |
||
785 | struct { |
||
786 | unsigned zt_se : 5; |
||
787 | unsigned zt_mi : 6; |
||
788 | unsigned zt_hr : 5; |
||
789 | } _tf; |
||
790 | } _t; |
||
791 | union { /* DOS file modification date word */ |
||
792 | ush zdate; |
||
793 | struct { |
||
794 | unsigned zd_dy : 5; |
||
795 | unsigned zd_mo : 4; |
||
796 | unsigned zd_yr : 7; |
||
797 | } _df; |
||
798 | } _d; |
||
799 | } zt; |
||
800 | |||
801 | #ifdef USE_EF_UT_TIME |
||
802 | iztimes z_utime; |
||
803 | struct tm *t; |
||
804 | #endif /* ?USE_EF_UT_TIME */ |
||
805 | |||
806 | fclose(G.outfile); |
||
807 | |||
808 | if ((fnum = s_open(A_SET, G.filename)) < 0) { |
||
809 | Info(slide, 0x201, ((char *)slide, |
||
810 | "warning: cannot open %s to set the time\n", |
||
811 | FnFilter1(G.filename))); |
||
812 | return; |
||
813 | } |
||
814 | |||
815 | if (s_get(T_FILE, fnum, &df, DSKFSIZE) < 0) { |
||
816 | s_close(0, fnum); |
||
817 | |||
818 | Info(slide, 0x201, ((char *)slide, |
||
819 | "warning: cannot get info on %s\n", FnFilter1(G.filename))); |
||
820 | return; |
||
821 | } |
||
822 | |||
823 | /* skip restoring time stamps on user's request */ |
||
824 | if (uO.D_flag <= 1) { |
||
825 | |||
826 | /*--------------------------------------------------------------------------- |
||
827 | Copy and/or convert time and date variables, if necessary; then fill in |
||
828 | the file time/date. |
||
829 | ---------------------------------------------------------------------------*/ |
||
830 | |||
831 | #ifdef USE_EF_UT_TIME |
||
832 | if (G.extra_field && |
||
833 | #ifdef IZ_CHECK_TZ |
||
834 | G.tz_is_valid && |
||
835 | #endif |
||
836 | (ef_scan_for_izux(G.extra_field, G.lrec.extra_field_length, 0, |
||
837 | G.lrec.last_mod_dos_datetime, &z_utime, NULL) & EB_UT_FL_MTIME)) |
||
838 | { |
||
839 | TTrace((stderr, "close_outfile: Unix e.f. modif. time = %ld\n", |
||
840 | z_utime.mtime)); |
||
841 | t = localtime(&(z_utime.mtime)); |
||
842 | } else |
||
843 | t = (struct tm *)NULL; |
||
844 | if (t != (struct tm *)NULL) { |
||
845 | if (t->tm_year < 80) { |
||
846 | df.df_modyear = 1980; |
||
847 | df.df_modmonth = 1; |
||
848 | df.df_modday = 1; |
||
849 | df.df_modhr = 0; |
||
850 | df.df_modmin = 0; |
||
851 | df.df_modsec = 0; |
||
852 | } else { |
||
853 | df.df_modyear = t->tm_year + 1900; |
||
854 | df.df_modmonth = t->tm_mon + 1; |
||
855 | df.df_modday = t->tm_mday; |
||
856 | df.df_modhr = t->tm_hour; |
||
857 | df.df_modmin = t->tm_min; |
||
858 | df.df_modsec = t->tm_sec; |
||
859 | } |
||
860 | } else |
||
861 | #endif /* ?USE_EF_UX_TIME */ |
||
862 | { |
||
863 | zt._t.ztime = (ush)(G.lrec.last_mod_dos_datetime) & 0xffff; |
||
864 | zt._d.zdate = (ush)(G.lrec.last_mod_dos_datetime >> 16); |
||
865 | |||
866 | df.df_modyear = 1980 + zt._d._df.zd_yr; |
||
867 | df.df_modmonth = zt._d._df.zd_mo; |
||
868 | df.df_modday = zt._d._df.zd_dy; |
||
869 | df.df_modhr = zt._t._tf.zt_hr; |
||
870 | df.df_modmin = zt._t._tf.zt_mi; |
||
871 | df.df_modsec = zt._t._tf.zt_se << 1; |
||
872 | } |
||
873 | } |
||
874 | |||
875 | /*--------------------------------------------------------------------------- |
||
876 | Fill in the file attributes. |
||
877 | ---------------------------------------------------------------------------*/ |
||
878 | |||
879 | df.df_attr1 = (UBYTE)G.pInfo->file_attr; |
||
880 | |||
881 | /*--------------------------------------------------------------------------- |
||
882 | Now we try to set the attributes & date/time. |
||
883 | ---------------------------------------------------------------------------*/ |
||
884 | |||
885 | if (s_set(T_FILE, fnum, &df, DSKFSIZE) < 0) |
||
886 | Info(slide, 0x201, ((char *)slide, |
||
887 | "warning: cannot set info for %s\n", FnFilter1(G.filename))); |
||
888 | |||
889 | s_close(0, fnum); |
||
890 | } /* end function close_outfile() */ |
||
891 | |||
892 | #ifndef SFX |
||
893 | |||
894 | /*************************/ |
||
895 | /* Function dateformat() */ |
||
896 | /*************************/ |
||
897 | |||
898 | int dateformat() |
||
899 | { |
||
900 | return DF_DMY; /* default for systems without locale info */ |
||
901 | } |
||
902 | |||
903 | /************************/ |
||
904 | /* Function version() */ |
||
905 | /************************/ |
||
906 | |||
907 | void version(__G) |
||
908 | __GDEF |
||
909 | { |
||
910 | int len; |
||
911 | |||
912 | len = sprintf((char *)slide, LoadFarString(CompiledWith), |
||
913 | "MetaWare High C", |
||
914 | "", |
||
915 | "FlexOS", |
||
916 | " (16-bit, big)", |
||
917 | |||
918 | #ifdef __DATE__ |
||
919 | " on ", __DATE__ |
||
920 | #else |
||
921 | "", "" |
||
922 | #endif |
||
923 | ); |
||
924 | |||
925 | (*G.message)((zvoid *)&G, slide, (ulg)len, 0); |
||
926 | } |
||
927 | |||
928 | #endif /* !SFX */ |
||
929 | |||
930 | /************************/ |
||
931 | /* Function _wildarg() */ |
||
932 | /************************/ |
||
933 | |||
934 | /* This prevents the PORTLIB startup code from preforming argument globbing */ |
||
935 | |||
936 | _wildarg() {}>><>>=>>>>':> |