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

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

unix.superglobalmegacorp.com

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