Annotation of hatari/src/file.c, revision 1.1.1.20

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.20! root       31: #ifndef HAVE_FTELLO
1.1.1.16  root       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: /**
1.1.1.20! root       75:  * Does filename's extension match? If so, return TRUE
1.1.1.11  root       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: /**
1.1.1.20! root       92:  * If filename's extension matches, replace it with a new extension and
        !            93:  * copy the result in the new filename.
        !            94:  * Return TRUE if OK
        !            95:  */
        !            96: bool File_ChangeFileExtension(const char *Filename_old, const char *Extension_old , char *Filename_new , const char *Extension_new)
        !            97: {
        !            98:        if ( strlen ( Filename_old ) >= FILENAME_MAX - strlen ( Extension_new ) )
        !            99:                return false;                                   /* file name is already too long */
        !           100: 
        !           101:        if ( strlen ( Filename_old ) < strlen ( Extension_old ) )
        !           102:                return false;
        !           103: 
        !           104:        if ( !strcasecmp ( Filename_old + strlen(Filename_old) - strlen(Extension_old) , Extension_old ) )
        !           105:        {
        !           106:                strcpy ( Filename_new , Filename_old );
        !           107:                strcpy ( Filename_new + strlen ( Filename_new ) - strlen ( Extension_old ) , Extension_new );
        !           108:                return true;
        !           109:        }
        !           110: 
        !           111:        return false;
        !           112: }
        !           113: 
        !           114: 
        !           115: /*-----------------------------------------------------------------------*/
        !           116: /**
1.1.1.11  root      117:  * Check if filename is from root
1.1.1.16  root      118:  *
1.1.1.11  root      119:  * Return TRUE if filename is '/', else give FALSE
                    120:  */
1.1.1.13  root      121: static bool File_IsRootFileName(const char *pszFileName)
1.1       root      122: {
1.1.1.11  root      123:        if (pszFileName[0] == '\0')     /* If NULL string return! */
1.1.1.15  root      124:                return false;
1.1       root      125: 
1.1.1.11  root      126:        if (pszFileName[0] == PATHSEP)
1.1.1.15  root      127:                return true;
1.1       root      128: 
1.1.1.10  root      129: #ifdef WIN32
1.1.1.11  root      130:        if (pszFileName[1] == ':')
1.1.1.15  root      131:                return true;
                    132: #endif
                    133: 
                    134: #ifdef GEKKO
                    135:        if (strlen(pszFileName) > 2 && pszFileName[2] == ':')   // sd:
                    136:                return true;
                    137:        if (strlen(pszFileName) > 3 && pszFileName[3] == ':')   // fat:
                    138:                return true;
                    139:        if (strlen(pszFileName) > 4 && pszFileName[4] == ':')   // fat3:
                    140:                return true;
1.1.1.10  root      141: #endif
                    142: 
1.1.1.15  root      143:        return false;
1.1       root      144: }
                    145: 
1.1.1.2   root      146: 
                    147: /*-----------------------------------------------------------------------*/
1.1.1.11  root      148: /**
                    149:  * Return string, to remove 'C:' part of filename
                    150:  */
1.1.1.8   root      151: const char *File_RemoveFileNameDrive(const char *pszFileName)
1.1       root      152: {
1.1.1.11  root      153:        if ( (pszFileName[0] != '\0') && (pszFileName[1] == ':') )
1.1.1.10  root      154:                return &pszFileName[2];
                    155:        else
                    156:                return pszFileName;
1.1       root      157: }
                    158: 
                    159: 
1.1.1.2   root      160: /*-----------------------------------------------------------------------*/
1.1.1.11  root      161: /**
                    162:  * Check if filename end with a '/'
1.1.1.16  root      163:  *
1.1.1.11  root      164:  * Return TRUE if filename ends with '/'
                    165:  */
1.1.1.13  root      166: bool File_DoesFileNameEndWithSlash(char *pszFileName)
1.1       root      167: {
1.1.1.10  root      168:        if (pszFileName[0] == '\0')    /* If NULL string return! */
1.1.1.15  root      169:                return false;
1.1.1.16  root      170: 
1.1.1.10  root      171:        /* Does string end in a '/'? */
                    172:        if (pszFileName[strlen(pszFileName)-1] == PATHSEP)
1.1.1.15  root      173:                return true;
1.1.1.16  root      174: 
1.1.1.15  root      175:        return false;
1.1       root      176: }
                    177: 
                    178: 
