From f1cd4d8f0d94e02e2001e628a0366d1aa113dc8c Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 15 Feb 2010 14:02:36 -0500 Subject: [PATCH] THIS is Christoph's PHYSFS_stat() work. I've merged some basic ideas from the other patch, which was Indy Sam's work, and cleaned up a few things. --- docs/CREDITS.txt | 4 ++ extras/physfs-swig.i | 2 + src/archiver_dir.c | 16 +++++- src/archiver_grp.c | 26 ++++++++- src/archiver_hog.c | 26 ++++++++- src/archiver_lzma.c | 41 +++++++++++++- src/archiver_mvl.c | 26 ++++++++- src/archiver_qpak.c | 37 +++++++++++- src/archiver_wad.c | 26 ++++++++- src/archiver_zip.c | 44 ++++++++++++++- src/physfs.c | 50 +++++++++++++++++ src/physfs.h | 55 +++++++++++++++++- src/physfs_internal.h | 14 +++++ src/platform_os2.c | 82 +++++++++++++++++++++++---- src/platform_pocketpc.c | 99 +++++++++++++++++++++++++++++++- src/platform_posix.c | 46 +++++++++++++++ src/platform_unix.c | 6 +- src/platform_windows.c | 121 ++++++++++++++++++++++++++++++++++++++++ test/test_physfs.c | 46 ++++++++++++++- 19 files changed, 732 insertions(+), 35 deletions(-) diff --git a/docs/CREDITS.txt b/docs/CREDITS.txt index 7e0cc3d..bc069ad 100644 --- a/docs/CREDITS.txt +++ b/docs/CREDITS.txt @@ -105,6 +105,10 @@ OS/2 updates: Bug fixes: Patrice Mandin +PHYSFS_stat() API: + Christoph Nelles + Indy Sams + Other stuff: Your name here! Patches go to icculus@icculus.org ... diff --git a/extras/physfs-swig.i b/extras/physfs-swig.i index 8f92f14..e5d368f 100644 --- a/extras/physfs-swig.i +++ b/extras/physfs-swig.i @@ -83,6 +83,8 @@ %rename(symbolicLinksPermitted) PHYSFS_symbolicLinksPermitted; %rename(mount) PHYSFS_mount; %rename(getMountPoint) PHYSFS_getMountPoint; +%rename(Stat) PHYSFS_Stat; /* !!! FIXME: case insensitive script languages? */ +%rename(stat) PHYSFS_stat; #endif /* SWIGPERL */ %include "../src/physfs.h" diff --git a/src/archiver_dir.c b/src/archiver_dir.c index bb84256..097bea3 100644 --- a/src/archiver_dir.c +++ b/src/archiver_dir.c @@ -243,6 +243,18 @@ static void DIR_dirClose(dvoid *opaque) } /* DIR_dirClose */ +static int DIR_stat(fvoid *opaque, const char *name, int *exists, + PHYSFS_Stat *stat) +{ + char *d = __PHYSFS_platformCvtToDependent((char *) opaque, name, NULL); + int retval = 0; + + BAIL_IF_MACRO(d == NULL, NULL, 0); + retval = __PHYSFS_platformStat(d, exists, stat); + allocator.Free(d); + return retval; +} /* DIR_stat */ + const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_DIR = { @@ -253,7 +265,6 @@ const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_DIR = }; - const PHYSFS_Archiver __PHYSFS_Archiver_DIR = { &__PHYSFS_ArchiveInfo_DIR, @@ -276,7 +287,8 @@ const PHYSFS_Archiver __PHYSFS_Archiver_DIR = DIR_tell, /* tell() method */ DIR_seek, /* seek() method */ DIR_fileLength, /* fileLength() method */ - DIR_fileClose /* fileClose() method */ + DIR_fileClose, /* fileClose() method */ + DIR_stat /* stat() method */ }; /* end of dir.c ... */ diff --git a/src/archiver_grp.c b/src/archiver_grp.c index a47e92e..dff943e 100644 --- a/src/archiver_grp.c +++ b/src/archiver_grp.c @@ -316,7 +316,7 @@ static void GRP_enumerateFiles(dvoid *opaque, const char *dname, } /* GRP_enumerateFiles */ -static GRPentry *grp_find_entry(GRPinfo *info, const char *name) +static GRPentry *grp_find_entry(const GRPinfo *info, const char *name) { char *ptr = strchr(name, '.'); GRPentry *a = info->entries; @@ -435,6 +435,27 @@ static int GRP_mkdir(dvoid *opaque, const char *name) } /* GRP_mkdir */ +static int GRP_stat(fvoid *opaque, const char *filename, int *exists, + PHYSFS_Stat *stat) +{ + const GRPinfo *info = (const GRPinfo *) opaque; + const GRPentry *entry = grp_find_entry(info, filename); + + *exists = (entry != 0); + if (!entry) + return 0; + + stat->filesize = entry->size; + stat->filetype = PHYSFS_FILETYPE_REGULAR; + stat->modtime = info->last_mod_time; + stat->createtime = info->last_mod_time; + stat->accesstime = -1; + stat->readonly = 1; + + return 0; +} /* GRP_stat */ + + const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_GRP = { "GRP", @@ -466,7 +487,8 @@ const PHYSFS_Archiver __PHYSFS_Archiver_GRP = GRP_tell, /* tell() method */ GRP_seek, /* seek() method */ GRP_fileLength, /* fileLength() method */ - GRP_fileClose /* fileClose() method */ + GRP_fileClose, /* fileClose() method */ + GRP_stat /* stat() method */ }; #endif /* defined PHYSFS_SUPPORTS_GRP */ diff --git a/src/archiver_hog.c b/src/archiver_hog.c index 5cea875..def68c2 100644 --- a/src/archiver_hog.c +++ b/src/archiver_hog.c @@ -355,7 +355,7 @@ static void HOG_enumerateFiles(dvoid *opaque, const char *dname, } /* HOG_enumerateFiles */ -static HOGentry *hog_find_entry(HOGinfo *info, const char *name) +static HOGentry *hog_find_entry(const HOGinfo *info, const char *name) { char *ptr = strchr(name, '.'); HOGentry *a = info->entries; @@ -474,6 +474,27 @@ static int HOG_mkdir(dvoid *opaque, const char *name) } /* HOG_mkdir */ +static int HOG_stat(fvoid *opaque, const char *filename, int *exists, + PHYSFS_Stat *stat) +{ + const HOGinfo *info = (const HOGinfo *) opaque; + const HOGentry *entry = hog_find_entry(info, filename); + + *exists = (entry != 0); + if (!entry) + return 0; + + stat->filesize = entry->size; + stat->filetype = PHYSFS_FILETYPE_REGULAR; + stat->modtime = info->last_mod_time; + stat->createtime = info->last_mod_time; + stat->accesstime = -1; + stat->readonly = 1; + + return 0; +} /* HOG_stat */ + + const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_HOG = { "HOG", @@ -505,7 +526,8 @@ const PHYSFS_Archiver __PHYSFS_Archiver_HOG = HOG_tell, /* tell() method */ HOG_seek, /* seek() method */ HOG_fileLength, /* fileLength() method */ - HOG_fileClose /* fileClose() method */ + HOG_fileClose, /* fileClose() method */ + HOG_stat /* stat() method */ }; #endif /* defined PHYSFS_SUPPORTS_HOG */ diff --git a/src/archiver_lzma.c b/src/archiver_lzma.c index cda8285..fcfefbb 100644 --- a/src/archiver_lzma.c +++ b/src/archiver_lzma.c @@ -207,7 +207,7 @@ static void lzma_file_swap(void *_a, PHYSFS_uint32 one, PHYSFS_uint32 two) /* * Find entry 'name' in 'archive' */ -static LZMAfile * lzma_find_file(LZMAarchive *archive, const char *name) +static LZMAfile * lzma_find_file(const LZMAarchive *archive, const char *name) { LZMAfile *file = bsearch(name, archive->files, archive->db.Database.NumFiles, sizeof(*archive->files), lzma_file_cmp_stdlib); /* FIXME: Should become __PHYSFS_search!!! */ @@ -695,6 +695,42 @@ static int LZMA_mkdir(dvoid *opaque, const char *name) BAIL_MACRO(ERR_NOT_SUPPORTED, 0); } /* LZMA_mkdir */ +static int LZMA_stat(fvoid *opaque, const char *filename, int *exists, + PHYSFS_Stat *stat) +{ + const LZMAarchive *archive = (const LZMAarchive *) opaque; + const LZMAfile *file = lzma_find_file(archive, filename); + + *exists = (file != 0); + if (!file) + return 0; + + if(file->item->IsDirectory) + { + stat->filesize = 0; + stat->filetype = PHYSFS_FILETYPE_DIRECTORY; + } /* if */ + else + { + stat->filesize = (PHYSFS_sint64) file->item->Size; + stat->filetype = PHYSFS_FILETYPE_REGULAR; + } /* else */ + + /* !!! FIXME: the 0's should be -1's? */ + if (file->item->IsLastWriteTimeDefined) + stat->modtime = lzma_filetime_to_unix_timestamp(&file->item->LastWriteTime); + else + stat->modtime = 0; + + /* real create and accesstype are currently not in the lzma SDK */ + stat->createtime = stat->modtime; + stat->accesstime = 0; + + stat->readonly = 1; /* 7zips are always read only */ + + return 0; +} /* LZMA_stat */ + const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_LZMA = { @@ -727,7 +763,8 @@ const PHYSFS_Archiver __PHYSFS_Archiver_LZMA = LZMA_tell, /* tell() method */ LZMA_seek, /* seek() method */ LZMA_fileLength, /* fileLength() method */ - LZMA_fileClose /* fileClose() method */ + LZMA_fileClose, /* fileClose() method */ + LZMA_stat /* stat() method */ }; #endif /* defined PHYSFS_SUPPORTS_7Z */ diff --git a/src/archiver_mvl.c b/src/archiver_mvl.c index 18279c9..9584117 100644 --- a/src/archiver_mvl.c +++ b/src/archiver_mvl.c @@ -312,7 +312,7 @@ static void MVL_enumerateFiles(dvoid *opaque, const char *dname, } /* MVL_enumerateFiles */ -static MVLentry *mvl_find_entry(MVLinfo *info, const char *name) +static MVLentry *mvl_find_entry(const MVLinfo *info, const char *name) { char *ptr = strchr(name, '.'); MVLentry *a = info->entries; @@ -431,6 +431,27 @@ static int MVL_mkdir(dvoid *opaque, const char *name) } /* MVL_mkdir */ +static int MVL_stat(fvoid *opaque, const char *filename, int *exists, + PHYSFS_Stat *stat) +{ + const MVLinfo *info = (const MVLinfo *) opaque; + const MVLentry *entry = mvl_find_entry(info, filename); + + *exists = (entry != 0); + if (!entry) + return 0; + + stat->filesize = entry->size; + stat->filetype = PHYSFS_FILETYPE_REGULAR; + stat->modtime = info->last_mod_time; + stat->createtime = info->last_mod_time; + stat->accesstime = 0; + stat->readonly = 1; + + return 0; +} /* MVL_stat */ + + const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_MVL = { "MVL", @@ -462,7 +483,8 @@ const PHYSFS_Archiver __PHYSFS_Archiver_MVL = MVL_tell, /* tell() method */ MVL_seek, /* seek() method */ MVL_fileLength, /* fileLength() method */ - MVL_fileClose /* fileClose() method */ + MVL_fileClose, /* fileClose() method */ + MVL_stat /* stat() method */ }; #endif /* defined PHYSFS_SUPPORTS_MVL */ diff --git a/src/archiver_qpak.c b/src/archiver_qpak.c index 2ce10fd..82b3584 100644 --- a/src/archiver_qpak.c +++ b/src/archiver_qpak.c @@ -445,7 +445,8 @@ static void QPAK_enumerateFiles(dvoid *opaque, const char *dname, * notation. Directories don't have QPAKentries associated with them, but * (*isDir) will be set to non-zero if a dir was hit. */ -static QPAKentry *qpak_find_entry(QPAKinfo *info, const char *path, int *isDir) +static QPAKentry *qpak_find_entry(const QPAKinfo *info, const char *path, + int *isDir) { QPAKentry *a = info->entries; PHYSFS_sint32 pathlen = strlen(path); @@ -590,6 +591,37 @@ static int QPAK_mkdir(dvoid *opaque, const char *name) } /* QPAK_mkdir */ +static int QPAK_stat(fvoid *opaque, const char *filename, int *exists, + PHYSFS_Stat *stat) +{ + int isDir = 0; + const QPAKinfo *info = (const QPAKinfo *) opaque; + const QPAKentry *entry = qpak_find_entry(info, filename, &isDir); + + *exists = ((isDir) || (entry != NULL)); + if (!exists) + return 0; + + if (isDir) + { + stat->filetype = PHYSFS_FILETYPE_DIRECTORY; + stat->filesize = 0; + } /* if */ + else + { + stat->filetype = PHYSFS_FILETYPE_REGULAR; + stat->filesize = entry->size; + } /* else */ + + stat->modtime = info->last_mod_time; + stat->createtime = info->last_mod_time; + stat->accesstime = 0; + stat->readonly = 1; + + return 0; +} /* QPAK_stat */ + + const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_QPAK = { "PAK", @@ -621,7 +653,8 @@ const PHYSFS_Archiver __PHYSFS_Archiver_QPAK = QPAK_tell, /* tell() method */ QPAK_seek, /* seek() method */ QPAK_fileLength, /* fileLength() method */ - QPAK_fileClose /* fileClose() method */ + QPAK_fileClose, /* fileClose() method */ + QPAK_stat /* stat() method */ }; #endif /* defined PHYSFS_SUPPORTS_QPAK */ diff --git a/src/archiver_wad.c b/src/archiver_wad.c index c0e49da..68f58d1 100644 --- a/src/archiver_wad.c +++ b/src/archiver_wad.c @@ -361,7 +361,7 @@ static void WAD_enumerateFiles(dvoid *opaque, const char *dname, } /* WAD_enumerateFiles */ -static WADentry *wad_find_entry(WADinfo *info, const char *name) +static WADentry *wad_find_entry(const WADinfo *info, const char *name) { WADentry *a = info->entries; PHYSFS_sint32 lo = 0; @@ -494,6 +494,27 @@ static int WAD_mkdir(dvoid *opaque, const char *name) } /* WAD_mkdir */ +static int WAD_stat(fvoid *opaque, const char *filename, int *exists, + PHYSFS_Stat *stat) +{ + const WADinfo *info = (const WADinfo *) opaque; + const WADentry *entry = wad_find_entry(info, filename); + + *exists = (entry != 0); + if (!entry) + return 0; + + stat->filesize = entry->size; + stat->filetype = PHYSFS_FILETYPE_REGULAR; + stat->accesstime = 0; + stat->modtime = ((WADinfo *) opaque)->last_mod_time; + stat->createtime = ((WADinfo *) opaque)->last_mod_time; + stat->readonly = 1; /* WADs are always readonly */ + + return 0; +} /* WAD_stat */ + + const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_WAD = { "WAD", @@ -525,7 +546,8 @@ const PHYSFS_Archiver __PHYSFS_Archiver_WAD = WAD_tell, /* tell() method */ WAD_seek, /* seek() method */ WAD_fileLength, /* fileLength() method */ - WAD_fileClose /* fileClose() method */ + WAD_fileClose, /* fileClose() method */ + WAD_stat /* stat() method */ }; #endif /* defined PHYSFS_SUPPORTS_WAD */ diff --git a/src/archiver_zip.c b/src/archiver_zip.c index 6023d1b..e9f7811 100644 --- a/src/archiver_zip.c +++ b/src/archiver_zip.c @@ -509,7 +509,8 @@ static void zip_free_entries(ZIPentry *entries, PHYSFS_uint32 max) * notation. Directories don't have ZIPentries associated with them, but * (*isDir) will be set to non-zero if a dir was hit. */ -static ZIPentry *zip_find_entry(ZIPinfo *info, const char *path, int *isDir) +static ZIPentry *zip_find_entry(const ZIPinfo *info, const char *path, + int *isDir) { ZIPentry *a = info->entries; PHYSFS_sint32 pathlen = strlen(path); @@ -1406,6 +1407,44 @@ static int ZIP_mkdir(dvoid *opaque, const char *name) } /* ZIP_mkdir */ +static int ZIP_stat(fvoid *opaque, const char *filename, int *exists, + PHYSFS_Stat *stat) +{ + int isDir = 0; + const ZIPinfo *info = (const ZIPinfo *) opaque; + const ZIPentry *entry = zip_find_entry(info, filename, &isDir); + + *exists = isDir || (entry != 0); + if (!*exists) + return 0; + + if (isDir) + { + stat->filesize = 0; + stat->filetype = PHYSFS_FILETYPE_DIRECTORY; + } /* if */ + + else if (zip_entry_is_symlink(entry)) + { + stat->filesize = 0; + stat->filetype = PHYSFS_FILETYPE_SYMLINK; + } /* else if */ + + else + { + stat->filesize = entry->uncompressed_size; + stat->filetype = PHYSFS_FILETYPE_REGULAR; + } /* else */ + + stat->modtime = ((entry) ? entry->last_mod_time : 0); + stat->createtime = stat->modtime; + stat->accesstime = 0; + stat->readonly = 1; /* .zip files are always read only */ + + return 0; +} /* ZIP_stat */ + + const PHYSFS_ArchiveInfo __PHYSFS_ArchiveInfo_ZIP = { "ZIP", @@ -1437,7 +1476,8 @@ const PHYSFS_Archiver __PHYSFS_Archiver_ZIP = ZIP_tell, /* tell() method */ ZIP_seek, /* seek() method */ ZIP_fileLength, /* fileLength() method */ - ZIP_fileClose /* fileClose() method */ + ZIP_fileClose, /* fileClose() method */ + ZIP_stat /* stat() method */ }; #endif /* defined PHYSFS_SUPPORTS_ZIP */ diff --git a/src/physfs.c b/src/physfs.c index 74fd464..c39020a 100644 --- a/src/physfs.c +++ b/src/physfs.c @@ -2144,6 +2144,56 @@ int PHYSFS_flush(PHYSFS_File *handle) } /* PHYSFS_flush */ +int PHYSFS_stat(const char *_fname, PHYSFS_Stat *stat) +{ + int retval = 0; + char *fname; + size_t len; + + BAIL_IF_MACRO(_fname == NULL, ERR_INVALID_ARGUMENT, -1); + BAIL_IF_MACRO(stat == NULL, ERR_INVALID_ARGUMENT, -1); + len = strlen(_fname) + 1; + fname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF_MACRO(fname == NULL, ERR_OUT_OF_MEMORY, -1); + + /* !!! FIXME: what should this be set to if we fail completely? */ + memset(stat, '\0', sizeof (PHYSFS_Stat)); + + if (sanitizePlatformIndependentPath(_fname, fname)) + { + if (*fname == '\0') + { + stat->filetype = PHYSFS_FILETYPE_DIRECTORY; + stat->readonly = !writeDir; /* Writeable if we have a writeDir */ + retval = 0; + } /* if */ + else + { + DirHandle *i; + int exists = 0; + __PHYSFS_platformGrabMutex(stateLock); + for (i = searchPath; ((i != NULL) && (!exists)); i = i->next) + { + char *arcfname = fname; + exists = partOfMountPoint(i, arcfname); + if (exists) + retval = 1; /* !!! FIXME: What's the right value? */ + else if (verifyPath(i, &arcfname, 0)) + { + stat->readonly = !(writeDir && + (strcmp(writeDir->dirName, i->dirName) == 0)); + retval = i->funcs->stat(i->opaque, arcfname, &exists, stat); + } /* else if */ + } /* for */ + __PHYSFS_platformReleaseMutex(stateLock); + } /* else */ + } /* if */ + + __PHYSFS_smallFree(fname); + return retval; +} /* PHYSFS_stat */ + + int PHYSFS_setAllocator(const PHYSFS_Allocator *a) { BAIL_IF_MACRO(initialized, ERR_IS_INITIALIZED, 0); diff --git a/src/physfs.h b/src/physfs.h index 015d7ec..794b149 100644 --- a/src/physfs.h +++ b/src/physfs.h @@ -417,7 +417,6 @@ typedef struct PHYSFS_Version } PHYSFS_Version; - #ifndef SWIG /* not available from scripting languages. */ #ifndef DOXYGEN_SHOULD_IGNORE_THIS @@ -2462,6 +2461,60 @@ PHYSFS_DECL const PHYSFS_Allocator *PHYSFS_getAllocator(void); #endif /* SWIG */ +/** + * \enum PHYSFS_FileType + * \brief Type of a File + * + * Possible types of a file. + * + * \sa PHYSFS_stat + */ +typedef enum PHYSFS_FileType +{ + PHYSFS_FILETYPE_REGULAR, /**< a normal file */ + PHYSFS_FILETYPE_DIRECTORY, /**< a directory */ + PHYSFS_FILETYPE_SYMLINK, /**< a symlink */ + PHYSFS_FILETYPE_OTHER /**< something completely different like a device */ +} PHYSFS_FileType; + +/** + * \struct PHYSFS_Stat + * \brief Meta data for a file or directory + * + * Container for various meta data about a file in the virtual file system. + * PHYSFS_stat() uses this structure for returning the information. The time + * data will be either a real timestamp or -1 if there is none. So every value + * is at least epoch. The FileSize is only valid for real files. And the + * readonly tells you whether when you open a file for writing you are writing + * to the same file as if you were opening it, given you have enough + * filesystem rights to do that. + * + * \sa PHYSFS_stat + * \sa PHYSFS_FileType + */ +typedef struct PHYSFS_Stat +{ + PHYSFS_sint64 filesize; /**< size in bytes, -1 for non-files and unknown */ + PHYSFS_sint64 modtime; /**< same value as PHYSFS_getLastModTime() */ + PHYSFS_sint64 createtime; /**< like modtime, but for file creation time */ + PHYSFS_sint64 accesstime; /**< like modtime, but for file access time */ + PHYSFS_FileType filetype; /**< File? Directory? Symlink? */ + int readonly; /**< non-zero if read only, zero if writable. */ +} PHYSFS_Stat; + +/** + * \fn int PHYSFS_stat(const char *fname, PHYSFS_Stat *stat) + * \brief Get various information about a directory or a file. + * + * Obtain various information about a file or directory from the meta data. + * + * \param fname filename to check, in platform-indepedent notation. + * \param stat pointer to structure to fill in with data about (fname). + * \return 0 on success, non-zero on error. + * + * \sa PHYSFS_Stat + */ +PHYSFS_DECL int PHYSFS_stat(const char *fname, PHYSFS_Stat *stat); /* Everything above this line is part of the PhysicsFS 2.1 API. */ diff --git a/src/physfs_internal.h b/src/physfs_internal.h index 898793d..f1a3119 100644 --- a/src/physfs_internal.h +++ b/src/physfs_internal.h @@ -937,6 +937,13 @@ typedef struct * file. On failure, call __PHYSFS_setError(). */ int (*fileClose)(fvoid *opaque); + + /* !!! FIXME: return info (may be|is) wrong. + * Obtain basic file metadata. + * Returns non-zero on success, zero if can't close + * file. On failure, call __PHYSFS_setError(). + */ + int (*stat)(fvoid *opaque, const char *fn, int *exists, PHYSFS_Stat *stat); } PHYSFS_Archiver; @@ -1244,6 +1251,13 @@ PHYSFS_sint64 __PHYSFS_platformTell(void *opaque); */ PHYSFS_sint64 __PHYSFS_platformFileLength(void *handle); + +/* + * !!! FIXME: comment me. + */ +int __PHYSFS_platformStat(const char *fn, int *exists, PHYSFS_Stat *stat); + + /* * Determine if a file is at EOF. (opaque) should be cast to whatever data * type your platform uses. diff --git a/src/platform_os2.c b/src/platform_os2.c index affc018..2262738 100644 --- a/src/platform_os2.c +++ b/src/platform_os2.c @@ -631,6 +631,25 @@ int __PHYSFS_platformDelete(const char *_path) } /* __PHYSFS_platformDelete */ +/* Convert to a format PhysicsFS can grok... */ +PHYSFS_sint64 os2TimeToUnixTime(const FDATE *date, const FTIME *time) +{ + struct tm tm; + + tm.tm_sec = ((PHYSFS_uint32) time->.twosecs) * 2; + tm.tm_min = time->minutes; + tm.tm_hour = time->hours; + tm.tm_mday = date->day; + tm.tm_mon = date->month; + tm.tm_year = ((PHYSFS_uint32) date->year) + 80; + tm.tm_wday = -1 /*st_localtz.wDayOfWeek*/; + tm.tm_yday = -1; + tm.tm_isdst = -1; + + return (PHYSFS_sint64) mktime(&tm); +} /* os2TimeToUnixTime */ + + PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *_fname) { const unsigned char *fname = (const unsigned char *) _fname; @@ -640,24 +659,63 @@ PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *_fname) APIRET rc = DosQueryPathInfo(fname, FIL_STANDARD, &fs, sizeof (fs)); BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, -1); - /* Convert to a format that mktime() can grok... */ - tm.tm_sec = ((PHYSFS_uint32) fs.ftimeLastWrite.twosecs) * 2; - tm.tm_min = fs.ftimeLastWrite.minutes; - tm.tm_hour = fs.ftimeLastWrite.hours; - tm.tm_mday = fs.fdateLastWrite.day; - tm.tm_mon = fs.fdateLastWrite.month; - tm.tm_year = ((PHYSFS_uint32) fs.fdateLastWrite.year) + 80; - tm.tm_wday = -1 /*st_localtz.wDayOfWeek*/; - tm.tm_yday = -1; - tm.tm_isdst = -1; - /* Convert to a format PhysicsFS can grok... */ - retval = (PHYSFS_sint64) mktime(&tm); + retval = os2TimeToUnixTime(&fs.fdateLastWrite, &fs.ftimeLastWrite); + BAIL_IF_MACRO(retval == -1, strerror(errno), -1); return retval; } /* __PHYSFS_platformGetLastModTime */ +static int __PHYSFS_platformStat(const char *_fname, int *exists, + PHYSFS_Stat *stat) +{ + struct tm tm; + FILESTATUS3 fs; + const unsigned char *fname = (const unsigned char *) _fname; + const APIRET rc = DosQueryPathInfo(fname, FIL_STANDARD, &fs, sizeof (fs)); + + if (rc != NO_ERROR) + { + if (rc == ERROR_PATH_NOT_FOUND) + { + *exists = 0; + return 0; + } /* if */ + BAIL_MACRO(get_os2_error_string(rc), -1); + } /* if */ + + *exists = 1; + + if (fs.attrFile & FILE_DIRECTORY) + { + stat->filetype = PHYSFS_FILETYPE_DIRECTORY; + stat->filesize = 0; + } /* if */ + else + { + stat->filetype = PHYSFS_FILETYPE_REGULAR; + stat->filesize = fs.cbFile; + } /* else */ + + stat->modtime = os2TimeToUnixTime(&fs.fdateLastWrite, &fs.ftimeLastWrite); + if (stat->modtime < 0) + stat->modtime = 0; + + stat->accesstime = os2TimeToUnixTime(&fs.fdateLastAccess, &fs.ftimeLastAccess); + if (stat->accesstime < 0) + stat->accesstime = 0; + + stat->createtime = os2TimeToUnixTime(&fs.fdateCreation, &fs.ftimeCreation); + if (stat->createtime < 0) + stat->createtime = 0; + + stat->readonly = ((fs.attrFile & FILE_READONLY) == FILE_READONLY); + + return 0; +} /* __PHYSFS_platformStat */ + + void *__PHYSFS_platformGetThreadID(void) { PTIB ptib; diff --git a/src/platform_pocketpc.c b/src/platform_pocketpc.c index 32c7f1e..d0ed676 100644 --- a/src/platform_pocketpc.c +++ b/src/platform_pocketpc.c @@ -561,6 +561,103 @@ int __PHYSFS_platformDelete(const char *path) } /* __PHYSFS_platformDelete */ +/* Shamelessly copied from platform_windows.c */ +static PHYSFS_sint64 FileTimeToPhysfsTime(const FILETIME *ft) +{ + SYSTEMTIME st_utc; + SYSTEMTIME st_localtz; + TIME_ZONE_INFORMATION tzi; + DWORD tzid; + PHYSFS_sint64 retval; + struct tm tm; + + BAIL_IF_MACRO(!FileTimeToSystemTime(ft, &st_utc), winApiStrError(), -1); + tzid = GetTimeZoneInformation(&tzi); + BAIL_IF_MACRO(tzid == TIME_ZONE_ID_INVALID, winApiStrError(), -1); + + /* (This API is unsupported and fails on non-NT systems. */ + if (!SystemTimeToTzSpecificLocalTime(&tzi, &st_utc, &st_localtz)) + { + /* do it by hand. Grumble... */ + ULARGE_INTEGER ui64; + FILETIME new_ft; + ui64.LowPart = ft->dwLowDateTime; + ui64.HighPart = ft->dwHighDateTime; + + if (tzid == TIME_ZONE_ID_STANDARD) + tzi.Bias += tzi.StandardBias; + else if (tzid == TIME_ZONE_ID_DAYLIGHT) + tzi.Bias += tzi.DaylightBias; + + /* convert from minutes to 100-nanosecond increments... */ + ui64.QuadPart -= (((LONGLONG) tzi.Bias) * (600000000)); + + /* Move it back into a FILETIME structure... */ + new_ft.dwLowDateTime = ui64.LowPart; + new_ft.dwHighDateTime = ui64.HighPart; + + /* Convert to something human-readable... */ + if (!FileTimeToSystemTime(&new_ft, &st_localtz)) + BAIL_MACRO(winApiStrError(), -1); + } /* if */ + + /* Convert to a format that mktime() can grok... */ + tm.tm_sec = st_localtz.wSecond; + tm.tm_min = st_localtz.wMinute; + tm.tm_hour = st_localtz.wHour; + tm.tm_mday = st_localtz.wDay; + tm.tm_mon = st_localtz.wMonth - 1; + tm.tm_year = st_localtz.wYear - 1900; + tm.tm_wday = -1 /*st_localtz.wDayOfWeek*/; + tm.tm_yday = -1; + tm.tm_isdst = -1; + + /* Convert to a format PhysicsFS can grok... */ + retval = (PHYSFS_sint64) mktime(&tm); + BAIL_IF_MACRO(retval == -1, strerror(errno), -1); + return retval; +} /* FileTimeToPhysfsTime */ + + +int __PHYSFS_platformStat(const char *filename, int *exists, PHYSFS_Stat *stat) +{ + WIN32_FIND_DATA winstat; + const HANDLE searchhandle = FindFirstFile(filename, &winstat); + + if (searchhandle == INVALID_HANDLE_VALUE) /* call failed? */ + { + /* !!! FIXME: FindFirstFile() doesn't set errno. Use GetLastError()?. */ + if (errno == ERROR_FILE_NOT_FOUND) + { + *exists = 0; + return 0; + } /* if */ + BAIL_MACRO(win32strerror, -1); + } /* if */ + + FindClose(searchhandle); /* close handle, not needed anymore */ + + *exists = 1; + + if(winstat.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + stat->filetype = PHYSFS_FILETYPE_DIRECTORY; + else if (winstat.dwFileAttributes & (FILE_ATTRIBUTE_OFFLINE | FILE_ATTRIBUTE_ROMMODULE)) + stat->filetype = PHYSFS_FILETYPE_OTHER; + else + stat->filetype = PHYSFS_FILETYPE_OTHER; /* !!! FIXME: _REGULAR? */ + + if (stat->filetype == PHYSFS_FILETYPE_REGULAR) + stat->filesize = (((PHYSFS_uint64) winstat.nFileSizeHigh) << 32) | winstat.nFileSizeLow; + + stat->modtime = FileTimeToPhysfsTime(&winstat.ftLastWriteTime); + stat->accesstime = FileTimeToPhysfsTime(&winstat.ftLastAccessTime); + stat->createtime = FileTimeToPhysfsTime(&winstat.ftCreationTime); + stat->readonly = ((winstat.dwFileAttributes & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_INROM)) != 0); + + return 0; +} /* __PHYSFS_platformStat */ + + /* * !!! FIXME: why aren't we using Critical Sections instead of Mutexes? * !!! FIXME: mutexes on Windows are for cross-process sync. CritSects are @@ -568,7 +665,7 @@ int __PHYSFS_platformDelete(const char *path) */ void *__PHYSFS_platformCreateMutex(void) { - return (void * CreateMutex(NULL, FALSE, NULL)); + return ((void *) CreateMutex(NULL, FALSE, NULL)); } /* __PHYSFS_platformCreateMutex */ diff --git a/src/platform_posix.c b/src/platform_posix.c index 9d5754d..15c8be6 100644 --- a/src/platform_posix.c +++ b/src/platform_posix.c @@ -405,6 +405,52 @@ PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname) return statbuf.st_mtime; } /* __PHYSFS_platformGetLastModTime */ + +int __PHYSFS_platformStat(const char *filename, int *exists, PHYSFS_Stat *st) +{ + struct stat statbuf; + + /* !!! FIXME: lstat()? */ + if (stat(filename, &statbuf)) + { + if (errno == ENOENT) + { + *exists = 0; + return 0; + } /* if */ + else + { + BAIL_MACRO(strerror(errno), -1); + } /* else */ + } /* if */ + + if (S_ISREG(statbuf.st_mode)) + { + st->filetype = PHYSFS_FILETYPE_REGULAR; + st->filesize = statbuf.st_size; + } /* if */ + + else if(S_ISDIR(statbuf.st_mode)) + { + st->filetype = PHYSFS_FILETYPE_DIRECTORY; + st->filesize = 0; + } /* else if */ + + else + { + st->filetype = PHYSFS_FILETYPE_OTHER; + st->filesize = statbuf.st_size; + } /* else */ + + st->modtime = statbuf.st_mtime; + st->createtime = statbuf.st_ctime; + st->accesstime = statbuf.st_atime; + + /* !!! FIXME: maybe we should just report full permissions? */ + st->readonly = access(filename, W_OK); + return 0; +} /* __PHYSFS_platformStat */ + #endif /* PHYSFS_PLATFORM_POSIX */ /* end of posix.c ... */ diff --git a/src/platform_unix.c b/src/platform_unix.c index 29dda6f..418968e 100644 --- a/src/platform_unix.c +++ b/src/platform_unix.c @@ -24,7 +24,7 @@ #include #include -#if (!defined PHYSFS_NO_THREAD_SUPPORT) +#if (!defined PHYSFS_NO_PTHREADS_SUPPORT) #include #endif @@ -345,7 +345,7 @@ int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a) } /* __PHYSFS_platformSetDefaultAllocator */ -#if (defined PHYSFS_NO_THREAD_SUPPORT) +#if (defined PHYSFS_NO_PTHREADS_SUPPORT) void *__PHYSFS_platformGetThreadID(void) { return ((void *) 0x0001); } void *__PHYSFS_platformCreateMutex(void) { return ((void *) 0x0001); } @@ -429,7 +429,7 @@ void __PHYSFS_platformReleaseMutex(void *mutex) } /* if */ } /* __PHYSFS_platformReleaseMutex */ -#endif /* !PHYSFS_NO_THREAD_SUPPORT */ +#endif /* !PHYSFS_NO_PTHREADS_SUPPORT */ #endif /* PHYSFS_PLATFORM_UNIX */ diff --git a/src/platform_windows.c b/src/platform_windows.c index e669618..096a730 100644 --- a/src/platform_windows.c +++ b/src/platform_windows.c @@ -1390,6 +1390,127 @@ PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname) } /* __PHYSFS_platformGetLastModTime */ +static int __PHYSFS_platformStatOldWay(const char *filename, int *exists, + PHYSFS_Stat *stat) +{ + WIN32_FIND_DATA winstat; + const HANDLE searchhandle = FindFirstFile(filename, &winstat); + + if (searchhandle == INVALID_HANDLE_VALUE) /* call failed? */ + { + /* !!! FIXME: not errno...try GetLastError() */ + if (errno == ERROR_FILE_NOT_FOUND) + { + *exists = 0; + return 0; + } /* if */ + BAIL_MACRO(strerror(errno), -1); + } /* if */ + + FindClose(searchhandle); /* close handle, not needed anymore */ + + *exists = 1; + + if (winstat.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + stat->filetype = PHYSFS_FILETYPE_DIRECTORY; + else if (winstat.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) + stat->filetype = PHYSFS_FILETYPE_OTHER; + else + stat->filetype = PHYSFS_FILETYPE_OTHER; /* !!! FIXME: _REGULAR? */ + + if (stat->filetype == PHYSFS_FILETYPE_REGULAR) + stat->filesize = (((PHYSFS_uint64) winstat.nFileSizeHigh) << 32) | winstat.nFileSizeLow; + + stat->modtime = FileTimeToPhysfsTime(&winstat.ftLastWriteTime); + stat->accesstime = FileTimeToPhysfsTime(&winstat.ftLastAccessTime); + stat->createtime = FileTimeToPhysfsTime(&winstat.ftCreationTime); + stat->readonly = ((winstat.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0); + + return 0; +} /* __PHYSFS_platformStatOldWay */ + + +static int __PHYSFS_platformStatNewWay(const char *filename, int *exists, + PHYSFS_Stat *stat) +{ + WIN32_FILE_ATTRIBUTE_DATA winstat; + WCHAR *wstr = NULL; + BOOL rc = 0; + + UTF8_TO_UNICODE_STACK_MACRO(wstr, filename); + if (!wstr) /* maybe better luck in the old way... */ + return __PHYSFS_platformStatOldWay(filename, exists, stat); + + if (pGetFileAttributesExW) + rc = pGetFileAttributesExW(wstr, GetFileExInfoStandard, &winstat); + else + { + const int len = (int) (wStrLen(wstr) + 1); + char *cp = (char *) __PHYSFS_smallAlloc(len); + if (cp) + { + WideCharToMultiByte(CP_ACP, 0, wstr, len, cp, len, 0, 0); + rc = pGetFileAttributesExA(cp, GetFileExInfoStandard, &winstat); + } /* if */ + } /* else */ + + __PHYSFS_smallFree(wstr); + + if (!rc) + { + if (errno == ERROR_FILE_NOT_FOUND) /* !!! FIXME: errno is wrong */ + { + *exists = 0; + return 0; + } /* if */ + else + { + BAIL_MACRO(strerror(errno), -1); + } /* else */ + } /* if */ + + *exists = 1; + + stat->modtime = FileTimeToPhysfsTime(&winstat.ftLastWriteTime); + stat->accesstime = FileTimeToPhysfsTime(&winstat.ftLastAccessTime); + stat->createtime = FileTimeToPhysfsTime(&winstat.ftCreationTime); + + if(winstat.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + stat->filetype = PHYSFS_FILETYPE_DIRECTORY; + stat->filesize = 0; + } /* if */ + + else if(winstat.dwFileAttributes & (FILE_ATTRIBUTE_OFFLINE | FILE_ATTRIBUTE_DEVICE)) + { + /* !!! FIXME: what are reparse points? */ + stat->filetype = PHYSFS_FILETYPE_OTHER; + /* !!! FIXME: don't rely on this */ + stat->filesize = 0; + } /* else if */ + + /* !!! FIXME: check for symlinks on Vista. */ + + else + { + stat->filetype = PHYSFS_FILETYPE_REGULAR; + filesize = (((PHYSFS_uint64) winstat.nFileSizeHigh) << 32) | winstat.nFileSizeLow; + } /* else */ + + stat->readonly = ((winstat.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0); + + return 0; +} /* __PHYSFS_platformStatNewWay */ + + +int __PHYSFS_platformStat(const char *filename, int *exists, PHYSFS_Stat *stat) +{ + if (pGetFileAttributesExW || pGetFileAttributesExA) + return __PHYSFS_platformStatNewWay(filename, exists, stat); + return __PHYSFS_platformStatOldWay(filename, exists, stat); +} /* __PHYSFS_platformStat */ + + /* !!! FIXME: Don't use C runtime for allocators? */ int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a) { diff --git a/test/test_physfs.c b/test/test_physfs.c index a19de06..e8e8742 100644 --- a/test/test_physfs.c +++ b/test/test_physfs.c @@ -773,7 +773,6 @@ static int cmd_filelength(char *args) return 1; } /* cmd_filelength */ - #define WRITESTR "The cat sat on the mat.\n\n" static int cmd_append(char *args) @@ -872,12 +871,13 @@ static int cmd_write(char *args) } /* cmd_write */ -static void modTimeToStr(PHYSFS_sint64 modtime, char *modstr, size_t strsize) +static char* modTimeToStr(PHYSFS_sint64 modtime, char *modstr, size_t strsize) { time_t t = (time_t) modtime; char *str = ctime(&t); strncpy(modstr, str, strsize); modstr[strsize-1] = '\0'; + return modstr; } /* modTimeToStr */ @@ -896,6 +896,44 @@ static int cmd_getlastmodtime(char *args) return 1; } /* cmd_getLastModTime */ +static int cmd_stat(char *args) +{ + PHYSFS_Stat stat; + char timestring[65]; + + if (*args == '\"') + { + args++; + args[strlen(args) - 1] = '\0'; + } /* if */ + + if(PHYSFS_stat(args, &stat)) + { + printf("failed to stat. Reason [%s].\n", PHYSFS_getLastError()); + return 1; + } /* if */ + + printf("Filename: %s\n", args); + printf("Size %d\n",(int) stat.filesize); + + if(stat.filetype == PHYSFS_FILETYPE_REGULAR) + printf("Type: File\n"); + else if(stat.filetype == PHYSFS_FILETYPE_DIRECTORY) + printf("Type: Directory\n"); + else if(stat.filetype == PHYSFS_FILETYPE_SYMLINK) + printf("Type: Symlink\n"); + else + printf("Type: Unknown\n"); + + printf("Created at: %s", modTimeToStr(stat.createtime, timestring, 64)); + printf("Last modified at: %s", modTimeToStr(stat.modtime, timestring, 64)); + printf("Last accessed at: %s", modTimeToStr(stat.accesstime, timestring, 64)); + printf("Readonly: %s\n", stat.readonly ? "true" : "false"); + + return 1; +} /* cmd_filelength */ + + /* must have spaces trimmed prior to this call. */ static int count_args(const char *str) @@ -959,6 +997,7 @@ static const command_info commands[] = { "issymlink", cmd_issymlink, 1, "" }, { "cat", cmd_cat, 1, "" }, { "filelength", cmd_filelength, 1, "" }, + { "stat", cmd_stat, 1, "" }, { "append", cmd_append, 1, "" }, { "write", cmd_write, 1, "" }, { "getlastmodtime", cmd_getlastmodtime, 1, "" }, @@ -1166,6 +1205,7 @@ int main(int argc, char **argv) open_history_file(); printf("Enter commands. Enter \"help\" for instructions.\n"); + fflush(stdout); do { @@ -1176,6 +1216,7 @@ int main(int argc, char **argv) buf = (char *) malloc(512); memset(buf, '\0', 512); printf("> "); + fflush(stdout); for (i = 0; i < 511; i++) { int ch = fgetc(stdin); @@ -1202,6 +1243,7 @@ int main(int argc, char **argv) #endif rc = process_command(buf); + fflush(stdout); if (buf != NULL) free(buf); } while (rc);