Tons of updates. Mostly implemented. Mostly compiling.

This commit is contained in:
Ryan C. Gordon 2001-07-07 03:52:43 +00:00
parent 7be11ab27d
commit a197f30eef
4 changed files with 535 additions and 253 deletions

525
physfs.c
View File

@ -25,16 +25,16 @@ typedef struct __PHYSFS_ERRMSGTYPE__
struct __PHYSFS_ERRMSGTYPE__ *next;
} ErrMsg;
typedef struct __PHYSFS_SEARCHDIRINFO__
typedef struct __PHYSFS_DIRINFO__
{
char *dirName;
DirReader *reader;
struct __PHYSFS_SEARCHDIRINFO__ *next;
} SearchDirInfo;
DirHandle *dirHandle;
struct __PHYSFS_DIRINFO__ *next;
} DirInfo;
typedef struct __PHYSFS_FILEHANDLELIST__
{
FileHandle *handle;
PHYSFS_file *handle;
struct __PHYSFS_FILEHANDLELIST__ *next;
} FileHandleList;
@ -73,12 +73,12 @@ static const DirFunctions *dirFunctions[] =
static int initialized = 0;
static ErrMsg *errorMessages = NULL;
static SearchDirInfo *searchPath = NULL;
static DirInfo *searchPath = NULL;
static DirInfo *writeDir = NULL;
static FileHandleList *openWriteList = NULL;
static FileHandleList *openReadList = NULL;
static char *baseDir = NULL;
static char *userDir = NULL;
static char *writeDir = NULL;
static int allowSymLinks = 0;
@ -107,7 +107,12 @@ static ErrMsg *findErrorForCurrentThread(void)
void __PHYSFS_setError(const char *str)
{
ErrMsg *err = findErrorForCurrentThread();
ErrMsg *err;
if (str == NULL)
return;
err = findErrorForCurrentThread();
if (err == NULL)
{
@ -126,6 +131,19 @@ void __PHYSFS_setError(const char *str)
} /* __PHYSFS_setError */
static void freeErrorMessages(void)
{
ErrMsg *i;
ErrMsg *next;
for (i = errorMessages; i != NULL; i = next)
{
next = i;
free(i);
} /* for */
} /* freeErrorMessages */
const char *PHYSFS_getLastError(void)
{
ErrMsg *err = findErrorForCurrentThread();
@ -149,22 +167,88 @@ void PHYSFS_getLinkedVersion(PHYSFS_Version *ver)
} /* PHYSFS_getLinkedVersion */
static const char *calculateUserDir(void)
static DirHandle *openDirectory(const char *d, int forWriting)
{
const DirFunctions **i;
for (i = dirFunctions; *i != NULL; i++)
{
if ((*i)->isArchive(d, forWriting))
return( (*i)->openArchive(d, forWriting) );
} /* for */
__PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
return(NULL);
} /* openDirectory */
static DirInfo *buildDirInfo(const char *newDir, int forWriting)
{
DirHandle *dirHandle = NULL;
DirInfo *di = NULL;
BAIL_IF_MACRO(newDir == NULL, ERR_INVALID_ARGUMENT, 0);
dirHandle = openDirectory(newDir, forWriting);
BAIL_IF_MACRO(dirHandle == NULL, NULL, 0);
di = (DirInfo *) malloc(sizeof (DirInfo));
if (di == NULL)
dirHandle->funcs->close(dirHandle);
BAIL_IF_MACRO(di == NULL, ERR_OUT_OF_MEMORY, 0);
di->dirName = (char *) malloc(strlen(newDir) + 1);
if (di->dirName == NULL)
{
free(di);
dirHandle->funcs->close(dirHandle);
__PHYSFS_setError(ERR_OUT_OF_MEMORY);
return(0);
} /* if */
di->next = NULL;
di->dirHandle = dirHandle;
strcpy(di->dirName, newDir);
return(di);
} /* buildDirInfo */
static int freeDirInfo(DirInfo *di, FileHandleList *openList)
{
FileHandleList *i;
if (di == NULL)
return(1);
for (i = openList; i != NULL; i = i->next)
{
const DirHandle *h = ((FileHandle *) i->handle->opaque)->dirHandle;
BAIL_IF_MACRO(h == di->dirHandle, ERR_FILES_STILL_OPEN, 0);
} /* for */
di->dirHandle->funcs->close(di->dirHandle);
free(di->dirName);
free(di);
return(1);
} /* freeDirInfo */
static char *calculateUserDir(void)
{
char *retval = NULL;
const char *str = NULL;
str = __PHYSFS_platformGetUserDir();
if (str != NULL)
retval = str;
retval = (char *) str;
else
{
const char *dirsep = PHYSFS_getDirSeparator();
const char *uname = __PHYSFS_platformGetUserName();
str = (uname != NULL) ? uname : "default";
retval = malloc(strlen(baseDir) + strlen(str) +
(strlen(dirsep) * 2) + 6);
retval = (char *) malloc(strlen(baseDir) + strlen(str) +
(strlen(dirsep) * 2) + 6);
if (retval == NULL)
__PHYSFS_setError(ERR_OUT_OF_MEMORY);
@ -172,21 +256,26 @@ static const char *calculateUserDir(void)
sprintf(retval, "%s%susers%s%s", baseDir, dirsep, dirsep, str);
if (uname != NULL)
free(uname);
free((void *) uname);
} /* else */
return(retval);
} /* calculateUserDir */
static char *calculateBaseDir(const char *argv0)
{
assert(0); return(NULL);
} /* calculateBaseDir */
int PHYSFS_init(const char *argv0)
{
BAIL_IF_MACRO(initialized, ERR_IS_INITIALIZED, 0);
BAIL_IF_MACRO(argv0 == NULL, ERR_INVALID_ARGUMENT, 0);
baseDir = calculateBaseDir(argv0);
if (baseDir == NULL)
return(0);
BAIL_IF_MACRO(baseDir == NULL, NULL, 0);
userDir = calculateUserDir();
if (userDir == NULL)
@ -201,42 +290,35 @@ int PHYSFS_init(const char *argv0)
} /* PHYSFS_init */
static void freeSearchDir(SearchDirInfo *sdi)
{
FileHandleList *i;
assert(sdi != NULL);
for (i = openReadList; i != NULL; i = i->next)
{
BAIL_IF_MACRO(i->handle->dirReader == sdi->reader,
ERR_FILES_OPEN_READ, 0);
} /* for */
sdi->reader->close(sdi->reader);
free(sdi->dirName);
free(sdi);
} /* freeSearchDir */
static void closeFileHandleList(FileHandleList **list)
static int closeFileHandleList(FileHandleList **list)
{
FileHandleList *i;
FileHandleList *next = NULL;
FileHandle *h;
for (i = *list; i != NULL; i = next)
{
next = i->next;
i->handle->close(i->handle);
h = (FileHandle *) (i->handle->opaque);
if (!h->funcs->close(i->handle->opaque))
{
*list = i;
return(0);
} /* if */
free(i->handle);
free(i);
} /* for */
*list = NULL;
} /* closeAllFiles */
return(1);
} /* closeFileHandleList */
static void freeSearchPath(void)
{
SearchDirInfo *i;
SearchDirInfo *next = NULL;
DirInfo *i;
DirInfo *next = NULL;
closeFileHandleList(&openReadList);
@ -245,19 +327,20 @@ static void freeSearchPath(void)
for (i = searchPath; i != NULL; i = next)
{
next = i;
freeSearchDir(i);
freeDirInfo(i, openReadList);
} /* for */
searchPath = NULL;
} /* if */
} /* freeSearchPath */
void PHYSFS_deinit(void)
int PHYSFS_deinit(void)
{
BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0);
closeFileHandleList(&openWriteList);
PHYSFS_setWriteDir(NULL);
BAIL_IF_MACRO(!PHYSFS_setWriteDir(NULL), ERR_FILES_STILL_OPEN, 0);
freeSearchPath();
freeErrorMessages();
@ -297,7 +380,7 @@ void PHYSFS_freeList(void *list)
const char *PHYSFS_getDirSeparator(void)
{
return(__PHYSFS_pathSeparator);
return(__PHYSFS_platformDirSeparator);
} /* PHYSFS_getDirSeparator */
@ -321,96 +404,55 @@ const char *PHYSFS_getUserDir(void)
const char *PHYSFS_getWriteDir(void)
{
return(writeDir);
if (writeDir == NULL)
return(NULL);
return(writeDir->dirName);
} /* PHYSFS_getWriteDir */
int PHYSFS_setWriteDir(const char *newDir)
{
BAIL_IF_MACRO(openWriteList != NULL, ERR_FILES_OPEN_WRITE, 0);
if (writeDir != NULL)
{
free(writeDir);
BAIL_IF_MACRO(!freeDirInfo(writeDir, openWriteList), NULL, 0);
writeDir = NULL;
} /* if */
if (newDir != NULL)
{
BAIL_IF_MACRO(!createDirs_dependent(newDir), ERR_NO_DIR_CREATE, 0);
writeDir = malloc(strlen(newDir) + 1);
BAIL_IF_MACRO(writeDir == NULL, ERR_OUT_OF_MEMORY, 0);
strcpy(writeDir, newDir);
writeDir = buildDirInfo(newDir, 1);
return(writeDir != NULL);
} /* if */
return(1);
} /* PHYSFS_setWriteDir */
static DirReader *getDirReader(const char *d)
{
DirFunctions **i;
for (i = dirFunctions; *i != NULL; i++)
{
if ((*i)->isArchive(d))
return( (*i)->openArchive(d) );
} /* for */
__PHYSFS_setError(ERR_UNSUPPORTED_ARCHIVE);
return(NULL);
} /* getDirReader */
int PHYSFS_addToSearchPath(const char *newDir, int appendToPath)
{
char *str = NULL;
SearchDirInfo *sdi = NULL;
DirReader *dirReader = NULL;
DirInfo *di = buildDirInfo(newDir, 0);
BAIL_IF_MACRO(newDir == NULL, ERR_INVALID_ARGUMENT, 0);
reader = getDirReader(newDir); /* This sets the error message. */
if (reader == NULL)
return(0);
sdi = (SearchDirInfo *) malloc(sizeof (SearchDirInfo));
if (sdi == NULL)
reader->close(reader);
BAIL_IF_MACRO(sdi == NULL, ERR_OUT_OF_MEMORY, 0);
sdi->dirName = (char *) malloc(strlen(newDir) + 1);
if (sdi->dirName == NULL)
{
free(sdi);
reader->close(reader);
__PHYSFS_setError(ERR_OUT_OF_MEMORY);
return(0);
} /* if */
sdi->dirReader = dirReader;
strcpy(sdi->dirName, newDir);
BAIL_IF_MACRO(di == NULL, NULL, 0);
if (appendToPath)
{
sdi->next = searchPath;
searchPath = sdi;
di->next = searchPath;
searchPath = di;
} /* if */
else
{
SearchDirInfo *i = searchPath;
SearchDirInfo *prev = NULL;
DirInfo *i = searchPath;
DirInfo *prev = NULL;
sdi->next = NULL;
di->next = NULL;
while (i != NULL)
prev = i;
if (prev == NULL)
searchPath = sdi;
searchPath = di;
else
prev->next = sdi;
prev->next = di;
} /* else */
return(1);
@ -419,9 +461,9 @@ int PHYSFS_addToSearchPath(const char *newDir, int appendToPath)
int PHYSFS_removeFromSearchPath(const char *oldDir)
{
SearchDirInfo *i;
SearchDirInfo *prev = NULL;
SearchDirInfo *next = NULL;
DirInfo *i;
DirInfo *prev = NULL;
DirInfo *next = NULL;
BAIL_IF_MACRO(oldDir == NULL, ERR_INVALID_ARGUMENT, 0);
@ -430,8 +472,7 @@ int PHYSFS_removeFromSearchPath(const char *oldDir)
if (strcmp(i->dirName, oldDir) == 0)
{
next = i->next;
if (!freeSearchDir(i))
return(0);
BAIL_IF_MACRO(!freeDirInfo(i, openReadList), NULL, 0);
if (prev == NULL)
searchPath = next;
@ -452,7 +493,7 @@ char **PHYSFS_getSearchPath(void)
{
int count = 1;
int x;
SearchDirInfo *i;
DirInfo *i;
char **retval;
for (i = searchPath; i != NULL; i = i->next)
@ -501,13 +542,12 @@ int PHYSFS_setSaneConfig(const char *appName, const char *archiveExt,
BAIL_IF_MACRO(str == NULL, ERR_OUT_OF_MEMORY, 0);
sprintf(str, "%s%s.%s", userdir, dirsep, appName);
rc = PHYSFS_setWriteDir(str);
if (!rc)
return(0); /* error set by PHYSFS_setWriteDir() ... */
BAIL_IF_MACRO(!rc, NULL, 0);
/* Put write dir related dirs on search path... */
PHYSFS_addToSearchPath(str, 1);
PHYSFS_mkdir(appName); /* don't care if this fails. */
strcat(str, dirSep);
strcat(str, dirsep);
strcat(str, appName);
PHYSFS_addToSearchPath(str, 1);
free(str);
@ -530,12 +570,12 @@ int PHYSFS_setSaneConfig(const char *appName, const char *archiveExt,
char **i;
for (i = cds; *i != NULL; i++)
{
PHYSFS_addToSearchPath(*i);
PHYSFS_addToSearchPath(*i, 1);
str = malloc(strlen(*i) + strlen(appName) + strlen(dirsep) + 1);
if (str != NULL)
{
sprintf(str, "%s%s%s", *i, dirsep, appName);
PHYSFS_addToSearchPath(str);
PHYSFS_addToSearchPath(str, 1);
free(str);
} /* if */
} /* for */
@ -563,7 +603,7 @@ int PHYSFS_setSaneConfig(const char *appName, const char *archiveExt,
if (str != NULL)
{
sprintf(str, "%s%s%s", d, dirsep, *i);
PHYSFS_addToSearchPath(d, str);
PHYSFS_addToSearchPath(str, archivesFirst == 0);
free(str);
} /* if */
} /* if */
@ -577,11 +617,16 @@ int PHYSFS_setSaneConfig(const char *appName, const char *archiveExt,
} /* PHYSFS_setSaneConfig */
void PHYSFS_permitSymbolicLinks(int allow)
{
allowSymLinks = allow;
} /* PHYSFS_permitSymbolicLinks */
/* string manipulation in C makes my ass itch. */
/* be sure to free this crap after you're done with it. */
static char *convertToDependentNotation(const char *prepend,
const char *dirName,
const char *append)
char *__PHYSFS_convertToDependentNotation(const char *prepend,
const char *dirName,
const char *append)
{
const char *dirsep = PHYSFS_getDirSeparator();
int sepsize = strlen(dirsep);
@ -590,7 +635,7 @@ static char *convertToDependentNotation(const char *prepend,
char *i2;
size_t allocSize;
allocSize = strlen(dirName) + strlen(writeDir) + sepsize + 1;
allocSize = strlen(dirName) + 1;
if (prepend != NULL)
allocSize += strlen(prepend) + sepsize;
if (append != NULL)
@ -599,11 +644,16 @@ static char *convertToDependentNotation(const char *prepend,
/* make sure there's enough space if the dir separator is bigger. */
if (sepsize > 1)
{
for (str = dirName; *str != '\0'; str++)
str = (char *) dirName;
do
{
if (*str == '/')
str = strchr(str, '/');
if (str != NULL)
{
allocSize += (sepsize - 1);
} /* for */
str++;
} /* if */
} while (str != NULL);
} /* if */
str = (char *) malloc(allocSize);
@ -616,7 +666,7 @@ static char *convertToDependentNotation(const char *prepend,
strcat(str, dirsep);
} /* if */
for (i1 = dirName, i2 = str + strlen(str); *i1 != '\0'; i1++, i2++)
for (i1 = (char *) dirName, i2 = str + strlen(str); *i1; i1++, i2++)
{
if (*i1 == '/')
{
@ -637,78 +687,123 @@ static char *convertToDependentNotation(const char *prepend,
} /* if */
return(str);
} /* convertToDependentNotation */
} /* __PHYSFS_convertToDependentNotation */
int __PHYSFS_verifySecurity(DirHandle *h, const char *fname)
{
int retval = 1;
char *start;
char *end;
char *str;
start = str = malloc(strlen(fname) + 1);
BAIL_IF_MACRO(str == NULL, ERR_OUT_OF_MEMORY, 0);
strcpy(str, fname);
while (1)
{
end = strchr(start, '/');
if (end != NULL)
*end = '\0';
if ( (strcmp(start, ".") == 0) ||
(strcmp(start, "..") == 0) ||
(strchr(start, ':') != NULL) )
{
__PHYSFS_setError(ERR_INSECURE_FNAME);
retval = 0;
break;
} /* if */
if ((!allowSymLinks) && (h->funcs->isSymLink(h, str)))
{
__PHYSFS_setError(ERR_SYMLINK_DISALLOWED);
retval = 0;
break;
} /* if */
if (end == NULL)
break;
*end = '/';
start = end + 1;
} /* while */
free(str);
return(retval);
} /* __PHYSFS_verifySecurity */
int PHYSFS_mkdir(const char *dirName)
{
DirHandle *h;
char *str;
int rc;
char *start;
char *end;
int retval = 0;
BAIL_IF_MACRO(writeDir == NULL, ERR_NO_WRITE_DIR, NULL);
BAIL_IF_MACRO(writeDir == NULL, ERR_NO_WRITE_DIR, 0);
h = writeDir->dirHandle;
BAIL_IF_MACRO(h->funcs->mkdir == NULL, ERR_NOT_SUPPORTED, 0);
BAIL_IF_MACRO(!__PHYSFS_verifySecurity(h, dirName), NULL, 0);
str = convertToDependentNotation(writeDir, dirName, NULL);
if (str == NULL) /* __PHYSFS_setError is called in convert call. */
return(0);
start = str = malloc(strlen(dirName) + 1);
BAIL_IF_MACRO(str == NULL, ERR_OUT_OF_MEMORY, 0);
strcpy(str, dirName);
while (1)
{
end = strchr(start, '/');
if (end != NULL)
*end = '\0';
retval = h->funcs->mkdir(h, str);
if (!retval)
break;
if (end == NULL)
break;
*end = '/';
start = end + 1;
} /* while */
rc = createDirs_dependent(str);
free(str);
return(rc);
return(retval);
} /* PHYSFS_mkdir */
int PHYSFS_delete(const char *filename)
int PHYSFS_delete(const char *fname)
{
char *str;
int rc;
BAIL_IF_MACRO(writeDir == NULL, ERR_NO_WRITE_DIR, NULL);
str = convertToDependentNotation(writeDir, fileName, NULL);
if (str == NULL) /* __PHYSFS_setError is called in convert call. */
return(0);
rc = remove(str);
free(str);
rc = (rc == 0);
if (!rc)
__PHYSFS_setError(strerror(errno));
return(rc);
DirHandle *h;
BAIL_IF_MACRO(writeDir == NULL, ERR_NO_WRITE_DIR, 0);
h = writeDir->dirHandle;
BAIL_IF_MACRO(h->funcs->remove == NULL, ERR_NOT_SUPPORTED, 0);
BAIL_IF_MACRO(!__PHYSFS_verifySecurity(h, fname), NULL, 0);
return(h->funcs->remove(h, fname));
} /* PHYSFS_delete */
void PHYSFS_permitSymbolicLinks(int allow)
{
allowSymLinks = allow;
} /* PHYSFS_permitSymbolicLinks */
/**
* Figure out where in the search path a file resides. The file is specified
* in platform-independent notation. The returned filename will be the
* element of the search path where the file was found, which may be a
* directory, or an archive. Even if there are multiple matches in different
* parts of the search path, only the first one found is used, just like
* when opening a file.
*
* So, if you look for "maps/level1.map", and C:\mygame is in your search
* path and C:\mygame\maps\level1.map exists, then "C:\mygame" is returned.
*
* If a match is a symbolic link, and you've not explicitly permitted symlinks,
* then it will be ignored, and the search for a match will continue.
*
* @param filename file to look for.
* @return READ ONLY string of element of search path containing the
* the file in question. NULL if not found.
*/
const char *PHYSFS_getRealDir(const char *filename)
{
DirInfo *i;
for (i = searchPath; i != NULL; i = i->next)
{
DirHandle *h = i->dirHandle;
if (__PHYSFS_verifySecurity(h, filename))
{
if (h->funcs->exists(h, filename))
return(i->dirName);
} /* if */
} /* for */
return(NULL);
} /* PHYSFS_getRealDir */
static void countList(LinkedStringList *list)
static int countList(LinkedStringList *list)
{
int retval = 0;
LinkedStringList *i;
@ -797,16 +892,19 @@ static void interpolateStringLists(LinkedStringList **final,
char **PHYSFS_enumerateFiles(const char *path)
{
SearchDirInfo *i;
DirInfo *i;
char **retval = NULL;
LinkedStringList *rc;
LinkedStringList *finalList = NULL;
for (i = searchPath; i != NULL; i = i->next)
{
assert(i->reader->funcs->enumerateFiles != NULL);
rc = i->reader->funcs->enumerateFiles(path);
interpolateStringLists(&finalList, rc);
DirHandle *h = i->dirHandle;
if (__PHYSFS_verifySecurity(h, path))
{
rc = h->funcs->enumerateFiles(h, path);
interpolateStringLists(&finalList, rc);
} /* if */
} /* for */
retval = convertStringListToPhysFSList(finalList);
@ -814,6 +912,50 @@ char **PHYSFS_enumerateFiles(const char *path)
} /* PHYSFS_enumerateFiles */
int PHYSFS_exists(const char *fname)
{
return(PHYSFS_getRealDir(fname) != NULL);
} /* PHYSFS_exists */
int PHYSFS_isDirectory(const char *fname)
{
DirInfo *i;
for (i = searchPath; i != NULL; i = i->next)
{
DirHandle *h = i->dirHandle;
if (__PHYSFS_verifySecurity(h, fname))
{
if (h->funcs->exists(h, fname))
return(h->funcs->isDirectory(h, fname));
} /* if */
} /* for */
return(0);
} /* PHYSFS_isDirectory */
int PHYSFS_isSymbolicLink(const char *fname)
{
DirInfo *i;
if (!allowSymLinks)
return(0);
for (i = searchPath; i != NULL; i = i->next)
{
DirHandle *h = i->dirHandle;
if (__PHYSFS_verifySecurity(h, fname))
{
if (h->funcs->exists(h, fname))
return(h->funcs->isSymLink(h, fname));
} /* if */
} /* for */
return(0);
} /* PHYSFS_isSymbolicLink */
/**
* Open a file for writing, in platform-independent notation and in relation
* to the write path as the root of the writable filesystem. The specified
@ -826,6 +968,7 @@ char **PHYSFS_enumerateFiles(const char *path)
*/
PHYSFS_file *PHYSFS_openWrite(const char *filename)
{
return NULL;
} /* PHYSFS_openWrite */
@ -842,6 +985,7 @@ PHYSFS_file *PHYSFS_openWrite(const char *filename)
*/
PHYSFS_file *PHYSFS_openAppend(const char *filename)
{
return NULL;
} /* PHYSFS_openAppend */
@ -857,44 +1001,31 @@ PHYSFS_file *PHYSFS_openAppend(const char *filename)
*/
PHYSFS_file *PHYSFS_openRead(const char *filename)
{
return NULL;
} /* PHYSFS_openRead */
/**
* Close a PhysicsFS filehandle. This call is capable of failing if the
* operating system was buffering writes to this file, and (now forced to
* write those changes to physical media) can not store the data for any
* reason. In such a case, the filehandle stays open. A well-written program
* should ALWAYS check the return value from the close call in addition to
* every writing call!
*
* @param handle handle returned from PHYSFS_open*().
* @return nonzero on success, zero on error. Specifics of the error can be
* gleaned from PHYSFS_getLastError().
*/
int PHYSFS_close(PHYSFS_file *handle)
{
FileHandle *h = (FileHandle *) handle->opaque;
FileHandleList *i;
FileHandleList **lists[] = { &openWriteList, &openReadList, NULL };
FileHandleList *prev;
FileHandleList **_lists[] = { &openWriteList, &openReadList, NULL };
FileHandleList ***lists = _lists; /* gay. */
int rc;
assert(h != NULL);
assert(h->funcs != NULL);
assert(h->funcs->close != NULL);
while (lists != NULL)
{
for (i = *(*lists); i != NULL; i = i->next)
for (i = *(*lists), prev = NULL; i != NULL; prev = i, i = i->next)
{
if (i->handle == h)
if (((FileHandle *) i->handle->opaque) == h)
{
rc = h->close(h);
rc = h->funcs->close(h);
if (!rc)
return(0);
if (prev == NULL)
*lists = i->next;
*(*lists) = i->next;
else
prev->next = i->next;
free(i);
@ -905,7 +1036,8 @@ int PHYSFS_close(PHYSFS_file *handle)
lists++;
} /* while */
assert(0); /* shouldn't EVER hit this. */
__PHYSFS_setError(ERR_NOT_A_HANDLE);
return(0);
} /* PHYSFS_close */
@ -960,5 +1092,6 @@ int PHYSFS_seek(PHYSFS_file *handle, int pos)
return(h->funcs->seek(h, pos));
} /* PHYSFS_seek */
/* end of physfs.c ... */

121
physfs.h
View File

@ -211,16 +211,22 @@ int PHYSFS_init(const char *argv0);
* Shutdown PhysicsFS. This closes any files opened via PhysicsFS, blanks the
* search/write paths, frees memory, and invalidates all of your handles.
*
* Once deinitialized, PHYSFS_init() can be called again to restart the
* subsystem.
* Note that this call can FAIL if there's a file open for writing that
* refuses to close (for example, the underlying operating system was
* buffering writes to network filesystem, and the fileserver has crashed,
* or a hard drive has failed, etc). It is usually best to close all write
* handles yourself before calling this function, so that you can gracefully
* handle a specific failure.
*
* This function can be used with atexit(), if you feel it's prudent to do so.
* Once successfully deinitialized, PHYSFS_init() can be called again to
* restart the subsystem. All defaults API states are restored at this
* point.
*
* @return nonzero on success, zero on error. Specifics of the error can be
* gleaned from PHYSFS_getLastError(). If failure, state of PhysFS is
* undefined, and probably badly screwed up.
*/
void PHYSFS_deinit(void);
int PHYSFS_deinit(void);
/**
@ -286,6 +292,30 @@ const char *PHYSFS_getLastError(void);
const char *PHYSFS_getDirSeparator(void);
/**
* Enable symbolic links. Some physical filesystems and archives contain
* files that are just pointers to other files. On the physical filesystem,
* opening such a link will (transparently) open the file that is pointed to.
*
* By default, PhysicsFS will check if a file is really a symlink during open
* calls and fail if it is. Otherwise, the link could take you outside the
* write and search paths, and compromise security.
*
* If you want to take that risk, call this function with a non-zero parameter.
* Note that this is more for sandboxing a program's scripting language, in
* case untrusted scripts try to compromise the system. Generally speaking,
* a user could very well have a legitimate reason to set up a symlink, so
* unless you feel there's a specific danger in allowing them, you should
* permit them.
*
* Symbolic link permission can be enabled or disabled at any time, and is
* disabled by default.
*
* @param allow nonzero to permit symlinks, zero to deny linking.
*/
void PHYSFS_permitSymbolicLinks(int allow);
/**
* Get an array of dirs to available CD-ROM drives.
*
@ -528,30 +558,6 @@ int PHYSFS_mkdir(const char *dirName);
int PHYSFS_delete(const char *filename);
/**
* Enable symbolic links. Some physical filesystems and archives contain
* files that are just pointers to other files. On the physical filesystem,
* opening such a link will (transparently) open the file that is pointed to.
*
* By default, PhysicsFS will check if a file is really a symlink during open
* calls and fail if it is. Otherwise, the link could take you outside the
* write and search paths, and compromise security.
*
* If you want to take that risk, call this function with a non-zero parameter.
* Note that this is more for sandboxing a program's scripting language, in
* case untrusted scripts try to compromise the system. Generally speaking,
* a user could very well have a legitimate reason to set up a symlink, so
* unless you feel there's a specific danger in allowing them, you should
* permit them.
*
* Symbolic link permission can be enabled or disabled at any time, and is
* disabled by default.
*
* @param allow nonzero to permit symlinks, zero to deny linking.
*/
void PHYSFS_permitSymbolicLinks(int allow);
/**
* Figure out where in the search path a file resides. The file is specified
* in platform-independent notation. The returned filename will be the
@ -563,8 +569,9 @@ void PHYSFS_permitSymbolicLinks(int allow);
* So, if you look for "maps/level1.map", and C:\mygame is in your search
* path and C:\mygame\maps\level1.map exists, then "C:\mygame" is returned.
*
* If a match is a symbolic link, and you've not explicitly permitted symlinks,
* then it will be ignored, and the search for a match will continue.
* If a any part of a match is a symbolic link, and you've not explicitly
* permitted symlinks, then it will be ignored, and the search for a match
* will continue.
*
* @param filename file to look for.
* @return READ ONLY string of element of search path containing the
@ -612,12 +619,58 @@ const char *PHYSFS_getRealDir(const char *filename);
char **PHYSFS_enumerateFiles(const char *dir);
/**
* Determine if there is an entry anywhere in the search path by the
* name of (fname).
*
* Note that entries that are symlinks are ignored if
* PHYSFS_permitSymbolicLinks(1) hasn't been called, so you
* might end up further down in the search path than expected.
*
* @param fname filename in platform-independent notation.
* @return non-zero if filename exists. zero otherwise.
*/
int PHYSFS_exists(const char *fname);
/**
* Determine if the first occurence of (fname) in the search path is
* really a directory entry.
*
* Note that entries that are symlinks are ignored if
* PHYSFS_permitSymbolicLinks(1) hasn't been called, so you
* might end up further down in the search path than expected.
*
* @param fname filename in platform-independent notation.
* @return non-zero if filename exists and is a directory. zero otherwise.
*/
int PHYSFS_isDirectory(const char *fname);
/**
* Determine if the first occurence of (fname) in the search path is
* really a symbolic link.
*
* Note that entries that are symlinks are ignored if
* PHYSFS_permitSymbolicLinks(1) hasn't been called, and as such,
* this function will always return 0 in that case.
*
* @param fname filename in platform-independent notation.
* @return non-zero if filename exists and is a symlink. zero otherwise.
*/
int PHYSFS_isSymbolicLink(const char *fname);
/**
* Open a file for writing, in platform-independent notation and in relation
* to the write dir as the root of the writable filesystem. The specified
* file is created if it doesn't exist. If it does exist, it is truncated to
* zero bytes, and the writing offset is set to the start.
*
* Note that entries that are symlinks are ignored if
* PHYSFS_permitSymbolicLinks(1) hasn't been called, and opening a
* symlink with this function will fail in such a case.
*
* @param filename File to open.
* @return A valid PhysicsFS filehandle on success, NULL on error. Specifics
* of the error can be gleaned from PHYSFS_getLastError().
@ -632,6 +685,10 @@ PHYSFS_file *PHYSFS_openWrite(const char *filename);
* is set to the end of the file, so the first write will be the byte after
* the end.
*
* Note that entries that are symlinks are ignored if
* PHYSFS_permitSymbolicLinks(1) hasn't been called, and opening a
* symlink with this function will fail in such a case.
*
* @param filename File to open.
* @return A valid PhysicsFS filehandle on success, NULL on error. Specifics
* of the error can be gleaned from PHYSFS_getLastError().
@ -645,6 +702,10 @@ PHYSFS_file *PHYSFS_openAppend(const char *filename);
* abstract filehandle is associated with it, and reading may be done.
* The reading offset is set to the first byte of the file.
*
* Note that entries that are symlinks are ignored if
* PHYSFS_permitSymbolicLinks(1) hasn't been called, and opening a
* symlink with this function will fail in such a case.
*
* @param filename File to open.
* @return A valid PhysicsFS filehandle on success, NULL on error. Specifics
* of the error can be gleaned from PHYSFS_getLastError().

View File

@ -14,7 +14,7 @@
#error Do not include this header from your applications.
#endif
struct __PHYSFS_DIRREADER__;
struct __PHYSFS_DIRHANDLE__;
struct __PHYSFS_FILEFUNCTIONS__;
typedef struct __PHYSFS_FILEHANDLE__
@ -27,7 +27,7 @@ typedef struct __PHYSFS_FILEHANDLE__
/*
* This should be the DirHandle that created this FileHandle.
*/
const struct __PHYSFS_DIRREADER__ *dirReader;
const struct __PHYSFS_DIRHANDLE__ *dirHandle;
/*
* Pointer to the file i/o functions for this filehandle.
@ -40,6 +40,9 @@ typedef struct __PHYSFS_FILEFUNCTIONS__
{
/*
* Read more from the file.
* Returns number of objects of (objSize) bytes read from file, -1
* if complete failure.
* On failure, call __PHYSFS_setError().
*/
int (*read)(FileHandle *handle, void *buffer,
unsigned int objSize, unsigned int objCount);
@ -47,6 +50,9 @@ typedef struct __PHYSFS_FILEFUNCTIONS__
/*
* Write more to the file. Archives don't have to implement this.
* (Set it to NULL if not implemented).
* Returns number of objects of (objSize) bytes written to file, -1
* if complete failure.
* On failure, call __PHYSFS_setError().
*/
int (*write)(FileHandle *handle, void *buffer,
unsigned int objSize, unsigned int objCount);
@ -64,17 +70,20 @@ typedef struct __PHYSFS_FILEFUNCTIONS__
/*
* Move read/write pointer to byte offset from start of file.
* Returns non-zero on success, zero on error.
* On failure, call __PHYSFS_setError().
*/
int (*seek)(FileHandle *handle, int offset);
/*
* Close the file, and free the FileHandle structure (including "opaque").
* returns non-zero on success, zero if can't close file.
* On failure, call __PHYSFS_setError().
*/
int (*close)(FileHandle *handle);
} FileFunctions;
typedef struct __PHYSFS_DIRREADER__
typedef struct __PHYSFS_DIRHANDLE__
{
/*
* This is reserved for the driver to store information.
@ -82,7 +91,7 @@ typedef struct __PHYSFS_DIRREADER__
void *opaque;
/*
* Pointer to the directory i/o functions for this reader.
* Pointer to the directory i/o functions for this handle.
*/
const struct __PHYSFS_DIRFUNCTIONS__ *funcs;
} DirHandle;
@ -104,16 +113,21 @@ typedef struct __PHYSFS_DIRFUNCTIONS__
/*
* Returns non-zero if (filename) is a valid archive that this
* driver can handle. This filename is in platform-dependent
* notation.
* notation. forWriting is non-zero if this is to be used for
* the write directory, and zero if this is to be used for an
* element of the search path.
*/
int (*isArchive)(const char *filename);
int (*isArchive)(const char *filename, int forWriting);
/*
* Return a DirHandle for dir/archive (name).
* This filename is in platform-dependent notation.
* return (NULL) on error.
* forWriting is non-zero if this is to be used for
* the write directory, and zero if this is to be used for an
* element of the search path.
* Returns NULL on failure, and calls __PHYSFS_setError().
*/
DirHandle *(*openArchive)(const char *name);
DirHandle *(*openArchive)(const char *name, int forWriting);
/*
* Returns a list of all files in dirname. Each element of this list
@ -123,7 +137,13 @@ typedef struct __PHYSFS_DIRFUNCTIONS__
* If you have a memory failure, return as much as you can.
* This dirname is in platform-independent notation.
*/
LinkedStringList **(*enumerateFiles)(DirHandle *r, const char *dirname);
LinkedStringList *(*enumerateFiles)(DirHandle *r, const char *dirname);
/*
* Returns non-zero if filename can be opened for reading.
* This filename is in platform-independent notation.
*/
int (*exists)(DirHandle *r, const char *name);
/*
* Returns non-zero if filename is really a directory.
@ -137,15 +157,12 @@ typedef struct __PHYSFS_DIRFUNCTIONS__
*/
int (*isSymLink)(DirHandle *r, const char *name);
/*
* Returns non-zero if filename can be opened for reading.
* This filename is in platform-independent notation.
*/
int (*isOpenable)(DirHandle *r, const char *name);
/*
* Open file for reading, and return a FileHandle.
* This filename is in platform-independent notation.
* If you can't handle multiple opens of the same file,
* you can opt to fail for the second call.
* Returns NULL on failure, and calls __PHYSFS_setError().
*/
FileHandle *(*openRead)(DirHandle *r, const char *filename);
@ -153,6 +170,9 @@ typedef struct __PHYSFS_DIRFUNCTIONS__
* Open file for writing, and return a FileHandle.
* This filename is in platform-independent notation.
* This method may be NULL.
* If you can't handle multiple opens of the same file,
* you can opt to fail for the second call.
* Returns NULL on failure, and calls __PHYSFS_setError().
*/
FileHandle *(*openWrite)(DirHandle *r, const char *filename);
@ -160,9 +180,33 @@ typedef struct __PHYSFS_DIRFUNCTIONS__
* Open file for appending, and return a FileHandle.
* This filename is in platform-independent notation.
* This method may be NULL.
* If you can't handle multiple opens of the same file,
* you can opt to fail for the second call.
* Returns NULL on failure, and calls __PHYSFS_setError().
*/
FileHandle *(*openAppend)(DirHandle *r, const char *filename);
/*
* Delete a file in the archive/directory.
* Return non-zero on success, zero on failure.
* This filename is in platform-independent notation.
* This method may be NULL.
* On failure, call __PHYSFS_setError().
*/
int (*remove)(DirHandle *r, const char *filename);
/*
* Create a directory in the archive/directory.
* If the application is trying to make multiple dirs, PhysicsFS
* will split them up into multiple calls before passing them to
* your driver.
* Return non-zero on success, zero on failure.
* This filename is in platform-independent notation.
* This method may be NULL.
* On failure, call __PHYSFS_setError().
*/
int (*mkdir)(DirHandle *r, const char *filename);
/*
* Close directories/archives, and free the handle, including
* the "opaque" entry. This should assume that it won't be called if
@ -176,25 +220,59 @@ typedef struct __PHYSFS_DIRFUNCTIONS__
#define ERR_IS_INITIALIZED "Already initialized"
#define ERR_NOT_INITIALIZED "Not initialized"
#define ERR_INVALID_ARGUMENT "Invalid argument"
#define ERR_FILES_OPEN_READ "Files still open for reading"
#define ERR_FILES_OPEN_WRITE "Files still open for writing"
#define ERR_FILES_STILL_OPEN "Files still open"
#define ERR_NO_DIR_CREATE "Failed to create directories"
#define ERR_OUT_OF_MEMORY "Out of memory"
#define ERR_NOT_IN_SEARCH_PATH "No such entry in search path"
#define ERR_NOT_SUPPORTED "Operation not supported"
#define ERR_UNSUPPORTED_ARCHIVE "Archive type unsupported"
#define ERR_NOT_A_HANDLE "Not a file handle"
#define ERR_INSECURE_FNAME "Insecure filename"
#define ERR_SYMLINK_DISALLOWED "Symbolic links are disabled"
#define ERR_NO_WRITE_DIR "Write directory is not set"
/*
* Call this to set the message returned by PHYSFS_getLastError().
* Please only use the ERR_* constants above, or add new constants to the
* above group, but I want these all in one place.
*
* Calling this with a NULL argument is a safe no-op.
*/
void __PHYSFS_setError(const char *err);
/*
* Convert (dirName) to platform-dependent notation, then prepend (prepend)
* and append (append) to the converted string.
*
* So, on Win32, calling:
* __PHYSFS_convertToDependentNotation("C:\", "my/files", NULL);
* ...will return the string "C:\my\files".
*
* This is a convenience function; you might want to hack something out that
* is less generic (and therefore more efficient).
*
* Be sure to free() the return value when done with it.
*/
char *__PHYSFS_convertToDependentNotation(const char *prepend,
const char *dirName,
const char *append);
/*
* Verify that (fname) (in platform-independent notation), in relation
* to (h) is secure. That means that each element of fname is checked
* for symlinks (if they aren't permitted). Also, elements such as
* ".", "..", or ":" are flagged.
*
* Returns non-zero if string is safe, zero if there's a security issue.
* PHYSFS_getLastError() will specify what was wrong.
*/
int __PHYSFS_verifySecurity(DirHandle *h, const char *fname);
/* This gets used all over for lessening code clutter. */
#define BAIL_IF_MACRO(c, e, r) if (c) { __PHYSFS_setError(e); return(r); }
#define BAIL_IF_MACRO(c, e, r) if (c) { __PHYSFS_setError(e); return r; }
@ -214,7 +292,7 @@ void __PHYSFS_setError(const char *err);
* The dir separator; "/" on unix, "\\" on win32, ":" on MacOS, etc...
* Obviously, this isn't a function, but it IS a null-terminated string.
*/
extern const char *__PHYSFS_PlatformDirSeparator;
extern const char *__PHYSFS_platformDirSeparator;
/*
* Platform implementation of PHYSFS_getCdRomDirs()...
@ -262,6 +340,11 @@ int __PHYSFS_platformStricmp(const char *str1, const char *str2);
*/
int __PHYSFS_platformIsSymlink(const char *fname);
/*
* Return non-zero if filename (in platform-dependent notation) is a symlink.
*/
int __PHYSFS_platformIsDirectory(const char *fname);
#ifdef __cplusplus
extern "C" {

21
unix.c
View File

@ -14,7 +14,7 @@
#include "physfs_internal.h"
const char *__PHYSFS_PlatformDirSeparator = "/";
const char *__PHYSFS_platformDirSeparator = "/";
char **__PHYSFS_platformDetectAvailableCDs(void)
{
@ -27,6 +27,16 @@ char *__PHYSFS_platformCalcBaseDir(char *argv0)
} /* __PHYSFS_platformCalcBaseDir */
char *__PHYSFS_platformGetUserName(void)
{
} /* __PHYSFS_platformGetUserName */
char *__PHYSFS_platformGetUserDir(void)
{
} /* __PHYSFS_platformGetUserDir */
int __PHYSFS_platformGetThreadID(void)
{
return((int) pthread_self());
@ -44,14 +54,9 @@ int __PHYSFS_platformIsSymlink(const char *fname)
} /* __PHYSFS_platformIsSymlink */
char *__PHYSFS_platformGetUserName(void)
int __PHYSFS_platformIsDirectory(const char *fname)
{
} /* __PHYSFS_platformGetUserName */
char *__PHYSFS_platformGetUserDir(void);
{
} /* __PHYSFS_platformGetUserDir */
} /* __PHYSFS_platformIsDirectory */
/* end of unix.c ... */