1.1.1.2   root      179: /*-----------------------------------------------------------------------*/
1.1.1.11  root      180: /**
                    181:  * Read file from disk into allocated buffer and return the buffer
                    182:  * or NULL for error.  If pFileSize is non-NULL, read file size
                    183:  * is set to that.
                    184:  */
                    185: Uint8 *File_Read(const char *pszFileName, long *pFileSize, const char * const ppszExts[])
1.1       root      186: {
1.1.1.11  root      187:        char *filepath = NULL;
1.1.1.15  root      188:        Uint8 *pFile = NULL;
1.1.1.10  root      189:        long FileSize = 0;
1.1       root      190: 
1.1.1.10  root      191:        /* Does the file exist? If not, see if can scan for other extensions and try these */
                    192:        if (!File_Exists(pszFileName) && ppszExts)
                    193:        {
1.1.1.11  root      194:                /* Try other extensions, if succeeds, returns correct one */
                    195:                filepath = File_FindPossibleExtFileName(pszFileName, ppszExts);
1.1.1.10  root      196:        }
1.1.1.11  root      197:        if (!filepath)
                    198:                filepath = strdup(pszFileName);
1.1.1.10  root      199: 
1.1.1.20! root      200: #if HAVE_LIBZ
1.1.1.10  root      201:        /* Is it a gzipped file? */
1.1.1.11  root      202:        if (File_DoesFileExtensionMatch(filepath, ".gz"))
1.1.1.10  root      203:        {
                    204:                gzFile hGzFile;
                    205:                /* Open and read gzipped file */
1.1.1.11  root      206:                hGzFile = gzopen(filepath, "rb");
1.1.1.10  root      207:                if (hGzFile != NULL)
                    208:                {
                    209:                        /* Find size of file: */
                    210:                        do
                    211:                        {
                    212:                                /* Seek through the file until we hit the end... */
1.1.1.19  root      213:                                char tmp[1024];
                    214:                                if (gzread(hGzFile, tmp, sizeof(tmp)) < 0)
                    215:                                {
                    216:                                        fprintf(stderr, "Failed to read gzip file!\n");
1.1.1.20! root      217:                                        free(filepath);
1.1.1.19  root      218:                                        return NULL;
                    219:                                }
1.1.1.10  root      220:                        }
                    221:                        while (!gzeof(hGzFile));
                    222:                        FileSize = gztell(hGzFile);
                    223:                        gzrewind(hGzFile);
                    224:                        /* Read in... */
1.1.1.11  root      225:                        pFile = malloc(FileSize);
1.1.1.10  root      226:                        if (pFile)
                    227:                                FileSize = gzread(hGzFile, pFile, FileSize);
                    228: 
                    229:                        gzclose(hGzFile);
                    230:                }
                    231:        }
1.1.1.11  root      232:        else if (File_DoesFileExtensionMatch(filepath, ".zip"))
1.1.1.10  root      233:        {
                    234:                /* It is a .ZIP file! -> Try to load the first file in the archive */
1.1.1.11  root      235:                pFile = ZIP_ReadFirstFile(filepath, &FileSize, ppszExts);
1.1.1.10  root      236:        }
                    237:        else          /* It is a normal file */
1.1.1.20! root      238: #endif  /* HAVE_LIBZ */
1.1.1.10  root      239:        {
                    240:                FILE *hDiskFile;
                    241:                /* Open and read normal file */
1.1.1.11  root      242:                hDiskFile = fopen(filepath, "rb");
1.1.1.10  root      243:                if (hDiskFile != NULL)
                    244:                {
                    245:                        /* Find size of file: */
                    246:                        fseek(hDiskFile, 0, SEEK_END);
                    247:                        FileSize = ftell(hDiskFile);
                    248:                        fseek(hDiskFile, 0, SEEK_SET);
                    249:                        /* Read in... */
1.1.1.11  root      250:                        pFile = malloc(FileSize);
1.1.1.10  root      251:                        if (pFile)
                    252:                                FileSize = fread(pFile, 1, FileSize, hDiskFile);
                    253: 
                    254:                        fclose(hDiskFile);
                    255:                }
                    256:        }
1.1.1.11  root      257:        free(filepath);
1.1.1.10  root      258: 
                    259:        /* Store size of file we read in (or 0 if failed) */
                    260:        if (pFileSize)
                    261:                *pFileSize = FileSize;
1.1       root      262: 
1.1.1.10  root      263:        return pFile;        /* Return to where read in/allocated */
1.1       root      264: }
                    265: 
