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