xref: /haiku/src/bin/unzip/beos.c (revision aff60bb217827097c13d643275fdf1f1c66e7f17)
1 /*
2   Copyright (c) 1990-2002 Info-ZIP.  All rights reserved.
3 
4   See the accompanying file LICENSE, version 2000-Apr-09 or later
5   (the contents of which are also included in unzip.h) for terms of use.
6   If, for some reason, all these files are missing, the Info-ZIP license
7   also may be found at:  ftp://ftp.info-zip.org/pub/infozip/license.html
8 */
9 /*---------------------------------------------------------------------------
10 
11   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              set_direc_attribs()
22              stamp_file()
23              version()
24              scanBeOSexfield()
25              set_file_attrs()
26              setBeOSexfield()
27              printBeOSexfield()
28              assign_MIME()
29 
30   ---------------------------------------------------------------------------*/
31 
32 
33 #define UNZIP_INTERNAL
34 #include "unzip.h"
35 
36 #include "beos.h"
37 #include <errno.h>             /* Just make sure we've got a few things... */
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 
42 #include <dirent.h>
43 
44 /* For the new post-DR8 file attributes */
45 #include <fs_attr.h>
46 #include <ByteOrder.h>
47 #include <Mime.h>
48 
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));
54 #endif
55 #ifdef BEOS_ASSIGN_FILETYPE
56 static void assign_MIME( const char * );
57 #endif
58 
59 #ifdef ACORN_FTYPE_NFS
60 /* Acorn bits for NFS filetyping */
61 typedef struct {
62   uch ID[2];
63   uch size[2];
64   uch ID_2[4];
65   uch loadaddr[4];
66   uch execaddr[4];
67   uch attr[4];
68 } RO_extra_block;
69 
70 #endif /* ACORN_FTYPE_NFS */
71 
72 static int created_dir;        /* used in mapname(), checkdir() */
73 static int renamed_fullpath;   /* ditto */
74 
75 #ifndef SFX
76 
77 /**********************/
78 /* Function do_wild() */   /* for porting:  dir separator; match(ignore_case) */
79 /**********************/
80 
81 char *do_wild(__G__ wildspec)
82     __GDEF
83     ZCONST char *wildspec;  /* only used first time on a given dir */
84 {
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;
89     struct dirent *file;
90 
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[].
94      */
95     if (!notfirstcall) {    /* first call:  must initialize everything */
96         notfirstcall = TRUE;
97 
98         if (!iswild(wildspec)) {
99             strcpy(matchname, wildspec);
100             have_dirname = FALSE;
101             wild_dir = NULL;
102             return matchname;
103         }
104 
105         /* break the wildspec into a directory part and a wildcard filename */
106         if ((wildname = strrchr(wildspec, '/')) == (ZCONST char *)NULL) {
107             dirname = ".";
108             dirnamelen = 1;
109             have_dirname = FALSE;
110             wildname = wildspec;
111         } else {
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 */
119             }
120             strncpy(dirname, wildspec, dirnamelen);
121             dirname[dirnamelen] = '\0';   /* terminate for strcpy below */
122             have_dirname = TRUE;
123         }
124 
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. */
130                     if (have_dirname) {
131                         strcpy(matchname, dirname);
132                         strcpy(matchname+dirnamelen, file->d_name);
133                     } else
134                         strcpy(matchname, file->d_name);
135                     return matchname;
136                 }
137             }
138             /* if we get to here directory is exhausted, so close it */
139             closedir(wild_dir);
140             wild_dir = (DIR *)NULL;
141         }
142 
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);
146         return matchname;
147     }
148 
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 */
152         if (have_dirname)
153             free(dirname);
154         return (char *)NULL;
155     }
156 
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
159      * matchname already.
160      */
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 */
165             if (have_dirname) {
166                 /* strcpy(matchname, dirname); */
167                 strcpy(matchname+dirnamelen, file->d_name);
168             } else
169                 strcpy(matchname, file->d_name);
170             return matchname;
171         }
172     }
173 
174     closedir(wild_dir);     /* have read at least one entry; nothing left */
175     wild_dir = (DIR *)NULL;
176     notfirstcall = FALSE;   /* reset for new wildspec */
177     if (have_dirname)
178         free(dirname);
179     return (char *)NULL;
180 
181 } /* end function do_wild() */
182 
183 #endif /* !SFX */
184 
185 
186 
187 
188 
189 /**********************/
190 /* Function mapattr() */
191 /**********************/
192 
193 int mapattr(__G)
194     __GDEF
195 {
196     ulg tmp = G.crec.external_file_attributes;
197 
198     switch (G.pInfo->hostnum) {
199         case AMIGA_:
200             tmp = (unsigned)(tmp>>17 & 7);   /* Amiga RWE bits */
201             G.pInfo->file_attr = (unsigned)(tmp<<6 | tmp<<3 | tmp);
202             break;
203         case THEOS_:
204             tmp &= 0xF1FFFFFFL;
205             if ((tmp & 0xF0000000L) != 0x40000000L)
206                 tmp &= 0x01FFFFFFL;     /* not a dir, mask all ftype bits */
207             else
208                 tmp &= 0x41FFFFFFL;     /* leave directory bit as set */
209             /* fall through! */
210         case BEOS_:
211         case UNIX_:
212         case VMS_:
213         case ACORN_:
214         case ATARI_:
215         case QDOS_:
216         case TANDEM_:
217             G.pInfo->file_attr = (unsigned)(tmp >> 16);
218             if (G.pInfo->file_attr != 0 || !G.extra_field) {
219                 return 0;
220             } else {
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!)
234                  */
235                 ush ebID;
236                 unsigned ebLen;
237                 uch *ef = G.extra_field;
238                 unsigned ef_len = G.crec.extra_field_length;
239                 int r = FALSE;
240 
241                 while (!r && ef_len >= EB_HEADSIZE) {
242                     ebID = makeword(ef);
243                     ebLen = (unsigned)makeword(ef+EB_LEN);
244                     if (ebLen > (ef_len - EB_HEADSIZE))
245                         /* discoverd some e.f. inconsistency! */
246                         break;
247                     switch (ebID) {
248                       case EF_ASIUNIX:
249                         if (ebLen >= (EB_ASI_MODE+2)) {
250                             G.pInfo->file_attr =
251                               (unsigned)makeword(ef+(EB_HEADSIZE+EB_ASI_MODE));
252                             /* force stop of loop: */
253                             ef_len = (ebLen + EB_HEADSIZE);
254                             break;
255                         }
256                         /* else: fall through! */
257                       case EF_PKVMS:
258                         /* "found nondecypherable e.f. with perm. attr" */
259                         r = TRUE;
260                       default:
261                         break;
262                     }
263                     ef_len -= (ebLen + EB_HEADSIZE);
264                     ef += (ebLen + EB_HEADSIZE);
265                 }
266                 if (!r)
267                     return 0;
268             }
269             /* fall through! */
270         /* all remaining cases:  expand MSDOS read-only bit into write perms */
271         case FS_FAT_:
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
276              * bits (see below).
277              */
278             G.pInfo->file_attr = (unsigned)(tmp >> 16);
279             /* fall through! */
280         case FS_HPFS_:
281         case FS_NTFS_:
282         case MAC_:
283         case TOPS20_:
284         default:
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.
288              */
289             if ((tmp & 0x10) == 0) {
290                 extent fnlen = strlen(G.filename);
291                 if (fnlen > 0 && G.filename[fnlen-1] == '/')
292                     tmp |= 0x10;
293             }
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!
299                  */
300                 return 0;
301             G.pInfo->file_attr = (unsigned)(0444 | tmp<<6 | tmp<<3 | tmp);
302             break;
303     } /* end switch (host-OS-created-by) */
304 
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;
308 
309     return 0;
310 
311 } /* end function mapattr() */
312 
313 
314 
315 
316 
317 /************************/
318 /*  Function mapname()  */
319 /************************/
320 
321 int mapname(__G__ renamed)
322     __GDEF
323     int renamed;
324 /*
325  * returns:
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]
333  */
334 {
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 */
341 #endif
342     int quote = FALSE;             /* flags */
343     int killed_ddot = FALSE;       /* is set when skipping "../" pathcomp */
344     int error = MPN_OK;
345     register unsigned workch;      /* hold the character being tested */
346 
347 
348 /*---------------------------------------------------------------------------
349     Initialize various pointers and counters and stuff.
350   ---------------------------------------------------------------------------*/
351 
352     if (G.pInfo->vollabel)
353         return MPN_VOL_LABEL;   /* can't set disk volume labels in BeOS */
354 
355     /* can create path as long as not just freshening, or if user told us */
356     G.create_dirs = (!uO.fflag || renamed);
357 
358     created_dir = FALSE;        /* not yet */
359 
360     /* user gave full pathname:  don't prepend rootpath */
361     renamed_fullpath = (renamed && (*G.filename == '/'));
362 
363     if (checkdir(__G__ (char *)NULL, INIT) == MPN_NOMEM)
364         return MPN_NOMEM;       /* initialize path buffer, unless no memory */
365 
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 */
372     else
373         ++cp;                   /* point to start of last component of path */
374 
375 /*---------------------------------------------------------------------------
376     Begin main loop through characters in filename.
377   ---------------------------------------------------------------------------*/
378 
379     while ((workch = (uch)*cp++) != 0) {
380 
381         if (quote) {                 /* if character quoted, */
382             *pp++ = (char)workch;    /*  include it literally */
383             quote = FALSE;
384         } else
385             switch (workch) {
386             case '/':             /* can assume -j flag not given */
387                 *pp = '\0';
388                 if (((error = checkdir(__G__ pathcomp, APPEND_DIR)) & MPN_MASK)
389                      > MPN_INF_TRUNC)
390                     return error;
391                 pp = pathcomp;    /* reset conversion buffer for next piece */
392                 lastsemi = (char *)NULL; /* leave directory semi-colons alone */
393                 break;
394 
395             case '.':
396                 if (pp == pathcomp) {   /* nothing appended yet... */
397                     if (*cp == '/') {   /* don't bother appending "./" to */
398                         ++cp;           /*  the path: skip behind the '/' */
399                         break;
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 */
404                         break;
405                     }
406                 }
407                 *pp++ = '.';
408                 break;
409 
410             case ';':             /* VMS version (or DEC-20 attrib?) */
411                 lastsemi = pp;
412                 *pp++ = ';';      /* keep for now; remove VMS ";##" */
413                 break;            /*  later, if requested */
414 
415 #ifdef ACORN_FTYPE_NFS
416             case ',':             /* NFS filetype extension */
417                 lastcomma = pp;
418                 *pp++ = ',';      /* keep for now; may need to remove */
419                 break;            /*  later, if requested */
420 #endif
421 
422             case '\026':          /* control-V quote for special chars */
423                 quote = TRUE;     /* set flag for next character */
424                 break;
425 
426             default:
427                 /* allow European characters in filenames: */
428                 if (isprint(workch) || (128 <= workch && workch <= 254))
429                     *pp++ = (char)workch;
430             } /* end switch */
431 
432     } /* end while loop */
433 
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;
441     }
442 
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-
446     fore exiting.
447   ---------------------------------------------------------------------------*/
448 
449     if (G.filename[strlen(G.filename) - 1] == '/') {
450         checkdir(__G__ G.filename, GETPATH);
451         if (created_dir) {
452             if (QCOND2) {
453                 Info(slide, 0, ((char *)slide, "   creating: %s\n",
454                   FnFilter1(G.filename)));
455             }
456 
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 );
460                 if (ptr) {
461                     setBeOSexfield( G.filename, ptr );
462                 }
463             }
464 
465 #ifndef NO_CHMOD
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");
469 #endif
470 
471             /* set dir time (note trailing '/') */
472             return (error & ~MPN_MASK) | MPN_CREATED_DIR;
473         }
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 );
479             if (ptr) {
480                 setBeOSexfield( G.filename, ptr );
481             }
482         }
483 
484         /* dir existed already; don't look for data to extract */
485         return (error & ~MPN_MASK) | MPN_INF_SKIP;
486     }
487 
488     *pp = '\0';                   /* done with pathcomp:  terminate it */
489 
490     /* if not saving them, remove VMS version numbers (appended ";###") */
491     if (!uO.V_flag && lastsemi) {
492         pp = lastsemi + 1;
493         while (isdigit((uch)(*pp)))
494             ++pp;
495         if (*pp == '\0')          /* only digits between ';' and end:  nuke */
496             *lastsemi = '\0';
497     }
498 
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)
505     {
506         /* file *must* have a RISC OS extra field */
507         long ft = (long)makelong((ef_spark->loadaddr);
508         /*32-bit*/
509         if (lastcomma) {
510             pp = lastcomma + 1;
511             while (isxdigit((uch)(*pp))) ++pp;
512             if (pp == lastcomma+4 && *pp == '\0') *lastcomma='\0'; /* nuke */
513         }
514         if ((ft & 1<<31)==0) ft=0x000FFD00;
515         sprintf(pathcomp+strlen(pathcomp), ",%03x", (int)(ft>>8) & 0xFFF);
516     }
517 #endif /* ACORN_FTYPE_NFS */
518 
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;
523     }
524 
525     checkdir(__G__ pathcomp, APPEND_NAME);  /* returns 1 if truncated: care? */
526     checkdir(__G__ G.filename, GETPATH);
527 
528     return error;
529 
530 } /* end function mapname() */
531 
532 
533 
534 
535 /***********************/
536 /* Function checkdir() */
537 /***********************/
538 
539 int checkdir(__G__ pathcomp, flag)
540     __GDEF
541     char *pathcomp;
542     int flag;
543 /*
544  * returns:
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
552  */
553 {
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') */
558 
559 #   define FN_MASK   7
560 #   define FUNCTION  (flag & FN_MASK)
561 
562 
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   ---------------------------------------------------------------------------*/
568 
569     if (FUNCTION == APPEND_DIR) {
570         int too_long = FALSE;
571 #ifdef SHORT_NAMES
572         char *old_end = end;
573 #endif
574 
575         Trace((stderr, "appending dir segment [%s]\n", FnFilter1(pathcomp)));
576         while ((*end = *pathcomp++) != '\0')
577             ++end;
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';
581 #endif
582 
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. */
587 
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) */
592                 free(buildpath);
593                 return MPN_INF_SKIP;    /* path doesn't exist: nothing to do */
594             }
595             if (too_long) {
596                 Info(slide, 1, ((char *)slide,
597                   "checkdir error:  path too long: %s\n",
598                   FnFilter1(buildpath)));
599                 free(buildpath);
600                 /* no room for filenames:  fatal */
601                 return MPN_ERR_TOOLONG;
602             }
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)));
608                 free(buildpath);
609                 /* path didn't exist, tried to create, failed */
610                 return MPN_ERR_SKIP;
611             }
612             created_dir = TRUE;
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)));
618             free(buildpath);
619             /* path existed but wasn't dir */
620             return MPN_ERR_SKIP;
621         }
622         if (too_long) {
623             Info(slide, 1, ((char *)slide,
624               "checkdir error:  path too long: %s\n", FnFilter1(buildpath)));
625             free(buildpath);
626             /* no room for filenames:  fatal */
627             return MPN_ERR_TOOLONG;
628         }
629         *end++ = '/';
630         *end = '\0';
631         Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
632         return MPN_OK;
633 
634     } /* end if (FUNCTION == APPEND_DIR) */
635 
636 /*---------------------------------------------------------------------------
637     GETPATH:  copy full path to the string pointed at by pathcomp, and free
638     buildpath.
639   ---------------------------------------------------------------------------*/
640 
641     if (FUNCTION == GETPATH) {
642         strcpy(pathcomp, buildpath);
643         Trace((stderr, "getting and freeing path [%s]\n",
644           FnFilter1(pathcomp)));
645         free(buildpath);
646         buildpath = end = (char *)NULL;
647         return MPN_OK;
648     }
649 
650 /*---------------------------------------------------------------------------
651     APPEND_NAME:  assume the path component is the filename; append it and
652     return without checking for existence.
653   ---------------------------------------------------------------------------*/
654 
655     if (FUNCTION == APPEND_NAME) {
656 #ifdef SHORT_NAMES
657         char *old_end = end;
658 #endif
659 
660         Trace((stderr, "appending filename [%s]\n", FnFilter1(pathcomp)));
661         while ((*end = *pathcomp++) != '\0') {
662             ++end;
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';
666 #endif
667             if ((end-buildpath) >= FILNAMSIZ) {
668                 *--end = '\0';
669                 Info(slide, 0x201, ((char *)slide,
670                   "checkdir warning:  path too long; truncating\n\
671                    %s\n                -> %s\n",
672                   FnFilter1(G.filename), FnFilter2(buildpath)));
673                 return MPN_INF_TRUNC;   /* filename truncated */
674             }
675         }
676         Trace((stderr, "buildpath now = [%s]\n", FnFilter1(buildpath)));
677         /* could check for existence here, prompt for new name... */
678         return MPN_OK;
679     }
680 
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
684     extract-to path.
685   ---------------------------------------------------------------------------*/
686 
687 /* GRR:  for VMS and TOPS-20, add up to 13 to strlen */
688 
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)))
694 #else
695         if ((buildpath = (char *)malloc(strlen(G.filename)+rootlen+1))
696 #endif
697             == (char *)NULL)
698             return MPN_NOMEM;
699         if ((rootlen > 0) && !renamed_fullpath) {
700             strcpy(buildpath, rootpath);
701             end = buildpath + rootlen;
702         } else {
703             *buildpath = '\0';
704             end = buildpath;
705         }
706         Trace((stderr, "[%s]\n", FnFilter1(buildpath)));
707         return MPN_OK;
708     }
709 
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
714     on the command line.
715   ---------------------------------------------------------------------------*/
716 
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) {
722             rootlen = 0;
723             return MPN_OK;
724         }
725         if (rootlen > 0)        /* rootpath was already set, nothing to do */
726             return MPN_OK;
727         if ((rootlen = strlen(pathcomp)) > 0) {
728             char *tmproot;
729 
730             if ((tmproot = (char *)malloc(rootlen+2)) == (char *)NULL) {
731                 rootlen = 0;
732                 return MPN_NOMEM;
733             }
734             strcpy(tmproot, pathcomp);
735             if (tmproot[rootlen-1] == '/') {
736                 tmproot[--rootlen] = '\0';
737             }
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) */ ) {
742                     free(tmproot);
743                     rootlen = 0;
744                     /* skip (or treat as stored file) */
745                     return MPN_INF_SKIP;
746                 }
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)));
753                     free(tmproot);
754                     rootlen = 0;
755                     /* path didn't exist, tried to create, and failed: */
756                     /* file exists, or 2+ subdir levels required */
757                     return MPN_ERR_SKIP;
758                 }
759             }
760             tmproot[rootlen++] = '/';
761             tmproot[rootlen] = '\0';
762             if ((rootpath = (char *)realloc(tmproot, rootlen+1)) == NULL) {
763                 free(tmproot);
764                 rootlen = 0;
765                 return MPN_NOMEM;
766             }
767             Trace((stderr, "rootpath now = [%s]\n", FnFilter1(rootpath)));
768         }
769         return MPN_OK;
770     }
771 #endif /* !SFX || SFX_EXDIR */
772 
773 /*---------------------------------------------------------------------------
774     END:  free rootpath, immediately prior to program exit.
775   ---------------------------------------------------------------------------*/
776 
777     if (FUNCTION == END) {
778         Trace((stderr, "freeing rootpath\n"));
779         if (rootlen > 0) {
780             free(rootpath);
781             rootlen = 0;
782         }
783         return MPN_OK;
784     }
785 
786     return MPN_INVALID; /* should never reach */
787 
788 } /* end function checkdir() */
789 
790 
791 
792 
793 
794 
795 /****************************/
796 /* Function close_outfile() */
797 /****************************/
798 
799 void close_outfile(__G)    /* GRR: change to return PK-style warning level */
800     __GDEF
801 {
802     iztimes zt;
803     ush z_uidgid[2];
804     unsigned eb_izux_flg;
805 
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   ---------------------------------------------------------------------------*/
812 
813 #ifdef SYMLINKS
814     if (G.symlnk) {
815         unsigned ucsize = (unsigned)G.lrec.ucsize;
816         char *linktarget = (char *)malloc((unsigned)G.lrec.ucsize+1);
817 
818         fclose(G.outfile);                      /* close "data" file... */
819         G.outfile = fopen(G.filename, FOPR);    /* ...and reopen for reading */
820         if (!linktarget || fread(linktarget, 1, ucsize, G.outfile) !=
821                            (int)ucsize)
822         {
823             Info(slide, 0x201, ((char *)slide,
824               "warning:  symbolic link (%s) failed\n", FnFilter1(G.filename)));
825             if (linktarget)
826                 free(linktarget);
827             fclose(G.outfile);
828             return;
829         }
830         fclose(G.outfile);                  /* close "data" file for good... */
831         unlink(G.filename);                 /* ...and delete it */
832         linktarget[ucsize] = '\0';
833         if (QCOND2)
834             Info(slide, 0, ((char *)slide, "-> %s ", FnFilter1(linktarget)));
835         if (symlink(linktarget, G.filename))  /* create the real link */
836             perror("symlink error");
837 
838         if (!uO.J_flag) {
839             /* Symlinks can have attributes, too. */
840             void *ptr = scanBeOSexfield( G.extra_field,
841                                          G.lrec.extra_field_length );
842             if (ptr) {
843                 setBeOSexfield( G.filename, ptr );
844             }
845         }
846 
847         free(linktarget);
848         return;                             /* can't set time on symlinks */
849     }
850 #endif /* SYMLINKS */
851 
852     fclose(G.outfile);
853 
854     /* handle the BeOS extra field if present */
855     if (!uO.J_flag) {
856         void *ptr = scanBeOSexfield( G.extra_field,
857                                      G.lrec.extra_field_length );
858 
859         if (ptr) {
860             setBeOSexfield( G.filename, ptr );
861 #ifdef BEOS_ASSIGN_FILETYPE
862         } else {
863             /* Otherwise, ask the system to try assigning a MIME type. */
864             assign_MIME( G.filename );
865 #endif
866         }
867     }
868 
869 /*---------------------------------------------------------------------------
870     Change the file permissions from default ones to those stored in the
871     zipfile.
872   ---------------------------------------------------------------------------*/
873 
874 #ifndef NO_CHMOD
875     if (chmod(G.filename, 0xffff & G.pInfo->file_attr))
876         perror("chmod (file attributes) error");
877 #endif
878 
879 /*---------------------------------------------------------------------------
880     Convert from MSDOS-format local time and date to Unix-format 32-bit GMT
881     time:  adjust base year from 1980 to 1970, do usual conversions from
882     yy/mm/dd hh:mm:ss to elapsed seconds, and account for timezone and day-
883     light savings time differences.  If we have a Unix extra field, however,
884     we're laughing:  both mtime and atime are ours.  On the other hand, we
885     then have to check for restoration of UID/GID.
886   ---------------------------------------------------------------------------*/
887 
888     eb_izux_flg = (G.extra_field ? ef_scan_for_izux(G.extra_field,
889                    G.lrec.extra_field_length, 0, G.lrec.last_mod_dos_datetime,
890 #ifdef IZ_CHECK_TZ
891                    (G.tz_is_valid ? &zt : NULL),
892 #else
893                    &zt,
894 #endif
895                    z_uidgid) : 0);
896     if (eb_izux_flg & EB_UT_FL_MTIME) {
897         TTrace((stderr, "\nclose_outfile:  Unix e.f. modif. time = %ld\n",
898           zt.mtime));
899     } else {
900         zt.mtime = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
901     }
902     if (eb_izux_flg & EB_UT_FL_ATIME) {
903         TTrace((stderr, "close_outfile:  Unix e.f. access time = %ld\n",
904           zt.atime));
905     } else {
906         zt.atime = zt.mtime;
907         TTrace((stderr, "\nclose_outfile:  modification/access times = %ld\n",
908           zt.mtime));
909     }
910 
911     /* if -X option was specified and we have UID/GID info, restore it */
912     if (uO.X_flag && eb_izux_flg & EB_UX2_VALID) {
913         TTrace((stderr, "close_outfile:  restoring Unix UID/GID info\n"));
914         if (chown(G.filename, (uid_t)z_uidgid[0], (gid_t)z_uidgid[1]))
915         {
916             if (uO.qflag)
917                 Info(slide, 0x201, ((char *)slide,
918                   "warning:  cannot set UID %d and/or GID %d for %s\n",
919                   z_uidgid[0], z_uidgid[1], FnFilter1(G.filename)));
920             else
921                 Info(slide, 0x201, ((char *)slide,
922                   " (warning) cannot set UID %d and/or GID %d",
923                   z_uidgid[0], z_uidgid[1]));
924         }
925     }
926 
927     /* set the file's access and modification times */
928     if (utime(G.filename, (struct utimbuf *)&zt)) {
929         if (uO.qflag)
930             Info(slide, 0x201, ((char *)slide,
931               "warning:  cannot set time for %s\n", FnFilter1(G.filename)));
932         else
933             Info(slide, 0x201, ((char *)slide,
934               " (warning) cannot set time"));
935     }
936 
937 } /* end function close_outfile() */
938 
939 
940 
941 
942 #ifdef SET_DIR_ATTRIB
943 /* messages of code for setting directory attributes */
944 static char Far DirlistUidGidFailed[] =
945   "warning:  cannot set UID %d and/or GID %d for %s\n";
946 static char Far DirlistUtimeFailed[] =
947   "warning:  cannot set modification, access times for %s\n";
948 #  ifndef NO_CHMOD
949   static char Far DirlistChmodFailed[] =
950     "warning:  cannot set permissions for %s\n";
951 #  endif
952 
953 
954 int set_direc_attribs(__G__ d)
955     __GDEF
956     dirtime *d;
957 {
958     int errval = PK_OK;
959 
960     if (d->have_uidgid &&
961         chown(d->fn, (uid_t)d->uidgid[0], (gid_t)d->uidgid[1]))
962     {
963         Info(slide, 0x201, ((char *)slide,
964           LoadFarString(DirlistUidGidFailed),
965           d->uidgid[0], d->uidgid[1], d->fn));
966         if (!errval)
967             errval = PK_WARN;
968     }
969     if (utime(d->fn, (const struct utimbuf *)&d->u.t2)) {
970         Info(slide, 0x201, ((char *)slide,
971           LoadFarString(DirlistUtimeFailed), FnFilter1(d->fn)));
972         if (!errval)
973             errval = PK_WARN;
974     }
975 #ifndef NO_CHMOD
976     if (chmod(d->fn, 0xffff & d->perms)) {
977         Info(slide, 0x201, ((char *)slide,
978           LoadFarString(DirlistChmodFailed), FnFilter1(d->fn)));
979         /* perror("chmod (file attributes) error"); */
980         if (!errval)
981             errval = PK_WARN;
982     }
983 #endif /* !NO_CHMOD */
984     return errval;
985 } /* end function set_directory_attributes() */
986 
987 #endif /* SET_DIR_ATTRIB */
988 
989 
990 
991 
992 #ifdef TIMESTAMP
993 
994 /***************************/
995 /*  Function stamp_file()  */
996 /***************************/
997 
998 int stamp_file(fname, modtime)
999     ZCONST char *fname;
1000     time_t modtime;
1001 {
1002     struct utimbuf tp;
1003 
1004     tp.modtime = tp.actime = modtime;
1005     return (utime(fname, &tp));
1006 
1007 } /* end function stamp_file() */
1008 
1009 #endif /* TIMESTAMP */
1010 
1011 
1012 
1013 
1014 #ifndef SFX
1015 
1016 /************************/
1017 /*  Function version()  */
1018 /************************/
1019 
1020 void version(__G)
1021     __GDEF
1022 {
1023     sprintf((char *)slide, LoadFarString(CompiledWith),
1024 #if defined(__MWERKS__)
1025       "Metrowerks CodeWarrior", "",
1026 #elif defined(__GNUC__)
1027       "GNU C ", __VERSION__,
1028 #endif
1029       "BeOS ",
1030 
1031 #ifdef __POWERPC__
1032       "(PowerPC)",
1033 #else
1034 # ifdef __INTEL__
1035       "(x86)",
1036 # else
1037       "(unknown)",   /* someday we may have other architectures... */
1038 # endif
1039 #endif
1040 
1041 #ifdef __DATE__
1042       " on ", __DATE__
1043 #else
1044       "", ""
1045 #endif
1046     );
1047 
1048     (*G.message)((zvoid *)&G, slide, (ulg)strlen((char *)slide), 0);
1049 
1050 } /* end function version() */
1051 
1052 #endif /* !SFX */
1053 
1054 
1055 
1056 /******************************/
1057 /* Extra field functions      */
1058 /******************************/
1059 
1060 /*
1061 ** Scan the extra fields in extra_field, and look for a BeOS EF; return a
1062 ** pointer to that EF, or NULL if it's not there.
1063 */
1064 static uch *scanBeOSexfield( const uch *ef_ptr, unsigned ef_len )
1065 {
1066     while( ef_ptr != NULL && ef_len >= EB_HEADSIZE ) {
1067         unsigned eb_id  = makeword(EB_ID + ef_ptr);
1068         unsigned eb_len = makeword(EB_LEN + ef_ptr);
1069 
1070         if (eb_len > (ef_len - EB_HEADSIZE)) {
1071             Trace((stderr,
1072               "scanBeOSexfield: block length %u > rest ef_size %u\n", eb_len,
1073               ef_len - EB_HEADSIZE));
1074             break;
1075         }
1076 
1077         if( eb_id == EF_BEOS && eb_len >= EB_BEOS_HLEN ) {
1078             return (uch *)ef_ptr;
1079         }
1080 
1081         ef_ptr += (eb_len + EB_HEADSIZE);
1082         ef_len -= (eb_len + EB_HEADSIZE);
1083     }
1084 
1085     return NULL;
1086 }
1087 
1088 /* Used by setBeOSexfield():
1089 
1090 Set a file/directory's attributes to the attributes passed in.
1091 
1092 If set_file_attrs() fails, an error will be returned:
1093 
1094      EOK - no errors occurred
1095 
1096 (other values will be whatever the failed function returned; no docs
1097 yet, or I'd list a few)
1098 */
1099 static int set_file_attrs( const char *name,
1100                            const unsigned char *attr_buff,
1101                            const off_t attr_size )
1102 {
1103     int                  retval = EOK;
1104     unsigned char       *ptr;
1105     const unsigned char *guard;
1106     int                  fd;
1107 
1108     ptr   = (unsigned char *)attr_buff;
1109     guard = ptr + attr_size;
1110 
1111     fd = open( name, O_RDWR | O_NOTRAVERSE );
1112     if( fd < 0 ) {
1113         return errno; /* should it be -fd ? */
1114     }
1115 
1116     while( ptr < guard ) {
1117         ssize_t              wrote_bytes;
1118         struct attr_info     fa_info;
1119         const char          *attr_name;
1120         unsigned char       *attr_data;
1121 
1122         attr_name  = (char *)&(ptr[0]);
1123         ptr       += strlen( attr_name ) + 1;
1124 
1125         /* The attr_info data is stored in big-endian format because the */
1126         /* PowerPC port was here first.                                  */
1127         memcpy( &fa_info, ptr, sizeof( struct attr_info ) );
1128         fa_info.type = (uint32)B_BENDIAN_TO_HOST_INT32( fa_info.type );
1129         fa_info.size = (off_t)B_BENDIAN_TO_HOST_INT64( fa_info.size );
1130         ptr     += sizeof( struct attr_info );
1131 
1132         if( fa_info.size < 0LL ) {
1133             Info(slide, 0x201, ((char *)slide,
1134                  "warning: skipping attribute with invalid length (%Ld)\n",
1135                  fa_info.size));
1136             break;
1137         }
1138 
1139         attr_data  = ptr;
1140         ptr       += fa_info.size;
1141 
1142         if( ptr > guard ) {
1143             /* We've got a truncated attribute. */
1144             Info(slide, 0x201, ((char *)slide,
1145                  "warning: truncated attribute\n"));
1146             break;
1147         }
1148 
1149         /* Wave the magic wand... this will swap Be-known types properly. */
1150         (void)swap_data( fa_info.type, attr_data, fa_info.size,
1151                          B_SWAP_BENDIAN_TO_HOST );
1152 
1153         wrote_bytes = fs_write_attr( fd, attr_name, fa_info.type, 0,
1154                                      attr_data, fa_info.size );
1155         if( wrote_bytes != fa_info.size ) {
1156             Info(slide, 0x201, ((char *)slide,
1157                  "warning: wrote %ld attribute bytes of %ld\n",
1158                  (unsigned long)wrote_bytes,(unsigned long)fa_info.size));
1159         }
1160     }
1161 
1162     close( fd );
1163 
1164     return retval;
1165 }
1166 
1167 static void setBeOSexfield( const char *path, uch *extra_field )
1168 {
1169     uch *ptr       = extra_field;
1170     ush  id        = 0;
1171     ush  size      = 0;
1172     ulg  full_size = 0;
1173     uch  flags     = 0;
1174     uch *attrbuff  = NULL;
1175     int retval;
1176 
1177     if( extra_field == NULL ) {
1178         return;
1179     }
1180 
1181     /* Collect the data from the extra field buffer. */
1182     id        = makeword( ptr );    ptr += 2;   /* we don't use this... */
1183     size      = makeword( ptr );    ptr += 2;
1184     full_size = makelong( ptr );    ptr += 4;
1185     flags     = *ptr;               ptr++;
1186 
1187     /* Do a little sanity checking. */
1188     if( flags & EB_BE_FL_BADBITS ) {
1189         /* corrupted or unsupported */
1190         Info(slide, 0x201, ((char *)slide,
1191              "Unsupported flags set for this BeOS extra field, skipping.\n"));
1192         return;
1193     }
1194     if( size <= EB_BEOS_HLEN ) {
1195         /* corrupted, unsupported, or truncated */
1196         Info(slide, 0x201, ((char *)slide,
1197              "BeOS extra field is %d bytes, should be at least %d.\n", size,
1198              EB_BEOS_HLEN));
1199         return;
1200     }
1201     if( full_size < ( size - EB_BEOS_HLEN ) ) {
1202         /* possible old archive? will this screw up on valid archives? */
1203         Info(slide, 0x201, ((char *)slide,
1204              "Skipping attributes: BeOS extra field is %d bytes, "
1205              "data size is %ld.\n", size - EB_BEOS_HLEN, full_size));
1206         return;
1207     }
1208 
1209     /* Find the BeOS file attribute data. */
1210     if( flags & EB_BE_FL_UNCMPR ) {
1211         /* Uncompressed data */
1212         attrbuff = ptr;
1213     } else {
1214         /* Compressed data */
1215         attrbuff = (uch *)malloc( full_size );
1216         if( attrbuff == NULL ) {
1217             /* No memory to uncompress attributes */
1218             Info(slide, 0x201, ((char *)slide,
1219                  "Can't allocate memory to uncompress file attributes.\n"));
1220             return;
1221         }
1222 
1223         retval = memextract( __G__ attrbuff, full_size,
1224                              ptr, size - EB_BEOS_HLEN );
1225         if( retval != PK_OK ) {
1226             /* error uncompressing attributes */
1227             Info(slide, 0x201, ((char *)slide,
1228                  "Error uncompressing file attributes.\n"));
1229 
1230             /* Some errors here might not be so bad; we should expect */
1231             /* some truncated data, for example.  If the data was     */
1232             /* corrupt, we should _not_ attempt to restore the attrs  */
1233             /* for this file... there's no way to detect what attrs   */
1234             /* are good and which are bad.                            */
1235             free( attrbuff );
1236             return;
1237         }
1238     }
1239 
1240     /* Now attempt to set the file attributes on the extracted file. */
1241     retval = set_file_attrs( path, attrbuff, (off_t)full_size );
1242     if( retval != EOK ) {
1243         Info(slide, 0x201, ((char *)slide,
1244              "Error writing file attributes.\n"));
1245     }
1246 
1247     /* Clean up, if necessary */
1248     if( attrbuff != ptr ) {
1249         free( attrbuff );
1250     }
1251 
1252     return;
1253 }
1254 
1255 #ifdef BEOS_USE_PRINTEXFIELD
1256 static void printBeOSexfield( int isdir, uch *extra_field )
1257 {
1258     uch *ptr       = extra_field;
1259     ush  id        = 0;
1260     ush  size      = 0;
1261     ulg  full_size = 0;
1262     uch  flags     = 0;
1263 
1264     /* Tell picky compilers to be quiet. */
1265     isdir = isdir;
1266 
1267     if( extra_field == NULL ) {
1268         return;
1269     }
1270 
1271     /* Collect the data from the buffer. */
1272     id        = makeword( ptr );    ptr += 2;
1273     size      = makeword( ptr );    ptr += 2;
1274     full_size = makelong( ptr );    ptr += 4;
1275     flags     = *ptr;               ptr++;
1276 
1277     if( id != EF_BEOS ) {
1278         /* not a 'Be' field */
1279         printf("\t*** Unknown field type (0x%04x, '%c%c')\n", id,
1280                (char)(id >> 8), (char)id);
1281     }
1282 
1283     if( flags & EB_BE_FL_BADBITS ) {
1284         /* corrupted or unsupported */
1285         printf("\t*** Corrupted BeOS extra field:\n");
1286         printf("\t*** unknown bits set in the flags\n");
1287         printf("\t*** (Possibly created by an old version of zip for BeOS.\n");
1288     }
1289 
1290     if( size <= EB_BEOS_HLEN ) {
1291         /* corrupted, unsupported, or truncated */
1292         printf("\t*** Corrupted BeOS extra field:\n");
1293         printf("\t*** size is %d, should be larger than %d\n", size,
1294                EB_BEOS_HLEN );
1295     }
1296 
1297     if( flags & EB_BE_FL_UNCMPR ) {
1298         /* Uncompressed data */
1299         printf("\tBeOS extra field data (uncompressed):\n");
1300         printf("\t\t%ld data bytes\n", full_size);
1301     } else {
1302         /* Compressed data */
1303         printf("\tBeOS extra field data (compressed):\n");
1304         printf("\t\t%d compressed bytes\n", size - EB_BEOS_HLEN);
1305         printf("\t\t%ld uncompressed bytes\n", full_size);
1306     }
1307 }
1308 #endif
1309 
1310 #ifdef BEOS_ASSIGN_FILETYPE
1311 /* Note: This will no longer be necessary in BeOS PR4; update_mime_info()    */
1312 /* will be updated to build its own absolute pathname if it's not given one. */
1313 static void assign_MIME( const char *file )
1314 {
1315     char *fullname;
1316     char buff[PATH_MAX], cwd_buff[PATH_MAX];
1317     int retval;
1318 
1319     if( file[0] == '/' ) {
1320         fullname = (char *)file;
1321     } else {
1322         sprintf( buff, "%s/%s", getcwd( cwd_buff, PATH_MAX ), file );
1323         fullname = buff;
1324     }
1325 
1326     retval = update_mime_info( fullname, FALSE, TRUE, TRUE );
1327 }
1328 #endif
1329