2 Copyright (c) 1990-2002 Info-ZIP. All rights reserved.
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
9 /*---------------------------------------------------------------------------
13 BeOS-specific routines for use with Info-ZIP's UnZip 5.30 and later.
14 (based on unix/unix.c)
16 Contains: do_wild() <-- generic enough to put in fileio.c?
30 ---------------------------------------------------------------------------*/
33 #define UNZIP_INTERNAL
37 #include <errno.h> /* Just make sure we've got a few things... */
38 #include <sys/types.h>
44 /* For the new post-DR8 file attributes */
46 #include <ByteOrder.h>
49 static uch
*scanBeOSexfield
OF((const uch
*ef_ptr
, unsigned ef_len
));
50 static int set_file_attrs( const char *, const unsigned char *, const off_t
);
51 static void setBeOSexfield
OF((const char *path
, uch
*extra_field
));
52 #ifdef BEOS_USE_PRINTEXFIELD
53 static void printBeOSexfield
OF((int isdir
, uch
*extra_field
));
55 #ifdef BEOS_ASSIGN_FILETYPE
56 static void assign_MIME( const char * );
59 #ifdef ACORN_FTYPE_NFS
60 /* Acorn bits for NFS filetyping */
70 #endif /* ACORN_FTYPE_NFS */
72 static int created_dir
; /* used in mapname(), checkdir() */
73 static int renamed_fullpath
; /* ditto */
77 /**********************/
78 /* Function do_wild() */ /* for porting: dir separator; match(ignore_case) */
79 /**********************/
81 char *do_wild(__G__ wildspec
)
83 ZCONST
char *wildspec
; /* only used first time on a given dir */
85 static DIR *wild_dir
= (DIR *)NULL
;
86 static ZCONST
char *wildname
;
87 static char *dirname
, matchname
[FILNAMSIZ
];
88 static int notfirstcall
=FALSE
, have_dirname
, dirnamelen
;
91 /* Even when we're just returning wildspec, we *always* do so in
92 * matchname[]--calling routine is allowed to append four characters
93 * to the returned string, and wildspec may be a pointer to argv[].
95 if (!notfirstcall
) { /* first call: must initialize everything */
98 if (!iswild(wildspec
)) {
99 strcpy(matchname
, wildspec
);
100 have_dirname
= FALSE
;
105 /* break the wildspec into a directory part and a wildcard filename */
106 if ((wildname
= strrchr(wildspec
, '/')) == (ZCONST
char *)NULL
) {
109 have_dirname
= FALSE
;
112 ++wildname
; /* point at character after '/' */
113 dirnamelen
= wildname
- wildspec
;
114 if ((dirname
= (char *)malloc(dirnamelen
+1)) == (char *)NULL
) {
115 Info(slide
, 0x201, ((char *)slide
,
116 "warning: cannot allocate wildcard buffers\n"));
117 strcpy(matchname
, wildspec
);
118 return matchname
; /* but maybe filespec was not a wildcard */
120 strncpy(dirname
, wildspec
, dirnamelen
);
121 dirname
[dirnamelen
] = '\0'; /* terminate for strcpy below */
125 if ((wild_dir
= opendir(dirname
)) != (DIR *)NULL
) {
126 while ((file
= readdir(wild_dir
)) != (struct dirent
*)NULL
) {
127 if (file
->d_name
[0] == '.' && wildname
[0] != '.')
128 continue; /* Unix: '*' and '?' do not match leading dot */
129 if (match(file
->d_name
, wildname
, 0)) { /* 0 == case sens. */
131 strcpy(matchname
, dirname
);
132 strcpy(matchname
+dirnamelen
, file
->d_name
);
134 strcpy(matchname
, file
->d_name
);
138 /* if we get to here directory is exhausted, so close it */
140 wild_dir
= (DIR *)NULL
;
143 /* return the raw wildspec in case that works (e.g., directory not
144 * searchable, but filespec was not wild and file is readable) */
145 strcpy(matchname
, wildspec
);
149 /* last time through, might have failed opendir but returned raw wildspec */
150 if (wild_dir
== (DIR *)NULL
) {
151 notfirstcall
= FALSE
; /* nothing left to try--reset for new wildspec */
157 /* If we've gotten this far, we've read and matched at least one entry
158 * successfully (in a previous call), so dirname has been copied into
161 while ((file
= readdir(wild_dir
)) != (struct dirent
*)NULL
) {
162 if (file
->d_name
[0] == '.' && wildname
[0] != '.')
163 continue; /* Unix: '*' and '?' do not match leading dot */
164 if (match(file
->d_name
, wildname
, 0)) { /* 0 == don't ignore case */
166 /* strcpy(matchname, dirname); */
167 strcpy(matchname
+dirnamelen
, file
->d_name
);
169 strcpy(matchname
, file
->d_name
);
174 closedir(wild_dir
); /* have read at least one entry; nothing left */
175 wild_dir
= (DIR *)NULL
;
176 notfirstcall
= FALSE
; /* reset for new wildspec */
181 } /* end function do_wild() */
189 /**********************/
190 /* Function mapattr() */
191 /**********************/
196 ulg tmp
= G
.crec
.external_file_attributes
;
198 switch (G
.pInfo
->hostnum
) {
200 tmp
= (unsigned)(tmp
>>17 & 7); /* Amiga RWE bits */
201 G
.pInfo
->file_attr
= (unsigned)(tmp
<<6 | tmp
<<3 | tmp
);
205 if ((tmp
& 0xF0000000L
) != 0x40000000L
)
206 tmp
&= 0x01FFFFFFL
; /* not a dir, mask all ftype bits */
208 tmp
&= 0x41FFFFFFL
; /* leave directory bit as set */
217 G
.pInfo
->file_attr
= (unsigned)(tmp
>> 16);
218 if (G
.pInfo
->file_attr
!= 0 || !G
.extra_field
) {
221 /* Some (non-Info-ZIP) implementations of Zip for Unix and
222 VMS (and probably others ??) leave 0 in the upper 16-bit
223 part of the external_file_attributes field. Instead, they
224 store file permission attributes in some extra field.
225 As a work-around, we search for the presence of one of
226 these extra fields and fall back to the MSDOS compatible
227 part of external_file_attributes if one of the known
228 e.f. types has been detected.
229 Later, we might implement extraction of the permission
230 bits from the VMS extra field. But for now, the work-around
231 should be sufficient to provide "readable" extracted files.
232 (For ASI Unix e.f., an experimental remap of the e.f.
233 mode value IS already provided!)
237 uch
*ef
= G
.extra_field
;
238 unsigned ef_len
= G
.crec
.extra_field_length
;
241 while (!r
&& ef_len
>= EB_HEADSIZE
) {
243 ebLen
= (unsigned)makeword(ef
+EB_LEN
);
244 if (ebLen
> (ef_len
- EB_HEADSIZE
))
245 /* discoverd some e.f. inconsistency! */
249 if (ebLen
>= (EB_ASI_MODE
+2)) {
251 (unsigned)makeword(ef
+(EB_HEADSIZE
+EB_ASI_MODE
));
252 /* force stop of loop: */
253 ef_len
= (ebLen
+ EB_HEADSIZE
);
256 /* else: fall through! */
258 /* "found nondecypherable e.f. with perm. attr" */
263 ef_len
-= (ebLen
+ EB_HEADSIZE
);
264 ef
+= (ebLen
+ EB_HEADSIZE
);
270 /* all remaining cases: expand MSDOS read-only bit into write perms */
272 /* PKWARE's PKZip for Unix marks entries as FS_FAT_, but stores the
273 * Unix attributes in the upper 16 bits of the external attributes
274 * field, just like Info-ZIP's Zip for Unix. We try to use that
275 * value, after a check for consistency with the MSDOS attribute
278 G
.pInfo
->file_attr
= (unsigned)(tmp
>> 16);
285 /* Ensure that DOS subdir bit is set when the entry's name ends
286 * in a '/'. Some third-party Zip programs fail to set the subdir
287 * bit for directory entries.
289 if ((tmp
& 0x10) == 0) {
290 extent fnlen
= strlen(G
.filename
);
291 if (fnlen
> 0 && G
.filename
[fnlen
-1] == '/')
294 /* read-only bit --> write perms; subdir bit --> dir exec bit */
295 tmp
= !(tmp
& 1) << 1 | (tmp
& 0x10) >> 4;
296 if ((G
.pInfo
->file_attr
& 0700) == (unsigned)(0400 | tmp
<<6))
297 /* keep previous G.pInfo->file_attr setting, when its "owner"
298 * part appears to be consistent with DOS attribute flags!
301 G
.pInfo
->file_attr
= (unsigned)(0444 | tmp
<<6 | tmp
<<3 | tmp
);
303 } /* end switch (host-OS-created-by) */
305 /* for originating systems with no concept of "group," "other," "system": */
306 umask( (int)(tmp
=umask(0)) ); /* apply mask to expanded r/w(/x) perms */
307 G
.pInfo
->file_attr
&= ~tmp
;
311 } /* end function mapattr() */
317 /************************/
318 /* Function mapname() */
319 /************************/
321 int mapname(__G__ renamed
)
326 * MPN_OK - no problem detected
327 * MPN_INF_TRUNC - caution (truncated filename)
328 * MPN_INF_SKIP - info "skip entry" (dir doesn't exist)
329 * MPN_ERR_SKIP - error -> skip entry
330 * MPN_ERR_TOOLONG - error -> path is too long
331 * MPN_NOMEM - error (memory allocation failed) -> skip entry
332 * [also MPN_VOL_LABEL, MPN_CREATED_DIR]
335 char pathcomp
[FILNAMSIZ
]; /* path-component buffer */
336 char *pp
, *cp
=(char *)NULL
; /* character pointers */
337 char *lastsemi
=(char *)NULL
; /* pointer to last semi-colon in pathcomp */
338 #ifdef ACORN_FTYPE_NFS
339 char *lastcomma
=(char *)NULL
; /* pointer to last comma in pathcomp */
340 RO_extra_block
*ef_spark
; /* pointer Acorn FTYPE ef block */
342 int quote
= FALSE
; /* flags */
343 int killed_ddot
= FALSE
; /* is set when skipping "../" pathcomp */
345 register unsigned workch
; /* hold the character being tested */
348 /*---------------------------------------------------------------------------
349 Initialize various pointers and counters and stuff.
350 ---------------------------------------------------------------------------*/
352 if (G
.pInfo
->vollabel
)
353 return MPN_VOL_LABEL
; /* can't set disk volume labels in BeOS */
355 /* can create path as long as not just freshening, or if user told us */
356 G
.create_dirs
= (!uO
.fflag
|| renamed
);
358 created_dir
= FALSE
; /* not yet */
360 /* user gave full pathname: don't prepend rootpath */
361 renamed_fullpath
= (renamed
&& (*G
.filename
== '/'));
363 if (checkdir(__G__ (char *)NULL
, INIT
) == MPN_NOMEM
)
364 return MPN_NOMEM
; /* initialize path buffer, unless no memory */
366 *pathcomp
= '\0'; /* initialize translation buffer */
367 pp
= pathcomp
; /* point to translation buffer */
368 if (uO
.jflag
) /* junking directories */
369 cp
= (char *)strrchr(G
.filename
, '/');
370 if (cp
== (char *)NULL
) /* no '/' or not junking dirs */
371 cp
= G
.filename
; /* point to internal zipfile-member pathname */
373 ++cp
; /* point to start of last component of path */
375 /*---------------------------------------------------------------------------
376 Begin main loop through characters in filename.
377 ---------------------------------------------------------------------------*/
379 while ((workch
= (uch
)*cp
++) != 0) {
381 if (quote
) { /* if character quoted, */
382 *pp
++ = (char)workch
; /* include it literally */
386 case '/': /* can assume -j flag not given */
388 if (((error
= checkdir(__G__ pathcomp
, APPEND_DIR
)) & MPN_MASK
)
391 pp
= pathcomp
; /* reset conversion buffer for next piece */
392 lastsemi
= (char *)NULL
; /* leave directory semi-colons alone */
396 if (pp
== pathcomp
) { /* nothing appended yet... */
397 if (*cp
== '/') { /* don't bother appending "./" to */
398 ++cp
; /* the path: skip behind the '/' */
400 } else if (!uO
.ddotflag
&& *cp
== '.' && cp
[1] == '/') {
401 /* "../" dir traversal detected */
402 cp
+= 2; /* skip over behind the '/' */
403 killed_ddot
= TRUE
; /* set "show message" flag */
410 case ';': /* VMS version (or DEC-20 attrib?) */
412 *pp
++ = ';'; /* keep for now; remove VMS ";##" */
413 break; /* later, if requested */
415 #ifdef ACORN_FTYPE_NFS
416 case ',': /* NFS filetype extension */
418 *pp
++ = ','; /* keep for now; may need to remove */
419 break; /* later, if requested */
422 case '\026': /* control-V quote for special chars */
423 quote
= TRUE
; /* set flag for next character */
427 /* allow European characters in filenames: */
428 if (isprint(workch
) || (128 <= workch
&& workch
<= 254))
429 *pp
++ = (char)workch
;
432 } /* end while loop */
434 /* Show warning when stripping insecure "parent dir" path components */
435 if (killed_ddot
&& QCOND2
) {
436 Info(slide
, 0, ((char *)slide
,
437 "warning: skipped \"../\" path component(s) in %s\n",
438 FnFilter1(G
.filename
)));
439 if (!(error
& ~MPN_MASK
))
440 error
= (error
& MPN_MASK
) | PK_WARN
;
443 /*---------------------------------------------------------------------------
444 Report if directory was created (and no file to create: filename ended
445 in '/'), check name to be sure it exists, and combine path and name be-
447 ---------------------------------------------------------------------------*/
449 if (G
.filename
[strlen(G
.filename
) - 1] == '/') {
450 checkdir(__G__ G
.filename
, GETPATH
);
453 Info(slide
, 0, ((char *)slide
, " creating: %s\n",
454 FnFilter1(G
.filename
)));
457 if (!uO
.J_flag
) { /* Handle the BeOS extra field if present. */
458 void *ptr
= scanBeOSexfield( G
.extra_field
,
459 G
.lrec
.extra_field_length
);
461 setBeOSexfield( G
.filename
, ptr
);
466 /* set approx. dir perms (make sure can still read/write in dir) */
467 if (chmod(G
.filename
, (0xffff & G
.pInfo
->file_attr
) | 0700))
468 perror("chmod (directory attributes) error");
471 /* set dir time (note trailing '/') */
472 return (error
& ~MPN_MASK
) | MPN_CREATED_DIR
;
474 /* TODO: should we re-write the BeOS extra field data in case it's */
475 /* changed? The answer is yes. [Sept 1999 - cjh] */
476 if (!uO
.J_flag
) { /* Handle the BeOS extra field if present. */
477 void *ptr
= scanBeOSexfield( G
.extra_field
,
478 G
.lrec
.extra_field_length
);
480 setBeOSexfield( G
.filename
, ptr
);
484 /* dir existed already; don't look for data to extract */
485 return (error
& ~MPN_MASK
) | MPN_INF_SKIP
;
488 *pp
= '\0'; /* done with pathcomp: terminate it */
490 /* if not saving them, remove VMS version numbers (appended ";###") */
491 if (!uO
.V_flag
&& lastsemi
) {
493 while (isdigit((uch
)(*pp
)))
495 if (*pp
== '\0') /* only digits between ';' and end: nuke */
499 #ifdef ACORN_FTYPE_NFS
500 /* translate Acorn filetype information if asked to do so */
501 if (uO
.acorn_nfs_ext
&&
502 (ef_spark
= (RO_extra_block
*)
503 getRISCOSexfield(G
.extra_field
, G
.lrec
.extra_field_length
))
504 != (RO_extra_block
*)NULL
)
506 /* file *must* have a RISC OS extra field */
507 long ft
= (long)makelong((ef_spark
->loadaddr
);
511 while (isxdigit((uch
)(*pp
))) ++pp
;
512 if (pp
== lastcomma
+4 && *pp
== '\0') *lastcomma
='\0'; /* nuke */
514 if ((ft
& 1<<31)==0) ft
=0x000FFD00;
515 sprintf(pathcomp
+strlen(pathcomp
), ",%03x", (int)(ft
>>8) & 0xFFF);
517 #endif /* ACORN_FTYPE_NFS */
519 if (*pathcomp
== '\0') {
520 Info(slide
, 1, ((char *)slide
, "mapname: conversion of %s failed\n",
521 FnFilter1(G
.filename
)));
522 return (error
& ~MPN_MASK
) | MPN_ERR_SKIP
;
525 checkdir(__G__ pathcomp
, APPEND_NAME
); /* returns 1 if truncated: care? */
526 checkdir(__G__ G
.filename
, GETPATH
);
530 } /* end function mapname() */
535 /***********************/
536 /* Function checkdir() */
537 /***********************/
539 int checkdir(__G__ pathcomp
, flag
)
545 * MPN_OK - no problem detected
546 * MPN_INF_TRUNC - (on APPEND_NAME) truncated filename
547 * MPN_INF_SKIP - path doesn't exist, not allowed to create
548 * MPN_ERR_SKIP - path doesn't exist, tried to create and failed; or path
549 * exists and is not a directory, but is supposed to be
550 * MPN_ERR_TOOLONG - path is too long
551 * MPN_NOMEM - can't allocate memory for filename buffers
554 static int rootlen
= 0; /* length of rootpath */
555 static char *rootpath
; /* user's "extract-to" directory */
556 static char *buildpath
; /* full path (so far) to extracted file */
557 static char *end
; /* pointer to end of buildpath ('\0') */
560 # define FUNCTION (flag & FN_MASK)
563 /*---------------------------------------------------------------------------
564 APPEND_DIR: append the path component to the path being built and check
565 for its existence. If doesn't exist and we are creating directories, do
566 so for this one; else signal success or error as appropriate.
567 ---------------------------------------------------------------------------*/
569 if (FUNCTION
== APPEND_DIR
) {
570 int too_long
= FALSE
;
575 Trace((stderr
, "appending dir segment [%s]\n", FnFilter1(pathcomp
)));
576 while ((*end
= *pathcomp
++) != '\0')
578 #ifdef SHORT_NAMES /* path components restricted to 14 chars, typically */
579 if ((end
-old_end
) > FILENAME_MAX
) /* GRR: proper constant? */
580 *(end
= old_end
+ FILENAME_MAX
) = '\0';
583 /* GRR: could do better check, see if overrunning buffer as we go:
584 * check end-buildpath after each append, set warning variable if
585 * within 20 of FILNAMSIZ; then if var set, do careful check when
586 * appending. Clear variable when begin new path. */
588 if ((end
-buildpath
) > FILNAMSIZ
-3) /* need '/', one-char name, '\0' */
589 too_long
= TRUE
; /* check if extracting directory? */
590 if (stat(buildpath
, &G
.statbuf
)) { /* path doesn't exist */
591 if (!G
.create_dirs
) { /* told not to create (freshening) */
593 return MPN_INF_SKIP
; /* path doesn't exist: nothing to do */
596 Info(slide
, 1, ((char *)slide
,
597 "checkdir error: path too long: %s\n",
598 FnFilter1(buildpath
)));
600 /* no room for filenames: fatal */
601 return MPN_ERR_TOOLONG
;
603 if (mkdir(buildpath
, 0777) == -1) { /* create the directory */
604 Info(slide
, 1, ((char *)slide
,
605 "checkdir error: cannot create %s\n\
606 unable to process %s.\n",
607 FnFilter2(buildpath
), FnFilter1(G
.filename
)));
609 /* path didn't exist, tried to create, failed */
613 } else if (!S_ISDIR(G
.statbuf
.st_mode
)) {
614 Info(slide
, 1, ((char *)slide
,
615 "checkdir error: %s exists but is not directory\n\
616 unable to process %s.\n",
617 FnFilter2(buildpath
), FnFilter1(G
.filename
)));
619 /* path existed but wasn't dir */
623 Info(slide
, 1, ((char *)slide
,
624 "checkdir error: path too long: %s\n", FnFilter1(buildpath
)));
626 /* no room for filenames: fatal */
627 return MPN_ERR_TOOLONG
;
631 Trace((stderr
, "buildpath now = [%s]\n", FnFilter1(buildpath
)));
634 } /* end if (FUNCTION == APPEND_DIR) */
636 /*---------------------------------------------------------------------------
637 GETPATH: copy full path to the string pointed at by pathcomp, and free
639 ---------------------------------------------------------------------------*/
641 if (FUNCTION
== GETPATH
) {
642 strcpy(pathcomp
, buildpath
);
643 Trace((stderr
, "getting and freeing path [%s]\n",
644 FnFilter1(pathcomp
)));
646 buildpath
= end
= (char *)NULL
;
650 /*---------------------------------------------------------------------------
651 APPEND_NAME: assume the path component is the filename; append it and
652 return without checking for existence.
653 ---------------------------------------------------------------------------*/
655 if (FUNCTION
== APPEND_NAME
) {
660 Trace((stderr
, "appending filename [%s]\n", FnFilter1(pathcomp
)));
661 while ((*end
= *pathcomp
++) != '\0') {
663 #ifdef SHORT_NAMES /* truncate name at 14 characters, typically */
664 if ((end
-old_end
) > FILENAME_MAX
) /* GRR: proper constant? */
665 *(end
= old_end
+ FILENAME_MAX
) = '\0';
667 if ((end
-buildpath
) >= FILNAMSIZ
) {
669 Info(slide
, 0x201, ((char *)slide
,
670 "checkdir warning: path too long; truncating\n\
672 FnFilter1(G
.filename
), FnFilter2(buildpath
)));
673 return MPN_INF_TRUNC
; /* filename truncated */
676 Trace((stderr
, "buildpath now = [%s]\n", FnFilter1(buildpath
)));
677 /* could check for existence here, prompt for new name... */
681 /*---------------------------------------------------------------------------
682 INIT: allocate and initialize buffer space for the file currently being
683 extracted. If file was renamed with an absolute path, don't prepend the
685 ---------------------------------------------------------------------------*/
687 /* GRR: for VMS and TOPS-20, add up to 13 to strlen */
689 if (FUNCTION
== INIT
) {
690 Trace((stderr
, "initializing buildpath to "));
691 #ifdef ACORN_FTYPE_NFS
692 if ((buildpath
= (char *)malloc(strlen(G
.filename
)+rootlen
+
693 (uO
.acorn_nfs_ext
? 5 : 1)))
695 if ((buildpath
= (char *)malloc(strlen(G
.filename
)+rootlen
+1))
699 if ((rootlen
> 0) && !renamed_fullpath
) {
700 strcpy(buildpath
, rootpath
);
701 end
= buildpath
+ rootlen
;
706 Trace((stderr
, "[%s]\n", FnFilter1(buildpath
)));
710 /*---------------------------------------------------------------------------
711 ROOT: if appropriate, store the path in rootpath and create it if
712 necessary; else assume it's a zipfile member and return. This path
713 segment gets used in extracting all members from every zipfile specified
715 ---------------------------------------------------------------------------*/
717 #if (!defined(SFX) || defined(SFX_EXDIR))
718 if (FUNCTION
== ROOT
) {
719 Trace((stderr
, "initializing root path to [%s]\n",
720 FnFilter1(pathcomp
)));
721 if (pathcomp
== (char *)NULL
) {
725 if (rootlen
> 0) /* rootpath was already set, nothing to do */
727 if ((rootlen
= strlen(pathcomp
)) > 0) {
730 if ((tmproot
= (char *)malloc(rootlen
+2)) == (char *)NULL
) {
734 strcpy(tmproot
, pathcomp
);
735 if (tmproot
[rootlen
-1] == '/') {
736 tmproot
[--rootlen
] = '\0';
738 if (rootlen
> 0 && (stat(tmproot
, &G
.statbuf
) ||
739 !S_ISDIR(G
.statbuf
.st_mode
)))
740 { /* path does not exist */
741 if (!G
.create_dirs
/* || iswild(tmproot) */ ) {
744 /* skip (or treat as stored file) */
747 /* create the directory (could add loop here scanning tmproot
748 * to create more than one level, but why really necessary?) */
749 if (mkdir(tmproot
, 0777) == -1) {
750 Info(slide
, 1, ((char *)slide
,
751 "checkdir: cannot create extraction directory: %s\n",
752 FnFilter1(tmproot
)));
755 /* path didn't exist, tried to create, and failed: */
756 /* file exists, or 2+ subdir levels required */
760 tmproot
[rootlen
++] = '/';
761 tmproot
[rootlen
] = '\0';
762 if ((rootpath
= (char *)realloc(tmproot
, rootlen
+1)) == NULL
) {
767 Trace((stderr
, "rootpath now = [%s]\n", FnFilter1(rootpath
)));
771 #endif /* !SFX || SFX_EXDIR */
773 /*---------------------------------------------------------------------------
774 END: free rootpath, immediately prior to program exit.
775 ---------------------------------------------------------------------------*/
777 if (FUNCTION
== END
) {
778 Trace((stderr
, "freeing rootpath\n"));
786 return MPN_INVALID
; /* should never reach */
788 } /* end function checkdir() */
795 /****************************/
796 /* Function close_outfile() */
797 /****************************/
799 void close_outfile(__G
) /* GRR: change to return PK-style warning level */
804 unsigned eb_izux_flg
;
806 /*---------------------------------------------------------------------------
807 If symbolic links are supported, allocate a storage area, put the uncom-
808 pressed "data" in it, and create the link. Since we know it's a symbolic
809 link to start with, we shouldn't have to worry about overflowing unsigned
810 ints with unsigned longs.
811 ---------------------------------------------------------------------------*/
815 unsigned ucsize
= (unsigned)G
.lrec
.ucsize
;
816 char *linktarget
= (char *)malloc((unsigned)G
.lrec
.ucsize
+1);
818 fclose(G
.outfile
); /* close "data" file... */
819 G
.outfile
= fopen(G
.filename
, FOPR
); /* ...and reopen for reading */
821 || fread(linktarget
, 1, ucsize
, G
.outfile
) != (size_t)ucsize
) {
822 Info(slide
, 0x201, ((char *)slide
,
823 "warning: symbolic link (%s) failed\n", FnFilter1(G
.filename
)));
829 fclose(G
.outfile
); /* close "data" file for good... */
830 unlink(G
.filename
); /* ...and delete it */
831 linktarget
[ucsize
] = '\0';
833 Info(slide
, 0, ((char *)slide
, "-> %s ", FnFilter1(linktarget
)));
834 if (symlink(linktarget
, G
.filename
)) /* create the real link */
835 perror("symlink error");
838 /* Symlinks can have attributes, too. */
839 void *ptr
= scanBeOSexfield( G
.extra_field
,
840 G
.lrec
.extra_field_length
);
842 setBeOSexfield( G
.filename
, ptr
);
847 return; /* can't set time on symlinks */
849 #endif /* SYMLINKS */
853 /* handle the BeOS extra field if present */
855 void *ptr
= scanBeOSexfield( G
.extra_field
,
856 G
.lrec
.extra_field_length
);
859 setBeOSexfield( G
.filename
, ptr
);
860 #ifdef BEOS_ASSIGN_FILETYPE
862 /* Otherwise, ask the system to try assigning a MIME type. */
863 assign_MIME( G
.filename
);
868 /*---------------------------------------------------------------------------
869 Change the file permissions from default ones to those stored in the
871 ---------------------------------------------------------------------------*/
874 if (chmod(G
.filename
, 0xffff & G
.pInfo
->file_attr
))
875 perror("chmod (file attributes) error");
878 /*---------------------------------------------------------------------------
879 Convert from MSDOS-format local time and date to Unix-format 32-bit GMT
880 time: adjust base year from 1980 to 1970, do usual conversions from
881 yy/mm/dd hh:mm:ss to elapsed seconds, and account for timezone and day-
882 light savings time differences. If we have a Unix extra field, however,
883 we're laughing: both mtime and atime are ours. On the other hand, we
884 then have to check for restoration of UID/GID.
885 ---------------------------------------------------------------------------*/
887 eb_izux_flg
= (G
.extra_field
? ef_scan_for_izux(G
.extra_field
,
888 G
.lrec
.extra_field_length
, 0, G
.lrec
.last_mod_dos_datetime
,
890 (G
.tz_is_valid
? &zt
: NULL
),
895 if (eb_izux_flg
& EB_UT_FL_MTIME
) {
896 TTrace((stderr
, "\nclose_outfile: Unix e.f. modif. time = %ld\n",
899 zt
.mtime
= dos_to_unix_time(G
.lrec
.last_mod_dos_datetime
);
901 if (eb_izux_flg
& EB_UT_FL_ATIME
) {
902 TTrace((stderr
, "close_outfile: Unix e.f. access time = %ld\n",
906 TTrace((stderr
, "\nclose_outfile: modification/access times = %ld\n",
910 /* if -X option was specified and we have UID/GID info, restore it */
911 if (uO
.X_flag
&& eb_izux_flg
& EB_UX2_VALID
) {
912 TTrace((stderr
, "close_outfile: restoring Unix UID/GID info\n"));
913 if (chown(G
.filename
, (uid_t
)z_uidgid
[0], (gid_t
)z_uidgid
[1]))
916 Info(slide
, 0x201, ((char *)slide
,
917 "warning: cannot set UID %d and/or GID %d for %s\n",
918 z_uidgid
[0], z_uidgid
[1], FnFilter1(G
.filename
)));
920 Info(slide
, 0x201, ((char *)slide
,
921 " (warning) cannot set UID %d and/or GID %d",
922 z_uidgid
[0], z_uidgid
[1]));
926 /* set the file's access and modification times */
927 if (utime(G
.filename
, (struct utimbuf
*)&zt
)) {
929 Info(slide
, 0x201, ((char *)slide
,
930 "warning: cannot set time for %s\n", FnFilter1(G
.filename
)));
932 Info(slide
, 0x201, ((char *)slide
,
933 " (warning) cannot set time"));
936 } /* end function close_outfile() */
941 #ifdef SET_DIR_ATTRIB
942 /* messages of code for setting directory attributes */
943 static char Far DirlistUidGidFailed
[] =
944 "warning: cannot set UID %d and/or GID %d for %s\n";
945 static char Far DirlistUtimeFailed
[] =
946 "warning: cannot set modification, access times for %s\n";
948 static char Far DirlistChmodFailed
[] =
949 "warning: cannot set permissions for %s\n";
953 int set_direc_attribs(__G__ d
)
959 if (d
->have_uidgid
&&
960 chown(d
->fn
, (uid_t
)d
->uidgid
[0], (gid_t
)d
->uidgid
[1]))
962 Info(slide
, 0x201, ((char *)slide
,
963 LoadFarString(DirlistUidGidFailed
),
964 d
->uidgid
[0], d
->uidgid
[1], d
->fn
));
968 if (utime(d
->fn
, (const struct utimbuf
*)&d
->u
.t2
)) {
969 Info(slide
, 0x201, ((char *)slide
,
970 LoadFarString(DirlistUtimeFailed
), FnFilter1(d
->fn
)));
975 if (chmod(d
->fn
, 0xffff & d
->perms
)) {
976 Info(slide
, 0x201, ((char *)slide
,
977 LoadFarString(DirlistChmodFailed
), FnFilter1(d
->fn
)));
978 /* perror("chmod (file attributes) error"); */
982 #endif /* !NO_CHMOD */
984 } /* end function set_directory_attributes() */
986 #endif /* SET_DIR_ATTRIB */
993 /***************************/
994 /* Function stamp_file() */
995 /***************************/
997 int stamp_file(fname
, modtime
)
1003 tp
.modtime
= tp
.actime
= modtime
;
1004 return (utime(fname
, &tp
));
1006 } /* end function stamp_file() */
1008 #endif /* TIMESTAMP */
1015 /************************/
1016 /* Function version() */
1017 /************************/
1022 sprintf((char *)slide
, LoadFarString(CompiledWith
),
1023 #if defined(__MWERKS__)
1024 "Metrowerks CodeWarrior", "",
1025 #elif defined(__GNUC__)
1026 "GNU C ", __VERSION__
,
1036 "(unknown)", /* someday we may have other architectures... */
1047 (*G
.message
)((zvoid
*)&G
, slide
, (ulg
)strlen((char *)slide
), 0);
1049 } /* end function version() */
1055 /******************************/
1056 /* Extra field functions */
1057 /******************************/
1060 ** Scan the extra fields in extra_field, and look for a BeOS EF; return a
1061 ** pointer to that EF, or NULL if it's not there.
1063 static uch
*scanBeOSexfield( const uch
*ef_ptr
, unsigned ef_len
)
1065 while( ef_ptr
!= NULL
&& ef_len
>= EB_HEADSIZE
) {
1066 unsigned eb_id
= makeword(EB_ID
+ ef_ptr
);
1067 unsigned eb_len
= makeword(EB_LEN
+ ef_ptr
);
1069 if (eb_len
> (ef_len
- EB_HEADSIZE
)) {
1071 "scanBeOSexfield: block length %u > rest ef_size %u\n", eb_len
,
1072 ef_len
- EB_HEADSIZE
));
1076 if( eb_id
== EF_BEOS
&& eb_len
>= EB_BEOS_HLEN
) {
1077 return (uch
*)ef_ptr
;
1080 ef_ptr
+= (eb_len
+ EB_HEADSIZE
);
1081 ef_len
-= (eb_len
+ EB_HEADSIZE
);
1087 /* Used by setBeOSexfield():
1089 Set a file/directory's attributes to the attributes passed in.
1091 If set_file_attrs() fails, an error will be returned:
1093 EOK - no errors occurred
1095 (other values will be whatever the failed function returned; no docs
1096 yet, or I'd list a few)
1098 static int set_file_attrs( const char *name
,
1099 const unsigned char *attr_buff
,
1100 const off_t total_attr_size
)
1104 const unsigned char *guard
;
1107 ptr
= (unsigned char *)attr_buff
;
1108 guard
= ptr
+ total_attr_size
;
1110 #ifdef HAIKU_USE_KERN_OPEN
1111 fd
= _kern_open( -1, name
, O_RDONLY
| O_NOTRAVERSE
, 0 );
1115 fd
= open( name
, O_RDONLY
| O_NOTRAVERSE
);
1117 return errno
; /* should it be -fd ? */
1121 while( ptr
< guard
) {
1122 ssize_t wrote_bytes
;
1123 const char *attr_name
;
1124 unsigned char *attr_data
;
1128 attr_name
= (char *)&(ptr
[0]);
1129 ptr
+= strlen( attr_name
) + 1;
1131 /* The attr_info data is stored in big-endian format because the */
1132 /* PowerPC port was here first. */
1133 memcpy( &attr_type
, ptr
, 4 ); ptr
+= 4;
1134 memcpy( &attr_size
, ptr
, 8 ); ptr
+= 8;
1136 attr_type
= (uint32
)B_BENDIAN_TO_HOST_INT32( attr_type
);
1137 attr_size
= (off_t
)B_BENDIAN_TO_HOST_INT64( attr_size
);
1139 if( attr_size
< 0LL ) {
1140 Info(slide
, 0x201, ((char *)slide
,
1141 "warning: skipping attribute with invalid length (%" B_PRIdOFF
1150 /* We've got a truncated attribute. */
1151 Info(slide
, 0x201, ((char *)slide
,
1152 "warning: truncated attribute\n"));
1156 /* Wave the magic wand... this will swap Be-known types properly. */
1157 (void)swap_data( attr_type
, attr_data
, attr_size
,
1158 B_SWAP_BENDIAN_TO_HOST
);
1160 wrote_bytes
= fs_write_attr( fd
, attr_name
, attr_type
, 0,
1161 attr_data
, attr_size
);
1162 if( wrote_bytes
!= attr_size
) {
1163 Info(slide
, 0x201, ((char *)slide
,
1164 "warning: wrote %ld attribute bytes of %ld\n",
1165 (unsigned long)wrote_bytes
,(unsigned long)attr_size
));
1174 static void setBeOSexfield(const char *path
, uch
*extra_field
)
1176 uch
*ptr
= extra_field
;
1181 uch
*attrbuff
= NULL
;
1184 if (extra_field
== NULL
)
1187 /* Collect the data from the extra field buffer. */
1188 id
= makeword( ptr
); ptr
+= 2; /* we don't use this... */
1189 size
= makeword( ptr
); ptr
+= 2;
1190 full_size
= makelong( ptr
); ptr
+= 4;
1191 flags
= *ptr
; ptr
++;
1193 /* Do a little sanity checking. */
1194 if (flags
& EB_BE_FL_BADBITS
) {
1195 /* corrupted or unsupported */
1196 Info(slide
, 0x201, ((char *)slide
,
1197 "Unsupported flags set for this BeOS extra field, skipping.\n"));
1200 if (size
<= EB_BEOS_HLEN
) {
1201 /* corrupted, unsupported, or truncated */
1202 Info(slide
, 0x201, ((char *)slide
,
1203 "BeOS extra field is %d bytes, should be at least %d.\n", size
,
1207 if (full_size
< (uint32
)(size
- EB_BEOS_HLEN
)) {
1208 /* possible old archive? will this screw up on valid archives? */
1209 Info(slide
, 0x201, ((char *)slide
,
1210 "Skipping attributes: BeOS extra field is %d bytes, "
1211 "data size is %ld.\n", size
- EB_BEOS_HLEN
, full_size
));
1215 /* Find the BeOS file attribute data. */
1216 if (flags
& EB_BE_FL_UNCMPR
) {
1217 /* Uncompressed data */
1220 /* Compressed data */
1221 attrbuff
= (uch
*)malloc(full_size
);
1222 if (attrbuff
== NULL
) {
1223 /* No memory to uncompress attributes */
1224 Info(slide
, 0x201, ((char *)slide
,
1225 "Can't allocate memory to uncompress file attributes.\n"));
1229 retval
= memextract(__G__ attrbuff
, full_size
, ptr
, size
- EB_BEOS_HLEN
);
1230 if (retval
!= PK_OK
) {
1231 /* error uncompressing attributes */
1232 Info(slide
, 0x201, ((char *)slide
,
1233 "Error uncompressing file attributes.\n"));
1235 /* Some errors here might not be so bad; we should expect */
1236 /* some truncated data, for example. If the data was */
1237 /* corrupt, we should _not_ attempt to restore the attrs */
1238 /* for this file... there's no way to detect what attrs */
1239 /* are good and which are bad. */
1245 /* Now attempt to set the file attributes on the extracted file. */
1246 retval
= set_file_attrs(path
, attrbuff
, (off_t
)full_size
);
1247 if (retval
!= EOK
) {
1248 Info(slide
, 0x201, ((char *)slide
,
1249 "Error writing file attributes.\n"));
1252 /* Clean up, if necessary */
1253 if (attrbuff
!= ptr
)
1257 #ifdef BEOS_USE_PRINTEXFIELD
1258 static void printBeOSexfield( int isdir
, uch
*extra_field
)
1260 uch
*ptr
= extra_field
;
1266 /* Tell picky compilers to be quiet. */
1269 if( extra_field
== NULL
) {
1273 /* Collect the data from the buffer. */
1274 id
= makeword( ptr
); ptr
+= 2;
1275 size
= makeword( ptr
); ptr
+= 2;
1276 full_size
= makelong( ptr
); ptr
+= 4;
1277 flags
= *ptr
; ptr
++;
1279 if( id
!= EF_BEOS
) {
1280 /* not a 'Be' field */
1281 printf("\t*** Unknown field type (0x%04x, '%c%c')\n", id
,
1282 (char)(id
>> 8), (char)id
);
1285 if( flags
& EB_BE_FL_BADBITS
) {
1286 /* corrupted or unsupported */
1287 printf("\t*** Corrupted BeOS extra field:\n");
1288 printf("\t*** unknown bits set in the flags\n");
1289 printf("\t*** (Possibly created by an old version of zip for BeOS.\n");
1292 if( size
<= EB_BEOS_HLEN
) {
1293 /* corrupted, unsupported, or truncated */
1294 printf("\t*** Corrupted BeOS extra field:\n");
1295 printf("\t*** size is %d, should be larger than %d\n", size
,
1299 if( flags
& EB_BE_FL_UNCMPR
) {
1300 /* Uncompressed data */
1301 printf("\tBeOS extra field data (uncompressed):\n");
1302 printf("\t\t%ld data bytes\n", full_size
);
1304 /* Compressed data */
1305 printf("\tBeOS extra field data (compressed):\n");
1306 printf("\t\t%d compressed bytes\n", size
- EB_BEOS_HLEN
);
1307 printf("\t\t%ld uncompressed bytes\n", full_size
);
1312 #ifdef BEOS_ASSIGN_FILETYPE
1313 /* Note: This will no longer be necessary in BeOS PR4; update_mime_info() */
1314 /* will be updated to build its own absolute pathname if it's not given one. */
1315 static void assign_MIME( const char *file
)
1318 char buff
[PATH_MAX
], cwd_buff
[PATH_MAX
];
1321 if( file
[0] == '/' ) {
1322 fullname
= (char *)file
;
1324 sprintf( buff
, "%s/%s", getcwd( cwd_buff
, PATH_MAX
), file
);
1328 retval
= update_mime_info( fullname
, FALSE
, TRUE
, TRUE
);