|
|
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.1.22 root 11: #include "main.h"
1.1 root 12: #include <sys/types.h>
13: #include <sys/stat.h>
1.1.1.22 root 14: #if HAVE_SYS_TIME_H
1.1.1.15 root 15: #include <sys/time.h>
1.1.1.22 root 16: #endif
1.1 root 17: #include <fcntl.h>
1.1.1.6 root 18: #include <unistd.h>
1.1.1.11 root 19: #include <assert.h>
20: #include <errno.h>
1.1.1.22 root 21: #if HAVE_ZLIB_H
1.1.1.6 root 22: #include <zlib.h>
1.1.1.22 root 23: #endif
1.1.1.15 root 24: #if defined(WIN32) && !defined(_VCWIN_)
1.1.1.14 root 25: #include <winsock2.h>
26: #endif
27:
1.1.1.7 root 28: #include "dialog.h"
1.1 root 29: #include "file.h"
30: #include "createBlankImage.h"
1.1.1.15 root 31: #include "str.h"
1.1.1.10 root 32: #include "zip.h"
1.1.1.23! root 33: #include "log.h"
1.1 root 34:
1.1.1.21 root 35: #ifdef HAVE_FLOCK
36: # include <sys/file.h>
37: #endif
1.1.1.2 root 38:
39: /*-----------------------------------------------------------------------*/
1.1.1.11 root 40: /**
41: * Remove any '/'s from end of filenames, but keeps / intact
42: */
1.1 root 43: void File_CleanFileName(char *pszFileName)
44: {
1.1.1.10 root 45: int len;
1.1.1.3 root 46:
1.1.1.10 root 47: len = strlen(pszFileName);
1.1.1.3 root 48:
1.1.1.16 root 49: /* Remove end slashes from filename! But / remains! Doh! */
50: while (len > 2 && pszFileName[--len] == PATHSEP)
51: pszFileName[len] = '\0';
1.1 root 52: }
53:
1.1.1.2 root 54:
55: /*-----------------------------------------------------------------------*/
1.1.1.11 root 56: /**
57: * Add '/' to end of filename
58: */
1.1 root 59: void File_AddSlashToEndFileName(char *pszFileName)
60: {
1.1.1.10 root 61: int len;
62:
63: len = strlen(pszFileName);
64:
65: /* Check dir/filenames */
66: if (len != 0)
67: {
1.1.1.11 root 68: if (pszFileName[len-1] != PATHSEP)
1.1.1.10 root 69: {
70: pszFileName[len] = PATHSEP; /* Must use end slash */
1.1.1.11 root 71: pszFileName[len+1] = '\0';
1.1.1.10 root 72: }
73: }
1.1 root 74: }
75:
1.1.1.3 root 76:
1.1 root 77: /*-----------------------------------------------------------------------*/
1.1.1.11 root 78: /**
1.1.1.20 root 79: * Does filename's extension match? If so, return TRUE
1.1.1.11 root 80: */
1.1.1.13 root 81: bool File_DoesFileExtensionMatch(const char *pszFileName, const char *pszExtension)
1.1 root 82: {
1.1.1.10 root 83: if (strlen(pszFileName) < strlen(pszExtension))
1.1.1.15 root 84: return false;
1.1.1.10 root 85: /* Is matching extension? */
86: if (!strcasecmp(&pszFileName[strlen(pszFileName)-strlen(pszExtension)], pszExtension))
1.1.1.15 root 87: return true;
1.1 root 88:
1.1.1.10 root 89: /* No */
1.1.1.15 root 90: return false;
1.1 root 91: }
92:
1.1.1.2 root 93:
94: /*-----------------------------------------------------------------------*/
1.1.1.11 root 95: /**
1.1.1.20 root 96: * If filename's extension matches, replace it with a new extension and
97: * copy the result in the new filename.
98: * Return TRUE if OK
99: */
100: bool File_ChangeFileExtension(const char *Filename_old, const char *Extension_old , char *Filename_new , const char *Extension_new)
101: {
102: if ( strlen ( Filename_old ) >= FILENAME_MAX - strlen ( Extension_new ) )
103: return false; /* file name is already too long */
104:
105: if ( strlen ( Filename_old ) < strlen ( Extension_old ) )
106: return false;
107:
108: if ( !strcasecmp ( Filename_old + strlen(Filename_old) - strlen(Extension_old) , Extension_old ) )
109: {
110: strcpy ( Filename_new , Filename_old );
111: strcpy ( Filename_new + strlen ( Filename_new ) - strlen ( Extension_old ) , Extension_new );
112: return true;
113: }
114:
115: return false;
116: }
117:
118:
119: /*-----------------------------------------------------------------------*/
120: /**
1.1.1.11 root 121: * Check if filename is from root
1.1.1.16 root 122: *
1.1.1.11 root 123: * Return TRUE if filename is '/', else give FALSE
124: */
1.1.1.13 root 125: static bool File_IsRootFileName(const char *pszFileName)
1.1 root 126: {
1.1.1.11 root 127: if (pszFileName[0] == '\0') /* If NULL string return! */
1.1.1.15 root 128: return false;
1.1 root 129:
1.1.1.11 root 130: if (pszFileName[0] == PATHSEP)
1.1.1.15 root 131: return true;
1.1 root 132:
1.1.1.10 root 133: #ifdef WIN32
1.1.1.11 root 134: if (pszFileName[1] == ':')
1.1.1.15 root 135: return true;
136: #endif
137:
138: #ifdef GEKKO
139: if (strlen(pszFileName) > 2 && pszFileName[2] == ':') // sd:
140: return true;
141: if (strlen(pszFileName) > 3 && pszFileName[3] == ':') // fat:
142: return true;
143: if (strlen(pszFileName) > 4 && pszFileName[4] == ':') // fat3:
144: return true;
1.1.1.10 root 145: #endif
146:
1.1.1.15 root 147: return false;
1.1 root 148: }
149:
1.1.1.2 root 150:
151: /*-----------------------------------------------------------------------*/
1.1.1.11 root 152: /**
153: * Return string, to remove 'C:' part of filename
154: */
1.1.1.8 root 155: const char *File_RemoveFileNameDrive(const char *pszFileName)
1.1 root 156: {
1.1.1.11 root 157: if ( (pszFileName[0] != '\0') && (pszFileName[1] == ':') )
1.1.1.10 root 158: return &pszFileName[2];
159: else
160: return pszFileName;
1.1 root 161: }
162:
163:
1.1.1.2 root 164: /*-----------------------------------------------------------------------*/
1.1.1.11 root 165: /**
166: * Check if filename end with a '/'
1.1.1.16 root 167: *
1.1.1.11 root 168: * Return TRUE if filename ends with '/'
169: */
1.1.1.13 root 170: bool File_DoesFileNameEndWithSlash(char *pszFileName)
1.1 root 171: {
1.1.1.10 root 172: if (pszFileName[0] == '\0') /* If NULL string return! */
1.1.1.15 root 173: return false;
1.1.1.16 root 174:
1.1.1.10 root 175: /* Does string end in a '/'? */
176: if (pszFileName[strlen(pszFileName)-1] == PATHSEP)
1.1.1.15 root 177: return true;
1.1.1.16 root 178:
1.1.1.15 root 179: return false;
1.1 root 180: }
181:
182:
1.1.1.2 root 183: /*-----------------------------------------------------------------------*/
1.1.1.11 root 184: /**
185: * Read file from disk into allocated buffer and return the buffer
186: * or NULL for error. If pFileSize is non-NULL, read file size
187: * is set to that.
188: */
189: Uint8 *File_Read(const char *pszFileName, long *pFileSize, const char * const ppszExts[])
1.1 root 190: {
1.1.1.11 root 191: char *filepath = NULL;
1.1.1.15 root 192: Uint8 *pFile = NULL;
1.1.1.10 root 193: long FileSize = 0;
1.1 root 194:
1.1.1.10 root 195: /* Does the file exist? If not, see if can scan for other extensions and try these */
196: if (!File_Exists(pszFileName) && ppszExts)
197: {
1.1.1.11 root 198: /* Try other extensions, if succeeds, returns correct one */
199: filepath = File_FindPossibleExtFileName(pszFileName, ppszExts);
1.1.1.10 root 200: }
1.1.1.11 root 201: if (!filepath)
202: filepath = strdup(pszFileName);
1.1.1.10 root 203:
1.1.1.20 root 204: #if HAVE_LIBZ
1.1.1.10 root 205: /* Is it a gzipped file? */
1.1.1.11 root 206: if (File_DoesFileExtensionMatch(filepath, ".gz"))
1.1.1.10 root 207: {
208: gzFile hGzFile;
209: /* Open and read gzipped file */
1.1.1.11 root 210: hGzFile = gzopen(filepath, "rb");
1.1.1.10 root 211: if (hGzFile != NULL)
212: {
213: /* Find size of file: */
214: do
215: {
216: /* Seek through the file until we hit the end... */
1.1.1.19 root 217: char tmp[1024];
218: if (gzread(hGzFile, tmp, sizeof(tmp)) < 0)
219: {
220: fprintf(stderr, "Failed to read gzip file!\n");
1.1.1.20 root 221: free(filepath);
1.1.1.19 root 222: return NULL;
223: }
1.1.1.10 root 224: }
225: while (!gzeof(hGzFile));
226: FileSize = gztell(hGzFile);
227: gzrewind(hGzFile);
228: /* Read in... */
1.1.1.11 root 229: pFile = malloc(FileSize);
1.1.1.10 root 230: if (pFile)
231: FileSize = gzread(hGzFile, pFile, FileSize);
232:
233: gzclose(hGzFile);
234: }
235: }
1.1.1.11 root 236: else if (File_DoesFileExtensionMatch(filepath, ".zip"))
1.1.1.10 root 237: {
238: /* It is a .ZIP file! -> Try to load the first file in the archive */
1.1.1.11 root 239: pFile = ZIP_ReadFirstFile(filepath, &FileSize, ppszExts);
1.1.1.10 root 240: }
241: else /* It is a normal file */
1.1.1.20 root 242: #endif /* HAVE_LIBZ */
1.1.1.10 root 243: {
244: FILE *hDiskFile;
245: /* Open and read normal file */
1.1.1.11 root 246: hDiskFile = fopen(filepath, "rb");
1.1.1.10 root 247: if (hDiskFile != NULL)
248: {
249: /* Find size of file: */
250: fseek(hDiskFile, 0, SEEK_END);
251: FileSize = ftell(hDiskFile);
252: fseek(hDiskFile, 0, SEEK_SET);
253: /* Read in... */
1.1.1.11 root 254: pFile = malloc(FileSize);
1.1.1.10 root 255: if (pFile)
256: FileSize = fread(pFile, 1, FileSize, hDiskFile);
257:
258: fclose(hDiskFile);
259: }
260: }
1.1.1.11 root 261: free(filepath);
1.1.1.10 root 262:
263: /* Store size of file we read in (or 0 if failed) */
264: if (pFileSize)
265: *pFileSize = FileSize;
1.1 root 266:
1.1.1.10 root 267: return pFile; /* Return to where read in/allocated */
1.1 root 268: }
269:
1.1.1.2 root 270:
271: /*-----------------------------------------------------------------------*/
1.1.1.11 root 272: /**
273: * Save file to disk, return FALSE if errors
274: */
1.1.1.13 root 275: bool File_Save(const char *pszFileName, const Uint8 *pAddress, size_t Size, bool bQueryOverwrite)
1.1 root 276: {
1.1.1.15 root 277: bool bRet = false;
1.1 root 278:
1.1.1.10 root 279: /* Check if need to ask user if to overwrite */
280: if (bQueryOverwrite)
281: {
282: /* If file exists, ask if OK to overwrite */
283: if (!File_QueryOverwrite(pszFileName))
1.1.1.15 root 284: return false;
1.1.1.10 root 285: }
286:
1.1.1.20 root 287: #if HAVE_LIBZ
1.1.1.10 root 288: /* Normal file or gzipped file? */
289: if (File_DoesFileExtensionMatch(pszFileName, ".gz"))
290: {
291: gzFile hGzFile;
292: /* Create a gzipped file: */
293: hGzFile = gzopen(pszFileName, "wb");
294: if (hGzFile != NULL)
295: {
296: /* Write data, set success flag */
297: if (gzwrite(hGzFile, pAddress, Size) == (int)Size)
1.1.1.15 root 298: bRet = true;
1.1.1.10 root 299:
300: gzclose(hGzFile);
301: }
302: }
303: else
1.1.1.20 root 304: #endif /* HAVE_LIBZ */
1.1.1.10 root 305: {
306: FILE *hDiskFile;
307: /* Create a normal file: */
308: hDiskFile = fopen(pszFileName, "wb");
309: if (hDiskFile != NULL)
310: {
311: /* Write data, set success flag */
312: if (fwrite(pAddress, 1, Size, hDiskFile) == Size)
1.1.1.15 root 313: bRet = true;
1.1.1.10 root 314:
315: fclose(hDiskFile);
316: }
317: }
1.1 root 318:
1.1.1.10 root 319: return bRet;
1.1 root 320: }
321:
1.1.1.2 root 322:
323: /*-----------------------------------------------------------------------*/
1.1.1.11 root 324: /**
325: * Return size of file, -1 if error
326: */
1.1.1.16 root 327: off_t File_Length(const char *pszFileName)
1.1 root 328: {
1.1.1.10 root 329: FILE *hDiskFile;
1.1.1.16 root 330: off_t FileSize;
1.1.1.9 root 331:
1.1.1.10 root 332: hDiskFile = fopen(pszFileName, "rb");
333: if (hDiskFile!=NULL)
334: {
1.1.1.22 root 335: fseeko(hDiskFile, 0, SEEK_END);
1.1.1.16 root 336: FileSize = ftello(hDiskFile);
1.1.1.10 root 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).
1.1.1.23! root 535: * Note: maxlen is the maximum length of the destination string, _not_
! 536: * including the final '\0' byte! So the destination buffer has to be
! 537: * at least one byte bigger than maxlen.
1.1.1.11 root 538: */
539: void File_ShrinkName(char *pDestFileName, const char *pSrcFileName, int maxlen)
1.1.1.3 root 540: {
1.1.1.10 root 541: int srclen = strlen(pSrcFileName);
542: if (srclen < maxlen)
543: strcpy(pDestFileName, pSrcFileName); /* It fits! */
544: else
545: {
1.1.1.11 root 546: assert(maxlen > 6);
1.1.1.10 root 547: strncpy(pDestFileName, pSrcFileName, maxlen/2);
548: if (maxlen&1) /* even or uneven? */
549: pDestFileName[maxlen/2-1] = 0;
550: else
551: pDestFileName[maxlen/2-2] = 0;
552: strcat(pDestFileName, "...");
553: strcat(pDestFileName, &pSrcFileName[strlen(pSrcFileName)-maxlen/2+1]);
554: }
1.1.1.3 root 555: }
556:
1.1.1.6 root 557:
558: /*-----------------------------------------------------------------------*/
1.1.1.11 root 559: /**
560: * Open given filename in given mode and handle "stdout" & "stderr"
561: * filenames specially. Return FILE* to the opened file or NULL on error.
562: */
563: FILE *File_Open(const char *path, const char *mode)
564: {
565: int wr = 0, rd = 0;
566: FILE *fp;
567:
1.1.1.14 root 568: /* empty name signifies file that shouldn't be opened/enabled */
569: if (!*path)
570: return NULL;
1.1.1.16 root 571:
1.1.1.11 root 572: /* special "stdout" and "stderr" files can be used
573: * for files which are written or appended
574: */
575: if (strchr(mode, 'w') || strchr(mode, 'a'))
576: wr = 1;
577: if (strchr(mode, 'r'))
578: rd = 1;
579: if (strcmp(path, "stdin") == 0)
580: {
581: assert(rd && !wr);
582: return stdin;
583: }
584: if (strcmp(path, "stdout") == 0)
585: {
586: assert(wr && !rd);
587: return stdout;
588: }
589: if (strcmp(path, "stderr") == 0)
590: {
591: assert(wr && !rd);
592: return stderr;
593: }
594: /* Open a normal log file */
595: fp = fopen(path, mode);
596: if (!fp)
1.1.1.18 root 597: fprintf(stderr, "Can't open file '%s' (wr=%i, rd=%i):\n %s\n",
598: path, wr, rd, strerror(errno));
599:
1.1.1.11 root 600: /* printf("'%s' opened in mode '%s'\n", path, mode, fp); */
601: return fp;
602: }
603:
604:
605: /*-----------------------------------------------------------------------*/
606: /**
607: * Close given FILE pointer and return the closed pointer
608: * as NULL for the idiom "fp = File_Close(fp);"
609: */
610: FILE *File_Close(FILE *fp)
611: {
612: if (fp && fp != stdin && fp != stdout && fp != stderr)
613: {
614: fclose(fp);
615: }
616: return NULL;
617: }
618:
619:
620: /*-----------------------------------------------------------------------*/
621: /**
1.1.1.21 root 622: * Internal lock function for File_Lock() / File_UnLock().
623: * Returns true on success, otherwise false.
624: */
625: static bool lock_operation(FILE *fp, int cmd)
626: {
627: #ifndef HAVE_FLOCK
628: # define DO_LOCK 0
629: # define DO_UNLOCK 0
630: return true;
631: #else
632: # define DO_LOCK (LOCK_EX|LOCK_NB)
633: # define DO_UNLOCK (LOCK_UN)
634: /* Advantage of locking is only small bit of extra safety if
635: * one runs (e.g. accidentally) multiple Hatari instances at
636: * same time, so replacing it with no-op is no big deal.
637: *
638: * NOTE: this uses BSD file locking as it's a bit more usable than POSIX one:
639: * http://0pointer.de/blog/projects/locking.html
640: */
641: int ret, fd = fileno(fp);
642: if (fd < 0)
643: return false;
644: ret = flock(fd, cmd);
645: return (ret >= 0);
646: #endif
647: }
648:
649: /*-----------------------------------------------------------------------*/
650: /**
651: * Takes advisory, exclusive lock on given file (FILE *).
652: * Returns false if locking fails (e.g. another Hatari
653: * instance has already file open for writing).
654: */
655: bool File_Lock(FILE *fp)
656: {
657: return lock_operation(fp, DO_LOCK);
658: }
659:
660: /*-----------------------------------------------------------------------*/
661: /**
662: * Releases advisory, exclusive lock on given file (FILE *).
663: */
664: void File_UnLock(FILE *fp)
665: {
666: lock_operation(fp, DO_UNLOCK);
667: }
668:
669:
670: /*-----------------------------------------------------------------------*/
671: /**
1.1.1.14 root 672: * Check if input is available at the specified file descriptor.
673: */
674: bool File_InputAvailable(FILE *fp)
675: {
1.1.1.15 root 676: #if HAVE_SELECT
1.1.1.14 root 677: fd_set rfds;
678: struct timeval tv;
679: int fh;
680: int ret;
681:
682: if (!fp || (fh = fileno(fp)) == -1)
683: return false;
684:
685: /* Add the file handle to the file descriptor set */
686: FD_ZERO(&rfds);
687: FD_SET(fh, &rfds);
688:
689: /* Return immediately */
690: tv.tv_sec = 0;
691: tv.tv_usec = 0;
692:
693: /* Check if file descriptor is ready for a read */
694: ret = select(fh+1, &rfds, NULL, NULL, &tv);
695:
696: if (ret > 0)
697: return true; /* Data available */
1.1.1.15 root 698: #endif
1.1.1.14 root 699:
700: return false;
701: }
702:
703:
704: /*-----------------------------------------------------------------------*/
705: /**
1.1.1.11 root 706: * Wrapper for File_MakeAbsoluteName() which special-cases stdin/out/err
1.1.1.14 root 707: * named files and empty file name. The given buffer should be opened
708: * with File_Open() and closed with File_Close() if this function is used!
1.1.1.11 root 709: * (On Linux one can use /dev/stdout etc, this is intended for other OSes)
710: */
711: void File_MakeAbsoluteSpecialName(char *path)
712: {
1.1.1.14 root 713: if (path[0] &&
714: strcmp(path, "stdin") != 0 &&
1.1.1.11 root 715: strcmp(path, "stdout") != 0 &&
716: strcmp(path, "stderr") != 0)
717: File_MakeAbsoluteName(path);
718: }
719:
720: /*-----------------------------------------------------------------------*/
721: /**
722: * Create a clean absolute file name from a (possibly) relative file name.
723: * I.e. filter out all occurancies of "./" and "../".
724: * pFileName needs to point to a buffer of at least FILENAME_MAX bytes.
725: */
1.1.1.6 root 726: void File_MakeAbsoluteName(char *pFileName)
727: {
1.1.1.10 root 728: char *pTempName;
729: int inpos, outpos;
1.1.1.6 root 730:
1.1.1.11 root 731: #if defined (__AMIGAOS4__)
732: /* This function does not work on Amiga OS */
733: return;
734: #endif
735:
1.1.1.10 root 736: inpos = 0;
737: pTempName = malloc(FILENAME_MAX);
738: if (!pTempName)
739: {
740: perror("File_MakeAbsoluteName - malloc");
741: return;
742: }
743:
744: /* Is it already an absolute name? */
745: if (File_IsRootFileName(pFileName))
746: {
747: outpos = 0;
748: }
749: else
750: {
751: if (!getcwd(pTempName, FILENAME_MAX))
752: {
753: perror("File_MakeAbsoluteName - getcwd");
754: free(pTempName);
755: return;
756: }
757: File_AddSlashToEndFileName(pTempName);
758: outpos = strlen(pTempName);
759: }
760:
761: /* Now filter out the relative paths "./" and "../" */
762: while (pFileName[inpos] != 0 && outpos < FILENAME_MAX)
763: {
764: if (pFileName[inpos] == '.' && pFileName[inpos+1] == PATHSEP)
765: {
766: /* Ignore "./" */
767: inpos += 2;
768: }
1.1.1.11 root 769: else if (pFileName[inpos] == '.' && pFileName[inpos+1] == 0)
770: {
771: inpos += 1; /* Ignore "." at the end of the path string */
772: if (outpos > 1)
773: pTempName[outpos - 1] = 0; /* Remove the last slash, too */
774: }
775: else if (pFileName[inpos] == '.' && pFileName[inpos+1] == '.'
776: && (pFileName[inpos+2] == PATHSEP || pFileName[inpos+2] == 0))
1.1.1.10 root 777: {
778: /* Handle "../" */
779: char *pSlashPos;
1.1.1.11 root 780: inpos += 2;
1.1.1.10 root 781: pTempName[outpos - 1] = 0;
782: pSlashPos = strrchr(pTempName, PATHSEP);
783: if (pSlashPos)
784: {
785: *(pSlashPos + 1) = 0;
786: outpos = strlen(pTempName);
787: }
788: else
789: {
790: pTempName[0] = PATHSEP;
791: outpos = 1;
792: }
1.1.1.11 root 793: /* Were we already at the end of the string or is there more to come? */
794: if (pFileName[inpos] == PATHSEP)
795: {
796: /* There was a slash after the '..', so skip slash and
797: * simply proceed with next part */
798: inpos += 1;
799: }
800: else
801: {
802: /* We were at the end of the string, so let's remove the slash
803: * from the new string, too */
804: if (outpos > 1)
805: pTempName[outpos - 1] = 0;
806: }
1.1.1.10 root 807: }
808: else
809: {
810: /* Copy until next slash or end of input string */
811: while (pFileName[inpos] != 0 && outpos < FILENAME_MAX)
812: {
813: pTempName[outpos++] = pFileName[inpos++];
814: if (pFileName[inpos - 1] == PATHSEP)
815: break;
816: }
817: }
818: }
1.1.1.6 root 819:
1.1.1.10 root 820: pTempName[outpos] = 0;
1.1.1.6 root 821:
1.1.1.10 root 822: strcpy(pFileName, pTempName); /* Copy back */
823: free(pTempName);
1.1.1.8 root 824: }
825:
826:
827: /*-----------------------------------------------------------------------*/
1.1.1.11 root 828: /**
829: * Create a valid path name from a possibly invalid name by erasing invalid
1.1.1.16 root 830: * path parts at the end of the string. If string doesn't contain any path
831: * component, it will be pointed to the root directory. Empty string will
832: * be left as-is to prevent overwriting past allocated area.
1.1.1.11 root 833: */
1.1.1.8 root 834: void File_MakeValidPathName(char *pPathName)
835: {
1.1.1.10 root 836: struct stat dirstat;
837: char *pLastSlash;
1.1.1.8 root 838:
1.1.1.10 root 839: do
840: {
841: /* Check for a valid path */
842: if (stat(pPathName, &dirstat) == 0 && S_ISDIR(dirstat.st_mode))
843: {
844: break;
845: }
846:
847: pLastSlash = strrchr(pPathName, PATHSEP);
848: if (pLastSlash)
849: {
850: /* Erase the (probably invalid) part after the last slash */
851: *pLastSlash = 0;
852: }
1.1.1.16 root 853: else
1.1.1.10 root 854: {
1.1.1.16 root 855: if (pPathName[0])
856: {
857: /* point to root */
858: pPathName[0] = PATHSEP;
859: pPathName[1] = 0;
860: }
861: return;
1.1.1.10 root 862: }
863: }
864: while (pLastSlash);
1.1.1.12 root 865:
866: /* Make sure that path name ends with a slash */
867: File_AddSlashToEndFileName(pPathName);
1.1.1.6 root 868: }
1.1.1.11 root 869:
870:
871: /*-----------------------------------------------------------------------*/
872: /**
873: * Remove given number of path elements from the end of the given path.
874: * Leaves '/' at the end if path still has directories. Given path
875: * may not be empty.
876: */
877: void File_PathShorten(char *path, int dirs)
878: {
879: int i, n = 0;
880: /* ignore last char, it may or may not be '/' */
881: i = strlen(path)-1;
882: assert(i >= 0);
883: while(i > 0 && n < dirs) {
884: if (path[--i] == PATHSEP)
885: n++;
886: }
887: if (path[i] == PATHSEP) {
888: path[i+1] = '\0';
889: } else {
890: path[0] = PATHSEP;
891: path[1] = '\0';
892: }
893: }
894:
895:
896: /*-----------------------------------------------------------------------*/
897: /*
898: If "/." or "/.." at end, remove that and in case of ".." remove
899: also preceding dir (go one dir up). Leave '/' at the end of
900: the path.
901: */
902: void File_HandleDotDirs(char *path)
903: {
904: int len = strlen(path);
905: if (len >= 2 &&
906: path[len-2] == PATHSEP &&
907: path[len-1] == '.')
908: {
909: /* keep in same dir */
910: path[len-1] = '\0';
911: }
912: else if (len >= 3 &&
913: path[len-3] == PATHSEP &&
914: path[len-2] == '.' &&
915: path[len-1] == '.')
916: {
917: /* go one dir up */
918: if (len == 3) {
919: path[1] = 0; /* already root */
920: } else {
921: char *ptr;
922: path[len-3] = 0;
923: ptr = strrchr(path, PATHSEP);
924: if (ptr)
925: *(ptr+1) = 0;
926: }
927: }
928: }
1.1.1.23! root 929:
! 930:
! 931: #if defined(WIN32)
! 932: static TCHAR szTempFileName[MAX_PATH];
! 933:
! 934: /*-----------------------------------------------------------------------*/
! 935: /**
! 936: * Get temporary filename for Windows
! 937: */
! 938: char* WinTmpFile(void)
! 939: {
! 940: DWORD dwRetVal = 0;
! 941: UINT uRetVal = 0;
! 942: TCHAR lpTempPathBuffer[MAX_PATH];
! 943:
! 944: /* Gets the temp path env string (no guarantee it's a valid path) */
! 945: dwRetVal = GetTempPath(MAX_PATH, /* length of the buffer */
! 946: lpTempPathBuffer); /* buffer for path */
! 947: if (dwRetVal > MAX_PATH || (dwRetVal == 0))
! 948: {
! 949: Log_Printf(LOG_ERROR, "GetTempPath failed.\n");
! 950: return NULL;
! 951: }
! 952:
! 953: /* Generates a temporary file name */
! 954: uRetVal = GetTempFileName(lpTempPathBuffer, /* directory for tmp files */
! 955: TEXT("HATARI"), /* temp file name prefix */
! 956: 0, /* create unique name */
! 957: szTempFileName); /* buffer for name */
! 958: if (uRetVal == 0)
! 959: {
! 960: Log_Printf(LOG_ERROR, "GetTempFileName failed.\n");
! 961: return NULL;
! 962: }
! 963: return (char*)szTempFileName;
! 964: }
! 965: #endif
! 966:
! 967:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.