1.1.1.2   root      266: 
                    267: /*-----------------------------------------------------------------------*/
1.1.1.11  root      268: /**
                    269:  * Save file to disk, return FALSE if errors
                    270:  */
1.1.1.13  root      271: bool File_Save(const char *pszFileName, const Uint8 *pAddress, size_t Size, bool bQueryOverwrite)
1.1       root      272: {
1.1.1.15  root      273:        bool bRet = false;
1.1       root      274: 
1.1.1.10  root      275:        /* Check if need to ask user if to overwrite */
                    276:        if (bQueryOverwrite)
                    277:        {
                    278:                /* If file exists, ask if OK to overwrite */
                    279:                if (!File_QueryOverwrite(pszFileName))
1.1.1.15  root      280:                        return false;
1.1.1.10  root      281:        }
                    282: 
1.1.1.20! root      283: #if HAVE_LIBZ
1.1.1.10  root      284:        /* Normal file or gzipped file? */
                    285:        if (File_DoesFileExtensionMatch(pszFileName, ".gz"))
                    286:        {
                    287:                gzFile hGzFile;
                    288:                /* Create a gzipped file: */
                    289:                hGzFile = gzopen(pszFileName, "wb");
                    290:                if (hGzFile != NULL)
                    291:                {
                    292:                        /* Write data, set success flag */
                    293:                        if (gzwrite(hGzFile, pAddress, Size) == (int)Size)
1.1.1.15  root      294:                                bRet = true;
1.1.1.10  root      295: 
                    296:                        gzclose(hGzFile);
                    297:                }
                    298:        }
                    299:        else
1.1.1.20! root      300: #endif  /* HAVE_LIBZ */
1.1.1.10  root      301:        {
                    302:                FILE *hDiskFile;
                    303:                /* Create a normal file: */
                    304:                hDiskFile = fopen(pszFileName, "wb");
                    305:                if (hDiskFile != NULL)
                    306:                {
                    307:                        /* Write data, set success flag */
                    308:                        if (fwrite(pAddress, 1, Size, hDiskFile) == Size)
1.1.1.15  root      309:                                bRet = true;
1.1.1.10  root      310: 
                    311:                        fclose(hDiskFile);
                    312:                }
                    313:        }
1.1       root      314: 
1.1.1.10  root      315:        return bRet;
1.1       root      316: }
                    317: 
1.1.1.2   root      318: 
                    319: /*-----------------------------------------------------------------------*/
1.1.1.11  root      320: /**
                    321:  * Return size of file, -1 if error
                    322:  */
1.1.1.16  root      323: off_t File_Length(const char *pszFileName)
1.1       root      324: {
1.1.1.10  root      325:        FILE *hDiskFile;
1.1.1.16  root      326:        off_t FileSize;
1.1.1.9   root      327: 
1.1.1.10  root      328:        hDiskFile = fopen(pszFileName, "rb");
                    329:        if (hDiskFile!=NULL)
                    330:        {
                    331:                fseek(hDiskFile, 0, SEEK_END);
1.1.1.16  root      332:                FileSize = ftello(hDiskFile);
1.1.1.10  root      333:                fseek(hDiskFile, 0, SEEK_SET);
                    334:                fclose(hDiskFile);
                    335:                return FileSize;
                    336:        }
1.1       root      337: 
1.1.1.10  root      338:        return -1;
1.1       root      339: }
                    340: 
1.1.1.2   root      341: 
                    342: /*-----------------------------------------------------------------------*/
