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