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 2007-Mar-04 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 | netware.c |
||
12 | |||
13 | This file implements these functions for a NetWare Loadable Module (NLM): |
||
14 | |||
15 | Contains: InitUnZipConsole() |
||
16 | do_wild() |
||
17 | mapattr() |
||
18 | mapname() |
||
19 | checkdir() |
||
20 | close_outfile() |
||
21 | stamp_file() |
||
22 | version() |
||
23 | screensize() |
||
24 | |||
25 | ---------------------------------------------------------------------------*/ |
||
26 | |||
27 | |||
28 | #define UNZIP_INTERNAL |
||
29 | #include "unzip.h" |
||
30 | #include |
||
31 | #include |
||
32 | #include |
||
33 | #include |
||
34 | #include |
||
35 | #include |
||
36 | |||
37 | #ifdef ACORN_FTYPE_NFS |
||
38 | /* Acorn bits for NFS filetyping */ |
||
39 | typedef struct { |
||
40 | uch ID[2]; |
||
41 | uch size[2]; |
||
42 | uch ID_2[4]; |
||
43 | uch loadaddr[4]; |
||
44 | uch execaddr[4]; |
||
45 | uch attr[4]; |
||
46 | } RO_extra_block; |
||
47 | |||
48 | #endif /* ACORN_FTYPE_NFS */ |
||
49 | |||
50 | static int created_dir; /* used in mapname(), checkdir() */ |
||
51 | static int renamed_fullpath; /* ditto */ |
||
52 | |||
53 | |||
54 | /*********************************/ |
||
55 | /* Function InitUnZipConsole() */ |
||
56 | /*********************************/ |
||
57 | |||
58 | void InitUnZipConsole() |
||
59 | { |
||
60 | unsigned int myHandle = GetNLMHandle(); |
||
61 | unsigned int *activeScreen = |
||
62 | ImportSymbol(myHandle, "activeScreen"); |
||
63 | unsigned int *systemConsoleScreen = |
||
64 | ImportSymbol(myHandle, "systemConsoleScreen"); |
||
65 | void (*pUseAccurateCaseForPaths)(int) = |
||
66 | ImportSymbol(myHandle, "UseAccurateCaseForPaths"); |
||
67 | |||
68 | if (!activeScreen || !systemConsoleScreen || |
||
69 | *activeScreen == *systemConsoleScreen) |
||
70 | CreateScreen("Info-ZIP UnZip Utility", 0); |
||
71 | else |
||
72 | CreateScreen("System Console", DONT_AUTO_ACTIVATE); |
||
73 | |||
74 | SetCurrentNameSpace(NW_NS_LONG); |
||
75 | if (pUseAccurateCaseForPaths) |
||
76 | pUseAccurateCaseForPaths(TRUE); |
||
77 | |||
78 | UnimportSymbol(myHandle, "activeScreen"); |
||
79 | UnimportSymbol(myHandle, "systemConsoleScreen"); |
||
80 | UnimportSymbol(myHandle, "UseAccurateCaseForPaths"); |
||
81 | } |
||
82 | |||
83 | |||
84 | /**********************/ |
||
85 | /* Function do_wild() */ /* for porting: dir separator; match(ignore_case) */ |
||
86 | /**********************/ |
||
87 | |||
88 | char *do_wild(__G__ wildspec) |
||
89 | __GDEF |
||
90 | ZCONST char *wildspec; /* only used first time on a given dir */ |
||
91 | { |
||
92 | static DIR *wild_dir = (DIR *)NULL; |
||
93 | static ZCONST char *wildname; |
||
94 | static char *dirname, matchname[FILNAMSIZ]; |
||
95 | static int notfirstcall=FALSE, have_dirname, dirnamelen; |
||
96 | struct dirent *file; |
||
97 | |||
98 | /* Even when we're just returning wildspec, we *always* do so in |
||
99 | * matchname[]--calling routine is allowed to append four characters |
||
100 | * to the returned string, and wildspec may be a pointer to argv[]. |
||
101 | */ |
||
102 | if (!notfirstcall) { /* first call: must initialize everything */ |
||
103 | notfirstcall = TRUE; |
||
104 | |||
105 | if (!iswild(wildspec)) { |
||
106 | strncpy(matchname, wildspec, FILNAMSIZ); |
||
107 | matchname[FILNAMSIZ-1] = '\0'; |
||
108 | have_dirname = FALSE; |
||
109 | dir = NULL; |
||
110 | return matchname; |
||
111 | } |
||
112 | |||
113 | /* break the wildspec into a directory part and a wildcard filename */ |
||
114 | if ((wildname = strrchr(wildspec, '/')) == (ZCONST char *)NULL) { |
||
115 | dirname = "."; |
||
116 | dirnamelen = 1; |
||
117 | have_dirname = FALSE; |
||
118 | wildname = wildspec; |
||
119 | } else { |
||
120 | ++wildname; /* point at character after '/' */ |
||
121 | dirnamelen = wildname - wildspec; |
||
122 | if ((dirname = (char *)malloc(dirnamelen+1)) == (char *)NULL) { |
||
123 | Info(slide, 0x201, ((char *)slide, |
||
124 | "warning: cannot allocate wildcard buffers\n")); |
||
125 | strncpy(matchname, wildspec, FILNAMSIZ); |
||
126 | matchname[FILNAMSIZ-1] = '\0'; |
||
127 | return matchname; /* but maybe filespec was not a wildcard */ |
||
128 | } |
||
129 | strncpy(dirname, wildspec, dirnamelen); |
||
130 | dirname[dirnamelen] = '\0'; /* terminate for strcpy below */ |
||
131 | have_dirname = TRUE; |
||
132 | } |
||
133 | |||
134 | if ((wild_dir = opendir(dirname)) != (DIR *)NULL) { |
||
135 | while ((file = readdir(wild_dir)) != (struct dirent *)NULL) { |
||
136 | Trace((stderr, "do_wild: readdir returns %s\n", |
||
137 | FnFilter1(file->d_name))); |
||
138 | if (file->d_name[0] == '.' && wildname[0] != '.') |
||
139 | continue; /* Unix: '*' and '?' do not match leading dot */ |
||
140 | if (match(file->d_name, wildname, 0 WISEP) && /* 0=case sens.*/ |
||
141 | /* skip "." and ".." directory entries */ |
||
142 | strcmp(file->d_name, ".") && strcmp(file->d_name, "..")) { |
||
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 | /* if we get to here directory is exhausted, so close it */ |
||
153 | closedir(wild_dir); |
||
154 | wild_dir = (DIR *)NULL; |
||
155 | } |
||
156 | |||
157 | /* return the raw wildspec in case that works (e.g., directory not |
||
158 | * searchable, but filespec was not wild and file is readable) */ |
||
159 | strncpy(matchname, wildspec, FILNAMSIZ); |
||
160 | matchname[FILNAMSIZ-1] = '\0'; |
||
161 | return matchname; |
||
162 | } |
||
163 | |||
164 | /* last time through, might have failed opendir but returned raw wildspec */ |
||
165 | if (wild_dir == (DIR *)NULL) { |
||
166 | notfirstcall = FALSE; /* nothing left to try--reset for new wildspec */ |
||
167 | if (have_dirname) |
||
168 | free(dirname); |
||
169 | return (char *)NULL; |
||
170 | } |
||
171 | |||
172 | /* If we've gotten this far, we've read and matched at least one entry |
||
173 | * successfully (in a previous call), so dirname has been copied into |
||
174 | * matchname already. |
||
175 | */ |
||
176 | while ((file = readdir(wild_dir)) != (struct dirent *)NULL) { |
||
177 | Trace((stderr, "do_wild: readdir returns %s\n", |
||
178 | FnFilter1(file->d_name))); |
||
179 | if (file->d_name[0] == '.' && wildname[0] != '.') |
||
180 | continue; /* Unix: '*' and '?' do not match leading dot */ |
||
181 | if (match(file->d_name, wildname, 0 WISEP)) { /* 0 == case sens. */ |
||
182 | Trace((stderr, "do_wild: match() succeeds\n")); |
||
183 | if (have_dirname) { |
||
184 | /* strcpy(matchname, dirname); */ |
||
185 | strcpy(matchname+dirnamelen, file->d_name); |
||
186 | } else |
||
187 | strcpy(matchname, file->d_name); |
||
188 | return matchname; |
||
189 | } |
||
190 | } |
||
191 | |||
192 | closedir(wild_dir); /* have read at least one entry; nothing left */ |
||
193 | wild_dir = (DIR *)NULL; |
||
194 | notfirstcall = FALSE; /* reset for new wildspec */ |
||
195 | if (have_dirname) |
||
196 | free(dirname); |
||
197 | return (char *)NULL; |
||
198 | |||
199 | } /* end function do_wild() */ |
||
200 | |||
201 | |||
202 | /**********************/ |
||
203 | /* Function mapattr() */ |
||
204 | /**********************/ |
||
205 | |||
206 | int mapattr(__G) |
||
207 | __GDEF |
||
208 | { |
||
209 | ulg tmp = G.crec.external_file_attributes; |
||
210 | |||
211 | G.pInfo->file_attr = 0; |
||
212 | /* initialized to 0 for check in "default" branch below... */ |
||
213 | |||
214 | switch (G.pInfo->hostnum) { |
||
215 | case AMIGA_: |
||
216 | tmp = (unsigned)(tmp>>17 & 7); /* Amiga RWE bits */ |
||
217 | G.pInfo->file_attr = (unsigned)(tmp<<6 | tmp<<3 | tmp); |
||
218 | break; |
||
219 | case UNIX_: |
||
220 | case VMS_: |
||
221 | case ACORN_: |
||
222 | case ATARI_: |
||
223 | case ATHEOS_: |
||
224 | case BEOS_: |
||
225 | case QDOS_: |
||
226 | case TANDEM_: |
||
227 | G.pInfo->file_attr = (unsigned)(tmp >> 16); |
||
228 | if (G.pInfo->file_attr != 0 || !G.extra_field) { |
||
229 | return 0; |
||
230 | } else { |
||
231 | /* Some (non-Info-ZIP) implementations of Zip for Unix and |
||
232 | * VMS (and probably others ??) leave 0 in the upper 16-bit |
||
233 | * part of the external_file_attributes field. Instead, they |
||
234 | * store file permission attributes in some extra field. |
||
235 | * As a work-around, we search for the presence of one of |
||
236 | * these extra fields and fall back to the MSDOS compatible |
||
237 | * part of external_file_attributes if one of the known |
||
238 | * e.f. types has been detected. |
||
239 | * Later, we might implement extraction of the permission |
||
240 | * bits from the VMS extra field. But for now, the work-around |
||
241 | * should be sufficient to provide "readable" extracted files. |
||
242 | * (For ASI Unix e.f., an experimental remap from the e.f. |
||
243 | * mode value IS already provided!) |
||
244 | */ |
||
245 | ush ebID; |
||
246 | unsigned ebLen; |
||
247 | uch *ef = G.extra_field; |
||
248 | unsigned ef_len = G.crec.extra_field_length; |
||
249 | int r = FALSE; |
||
250 | |||
251 | while (!r && ef_len >= EB_HEADSIZE) { |
||
252 | ebID = makeword(ef); |
||
253 | ebLen = (unsigned)makeword(ef+EB_LEN); |
||
254 | if (ebLen > (ef_len - EB_HEADSIZE)) |
||
255 | /* discoverd some e.f. inconsistency! */ |
||
256 | break; |
||
257 | switch (ebID) { |
||
258 | case EF_ASIUNIX: |
||
259 | if (ebLen >= (EB_ASI_MODE+2)) { |
||
260 | G.pInfo->file_attr = |
||
261 | (unsigned)makeword(ef+(EB_HEADSIZE+EB_ASI_MODE)); |
||
262 | /* force stop of loop: */ |
||
263 | ef_len = (ebLen + EB_HEADSIZE); |
||
264 | break; |
||
265 | } |
||
266 | /* else: fall through! */ |
||
267 | case EF_PKVMS: |
||
268 | /* "found nondecypherable e.f. with perm. attr" */ |
||
269 | r = TRUE; |
||
270 | default: |
||
271 | break; |
||
272 | } |
||
273 | ef_len -= (ebLen + EB_HEADSIZE); |
||
274 | ef += (ebLen + EB_HEADSIZE); |
||
275 | } |
||
276 | if (!r) |
||
277 | return 0; |
||
278 | } |
||
279 | /* fall through! */ |
||
280 | /* all remaining cases: expand MSDOS read-only bit into write perms */ |
||
281 | case FS_FAT_: |
||
282 | /* PKWARE's PKZip for Unix marks entries as FS_FAT_, but stores the |
||
283 | * Unix attributes in the upper 16 bits of the external attributes |
||
284 | * field, just like Info-ZIP's Zip for Unix. We try to use that |
||
285 | * value, after a check for consistency with the MSDOS attribute |
||
286 | * bits (see below). |
||
287 | */ |
||
288 | G.pInfo->file_attr = (unsigned)(tmp >> 16); |
||
289 | /* fall through! */ |
||
290 | case FS_HPFS_: |
||
291 | case FS_NTFS_: |
||
292 | case MAC_: |
||
293 | case TOPS20_: |
||
294 | default: |
||
295 | /* Ensure that DOS subdir bit is set when the entry's name ends |
||
296 | * in a '/'. Some third-party Zip programs fail to set the subdir |
||
297 | * bit for directory entries. |
||
298 | */ |
||
299 | if ((tmp & 0x10) == 0) { |
||
300 | extent fnlen = strlen(G.filename); |
||
301 | if (fnlen > 0 && G.filename[fnlen-1] == '/') |
||
302 | tmp |= 0x10; |
||
303 | } |
||
304 | /* read-only bit --> write perms; subdir bit --> dir exec bit */ |
||
305 | tmp = !(tmp & 1) << 1 | (tmp & 0x10) >> 4; |
||
306 | if ((G.pInfo->file_attr & 0700) == (unsigned)(0400 | tmp<<6)) |
||
307 | /* keep previous G.pInfo->file_attr setting, when its "owner" |
||
308 | * part appears to be consistent with DOS attribute flags! |
||
309 | */ |
||
310 | return 0; |
||
311 | G.pInfo->file_attr = (unsigned)(0444 | tmp<<6 | tmp<<3 | tmp); |
||
312 | break; |
||
313 | } /* end switch (host-OS-created-by) */ |
||
314 | |||
315 | /* for originating systems with no concept of "group," "other," "system": */ |
||
316 | umask( (int)(tmp=umask(0)) ); /* apply mask to expanded r/w(/x) perms */ |
||
317 | G.pInfo->file_attr &= ~tmp; |
||
318 | |||
319 | return 0; |
||
320 | |||
321 | } /* end function mapattr() */ |
||
322 | |||
323 | |||
324 | |||
325 | |||
326 | /**********************/ |
||
327 | /* Function mapname() */ |
||
328 | /**********************/ |
||
329 | |||
330 | int mapname(__G__ renamed) |
||
331 | __GDEF |
||
332 | int renamed; |
||
333 | /* |
||
334 | * returns: |
||
335 | * MPN_OK - no problem detected |
||
336 | * MPN_INF_TRUNC - caution (truncated filename) |
||
337 | * MPN_INF_SKIP - info "skip entry" (dir doesn't exist) |
||
338 | * MPN_ERR_SKIP - error -> skip entry |
||
339 | * MPN_ERR_TOOLONG - error -> path is too long |
||
340 | * MPN_NOMEM - error (memory allocation failed) -> skip entry |
||
341 | * [also MPN_VOL_LABEL, MPN_CREATED_DIR] |
||
342 | */ |
||
343 | { |
||
344 | char pathcomp[FILNAMSIZ]; /* path-component buffer */ |
||
345 | char *pp, *cp=(char *)NULL; /* character pointers */ |
||
346 | char *lastsemi=(char *)NULL; /* pointer to last semi-colon in pathcomp */ |
||
347 | #ifdef ACORN_FTYPE_NFS |
||
348 | char *lastcomma=(char *)NULL; /* pointer to last comma in pathcomp */ |
||
349 | RO_extra_block *ef_spark; /* pointer Acorn FTYPE ef block */ |
||
350 | #endif |
||
351 | int killed_ddot = FALSE; /* is set when skipping "../" pathcomp */ |
||
352 | int error = MPN_OK; |
||
353 | register unsigned workch; /* hold the character being tested */ |
||
354 | |||
355 | |||
356 | /*--------------------------------------------------------------------------- |
||
357 | Initialize various pointers and counters and stuff. |
||
358 | ---------------------------------------------------------------------------*/ |
||
359 | |||
360 | if (G.pInfo->vollabel) |
||
361 | return MPN_VOL_LABEL; /* can't set disk volume labels in Netware */ |
||
362 | |||
363 | /* can create path as long as not just freshening, or if user told us */ |
||
364 | G.create_dirs = (!uO.fflag || renamed); |
||
365 | |||
366 | created_dir = FALSE; /* not yet */ |
||
367 | |||
368 | /* user gave full pathname: don't prepend rootpath */ |
||
369 | renamed_fullpath = (renamed && (*G.filename == '/')); |
||
370 | |||
371 | if (checkdir(__G__ (char *)NULL, INIT) == MPN_NOMEM) |
||
372 | return MPN_NOMEM; /* initialize path buffer, unless no memory */ |
||
373 | |||
374 | *pathcomp = '\0'; /* initialize translation buffer */ |
||
375 | pp = pathcomp; /* point to translation buffer */ |
||
376 | if (uO.jflag) /* junking directories */ |
||
377 | cp = (char *)strrchr(G.filename, '/'); |
||
378 | if (cp == (char *)NULL) /* no '/' or not junking dirs */ |
||
379 | cp = G.filename; /* point to internal zipfile-member pathname */ |
||
380 | else |
||
381 | ++cp; /* point to start of last component of path */ |
||
382 | |||
383 | /*--------------------------------------------------------------------------- |
||
384 | Begin main loop through characters in filename. |
||
385 | ---------------------------------------------------------------------------*/ |
||
386 | |||
387 | while ((workch = (uch)*cp++) != 0) { |
||
388 | |||
389 | switch (workch) { |
||
390 | case '/': /* can assume -j flag not given */ |
||
391 | *pp = '\0'; |
||
392 | if (strcmp(pathcomp, ".") == 0) { |
||
393 | /* don't bother appending "./" to the path */ |
||
394 | *pathcomp = '\0'; |
||
395 | } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) { |
||
396 | /* "../" dir traversal detected, skip over it */ |
||
397 | *pathcomp = '\0'; |
||
398 | killed_ddot = TRUE; /* set "show message" flag */ |
||
399 | } |
||
400 | /* when path component is not empty, append it now */ |
||
401 | if (*pathcomp != '\0' && |
||
402 | ((error = checkdir(__G__ pathcomp, APPEND_DIR)) |
||
403 | & MPN_MASK) > MPN_INF_TRUNC) |
||
404 | return error; |
||
405 | pp = pathcomp; /* reset conversion buffer for next piece */ |
||
406 | lastsemi = (char *)NULL; /* leave direct. semi-colons alone */ |
||
407 | break; |
||
408 | |||
409 | case ';': /* VMS version (or DEC-20 attrib?) */ |
||
410 | lastsemi = pp; |
||
411 | *pp++ = ';'; /* keep for now; remove VMS ";##" */ |
||
412 | break; /* later, if requested */ |
||
413 | |||
414 | #ifdef ACORN_FTYPE_NFS |
||
415 | case ',': /* NFS filetype extension */ |
||
416 | lastcomma = pp; |
||
417 | *pp++ = ','; /* keep for now; may need to remove */ |
||
418 | break; /* later, if requested */ |
||
419 | #endif |
||
420 | |||
421 | default: |
||
422 | /* allow European characters in filenames: */ |
||
423 | if (isprint(workch) || (128 <= workch && workch <= 254)) |
||
424 | *pp++ = (char)workch; |
||
425 | } /* end switch */ |
||
426 | |||
427 | } /* end while loop */ |
||
428 | |||
429 | /* Show warning when stripping insecure "parent dir" path components */ |
||
430 | if (killed_ddot && QCOND2) { |
||
431 | Info(slide, 0, ((char *)slide, |
||
432 | "warning: skipped \"../\" path component(s) in %s\n", |
||
433 | FnFilter1(G.filename))); |
||
434 | if (!(error & ~MPN_MASK)) |
||
435 | error = (error & MPN_MASK) | PK_WARN; |
||
436 | } |
||
437 | |||
438 | /*--------------------------------------------------------------------------- |
||
439 | Report if directory was created (and no file to create: filename ended |
||
440 | in '/'), check name to be sure it exists, and combine path and name be- |
||
441 | fore exiting. |
||
442 | ---------------------------------------------------------------------------*/ |
||
443 | |||
444 | if (G.filename[strlen(G.filename) - 1] == '/') { |
||
445 | checkdir(__G__ G.filename, GETPATH); |
||
446 | if (created_dir) { |
||
447 | if (QCOND2) { |
||
448 | Info(slide, 0, ((char *)slide, " creating: %s\n", |
||
449 | FnFilter1(G.filename))); |
||
450 | } |
||
451 | #if !defined(NO_CHMOD) && !defined(NLM) |
||
452 | /* In NetWare, chmod does not work on directories */ |
||
453 | /* set approx. dir perms (make sure can still read/write in dir) */ |
||
454 | if (chmod(G.filename, (0xffff & G.pInfo->file_attr) | 0700)) |
||
455 | perror("chmod (directory attributes) error"); |
||
456 | #endif |
||
457 | /* set dir time (note trailing '/') */ |
||
458 | return (error & ~MPN_MASK) | MPN_CREATED_DIR; |
||
459 | } |
||
460 | /* dir existed already; don't look for data to extract */ |
||
461 | return (error & ~MPN_MASK) | MPN_INF_SKIP; |
||
462 | } |
||
463 | |||
464 | *pp = '\0'; /* done with pathcomp: terminate it */ |
||
465 | |||
466 | /* if not saving them, remove VMS version numbers (appended ";###") */ |
||
467 | if (!uO.V_flag && lastsemi) { |
||
468 | pp = lastsemi + 1; |
||
469 | while (isdigit((uch)(*pp))) |
||
470 | ++pp; |
||
471 | if (*pp == '\0') /* only digits between ';' and end: nuke */ |
||
472 | *lastsemi = '\0'; |
||
473 | } |
||
474 | |||
475 | #ifdef ACORN_FTYPE_NFS |
||
476 | /* translate Acorn filetype information if asked to do so */ |
||
477 | if (uO.acorn_nfs_ext && |
||
478 | (ef_spark = (RO_extra_block *) |
||
479 | getRISCOSexfield(G.extra_field, G.lrec.extra_field_length)) |
||
480 | != (RO_extra_block *)NULL) |
||
481 | { |
||
482 | /* file *must* have a RISC OS extra field */ |
||
483 | long ft = (long)makelong(ef_spark->loadaddr); |
||
484 | /*32-bit*/ |
||
485 | if (lastcomma) { |
||
486 | pp = lastcomma + 1; |
||
487 | while (isxdigit((uch)(*pp))) ++pp; |
||
488 | if (pp == lastcomma+4 && *pp == '\0') *lastcomma='\0'; /* nuke */ |
||
489 | } |
||
490 | if ((ft & 1<<31)==0) ft=0x000FFD00; |
||
491 | sprintf(pathcomp+strlen(pathcomp), ",%03x", (int)(ft>>8) & 0xFFF); |
||
492 | } |
||
493 | #endif /* ACORN_FTYPE_NFS */ |
||
494 | |||
495 | if (*pathcomp == '\0') { |
||
496 | Info(slide, 1, ((char *)slide, "mapname: conversion of %s failed\n", |
||
497 | FnFilter1(G.filename))); |
||
498 | return (error & ~MPN_MASK) | MPN_ERR_SKIP; |
||
499 | } |
||
500 | |||
501 | checkdir(__G__ pathcomp, APPEND_NAME); /* returns 1 if truncated: care? */ |
||
502 | checkdir(__G__ G.filename, GETPATH); |
||
503 | |||
504 | return error; |
||
505 | |||
506 | } /* end function mapname() */ |
||
507 | |||
508 | |||
509 | |||
510 | |||
511 | /***********************/ |
||
512 | /* Function checkdir() */ |
||
513 | /***********************/ |
||
514 | |||
515 | int checkdir(__G__ pathcomp, flag) |
||
516 | __GDEF |
||
517 | char *pathcomp; |
||
518 | int flag; |
||
519 | /* |
||
520 | * returns: |
||
521 | * MPN_OK - no problem detected |
||
522 | * MPN_INF_TRUNC - (on APPEND_NAME) truncated filename |
||
523 | * MPN_INF_SKIP - path doesn't exist, not allowed to create |
||
524 | * MPN_ERR_SKIP - path doesn't exist, tried to create and failed; or path |
||
525 | * exists and is not a directory, but is supposed to be |
||
526 | * MPN_ERR_TOOLONG - path is too long |
||
527 | * MPN_NOMEM - can't allocate memory for filename buffers |
||
528 | */ |
||
529 | { |
||
530 | static int rootlen = 0; /* length of rootpath */ |
||
531 | static char *rootpath; /* user's "extract-to" directory */ |
||
532 | static char *buildpath; /* full path (so far) to extracted file */ |
||
533 | static char *end; /* pointer to end of buildpath ('\0') */ |
||
534 | |||
535 | # define FN_MASK 7 |
||
536 | # define FUNCTION (flag & FN_MASK) |
||
537 | |||
538 | |||
539 | /*--------------------------------------------------------------------------- |
||
540 | APPEND_DIR: append the path component to the path being built and check |
||
541 | for its existence. If doesn't exist and we are creating directories, do |
||
542 | so for this one; else signal success or error as appropriate. |
||
543 | ---------------------------------------------------------------------------*/ |
||
544 | |||
545 | if (FUNCTION == APPEND_DIR) { |
||
546 | int too_long = FALSE; |
||
547 | #ifdef SHORT_NAMES |
||
548 | char *old_end = end; |
||
549 | #endif |
||
550 | |||
551 | Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp))); |
||
552 | while ((*end = *pathcomp++) != '\0') |
||
553 | ++end; |
||
554 | #ifdef SHORT_NAMES /* path components restricted to 14 chars, typically */ |
||
555 | if ((end-old_end) > FILENAME_MAX) /* GRR: proper constant? */ |
||
556 | *(end = old_end + FILENAME_MAX) = '\0'; |
||
557 | #endif |
||
558 | |||
559 | /* GRR: could do better check, see if overrunning buffer as we go: |
||
560 | * check end-buildpath after each append, set warning variable if |
||
561 | * within 20 of FILNAMSIZ; then if var set, do careful check when |
||
562 | * appending. Clear variable when begin new path. */ |
||
563 | |||
564 | if ((end-buildpath) > FILNAMSIZ-3) /* need '/', one-char name, '\0' */ |
||
565 | too_long = TRUE; /* check if extracting directory? */ |
||
566 | if (stat(buildpath, &G.statbuf)) { /* path doesn't exist */ |
||
567 | if (!G.create_dirs) { /* told not to create (freshening) */ |
||
568 | free(buildpath); |
||
569 | /* path doesn't exist: nothing to do */ |
||
570 | return MPN_INF_SKIP; |
||
571 | } |
||
572 | if (too_long) { |
||
573 | Info(slide, 1, ((char *)slide, |
||
574 | "checkdir error: path too long: %s\n", |
||
575 | FnFilter1(buildpath))); |
||
576 | free(buildpath); |
||
577 | /* no room for filenames: fatal */ |
||
578 | return MPN_ERR_TOOLONG; |
||
579 | } |
||
580 | if (mkdir(buildpath) == -1) { /* create the directory */ |
||
581 | Info(slide, 1, ((char *)slide, |
||
582 | "checkdir error: cannot create %s\n\ |
||
583 | unable to process %s.\n", |
||
584 | FnFilter2(buildpath), FnFilter1(G.filename))); |
||
585 | free(buildpath); |
||
586 | /* path didn't exist, tried to create, failed */ |
||
587 | return MPN_ERR_SKIP; |
||
588 | } |
||
589 | created_dir = TRUE; |
||
590 | } else if (!S_ISDIR(G.statbuf.st_mode)) { |
||
591 | Info(slide, 1, ((char *)slide, |
||
592 | "checkdir error: %s exists but is not directory\n\ |
||
593 | unable to process %s.\n", |
||
594 | FnFilter2(buildpath), FnFilter1(G.filename))); |
||
595 | free(buildpath); |
||
596 | /* path existed but wasn't dir */ |
||
597 | return MPN_ERR_SKIP; |
||
598 | } |
||
599 | if (too_long) { |
||
600 | Info(slide, 1, ((char *)slide, |
||
601 | "checkdir error: path too long: %s\n", FnFilter1(buildpath))); |
||
602 | free(buildpath); |
||
603 | /* no room for filenames: fatal */ |
||
604 | return MPN_ERR_TOOLONG; |
||
605 | } |
||
606 | *end++ = '/'; |
||
607 | *end = '\0'; |
||
608 | Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath))); |
||
609 | return MPN_OK; |
||
610 | |||
611 | } /* end if (FUNCTION == APPEND_DIR) */ |
||
612 | |||
613 | /*--------------------------------------------------------------------------- |
||
614 | GETPATH: copy full path to the string pointed at by pathcomp, and free |
||
615 | buildpath. |
||
616 | ---------------------------------------------------------------------------*/ |
||
617 | |||
618 | if (FUNCTION == GETPATH) { |
||
619 | strcpy(pathcomp, buildpath); |
||
620 | Trace((stderr, "getting and freeing path [%s]\n", |
||
621 | FnFilter1(pathcomp))); |
||
622 | free(buildpath); |
||
623 | buildpath = end = (char *)NULL; |
||
624 | return MPN_OK; |
||
625 | } |
||
626 | |||
627 | /*--------------------------------------------------------------------------- |
||
628 | APPEND_NAME: assume the path component is the filename; append it and |
||
629 | return without checking for existence. |
||
630 | ---------------------------------------------------------------------------*/ |
||
631 | |||
632 | if (FUNCTION == APPEND_NAME) { |
||
633 | #ifdef SHORT_NAMES |
||
634 | char *old_end = end; |
||
635 | #endif |
||
636 | |||
637 | Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp))); |
||
638 | while ((*end = *pathcomp++) != '\0') { |
||
639 | ++end; |
||
640 | #ifdef SHORT_NAMES /* truncate name at 14 characters, typically */ |
||
641 | if ((end-old_end) > FILENAME_MAX) /* GRR: proper constant? */ |
||
642 | *(end = old_end + FILENAME_MAX) = '\0'; |
||
643 | #endif |
||
644 | if ((end-buildpath) >= FILNAMSIZ) { |
||
645 | *--end = '\0'; |
||
646 | Info(slide, 0x201, ((char *)slide, |
||
647 | "checkdir warning: path too long; truncating\n\ |
||
648 | %s\n -> %s\n", |
||
649 | FnFilter1(G.filename), FnFilter2(buildpath))); |
||
650 | return MPN_INF_TRUNC; /* filename truncated */ |
||
651 | } |
||
652 | } |
||
653 | Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath))); |
||
654 | /* could check for existence here, prompt for new name... */ |
||
655 | return MPN_OK; |
||
656 | } |
||
657 | |||
658 | /*--------------------------------------------------------------------------- |
||
659 | INIT: allocate and initialize buffer space for the file currently being |
||
660 | extracted. If file was renamed with an absolute path, don't prepend the |
||
661 | extract-to path. |
||
662 | ---------------------------------------------------------------------------*/ |
||
663 | |||
664 | /* GRR: for VMS and TOPS-20, add up to 13 to strlen */ |
||
665 | |||
666 | if (FUNCTION == INIT) { |
||
667 | Trace((stderr, "initializing buildpath to ")); |
||
668 | #ifdef ACORN_FTYPE_NFS |
||
669 | if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+ |
||
670 | (uO.acorn_nfs_ext ? 5 : 1))) |
||
671 | #else |
||
672 | if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+1)) |
||
673 | #endif |
||
674 | == (char *)NULL) |
||
675 | return MPN_NOMEM; |
||
676 | if ((rootlen > 0) && !renamed_fullpath) { |
||
677 | strcpy(buildpath, rootpath); |
||
678 | end = buildpath + rootlen; |
||
679 | } else { |
||
680 | *buildpath = '\0'; |
||
681 | end = buildpath; |
||
682 | } |
||
683 | Trace((stderr, "[%s]\n", FnFilter1(buildpath))); |
||
684 | return MPN_OK; |
||
685 | } |
||
686 | |||
687 | /*--------------------------------------------------------------------------- |
||
688 | ROOT: if appropriate, store the path in rootpath and create it if neces- |
||
689 | sary; else assume it's a zipfile member and return. This path segment |
||
690 | gets used in extracting all members from every zipfile specified on the |
||
691 | command line. |
||
692 | ---------------------------------------------------------------------------*/ |
||
693 | |||
694 | #if (!defined(SFX) || defined(SFX_EXDIR)) |
||
695 | if (FUNCTION == ROOT) { |
||
696 | Trace((stderr, "initializing root path to [%s]\n", |
||
697 | FnFilter1(pathcomp))); |
||
698 | if (pathcomp == (char *)NULL) { |
||
699 | rootlen = 0; |
||
700 | return MPN_OK; |
||
701 | } |
||
702 | if ((rootlen = strlen(pathcomp)) > 0) { |
||
703 | if (pathcomp[rootlen-1] == '/') { |
||
704 | pathcomp[--rootlen] = '\0'; |
||
705 | } |
||
706 | if (rootlen > 0 && (stat(pathcomp, &G.statbuf) || |
||
707 | !S_ISDIR(G.statbuf.st_mode))) /* path does not exist */ |
||
708 | { |
||
709 | if (!G.create_dirs /* || iswild(pathcomp) */ ) { |
||
710 | rootlen = 0; |
||
711 | /* skip (or treat as stored file) */ |
||
712 | return MPN_INF_SKIP; |
||
713 | } |
||
714 | /* create the directory (could add loop here to scan pathcomp |
||
715 | * and create more than one level, but why really necessary?) */ |
||
716 | if (mkdir(pathcomp) == -1) { |
||
717 | Info(slide, 1, ((char *)slide, |
||
718 | "checkdir: cannot create extraction directory: %s\n", |
||
719 | FnFilter1(pathcomp))); |
||
720 | rootlen = 0; |
||
721 | /* path didn't exist, tried to create, and failed: */ |
||
722 | /* file exists, or 2+ subdirectory levels required */ |
||
723 | return MPN_ERR_SKIP; |
||
724 | } |
||
725 | } |
||
726 | if ((rootpath = (char *)malloc(rootlen+2)) == (char *)NULL) { |
||
727 | rootlen = 0; |
||
728 | return MPN_NOMEM; |
||
729 | } |
||
730 | strcpy(rootpath, pathcomp); |
||
731 | rootpath[rootlen++] = '/'; |
||
732 | rootpath[rootlen] = '\0'; |
||
733 | Trace((stderr, "rootpath now = [%s]\n", FnFilter1(rootpath))); |
||
734 | } |
||
735 | return MPN_OK; |
||
736 | } |
||
737 | #endif /* !SFX || SFX_EXDIR */ |
||
738 | |||
739 | /*--------------------------------------------------------------------------- |
||
740 | END: free rootpath, immediately prior to program exit. |
||
741 | ---------------------------------------------------------------------------*/ |
||
742 | |||
743 | if (FUNCTION == END) { |
||
744 | Trace((stderr, "freeing rootpath\n")); |
||
745 | if (rootlen > 0) { |
||
746 | free(rootpath); |
||
747 | rootlen = 0; |
||
748 | } |
||
749 | return MPN_OK; |
||
750 | } |
||
751 | |||
752 | return MPN_INVALID; /* should never reach */ |
||
753 | |||
754 | } /* end function checkdir() */ |
||
755 | |||
756 | |||
757 | |||
758 | /****************************/ |
||
759 | /* Function close_outfile() */ |
||
760 | /****************************/ |
||
761 | |||
762 | void close_outfile(__G) /* GRR: change to return PK-style warning level */ |
||
763 | __GDEF |
||
764 | { |
||
765 | fclose(G.outfile); |
||
766 | |||
767 | /* skip restoring time stamps on user's request */ |
||
768 | if (uO.D_flag <= 1) { |
||
769 | WORD date = G.lrec.last_mod_dos_datetime >> 16; |
||
770 | WORD time = G.lrec.last_mod_dos_datetime & 0xffff; |
||
771 | static struct ModifyStructure changeBuffer; |
||
772 | |||
773 | /* set the file's access and modification times */ |
||
774 | changeBuffer.MLastAccessedDate = date; |
||
775 | changeBuffer.MLastUpdatedDate = date; |
||
776 | changeBuffer.MLastUpdatedTime = time; |
||
777 | if (ChangeDirectoryEntry(G.filename, &changeBuffer, |
||
778 | MLastAccessedDateBit | MLastUpdatedDateBit | MLastUpdatedTimeBit, |
||
779 | 0)) |
||
780 | { |
||
781 | if (uO.qflag) |
||
782 | Info(slide, 0x201, ((char *)slide, |
||
783 | "warning: cannot set times for %s\n", |
||
784 | FnFilter1(G.filename))); |
||
785 | else |
||
786 | Info(slide, 0x201, ((char *)slide, |
||
787 | " (warning) cannot set times")); |
||
788 | } |
||
789 | } |
||
790 | |||
791 | /*--------------------------------------------------------------------------- |
||
792 | Change the file permissions from default ones to those stored in the |
||
793 | zipfile. |
||
794 | ---------------------------------------------------------------------------*/ |
||
795 | |||
796 | if (chmod(G.filename, 0xffff & G.pInfo->file_attr)) |
||
797 | perror("chmod (file attributes) error"); |
||
798 | |||
799 | } /* end function close_outfile() */ |
||
800 | |||
801 | |||
802 | #ifdef TIMESTAMP |
||
803 | |||
804 | /***************************/ |
||
805 | /* Function stamp_file() */ |
||
806 | /***************************/ |
||
807 | |||
808 | int stamp_file(fname, modtime) |
||
809 | ZCONST char *fname; |
||
810 | time_t modtime; |
||
811 | { |
||
812 | ztimbuf tp; |
||
813 | |||
814 | tp.modtime = tp.actime = modtime; |
||
815 | return (utime(fname, &tp)); |
||
816 | |||
817 | } /* end function stamp_file() */ |
||
818 | |||
819 | #endif /* TIMESTAMP */ |
||
820 | |||
821 | |||
822 | /************************/ |
||
823 | /* Function version() */ |
||
824 | /************************/ |
||
825 | |||
826 | void version(__G) |
||
827 | __GDEF |
||
828 | { |
||
829 | int len; |
||
830 | #if defined(__IBMC__) || defined(__WATCOMC__) || defined(_MSC_VER) |
||
831 | char buf[80]; |
||
832 | #endif |
||
833 | |||
834 | len = sprintf((char *)slide, LoadFarString(CompiledWith), |
||
835 | |||
836 | #if defined(__GNUC__) |
||
837 | # ifdef __EMX__ /* __EMX__ is defined as "1" only (sigh) */ |
||
838 | "emx+gcc ", __VERSION__, |
||
839 | # else |
||
840 | "gcc/2 ", __VERSION__, |
||
841 | # endif |
||
842 | #elif defined(__WATCOMC__) |
||
843 | "Watcom C", (sprintf(buf, " (__WATCOMC__ = %d)", __WATCOMC__), buf), |
||
844 | #elif defined(__TURBOC__) |
||
845 | # ifdef __BORLANDC__ |
||
846 | "Borland C++", |
||
847 | # if (__BORLANDC__ < 0x0460) |
||
848 | " 1.0", |
||
849 | # elif (__BORLANDC__ == 0x0460) |
||
850 | " 1.5", /* from Kai Uwe: three less than DOS */ |
||
851 | # else |
||
852 | " 2.0", /* (__BORLANDC__ == 0x0500)? */ |
||
853 | # endif |
||
854 | # else |
||
855 | "Turbo C", /* these are probably irrelevant */ |
||
856 | # if (__TURBOC__ >= 661) |
||
857 | "++ 1.0 or later", |
||
858 | # elif (__TURBOC__ == 661) |
||
859 | " 3.0?", |
||
860 | # elif (__TURBOC__ == 397) |
||
861 | " 2.0", |
||
862 | # else |
||
863 | " 1.0 or 1.5?", |
||
864 | # endif |
||
865 | # endif |
||
866 | #elif defined(MSC) |
||
867 | "Microsoft C ", |
||
868 | # ifdef _MSC_VER |
||
869 | (sprintf(buf, "%d.%02d", _MSC_VER/100, _MSC_VER%100), buf), |
||
870 | # else |
||
871 | "5.1 or earlier", |
||
872 | # endif |
||
873 | #else |
||
874 | "unknown compiler", "", |
||
875 | #endif /* ?compilers */ |
||
876 | |||
877 | "NetWare", |
||
878 | " (32-bit)", |
||
879 | |||
880 | #ifdef __DATE__ |
||
881 | " on ", __DATE__ |
||
882 | #else |
||
883 | "", "" |
||
884 | #endif |
||
885 | ); |
||
886 | |||
887 | (*G.message)((zvoid *)&G, slide, (ulg)len, 0); |
||
888 | /* MSC can't handle huge macro expansions */ |
||
889 | |||
890 | } /* end function version() */ |
||
891 | |||
892 | |||
893 | #ifdef MORE |
||
894 | |||
895 | /*************************/ |
||
896 | /* Function screensize() */ |
||
897 | /*************************/ |
||
898 | |||
899 | int screensize(int *tt_rows, int *tt_cols) |
||
900 | { |
||
901 | WORD height; |
||
902 | WORD width; |
||
903 | |||
904 | if (GetSizeOfScreen(&height, &width) == 0) { |
||
905 | if (tt_rows != NULL) *tt_rows = height; |
||
906 | if (tt_cols != NULL) *tt_cols = width; |
||
907 | return 0; /* signal success */ |
||
908 | } else { |
||
909 | if (tt_rows != NULL) *tt_rows = 25; |
||
910 | if (tt_cols != NULL) *tt_cols = 80; |
||
911 | return 1; /* signal failure */ |
||
912 | } |
||
913 | } |
||
914 | |||
915 | #endif /* MORE */>=>31)==0)><31)==0)>=>=>3><3>6><6>6)) |