1.1.1.11  root      343: /**
                    344:  * Return TRUE if file exists, is readable or writable at least and is not
1.1.1.16  root      345:  * a directory.
1.1.1.11  root      346:  */
1.1.1.13  root      347: bool File_Exists(const char *filename)
1.1.1.11  root      348: {
                    349:        struct stat buf;
                    350:        if (stat(filename, &buf) == 0 &&
                    351:            (buf.st_mode & (S_IRUSR|S_IWUSR)) && !(buf.st_mode & S_IFDIR))
                    352:        {
                    353:                /* file points to user readable regular file */
1.1.1.15  root      354:                return true;
1.1.1.11  root      355:        }
1.1.1.15  root      356:        return false;
1.1.1.11  root      357: }
1.1       root      358: 
1.1.1.11  root      359: 
                    360: /*-----------------------------------------------------------------------*/
                    361: /**
1.1.1.16  root      362:  * Return TRUE if directory exists.
1.1.1.11  root      363:  */
1.1.1.17  root      364: bool File_DirExists(const char *path)
1.1.1.11  root      365: {
                    366:        struct stat buf;
1.1.1.17  root      367:        return (stat(path, &buf) == 0 && S_ISDIR(buf.st_mode));
1.1       root      368: }
                    369: 
1.1.1.2   root      370: 
                    371: /*-----------------------------------------------------------------------*/
1.1.1.11  root      372: /**
                    373:  * Find if file exists, and if so ask user if OK to overwrite
                    374:  */
1.1.1.13  root      375: bool File_QueryOverwrite(const char *pszFileName)
1.1       root      376: {
1.1.1.11  root      377:        const char *fmt;
                    378:        char *szString;
1.1.1.15  root      379:        bool ret = true;
1.1       root      380: 
1.1.1.10  root      381:        /* Try and find if file exists */
                    382:        if (File_Exists(pszFileName))
                    383:        {
1.1.1.11  root      384:                fmt = "File '%s' exists, overwrite?";
1.1.1.10  root      385:                /* File does exist, are we OK to overwrite? */
1.1.1.11  root      386:                szString = malloc(strlen(pszFileName) + strlen(fmt) + 1);
                    387:                sprintf(szString, fmt, pszFileName);
1.1.1.10  root      388:                fprintf(stderr, "%s\n", szString);
1.1.1.11  root      389:                ret = DlgAlert_Query(szString);
                    390:                free(szString);
1.1.1.10  root      391:        }
1.1.1.11  root      392:        return ret;
1.1       root      393: }
                    394: 
1.1.1.2   root      395: 
                    396: /*-----------------------------------------------------------------------*/
1.1.1.11  root      397: /**
                    398:  * Try filename with various extensions and check if file exists
                    399:  * - if so, return allocated string which caller should free,
                    400:  *   otherwise return NULL
                    401:  */
                    402: char * File_FindPossibleExtFileName(const char *pszFileName, const char * const ppszExts[])
1.1       root      403: {
1.1.1.10  root      404:        char *szSrcDir, *szSrcName, *szSrcExt;
1.1.1.11  root      405:        int i;
1.1.1.16  root      406: 
1.1.1.10  root      407:        /* Allocate temporary memory for strings: */
1.1.1.11  root      408:        szSrcDir = malloc(3 * FILENAME_MAX);
                    409:        if (!szSrcDir)
1.1.1.10  root      410:        {
                    411:                perror("File_FindPossibleExtFileName");
1.1.1.15  root      412:                return false;
1.1.1.10  root      413:        }
                    414:        szSrcName = szSrcDir + FILENAME_MAX;
                    415:        szSrcExt = szSrcName + FILENAME_MAX;
1.1.1.16  root      416: 
1.1.1.10  root      417:        /* Split filename into parts */
1.1.1.11  root      418:        File_SplitPath(pszFileName, szSrcDir, szSrcName, szSrcExt);
1.1.1.10  root      419: 
                    420:        /* Scan possible extensions */
1.1.1.11  root      421:        for (i = 0; ppszExts[i]; i++)
1.1.1.10  root      422:        {
1.1.1.11  root      423:                char *szTempFileName;
                    424: 
1.1.1.10  root      425:                /* Re-build with new file extension */
1.1.1.11  root      426:                szTempFileName = File_MakePath(szSrcDir, szSrcName, ppszExts[i]);
                    427:                if (szTempFileName)
1.1.1.10  root      428:                {
1.1.1.11  root      429:                        /* Does this file exist? */
                    430:                        if (File_Exists(szTempFileName))
                    431:                        {
                    432:                                free(szSrcDir);
                    433:                                /* return filename without extra strings */
                    434:                                return szTempFileName;
                    435:                        }
                    436:                        free(szTempFileName);
1.1.1.10  root      437:                }
                    438:        }
1.1.1.11  root      439:        free(szSrcDir);
                    440:        return NULL;
1.1       root      441: }
1.1.1.3   root      442: 
                    443: 
                    444: /*-----------------------------------------------------------------------*/
