|
|
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.