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
49static uch *scanBeOSexfield  OF((const uch *ef_ptr, unsigned ef_len));
50static int  set_file_attrs( const char *, const unsigned char *, const off_t );
51static void setBeOSexfield   OF((const char *path, uch *extra_field));
52#ifdef BEOS_USE_PRINTEXFIELD
53static void printBeOSexfield OF((int isdir, uch *extra_field));
54#endif
55#ifdef BEOS_ASSIGN_FILETYPE
56static void assign_MIME( const char * );
57#endif
58
59#ifdef ACORN_FTYPE_NFS
60/* Acorn bits for NFS filetyping */
61typedef 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
72static int created_dir;        /* used in mapname(), checkdir() */
73static int renamed_fullpath;   /* ditto */
74
75#ifndef SFX
76
77/**********************/
78/* Function do_wild() */   /* for porting:  dir separator; match(ignore_case) */
79/**********************/
80
81char *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
193int 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
321int 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
539int 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
799void 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
821        	|| fread(linktarget, 1, ucsize, G.outfile) != (size_t)ucsize) {
822            Info(slide, 0x201, ((char *)slide,
823              "warning:  symbolic link (%s) failed\n", FnFilter1(G.filename)));
824            if (linktarget)
825                free(linktarget);
826            fclose(G.outfile);
827            return;
828        }
829        fclose(G.outfile);                  /* close "data" file for good... */
830        unlink(G.filename);                 /* ...and delete it */
831        linktarget[ucsize] = '\0';
832        if (QCOND2)
833            Info(slide, 0, ((char *)slide, "-> %s ", FnFilter1(linktarget)));
834        if (symlink(linktarget, G.filename))  /* create the real link */
835            perror("symlink error");
836
837        if (!uO.J_flag) {
838            /* Symlinks can have attributes, too. */
839            void *ptr = scanBeOSexfield( G.extra_field,
840                                         G.lrec.extra_field_length );
841            if (ptr) {
842                setBeOSexfield( G.filename, ptr );
843            }
844        }
845
846        free(linktarget);
847        return;                             /* can't set time on symlinks */
848    }
849#endif /* SYMLINKS */
850
851    fclose(G.outfile);
852
853    /* handle the BeOS extra field if present */
854    if (!uO.J_flag) {
855        void *ptr = scanBeOSexfield( G.extra_field,
856                                     G.lrec.extra_field_length );
857
858        if (ptr) {
859            setBeOSexfield( G.filename, ptr );
860#ifdef BEOS_ASSIGN_FILETYPE
861        } else {
862            /* Otherwise, ask the system to try assigning a MIME type. */
863            assign_MIME( G.filename );
864#endif
865        }
866    }
867
868/*---------------------------------------------------------------------------
869    Change the file permissions from default ones to those stored in the
870    zipfile.
871  ---------------------------------------------------------------------------*/
872
873#ifndef NO_CHMOD
874    if (chmod(G.filename, 0xffff & G.pInfo->file_attr))
875        perror("chmod (file attributes) error");
876#endif
877
878/*---------------------------------------------------------------------------
879    Convert from MSDOS-format local time and date to Unix-format 32-bit GMT
880    time:  adjust base year from 1980 to 1970, do usual conversions from
881    yy/mm/dd hh:mm:ss to elapsed seconds, and account for timezone and day-
882    light savings time differences.  If we have a Unix extra field, however,
883    we're laughing:  both mtime and atime are ours.  On the other hand, we
884    then have to check for restoration of UID/GID.
885  ---------------------------------------------------------------------------*/
886
887    eb_izux_flg = (G.extra_field ? ef_scan_for_izux(G.extra_field,
888                   G.lrec.extra_field_length, 0, G.lrec.last_mod_dos_datetime,
889#ifdef IZ_CHECK_TZ
890                   (G.tz_is_valid ? &zt : NULL),
891#else
892                   &zt,
893#endif
894                   z_uidgid) : 0);
895    if (eb_izux_flg & EB_UT_FL_MTIME) {
896        TTrace((stderr, "\nclose_outfile:  Unix e.f. modif. time = %ld\n",
897          zt.mtime));
898    } else {
899        zt.mtime = dos_to_unix_time(G.lrec.last_mod_dos_datetime);
900    }
901    if (eb_izux_flg & EB_UT_FL_ATIME) {
902        TTrace((stderr, "close_outfile:  Unix e.f. access time = %ld\n",
903          zt.atime));
904    } else {
905        zt.atime = zt.mtime;
906        TTrace((stderr, "\nclose_outfile:  modification/access times = %ld\n",
907          zt.mtime));
908    }
909
910    /* if -X option was specified and we have UID/GID info, restore it */
911    if (uO.X_flag && eb_izux_flg & EB_UX2_VALID) {
912        TTrace((stderr, "close_outfile:  restoring Unix UID/GID info\n"));
913        if (chown(G.filename, (uid_t)z_uidgid[0], (gid_t)z_uidgid[1]))
914        {
915            if (uO.qflag)
916                Info(slide, 0x201, ((char *)slide,
917                  "warning:  cannot set UID %d and/or GID %d for %s\n",
918                  z_uidgid[0], z_uidgid[1], FnFilter1(G.filename)));
919            else
920                Info(slide, 0x201, ((char *)slide,
921                  " (warning) cannot set UID %d and/or GID %d",
922                  z_uidgid[0], z_uidgid[1]));
923        }
924    }
925
926    /* set the file's access and modification times */
927    if (utime(G.filename, (struct utimbuf *)&zt)) {
928        if (uO.qflag)
929            Info(slide, 0x201, ((char *)slide,
930              "warning:  cannot set time for %s\n", FnFilter1(G.filename)));
931        else
932            Info(slide, 0x201, ((char *)slide,
933              " (warning) cannot set time"));
934    }
935
936} /* end function close_outfile() */
937
938
939
940
941#ifdef SET_DIR_ATTRIB
942/* messages of code for setting directory attributes */
943static char Far DirlistUidGidFailed[] =
944  "warning:  cannot set UID %d and/or GID %d for %s\n";
945static char Far DirlistUtimeFailed[] =
946  "warning:  cannot set modification, access times for %s\n";
947#  ifndef NO_CHMOD
948  static char Far DirlistChmodFailed[] =
949    "warning:  cannot set permissions for %s\n";
950#  endif
951
952
953int set_direc_attribs(__G__ d)
954    __GDEF
955    dirtime *d;
956{
957    int errval = PK_OK;
958
959    if (d->have_uidgid &&
960        chown(d->fn, (uid_t)d->uidgid[0], (gid_t)d->uidgid[1]))
961    {
962        Info(slide, 0x201, ((char *)slide,
963          LoadFarString(DirlistUidGidFailed),
964          d->uidgid[0], d->uidgid[1], d->fn));
965        if (!errval)
966            errval = PK_WARN;
967    }
968    if (utime(d->fn, (const struct utimbuf *)&d->u.t2)) {
969        Info(slide, 0x201, ((char *)slide,
970          LoadFarString(DirlistUtimeFailed), FnFilter1(d->fn)));
971        if (!errval)
972            errval = PK_WARN;
973    }
974#ifndef NO_CHMOD
975    if (chmod(d->fn, 0xffff & d->perms)) {
976        Info(slide, 0x201, ((char *)slide,
977          LoadFarString(DirlistChmodFailed), FnFilter1(d->fn)));
978        /* perror("chmod (file attributes) error"); */
979        if (!errval)
980            errval = PK_WARN;
981    }
982#endif /* !NO_CHMOD */
983    return errval;
984} /* end function set_directory_attributes() */
985
986#endif /* SET_DIR_ATTRIB */
987
988
989
990
991#ifdef TIMESTAMP
992
993/***************************/
994/*  Function stamp_file()  */
995/***************************/
996
997int stamp_file(fname, modtime)
998    ZCONST char *fname;
999    time_t modtime;
1000{
1001    struct utimbuf tp;
1002
1003    tp.modtime = tp.actime = modtime;
1004    return (utime(fname, &tp));
1005
1006} /* end function stamp_file() */
1007
1008#endif /* TIMESTAMP */
1009
1010
1011
1012
1013#ifndef SFX
1014
1015/************************/
1016/*  Function version()  */
1017/************************/
1018
1019void version(__G)
1020    __GDEF
1021{
1022    sprintf((char *)slide, LoadFarString(CompiledWith),
1023#if defined(__MWERKS__)
1024      "Metrowerks CodeWarrior", "",
1025#elif defined(__GNUC__)
1026      "GNU C ", __VERSION__,
1027#endif
1028      "BeOS ",
1029
1030#ifdef __POWERPC__
1031      "(PowerPC)",
1032#else
1033# ifdef __i386__
1034      "(x86)",
1035# else
1036      "(unknown)",   /* someday we may have other architectures... */
1037# endif
1038#endif
1039
1040#ifdef __DATE__
1041      " on ", __DATE__
1042#else
1043      "", ""
1044#endif
1045    );
1046
1047    (*G.message)((zvoid *)&G, slide, (ulg)strlen((char *)slide), 0);
1048
1049} /* end function version() */
1050
1051#endif /* !SFX */
1052
1053
1054
1055/******************************/
1056/* Extra field functions      */
1057/******************************/
1058
1059/*
1060** Scan the extra fields in extra_field, and look for a BeOS EF; return a
1061** pointer to that EF, or NULL if it's not there.
1062*/
1063static uch *scanBeOSexfield( const uch *ef_ptr, unsigned ef_len )
1064{
1065    while( ef_ptr != NULL && ef_len >= EB_HEADSIZE ) {
1066        unsigned eb_id  = makeword(EB_ID + ef_ptr);
1067        unsigned eb_len = makeword(EB_LEN + ef_ptr);
1068
1069        if (eb_len > (ef_len - EB_HEADSIZE)) {
1070            Trace((stderr,
1071              "scanBeOSexfield: block length %u > rest ef_size %u\n", eb_len,
1072              ef_len - EB_HEADSIZE));
1073            break;
1074        }
1075
1076        if( eb_id == EF_BEOS && eb_len >= EB_BEOS_HLEN ) {
1077            return (uch *)ef_ptr;
1078        }
1079
1080        ef_ptr += (eb_len + EB_HEADSIZE);
1081        ef_len -= (eb_len + EB_HEADSIZE);
1082    }
1083
1084    return NULL;
1085}
1086
1087/* Used by setBeOSexfield():
1088
1089Set a file/directory's attributes to the attributes passed in.
1090
1091If set_file_attrs() fails, an error will be returned:
1092
1093     EOK - no errors occurred
1094
1095(other values will be whatever the failed function returned; no docs
1096yet, or I'd list a few)
1097*/
1098static int set_file_attrs( const char *name,
1099                           const unsigned char *attr_buff,
1100                           const off_t total_attr_size )
1101{
1102    int                  retval = EOK;
1103    unsigned char       *ptr;
1104    const unsigned char *guard;
1105    int                  fd;
1106
1107    ptr   = (unsigned char *)attr_buff;
1108    guard = ptr + total_attr_size;
1109
1110#ifdef HAIKU_USE_KERN_OPEN
1111    fd = _kern_open( -1, name, O_RDONLY | O_NOTRAVERSE, 0 );
1112	if( fd < 0 )
1113         return fd;
1114#else
1115    fd = open( name, O_RDONLY | O_NOTRAVERSE );
1116    if( fd < 0 ) {
1117        return errno; /* should it be -fd ? */
1118    }
1119#endif
1120
1121    while( ptr < guard ) {
1122        ssize_t              wrote_bytes;
1123        const char          *attr_name;
1124        unsigned char       *attr_data;
1125		uint32				attr_type;
1126		int64				attr_size;
1127
1128        attr_name  = (char *)&(ptr[0]);
1129        ptr       += strlen( attr_name ) + 1;
1130
1131        /* The attr_info data is stored in big-endian format because the */
1132        /* PowerPC port was here first.                                  */
1133		memcpy( &attr_type, ptr, 4 ); ptr += 4;
1134		memcpy( &attr_size, ptr, 8 ); ptr += 8;
1135
1136        attr_type = (uint32)B_BENDIAN_TO_HOST_INT32( attr_type );
1137        attr_size = (off_t)B_BENDIAN_TO_HOST_INT64( attr_size );
1138
1139        if( attr_size < 0LL ) {
1140            Info(slide, 0x201, ((char *)slide,
1141                 "warning: skipping attribute with invalid length (%" B_PRIdOFF
1142				 ")\n", attr_size));
1143            break;
1144        }
1145
1146        attr_data  = ptr;
1147        ptr       += attr_size;
1148
1149        if( ptr > guard ) {
1150            /* We've got a truncated attribute. */
1151            Info(slide, 0x201, ((char *)slide,
1152                 "warning: truncated attribute\n"));
1153            break;
1154        }
1155
1156        /* Wave the magic wand... this will swap Be-known types properly. */
1157        (void)swap_data( attr_type, attr_data, attr_size,
1158                         B_SWAP_BENDIAN_TO_HOST );
1159
1160        wrote_bytes = fs_write_attr( fd, attr_name, attr_type, 0,
1161                                     attr_data, attr_size );
1162        if( wrote_bytes != attr_size ) {
1163            Info(slide, 0x201, ((char *)slide,
1164                 "warning: wrote %ld attribute bytes of %ld\n",
1165                 (unsigned long)wrote_bytes,(unsigned long)attr_size));
1166        }
1167    }
1168
1169    close( fd );
1170
1171    return retval;
1172}
1173
1174static void setBeOSexfield(const char *path, uch *extra_field)
1175{
1176    uch *ptr       = extra_field;
1177    ush  id        = 0;
1178    ush  size      = 0;
1179    ulg  full_size = 0;
1180    uch  flags     = 0;
1181    uch *attrbuff  = NULL;
1182    int retval;
1183
1184    if (extra_field == NULL)
1185        return;
1186
1187    /* Collect the data from the extra field buffer. */
1188    id        = makeword( ptr );    ptr += 2;   /* we don't use this... */
1189    size      = makeword( ptr );    ptr += 2;
1190    full_size = makelong( ptr );    ptr += 4;
1191    flags     = *ptr;               ptr++;
1192
1193    /* Do a little sanity checking. */
1194    if (flags & EB_BE_FL_BADBITS) {
1195        /* corrupted or unsupported */
1196        Info(slide, 0x201, ((char *)slide,
1197             "Unsupported flags set for this BeOS extra field, skipping.\n"));
1198        return;
1199    }
1200    if (size <= EB_BEOS_HLEN) {
1201        /* corrupted, unsupported, or truncated */
1202        Info(slide, 0x201, ((char *)slide,
1203             "BeOS extra field is %d bytes, should be at least %d.\n", size,
1204             EB_BEOS_HLEN));
1205        return;
1206    }
1207    if (full_size < (uint32)(size - EB_BEOS_HLEN)) {
1208        /* possible old archive? will this screw up on valid archives? */
1209        Info(slide, 0x201, ((char *)slide,
1210             "Skipping attributes: BeOS extra field is %d bytes, "
1211             "data size is %ld.\n", size - EB_BEOS_HLEN, full_size));
1212        return;
1213    }
1214
1215    /* Find the BeOS file attribute data. */
1216    if (flags & EB_BE_FL_UNCMPR) {
1217        /* Uncompressed data */
1218        attrbuff = ptr;
1219    } else {
1220        /* Compressed data */
1221        attrbuff = (uch *)malloc(full_size);
1222        if (attrbuff == NULL) {
1223            /* No memory to uncompress attributes */
1224            Info(slide, 0x201, ((char *)slide,
1225                 "Can't allocate memory to uncompress file attributes.\n"));
1226            return;
1227        }
1228
1229        retval = memextract(__G__ attrbuff, full_size, ptr, size - EB_BEOS_HLEN);
1230        if (retval != PK_OK) {
1231            /* error uncompressing attributes */
1232            Info(slide, 0x201, ((char *)slide,
1233                 "Error uncompressing file attributes.\n"));
1234
1235            /* Some errors here might not be so bad; we should expect */
1236            /* some truncated data, for example.  If the data was     */
1237            /* corrupt, we should _not_ attempt to restore the attrs  */
1238            /* for this file... there's no way to detect what attrs   */
1239            /* are good and which are bad.                            */
1240            free(attrbuff);
1241            return;
1242        }
1243    }
1244
1245    /* Now attempt to set the file attributes on the extracted file. */
1246    retval = set_file_attrs(path, attrbuff, (off_t)full_size);
1247    if (retval != EOK) {
1248        Info(slide, 0x201, ((char *)slide,
1249             "Error writing file attributes.\n"));
1250    }
1251
1252    /* Clean up, if necessary */
1253    if (attrbuff != ptr)
1254        free(attrbuff);
1255}
1256
1257#ifdef BEOS_USE_PRINTEXFIELD
1258static void printBeOSexfield( int isdir, uch *extra_field )
1259{
1260    uch *ptr       = extra_field;
1261    ush  id        = 0;
1262    ush  size      = 0;
1263    ulg  full_size = 0;
1264    uch  flags     = 0;
1265
1266    /* Tell picky compilers to be quiet. */
1267    isdir = isdir;
1268
1269    if( extra_field == NULL ) {
1270        return;
1271    }
1272
1273    /* Collect the data from the buffer. */
1274    id        = makeword( ptr );    ptr += 2;
1275    size      = makeword( ptr );    ptr += 2;
1276    full_size = makelong( ptr );    ptr += 4;
1277    flags     = *ptr;               ptr++;
1278
1279    if( id != EF_BEOS ) {
1280        /* not a 'Be' field */
1281        printf("\t*** Unknown field type (0x%04x, '%c%c')\n", id,
1282               (char)(id >> 8), (char)id);
1283    }
1284
1285    if( flags & EB_BE_FL_BADBITS ) {
1286        /* corrupted or unsupported */
1287        printf("\t*** Corrupted BeOS extra field:\n");
1288        printf("\t*** unknown bits set in the flags\n");
1289        printf("\t*** (Possibly created by an old version of zip for BeOS.\n");
1290    }
1291
1292    if( size <= EB_BEOS_HLEN ) {
1293        /* corrupted, unsupported, or truncated */
1294        printf("\t*** Corrupted BeOS extra field:\n");
1295        printf("\t*** size is %d, should be larger than %d\n", size,
1296               EB_BEOS_HLEN );
1297    }
1298
1299    if( flags & EB_BE_FL_UNCMPR ) {
1300        /* Uncompressed data */
1301        printf("\tBeOS extra field data (uncompressed):\n");
1302        printf("\t\t%ld data bytes\n", full_size);
1303    } else {
1304        /* Compressed data */
1305        printf("\tBeOS extra field data (compressed):\n");
1306        printf("\t\t%d compressed bytes\n", size - EB_BEOS_HLEN);
1307        printf("\t\t%ld uncompressed bytes\n", full_size);
1308    }
1309}
1310#endif
1311
1312#ifdef BEOS_ASSIGN_FILETYPE
1313/* Note: This will no longer be necessary in BeOS PR4; update_mime_info()    */
1314/* will be updated to build its own absolute pathname if it's not given one. */
1315static void assign_MIME( const char *file )
1316{
1317    char *fullname;
1318    char buff[PATH_MAX], cwd_buff[PATH_MAX];
1319    int retval;
1320
1321    if( file[0] == '/' ) {
1322        fullname = (char *)file;
1323    } else {
1324        sprintf( buff, "%s/%s", getcwd( cwd_buff, PATH_MAX ), file );
1325        fullname = buff;
1326    }
1327
1328    retval = update_mime_info( fullname, FALSE, TRUE, TRUE );
1329}
1330#endif
1331