1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261 |
- //-----------------------------------------------------------------------------
- // 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.
- */
- // evil hack to get around insane X windows #define-happy header files
- #ifdef Status
- #undef Status
- #endif
- #include "platformX86UNIX/platformX86UNIX.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"
- #if defined(__FreeBSD__)
- #include <sys/types.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>
- 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);
- const int MaxPath = PATH_MAX;
- 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;
- }
- }
|