1.1.1.11  root      445: /**
                    446:  * Split a complete filename into path, filename and extension.
                    447:  * If pExt is NULL, don't split the extension from the file name!
                    448:  * It's safe for pSrcFileName and pDir to be the same string.
                    449:  */
                    450: void File_SplitPath(const char *pSrcFileName, char *pDir, char *pName, char *pExt)
1.1.1.3   root      451: {
1.1.1.10  root      452:        char *ptr1, *ptr2;
1.1.1.3   root      453: 
1.1.1.10  root      454:        /* Build pathname: */
                    455:        ptr1 = strrchr(pSrcFileName, PATHSEP);
                    456:        if (ptr1)
                    457:        {
                    458:                strcpy(pName, ptr1+1);
1.1.1.16  root      459:                memmove(pDir, pSrcFileName, ptr1-pSrcFileName);
                    460:                pDir[ptr1-pSrcFileName] = 0;
1.1.1.10  root      461:        }
                    462:        else
                    463:        {
1.1.1.11  root      464:                strcpy(pName, pSrcFileName);
1.1.1.10  root      465:                sprintf(pDir, ".%c", PATHSEP);
                    466:        }
                    467: 
                    468:        /* Build the raw filename: */
                    469:        if (pExt != NULL)
                    470:        {
                    471:                ptr2 = strrchr(pName+1, '.');
                    472:                if (ptr2)
                    473:                {
                    474:                        pName[ptr2-pName] = 0;
                    475:                        /* Copy the file extension: */
                    476:                        strcpy(pExt, ptr2+1);
                    477:                }
                    478:                else
                    479:                        pExt[0] = 0;
                    480:        }
1.1.1.3   root      481: }
                    482: 
                    483: 
                    484: /*-----------------------------------------------------------------------*/
1.1.1.11  root      485: /**
                    486:  * Construct a complete filename from path, filename and extension.
                    487:  * Return the constructed filename.
                    488:  * pExt can also be NULL.
                    489:  */
                    490: char * File_MakePath(const char *pDir, const char *pName, const char *pExt)
1.1.1.3   root      491: {
1.1.1.11  root      492:        char *filepath;
1.1.1.10  root      493:        int len;
                    494: 
1.1.1.11  root      495:        /* dir or "." + "/" + name + "." + ext + \0 */
                    496:        len = strlen(pDir) + 2 + strlen(pName) + 1 + (pExt ? strlen(pExt) : 0) + 1;
                    497:        filepath = malloc(len);
                    498:        if (!filepath)
1.1.1.10  root      499:        {
1.1.1.11  root      500:                perror("File_MakePath");
                    501:                return NULL;
1.1.1.10  root      502:        }
1.1.1.11  root      503:        if (!pDir[0])
                    504:        {
                    505:                filepath[0] = '.';
                    506:                filepath[1] = '\0';
                    507:        } else {
                    508:                strcpy(filepath, pDir);
                    509:        }
                    510:        len = strlen(filepath);
                    511:        if (filepath[len-1] != PATHSEP)
                    512:        {
                    513:                filepath[len++] = PATHSEP;
                    514:        }
                    515:        strcpy(&filepath[len], pName);
1.1.1.10  root      516: 
1.1.1.11  root      517:        if (pExt != NULL && pExt[0])
1.1.1.10  root      518:        {
1.1.1.11  root      519:                len += strlen(pName);
                    520:                if (pExt[0] != '.')
                    521:                        strcat(&filepath[len++], ".");
                    522:                strcat(&filepath[len], pExt);
1.1.1.10  root      523:        }
1.1.1.11  root      524:        return filepath;
1.1.1.3   root      525: }
                    526: 
                    527: 
                    528: /*-----------------------------------------------------------------------*/
