1 /*---------------------------------------------------------------------------
5 Unix-specific routines for use with Info-ZIP's UnZip 5.3 and later.
8 do_wild() <-- generic enough to put in fileio.c?
18 ---------------------------------------------------------------------------*/
21 #define UNZIP_INTERNAL
26 #else /* SCO Unix, AIX, DNIX, TI SysV, Coherent 4.x, ... */
27 # if defined(__convexc__) || defined(SYSV) || defined(CRAY) || defined(BSD4_4)
35 # if defined(_I386) || (defined(__COHERENT__) && (__COHERENT__ >= 0x420))
40 /* GRR: may need to uncomment this: */
42 #if defined(_POSIX_VERSION)
52 # include <sys/ndir.h>
62 # define dirent direct
66 #ifdef ACORN_FTYPE_NFS
67 /* Acorn bits for NFS filetyping */
77 static int isRISCOSexfield
OF((uch
*extra_field
));
78 #endif /* ACORN_FTYPE_NFS */
80 static int created_dir
; /* used in mapname(), checkdir() */
81 static int renamed_fullpath
; /* ditto */
85 #ifdef NO_DIR /* for AT&T 3B1 */
87 #define opendir(path) fopen(path,"r")
88 #define closedir(dir) fclose(dir)
92 * Apparently originally by Rich Salz.
93 * Cleaned up and modified by James W. Birdsall.
95 struct dirent
*readdir(dirp
)
98 static struct dirent entry
;
104 if (fread(&entry
, sizeof (struct dirent
), 1, dirp
) == 0)
105 return (struct dirent
*)NULL
;
106 else if (entry
.d_ino
)
109 } /* end function readdir() */
114 /**********************/
115 /* Function do_wild() */ /* for porting: dir separator; match(ignore_case) */
116 /**********************/
118 char *do_wild(__G__ wildspec
)
120 char *wildspec
; /* only used first time on a given dir */
122 static DIR *dir
= (DIR *)NULL
;
123 static char *dirname
, *wildname
, matchname
[FILNAMSIZ
];
124 static int firstcall
=TRUE
, have_dirname
, dirnamelen
;
128 /* Even when we're just returning wildspec, we *always* do so in
129 * matchname[]--calling routine is allowed to append four characters
130 * to the returned string, and wildspec may be a pointer to argv[].
132 if (firstcall
) { /* first call: must initialize everything */
135 if (!iswild(wildspec
)) {
136 strcpy(matchname
, wildspec
);
137 have_dirname
= FALSE
;
142 /* break the wildspec into a directory part and a wildcard filename */
143 if ((wildname
= strrchr(wildspec
, '/')) == (char *)NULL
) {
146 have_dirname
= FALSE
;
149 ++wildname
; /* point at character after '/' */
150 dirnamelen
= wildname
- wildspec
;
151 if ((dirname
= (char *)malloc(dirnamelen
+1)) == (char *)NULL
) {
152 Info(slide
, 0x201, ((char *)slide
,
153 "warning: cannot allocate wildcard buffers\n"));
154 strcpy(matchname
, wildspec
);
155 return matchname
; /* but maybe filespec was not a wildcard */
157 strncpy(dirname
, wildspec
, dirnamelen
);
158 dirname
[dirnamelen
] = '\0'; /* terminate for strcpy below */
162 if ((dir
= opendir(dirname
)) != (DIR *)NULL
) {
163 while ((file
= readdir(dir
)) != (struct dirent
*)NULL
) {
164 Trace((stderr
, "do_wild: readdir returns %s\n", file
->d_name
));
165 if (file
->d_name
[0] == '.' && wildname
[0] != '.')
166 continue; /* Unix: '*' and '?' do not match leading dot */
167 if (match(file
->d_name
, wildname
, 0) && /* 0 == case sens. */
168 /* skip "." and ".." directory entries */
169 strcmp(file
->d_name
, ".") && strcmp(file
->d_name
, "..")) {
170 Trace((stderr
, "do_wild: match() succeeds\n"));
172 strcpy(matchname
, dirname
);
173 strcpy(matchname
+dirnamelen
, file
->d_name
);
175 strcpy(matchname
, file
->d_name
);
179 /* if we get to here directory is exhausted, so close it */
184 /* return the raw wildspec in case that works (e.g., directory not
185 * searchable, but filespec was not wild and file is readable) */
186 strcpy(matchname
, wildspec
);
190 /* last time through, might have failed opendir but returned raw wildspec */
191 if (dir
== (DIR *)NULL
) {
192 firstcall
= TRUE
; /* nothing left to try--reset for new wildspec */
198 /* If we've gotten this far, we've read and matched at least one entry
199 * successfully (in a previous call), so dirname has been copied into
202 while ((file
= readdir(dir
)) != (struct dirent
*)NULL
) {
203 Trace((stderr
, "do_wild: readdir returns %s\n", file
->d_name
));
204 if (file
->d_name
[0] == '.' && wildname
[0] != '.')
205 continue; /* Unix: '*' and '?' do not match leading dot */
206 if (match(file
->d_name
, wildname
, 0)) { /* 0 == don't ignore case */
207 Trace((stderr
, "do_wild: match() succeeds\n"));
209 /* strcpy(matchname, dirname); */
210 strcpy(matchname
+dirnamelen
, file
->d_name
);
212 strcpy(matchname
, file
->d_name
);
217 closedir(dir
); /* have read at least one dir entry; nothing left */
219 firstcall
= TRUE
; /* reset for new wildspec */
224 } /* end function do_wild() */
232 /**********************/
233 /* Function mapattr() */
234 /**********************/
239 ulg tmp
= G
.crec
.external_file_attributes
;
241 G
.pInfo
->file_attr
= 0;
242 /* initialized to 0 for check in "default" branch below... */
244 switch (G
.pInfo
->hostnum
) {
246 tmp
= (unsigned)(tmp
>>17 & 7); /* Amiga RWE bits */
247 G
.pInfo
->file_attr
= (unsigned)(tmp
<<6 | tmp
<<3 | tmp
);
256 G
.pInfo
->file_attr
= (unsigned)(tmp
>> 16);
257 if (G
.pInfo
->file_attr
!= 0 || !G
.extra_field
) {
260 /* Some (non-Info-ZIP) implementations of Zip for Unix and
261 * VMS (and probably others ??) leave 0 in the upper 16-bit
262 * part of the external_file_attributes field. Instead, they
263 * store file permission attributes in some extra field.
264 * As a work-around, we search for the presence of one of
265 * these extra fields and fall back to the MSDOS compatible
266 * part of external_file_attributes if one of the known
267 * e.f. types has been detected.
268 * Later, we might implement extraction of the permission
269 * bits from the VMS extra field. But for now, the work-around
270 * should be sufficient to provide "readable" extracted files.
271 * (For ASI Unix e.f., an experimental remap from the e.f.
272 * mode value IS already provided!)
276 uch
*ef
= G
.extra_field
;
277 unsigned ef_len
= G
.crec
.extra_field_length
;
280 while (!r
&& ef_len
>= EB_HEADSIZE
) {
282 ebLen
= (unsigned)makeword(ef
+EB_LEN
);
283 if (ebLen
> (ef_len
- EB_HEADSIZE
))
284 /* discoverd some e.f. inconsistency! */
288 if (ebLen
>= (EB_ASI_MODE
+2)) {
290 (unsigned)makeword(ef
+(EB_HEADSIZE
+EB_ASI_MODE
));
291 /* force stop of loop: */
292 ef_len
= (ebLen
+ EB_HEADSIZE
);
295 /* else: fall through! */
297 /* "found nondecypherable e.f. with perm. attr" */
302 ef_len
-= (ebLen
+ EB_HEADSIZE
);
303 ef
+= (ebLen
+ EB_HEADSIZE
);
309 /* all remaining cases: expand MSDOS read-only bit into write perms */
311 /* PKWARE's PKZip for Unix marks entries as FS_FAT_, but stores the
312 * Unix attributes in the upper 16 bits of the external attributes
313 * field, just like Info-ZIP's Zip for Unix. We try to use that
314 * value, after a check for consistency with the MSDOS attribute
317 G
.pInfo
->file_attr
= (unsigned)(tmp
>> 16);
324 /* read-only bit --> write perms; subdir bit --> dir exec bit */
325 tmp
= !(tmp
& 1) << 1 | (tmp
& 0x10) >> 4;
326 if ((G
.pInfo
->file_attr
& 0700) == (unsigned)(0400 | tmp
<<6))
327 /* keep previous G.pInfo->file_attr setting, when its "owner"
328 * part appears to be consistent with DOS attribute flags!
331 G
.pInfo
->file_attr
= (unsigned)(0444 | tmp
<<6 | tmp
<<3 | tmp
);
333 } /* end switch (host-OS-created-by) */
335 /* for originating systems with no concept of "group," "other," "system": */
336 umask( (int)(tmp
=umask(0)) ); /* apply mask to expanded r/w(/x) perms */
337 G
.pInfo
->file_attr
&= ~tmp
;
341 } /* end function mapattr() */
347 /************************/
348 /* Function mapname() */
349 /************************/
350 /* return 0 if no error, 1 if caution (filename */
351 int mapname(__G__ renamed
) /* truncated), 2 if warning (skip file because */
352 __GDEF
/* dir doesn't exist), 3 if error (skip file), */
353 int renamed
; /* or 10 if out of memory (skip file) */
354 { /* [also IZ_VOL_LABEL, IZ_CREATED_DIR] */
355 char pathcomp
[FILNAMSIZ
]; /* path-component buffer */
356 char *pp
, *cp
=(char *)NULL
; /* character pointers */
357 char *lastsemi
=(char *)NULL
; /* pointer to last semi-colon in pathcomp */
358 #ifdef ACORN_FTYPE_NFS
359 char *lastcomma
=(char *)NULL
; /* pointer to last comma in pathcomp */
361 int quote
= FALSE
; /* flags */
363 register unsigned workch
; /* hold the character being tested */
366 /*---------------------------------------------------------------------------
367 Initialize various pointers and counters and stuff.
368 ---------------------------------------------------------------------------*/
370 if (G
.pInfo
->vollabel
)
371 return IZ_VOL_LABEL
; /* can't set disk volume labels in Unix */
373 /* can create path as long as not just freshening, or if user told us */
374 G
.create_dirs
= (!uO
.fflag
|| renamed
);
376 created_dir
= FALSE
; /* not yet */
378 /* user gave full pathname: don't prepend rootpath */
379 renamed_fullpath
= (renamed
&& (*G
.filename
== '/'));
381 if (checkdir(__G__ (char *)NULL
, INIT
) == 10)
382 return 10; /* initialize path buffer, unless no memory */
384 *pathcomp
= '\0'; /* initialize translation buffer */
385 pp
= pathcomp
; /* point to translation buffer */
386 if (uO
.jflag
) /* junking directories */
387 cp
= (char *)strrchr(G
.filename
, '/');
388 if (cp
== (char *)NULL
) /* no '/' or not junking dirs */
389 cp
= G
.filename
; /* point to internal zipfile-member pathname */
391 ++cp
; /* point to start of last component of path */
393 /*---------------------------------------------------------------------------
394 Begin main loop through characters in filename.
395 ---------------------------------------------------------------------------*/
397 while ((workch
= (uch
)*cp
++) != 0) {
399 if (quote
) { /* if character quoted, */
400 *pp
++ = (char)workch
; /* include it literally */
404 case '/': /* can assume -j flag not given */
406 if ((error
= checkdir(__G__ pathcomp
, APPEND_DIR
)) > 1)
408 pp
= pathcomp
; /* reset conversion buffer for next piece */
409 lastsemi
= (char *)NULL
; /* leave directory semi-colons alone */
412 case ';': /* VMS version (or DEC-20 attrib?) */
414 *pp
++ = ';'; /* keep for now; remove VMS ";##" */
415 break; /* later, if requested */
417 #ifdef ACORN_FTYPE_NFS
418 case ',': /* NFS filetype extension */
420 *pp
++ = ','; /* keep for now; may need to remove */
421 break; /* later, if requested */
424 case '\026': /* control-V quote for special chars */
425 quote
= TRUE
; /* set flag for next character */
429 case ' ': /* change spaces to underscore under */
430 *pp
++ = '_'; /* MTS; leave as spaces under Unix */
435 /* allow European characters in filenames: */
436 if (isprint(workch
) || (128 <= workch
&& workch
<= 254))
437 *pp
++ = (char)workch
;
440 } /* end while loop */
442 *pp
= '\0'; /* done with pathcomp: terminate it */
444 /* if not saving them, remove VMS version numbers (appended ";###") */
445 if (!uO
.V_flag
&& lastsemi
) {
447 while (isdigit((uch
)(*pp
)))
449 if (*pp
== '\0') /* only digits between ';' and end: nuke */
453 #ifdef ACORN_FTYPE_NFS
454 /* translate Acorn filetype information if asked to do so */
455 if (uO
.acorn_nfs_ext
&& isRISCOSexfield(G
.extra_field
)) {
456 /* file *must* have a RISC OS extra field */
457 int ft
= (int)makelong(((RO_extra_block
*)G
.extra_field
)->loadaddr
);
461 while (isxdigit((uch
)(*pp
))) ++pp
;
462 if (pp
== lastcomma
+4 && *pp
== '\0') *lastcomma
='\0'; /* nuke */
464 if ((ft
& 1<<31)==0) ft
=0x000FFD00;
465 sprintf(pathcomp
+strlen(pathcomp
), ",%03x", ft
>>8 & 0xFFF);
467 #endif /* ACORN_FTYPE_NFS */
469 /*---------------------------------------------------------------------------
470 Report if directory was created (and no file to create: filename ended
471 in '/'), check name to be sure it exists, and combine path and name be-
473 ---------------------------------------------------------------------------*/
475 if (G
.filename
[strlen(G
.filename
) - 1] == '/') {
476 checkdir(__G__ G
.filename
, GETPATH
);
479 Info(slide
, 0, ((char *)slide
, " creating: %s\n",
483 /* set approx. dir perms (make sure can still read/write in dir) */
484 if (chmod(G
.filename
, (0xffff & G
.pInfo
->file_attr
) | 0700))
485 perror("chmod (directory attributes) error");
487 return IZ_CREATED_DIR
; /* set dir time (note trailing '/') */
489 return 2; /* dir existed already; don't look for data to extract */
492 if (*pathcomp
== '\0') {
493 Info(slide
, 1, ((char *)slide
, "mapname: conversion of %s failed\n",
498 checkdir(__G__ pathcomp
, APPEND_NAME
); /* returns 1 if truncated: care? */
499 checkdir(__G__ G
.filename
, GETPATH
);
503 } /* end function mapname() */
508 #if 0 /*========== NOTES ==========*/
510 extract
-to dir
: a
:path
/
511 buildpath
: path1
/path2
/ ... (NULL
-terminated
)
515 loop over chars in zipfile member name
516 checkdir(path component
, COMPONENT
| CREATEDIR
) --> map as required
?
517 (d
:/tmp
/unzip
/) (disk
:[tmp
.unzip
.)
518 (d
:/tmp
/unzip
/jj
/) (disk
:[tmp
.unzip
.jj
.)
519 (d
:/tmp
/unzip
/jj
/temp
/) (disk
:[tmp
.unzip
.jj
.temp
.)
520 finally add filename itself
and check
for existence
? (could use with rename
)
521 (d
:/tmp
/unzip
/jj
/temp
/msg
.outdir
) (disk
:[tmp
.unzip
.jj
.temp
]msg
.outdir
)
522 checkdir(name
, GETPATH
) --> copy path to name
and free space
529 /***********************/
530 /* Function checkdir() */
531 /***********************/
533 int checkdir(__G__ pathcomp
, flag
)
538 * returns: 1 - (on APPEND_NAME) truncated filename
539 * 2 - path doesn't exist, not allowed to create
540 * 3 - path doesn't exist, tried to create and failed; or
541 * path exists and is not a directory, but is supposed to be
542 * 4 - path is too long
543 * 10 - can't allocate memory for filename buffers
546 static int rootlen
= 0; /* length of rootpath */
547 static char *rootpath
; /* user's "extract-to" directory */
548 static char *buildpath
; /* full path (so far) to extracted file */
549 static char *end
; /* pointer to end of buildpath ('\0') */
552 # define FUNCTION (flag & FN_MASK)
556 /*---------------------------------------------------------------------------
557 APPEND_DIR: append the path component to the path being built and check
558 for its existence. If doesn't exist and we are creating directories, do
559 so for this one; else signal success or error as appropriate.
560 ---------------------------------------------------------------------------*/
562 if (FUNCTION
== APPEND_DIR
) {
563 int too_long
= FALSE
;
568 Trace((stderr
, "appending dir segment [%s]\n", pathcomp
));
569 while ((*end
= *pathcomp
++) != '\0')
571 #ifdef SHORT_NAMES /* path components restricted to 14 chars, typically */
572 if ((end
-old_end
) > FILENAME_MAX
) /* GRR: proper constant? */
573 *(end
= old_end
+ FILENAME_MAX
) = '\0';
576 /* GRR: could do better check, see if overrunning buffer as we go:
577 * check end-buildpath after each append, set warning variable if
578 * within 20 of FILNAMSIZ; then if var set, do careful check when
579 * appending. Clear variable when begin new path. */
581 if ((end
-buildpath
) > FILNAMSIZ
-3) /* need '/', one-char name, '\0' */
582 too_long
= TRUE
; /* check if extracting directory? */
583 if (stat(buildpath
, &G
.statbuf
)) { /* path doesn't exist */
584 if (!G
.create_dirs
) { /* told not to create (freshening) */
586 return 2; /* path doesn't exist: nothing to do */
589 Info(slide
, 1, ((char *)slide
,
590 "checkdir error: path too long: %s\n", buildpath
));
592 return 4; /* no room for filenames: fatal */
594 if (mkdir(buildpath
, 0777) == -1) { /* create the directory */
595 Info(slide
, 1, ((char *)slide
,
596 "checkdir error: cannot create %s\n\
597 unable to process %s.\n", buildpath
, G
.filename
));
599 return 3; /* path didn't exist, tried to create, failed */
602 } else if (!S_ISDIR(G
.statbuf
.st_mode
)) {
603 Info(slide
, 1, ((char *)slide
,
604 "checkdir error: %s exists but is not directory\n\
605 unable to process %s.\n", buildpath
, G
.filename
));
607 return 3; /* path existed but wasn't dir */
610 Info(slide
, 1, ((char *)slide
,
611 "checkdir error: path too long: %s\n", buildpath
));
613 return 4; /* no room for filenames: fatal */
617 Trace((stderr
, "buildpath now = [%s]\n", buildpath
));
620 } /* end if (FUNCTION == APPEND_DIR) */
622 /*---------------------------------------------------------------------------
623 GETPATH: copy full path to the string pointed at by pathcomp, and free
625 ---------------------------------------------------------------------------*/
627 if (FUNCTION
== GETPATH
) {
628 strcpy(pathcomp
, buildpath
);
629 Trace((stderr
, "getting and freeing path [%s]\n", pathcomp
));
631 buildpath
= end
= (char *)NULL
;
635 /*---------------------------------------------------------------------------
636 APPEND_NAME: assume the path component is the filename; append it and
637 return without checking for existence.
638 ---------------------------------------------------------------------------*/
640 if (FUNCTION
== APPEND_NAME
) {
645 Trace((stderr
, "appending filename [%s]\n", pathcomp
));
646 while ((*end
= *pathcomp
++) != '\0') {
648 #ifdef SHORT_NAMES /* truncate name at 14 characters, typically */
649 if ((end
-old_end
) > FILENAME_MAX
) /* GRR: proper constant? */
650 *(end
= old_end
+ FILENAME_MAX
) = '\0';
652 if ((end
-buildpath
) >= FILNAMSIZ
) {
654 Info(slide
, 0x201, ((char *)slide
,
655 "checkdir warning: path too long; truncating\n\
656 %s\n -> %s\n", G
.filename
, buildpath
));
657 return 1; /* filename truncated */
660 Trace((stderr
, "buildpath now = [%s]\n", buildpath
));
661 return 0; /* could check for existence here, prompt for new name... */
664 /*---------------------------------------------------------------------------
665 INIT: allocate and initialize buffer space for the file currently being
666 extracted. If file was renamed with an absolute path, don't prepend the
668 ---------------------------------------------------------------------------*/
670 /* GRR: for VMS and TOPS-20, add up to 13 to strlen */
672 if (FUNCTION
== INIT
) {
673 Trace((stderr
, "initializing buildpath to "));
674 #ifdef ACORN_FTYPE_NFS
675 if ((buildpath
= (char *)malloc(strlen(G
.filename
)+rootlen
+
676 (uO
.acorn_nfs_ext
? 5 : 1)))
678 if ((buildpath
= (char *)malloc(strlen(G
.filename
)+rootlen
+1))
682 if ((rootlen
> 0) && !renamed_fullpath
) {
683 strcpy(buildpath
, rootpath
);
684 end
= buildpath
+ rootlen
;
689 Trace((stderr
, "[%s]\n", buildpath
));
693 /*---------------------------------------------------------------------------
694 ROOT: if appropriate, store the path in rootpath and create it if neces-
695 sary; else assume it's a zipfile member and return. This path segment
696 gets used in extracting all members from every zipfile specified on the
698 ---------------------------------------------------------------------------*/
700 #if (!defined(SFX) || defined(SFX_EXDIR))
701 if (FUNCTION
== ROOT
) {
702 Trace((stderr
, "initializing root path to [%s]\n", pathcomp
));
703 if (pathcomp
== (char *)NULL
) {
707 if ((rootlen
= strlen(pathcomp
)) > 0) {
708 if (pathcomp
[rootlen
-1] == '/') {
709 pathcomp
[--rootlen
] = '\0';
711 if (rootlen
> 0 && (stat(pathcomp
, &G
.statbuf
) ||
712 !S_ISDIR(G
.statbuf
.st_mode
))) /* path does not exist */
714 if (!G
.create_dirs
/* || iswild(pathcomp) */ ) {
716 return 2; /* skip (or treat as stored file) */
718 /* create the directory (could add loop here to scan pathcomp
719 * and create more than one level, but why really necessary?) */
720 if (mkdir(pathcomp
, 0777) == -1) {
721 Info(slide
, 1, ((char *)slide
,
722 "checkdir: cannot create extraction directory: %s\n",
724 rootlen
= 0; /* path didn't exist, tried to create, and */
725 return 3; /* failed: file exists, or 2+ levels required */
728 if ((rootpath
= (char *)malloc(rootlen
+2)) == (char *)NULL
) {
732 strcpy(rootpath
, pathcomp
);
733 rootpath
[rootlen
++] = '/';
734 rootpath
[rootlen
] = '\0';
735 Trace((stderr
, "rootpath now = [%s]\n", rootpath
));
739 #endif /* !SFX || SFX_EXDIR */
741 /*---------------------------------------------------------------------------
742 END: free rootpath, immediately prior to program exit.
743 ---------------------------------------------------------------------------*/
745 if (FUNCTION
== END
) {
746 Trace((stderr
, "freeing rootpath\n"));
754 return 99; /* should never reach */
756 } /* end function checkdir() */
764 /********************/
765 /* Function mkdir() */
766 /********************/
768 int mkdir(path
, mode
)
770 int mode
; /* ignored */
772 * returns: 0 - successful
773 * -1 - failed (errno not set, however)
776 char command
[FILNAMSIZ
+40]; /* buffer for system() call */
778 /* GRR 930416: added single quotes around path to avoid bug with
779 * creating directories with ampersands in name; not yet tested */
780 sprintf(command
, "IFS=\" \t\n\" /bin/mkdir '%s' 2>/dev/null", path
);
786 #endif /* NO_MKDIR */
795 /**************************/
796 /* Function screenlines() */
797 /**************************/
801 char *envptr
, *getenv();
804 /* GRR: this is overly simplistic; should use winsize struct and
805 * appropriate TIOCGWINSZ ioctl(), assuming exists on enough systems
807 envptr
= getenv("LINES");
808 if (envptr
== (char *)NULL
|| (n
= atoi(envptr
)) < 5)
809 return 24; /* VT-100 assumed to be minimal hardware */
823 /****************************/
824 /* Function close_outfile() */
825 /****************************/
827 void close_outfile(__G
) /* GRR: change to return PK-style warning level */
832 unsigned eb_izux_flg
;
834 /*---------------------------------------------------------------------------
835 If symbolic links are supported, allocate a storage area, put the uncom-
836 pressed "data" in it, and create the link. Since we know it's a symbolic
837 link to start with, we shouldn't have to worry about overflowing unsigned
838 ints with unsigned longs.
839 ---------------------------------------------------------------------------*/
843 unsigned ucsize
= (unsigned)G
.lrec
.ucsize
;
844 char *linktarget
= (char *)malloc((unsigned)G
.lrec
.ucsize
+1);
846 fclose(G
.outfile
); /* close "data" file... */
847 G
.outfile
= fopen(G
.filename
, FOPR
); /* ...and reopen for reading */
848 if (!linktarget
|| fread(linktarget
, 1, ucsize
, G
.outfile
) !=
851 Info(slide
, 0x201, ((char *)slide
,
852 "warning: symbolic link (%s) failed\n", G
.filename
));
858 fclose(G
.outfile
); /* close "data" file for good... */
859 unlink(G
.filename
); /* ...and delete it */
860 linktarget
[ucsize
] = '\0';
862 Info(slide
, 0, ((char *)slide
, "-> %s ", linktarget
));
863 if (symlink(linktarget
, G
.filename
)) /* create the real link */
864 perror("symlink error");
866 return; /* can't set time on symlinks */
868 #endif /* SYMLINKS */
873 static void qlfix
OF((__GPRO__ uch
*ef_ptr
, unsigned ef_len
));
875 qlfix(__G__ G
.extra_field
, G
.lrec
.extra_field_length
);
879 /*---------------------------------------------------------------------------
880 Convert from MSDOS-format local time and date to Unix-format 32-bit GMT
881 time: adjust base year from 1980 to 1970, do usual conversions from
882 yy/mm/dd hh:mm:ss to elapsed seconds, and account for timezone and day-
883 light savings time differences. If we have a Unix extra field, however,
884 we're laughing: both mtime and atime are ours. On the other hand, we
885 then have to check for restoration of UID/GID.
886 ---------------------------------------------------------------------------*/
888 eb_izux_flg
= (G
.extra_field
? ef_scan_for_izux(G
.extra_field
,
889 G
.lrec
.extra_field_length
, 0, G
.lrec
.last_mod_dos_datetime
,
891 (G
.tz_is_valid
? &zt
: NULL
),
896 if (eb_izux_flg
& EB_UT_FL_MTIME
) {
897 TTrace((stderr
, "\nclose_outfile: Unix e.f. modif. time = %ld\n",
900 zt
.mtime
= dos_to_unix_time(G
.lrec
.last_mod_dos_datetime
);
902 if (eb_izux_flg
& EB_UT_FL_ATIME
) {
903 TTrace((stderr
, "close_outfile: Unix e.f. access time = %ld\n",
907 TTrace((stderr
, "\nclose_outfile: modification/access times = %ld\n",
911 /* if -X option was specified and we have UID/GID info, restore it */
912 if (uO
.X_flag
&& eb_izux_flg
& EB_UX2_VALID
) {
913 TTrace((stderr
, "close_outfile: restoring Unix UID/GID info\n"));
914 if (chown(G
.filename
, (uid_t
)z_uidgid
[0], (gid_t
)z_uidgid
[1]))
917 Info(slide
, 0x201, ((char *)slide
,
918 "warning: cannot set UID %d and/or GID %d for %s\n",
919 z_uidgid
[0], z_uidgid
[1], G
.filename
));
921 Info(slide
, 0x201, ((char *)slide
,
922 " (warning) cannot set UID %d and/or GID %d",
923 z_uidgid
[0], z_uidgid
[1]));
927 /* set the file's access and modification times */
928 if (utime(G
.filename
, (ztimbuf
*)&zt
)) {
931 Info(slide
, 0x201, ((char *)slide
, "... cannot set time for %s\n",
934 Info(slide
, 0x201, ((char *)slide
, "... cannot set time"));
937 Info(slide
, 0x201, ((char *)slide
,
938 "warning: cannot set times for %s\n", G
.filename
));
940 Info(slide
, 0x201, ((char *)slide
,
941 " (warning) cannot set times"));
945 /*---------------------------------------------------------------------------
946 Change the file permissions from default ones to those stored in the
948 ---------------------------------------------------------------------------*/
951 if (chmod(G
.filename
, 0xffff & G
.pInfo
->file_attr
))
952 perror("chmod (file attributes) error");
955 } /* end function close_outfile() */
962 #ifdef SET_DIR_ATTRIB
963 /* messages of code for setting directory attributes */
964 static char Far DirlistUidGidFailed
[] =
965 "warning: cannot set UID %d and/or GID %d for %s\n";
966 static char Far DirlistUtimeFailed
[] =
967 "warning: cannot set modification, access times for %s\n";
969 static char Far DirlistChmodFailed
[] =
970 "warning: cannot set permissions for %s\n";
974 int set_direc_attribs(__G__ d
)
980 if (d
->have_uidgid
&&
981 chown(d
->fn
, (uid_t
)d
->uidgid
[0], (gid_t
)d
->uidgid
[1]))
983 Info(slide
, 0x201, ((char *)slide
,
984 LoadFarString(DirlistUidGidFailed
),
985 d
->uidgid
[0], d
->uidgid
[1], d
->fn
));
989 if (utime(d
->fn
, &d
->u
.t2
)) {
990 Info(slide
, 0x201, ((char *)slide
,
991 LoadFarString(DirlistUtimeFailed
), d
->fn
));
996 if (chmod(d
->fn
, 0xffff & d
->perms
)) {
997 Info(slide
, 0x201, ((char *)slide
,
998 LoadFarString(DirlistChmodFailed
), d
->fn
));
999 /* perror("chmod (file attributes) error"); */
1003 #endif /* !NO_CHMOD */
1005 } /* end function set_directory_attributes() */
1007 #endif /* SET_DIR_ATTRIB */
1014 /***************************/
1015 /* Function stamp_file() */
1016 /***************************/
1018 int stamp_file(fname
, modtime
)
1024 tp
.modtime
= tp
.actime
= modtime
;
1025 return (utime(fname
, &tp
));
1027 } /* end function stamp_file() */
1029 #endif /* TIMESTAMP */
1036 /************************/
1037 /* Function version() */
1038 /************************/
1043 #if defined(CRAY) || defined(NX_CURRENT_COMPILER_RELEASE) || defined(NetBSD)
1045 #if defined(CRAY) || defined(NX_CURRENT_COMPILER_RELEASE)
1050 /* Pyramid, NeXT have problems with huge macro expansion, too: no Info() */
1051 sprintf((char *)slide
, LoadFarString(CompiledWith
),
1054 # ifdef NX_CURRENT_COMPILER_RELEASE
1055 (sprintf(buf1
, "NeXT DevKit %d.%02d ", NX_CURRENT_COMPILER_RELEASE
/100,
1056 NX_CURRENT_COMPILER_RELEASE%100
), buf1
),
1057 (strlen(__VERSION__
) > 8)? "(gcc)" :
1058 (sprintf(buf2
, "(gcc %s)", __VERSION__
), buf2
),
1060 "gcc ", __VERSION__
,
1063 # if defined(CRAY) && defined(_RELEASE)
1064 "cc ", (sprintf(buf1
, "version %d", _RELEASE
), buf1
),
1076 #if defined(sgi) || defined(__sgi)
1077 " (Silicon Graphics IRIX)",
1082 " (Sun SPARC/Solaris)",
1083 # else /* may or may not be SunOS */
1087 # if defined(sun386) || defined(i386)
1090 # if defined(mc68020) || defined(__mc68020__)
1092 # else /* mc68010 or mc68000: Sun 2 or earlier */
1110 #if defined(CRAY) || defined(cray)
1112 (sprintf(buf2
, " (Cray UNICOS release %d)", _UNICOS
), buf2
),
1117 #if defined(uts) || defined(UTS)
1122 " (NeXTStep/black)",
1124 " (NeXTStep for Intel)",
1126 #else /* the next dozen or so are somewhat order-dependent */
1145 (sprintf(buf1
, " (NetBSD 0.8%c)", (char)(NetBSD0_8
- 1 + 'A')), buf1
),
1148 (sprintf(buf1
, " (NetBSD 0.9%c)", (char)(NetBSD0_9
- 1 + 'A')), buf1
),
1151 (sprintf(buf1
, " (NetBSD 1.0%c)", (char)(NetBSD1_0
- 1 + 'A')), buf1
),
1153 (BSD4_4
== 0.5)? " (NetBSD before 0.9)" : " (NetBSD 1.1 or later)",
1159 (BSD4_4
== 0.5)? " (FreeBSD 1.x)" : " (FreeBSD 2.0 or later)",
1162 (BSD4_4
== 0.5)? " (BSD/386 1.0)" : " (BSD/386 1.1 or later)",
1165 (BSD4_4
== 1)? " (386BSD, post-4.4 release)" : " (386BSD)",
1167 #if defined(i486) || defined(__i486) || defined(__i486__)
1170 #if defined(i386) || defined(__i386) || defined(__i386__)
1182 # else /* __alpha? */
1207 #endif /* QNX Neutrino */
1213 #endif /* Pyramid */
1217 #endif /* BSDI BSD/386 */
1219 #endif /* FreeBSD */
1220 #endif /* SCO Xenix */
1221 #endif /* SCO Unix */
1241 (*G
.message
)((zvoid
*)&G
, slide
, (ulg
)strlen((char *)slide
), 0);
1243 } /* end function version() */
1253 long d_length
__attribute__ ((packed
)); /* file length */
1254 unsigned char d_access
__attribute__ ((packed
)); /* file access type */
1255 unsigned char d_type
__attribute__ ((packed
)); /* file type */
1256 long d_datalen
__attribute__ ((packed
)); /* data length */
1257 long d_reserved
__attribute__ ((packed
));/* Unused */
1258 short d_szname
__attribute__ ((packed
)); /* size of name */
1259 char d_name
[36] __attribute__ ((packed
));/* name area */
1260 long d_update
__attribute__ ((packed
)); /* last update */
1261 long d_refdate
__attribute__ ((packed
));
1262 long d_backup
__attribute__ ((packed
)); /* EOD */
1265 #define LONGID "QDOS02"
1266 #define EXTRALEN (sizeof(struct qdirect) + 8)
1267 #define JBLONGID "QZHD"
1268 #define JBEXTRALEN (sizeof(jbextra) - 4 * sizeof(char))
1271 char eb_header
[4] __attribute__ ((packed
)); /* place_holder */
1272 char longid
[8] __attribute__ ((packed
));
1273 struct qdirect header
__attribute__ ((packed
));
1277 char eb_header
[4]; /* place_holder */
1279 struct qdirect header
;
1284 /* The following two functions SH() and LG() convert big-endian short
1285 * and long numbers into native byte order. They are some kind of
1286 * counterpart to the generic UnZip's makeword() and makelong() functions.
1288 static ush
SH(ush val
)
1292 swapbuf
[1] = (uch
)(val
& 0xff);
1293 swapbuf
[0] = (uch
)(val
>> 8);
1294 return (*(ush
*)swapbuf
);
1299 static ulg
LG(ulg val
)
1301 /* convert the big-endian unsigned long number `val' to the machine
1302 * dependant representation
1306 swapbuf
[1] = SH((ush
)(val
& 0xffff));
1307 swapbuf
[0] = SH((ush
)(val
>> 16));
1308 return (*(ulg
*)swapbuf
);
1313 static void qlfix(__G__ ef_ptr
, ef_len
)
1318 while (ef_len
>= EB_HEADSIZE
)
1320 unsigned eb_id
= makeword(EB_ID
+ ef_ptr
);
1321 unsigned eb_len
= makeword(EB_LEN
+ ef_ptr
);
1323 if (eb_len
> (ef_len
- EB_HEADSIZE
)) {
1324 /* discovered some extra field inconsistency! */
1326 "qlfix: block length %u > rest ef_size %u\n", eb_len
,
1327 ef_len
- EB_HEADSIZE
));
1341 qdosextra
*extra
= (qdosextra
*)ef_ptr
;
1342 jbextra
*jbp
= (jbextra
*)ef_ptr
;
1344 if (!strncmp(extra
->longid
, LONGID
, strlen(LONGID
)))
1346 if (eb_len
!= EXTRALEN
)
1348 Info(slide
, 0x201, ((char *)slide
,
1349 "warning: invalid length in Qdos field for %s\n",
1352 Info(slide
, 0x201, ((char *)slide
,
1353 "warning: invalid length in Qdos field"));
1355 if (extra
->header
.d_type
)
1357 dlen
= extra
->header
.d_datalen
;
1361 if (!strncmp(jbp
->longid
, JBLONGID
, strlen(JBLONGID
)))
1363 if (eb_len
!= JBEXTRALEN
)
1365 Info(slide
, 0x201, ((char *)slide
,
1366 "warning: invalid length in QZ field for %s\n",
1369 Info(slide
, 0x201, ((char *)slide
,
1370 "warning: invalid length in QZ field"));
1371 if(jbp
->header
.d_type
)
1373 dlen
= jbp
->header
.d_datalen
;
1377 if ((long)LG(dlen
) > 0)
1379 G
.outfile
= fopen(G
.filename
,"r+");
1380 fseek(G
.outfile
, -8, SEEK_END
);
1381 fread(&ntc
, 8, 1, G
.outfile
);
1382 if(ntc
.id
!= *(long *)"XTcc")
1384 ntc
.id
= *(long *)"XTcc";
1386 fwrite (&ntc
, 8, 1, G
.outfile
);
1388 Info(slide
, 0x201, ((char *)slide
, "QData = %d", LG(dlen
)));
1391 return; /* finished, cancel further extra field scanning */
1395 Trace((stderr
,"qlfix: unknown extra field block, ID=%d\n",
1399 /* Skip this extra field block */
1400 ef_ptr
+= (eb_len
+ EB_HEADSIZE
);
1401 ef_len
-= (eb_len
+ EB_HEADSIZE
);
1409 #ifdef ACORN_FTYPE_NFS
1411 /* Acorn bits for NFS filetyping */
1413 static int isRISCOSexfield(uch
*extra_field
)
1415 if (extra_field
!= NULL
) {
1416 RO_extra_block
*block
= (RO_extra_block
*)extra_field
;
1418 makeword(block
->ID
) == EF_SPARK
&&
1419 (makeword(block
->size
) == 24 || makeword(block
->size
) == 20) &&
1420 makelong(block
->ID_2
) == 0x30435241 /* ARC0 */);
1424 #endif /* ACORN_FTYPE_NFS */