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

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

unix.superglobalmegacorp.com

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