1.1.1.11  root      529: /**
                    530:  * Shrink a file name to a certain length and insert some dots if we cut
                    531:  * something away (useful for showing file names in a dialog).
                    532:  */
                    533: void File_ShrinkName(char *pDestFileName, const char *pSrcFileName, int maxlen)
1.1.1.3   root      534: {
1.1.1.10  root      535:        int srclen = strlen(pSrcFileName);
                    536:        if (srclen < maxlen)
                    537:                strcpy(pDestFileName, pSrcFileName);  /* It fits! */
                    538:        else
                    539:        {
1.1.1.11  root      540:                assert(maxlen > 6);
1.1.1.10  root      541:                strncpy(pDestFileName, pSrcFileName, maxlen/2);
                    542:                if (maxlen&1)  /* even or uneven? */
                    543:                        pDestFileName[maxlen/2-1] = 0;
                    544:                else
                    545:                        pDestFileName[maxlen/2-2] = 0;
                    546:                strcat(pDestFileName, "...");
                    547:                strcat(pDestFileName, &pSrcFileName[strlen(pSrcFileName)-maxlen/2+1]);
                    548:        }
1.1.1.3   root      549: }
                    550: 
1.1.1.6   root      551: 
                    552: /*-----------------------------------------------------------------------*/
1.1.1.11  root      553: /**
                    554:  * Open given filename in given mode and handle "stdout" & "stderr"
                    555:  * filenames specially. Return FILE* to the opened file or NULL on error.
                    556:  */
                    557: FILE *File_Open(const char *path, const char *mode)
                    558: {
                    559:        int wr = 0, rd = 0;
                    560:        FILE *fp;
                    561: 
1.1.1.14  root      562:        /* empty name signifies file that shouldn't be opened/enabled */
                    563:        if (!*path)
                    564:                return NULL;
1.1.1.16  root      565: 
1.1.1.11  root      566:        /* special "stdout" and "stderr" files can be used
                    567:         * for files which are written or appended
                    568:         */
                    569:        if (strchr(mode, 'w') || strchr(mode, 'a'))
                    570:                wr = 1;
                    571:        if (strchr(mode, 'r'))
                    572:                rd = 1;
                    573:        if (strcmp(path, "stdin") == 0)
                    574:        {
                    575:                assert(rd && !wr);
                    576:                return stdin;
                    577:        }
                    578:        if (strcmp(path, "stdout") == 0)
                    579:        {
                    580:                assert(wr && !rd);
                    581:                return stdout;
                    582:        }
                    583:        if (strcmp(path, "stderr") == 0)
                    584:        {
                    585:                assert(wr && !rd);
                    586:                return stderr;
                    587:        }
                    588:        /* Open a normal log file */
                    589:        fp = fopen(path, mode);
                    590:        if (!fp)
1.1.1.18  root      591:                fprintf(stderr, "Can't open file '%s' (wr=%i, rd=%i):\n  %s\n",
                    592:                        path, wr, rd, strerror(errno));
                    593: 
1.1.1.11  root      594:        /* printf("'%s' opened in mode '%s'\n", path, mode, fp); */
                    595:        return fp;
                    596: }
                    597: 
                    598: 
                    599: /*-----------------------------------------------------------------------*/
                    600: /**
                    601:  * Close given FILE pointer and return the closed pointer
                    602:  * as NULL for the idiom "fp = File_Close(fp);"
                    603:  */
                    604: FILE *File_Close(FILE *fp)
                    605: {
                    606:        if (fp && fp != stdin && fp != stdout && fp != stderr)
                    607:        {
                    608:                fclose(fp);
                    609:        }
                    610:        return NULL;
                    611: }
                    612: 
                    613: 
                    614: /*-----------------------------------------------------------------------*/
                    615: /**
1.1.1.14  root      616:  * Check if input is available at the specified file descriptor.
                    617:  */
                    618: bool File_InputAvailable(FILE *fp)
                    619: {
1.1.1.15  root      620: #if HAVE_SELECT
1.1.1.14  root      621:        fd_set rfds;
                    622:        struct timeval tv;
                    623:        int fh;
                    624:        int ret;
                    625: 
                    626:        if (!fp || (fh = fileno(fp)) == -1)
                    627:                return false;
                    628: 
                    629:        /* Add the file handle to the file descriptor set */
                    630:        FD_ZERO(&rfds);
                    631:        FD_SET(fh, &rfds);
                    632: 
                    633:        /* Return immediately */
                    634:        tv.tv_sec = 0;
                    635:        tv.tv_usec = 0;
                    636: 
                    637:        /* Check if file descriptor is ready for a read */
                    638:        ret = select(fh+1, &rfds, NULL, NULL, &tv);
                    639: 
                    640:        if (ret > 0)
                    641:                return true;    /* Data available */
1.1.1.15  root      642: #endif
1.1.1.14  root      643: 
                    644:        return false;
                    645: }
                    646: 
                    647: 
                    648: /*-----------------------------------------------------------------------*/
                    649: /**
1.1.1.11  root      650:  * Wrapper for File_MakeAbsoluteName() which special-cases stdin/out/err
1.1.1.14  root      651:  * named files and empty file name.  The given buffer should be opened
                    652:  * with File_Open() and closed with File_Close() if this function is used!
1.1.1.11  root      653:  * (On Linux one can use /dev/stdout etc, this is intended for other OSes)
                    654:  */
                    655: void File_MakeAbsoluteSpecialName(char *path)
                    656: {
1.1.1.14  root      657:        if (path[0] &&
                    658:            strcmp(path, "stdin")  != 0 &&
1.1.1.11  root      659:            strcmp(path, "stdout") != 0 &&
                    660:            strcmp(path, "stderr") != 0)
                    661:                File_MakeAbsoluteName(path);
                    662: }
                    663: 
                    664: /*-----------------------------------------------------------------------*/
                    665: /**
                    666:  * Create a clean absolute file name from a (possibly) relative file name.
                    667:  * I.e. filter out all occurancies of "./" and "../".
                    668:  * pFileName needs to point to a buffer of at least FILENAME_MAX bytes.
                    669:  */
