|
|
1.1 root 1: /*
1.1.1.6 root 2: Hatari - file.c
1.1 root 3:
1.1.1.5 root 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:
1.1.1.6 root 7: Common file access functions.
1.1 root 8: */
1.1.1.14 root 9: const char File_fileid[] = "Hatari file.c : " __DATE__ " " __TIME__;
1.1.1.11 root 10:
1.1 root 11: #include <sys/types.h>
12: #include <sys/stat.h>
1.1.1.15 root 13: #include <sys/time.h>
1.1 root 14: #include <fcntl.h>
1.1.1.6 root 15: #include <unistd.h>
1.1.1.11 root 16: #include <assert.h>
17: #include <errno.h>
1.1.1.6 root 18: #include <zlib.h>
1.1 root 19:
1.1.1.15 root 20: #if defined(WIN32) && !defined(_VCWIN_)
1.1.1.14 root 21: #include <winsock2.h>
22: #endif
23:
1.1 root 24: #include "main.h"
1.1.1.7 root 25: #include "dialog.h"
1.1 root 26: #include "file.h"
27: #include "createBlankImage.h"
1.1.1.15 root 28: #include "str.h"
1.1.1.10 root 29: #include "zip.h"
1.1 root 30:
1.1.1.16! root 31: #if defined(WIN32)
! 32: #define ftello ftell
! 33: #endif
1.1.1.2 root 34:
35: /*-----------------------------------------------------------------------*/
1.1.1.11 root 36: /**
37: * Remove any '/'s from end of filenames, but keeps / intact
38: */
1.1 root 39: void File_CleanFileName(char *pszFileName)
40: {
1.1.1.10 root 41: int len;
1.1.1.3 root 42:
1.1.1.10 root 43: len = strlen(pszFileName);
1.1.1.3 root 44:
1.1.1.16! root 45: /* Remove end slashes from filename! But / remains! Doh! */
! 46: while (len > 2 && pszFileName[--len] == PATHSEP)
! 47: pszFileName[len] = '\0';
1.1 root 48: }
49:
1.1.1.2 root 50:
51: /*-----------------------------------------------------------------------*/
1.1.1.11 root 52: /**
53: * Add '/' to end of filename
54: */
1.1 root 55: void File_AddSlashToEndFileName(char *pszFileName)
56: {
1.1.1.10 root 57: int len;
58:
59: len = strlen(pszFileName);
60:
61: /* Check dir/filenames */
62: if (len != 0)
63: {
1.1.1.11 root 64: if (pszFileName[len-1] != PATHSEP)
1.1.1.10 root 65: {
66: pszFileName[len] = PATHSEP; /* Must use end slash */
1.1.1.11 root 67: pszFileName[len+1] = '\0';
1.1.1.10 root 68: }
69: }
1.1 root 70: }
71:
1.1.1.3 root 72:
1.1 root 73: /*-----------------------------------------------------------------------*/
1.1.1.11 root 74: /**
75: * Does filename extension match? If so, return TRUE
76: */
1.1.1.13 root 77: bool File_DoesFileExtensionMatch(const char *pszFileName, const char *pszExtension)
1.1 root 78: {
1.1.1.10 root 79: if (strlen(pszFileName) < strlen(pszExtension))
1.1.1.15 root 80: return false;
1.1.1.10 root 81: /* Is matching extension? */
82: if (!strcasecmp(&pszFileName[strlen(pszFileName)-strlen(pszExtension)], pszExtension))
1.1.1.15 root 83: return true;
1.1 root 84:
1.1.1.10 root 85: /* No */
1.1.1.15 root 86: return false;
1.1 root 87: }
88:
1.1.1.2 root 89:
90: /*-----------------------------------------------------------------------*/
1.1.1.11 root 91: /**
92: * Check if filename is from root
1.1.1.16! root 93: *
1.1.1.11 root 94: * Return TRUE if filename is '/', else give FALSE
95: */
1.1.1.13 root 96: static bool File_IsRootFileName(const char *pszFileName)
1.1 root 97: {
1.1.1.11 root 98: if (pszFileName[0] == '\0') /* If NULL string return! */
1.1.1.15 root 99: return false;
1.1 root 100:
1.1.1.11 root 101: if (pszFileName[0] == PATHSEP)
1.1.1.15 root 102: return true;
1.1 root 103:
1.1.1.10 root 104: #ifdef WIN32
1.1.1.11 root 105: if (pszFileName[1] == ':')
1.1.1.15 root 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;
1.1.1.10 root 116: #endif
117:
1.1.1.15 root 118: return false;
1.1 root 119: }
120:
1.1.1.2 root 121:
122: /*-----------------------------------------------------------------------*/
1.1.1.11 root 123: /**
124: * Return string, to remove 'C:' part of filename
125: */
1.1.1.8 root 126: const char *File_RemoveFileNameDrive(const char *pszFileName)
1.1 root 127: {
1.1.1.11 root 128: if ( (pszFileName[0] != '\0') && (pszFileName[1] == ':') )
1.1.1.10 root 129: return &pszFileName[2];
130: else
131: return pszFileName;
1.1 root 132: }
133:
134:
1.1.1.2 root 135: /*-----------------------------------------------------------------------*/
1.1.1.11 root 136: /**
1.1.1.13 root 137: * Check if given filename is an existing directory
1.1.1.16! root 138: *
1.1.1.13 root 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: }
1.1.1.16! root 146:
1.1.1.13 root 147:
148: /*-----------------------------------------------------------------------*/
149: /**
1.1.1.11 root 150: * Check if filename end with a '/'
1.1.1.16! root 151: *
1.1.1.11 root 152: * Return TRUE if filename ends with '/'
153: */
1.1.1.13 root 154: bool File_DoesFileNameEndWithSlash(char *pszFileName)
1.1 root 155: {
1.1.1.10 root 156: if (pszFileName[0] == '\0') /* If NULL string return! */
1.1.1.15 root 157: return false;
1.1.1.16! root 158:
1.1.1.10 root 159: /* Does string end in a '/'? */
160: if (pszFileName[strlen(pszFileName)-1] == PATHSEP)
1.1.1.15 root 161: return true;
1.1.1.16! root 162:
1.1.1.15 root 163: return false;
1.1 root 164: }
165:
166:
1.1.1.2 root 167: /*-----------------------------------------------------------------------*/
1.1.1.11 root 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[])
1.1 root 174: {
1.1.1.11 root 175: char *filepath = NULL;
1.1.1.15 root 176: Uint8 *pFile = NULL;
1.1.1.10 root 177: long FileSize = 0;
1.1 root 178:
1.1.1.10 root 179: /* Does the file exist? If not, see if can scan for other extensions and try these */
180: if (!File_Exists(pszFileName) && ppszExts)
181: {
1.1.1.11 root 182: /* Try other extensions, if succeeds, returns correct one */
183: filepath = File_FindPossibleExtFileName(pszFileName, ppszExts);
1.1.1.10 root 184: }
1.1.1.11 root 185: if (!filepath)
186: filepath = strdup(pszFileName);
1.1.1.10 root 187:
188: /* Is it a gzipped file? */
1.1.1.11 root 189: if (File_DoesFileExtensionMatch(filepath, ".gz"))
1.1.1.10 root 190: {
191: gzFile hGzFile;
192: /* Open and read gzipped file */
1.1.1.11 root 193: hGzFile = gzopen(filepath, "rb");
1.1.1.10 root 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... */
1.1.1.11 root 206: pFile = malloc(FileSize);
1.1.1.10 root 207: if (pFile)
208: FileSize = gzread(hGzFile, pFile, FileSize);
209:
210: gzclose(hGzFile);
211: }
212: }
1.1.1.11 root 213: else if (File_DoesFileExtensionMatch(filepath, ".zip"))
1.1.1.10 root 214: {
215: /* It is a .ZIP file! -> Try to load the first file in the archive */
1.1.1.11 root 216: pFile = ZIP_ReadFirstFile(filepath, &FileSize, ppszExts);
1.1.1.10 root 217: }
218: else /* It is a normal file */
219: {
220: FILE *hDiskFile;
221: /* Open and read normal file */
1.1.1.11 root 222: hDiskFile = fopen(filepath, "rb");
1.1.1.10 root 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... */
1.1.1.11 root 230: pFile = malloc(FileSize);
1.1.1.10 root 231: if (pFile)
232: FileSize = fread(pFile, 1, FileSize, hDiskFile);
233:
234: fclose(hDiskFile);
235: }
236: }
1.1.1.11 root 237: free(filepath);
1.1.1.10 root 238:
239: /* Store size of file we read in (or 0 if failed) */
240: if (pFileSize)
241: *pFileSize = FileSize;
1.1 root 242:
1.1.1.10 root 243: return pFile; /* Return to where read in/allocated */
1.1 root 244: }
245:
1.1.1.2 root 246:
247: /*-----------------------------------------------------------------------*/
1.1.1.11 root 248: /**
249: * Save file to disk, return FALSE if errors
250: */
1.1.1.13 root 251: bool File_Save(const char *pszFileName, const Uint8 *pAddress, size_t Size, bool bQueryOverwrite)
1.1 root 252: {
1.1.1.15 root 253: bool bRet = false;
1.1 root 254:
1.1.1.10 root 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))
1.1.1.15 root 260: return false;
1.1.1.10 root 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)
1.1.1.15 root 273: bRet = true;
1.1.1.10 root 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)
1.1.1.15 root 287: bRet = true;
1.1.1.10 root 288:
289: fclose(hDiskFile);
290: }
291: }
1.1 root 292:
1.1.1.10 root 293: return bRet;
1.1 root 294: }
295:
1.1.1.2 root 296:
297: /*-----------------------------------------------------------------------*/
1.1.1.11 root 298: /**
299: * Return size of file, -1 if error
300: */
1.1.1.16! root 301: off_t File_Length(const char *pszFileName)
1.1 root 302: {
1.1.1.10 root 303: FILE *hDiskFile;
1.1.1.16! root 304: off_t FileSize;
1.1.1.9 root 305:
1.1.1.10 root 306: hDiskFile = fopen(pszFileName, "rb");
307: if (hDiskFile!=NULL)
308: {
309: fseek(hDiskFile, 0, SEEK_END);
1.1.1.16! root 310: FileSize = ftello(hDiskFile);
1.1.1.10 root 311: fseek(hDiskFile, 0, SEEK_SET);
312: fclose(hDiskFile);
313: return FileSize;
314: }
1.1 root 315:
1.1.1.10 root 316: return -1;
1.1 root 317: }
318:
1.1.1.2 root 319:
320: /*-----------------------------------------------------------------------*/
1.1.1.11 root 321: /**
322: * Return TRUE if file exists, is readable or writable at least and is not
1.1.1.16! root 323: * a directory.
1.1.1.11 root 324: */
1.1.1.13 root 325: bool File_Exists(const char *filename)
1.1.1.11 root 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 */
1.1.1.15 root 332: return true;
1.1.1.11 root 333: }
1.1.1.15 root 334: return false;
1.1.1.11 root 335: }
1.1 root 336:
1.1.1.11 root 337:
338: /*-----------------------------------------------------------------------*/
339: /**
1.1.1.16! root 340: * Return TRUE if directory exists.
1.1.1.11 root 341: */
1.1.1.13 root 342: bool File_DirectoryExists(const char *psDirName)
1.1.1.11 root 343: {
344: struct stat buf;
345: if (stat(psDirName, &buf) == 0 && (buf.st_mode & S_IFDIR))
1.1.1.10 root 346: {
1.1.1.15 root 347: return true;
1.1.1.10 root 348: }
1.1.1.15 root 349: return false;
1.1 root 350: }
351:
1.1.1.2 root 352:
353: /*-----------------------------------------------------------------------*/
1.1.1.11 root 354: /**
355: * Find if file exists, and if so ask user if OK to overwrite
356: */
1.1.1.13 root 357: bool File_QueryOverwrite(const char *pszFileName)
1.1 root 358: {
1.1.1.11 root 359: const char *fmt;
360: char *szString;
1.1.1.15 root 361: bool ret = true;
1.1 root 362:
1.1.1.10 root 363: /* Try and find if file exists */
364: if (File_Exists(pszFileName))
365: {
1.1.1.11 root 366: fmt = "File '%s' exists, overwrite?";
1.1.1.10 root 367: /* File does exist, are we OK to overwrite? */
1.1.1.11 root 368: szString = malloc(strlen(pszFileName) + strlen(fmt) + 1);
369: sprintf(szString, fmt, pszFileName);
1.1.1.10 root 370: fprintf(stderr, "%s\n", szString);
1.1.1.11 root 371: ret = DlgAlert_Query(szString);
372: free(szString);
1.1.1.10 root 373: }
1.1.1.11 root 374: return ret;
1.1 root 375: }
376:
1.1.1.2 root 377:
378: /*-----------------------------------------------------------------------*/
1.1.1.11 root 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[])
1.1 root 385: {
1.1.1.10 root 386: char *szSrcDir, *szSrcName, *szSrcExt;
1.1.1.11 root 387: int i;
1.1.1.16! root 388:
1.1.1.10 root 389: /* Allocate temporary memory for strings: */
1.1.1.11 root 390: szSrcDir = malloc(3 * FILENAME_MAX);
391: if (!szSrcDir)
1.1.1.10 root 392: {
393: perror("File_FindPossibleExtFileName");
1.1.1.15 root 394: return false;
1.1.1.10 root 395: }
396: szSrcName = szSrcDir + FILENAME_MAX;
397: szSrcExt = szSrcName + FILENAME_MAX;
1.1.1.16! root 398:
1.1.1.10 root 399: /* Split filename into parts */
1.1.1.11 root 400: File_SplitPath(pszFileName, szSrcDir, szSrcName, szSrcExt);
1.1.1.10 root 401:
402: /* Scan possible extensions */
1.1.1.11 root 403: for (i = 0; ppszExts[i]; i++)
1.1.1.10 root 404: {
1.1.1.11 root 405: char *szTempFileName;
406:
1.1.1.10 root 407: /* Re-build with new file extension */
1.1.1.11 root 408: szTempFileName = File_MakePath(szSrcDir, szSrcName, ppszExts[i]);
409: if (szTempFileName)
1.1.1.10 root 410: {
1.1.1.11 root 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);
1.1.1.10 root 419: }
420: }
1.1.1.11 root 421: free(szSrcDir);
422: return NULL;
1.1 root 423: }
1.1.1.3 root 424:
425:
426: /*-----------------------------------------------------------------------*/
1.1.1.11 root 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)
1.1.1.3 root 433: {
1.1.1.10 root 434: char *ptr1, *ptr2;
1.1.1.3 root 435:
1.1.1.10 root 436: /* Build pathname: */
437: ptr1 = strrchr(pSrcFileName, PATHSEP);
438: if (ptr1)
439: {
440: strcpy(pName, ptr1+1);
1.1.1.16! root 441: memmove(pDir, pSrcFileName, ptr1-pSrcFileName);
! 442: pDir[ptr1-pSrcFileName] = 0;
1.1.1.10 root 443: }
444: else
445: {
1.1.1.11 root 446: strcpy(pName, pSrcFileName);
1.1.1.10 root 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: }
1.1.1.3 root 463: }
464:
465:
466: /*-----------------------------------------------------------------------*/
1.1.1.11 root 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)
1.1.1.3 root 473: {
1.1.1.11 root 474: char *filepath;
1.1.1.10 root 475: int len;
476:
1.1.1.11 root 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)
1.1.1.10 root 481: {
1.1.1.11 root 482: perror("File_MakePath");
483: return NULL;
1.1.1.10 root 484: }
1.1.1.11 root 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);
1.1.1.10 root 498:
1.1.1.11 root 499: if (pExt != NULL && pExt[0])
1.1.1.10 root 500: {
1.1.1.11 root 501: len += strlen(pName);
502: if (pExt[0] != '.')
503: strcat(&filepath[len++], ".");
504: strcat(&filepath[len], pExt);
1.1.1.10 root 505: }
1.1.1.11 root 506: return filepath;
1.1.1.3 root 507: }
508:
509:
510: /*-----------------------------------------------------------------------*/
1.1.1.11 root 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)
1.1.1.3 root 516: {
1.1.1.10 root 517: int srclen = strlen(pSrcFileName);
518: if (srclen < maxlen)
519: strcpy(pDestFileName, pSrcFileName); /* It fits! */
520: else
521: {
1.1.1.11 root 522: assert(maxlen > 6);
1.1.1.10 root 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: }
1.1.1.3 root 531: }
532:
1.1.1.6 root 533:
534: /*-----------------------------------------------------------------------*/
1.1.1.11 root 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:
1.1.1.14 root 544: /* empty name signifies file that shouldn't be opened/enabled */
545: if (!*path)
546: return NULL;
1.1.1.16! root 547:
1.1.1.11 root 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: /**
1.1.1.14 root 596: * Check if input is available at the specified file descriptor.
597: */
598: bool File_InputAvailable(FILE *fp)
599: {
1.1.1.15 root 600: #if HAVE_SELECT
1.1.1.14 root 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 */
1.1.1.15 root 622: #endif
1.1.1.14 root 623:
624: return false;
625: }
626:
627:
628: /*-----------------------------------------------------------------------*/
629: /**
1.1.1.11 root 630: * Wrapper for File_MakeAbsoluteName() which special-cases stdin/out/err
1.1.1.14 root 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!
1.1.1.11 root 633: * (On Linux one can use /dev/stdout etc, this is intended for other OSes)
634: */
635: void File_MakeAbsoluteSpecialName(char *path)
636: {
1.1.1.14 root 637: if (path[0] &&
638: strcmp(path, "stdin") != 0 &&
1.1.1.11 root 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: */
1.1.1.6 root 650: void File_MakeAbsoluteName(char *pFileName)
651: {
1.1.1.10 root 652: char *pTempName;
653: int inpos, outpos;
1.1.1.6 root 654:
1.1.1.11 root 655: #if defined (__AMIGAOS4__)
656: /* This function does not work on Amiga OS */
657: return;
658: #endif
659:
1.1.1.10 root 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: }
1.1.1.11 root 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))
1.1.1.10 root 701: {
702: /* Handle "../" */
703: char *pSlashPos;
1.1.1.11 root 704: inpos += 2;
1.1.1.10 root 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: }
1.1.1.11 root 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: }
1.1.1.10 root 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: }
1.1.1.6 root 743:
1.1.1.10 root 744: pTempName[outpos] = 0;
1.1.1.6 root 745:
1.1.1.10 root 746: strcpy(pFileName, pTempName); /* Copy back */
747: free(pTempName);
1.1.1.8 root 748: }
749:
750:
751: /*-----------------------------------------------------------------------*/
1.1.1.11 root 752: /**
753: * Create a valid path name from a possibly invalid name by erasing invalid
1.1.1.16! root 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.
1.1.1.11 root 757: */
1.1.1.8 root 758: void File_MakeValidPathName(char *pPathName)
759: {
1.1.1.10 root 760: struct stat dirstat;
761: char *pLastSlash;
1.1.1.8 root 762:
1.1.1.10 root 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: }
1.1.1.16! root 777: else
1.1.1.10 root 778: {
1.1.1.16! root 779: if (pPathName[0])
! 780: {
! 781: /* point to root */
! 782: pPathName[0] = PATHSEP;
! 783: pPathName[1] = 0;
! 784: }
! 785: return;
1.1.1.10 root 786: }
787: }
788: while (pLastSlash);
1.1.1.12 root 789:
790: /* Make sure that path name ends with a slash */
791: File_AddSlashToEndFileName(pPathName);
1.1.1.6 root 792: }
1.1.1.11 root 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.