|
|
1.1 ! root 1: /* ! 2: Hatari - file.c ! 3: ! 4: This file is distributed under the GNU Public License, version 2 or at ! 5: your option any later version. Read the file gpl.txt for details. ! 6: ! 7: Common file access functions. ! 8: */ ! 9: const char File_fileid[] = "Hatari file.c : " __DATE__ " " __TIME__; ! 10: ! 11: #include <sys/types.h> ! 12: #include <sys/stat.h> ! 13: #include <sys/time.h> ! 14: #include <fcntl.h> ! 15: #include <unistd.h> ! 16: #include <assert.h> ! 17: #include <errno.h> ! 18: #include <zlib.h> ! 19: ! 20: #if defined(WIN32) && !defined(_VCWIN_) ! 21: #include <winsock2.h> ! 22: #endif ! 23: ! 24: #include "main.h" ! 25: #include "dialog.h" ! 26: #include "file.h" ! 27: #include "createBlankImage.h" ! 28: #include "str.h" ! 29: #include "zip.h" ! 30: ! 31: #if defined(WIN32) ! 32: #define ftello ftell ! 33: #endif ! 34: ! 35: /*-----------------------------------------------------------------------*/ ! 36: /** ! 37: * Remove any '/'s from end of filenames, but keeps / intact ! 38: */ ! 39: void File_CleanFileName(char *pszFileName) ! 40: { ! 41: int len; ! 42: ! 43: len = strlen(pszFileName); ! 44: ! 45: /* Remove end slashes from filename! But / remains! Doh! */ ! 46: while (len > 2 && pszFileName[--len] == PATHSEP) ! 47: pszFileName[len] = '\0'; ! 48: } ! 49: ! 50: ! 51: /*-----------------------------------------------------------------------*/ ! 52: /** ! 53: * Add '/' to end of filename ! 54: */ ! 55: void File_AddSlashToEndFileName(char *pszFileName) ! 56: { ! 57: int len; ! 58: ! 59: len = strlen(pszFileName); ! 60: ! 61: /* Check dir/filenames */ ! 62: if (len != 0) ! 63: { ! 64: if (pszFileName[len-1] != PATHSEP) ! 65: { ! 66: pszFileName[len] = PATHSEP; /* Must use end slash */ ! 67: pszFileName[len+1] = '\0'; ! 68: } ! 69: } ! 70: } ! 71: ! 72: ! 73: /*-----------------------------------------------------------------------*/ ! 74: /** ! 75: * Does filename extension match? If so, return TRUE ! 76: */ ! 77: bool File_DoesFileExtensionMatch(const char *pszFileName, const char *pszExtension) ! 78: { ! 79: if (strlen(pszFileName) < strlen(pszExtension)) ! 80: return false; ! 81: /* Is matching extension? */ ! 82: if (!strcasecmp(&pszFileName[strlen(pszFileName)-strlen(pszExtension)], pszExtension)) ! 83: return true; ! 84: ! 85: /* No */ ! 86: return false; ! 87: } ! 88: ! 89: ! 90: /*-----------------------------------------------------------------------*/ ! 91: /** ! 92: * Check if filename is from root ! 93: * ! 94: * Return TRUE if filename is '/', else give FALSE ! 95: */ ! 96: static bool File_IsRootFileName(const char *pszFileName) ! 97: { ! 98: if (pszFileName[0] == '\0') /* If NULL string return! */ ! 99: return false; ! 100: ! 101: if (pszFileName[0] == PATHSEP) ! 102: return true; ! 103: ! 104: #ifdef WIN32 ! 105: if (pszFileName[1] == ':') ! 106: return true; ! 107: #endif ! 108: ! 109: #ifdef GEKKO ! 110: if (strlen(pszFileName) > 2 && pszFileName[2] == ':') // sd: ! 111: return true; ! 112: if (strlen(pszFileName) > 3 && pszFileName[3] == ':') // fat: ! 113: return true; ! 114: if (strlen(pszFileName) > 4 && pszFileName[4] == ':') // fat3: ! 115: return true; ! 116: #endif ! 117: ! 118: return false; ! 119: } ! 120: ! 121: ! 122: /*-----------------------------------------------------------------------*/ ! 123: /** ! 124: * Return string, to remove 'C:' part of filename ! 125: */ ! 126: const char *File_RemoveFileNameDrive(const char *pszFileName) ! 127: { ! 128: if ( (pszFileName[0] != '\0') && (pszFileName[1] == ':') ) ! 129: return &pszFileName[2]; ! 130: else ! 131: return pszFileName; ! 132: } ! 133: ! 134: ! 135: /*-----------------------------------------------------------------------*/ ! 136: /** ! 137: * Check if given filename is an existing directory ! 138: * ! 139: * Return TRUE if directory, else give FALSE ! 140: */ ! 141: bool File_DirExists(const char *path) ! 142: { ! 143: struct stat filestat; ! 144: return (stat(path, &filestat) == 0 && S_ISDIR(filestat.st_mode)); ! 145: } ! 146: ! 147: ! 148: /*-----------------------------------------------------------------------*/ ! 149: /** ! 150: * Check if filename end with a '/' ! 151: * ! 152: * Return TRUE if filename ends with '/' ! 153: */ ! 154: bool File_DoesFileNameEndWithSlash(char *pszFileName) ! 155: { ! 156: if (pszFileName[0] == '\0') /* If NULL string return! */ ! 157: return false; ! 158: ! 159: /* Does string end in a '/'? */ ! 160: if (pszFileName[strlen(pszFileName)-1] == PATHSEP) ! 161: return true; ! 162: ! 163: return false; ! 164: } ! 165: ! 166: ! 167: /*-----------------------------------------------------------------------*/ ! 168: /** ! 169: * Read file from disk into allocated buffer and return the buffer ! 170: * or NULL for error. If pFileSize is non-NULL, read file size ! 171: * is set to that. ! 172: */ ! 173: Uint8 *File_Read(const char *pszFileName, long *pFileSize, const char * const ppszExts[]) ! 174: { ! 175: char *filepath = NULL; ! 176: Uint8 *pFile = NULL; ! 177: long FileSize = 0; ! 178: ! 179: /* Does the file exist? If not, see if can scan for other extensions and try these */ ! 180: if (!File_Exists(pszFileName) && ppszExts) ! 181: { ! 182: /* Try other extensions, if succeeds, returns correct one */ ! 183: filepath = File_FindPossibleExtFileName(pszFileName, ppszExts); ! 184: } ! 185: if (!filepath) ! 186: filepath = strdup(pszFileName); ! 187: ! 188: /* Is it a gzipped file? */ ! 189: if (File_DoesFileExtensionMatch(filepath, ".gz")) ! 190: { ! 191: gzFile hGzFile; ! 192: /* Open and read gzipped file */ ! 193: hGzFile = gzopen(filepath, "rb"); ! 194: if (hGzFile != NULL) ! 195: { ! 196: /* Find size of file: */ ! 197: do ! 198: { ! 199: /* Seek through the file until we hit the end... */ ! 200: gzseek(hGzFile, 1024, SEEK_CUR); ! 201: } ! 202: while (!gzeof(hGzFile)); ! 203: FileSize = gztell(hGzFile); ! 204: gzrewind(hGzFile); ! 205: /* Read in... */ ! 206: pFile = malloc(FileSize); ! 207: if (pFile) ! 208: FileSize = gzread(hGzFile, pFile, FileSize); ! 209: ! 210: gzclose(hGzFile); ! 211: } ! 212: } ! 213: else if (File_DoesFileExtensionMatch(filepath, ".zip")) ! 214: { ! 215: /* It is a .ZIP file! -> Try to load the first file in the archive */ ! 216: pFile = ZIP_ReadFirstFile(filepath, &FileSize, ppszExts); ! 217: } ! 218: else /* It is a normal file */ ! 219: { ! 220: FILE *hDiskFile; ! 221: /* Open and read normal file */ ! 222: hDiskFile = fopen(filepath, "rb"); ! 223: if (hDiskFile != NULL) ! 224: { ! 225: /* Find size of file: */ ! 226: fseek(hDiskFile, 0, SEEK_END); ! 227: FileSize = ftell(hDiskFile); ! 228: fseek(hDiskFile, 0, SEEK_SET); ! 229: /* Read in... */ ! 230: pFile = malloc(FileSize); ! 231: if (pFile) ! 232: FileSize = fread(pFile, 1, FileSize, hDiskFile); ! 233: ! 234: fclose(hDiskFile); ! 235: } ! 236: } ! 237: free(filepath); ! 238: ! 239: /* Store size of file we read in (or 0 if failed) */ ! 240: if (pFileSize) ! 241: *pFileSize = FileSize; ! 242: ! 243: return pFile; /* Return to where read in/allocated */ ! 244: } ! 245: ! 246: ! 247: /*-----------------------------------------------------------------------*/ ! 248: /** ! 249: * Save file to disk, return FALSE if errors ! 250: */ ! 251: bool File_Save(const char *pszFileName, const Uint8 *pAddress, size_t Size, bool bQueryOverwrite) ! 252: { ! 253: bool bRet = false; ! 254: ! 255: /* Check if need to ask user if to overwrite */ ! 256: if (bQueryOverwrite) ! 257: { ! 258: /* If file exists, ask if OK to overwrite */ ! 259: if (!File_QueryOverwrite(pszFileName)) ! 260: return false; ! 261: } ! 262: ! 263: /* Normal file or gzipped file? */ ! 264: if (File_DoesFileExtensionMatch(pszFileName, ".gz")) ! 265: { ! 266: gzFile hGzFile; ! 267: /* Create a gzipped file: */ ! 268: hGzFile = gzopen(pszFileName, "wb"); ! 269: if (hGzFile != NULL) ! 270: { ! 271: /* Write data, set success flag */ ! 272: if (gzwrite(hGzFile, pAddress, Size) == (int)Size) ! 273: bRet = true; ! 274: ! 275: gzclose(hGzFile); ! 276: } ! 277: } ! 278: else ! 279: { ! 280: FILE *hDiskFile; ! 281: /* Create a normal file: */ ! 282: hDiskFile = fopen(pszFileName, "wb"); ! 283: if (hDiskFile != NULL) ! 284: { ! 285: /* Write data, set success flag */ ! 286: if (fwrite(pAddress, 1, Size, hDiskFile) == Size) ! 287: bRet = true; ! 288: ! 289: fclose(hDiskFile); ! 290: } ! 291: } ! 292: ! 293: return bRet; ! 294: } ! 295: ! 296: ! 297: /*-----------------------------------------------------------------------*/ ! 298: /** ! 299: * Return size of file, -1 if error ! 300: */ ! 301: off_t File_Length(const char *pszFileName) ! 302: { ! 303: FILE *hDiskFile; ! 304: off_t FileSize; ! 305: ! 306: hDiskFile = fopen(pszFileName, "rb"); ! 307: if (hDiskFile!=NULL) ! 308: { ! 309: fseek(hDiskFile, 0, SEEK_END); ! 310: FileSize = ftello(hDiskFile); ! 311: fseek(hDiskFile, 0, SEEK_SET); ! 312: fclose(hDiskFile); ! 313: return FileSize; ! 314: } ! 315: ! 316: return -1; ! 317: } ! 318: ! 319: ! 320: /*-----------------------------------------------------------------------*/ ! 321: /** ! 322: * Return TRUE if file exists, is readable or writable at least and is not ! 323: * a directory. ! 324: */ ! 325: bool File_Exists(const char *filename) ! 326: { ! 327: struct stat buf; ! 328: if (stat(filename, &buf) == 0 && ! 329: (buf.st_mode & (S_IRUSR|S_IWUSR)) && !(buf.st_mode & S_IFDIR)) ! 330: { ! 331: /* file points to user readable regular file */ ! 332: return true; ! 333: } ! 334: return false; ! 335: } ! 336: ! 337: ! 338: /*-----------------------------------------------------------------------*/ ! 339: /** ! 340: * Return TRUE if directory exists. ! 341: */ ! 342: bool File_DirectoryExists(const char *psDirName) ! 343: { ! 344: struct stat buf; ! 345: if (stat(psDirName, &buf) == 0 && (buf.st_mode & S_IFDIR)) ! 346: { ! 347: return true; ! 348: } ! 349: return false; ! 350: } ! 351: ! 352: ! 353: /*-----------------------------------------------------------------------*/ ! 354: /** ! 355: * Find if file exists, and if so ask user if OK to overwrite ! 356: */ ! 357: bool File_QueryOverwrite(const char *pszFileName) ! 358: { ! 359: const char *fmt; ! 360: char *szString; ! 361: bool ret = true; ! 362: ! 363: /* Try and find if file exists */ ! 364: if (File_Exists(pszFileName)) ! 365: { ! 366: fmt = "File '%s' exists, overwrite?"; ! 367: /* File does exist, are we OK to overwrite? */ ! 368: szString = malloc(strlen(pszFileName) + strlen(fmt) + 1); ! 369: sprintf(szString, fmt, pszFileName); ! 370: fprintf(stderr, "%s\n", szString); ! 371: ret = DlgAlert_Query(szString); ! 372: free(szString); ! 373: } ! 374: return ret; ! 375: } ! 376: ! 377: ! 378: /*-----------------------------------------------------------------------*/ ! 379: /** ! 380: * Try filename with various extensions and check if file exists ! 381: * - if so, return allocated string which caller should free, ! 382: * otherwise return NULL ! 383: */ ! 384: char * File_FindPossibleExtFileName(const char *pszFileName, const char * const ppszExts[]) ! 385: { ! 386: char *szSrcDir, *szSrcName, *szSrcExt; ! 387: int i; ! 388: ! 389: /* Allocate temporary memory for strings: */ ! 390: szSrcDir = malloc(3 * FILENAME_MAX); ! 391: if (!szSrcDir) ! 392: { ! 393: perror("File_FindPossibleExtFileName"); ! 394: return false; ! 395: } ! 396: szSrcName = szSrcDir + FILENAME_MAX; ! 397: szSrcExt = szSrcName + FILENAME_MAX; ! 398: ! 399: /* Split filename into parts */ ! 400: File_SplitPath(pszFileName, szSrcDir, szSrcName, szSrcExt); ! 401: ! 402: /* Scan possible extensions */ ! 403: for (i = 0; ppszExts[i]; i++) ! 404: { ! 405: char *szTempFileName; ! 406: ! 407: /* Re-build with new file extension */ ! 408: szTempFileName = File_MakePath(szSrcDir, szSrcName, ppszExts[i]); ! 409: if (szTempFileName) ! 410: { ! 411: /* Does this file exist? */ ! 412: if (File_Exists(szTempFileName)) ! 413: { ! 414: free(szSrcDir); ! 415: /* return filename without extra strings */ ! 416: return szTempFileName; ! 417: } ! 418: free(szTempFileName); ! 419: } ! 420: } ! 421: free(szSrcDir); ! 422: return NULL; ! 423: } ! 424: ! 425: ! 426: /*-----------------------------------------------------------------------*/ ! 427: /** ! 428: * Split a complete filename into path, filename and extension. ! 429: * If pExt is NULL, don't split the extension from the file name! ! 430: * It's safe for pSrcFileName and pDir to be the same string. ! 431: */ ! 432: void File_SplitPath(const char *pSrcFileName, char *pDir, char *pName, char *pExt) ! 433: { ! 434: char *ptr1, *ptr2; ! 435: ! 436: /* Build pathname: */ ! 437: ptr1 = strrchr(pSrcFileName, PATHSEP); ! 438: if (ptr1) ! 439: { ! 440: strcpy(pName, ptr1+1); ! 441: memmove(pDir, pSrcFileName, ptr1-pSrcFileName); ! 442: pDir[ptr1-pSrcFileName] = 0; ! 443: } ! 444: else ! 445: { ! 446: strcpy(pName, pSrcFileName); ! 447: sprintf(pDir, ".%c", PATHSEP); ! 448: } ! 449: ! 450: /* Build the raw filename: */ ! 451: if (pExt != NULL) ! 452: { ! 453: ptr2 = strrchr(pName+1, '.'); ! 454: if (ptr2) ! 455: { ! 456: pName[ptr2-pName] = 0; ! 457: /* Copy the file extension: */ ! 458: strcpy(pExt, ptr2+1); ! 459: } ! 460: else ! 461: pExt[0] = 0; ! 462: } ! 463: } ! 464: ! 465: ! 466: /*-----------------------------------------------------------------------*/ ! 467: /** ! 468: * Construct a complete filename from path, filename and extension. ! 469: * Return the constructed filename. ! 470: * pExt can also be NULL. ! 471: */ ! 472: char * File_MakePath(const char *pDir, const char *pName, const char *pExt) ! 473: { ! 474: char *filepath; ! 475: int len; ! 476: ! 477: /* dir or "." + "/" + name + "." + ext + \0 */ ! 478: len = strlen(pDir) + 2 + strlen(pName) + 1 + (pExt ? strlen(pExt) : 0) + 1; ! 479: filepath = malloc(len); ! 480: if (!filepath) ! 481: { ! 482: perror("File_MakePath"); ! 483: return NULL; ! 484: } ! 485: if (!pDir[0]) ! 486: { ! 487: filepath[0] = '.'; ! 488: filepath[1] = '\0'; ! 489: } else { ! 490: strcpy(filepath, pDir); ! 491: } ! 492: len = strlen(filepath); ! 493: if (filepath[len-1] != PATHSEP) ! 494: { ! 495: filepath[len++] = PATHSEP; ! 496: } ! 497: strcpy(&filepath[len], pName); ! 498: ! 499: if (pExt != NULL && pExt[0]) ! 500: { ! 501: len += strlen(pName); ! 502: if (pExt[0] != '.') ! 503: strcat(&filepath[len++], "."); ! 504: strcat(&filepath[len], pExt); ! 505: } ! 506: return filepath; ! 507: } ! 508: ! 509: ! 510: /*-----------------------------------------------------------------------*/ ! 511: /** ! 512: * Shrink a file name to a certain length and insert some dots if we cut ! 513: * something away (useful for showing file names in a dialog). ! 514: */ ! 515: void File_ShrinkName(char *pDestFileName, const char *pSrcFileName, int maxlen) ! 516: { ! 517: int srclen = strlen(pSrcFileName); ! 518: if (srclen < maxlen) ! 519: strcpy(pDestFileName, pSrcFileName); /* It fits! */ ! 520: else ! 521: { ! 522: assert(maxlen > 6); ! 523: strncpy(pDestFileName, pSrcFileName, maxlen/2); ! 524: if (maxlen&1) /* even or uneven? */ ! 525: pDestFileName[maxlen/2-1] = 0; ! 526: else ! 527: pDestFileName[maxlen/2-2] = 0; ! 528: strcat(pDestFileName, "..."); ! 529: strcat(pDestFileName, &pSrcFileName[strlen(pSrcFileName)-maxlen/2+1]); ! 530: } ! 531: } ! 532: ! 533: ! 534: /*-----------------------------------------------------------------------*/ ! 535: /** ! 536: * Open given filename in given mode and handle "stdout" & "stderr" ! 537: * filenames specially. Return FILE* to the opened file or NULL on error. ! 538: */ ! 539: FILE *File_Open(const char *path, const char *mode) ! 540: { ! 541: int wr = 0, rd = 0; ! 542: FILE *fp; ! 543: ! 544: /* empty name signifies file that shouldn't be opened/enabled */ ! 545: if (!*path) ! 546: return NULL; ! 547: ! 548: /* special "stdout" and "stderr" files can be used ! 549: * for files which are written or appended ! 550: */ ! 551: if (strchr(mode, 'w') || strchr(mode, 'a')) ! 552: wr = 1; ! 553: if (strchr(mode, 'r')) ! 554: rd = 1; ! 555: if (strcmp(path, "stdin") == 0) ! 556: { ! 557: assert(rd && !wr); ! 558: return stdin; ! 559: } ! 560: if (strcmp(path, "stdout") == 0) ! 561: { ! 562: assert(wr && !rd); ! 563: return stdout; ! 564: } ! 565: if (strcmp(path, "stderr") == 0) ! 566: { ! 567: assert(wr && !rd); ! 568: return stderr; ! 569: } ! 570: /* Open a normal log file */ ! 571: fp = fopen(path, mode); ! 572: if (!fp) ! 573: fprintf(stderr, "Can't open file '%s':\n %s\n", path, strerror(errno)); ! 574: /* printf("'%s' opened in mode '%s'\n", path, mode, fp); */ ! 575: return fp; ! 576: } ! 577: ! 578: ! 579: /*-----------------------------------------------------------------------*/ ! 580: /** ! 581: * Close given FILE pointer and return the closed pointer ! 582: * as NULL for the idiom "fp = File_Close(fp);" ! 583: */ ! 584: FILE *File_Close(FILE *fp) ! 585: { ! 586: if (fp && fp != stdin && fp != stdout && fp != stderr) ! 587: { ! 588: fclose(fp); ! 589: } ! 590: return NULL; ! 591: } ! 592: ! 593: ! 594: /*-----------------------------------------------------------------------*/ ! 595: /** ! 596: * Check if input is available at the specified file descriptor. ! 597: */ ! 598: bool File_InputAvailable(FILE *fp) ! 599: { ! 600: #if HAVE_SELECT ! 601: fd_set rfds; ! 602: struct timeval tv; ! 603: int fh; ! 604: int ret; ! 605: ! 606: if (!fp || (fh = fileno(fp)) == -1) ! 607: return false; ! 608: ! 609: /* Add the file handle to the file descriptor set */ ! 610: FD_ZERO(&rfds); ! 611: FD_SET(fh, &rfds); ! 612: ! 613: /* Return immediately */ ! 614: tv.tv_sec = 0; ! 615: tv.tv_usec = 0; ! 616: ! 617: /* Check if file descriptor is ready for a read */ ! 618: ret = select(fh+1, &rfds, NULL, NULL, &tv); ! 619: ! 620: if (ret > 0) ! 621: return true; /* Data available */ ! 622: #endif ! 623: ! 624: return false; ! 625: } ! 626: ! 627: ! 628: /*-----------------------------------------------------------------------*/ ! 629: /** ! 630: * Wrapper for File_MakeAbsoluteName() which special-cases stdin/out/err ! 631: * named files and empty file name. The given buffer should be opened ! 632: * with File_Open() and closed with File_Close() if this function is used! ! 633: * (On Linux one can use /dev/stdout etc, this is intended for other OSes) ! 634: */ ! 635: void File_MakeAbsoluteSpecialName(char *path) ! 636: { ! 637: if (path[0] && ! 638: strcmp(path, "stdin") != 0 && ! 639: strcmp(path, "stdout") != 0 && ! 640: strcmp(path, "stderr") != 0) ! 641: File_MakeAbsoluteName(path); ! 642: } ! 643: ! 644: /*-----------------------------------------------------------------------*/ ! 645: /** ! 646: * Create a clean absolute file name from a (possibly) relative file name. ! 647: * I.e. filter out all occurancies of "./" and "../". ! 648: * pFileName needs to point to a buffer of at least FILENAME_MAX bytes. ! 649: */ ! 650: void File_MakeAbsoluteName(char *pFileName) ! 651: { ! 652: char *pTempName; ! 653: int inpos, outpos; ! 654: ! 655: #if defined (__AMIGAOS4__) ! 656: /* This function does not work on Amiga OS */ ! 657: return; ! 658: #endif ! 659: ! 660: inpos = 0; ! 661: pTempName = malloc(FILENAME_MAX); ! 662: if (!pTempName) ! 663: { ! 664: perror("File_MakeAbsoluteName - malloc"); ! 665: return; ! 666: } ! 667: ! 668: /* Is it already an absolute name? */ ! 669: if (File_IsRootFileName(pFileName)) ! 670: { ! 671: outpos = 0; ! 672: } ! 673: else ! 674: { ! 675: if (!getcwd(pTempName, FILENAME_MAX)) ! 676: { ! 677: perror("File_MakeAbsoluteName - getcwd"); ! 678: free(pTempName); ! 679: return; ! 680: } ! 681: File_AddSlashToEndFileName(pTempName); ! 682: outpos = strlen(pTempName); ! 683: } ! 684: ! 685: /* Now filter out the relative paths "./" and "../" */ ! 686: while (pFileName[inpos] != 0 && outpos < FILENAME_MAX) ! 687: { ! 688: if (pFileName[inpos] == '.' && pFileName[inpos+1] == PATHSEP) ! 689: { ! 690: /* Ignore "./" */ ! 691: inpos += 2; ! 692: } ! 693: else if (pFileName[inpos] == '.' && pFileName[inpos+1] == 0) ! 694: { ! 695: inpos += 1; /* Ignore "." at the end of the path string */ ! 696: if (outpos > 1) ! 697: pTempName[outpos - 1] = 0; /* Remove the last slash, too */ ! 698: } ! 699: else if (pFileName[inpos] == '.' && pFileName[inpos+1] == '.' ! 700: && (pFileName[inpos+2] == PATHSEP || pFileName[inpos+2] == 0)) ! 701: { ! 702: /* Handle "../" */ ! 703: char *pSlashPos; ! 704: inpos += 2; ! 705: pTempName[outpos - 1] = 0; ! 706: pSlashPos = strrchr(pTempName, PATHSEP); ! 707: if (pSlashPos) ! 708: { ! 709: *(pSlashPos + 1) = 0; ! 710: outpos = strlen(pTempName); ! 711: } ! 712: else ! 713: { ! 714: pTempName[0] = PATHSEP; ! 715: outpos = 1; ! 716: } ! 717: /* Were we already at the end of the string or is there more to come? */ ! 718: if (pFileName[inpos] == PATHSEP) ! 719: { ! 720: /* There was a slash after the '..', so skip slash and ! 721: * simply proceed with next part */ ! 722: inpos += 1; ! 723: } ! 724: else ! 725: { ! 726: /* We were at the end of the string, so let's remove the slash ! 727: * from the new string, too */ ! 728: if (outpos > 1) ! 729: pTempName[outpos - 1] = 0; ! 730: } ! 731: } ! 732: else ! 733: { ! 734: /* Copy until next slash or end of input string */ ! 735: while (pFileName[inpos] != 0 && outpos < FILENAME_MAX) ! 736: { ! 737: pTempName[outpos++] = pFileName[inpos++]; ! 738: if (pFileName[inpos - 1] == PATHSEP) ! 739: break; ! 740: } ! 741: } ! 742: } ! 743: ! 744: pTempName[outpos] = 0; ! 745: ! 746: strcpy(pFileName, pTempName); /* Copy back */ ! 747: free(pTempName); ! 748: } ! 749: ! 750: ! 751: /*-----------------------------------------------------------------------*/ ! 752: /** ! 753: * Create a valid path name from a possibly invalid name by erasing invalid ! 754: * path parts at the end of the string. If string doesn't contain any path ! 755: * component, it will be pointed to the root directory. Empty string will ! 756: * be left as-is to prevent overwriting past allocated area. ! 757: */ ! 758: void File_MakeValidPathName(char *pPathName) ! 759: { ! 760: struct stat dirstat; ! 761: char *pLastSlash; ! 762: ! 763: do ! 764: { ! 765: /* Check for a valid path */ ! 766: if (stat(pPathName, &dirstat) == 0 && S_ISDIR(dirstat.st_mode)) ! 767: { ! 768: break; ! 769: } ! 770: ! 771: pLastSlash = strrchr(pPathName, PATHSEP); ! 772: if (pLastSlash) ! 773: { ! 774: /* Erase the (probably invalid) part after the last slash */ ! 775: *pLastSlash = 0; ! 776: } ! 777: else ! 778: { ! 779: if (pPathName[0]) ! 780: { ! 781: /* point to root */ ! 782: pPathName[0] = PATHSEP; ! 783: pPathName[1] = 0; ! 784: } ! 785: return; ! 786: } ! 787: } ! 788: while (pLastSlash); ! 789: ! 790: /* Make sure that path name ends with a slash */ ! 791: File_AddSlashToEndFileName(pPathName); ! 792: } ! 793: ! 794: ! 795: /*-----------------------------------------------------------------------*/ ! 796: /** ! 797: * Remove given number of path elements from the end of the given path. ! 798: * Leaves '/' at the end if path still has directories. Given path ! 799: * may not be empty. ! 800: */ ! 801: void File_PathShorten(char *path, int dirs) ! 802: { ! 803: int i, n = 0; ! 804: /* ignore last char, it may or may not be '/' */ ! 805: i = strlen(path)-1; ! 806: assert(i >= 0); ! 807: while(i > 0 && n < dirs) { ! 808: if (path[--i] == PATHSEP) ! 809: n++; ! 810: } ! 811: if (path[i] == PATHSEP) { ! 812: path[i+1] = '\0'; ! 813: } else { ! 814: path[0] = PATHSEP; ! 815: path[1] = '\0'; ! 816: } ! 817: } ! 818: ! 819: ! 820: /*-----------------------------------------------------------------------*/ ! 821: /* ! 822: If "/." or "/.." at end, remove that and in case of ".." remove ! 823: also preceding dir (go one dir up). Leave '/' at the end of ! 824: the path. ! 825: */ ! 826: void File_HandleDotDirs(char *path) ! 827: { ! 828: int len = strlen(path); ! 829: if (len >= 2 && ! 830: path[len-2] == PATHSEP && ! 831: path[len-1] == '.') ! 832: { ! 833: /* keep in same dir */ ! 834: path[len-1] = '\0'; ! 835: } ! 836: else if (len >= 3 && ! 837: path[len-3] == PATHSEP && ! 838: path[len-2] == '.' && ! 839: path[len-1] == '.') ! 840: { ! 841: /* go one dir up */ ! 842: if (len == 3) { ! 843: path[1] = 0; /* already root */ ! 844: } else { ! 845: char *ptr; ! 846: path[len-3] = 0; ! 847: ptr = strrchr(path, PATHSEP); ! 848: if (ptr) ! 849: *(ptr+1) = 0; ! 850: } ! 851: } ! 852: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.