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