123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- /* JMQ:
- Here's the scoop on unix file IO. The windows platform makes some
- assumptions about fileio: 1) the file system is case-insensitive, and
- 2) the platform can write to the directory in which
- the game is running. Both of these are usually false on linux. So, to
- compensate, we "route" created files and directories to the user's home
- directory (see GetPrefPath()). When a file is to be accessed, the code
- looks in the home directory first. If the file is not found there and the
- open mode is read only, the code will look in the game installation
- directory. Files are never created or modified in the game directory.
- For case-sensitivity, the MungePath code will test whether a given path
- specified by the engine exists. If not, it will use the ResolvePathCaseInsensitive function
- which will try to determine if an actual filesystem path matches the
- specified path case insensitive. If one is found, the actual path
- transparently (we hope) replaces the one requested by the engine.
- The preference directory is global to all torque executables with the same
- name. You should make sure you keep it clean if you build from multiple
- torque development trees.
- */
- #if defined(__FreeBSD__)
- #include <sys/types.h>
- #endif
- #if defined(__APPLE__)
- #include <sys/syslimits.h>
- #endif
- #include <utime.h>
- /* these are for reading directors, getting stats, etc. */
- #include <dirent.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <errno.h>
- #include <stdlib.h>
- #include "core/fileio.h"
- #include "core/util/tVector.h"
- #include "core/stringTable.h"
- #include "console/console.h"
- #include "core/strings/stringFunctions.h"
- #include "util/tempAlloc.h"
- #include "cinterface/c_controlInterface.h"
- #include "core/volume.h"
- const int MaxPath = PATH_MAX;
- //------------------------------------------------------------------------------
- // munge the case of the specified pathName. This means try to find the actual
- // filename in with case-insensitive matching on the specified pathName, and
- // store the actual found name.
- bool ResolvePathCaseInsensitive(char* pathName, S32 pathNameSize, bool requiredAbsolute)
- {
- char tempBuf[MaxPath];
- dStrncpy(tempBuf, pathName, pathNameSize);
- // Check if we're an absolute path
- if (pathName[0] != '/')
- {
- AssertFatal(!requiredAbsolute, "PATH must be absolute");
- return false;
- }
- struct stat filestat;
- const int MaxPathEl = 200;
- char *currChar = pathName;
- char testPath[MaxPath];
- char pathEl[MaxPathEl];
- bool done = false;
- bool foundMatch = false;
- dStrncpy(tempBuf, "/", MaxPath);
- currChar++;
- while (!done)
- {
- char* termChar = dStrchr(currChar, '/');
- if (termChar == NULL)
- termChar = dStrchr(currChar, '\0');
- AssertFatal(termChar, "Can't find / or NULL terminator");
- S32 pathElLen = (termChar - currChar);
- dStrncpy(pathEl, currChar, pathElLen);
- pathEl[pathElLen] = '\0';
- dStrncpy(testPath, tempBuf, MaxPath);
- dStrcat(testPath, pathEl, MaxPath);
- if (stat(testPath, &filestat) != -1)
- {
- dStrncpy(tempBuf, testPath, MaxPath);
- }
- else
- {
- DIR *dir = opendir(tempBuf);
- struct dirent* ent;
- while (dir != NULL && (ent = readdir(dir)) != NULL)
- {
- if (dStricmp(pathEl, ent->d_name) == 0)
- {
- foundMatch = true;
- dStrcat(tempBuf, ent->d_name, MaxPath);
- break;
- }
- }
- if (!foundMatch)
- dStrncpy(tempBuf, testPath, MaxPath);
- if (dir)
- closedir(dir);
- }
- if (*termChar == '/')
- {
- dStrcat(tempBuf, "/", MaxPath);
- termChar++;
- currChar = termChar;
- }
- else
- done = true;
- }
- dStrncpy(pathName, tempBuf, pathNameSize);
- return foundMatch;
- }
- #ifndef __APPLE__
- // evil hack to get around insane X windows #define-happy header files
- #ifdef Status
- #undef Status
- #endif
- #include "platformPOSIX/platformPOSIX.h"
- extern int x86UNIXOpen(const char *path, int oflag);
- extern int x86UNIXClose(int fd);
- extern ssize_t x86UNIXRead(int fd, void *buf, size_t nbytes);
- extern ssize_t x86UNIXWrite(int fd, const void *buf, size_t nbytes);
- extern bool ResolvePathCaseInsensitive(char* pathName, S32 pathNameSize, bool requiredAbsolute);
- namespace
- {
- const char sTempDir[] = "/tmp/";
- static char sBinPathName[MaxPath] = "";
- static char *sBinName = sBinPathName;
- bool sUseRedirect = true;
- }
- StringTableEntry osGetTemporaryDirectory()
- {
- return StringTable->insert(sTempDir);
- }
- // Various handy utility functions:
- //------------------------------------------------------------------------------
- // find all \ in a path and convert them in place to /
- static void ForwardSlash(char *str)
- {
- while(*str)
- {
- if(*str == '\\')
- *str = '/';
- str++;
- }
- }
- //------------------------------------------------------------------------------
- // copy a file from src to dest
- static bool CopyFile(const char* src, const char* dest)
- {
- S32 srcFd = x86UNIXOpen(src, O_RDONLY);
- S32 destFd = x86UNIXOpen(dest, O_WRONLY | O_CREAT | O_TRUNC);
- bool error = false;
- if (srcFd != -1 && destFd != -1)
- {
- const int BufSize = 8192;
- char buf[BufSize];
- S32 bytesRead = 0;
- while ((bytesRead = x86UNIXRead(srcFd, buf, BufSize)) > 0)
- {
- // write data
- if (x86UNIXWrite(destFd, buf, bytesRead) == -1)
- {
- error = true;
- break;
- }
- }
- if (bytesRead == -1)
- error = true;
- }
- if (srcFd != -1)
- x86UNIXClose(srcFd);
- if (destFd != -1)
- x86UNIXClose(destFd);
- if (error)
- {
- Con::errorf("Error copying file: %s, %s", src, dest);
- remove(dest);
- }
- return error;
- }
- bool dPathCopy(const char *fromName, const char *toName, bool nooverwrite)
- {
- return CopyFile(fromName,toName);
- }
- //-----------------------------------------------------------------------------
- static char sgPrefDir[MaxPath];
- static bool sgPrefDirInitialized = false;
- // get the "pref dir", which is where game output files are stored. the pref
- // dir is ~/PREF_DIR_ROOT/PREF_DIR_GAME_NAME
- static const char* GetPrefDir()
- {
- if (sgPrefDirInitialized)
- return sgPrefDir;
- if (sUseRedirect)
- {
- const char *home = getenv("HOME");
- AssertFatal(home, "HOME environment variable must be set");
- dSprintf(sgPrefDir, MaxPath, "%s/%s/%s",
- home, PREF_DIR_ROOT, PREF_DIR_GAME_NAME);
- }
- else
- {
- getcwd(sgPrefDir, MaxPath);
- }
- sgPrefDirInitialized = true;
- return sgPrefDir;
- }
- //-----------------------------------------------------------------------------
- // Returns true if the pathname exists, false otherwise. If isFile is true,
- // the pathname is assumed to be a file path, and only the directory part
- // will be examined (everything before last /)
- bool DirExists(char* pathname, bool isFile)
- {
- static char testpath[20000];
- dStrncpy(testpath, pathname, sizeof(testpath));
- if (isFile)
- {
- // find the last / and make it into null
- char* lastSlash = dStrrchr(testpath, '/');
- if (lastSlash != NULL)
- *lastSlash = 0;
- }
- return Platform::isDirectory(testpath);
- }
- //-----------------------------------------------------------------------------
- // Munge the specified path.
- static void MungePath(char* dest, S32 destSize,
- const char* src, const char* absolutePrefix)
- {
- char tempBuf[MaxPath];
- dStrncpy(dest, src, MaxPath);
- // translate all \ to /
- ForwardSlash(dest);
- // if it is relative, make it absolute with the absolutePrefix
- if (dest[0] != '/')
- {
- AssertFatal(absolutePrefix, "Absolute Prefix must not be NULL");
- dSprintf(tempBuf, MaxPath, "%s/%s",
- absolutePrefix, dest);
- // copy the result back into dest
- dStrncpy(dest, tempBuf, destSize);
- }
- // if the path exists, we're done
- struct stat filestat;
- if (stat(dest, &filestat) != -1)
- return;
- // otherwise munge the case of the path
- ResolvePathCaseInsensitive(dest, destSize, true);
- }
- //-----------------------------------------------------------------------------
- enum
- {
- TOUCH,
- DELETE
- };
- //-----------------------------------------------------------------------------
- // perform a modification on the specified file. allowed modifications are
- // specified in the enum above.
- bool ModifyFile(const char * name, S32 modType)
- {
- if(!name || (dStrlen(name) >= MaxPath) || dStrstr(name, "../") != NULL)
- return(false);
- // if its absolute skip it
- if (name[0]=='/' || name[0]=='\\')
- return(false);
- // only modify files in home directory
- char prefPathName[MaxPath];
- MungePath(prefPathName, MaxPath, name, GetPrefDir());
- if (modType == TOUCH)
- return(utime(prefPathName, 0) != -1);
- else if (modType == DELETE)
- return (remove(prefPathName) == 0);
- else
- AssertFatal(false, "Unknown File Mod type");
- return false;
- }
- //-----------------------------------------------------------------------------
- static bool RecurseDumpPath(const char *path, const char* relativePath, const char *pattern, Vector<Platform::FileInfo> &fileVector, S32 currentDepth = 0, S32 recurseDepth = -1)
- {
- char search[1024];
- dSprintf(search, sizeof(search), "%s", path, pattern);
- DIR *directory = opendir(search);
- if (directory == NULL)
- return false;
- struct dirent *fEntry;
- fEntry = readdir(directory); // read the first "file" in the directory
- if (fEntry == NULL)
- {
- closedir(directory);
- return false;
- }
- do
- {
- char filename[BUFSIZ+1];
- struct stat fStat;
- dSprintf(filename, sizeof(filename), "%s/%s", search, fEntry->d_name); // "construct" the file name
- stat(filename, &fStat); // get the file stats
- if ( (fStat.st_mode & S_IFMT) == S_IFDIR )
- {
- // Directory
- // skip . and .. directories
- if (dStrcmp(fEntry->d_name, ".") == 0 || dStrcmp(fEntry->d_name, "..") == 0)
- continue;
- // skip excluded directories
- if( Platform::isExcludedDirectory(fEntry->d_name))
- continue;
- char child[MaxPath];
- dSprintf(child, sizeof(child), "%s/%s", path, fEntry->d_name);
- char* childRelative = NULL;
- char childRelativeBuf[MaxPath];
- if (relativePath)
- {
- dSprintf(childRelativeBuf, sizeof(childRelativeBuf), "%s/%s",
- relativePath, fEntry->d_name);
- childRelative = childRelativeBuf;
- }
- if (currentDepth < recurseDepth || recurseDepth == -1 )
- RecurseDumpPath(child, childRelative, pattern, fileVector, currentDepth+1, recurseDepth);
- }
- else
- {
- // File
- // add it to the list
- fileVector.increment();
- Platform::FileInfo& rInfo = fileVector.last();
- if (relativePath)
- rInfo.pFullPath = StringTable->insert(relativePath);
- else
- rInfo.pFullPath = StringTable->insert(path);
- rInfo.pFileName = StringTable->insert(fEntry->d_name);
- rInfo.fileSize = fStat.st_size;
- //dPrintf("Adding file: %s/%s\n", rInfo.pFullPath, rInfo.pFileName);
- }
- } while( (fEntry = readdir(directory)) != NULL );
- closedir(directory);
- return true;
- }
- //-----------------------------------------------------------------------------
- bool dFileDelete(const char * name)
- {
- return ModifyFile(name, DELETE);
- }
- //-----------------------------------------------------------------------------
- bool dFileTouch(const char * name)
- {
- return ModifyFile(name, TOUCH);
- }
- bool dFileRename(const char *oldName, const char *newName)
- {
- AssertFatal( oldName != NULL && newName != NULL, "dFileRename - NULL file name" );
- // only modify files in home directory
- TempAlloc<char> oldPrefPathName(MaxPath);
- TempAlloc<char> newPrefPathName(MaxPath);
- MungePath(oldPrefPathName, MaxPath, oldName, GetPrefDir());
- MungePath(newPrefPathName, MaxPath, newName, GetPrefDir());
- return rename(oldPrefPathName, newPrefPathName) == 0;
- }
- //-----------------------------------------------------------------------------
- // Constructors & Destructor
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- // After construction, the currentStatus will be Closed and the capabilities
- // will be 0.
- //-----------------------------------------------------------------------------
- File::File()
- : currentStatus(Closed), capability(0)
- {
- // AssertFatal(sizeof(int) == sizeof(void *), "File::File: cannot cast void* to int");
- handle = (void *)NULL;
- }
- //-----------------------------------------------------------------------------
- // insert a copy constructor here... (currently disabled)
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- // Destructor
- //-----------------------------------------------------------------------------
- File::~File()
- {
- close();
- handle = (void *)NULL;
- }
- //-----------------------------------------------------------------------------
- // Open a file in the mode specified by openMode (Read, Write, or ReadWrite).
- // Truncate the file if the mode is either Write or ReadWrite and truncate is
- // true.
- //
- // Sets capability appropriate to the openMode.
- // Returns the currentStatus of the file.
- //-----------------------------------------------------------------------------
- File::FileStatus File::open(const char *filename, const AccessMode openMode)
- {
- AssertFatal(NULL != filename, "File::open: NULL filename");
- AssertWarn(NULL == handle, "File::open: handle already valid");
- // Close the file if it was already open...
- if (Closed != currentStatus)
- close();
- char prefPathName[MaxPath];
- char gamePathName[MaxPath];
- char cwd[MaxPath];
- getcwd(cwd, MaxPath);
- MungePath(prefPathName, MaxPath, filename, GetPrefDir());
- MungePath(gamePathName, MaxPath, filename, cwd);
- int oflag;
- struct stat filestat;
- handle = (void *)dRealMalloc(sizeof(int));
- switch (openMode)
- {
- case Read:
- oflag = O_RDONLY;
- break;
- case Write:
- oflag = O_WRONLY | O_CREAT | O_TRUNC;
- break;
- case ReadWrite:
- oflag = O_RDWR | O_CREAT;
- // if the file does not exist copy it before reading/writing
- if (stat(prefPathName, &filestat) == -1)
- bool ret = CopyFile(gamePathName, prefPathName);
- break;
- case WriteAppend:
- oflag = O_WRONLY | O_CREAT | O_APPEND;
- // if the file does not exist copy it before appending
- if (stat(prefPathName, &filestat) == -1)
- bool ret = CopyFile(gamePathName, prefPathName);
- break;
- default:
- AssertFatal(false, "File::open: bad access mode"); // impossible
- }
- // if we are writing, make sure output path exists
- if (openMode == Write || openMode == ReadWrite || openMode == WriteAppend)
- Platform::createPath(prefPathName);
- int fd = -1;
- fd = x86UNIXOpen(prefPathName, oflag);
- if (fd == -1 && openMode == Read)
- // for read only files we can use the gamePathName
- fd = x86UNIXOpen(gamePathName, oflag);
- dMemcpy(handle, &fd, sizeof(int));
- #ifdef DEBUG
- // fprintf(stdout,"fd = %d, handle = %d\n", fd, *((int *)handle));
- #endif
- if (*((int *)handle) == -1)
- {
- // handle not created successfully
- Con::errorf("Can't open file: %s", filename);
- return setStatus();
- }
- else
- {
- // successfully created file, so set the file capabilities...
- switch (openMode)
- {
- case Read:
- capability = U32(FileRead);
- break;
- case Write:
- case WriteAppend:
- capability = U32(FileWrite);
- break;
- case ReadWrite:
- capability = U32(FileRead) |
- U32(FileWrite);
- break;
- default:
- AssertFatal(false, "File::open: bad access mode");
- }
- return currentStatus = Ok; // success!
- }
- }
- //-----------------------------------------------------------------------------
- // Get the current position of the file pointer.
- //-----------------------------------------------------------------------------
- U32 File::getPosition() const
- {
- AssertFatal(Closed != currentStatus, "File::getPosition: file closed");
- AssertFatal(NULL != handle, "File::getPosition: invalid file handle");
- #ifdef DEBUG
- // fprintf(stdout, "handle = %d\n",*((int *)handle));fflush(stdout);
- #endif
- return (U32) lseek(*((int *)handle), 0, SEEK_CUR);
- }
- //-----------------------------------------------------------------------------
- // Set the position of the file pointer.
- // Absolute and relative positioning is supported via the absolutePos
- // parameter.
- //
- // If positioning absolutely, position MUST be positive - an IOError results if
- // position is negative.
- // Position can be negative if positioning relatively, however positioning
- // before the start of the file is an IOError.
- //
- // Returns the currentStatus of the file.
- //-----------------------------------------------------------------------------
- File::FileStatus File::setPosition(S32 position, bool absolutePos)
- {
- AssertFatal(Closed != currentStatus, "File::setPosition: file closed");
- AssertFatal(NULL != handle, "File::setPosition: invalid file handle");
- if (Ok != currentStatus && EOS != currentStatus)
- return currentStatus;
- U32 finalPos = 0;
- if (absolutePos)
- {
- AssertFatal(0 <= position, "File::setPosition: negative absolute position");
- // position beyond EOS is OK
- finalPos = lseek(*((int *)handle), position, SEEK_SET);
- }
- else
- {
- AssertFatal((getPosition() >= (U32)abs(position) && 0 > position) || 0 <= position, "File::setPosition: negative relative position");
- // position beyond EOS is OK
- finalPos = lseek(*((int *)handle), position, SEEK_CUR);
- }
- if (0xffffffff == finalPos)
- return setStatus(); // unsuccessful
- else if (finalPos >= getSize())
- return currentStatus = EOS; // success, at end of file
- else
- return currentStatus = Ok; // success!
- }
- //-----------------------------------------------------------------------------
- // Get the size of the file in bytes.
- // It is an error to query the file size for a Closed file, or for one with an
- // error status.
- //-----------------------------------------------------------------------------
- U32 File::getSize() const
- {
- AssertWarn(Closed != currentStatus, "File::getSize: file closed");
- AssertFatal(NULL != handle, "File::getSize: invalid file handle");
- if (Ok == currentStatus || EOS == currentStatus)
- {
- long currentOffset = getPosition(); // keep track of our current position
- long fileSize;
- lseek(*((int *)handle), 0, SEEK_END); // seek to the end of the file
- fileSize = getPosition(); // get the file size
- lseek(*((int *)handle), currentOffset, SEEK_SET); // seek back to our old offset
- return fileSize; // success!
- }
- else
- return 0; // unsuccessful
- }
- //-----------------------------------------------------------------------------
- // Flush the file.
- // It is an error to flush a read-only file.
- // Returns the currentStatus of the file.
- //-----------------------------------------------------------------------------
- File::FileStatus File::flush()
- {
- AssertFatal(Closed != currentStatus, "File::flush: file closed");
- AssertFatal(NULL != handle, "File::flush: invalid file handle");
- AssertFatal(true == hasCapability(FileWrite), "File::flush: cannot flush a read-only file");
- if (fsync(*((int *)handle)) == 0)
- return currentStatus = Ok; // success!
- else
- return setStatus(); // unsuccessful
- }
- //-----------------------------------------------------------------------------
- // Close the File.
- //
- // Returns the currentStatus
- //-----------------------------------------------------------------------------
- File::FileStatus File::close()
- {
- // if the handle is non-NULL, close it if necessary and free it
- if (NULL != handle)
- {
- // make a local copy of the handle value and
- // free the handle
- int handleVal = *((int *)handle);
- dRealFree(handle);
- handle = (void *)NULL;
- // close the handle if it is valid
- if (handleVal != -1 && x86UNIXClose(handleVal) != 0)
- return setStatus(); // unsuccessful
- }
- // Set the status to closed
- return currentStatus = Closed;
- }
- //-----------------------------------------------------------------------------
- // Self-explanatory.
- //-----------------------------------------------------------------------------
- File::FileStatus File::getStatus() const
- {
- return currentStatus;
- }
- //-----------------------------------------------------------------------------
- // Sets and returns the currentStatus when an error has been encountered.
- //-----------------------------------------------------------------------------
- File::FileStatus File::setStatus()
- {
- Con::printf("File IO error: %s", strerror(errno));
- return currentStatus = IOError;
- }
- //-----------------------------------------------------------------------------
- // Sets and returns the currentStatus to status.
- //-----------------------------------------------------------------------------
- File::FileStatus File::setStatus(File::FileStatus status)
- {
- return currentStatus = status;
- }
- //-----------------------------------------------------------------------------
- // Read from a file.
- // The number of bytes to read is passed in size, the data is returned in src.
- // The number of bytes read is available in bytesRead if a non-Null pointer is
- // provided.
- //-----------------------------------------------------------------------------
- File::FileStatus File::read(U32 size, char *dst, U32 *bytesRead)
- {
- #ifdef DEBUG
- // fprintf(stdout,"reading %d bytes\n",size);fflush(stdout);
- #endif
- AssertFatal(Closed != currentStatus, "File::read: file closed");
- AssertFatal(NULL != handle, "File::read: invalid file handle");
- AssertFatal(NULL != dst, "File::read: NULL destination pointer");
- AssertFatal(true == hasCapability(FileRead), "File::read: file lacks capability");
- AssertWarn(0 != size, "File::read: size of zero");
- /* show stats for this file */
- #ifdef DEBUG
- //struct stat st;
- //fstat(*((int *)handle), &st);
- //fprintf(stdout,"file size = %d\n", st.st_size);
- #endif
- /****************************/
- if (Ok != currentStatus || 0 == size)
- return currentStatus;
- else
- {
- long lastBytes;
- long *bytes = (NULL == bytesRead) ? &lastBytes : (long *)bytesRead;
- if ( (*((U32 *)bytes) = x86UNIXRead(*((int *)handle), dst, size)) == -1)
- {
- #ifdef DEBUG
- // fprintf(stdout,"unsuccessful: %d\n", *((U32 *)bytes));fflush(stdout);
- #endif
- return setStatus(); // unsuccessful
- } else {
- // dst[*((U32 *)bytes)] = '\0';
- if (*((U32 *)bytes) != size || *((U32 *)bytes) == 0) {
- #ifdef DEBUG
- // fprintf(stdout,"end of stream: %d\n", *((U32 *)bytes));fflush(stdout);
- #endif
- return currentStatus = EOS; // end of stream
- }
- }
- }
- // dst[*bytesRead] = '\0';
- #ifdef DEBUG
- //fprintf(stdout, "We read:\n");
- //fprintf(stdout, "====================================================\n");
- //fprintf(stdout, "%s\n",dst);
- //fprintf(stdout, "====================================================\n");
- //fprintf(stdout,"read ok: %d\n", *bytesRead);fflush(stdout);
- #endif
- return currentStatus = Ok; // successfully read size bytes
- }
- //-----------------------------------------------------------------------------
- // Write to a file.
- // The number of bytes to write is passed in size, the data is passed in src.
- // The number of bytes written is available in bytesWritten if a non-Null
- // pointer is provided.
- //-----------------------------------------------------------------------------
- File::FileStatus File::write(U32 size, const char *src, U32 *bytesWritten)
- {
- // JMQ: despite the U32 parameters, the maximum filesize supported by this
- // function is probably the max value of S32, due to the unix syscall
- // api.
- AssertFatal(Closed != currentStatus, "File::write: file closed");
- AssertFatal(NULL != handle, "File::write: invalid file handle");
- AssertFatal(NULL != src, "File::write: NULL source pointer");
- AssertFatal(true == hasCapability(FileWrite), "File::write: file lacks capability");
- AssertWarn(0 != size, "File::write: size of zero");
- if ((Ok != currentStatus && EOS != currentStatus) || 0 == size)
- return currentStatus;
- else
- {
- S32 numWritten = x86UNIXWrite(*((int *)handle), src, size);
- if (numWritten < 0)
- return setStatus();
- if (bytesWritten)
- *bytesWritten = static_cast<U32>(numWritten);
- return currentStatus = Ok;
- }
- }
- //-----------------------------------------------------------------------------
- // Self-explanatory. JMQ: No explanation needed. Move along. These aren't
- // the droids you're looking for.
- //-----------------------------------------------------------------------------
- bool File::hasCapability(Capability cap) const
- {
- return (0 != (U32(cap) & capability));
- }
- //-----------------------------------------------------------------------------
- S32 Platform::compareFileTimes(const FileTime &a, const FileTime &b)
- {
- if(a > b)
- return 1;
- if(a < b)
- return -1;
- return 0;
- }
- //-----------------------------------------------------------------------------
- static bool GetFileTimes(const char *filePath, FileTime *createTime, FileTime *modifyTime)
- {
- struct stat fStat;
- if (stat(filePath, &fStat) == -1)
- return false;
- if(createTime)
- {
- // no where does SysV/BSD UNIX keep a record of a file's
- // creation time. instead of creation time I'll just use
- // changed time for now.
- *createTime = fStat.st_ctime;
- }
- if(modifyTime)
- {
- *modifyTime = fStat.st_mtime;
- }
- return true;
- }
- //-----------------------------------------------------------------------------
- bool Platform::getFileTimes(const char *filePath, FileTime *createTime, FileTime *modifyTime)
- {
- char pathName[MaxPath];
- // if it starts with cwd, we need to strip that off so that we can look for
- // the file in the pref dir
- char cwd[MaxPath];
- getcwd(cwd, MaxPath);
- if (dStrstr(filePath, cwd) == filePath)
- filePath = filePath + dStrlen(cwd) + 1;
- // if its relative, first look in the pref dir
- if (filePath[0] != '/' && filePath[0] != '\\')
- {
- MungePath(pathName, MaxPath, filePath, GetPrefDir());
- if (GetFileTimes(pathName, createTime, modifyTime))
- return true;
- }
- // here if the path is absolute or not in the pref dir
- MungePath(pathName, MaxPath, filePath, cwd);
- return GetFileTimes(pathName, createTime, modifyTime);
- }
- //-----------------------------------------------------------------------------
- bool Platform::createPath(const char *file)
- {
- char pathbuf[MaxPath];
- const char *dir;
- pathbuf[0] = 0;
- U32 pathLen = 0;
- // all paths should be created in home directory
- char prefPathName[MaxPath];
- MungePath(prefPathName, MaxPath, file, GetPrefDir());
- file = prefPathName;
- // does the directory exist already?
- if (DirExists(prefPathName, true)) // true means that the path is a filepath
- return true;
- while((dir = dStrchr(file, '/')) != NULL)
- {
- dStrncpy(pathbuf + pathLen, file, dir - file);
- pathbuf[pathLen + dir-file] = 0;
- bool ret = mkdir(pathbuf, 0700);
- pathLen += dir - file;
- pathbuf[pathLen++] = '/';
- file = dir + 1;
- }
- return true;
- }
- // JMQ: Platform:cdFileExists in unimplemented
- //------------------------------------------------------------------------------
- // bool Platform::cdFileExists(const char *filePath, const char *volumeName,
- // S32 serialNum)
- // {
- // }
- //-----------------------------------------------------------------------------
- bool Platform::dumpPath(const char *path, Vector<Platform::FileInfo> &fileVector, int depth)
- {
- const char* pattern = "*";
- // if it is not absolute, dump the pref dir first
- if (path[0] != '/' && path[0] != '\\')
- {
- char prefPathName[MaxPath];
- MungePath(prefPathName, MaxPath, path, GetPrefDir());
- RecurseDumpPath(prefPathName, path, pattern, fileVector, 0, depth);
- }
- // munge the requested path and dump it
- char mungedPath[MaxPath];
- char cwd[MaxPath];
- getcwd(cwd, MaxPath);
- MungePath(mungedPath, MaxPath, path, cwd);
- return RecurseDumpPath(mungedPath, path, pattern, fileVector, 0, depth);
- }
- //-----------------------------------------------------------------------------
- StringTableEntry Platform::getCurrentDirectory()
- {
- char cwd_buf[2048];
- getcwd(cwd_buf, 2047);
- return StringTable->insert(cwd_buf);
- }
- //-----------------------------------------------------------------------------
- bool Platform::setCurrentDirectory(StringTableEntry newDir)
- {
- if (Platform::getWebDeployment())
- return true;
- TempAlloc< UTF8 > buf( dStrlen( newDir ) + 2 );
- dStrcpy( buf, newDir, buf.size );
- ForwardSlash( buf );
- return chdir( buf ) == 0;
- }
- //-----------------------------------------------------------------------------
- const char *Platform::getUserDataDirectory()
- {
- return StringTable->insert( GetPrefDir() );
- }
- //-----------------------------------------------------------------------------
- StringTableEntry Platform::getUserHomeDirectory()
- {
- char *home = getenv( "HOME" );
- return StringTable->insert( home );
- }
- StringTableEntry Platform::getExecutablePath()
- {
- if( !sBinPathName[0] )
- {
- const char *cpath;
- if( (cpath = torque_getexecutablepath()) )
- {
- dStrncpy(sBinPathName, cpath, sizeof(sBinPathName));
- chdir(sBinPathName);
- }
- else
- {
- getcwd(sBinPathName, sizeof(sBinPathName)-1);
- }
- }
- return StringTable->insert(sBinPathName);
- }
- //-----------------------------------------------------------------------------
- bool Platform::isFile(const char *pFilePath)
- {
- if (!pFilePath || !*pFilePath)
- return false;
- // Get file info
- struct stat fStat;
- if (stat(pFilePath, &fStat) < 0)
- {
- // Since file does not exist on disk see if it exists in a zip file loaded
- return Torque::FS::IsFile(pFilePath);
- }
- // if the file is a "regular file" then true
- if ( (fStat.st_mode & S_IFMT) == S_IFREG)
- return true;
- // must be some other file (directory, device, etc.)
- return false;
- }
- //-----------------------------------------------------------------------------
- S32 Platform::getFileSize(const char *pFilePath)
- {
- if (!pFilePath || !*pFilePath)
- return -1;
- // Get the file info
- struct stat fStat;
- if (stat(pFilePath, &fStat) < 0)
- return -1;
- // if the file is a "regular file" then return the size
- if ( (fStat.st_mode & S_IFMT) == S_IFREG)
- return fStat.st_size;
- // Must be something else or we can't read the file.
- return -1;
- }
- //-----------------------------------------------------------------------------
- bool Platform::isDirectory(const char *pDirPath)
- {
- if (!pDirPath || !*pDirPath)
- return false;
- // Get file info
- struct stat fStat;
- if (stat(pDirPath, &fStat) < 0)
- return false;
- // if the file is a Directory then true
- if ( (fStat.st_mode & S_IFMT) == S_IFDIR)
- return true;
- return false;
- }
- //-----------------------------------------------------------------------------
- bool Platform::isSubDirectory(const char *pParent, const char *pDir)
- {
- if (!pParent || !*pDir)
- return false;
- // this is somewhat of a brute force method but we need to be 100% sure
- // that the user cannot enter things like ../dir or /dir etc,...
- DIR *directory;
- directory = opendir(pParent);
- if (directory == NULL)
- return false;
- struct dirent *fEntry;
- fEntry = readdir(directory);
- if ( fEntry == NULL )
- {
- closedir(directory);
- return false;
- }
- do
- {
- char dirBuf[MaxPath];
- struct stat fStat;
- dSprintf(dirBuf, sizeof(dirBuf), "%s/%s", pParent, fEntry->d_name);
- if (stat(dirBuf, &fStat) < 0)
- continue;
- // if it is a directory...
- if ( (fStat.st_mode & S_IFMT) == S_IFDIR)
- {
- // and the names match
- if (dStrcmp(pDir, fEntry->d_name ) == 0)
- {
- // then we have a real sub directory
- closedir(directory);
- return true;
- }
- }
- } while( (fEntry = readdir(directory)) != NULL );
- closedir(directory);
- return false;
- }
- //-----------------------------------------------------------------------------
- // This is untested -- BJG
- bool Platform::fileTimeToString(FileTime * time, char * string, U32 strLen)
- {
- if(!time || !string)
- return(false);
- dSprintf(string, strLen, "%ld", *time);
- return(true);
- }
- bool Platform::stringToFileTime(const char * string, FileTime * time)
- {
- if(!time || !string)
- return(false);
- *time = dAtoi(string);
- return(true);
- }
- bool Platform::hasSubDirectory(const char *pPath)
- {
- if (!pPath)
- return false;
- struct dirent *d;
- DIR *dip;
- dip = opendir(pPath);
- if (dip == NULL)
- return false;
- while ((d = readdir(dip)))
- {
- bool isDir = false;
- if (d->d_type == DT_UNKNOWN)
- {
- char child [1024];
- if ((pPath[dStrlen(pPath) - 1] == '/'))
- dSprintf(child, 1024, "%s%s", pPath, d->d_name);
- else
- dSprintf(child, 1024, "%s/%s", pPath, d->d_name);
- isDir = Platform::isDirectory (child);
- }
- else if (d->d_type & DT_DIR)
- isDir = true;
- if( isDir )
- {
- // Skip the . and .. directories
- if (dStrcmp(d->d_name, ".") == 0 ||dStrcmp(d->d_name, "..") == 0)
- continue;
- if (Platform::isExcludedDirectory(d->d_name))
- continue;
- Platform::clearExcludedDirectories();
- closedir(dip);
- return true;
- }
- }
- closedir(dip);
- Platform::clearExcludedDirectories();
- return false;
- }
- bool Platform::fileDelete(const char * name)
- {
- return ModifyFile(name, DELETE);
- }
- static bool recurseDumpDirectories(const char *basePath, const char *subPath, Vector<StringTableEntry> &directoryVector, S32 currentDepth, S32 recurseDepth, bool noBasePath)
- {
- char Path[1024];
- DIR *dip;
- struct dirent *d;
- dsize_t trLen = basePath ? dStrlen(basePath) : 0;
- dsize_t subtrLen = subPath ? dStrlen(subPath) : 0;
- char trail = trLen > 0 ? basePath[trLen - 1] : '\0';
- char subTrail = subtrLen > 0 ? subPath[subtrLen - 1] : '\0';
- char subLead = subtrLen > 0 ? subPath[0] : '\0';
- if (trail == '/')
- {
- if (subPath && (dStrncmp(subPath, "", 1) != 0))
- {
- if (subTrail == '/')
- dSprintf(Path, 1024, "%s%s", basePath, subPath);
- else
- dSprintf(Path, 1024, "%s%s/", basePath, subPath);
- }
- else
- dSprintf(Path, 1024, "%s", basePath);
- }
- else
- {
- if (subPath && (dStrncmp(subPath, "", 1) != 0))
- {
- if (subTrail == '/')
- dSprintf(Path, 1024, "%s%s", basePath, subPath);
- else
- dSprintf(Path, 1024, "%s%s/", basePath, subPath);
- }
- else
- dSprintf(Path, 1024, "%s/", basePath);
- }
- dip = opendir(Path);
- if (dip == NULL)
- return false;
- //////////////////////////////////////////////////////////////////////////
- // add path to our return list ( provided it is valid )
- //////////////////////////////////////////////////////////////////////////
- if (!Platform::isExcludedDirectory(subPath))
- {
- if (noBasePath)
- {
- // We have a path and it's not an empty string or an excluded directory
- if ( (subPath && (dStrncmp (subPath, "", 1) != 0)) )
- directoryVector.push_back(StringTable->insert(subPath));
- }
- else
- {
- if ( (subPath && (dStrncmp(subPath, "", 1) != 0)) )
- {
- char szPath[1024];
- dMemset(szPath, 0, 1024);
- if (trail == '/')
- {
- if ((basePath[dStrlen(basePath) - 1]) != '/')
- dSprintf(szPath, 1024, "%s%s", basePath, &subPath[1]);
- else
- dSprintf(szPath, 1024, "%s%s", basePath, subPath);
- }
- else
- {
- if ((basePath[dStrlen(basePath) - 1]) != '/')
- dSprintf(szPath, 1024, "%s%s", basePath, subPath);
- else
- dSprintf(szPath, 1024, "%s/%s", basePath, subPath);
- }
- directoryVector.push_back(StringTable->insert(szPath));
- }
- else
- directoryVector.push_back(StringTable->insert(basePath));
- }
- }
- //////////////////////////////////////////////////////////////////////////
- // Iterate through and grab valid directories
- //////////////////////////////////////////////////////////////////////////
- while ((d = readdir(dip)))
- {
- bool isDir;
- isDir = false;
- if (d->d_type == DT_UNKNOWN)
- {
- char child [1024];
- if (Path[dStrlen(Path) - 1] == '/')
- dSprintf(child, 1024, "%s%s", Path, d->d_name);
- else
- dSprintf(child, 1024, "%s/%s", Path, d->d_name);
- isDir = Platform::isDirectory (child);
- }
- else if (d->d_type & DT_DIR)
- isDir = true;
- if ( isDir )
- {
- if (dStrcmp(d->d_name, ".") == 0 ||
- dStrcmp(d->d_name, "..") == 0)
- continue;
- if (Platform::isExcludedDirectory(d->d_name))
- continue;
- if ( (subPath && (dStrncmp(subPath, "", 1) != 0)) )
- {
- char child[1024];
- if ((subPath[dStrlen(subPath) - 1] == '/'))
- dSprintf(child, 1024, "%s%s", subPath, d->d_name);
- else
- dSprintf(child, 1024, "%s/%s", subPath, d->d_name);
- if (currentDepth < recurseDepth || recurseDepth == -1 )
- recurseDumpDirectories(basePath, child, directoryVector,
- currentDepth + 1, recurseDepth,
- noBasePath);
- }
- else
- {
- char child[1024];
- if ( (basePath[dStrlen(basePath) - 1]) == '/')
- dStrcpy (child, d->d_name, 1024);
- else
- dSprintf(child, 1024, "/%s", d->d_name);
- if (currentDepth < recurseDepth || recurseDepth == -1)
- recurseDumpDirectories(basePath, child, directoryVector,
- currentDepth + 1, recurseDepth,
- noBasePath);
- }
- }
- }
- closedir(dip);
- return true;
- }
- bool Platform::dumpDirectories(const char *path, Vector<StringTableEntry> &directoryVector, S32 depth, bool noBasePath)
- {
- #ifdef TORQUE_POSIX_PATH_CASE_INSENSITIVE
- dsize_t pathLength = dStrlen(path);
- char* caseSensitivePath = new char[pathLength + 1];
- // Load path into temporary buffer
- dMemcpy(caseSensitivePath, path, pathLength);
- caseSensitivePath[pathLength] = 0x00;
- ResolvePathCaseInsensitive(caseSensitivePath, pathLength, false);
- #else
- const char* caseSensitivePath = path;
- #endif
- bool retVal = recurseDumpDirectories(caseSensitivePath, "", directoryVector, -1, depth, noBasePath);
- clearExcludedDirectories();
- #ifdef TORQUE_POSIX_PATH_CASE_INSENSITIVE
- delete[] caseSensitivePath;
- #endif
- return retVal;
- }
- StringTableEntry Platform::getExecutableName()
- {
- return StringTable->insert(sBinName);
- }
- extern "C"
- {
- void setExePathName(const char* exePathName)
- {
- if (exePathName == NULL)
- sBinPathName[0] = '\0';
- else
- dStrncpy(sBinPathName, exePathName, sizeof(sBinPathName));
- // set the base exe name field
- char *binName = dStrrchr(sBinPathName, '/');
- if( !binName )
- binName = sBinPathName;
- else
- *binName++ = '\0';
- sBinName = binName;
- }
- }
- #endif
|