fix build failure caused by incorrect %setup
[toolchains/unzip.git] / beos / beos.c
1 /*
2   Copyright (c) 1990-2009 Info-ZIP.  All rights reserved.
3
4   See the accompanying file LICENSE, version 2009-Jan-02 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   beos.c
12
13   BeOS-specific routines for use with Info-ZIP's UnZip 5.30 and later.
14   (based on unix/unix.c)
15
16   Contains:  do_wild()           <-- generic enough to put in fileio.c?
17              mapattr()
18              mapname()
19              checkdir()
20              close_outfile()
21              defer_dir_attribs()
22              set_direc_attribs()
23              stamp_file()
24              version()
25              scanBeOSexfield()
26              set_file_attrs()
27              setBeOSexfield()
28              printBeOSexfield()
29              assign_MIME()
30
31   ---------------------------------------------------------------------------*/
32
33
34 #define UNZIP_INTERNAL
35 #include "unzip.h"
36
37 #include "beos.h"
38 #include <errno.h>             /* Just make sure we've got a few things... */
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <fcntl.h>
42
43 #include <dirent.h>
44
45 /* For the new post-DR8 file attributes */
46 #include <kernel/fs_attr.h>
47 #include <support/byteorder.h>
48 #include <storage/Mime.h>
49
50 static unsigned filtattr OF((__GPRO__ unsigned perms));
51 static uch *scanBeOSexfield  OF((const uch *ef_ptr, unsigned ef_len));
52 static int  set_file_attrs( const char *, const unsigned char *, const off_t );
53 static void setBeOSexfield   OF((const char *path, uch *extra_field));
54 #ifdef BEOS_USE_PRINTEXFIELD
55 static void printBeOSexfield OF((int isdir, uch *extra_field));
56 #endif
57 #ifdef BEOS_ASSIGN_FILETYPE
58 static void assign_MIME( const char * );
59 #endif
60
61 #ifdef SET_DIR_ATTRIB
62 typedef struct uxdirattr {      /* struct for holding unix style directory */
63     struct uxdirattr *next;     /*  info until can be sorted and set at end */
64     char *fn;                   /* filename of directory */
65     union {
66         iztimes t3;             /* mtime, atime, ctime */
67         ztimbuf t2;             /* modtime, actime */
68     } u;
69     unsigned perms;             /* same as min_info.file_attr */
70     int have_uidgid;            /* flag */
71     ulg uidgid[2];
72     char fnbuf[1];              /* buffer stub for directory name */
73 } uxdirattr;
74 #define UxAtt(d)  ((uxdirattr *)d)    /* typecast shortcut */
75 #endif /* SET_DIR_ATTRIB */
76
77 #ifdef ACORN_FTYPE_NFS
78 /* Acorn bits for NFS filetyping */
79 typedef struct {
80   uch ID[2];
81   uch size[2];
82   uch ID_2[4];
83   uch loadaddr[4];
84   uch execaddr[4];
85   uch attr[4];
86 } RO_extra_block;
87
88 #endif /* ACORN_FTYPE_NFS */
89
90 /* static int created_dir;      */      /* used in mapname(), checkdir() */
91 /* static int renamed_fullpath; */      /* ditto */
92
93
94 /*****************************/
95 /* Strings used multiple     */
96 /* times in beos.c           */
97 /*****************************/
98
99 /* messages of code for setting file/directory attributes */
100 static ZCONST char CannotSetItemUidGid[] =
101   "warning:  cannot set UID %lu and/or GID %lu for %s\n          %s\n";
102 static ZCONST char CannotSetUidGid[] =
103   " (warning) cannot set UID %lu and/or GID %lu\n          %s";
104 static ZCONST char CannotSetItemTimestamps[] =
105   "warning:  cannot set modif./access times for %s\n          %s\n";
106 static ZCONST char CannotSetTimestamps[] =
107   " (warning) cannot set modif./access times\n          %s";
108
109
110 #ifndef SFX
111
112 /**********************/
113 /* Function do_wild() */   /* for porting: dir separator; match(ignore_case) */
114 /**********************/
115
116 char *do_wild(__G__ wildspec)
117     __GDEF
118     ZCONST char *wildspec;  /* only used first time on a given dir */
119 {
120 /* these statics are now declared in SYSTEM_SPECIFIC_GLOBALS in beocfg.h:
121     static DIR *wild_dir = (DIR *)NULL;
122     static ZCONST char *wildname;
123     static char *dirname, matchname[FILNAMSIZ];
124     static int notfirstcall=FALSE, have_dirname, dirnamelen;
125 */
126     struct dirent *file;
127
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[].
131      */
132     if (!G.notfirstcall) {  /* first call:  must initialize everything */
133         G.notfirstcall = TRUE;
134
135         if (!iswild(wildspec)) {
136             strncpy(G.matchname, wildspec, FILNAMSIZ);
137             G.matchname[FILNAMSIZ-1] = '\0';
138             G.have_dirname = FALSE;
139             G.wild_dir = NULL;
140             return G.matchname;
141         }
142
143         /* break the wildspec into a directory part and a wildcard filename */
144         if ((G.wildname = (ZCONST char *)strrchr(wildspec, '/')) == NULL) {
145             G.dirname = ".";
146             G.dirnamelen = 1;
147             G.have_dirname = FALSE;
148             G.wildname = wildspec;
149         } else {
150             ++G.wildname;     /* point at character after '/' */
151             G.dirnamelen = G.wildname - wildspec;
152             if ((G.dirname = (char *)malloc(G.dirnamelen+1)) == (char *)NULL) {
153                 Info(slide, 0x201, ((char *)slide,
154                   "warning:  cannot allocate wildcard buffers\n"));
155                 strncpy(G.matchname, wildspec, FILNAMSIZ);
156                 G.matchname[FILNAMSIZ-1] = '\0';
157                 return G.matchname; /* but maybe filespec was not a wildcard */
158             }
159             strncpy(G.dirname, wildspec, G.dirnamelen);
160             G.dirname[G.dirnamelen] = '\0';   /* terminate for strcpy below */
161             G.have_dirname = TRUE;
162         }
163
164         if ((G.wild_dir = (zvoid *)opendir(G.dirname)) != (zvoid *)NULL) {
165             while ((file = readdir((DIR *)G.wild_dir)) !=
166                    (struct dirent *)NULL) {
167                 Trace((stderr, "do_wild:  readdir returns %s\n",
168                   FnFilter1(file->d_name)));
169                 if (file->d_name[0] == '.' && G.wildname[0] != '.')
170                     continue; /* Unix:  '*' and '?' do not match leading dot */
171                 if (match(file->d_name, G.wildname, 0 WISEP) &&/*0=case sens.*/
172                     /* skip "." and ".." directory entries */
173                     strcmp(file->d_name, ".") && strcmp(file->d_name, "..")) {
174                     Trace((stderr, "do_wild:  match() succeeds\n"));
175                     if (G.have_dirname) {
176                         strcpy(G.matchname, G.dirname);
177                         strcpy(G.matchname+G.dirnamelen, file->d_name);
178                     } else
179                         strcpy(G.matchname, file->d_name);
180                     return G.matchname;
181                 }
182             }
183             /* if we get to here directory is exhausted, so close it */
184             closedir((DIR *)G.wild_dir);
185             G.wild_dir = (zvoid *)NULL;
186         }
187         Trace((stderr, "do_wild:  opendir(%s) returns NULL\n",
188           FnFilter1(G.dirname)));
189
190         /* return the raw wildspec in case that works (e.g., directory not
191          * searchable, but filespec was not wild and file is readable) */
192         strncpy(G.matchname, wildspec, FILNAMSIZ);
193         G.matchname[FILNAMSIZ-1] = '\0';
194         return G.matchname;
195     }
196
197     /* last time through, might have failed opendir but returned raw wildspec */
198     if ((DIR *)G.wild_dir == (DIR *)NULL) {
199         G.notfirstcall = FALSE; /* nothing left--reset for new wildspec */
200         if (G.have_dirname)
201             free(G.dirname);
202         return (char *)NULL;
203     }
204
205     /* If we've gotten this far, we've read and matched at least one entry
206      * successfully (in a previous call), so dirname has been copied into
207      * matchname already.
208      */
209     while ((file = readdir((DIR *)G.wild_dir)) != (struct dirent *)NULL) {
210         Trace((stderr, "do_wild:  readdir returns %s\n",
211           FnFilter1(file->d_name)));
212         if (file->d_name[0] == '.' && G.wildname[0] != '.')
213             continue;   /* Unix:  '*' and '?' do not match leading dot */
214         if (match(file->d_name, G.wildname, 0 WISEP)) { /* 0 == case sens. */
215             Trace((stderr, "do_wild:  match() succeeds\n"));
216             if (G.have_dirname) {
217                 /* strcpy(G.matchname, G.dirname); */
218                 strcpy(G.matchname+G.dirnamelen, file->d_name);
219             } else
220                 strcpy(G.matchname, file->d_name);
221             return G.matchname;
222         }
223     }
224
225     closedir((DIR *)G.wild_dir);  /* at least one entry read; nothing left */
226     G.wild_dir = (zvoid *)NULL;
227     G.notfirstcall = FALSE;       /* reset for new wildspec */
228     if (G.have_dirname)
229         free(G.dirname);
230     return (char *)NULL;
231
232 } /* end function do_wild() */
233
234 #endif /* !SFX */
235
236
237
238
239 #ifndef S_ISUID
240 # define S_ISUID        0004000 /* set user id on execution */
241 #endif
242 #ifndef S_ISGID
243 # define S_ISGID        0002000 /* set group id on execution */
244 #endif
245 #ifndef S_ISVTX
246 # define S_ISVTX        0001000 /* save swapped text even after use */
247 #endif
248
249 /************************/
250 /*  Function filtattr() */
251 /************************/
252 /* This is used to clear or keep the SUID and SGID bits on file permissions.
253  * It's possible that a file in an archive could have one of these bits set
254  * and, unknown to the person unzipping, could allow others to execute the
255  * file as the user or group.  The new option -K bypasses this check.
256  */
257
258 static unsigned filtattr(__G__ perms)
259     __GDEF
260     unsigned perms;
261 {
262     /* keep setuid/setgid/tacky perms? */
263     if (!uO.K_flag)
264         perms &= ~(S_ISUID | S_ISGID | S_ISVTX);
265
266     return (0xffff & perms);
267 } /* end function filtattr() */
268
269
270
271
272
273 /**********************/
274 /* Function mapattr() */
275 /**********************/
276
277 int mapattr(__G)
278     __GDEF
279 {
280     int r;
281     ulg tmp = G.crec.external_file_attributes;
282
283     G.pInfo->file_attr = 0;
284     /* initialized to 0 for check in "default" branch below... */
285
286     switch (G.pInfo->hostnum) {
287         case AMIGA_:
288             tmp = (unsigned)(tmp>>17 & 7);   /* Amiga RWE bits */
289             G.pInfo->file_attr = (unsigned)(tmp<<6 | tmp<<3 | tmp);
290             break;
291         case THEOS_:
292             tmp &= 0xF1FFFFFFL;
293             if ((tmp & 0xF0000000L) != 0x40000000L)
294                 tmp &= 0x01FFFFFFL;     /* not a dir, mask all ftype bits */
295             else
296                 tmp &= 0x41FFFFFFL;     /* leave directory bit as set */
297             /* fall through! */
298         case BEOS_:
299         case UNIX_:
300         case VMS_:
301         case ACORN_:
302         case ATARI_:
303         case ATHEOS_:
304         case QDOS_:
305         case TANDEM_:
306             r = FALSE;
307             G.pInfo->file_attr = (unsigned)(tmp >> 16);
308             if (G.pInfo->file_attr == 0 && G.extra_field) {
309                 /* Some (non-Info-ZIP) implementations of Zip for Unix and
310                  * VMS (and probably others ??) leave 0 in the upper 16-bit
311                  * part of the external_file_attributes field. Instead, they
312                  * store file permission attributes in some extra field.
313                  * As a work-around, we search for the presence of one of
314                  * these extra fields and fall back to the MSDOS compatible
315                  * part of external_file_attributes if one of the known
316                  * e.f. types has been detected.
317                  * Later, we might implement extraction of the permission
318                  * bits from the VMS extra field. But for now, the work-around
319                  * should be sufficient to provide "readable" extracted files.
320                  * (For ASI Unix e.f., an experimental remap of the e.f.
321                  * mode value IS already provided!)
322                  */
323                 ush ebID;
324                 unsigned ebLen;
325                 uch *ef = G.extra_field;
326                 unsigned ef_len = G.crec.extra_field_length;
327
328                 while (!r && ef_len >= EB_HEADSIZE) {
329                     ebID = makeword(ef);
330                     ebLen = (unsigned)makeword(ef+EB_LEN);
331                     if (ebLen > (ef_len - EB_HEADSIZE))
332                         /* discoverd some e.f. inconsistency! */
333                         break;
334                     switch (ebID) {
335                       case EF_ASIUNIX:
336                         if (ebLen >= (EB_ASI_MODE+2)) {
337                             G.pInfo->file_attr =
338                               (unsigned)makeword(ef+(EB_HEADSIZE+EB_ASI_MODE));
339                             /* force stop of loop: */
340                             ef_len = (ebLen + EB_HEADSIZE);
341                             break;
342                         }
343                         /* else: fall through! */
344                       case EF_PKVMS:
345                         /* "found nondecypherable e.f. with perm. attr" */
346                         r = TRUE;
347                       default:
348                         break;
349                     }
350                     ef_len -= (ebLen + EB_HEADSIZE);
351                     ef += (ebLen + EB_HEADSIZE);
352                 }
353             }
354             if (!r) {
355 #ifdef SYMLINKS
356                 /* Check if the file is a (POSIX-compatible) symbolic link.
357                  * We restrict symlink support to those "made-by" hosts that
358                  * are known to support symbolic links.
359                  */
360                 G.pInfo->symlink = S_ISLNK(G.pInfo->file_attr) &&
361                                    SYMLINK_HOST(G.pInfo->hostnum);
362 #endif
363                 return 0;
364             }
365             /* fall through! */
366         /* all remaining cases:  expand MSDOS read-only bit into write perms */
367         case FS_FAT_:
368             /* PKWARE's PKZip for Unix marks entries as FS_FAT_, but stores the
369              * Unix attributes in the upper 16 bits of the external attributes
370              * field, just like Info-ZIP's Zip for Unix.  We try to use that
371              * value, after a check for consistency with the MSDOS attribute
372              * bits (see below).
373              */
374             G.pInfo->file_attr = (unsigned)(tmp >> 16);
375             /* fall through! */
376         case FS_HPFS_:
377         case FS_NTFS_:
378         case MAC_:
379         case TOPS20_:
380         default:
381             /* Ensure that DOS subdir bit is set when the entry's name ends
382              * in a '/'.  Some third-party Zip programs fail to set the subdir
383              * bit for directory entries.
384              */
385             if ((tmp & 0x10) == 0) {
386                 extent fnlen = strlen(G.filename);
387                 if (fnlen > 0 && G.filename[fnlen-1] == '/')
388                     tmp |= 0x10;
389             }
390             /* read-only bit --> write perms; subdir bit --> dir exec bit */
391             tmp = !(tmp & 1) << 1  |  (tmp & 0x10) >> 4;
392             if ((G.pInfo->file_attr & 0700) == (unsigned)(0400 | tmp<<6)) {
393                 /* keep previous G.pInfo->file_attr setting, when its "owner"
394                  * part appears to be consistent with DOS attribute flags!
395                  */
396 #ifdef SYMLINKS
397                 /* Entries "made by FS_FAT_" could have been zipped on a
398                  * system that supports POSIX-style symbolic links.
399                  */
400                 G.pInfo->symlink = S_ISLNK(G.pInfo->file_attr) &&
401                                    (G.pInfo->hostnum == FS_FAT_);
402 #endif
403                 return 0;
404             }
405             G.pInfo->file_attr = (unsigned)(0444 | tmp<<6 | tmp<<3 | tmp);
406             break;
407     } /* end switch (host-OS-created-by) */
408
409     /* for originating systems with no concept of "group," "other," "system": */
410     umask( (int)(tmp=umask(0)) );    /* apply mask to expanded r/w(/x) perms */
411     G.pInfo->file_attr &= ~tmp;
412
413     return 0;
414
415 } /* end function mapattr() */
416
417
418
419
420
421 /************************/
422 /*  Function mapname()  */
423 /************************/
424
425 int mapname(__G__ renamed)
426     __GDEF
427     int renamed;
428 /*
429  * returns:
430  *  MPN_OK          - no problem detected
431  *  MPN_INF_TRUNC   - caution (truncated filename)
432  *  MPN_INF_SKIP    - info "skip entry" (dir doesn't exist)
433  *  MPN_ERR_SKIP    - error -> skip entry
434  *  MPN_ERR_TOOLONG - error -> path is too long
435  *  MPN_NOMEM       - error (memory allocation failed) -> skip entry
436  *  [also MPN_VOL_LABEL, MPN_CREATED_DIR]
437  */
438 {
439     char pathcomp[FILNAMSIZ];      /* path-component buffer */
440     char *pp, *cp=(char *)NULL;    /* character pointers */
441     char *lastsemi=(char *)NULL;   /* pointer to last semi-colon in pathcomp */
442 #ifdef ACORN_FTYPE_NFS
443     char *lastcomma=(char *)NULL;  /* pointer to last comma in pathcomp */
444     RO_extra_block *ef_spark;      /* pointer Acorn FTYPE ef block */
445 #endif
446     int killed_ddot = FALSE;       /* is set when skipping "../" pathcomp */
447     int error = MPN_OK;
448     register unsigned workch;      /* hold the character being tested */
449
450
451 /*---------------------------------------------------------------------------
452     Initialize various pointers and counters and stuff.
453   ---------------------------------------------------------------------------*/
454
455     if (G.pInfo->vollabel)
456         return MPN_VOL_LABEL;   /* can't set disk volume labels in BeOS */
457
458     /* can create path as long as not just freshening, or if user told us */
459     G.create_dirs = (!uO.fflag || renamed);
460
461     G.created_dir = FALSE;      /* not yet */
462
463     /* user gave full pathname:  don't prepend rootpath */
464     G.renamed_fullpath = (renamed && (*G.filename == '/'));
465
466     if (checkdir(__G__ (char *)NULL, INIT) == MPN_NOMEM)
467         return MPN_NOMEM;       /* initialize path buffer, unless no memory */
468
469     *pathcomp = '\0';           /* initialize translation buffer */
470     pp = pathcomp;              /* point to translation buffer */
471     if (uO.jflag)               /* junking directories */
472         cp = (char *)strrchr(G.filename, '/');
473     if (cp == (char *)NULL)     /* no '/' or not junking dirs */
474         cp = G.filename;        /* point to internal zipfile-member pathname */
475     else
476         ++cp;                   /* point to start of last component of path */
477
478 /*---------------------------------------------------------------------------
479     Begin main loop through characters in filename.
480   ---------------------------------------------------------------------------*/
481
482     while ((workch = (uch)*cp++) != 0) {
483
484         switch (workch) {
485             case '/':             /* can assume -j flag not given */
486                 *pp = '\0';
487                 if (strcmp(pathcomp, ".") == 0) {
488                     /* don't bother appending "./" to the path */
489                     *pathcomp = '\0';
490                 } else if (!uO.ddotflag && strcmp(pathcomp, "..") == 0) {
491                     /* "../" dir traversal detected, skip over it */
492                     *pathcomp = '\0';
493                     killed_ddot = TRUE;     /* set "show message" flag */
494                 }
495                 /* when path component is not empty, append it now */
496                 if (*pathcomp != '\0' &&
497                     ((error = checkdir(__G__ pathcomp, APPEND_DIR))
498                      & MPN_MASK) > MPN_INF_TRUNC)
499                     return error;
500                 pp = pathcomp;    /* reset conversion buffer for next piece */
501                 lastsemi = (char *)NULL; /* leave direct. semi-colons alone */
502                 break;
503
504             case ';':             /* VMS version (or DEC-20 attrib?) */
505                 lastsemi = pp;
506                 *pp++ = ';';      /* keep for now; remove VMS ";##" */
507                 break;            /*  later, if requested */
508
509 #ifdef ACORN_FTYPE_NFS
510             case ',':             /* NFS filetype extension */
511                 lastcomma = pp;
512                 *pp++ = ',';      /* keep for now; may need to remove */
513                 break;            /*  later, if requested */
514 #endif
515
516             default:
517                 /* allow European characters in filenames: */
518                 if (isprint(workch) || (128 <= workch && workch <= 254))
519                     *pp++ = (char)workch;
520         } /* end switch */
521
522     } /* end while loop */
523
524     /* Show warning when stripping insecure "parent dir" path components */
525     if (killed_ddot && QCOND2) {
526         Info(slide, 0, ((char *)slide,
527           "warning:  skipped \"../\" path component(s) in %s\n",
528           FnFilter1(G.filename)));
529         if (!(error & ~MPN_MASK))
530             error = (error & MPN_MASK) | PK_WARN;
531     }
532
533 /*---------------------------------------------------------------------------
534     Report if directory was created (and no file to create:  filename ended
535     in '/'), check name to be sure it exists, and combine path and name be-
536     fore exiting.
537   ---------------------------------------------------------------------------*/
538
539     if (G.filename[strlen(G.filename) - 1] == '/') {
540         checkdir(__G__ G.filename, GETPATH);
541         if (G.created_dir) {
542             if (QCOND2) {
543                 Info(slide, 0, ((char *)slide, "   creating: %s\n",
544                   FnFilter1(G.filename)));
545             }
546
547             if (!uO.J_flag) {   /* Handle the BeOS extra field if present. */
548                 void *ptr = scanBeOSexfield(G.extra_field,
549                                             G.lrec.extra_field_length);
550                 if (ptr) {
551                     setBeOSexfield(G.filename, ptr);
552                 }
553             }
554
555 #ifndef NO_CHMOD
556             /* set approx. dir perms (make sure can still read/write in dir) */
557             if (chmod(G.filename, filtattr(__G__ G.pInfo->file_attr) | 0700))
558                 perror("chmod (directory attributes) error");
559 #endif
560
561             /* set dir time (note trailing '/') */
562             return (error & ~MPN_MASK) | MPN_CREATED_DIR;
563         }
564         /* TODO: should we re-write the BeOS extra field data in case it's */
565         /* changed?  The answer is yes. [Sept 1999 - cjh]                  */
566         if (!uO.J_flag) {   /* Handle the BeOS extra field if present. */
567             void *ptr = scanBeOSexfield(G.extra_field,
568                                         G.lrec.extra_field_length);
569             if (ptr) {
570                 setBeOSexfield(G.filename, ptr);
571             }
572         }
573
574         /* dir existed already; don't look for data to extract */
575         return (error & ~MPN_MASK) | MPN_INF_SKIP;
576     }
577
578     *pp = '\0';                   /* done with pathcomp:  terminate it */
579
580     /* if not saving them, remove VMS version numbers (appended ";###") */
581     if (!uO.V_flag && lastsemi) {
582         pp = lastsemi + 1;
583         while (isdigit((uch)(*pp)))
584             ++pp;
585         if (*pp == '\0')          /* only digits between ';' and end:  nuke */
586             *lastsemi = '\0';
587     }
588
589     /* On UNIX (and compatible systems), "." and ".." are reserved for
590      * directory navigation and cannot be used as regular file names.
591      * These reserved one-dot and two-dot names are mapped to "_" and "__".
592      */
593     if (strcmp(pathcomp, ".") == 0)
594         *pathcomp = '_';
595     else if (strcmp(pathcomp, "..") == 0)
596         strcpy(pathcomp, "__");
597
598 #ifdef ACORN_FTYPE_NFS
599     /* translate Acorn filetype information if asked to do so */
600     if (uO.acorn_nfs_ext &&
601         (ef_spark = (RO_extra_block *)
602                     getRISCOSexfield(G.extra_field, G.lrec.extra_field_length))
603         != (RO_extra_block *)NULL)
604     {
605         /* file *must* have a RISC OS extra field */
606         long ft = (long)makelong(ef_spark->loadaddr);
607         /*32-bit*/
608         if (lastcomma) {
609             pp = lastcomma + 1;
610             while (isxdigit((uch)(*pp))) ++pp;
611             if (pp == lastcomma+4 && *pp == '\0') *lastcomma='\0'; /* nuke */
612         }
613         if ((ft & 1<<31)==0) ft=0x000FFD00;
614         sprintf(pathcomp+strlen(pathcomp), ",%03x", (int)(ft>>8) & 0xFFF);
615     }
616 #endif /* ACORN_FTYPE_NFS */
617
618     if (*pathcomp == '\0') {
619         Info(slide, 1, ((char *)slide, "mapname:  conversion of %s failed\n",
620           FnFilter1(G.filename)));
621         return (error & ~MPN_MASK) | MPN_ERR_SKIP;
622     }
623
624     checkdir(__G__ pathcomp, APPEND_NAME);  /* returns 1 if truncated: care? */
625     checkdir(__G__ G.filename, GETPATH);
626
627     return error;
628
629 } /* end function mapname() */
630
631
632
633
634 /***********************/
635 /* Function checkdir() */
636 /***********************/
637
638 int checkdir(__G__ pathcomp, flag)
639     __GDEF
640     char *pathcomp;
641     int flag;
642 /*
643  * returns:
644  *  MPN_OK          - no problem detected
645  *  MPN_INF_TRUNC   - (on APPEND_NAME) truncated filename
646  *  MPN_INF_SKIP    - path doesn't exist, not allowed to create
647  *  MPN_ERR_SKIP    - path doesn't exist, tried to create and failed; or path
648  *                    exists and is not a directory, but is supposed to be
649  *  MPN_ERR_TOOLONG - path is too long
650  *  MPN_NOMEM       - can't allocate memory for filename buffers
651  */
652 {
653  /* static int rootlen = 0; */  /* length of rootpath */
654  /* static char *rootpath;  */  /* user's "extract-to" directory */
655  /* static char *buildpath; */  /* full path (so far) to extracted file */
656  /* static char *end;       */  /* pointer to end of buildpath ('\0') */
657
658 #   define FN_MASK   7
659 #   define FUNCTION  (flag & FN_MASK)
660
661
662
663 /*---------------------------------------------------------------------------
664     APPEND_DIR:  append the path component to the path being built and check
665     for its existence.  If doesn't exist and we are creating directories, do
666     so for this one; else signal success or error as appropriate.
667   ---------------------------------------------------------------------------*/
668
669     if (FUNCTION == APPEND_DIR) {
670         int too_long = FALSE;
671 #ifdef SHORT_NAMES
672         char *old_end = end;
673 #endif
674
675         Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp)));
676         while ((*G.end = *pathcomp++) != '\0')
677             ++G.end;
678 #ifdef SHORT_NAMES   /* path components restricted to 14 chars, typically */
679         if ((G.end-old_end) > FILENAME_MAX)  /* GRR:  proper constant? */
680             *(G.end = old_end + FILENAME_MAX) = '\0';
681 #endif
682
683         /* GRR:  could do better check, see if overrunning buffer as we go:
684          * check end-buildpath after each append, set warning variable if
685          * within 20 of FILNAMSIZ; then if var set, do careful check when
686          * appending.  Clear variable when begin new path. */
687
688         /* next check: need to append '/', at least one-char name, '\0' */
689         if ((G.end-G.buildpath) > FILNAMSIZ-3)
690             too_long = TRUE;                    /* check if extracting dir? */
691         if (SSTAT(G.buildpath, &G.statbuf)) {   /* path doesn't exist */
692             if (!G.create_dirs) { /* told not to create (freshening) */
693                 free(G.buildpath);
694                 return MPN_INF_SKIP;    /* path doesn't exist: nothing to do */
695             }
696             if (too_long) {
697                 Info(slide, 1, ((char *)slide,
698                   "checkdir error:  path too long: %s\n",
699                   FnFilter1(G.buildpath)));
700                 free(G.buildpath);
701                 /* no room for filenames:  fatal */
702                 return MPN_ERR_TOOLONG;
703             }
704             if (mkdir(G.buildpath, 0777) == -1) {   /* create the directory */
705                 Info(slide, 1, ((char *)slide,
706                   "checkdir error:  cannot create %s\n\
707                  %s\n\
708                  unable to process %s.\n",
709                   FnFilter2(G.buildpath),
710                   strerror(errno),
711                   FnFilter1(G.filename)));
712                 free(G.buildpath);
713                 /* path didn't exist, tried to create, failed */
714                 return MPN_ERR_SKIP;
715             }
716             G.created_dir = TRUE;
717         } else if (!S_ISDIR(G.statbuf.st_mode)) {
718             Info(slide, 1, ((char *)slide,
719               "checkdir error:  %s exists but is not directory\n\
720                  unable to process %s.\n",
721               FnFilter2(G.buildpath), FnFilter1(G.filename)));
722             free(G.buildpath);
723             /* path existed but wasn't dir */
724             return MPN_ERR_SKIP;
725         }
726         if (too_long) {
727             Info(slide, 1, ((char *)slide,
728               "checkdir error:  path too long: %s\n", FnFilter1(G.buildpath)));
729             free(G.buildpath);
730             /* no room for filenames:  fatal */
731             return MPN_ERR_TOOLONG;
732         }
733         *G.end++ = '/';
734         *G.end = '\0';
735         Trace((stderr, "buildpath now = [%s]\n", FnFilter1(G.buildpath)));
736         return MPN_OK;
737
738     } /* end if (FUNCTION == APPEND_DIR) */
739
740 /*---------------------------------------------------------------------------
741     GETPATH:  copy full path to the string pointed at by pathcomp, and free
742     G.buildpath.
743   ---------------------------------------------------------------------------*/
744
745     if (FUNCTION == GETPATH) {
746         strcpy(pathcomp, G.buildpath);
747         Trace((stderr, "getting and freeing path [%s]\n",
748           FnFilter1(pathcomp)));
749         free(G.buildpath);
750         G.buildpath = G.end = (char *)NULL;
751         return MPN_OK;
752     }
753
754 /*---------------------------------------------------------------------------
755     APPEND_NAME:  assume the path component is the filename; append it and
756     return without checking for existence.
757   ---------------------------------------------------------------------------*/
758
759     if (FUNCTION == APPEND_NAME) {
760 #ifdef SHORT_NAMES
761         char *old_end = end;
762 #endif
763
764         Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
765         while ((*G.end = *pathcomp++) != '\0') {
766             ++G.end;
767 #ifdef SHORT_NAMES  /* truncate name at 14 characters, typically */
768             if ((G.end-old_end) > FILENAME_MAX)    /* GRR:  proper constant? */
769                 *(G.end = old_end + FILENAME_MAX) = '\0';
770 #endif
771             if ((G.end-G.buildpath) >= FILNAMSIZ) {
772                 *--G.end = '\0';
773                 Info(slide, 0x201, ((char *)slide,
774                   "checkdir warning:  path too long; truncating\n\
775                    %s\n                -> %s\n",
776                   FnFilter1(G.filename), FnFilter2(G.buildpath)));
777                 return MPN_INF_TRUNC;   /* filename truncated */
778             }
779         }
780         Trace((stderr, "buildpath now = [%s]\n", FnFilter1(G.buildpath)));
781         /* could check for existence here, prompt for new name... */
782         return MPN_OK;
783     }
784
785 /*---------------------------------------------------------------------------
786     INIT:  allocate and initialize buffer space for the file currently being
787     extracted.  If file was renamed with an absolute path, don't prepend the
788     extract-to path.
789   ---------------------------------------------------------------------------*/
790
791 /* GRR:  for VMS and TOPS-20, add up to 13 to strlen */
792
793     if (FUNCTION == INIT) {
794         Trace((stderr, "initializing buildpath to "));
795 #ifdef ACORN_FTYPE_NFS
796         if ((G.buildpath = (char *)malloc(strlen(G.filename)+G.rootlen+
797                                           (uO.acorn_nfs_ext ? 5 : 1)))
798 #else
799         if ((G.buildpath = (char *)malloc(strlen(G.filename)+G.rootlen+1))
800 #endif
801             == (char *)NULL)
802             return MPN_NOMEM;
803         if ((G.rootlen > 0) && !G.renamed_fullpath) {
804             strcpy(G.buildpath, G.rootpath);
805             G.end = G.buildpath + G.rootlen;
806         } else {
807             *G.buildpath = '\0';
808             G.end = G.buildpath;
809         }
810         Trace((stderr, "[%s]\n", FnFilter1(G.buildpath)));
811         return MPN_OK;
812     }
813
814 /*---------------------------------------------------------------------------
815     ROOT:  if appropriate, store the path in rootpath and create it if
816     necessary; else assume it's a zipfile member and return.  This path
817     segment gets used in extracting all members from every zipfile specified
818     on the command line.
819   ---------------------------------------------------------------------------*/
820
821 #if (!defined(SFX) || defined(SFX_EXDIR))
822     if (FUNCTION == ROOT) {
823         Trace((stderr, "initializing root path to [%s]\n",
824           FnFilter1(pathcomp)));
825         if (pathcomp == (char *)NULL) {
826             G.rootlen = 0;
827             return MPN_OK;
828         }
829         if (G.rootlen > 0)      /* rootpath was already set, nothing to do */
830             return MPN_OK;
831         if ((G.rootlen = strlen(pathcomp)) > 0) {
832             char *tmproot;
833
834             if ((tmproot = (char *)malloc(G.rootlen+2)) == (char *)NULL) {
835                 G.rootlen = 0;
836                 return MPN_NOMEM;
837             }
838             strcpy(tmproot, pathcomp);
839             if (tmproot[G.rootlen-1] == '/') {
840                 tmproot[--G.rootlen] = '\0';
841             }
842             if (G.rootlen > 0 && (SSTAT(tmproot, &G.statbuf) ||
843                                   !S_ISDIR(G.statbuf.st_mode)))
844             {   /* path does not exist */
845                 if (!G.create_dirs /* || iswild(tmproot) */ ) {
846                     free(tmproot);
847                     G.rootlen = 0;
848                     /* skip (or treat as stored file) */
849                     return MPN_INF_SKIP;
850                 }
851                 /* create the directory (could add loop here scanning tmproot
852                  * to create more than one level, but why really necessary?) */
853                 if (mkdir(tmproot, 0777) == -1) {
854                     Info(slide, 1, ((char *)slide,
855                       "checkdir:  cannot create extraction directory: %s\n\
856            %s\n",
857                       FnFilter1(tmproot), strerror(errno)));
858                     free(tmproot);
859                     G.rootlen = 0;
860                     /* path didn't exist, tried to create, and failed: */
861                     /* file exists, or 2+ subdir levels required */
862                     return MPN_ERR_SKIP;
863                 }
864             }
865             tmproot[G.rootlen++] = '/';
866             tmproot[G.rootlen] = '\0';
867             if ((G.rootpath = (char *)realloc(tmproot, G.rootlen+1)) == NULL) {
868                 free(tmproot);
869                 G.rootlen = 0;
870                 return MPN_NOMEM;
871             }
872             Trace((stderr, "rootpath now = [%s]\n", FnFilter1(G.rootpath)));
873         }
874         return MPN_OK;
875     }
876 #endif /* !SFX || SFX_EXDIR */
877
878 /*---------------------------------------------------------------------------
879     END:  free rootpath, immediately prior to program exit.
880   ---------------------------------------------------------------------------*/
881
882     if (FUNCTION == END) {
883         Trace((stderr, "freeing rootpath\n"));
884         if (G.rootlen > 0) {
885             free(G.rootpath);
886             G.rootlen = 0;
887         }
888         return MPN_OK;
889     }
890
891     return MPN_INVALID; /* should never reach */
892
893 } /* end function checkdir() */
894
895
896
897
898
899 static int get_extattribs OF((__GPRO__ iztimes *pzt, ulg z_uidgid[2]));
900
901 static int get_extattribs(__G__ pzt, z_uidgid)
902     __GDEF
903     iztimes *pzt;
904     ulg z_uidgid[2];
905 {
906 /*---------------------------------------------------------------------------
907     Convert from MSDOS-format local time and date to Unix-format 32-bit GMT
908     time:  adjust base year from 1980 to 1970, do usual conversions from
909     yy/mm/dd hh:mm:ss to elapsed seconds, and account for timezone and day-
910     light savings time differences.  If we have a Unix extra field, however,
911     we're laughing:  both mtime and atime are ours.  On the other hand, we
912     then have to check for restoration of UID/GID.
913   ---------------------------------------------------------------------------*/
914     int have_uidgid_flg;
915     unsigned eb_izux_flg;
916
917     eb_izux_flg = (G.extra_field ? ef_scan_for_izux(G.extra_field,
918                    G.lrec.extra_field_length, 0, G.lrec.last_mod_dos_datetime,
919 #ifdef IZ_CHECK_TZ
920                    (G.tz_is_valid ? pzt : NULL),
921 #else
922                    pzt,
923 #endif
924                    z_uidgid) : 0);
925     if (eb_izux_flg & EB_UT_FL_MTIME) {
926         TTrace((stderr, "\nget_extattribs:  Unix e.f. modif. time = %ld\n",
927           pzt->mtime));
928     } else {
929         pzt->mtime = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
930     }
931     if (eb_izux_flg & EB_UT_FL_ATIME) {
932         TTrace((stderr, "get_extattribs:  Unix e.f. access time = %ld\n",
933           pzt->atime));
934     } else {
935         pzt->atime = pzt->mtime;
936         TTrace((stderr, "\nget_extattribs:  modification/access times = %ld\n",
937           pzt->mtime));
938     }
939
940     /* if -X option was specified and we have UID/GID info, restore it */
941     have_uidgid_flg =
942 #ifdef RESTORE_UIDGID
943             (uO.X_flag && (eb_izux_flg & EB_UX2_VALID));
944 #else
945             0;
946 #endif
947     return have_uidgid_flg;
948 }
949
950
951
952
953 /****************************/
954 /* Function close_outfile() */
955 /****************************/
956
957 void close_outfile(__G)    /* GRR: change to return PK-style warning level */
958     __GDEF
959 {
960     union {
961         iztimes t3;             /* mtime, atime, ctime */
962         ztimbuf t2;             /* modtime, actime */
963     } zt;
964     ulg z_uidgid[2];
965     int have_uidgid_flg;
966
967     have_uidgid_flg = get_extattribs(__G__ &(zt.t3), z_uidgid);
968
969 /*---------------------------------------------------------------------------
970     If symbolic links are supported, allocate storage for a symlink control
971     structure, put the uncompressed "data" and other required info in it, and
972     add the structure to the "deferred symlinks" chain.  Since we know it's a
973     symbolic link to start with, we shouldn't have to worry about overflowing
974     unsigned ints with unsigned longs.
975   ---------------------------------------------------------------------------*/
976
977 #ifdef SYMLINKS
978     if (G.symlnk) {
979         extent ucsize = (extent)G.lrec.ucsize;
980         unsigned BeOSef_len = 0;
981         extent slnk_entrysize;
982         uch *BeOS_exfld;
983         slinkentry *slnk_entry;
984
985         if (!uO.J_flag) {
986             /* Symlinks can have attributes, too. */
987             BeOS_exfld = scanBeOSexfield(G.extra_field,
988                                          G.lrec.extra_field_length);
989             if (BeOS_exfld) {
990                 BeOSef_len = makeword(EB_LEN + BeOS_exfld) + EB_HEADSIZE;
991             }
992         }
993
994         /* size of the symlink entry is the sum of
995          *  (struct size (includes 1st '\0') + 1 additional trailing '\0'),
996          *  system specific attribute data size (might be 0),
997          *  and the lengths of name and link target.
998          */
999         slnk_entrysize = (sizeof(slinkentry) + 1) + BeOSef_len +
1000                          ucsize + strlen(G.filename);
1001
1002         if (slnk_entrysize < ucsize) {
1003             Info(slide, 0x201, ((char *)slide,
1004               "warning:  symbolic link (%s) failed: mem alloc overflow\n",
1005               FnFilter1(G.filename)));
1006             fclose(G.outfile);
1007             return;
1008         }
1009
1010         if ((slnk_entry = (slinkentry *)malloc(slnk_entrysize)) == NULL) {
1011             Info(slide, 0x201, ((char *)slide,
1012               "warning:  symbolic link (%s) failed: no mem\n",
1013               FnFilter1(G.filename)));
1014             fclose(G.outfile);
1015             return;
1016         }
1017         slnk_entry->next = NULL;
1018         slnk_entry->targetlen = ucsize;
1019         slnk_entry->attriblen = BeOSef_len;
1020         slnk_entry->target = slnk_entry->buf + BeOSef_len;
1021         slnk_entry->fname = slnk_entry->target + ucsize + 1;
1022         strcpy(slnk_entry->fname, G.filename);
1023         if (BeOSef_len > 0)
1024             memcpy(slnk_entry->buf, BeOS_exfld, BeOSef_len);
1025
1026         /* move back to the start of the file to re-read the "link data" */
1027         rewind(G.outfile);
1028
1029         if (fread(slnk_entry->target, 1, ucsize, G.outfile) != ucsize)
1030         {
1031             Info(slide, 0x201, ((char *)slide,
1032               "warning:  symbolic link (%s) failed\n",
1033               FnFilter1(G.filename)));
1034             free(slnk_entry);
1035             fclose(G.outfile);
1036             return;
1037         }
1038         fclose(G.outfile);                  /* close "link" file for good... */
1039         slnk_entry->target[ucsize] = '\0';
1040         if (QCOND2)
1041             Info(slide, 0, ((char *)slide, "-> %s ",
1042               FnFilter1(slnk_entry->target)));
1043         /* add this symlink record to the list of deferred symlinks */
1044         if (G.slink_last != NULL)
1045             G.slink_last->next = slnk_entry;
1046         else
1047             G.slink_head = slnk_entry;
1048         G.slink_last = slnk_entry;
1049         return;
1050     }
1051 #endif /* SYMLINKS */
1052
1053     fclose(G.outfile);
1054
1055     /* handle the BeOS extra field if present */
1056     if (!uO.J_flag) {
1057         void *ptr = scanBeOSexfield(G.extra_field,
1058                                     G.lrec.extra_field_length);
1059
1060         if (ptr) {
1061             setBeOSexfield(G.filename, ptr);
1062 #ifdef BEOS_ASSIGN_FILETYPE
1063         } else {
1064             /* Otherwise, ask the system to try assigning a MIME type. */
1065             assign_MIME( G.filename );
1066 #endif
1067         }
1068     }
1069
1070 /*---------------------------------------------------------------------------
1071     Change the file permissions from default ones to those stored in the
1072     zipfile.
1073   ---------------------------------------------------------------------------*/
1074
1075 #ifndef NO_CHMOD
1076     if (chmod(G.filename, filtattr(__G__ G.pInfo->file_attr)))
1077         perror("chmod (file attributes) error");
1078 #endif
1079
1080     /* if -X option was specified and we have UID/GID info, restore it */
1081     if (have_uidgid_flg
1082         /* check that both uid and gid values fit into their data sizes */
1083         && ((ulg)(uid_t)(z_uidgid[0]) == z_uidgid[0])
1084         && ((ulg)(gid_t)(z_uidgid[1]) == z_uidgid[1])) {
1085         TTrace((stderr, "close_outfile:  restoring Unix UID/GID info\n"));
1086         if (chown(G.filename, (uid_t)z_uidgid[0], (gid_t)z_uidgid[1]))
1087         {
1088             if (uO.qflag)
1089                 Info(slide, 0x201, ((char *)slide, CannotSetItemUidGid,
1090                   z_uidgid[0], z_uidgid[1], FnFilter1(G.filename),
1091                   strerror(errno)));
1092             else
1093                 Info(slide, 0x201, ((char *)slide, CannotSetUidGid,
1094                   z_uidgid[0], z_uidgid[1], strerror(errno)));
1095         }
1096     }
1097
1098     /* skip restoring time stamps on user's request */
1099     if (uO.D_flag <= 1) {
1100         /* set the file's access and modification times */
1101         if (utime(G.filename, &(zt.t2))) {
1102             if (uO.qflag)
1103                 Info(slide, 0x201, ((char *)slide, CannotSetItemTimestamps,
1104                   FnFilter1(G.filename), strerror(errno)));
1105             else
1106                 Info(slide, 0x201, ((char *)slide, CannotSetTimestamps,
1107                   strerror(errno)));
1108         }
1109     }
1110
1111 } /* end function close_outfile() */
1112
1113
1114
1115
1116 #ifdef SYMLINKS
1117 int set_symlnk_attribs(__G__ slnk_entry)
1118     __GDEF
1119     slinkentry *slnk_entry;
1120 {
1121     if (slnk_entry->attriblen > 0)
1122         setBeOSexfield(slnk_entry->fname, (uch *)slnk_entry->buf);
1123     /* currently, no error propagation... */
1124     return PK_OK;
1125 } /* end function set_symlnk_attribs() */
1126 #endif /* SYMLINKS */
1127
1128
1129
1130
1131 #ifdef SET_DIR_ATTRIB
1132 /* messages of code for setting directory attributes */
1133 #  ifndef NO_CHMOD
1134   static ZCONST char DirlistChmodFailed[] =
1135     "warning:  cannot set permissions for %s\n          %s\n";
1136 #  endif
1137
1138
1139 int defer_dir_attribs(__G__ pd)
1140     __GDEF
1141     direntry **pd;
1142 {
1143     uxdirattr *d_entry;
1144
1145     d_entry = (uxdirattr *)malloc(sizeof(uxdirattr) + strlen(G.filename));
1146     *pd = (direntry *)d_entry;
1147     if (d_entry == (uxdirattr *)NULL) {
1148         return PK_MEM;
1149     }
1150     d_entry->fn = d_entry->fnbuf;
1151     strcpy(d_entry->fn, G.filename);
1152
1153     d_entry->perms = G.pInfo->file_attr;
1154
1155     d_entry->have_uidgid = get_extattribs(__G__ &(d_entry->u.t3),
1156                                           d_entry->uidgid);
1157     return PK_OK;
1158 } /* end function defer_dir_attribs() */
1159
1160
1161 int set_direc_attribs(__G__ d)
1162     __GDEF
1163     direntry *d;
1164 {
1165     int errval = PK_OK;
1166
1167     if (UxAtt(d)->have_uidgid &&
1168         /* check that both uid and gid values fit into their data sizes */
1169         ((ulg)(uid_t)(UxAtt(d)->uidgid[0]) == UxAtt(d)->uidgid[0]) &&
1170         ((ulg)(gid_t)(UxAtt(d)->uidgid[1]) == UxAtt(d)->uidgid[1]) &&
1171         chown(UxAtt(d)->fn, (uid_t)UxAtt(d)->uidgid[0],
1172               (gid_t)UxAtt(d)->uidgid[1]))
1173     {
1174         Info(slide, 0x201, ((char *)slide, CannotSetItemUidGid,
1175           UxAtt(d)->uidgid[0], UxAtt(d)->uidgid[1], FnFilter1(d->fn),
1176           strerror(errno)));
1177         if (!errval)
1178             errval = PK_WARN;
1179     }
1180     /* Skip restoring directory time stamps on user' request. */
1181     if (uO.D_flag <= 0) {
1182         /* restore directory timestamps */
1183         if (utime(d->fn, (const struct utimbuf *)&UxAtt(d)->u.t2)) {
1184             Info(slide, 0x201, ((char *)slide, CannotSetItemTimestamps,
1185               FnFilter1(d->fn), strerror(errno)));
1186             if (!errval)
1187                 errval = PK_WARN;
1188         }
1189     }
1190 #ifndef NO_CHMOD
1191     if (chmod(d->fn, filtattr(__G__ UxAtt(d)->perms))) {
1192         Info(slide, 0x201, ((char *)slide, DirlistChmodFailed,
1193           FnFilter1(d->fn), strerror(errno)));
1194         if (!errval)
1195             errval = PK_WARN;
1196     }
1197 #endif /* !NO_CHMOD */
1198     return errval;
1199 } /* end function set_direc_attribs() */
1200
1201 #endif /* SET_DIR_ATTRIB */
1202
1203
1204
1205
1206 #ifdef TIMESTAMP
1207
1208 /***************************/
1209 /*  Function stamp_file()  */
1210 /***************************/
1211
1212 int stamp_file(fname, modtime)
1213     ZCONST char *fname;
1214     time_t modtime;
1215 {
1216     struct utimbuf tp;
1217
1218     tp.modtime = tp.actime = modtime;
1219     return (utime(fname, &tp));
1220
1221 } /* end function stamp_file() */
1222
1223 #endif /* TIMESTAMP */
1224
1225
1226
1227
1228 #ifndef SFX
1229
1230 /************************/
1231 /*  Function version()  */
1232 /************************/
1233
1234 void version(__G)
1235     __GDEF
1236 {
1237     sprintf((char *)slide, LoadFarString(CompiledWith),
1238 #if defined(__MWERKS__)
1239       "Metrowerks CodeWarrior", "",
1240 #elif defined(__GNUC__)
1241       "GNU C ", __VERSION__,
1242 #else
1243       "(unknown compiler) ","",
1244 #endif
1245       "BeOS ",
1246
1247 #ifdef __POWERPC__
1248       "(PowerPC)",
1249 #else
1250 # ifdef __INTEL__
1251       "(x86)",
1252 # else
1253       "(unknown)",   /* someday we may have other architectures... */
1254 # endif
1255 #endif
1256
1257 #ifdef __DATE__
1258       " on ", __DATE__
1259 #else
1260       "", ""
1261 #endif
1262     );
1263
1264     (*G.message)((zvoid *)&G, slide, (ulg)strlen((char *)slide), 0);
1265
1266 } /* end function version() */
1267
1268 #endif /* !SFX */
1269
1270
1271
1272 /******************************/
1273 /* Extra field functions      */
1274 /******************************/
1275
1276 /*
1277 ** Scan the extra fields in extra_field, and look for a BeOS EF; return a
1278 ** pointer to that EF, or NULL if it's not there.
1279 */
1280 static uch *scanBeOSexfield(const uch *ef_ptr, unsigned ef_len)
1281 {
1282     while( ef_ptr != NULL && ef_len >= EB_HEADSIZE ) {
1283         unsigned eb_id  = makeword(EB_ID + ef_ptr);
1284         unsigned eb_len = makeword(EB_LEN + ef_ptr);
1285
1286         if (eb_len > (ef_len - EB_HEADSIZE)) {
1287             Trace((stderr,
1288               "scanBeOSexfield: block length %u > rest ef_size %u\n", eb_len,
1289               ef_len - EB_HEADSIZE));
1290             break;
1291         }
1292
1293         if (eb_id == EF_BEOS && eb_len >= EB_BEOS_HLEN) {
1294             return (uch *)ef_ptr;
1295         }
1296
1297         ef_ptr += (eb_len + EB_HEADSIZE);
1298         ef_len -= (eb_len + EB_HEADSIZE);
1299     }
1300
1301     return NULL;
1302 }
1303
1304 /* Used by setBeOSexfield():
1305
1306 Set a file/directory's attributes to the attributes passed in.
1307
1308 If set_file_attrs() fails, an error will be returned:
1309
1310      EOK - no errors occurred
1311
1312 (other values will be whatever the failed function returned; no docs
1313 yet, or I'd list a few)
1314 */
1315 static int set_file_attrs( const char *name,
1316                            const unsigned char *attr_buff,
1317                            const off_t attr_size )
1318 {
1319     int                  retval = EOK;
1320     unsigned char       *ptr;
1321     const unsigned char *guard;
1322     int                  fd;
1323
1324     ptr   = (unsigned char *)attr_buff;
1325     guard = ptr + attr_size;
1326
1327     fd = open(name, O_RDWR | O_NOTRAVERSE);
1328     if (fd < 0) {
1329         return errno; /* should it be -fd ? */
1330     }
1331
1332     while (ptr < guard) {
1333         ssize_t              wrote_bytes;
1334         struct attr_info     fa_info;
1335         const char          *attr_name;
1336         unsigned char       *attr_data;
1337
1338         attr_name  = (char *)&(ptr[0]);
1339         ptr       += strlen(attr_name) + 1;
1340
1341         /* The attr_info data is stored in big-endian format because the */
1342         /* PowerPC port was here first.                                  */
1343         memcpy(&fa_info, ptr, sizeof(struct attr_info));
1344         fa_info.type = (uint32)B_BENDIAN_TO_HOST_INT32( fa_info.type );
1345         fa_info.size = (off_t)B_BENDIAN_TO_HOST_INT64( fa_info.size );
1346         ptr     += sizeof(struct attr_info);
1347
1348         if (fa_info.size < 0LL) {
1349             Info(slide, 0x201, ((char *)slide,
1350                  "warning: skipping attribute with invalid length (%Ld)\n",
1351                  fa_info.size));
1352             break;
1353         }
1354
1355         attr_data  = ptr;
1356         ptr       += fa_info.size;
1357
1358         if (ptr > guard) {
1359             /* We've got a truncated attribute. */
1360             Info(slide, 0x201, ((char *)slide,
1361                  "warning: truncated attribute\n"));
1362             break;
1363         }
1364
1365         /* Wave the magic wand... this will swap Be-known types properly. */
1366         (void)swap_data( fa_info.type, attr_data, fa_info.size,
1367                          B_SWAP_BENDIAN_TO_HOST );
1368
1369         wrote_bytes = fs_write_attr(fd, attr_name, fa_info.type, 0,
1370                                     attr_data, fa_info.size);
1371         if (wrote_bytes != fa_info.size) {
1372             Info(slide, 0x201, ((char *)slide,
1373                  "warning: wrote %ld attribute bytes of %ld\n",
1374                  (unsigned long)wrote_bytes,(unsigned long)fa_info.size));
1375         }
1376     }
1377
1378     close(fd);
1379
1380     return retval;
1381 }
1382
1383 static void setBeOSexfield(const char *path, uch *extra_field)
1384 {
1385     uch *ptr       = extra_field;
1386     ush  id        = 0;
1387     ush  size      = 0;
1388     ulg  full_size = 0;
1389     uch  flags     = 0;
1390     uch *attrbuff  = NULL;
1391     int retval;
1392
1393     if( extra_field == NULL ) {
1394         return;
1395     }
1396
1397     /* Collect the data from the extra field buffer. */
1398     id        = makeword(ptr);    ptr += 2;   /* we don't use this... */
1399     size      = makeword(ptr);    ptr += 2;
1400     full_size = makelong(ptr);    ptr += 4;
1401     flags     = *ptr;             ptr++;
1402
1403     /* Do a little sanity checking. */
1404     if (flags & EB_BE_FL_BADBITS) {
1405         /* corrupted or unsupported */
1406         Info(slide, 0x201, ((char *)slide,
1407           "Unsupported flags set for this BeOS extra field, skipping.\n"));
1408         return;
1409     }
1410     if (size <= EB_BEOS_HLEN) {
1411         /* corrupted, unsupported, or truncated */
1412         Info(slide, 0x201, ((char *)slide,
1413              "BeOS extra field is %d bytes, should be at least %d.\n", size,
1414              EB_BEOS_HLEN));
1415         return;
1416     }
1417     if (full_size < (size - EB_BEOS_HLEN)) {
1418         /* possible old archive? will this screw up on valid archives? */
1419         Info(slide, 0x201, ((char *)slide,
1420              "Skipping attributes: BeOS extra field is %d bytes, "
1421              "data size is %ld.\n", size - EB_BEOS_HLEN, full_size));
1422         return;
1423     }
1424
1425     /* Find the BeOS file attribute data. */
1426     if (flags & EB_BE_FL_UNCMPR) {
1427         /* Uncompressed data */
1428         attrbuff = ptr;
1429     } else {
1430         /* Compressed data */
1431         attrbuff = (uch *)malloc( full_size );
1432         if (attrbuff == NULL) {
1433             /* No memory to uncompress attributes */
1434             Info(slide, 0x201, ((char *)slide,
1435                  "Can't allocate memory to uncompress file attributes.\n"));
1436             return;
1437         }
1438
1439         retval = memextract(__G__ attrbuff, full_size,
1440                             ptr, size - EB_BEOS_HLEN);
1441         if( retval != PK_OK ) {
1442             /* error uncompressing attributes */
1443             Info(slide, 0x201, ((char *)slide,
1444                  "Error uncompressing file attributes.\n"));
1445
1446             /* Some errors here might not be so bad; we should expect */
1447             /* some truncated data, for example.  If the data was     */
1448             /* corrupt, we should _not_ attempt to restore the attrs  */
1449             /* for this file... there's no way to detect what attrs   */
1450             /* are good and which are bad.                            */
1451             free (attrbuff);
1452             return;
1453         }
1454     }
1455
1456     /* Now attempt to set the file attributes on the extracted file. */
1457     retval = set_file_attrs(path, attrbuff, (off_t)full_size);
1458     if (retval != EOK) {
1459         Info(slide, 0x201, ((char *)slide,
1460              "Error writing file attributes.\n"));
1461     }
1462
1463     /* Clean up, if necessary */
1464     if (attrbuff != ptr) {
1465         free(attrbuff);
1466     }
1467
1468     return;
1469 }
1470
1471 #ifdef BEOS_USE_PRINTEXFIELD
1472 static void printBeOSexfield( int isdir, uch *extra_field )
1473 {
1474     uch *ptr       = extra_field;
1475     ush  id        = 0;
1476     ush  size      = 0;
1477     ulg  full_size = 0;
1478     uch  flags     = 0;
1479
1480     /* Tell picky compilers to be quiet. */
1481     isdir = isdir;
1482
1483     if( extra_field == NULL ) {
1484         return;
1485     }
1486
1487     /* Collect the data from the buffer. */
1488     id        = makeword( ptr );    ptr += 2;
1489     size      = makeword( ptr );    ptr += 2;
1490     full_size = makelong( ptr );    ptr += 4;
1491     flags     = *ptr;               ptr++;
1492
1493     if( id != EF_BEOS ) {
1494         /* not a 'Be' field */
1495         printf("\t*** Unknown field type (0x%04x, '%c%c')\n", id,
1496                (char)(id >> 8), (char)id);
1497     }
1498
1499     if( flags & EB_BE_FL_BADBITS ) {
1500         /* corrupted or unsupported */
1501         printf("\t*** Corrupted BeOS extra field:\n");
1502         printf("\t*** unknown bits set in the flags\n");
1503         printf("\t*** (Possibly created by an old version of zip for BeOS.\n");
1504     }
1505
1506     if( size <= EB_BEOS_HLEN ) {
1507         /* corrupted, unsupported, or truncated */
1508         printf("\t*** Corrupted BeOS extra field:\n");
1509         printf("\t*** size is %d, should be larger than %d\n", size,
1510                EB_BEOS_HLEN );
1511     }
1512
1513     if( flags & EB_BE_FL_UNCMPR ) {
1514         /* Uncompressed data */
1515         printf("\tBeOS extra field data (uncompressed):\n");
1516         printf("\t\t%ld data bytes\n", full_size);
1517     } else {
1518         /* Compressed data */
1519         printf("\tBeOS extra field data (compressed):\n");
1520         printf("\t\t%d compressed bytes\n", size - EB_BEOS_HLEN);
1521         printf("\t\t%ld uncompressed bytes\n", full_size);
1522     }
1523 }
1524 #endif
1525
1526 #ifdef BEOS_ASSIGN_FILETYPE
1527 /* Note: This will no longer be necessary in BeOS PR4; update_mime_info()    */
1528 /* will be updated to build its own absolute pathname if it's not given one. */
1529 static void assign_MIME( const char *file )
1530 {
1531     char *fullname;
1532     char buff[PATH_MAX], cwd_buff[PATH_MAX];
1533     int retval;
1534
1535     if( file[0] == '/' ) {
1536         fullname = (char *)file;
1537     } else {
1538         sprintf( buff, "%s/%s", getcwd( cwd_buff, PATH_MAX ), file );
1539         fullname = buff;
1540     }
1541
1542     retval = update_mime_info( fullname, FALSE, TRUE, TRUE );
1543 }
1544 #endif