1.1.1.6   root      670: void File_MakeAbsoluteName(char *pFileName)
                    671: {
1.1.1.10  root      672:        char *pTempName;
                    673:        int inpos, outpos;
1.1.1.6   root      674: 
1.1.1.11  root      675: #if defined (__AMIGAOS4__)
                    676:        /* This function does not work on Amiga OS */
                    677:        return;
                    678: #endif
                    679: 
1.1.1.10  root      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:                }
1.1.1.11  root      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))
1.1.1.10  root      721:                {
                    722:                        /* Handle "../" */
                    723:                        char *pSlashPos;
1.1.1.11  root      724:                        inpos += 2;
1.1.1.10  root      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:                        }
1.1.1.11  root      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:                        }
1.1.1.10  root      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:        }
1.1.1.6   root      763: 
1.1.1.10  root      764:        pTempName[outpos] = 0;
1.1.1.6   root      765: 
1.1.1.10  root      766:        strcpy(pFileName, pTempName);          /* Copy back */
                    767:        free(pTempName);
1.1.1.8   root      768: }
                    769: 
                    770: 
                    771: /*-----------------------------------------------------------------------*/
1.1.1.11  root      772: /**
                    773:  * Create a valid path name from a possibly invalid name by erasing invalid
1.1.1.16  root      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.
1.1.1.11  root      777:  */
1.1.1.8   root      778: void File_MakeValidPathName(char *pPathName)
                    779: {
1.1.1.10  root      780:        struct stat dirstat;
                    781:        char *pLastSlash;
1.1.1.8   root      782: 
1.1.1.10  root      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:                }
1.1.1.16  root      797:                else
1.1.1.10  root      798:                {
1.1.1.16  root      799:                        if (pPathName[0])
                    800:                        {
                    801:                                /* point to root */
                    802:                                pPathName[0] = PATHSEP;
                    803:                                pPathName[1] = 0;
                    804:                        }
                    805:                        return;
1.1.1.10  root      806:                }
                    807:        }
                    808:        while (pLastSlash);
1.1.1.12  root      809: 
                    810:        /* Make sure that path name ends with a slash */
                    811:        File_AddSlashToEndFileName(pPathName);
1.1.1.6   root      812: }
1.1.1.11  root      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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.