Torque3D Documentation / _generateds / x86UNIXFileio.cpp

x86UNIXFileio.cpp

Engine/source/platformX86UNIX/x86UNIXFileio.cpp

More...

Public Enumerations

enum
_Anonymous_ {
  TOUCH 
  DELETE 
}

Public Variables

char
sgPrefDir [MaxPath]

Public Functions

bool
CopyFile(const char * src, const char * dest)
bool
dFileDelete(const char * name)
bool
dFileRename(const char * oldName, const char * newName)
bool
dFileTouch(const char * name)
bool
DirExists(char * pathname, bool isFile)
bool
dPathCopy(const char * fromName, const char * toName, bool nooverwrite)
ForwardSlash(char * str)
bool
GetFileTimes(const char * filePath, FileTime * createTime, FileTime * modifyTime)
const char *
bool
ModifyFile(const char * name, S32 modType)
MungeCase(char * pathName, S32 pathNameSize)
MungePath(char * dest, S32 destSize, const char * src, const char * absolutePrefix)
bool
recurseDumpDirectories(const char * basePath, const char * subPath, Vector< StringTableEntry > & directoryVector, S32 currentDepth, S32 recurseDepth, bool noBasePath)
bool
RecurseDumpPath(const char * path, const char * relativePath, const char * pattern, Vector< Platform::FileInfo > & fileVector, S32 currentDepth, S32 recurseDepth)
setExePathName(const char * exePathName)
int
x86UNIXClose(int fd)
int
x86UNIXOpen(const char * path, int oflag)
ssize_t
x86UNIXRead(int fd, void * buf, size_t nbytes)
ssize_t
x86UNIXWrite(int fd, const void * buf, size_t nbytes)

Detailed Description

Public Enumerations

@148

Enumerator

TOUCH
DELETE

Public Variables

const int MaxPath 
char sgPrefDir [MaxPath]
bool sgPrefDirInitialized 

Public Functions

CopyFile(const char * src, const char * dest)

dFileDelete(const char * name)

dFileRename(const char * oldName, const char * newName)

dFileTouch(const char * name)

DirExists(char * pathname, bool isFile)

dPathCopy(const char * fromName, const char * toName, bool nooverwrite)

ForwardSlash(char * str)

GetFileTimes(const char * filePath, FileTime * createTime, FileTime * modifyTime)

GetPrefDir()

ModifyFile(const char * name, S32 modType)

MungeCase(char * pathName, S32 pathNameSize)

MungePath(char * dest, S32 destSize, const char * src, const char * absolutePrefix)

osGetTemporaryDirectory()

recurseDumpDirectories(const char * basePath, const char * subPath, Vector< StringTableEntry > & directoryVector, S32 currentDepth, S32 recurseDepth, bool noBasePath)

RecurseDumpPath(const char * path, const char * relativePath, const char * pattern, Vector< Platform::FileInfo > & fileVector, S32 currentDepth, S32 recurseDepth)

setExePathName(const char * exePathName)

x86UNIXClose(int fd)

x86UNIXOpen(const char * path, int oflag)

x86UNIXRead(int fd, void * buf, size_t nbytes)

x86UNIXWrite(int fd, const void * buf, size_t nbytes)

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24 /* JMQ:
  25
  26    Here's the scoop on unix file IO.  The windows platform makes some
  27    assumptions about fileio: 1) the file system is case-insensitive, and
  28    2) the platform can write to the directory in which
  29    the game is running.  Both of these are usually false on linux.  So, to
  30    compensate, we "route" created files and directories to the user's home
  31    directory (see GetPrefPath()).  When a file is to be accessed, the code
  32    looks in the home directory first.  If the file is not found there and the
  33    open mode is read only, the code will look in the game installation
  34    directory.  Files are never created or modified in the game directory.
  35
  36    For case-sensitivity, the MungePath code will test whether a given path
  37    specified by the engine exists.  If not, it will use the MungeCase function
  38    which will try to determine if an actual filesystem path matches the
  39    specified path case insensitive.  If one is found, the actual path
  40    transparently (we hope) replaces the one requested by the engine.
  41
  42    The preference directory is global to all torque executables with the same
  43    name.  You should make sure you keep it clean if you build from multiple
  44    torque development trees.
  45 */
  46
  47 // evil hack to get around insane X windows #define-happy header files
  48 #ifdef Status
  49 #undef Status
  50 #endif
  51
  52 #include "platformX86UNIX/platformX86UNIX.h"
  53 #include "core/fileio.h"
  54 #include "core/util/tVector.h"
  55 #include "core/stringTable.h"
  56 #include "console/console.h"
  57 #include "core/strings/stringFunctions.h"
  58 #include "util/tempAlloc.h"
  59 #include "cinterface/c_controlInterface.h"
  60 #include "core/volume.h"
  61
  62 #if defined(__FreeBSD__)
  63    #include <sys/types.h>
  64 #endif
  65 #include <utime.h>
  66
  67 /* these are for reading directors, getting stats, etc. */
  68 #include <dirent.h>
  69 #include <sys/types.h>
  70 #include <sys/stat.h>
  71 #include <unistd.h>
  72 #include <fcntl.h>
  73 #include <errno.h>
  74 #include <stdlib.h>
  75
  76 extern int x86UNIXOpen(const char *path, int oflag);
  77 extern int x86UNIXClose(int fd);
  78 extern ssize_t x86UNIXRead(int fd, void *buf, size_t nbytes);
  79 extern ssize_t x86UNIXWrite(int fd, const void *buf, size_t nbytes);
  80
  81 const int MaxPath = PATH_MAX;
  82
  83 namespace
  84 {
  85   const char sTempDir[] = "/tmp/";
  86
  87   static char sBinPathName[MaxPath] = "";
  88   static char *sBinName = sBinPathName;
  89   bool sUseRedirect = true;
  90 }
  91
  92 StringTableEntry osGetTemporaryDirectory()
  93 {
  94    return StringTable->insert(sTempDir);
  95 }
  96
  97 // Various handy utility functions:
  98 //------------------------------------------------------------------------------
  99 // find all \ in a path and convert them in place to /
 100 static void ForwardSlash(char *str)
 101 {
 102    while(*str)
 103    {
 104       if(*str == '\\')
 105          *str = '/';
 106       str++;
 107    }
 108 }
 109
 110 //------------------------------------------------------------------------------
 111 // copy a file from src to dest
 112 static bool CopyFile(const char* src, const char* dest)
 113 {
 114    S32 srcFd = x86UNIXOpen(src, O_RDONLY);
 115    S32 destFd = x86UNIXOpen(dest, O_WRONLY | O_CREAT | O_TRUNC);
 116    bool error = false;
 117
 118    if (srcFd != -1 && destFd != -1)
 119    {
 120       const int BufSize = 8192;
 121       char buf[BufSize];
 122       S32 bytesRead = 0;
 123       while ((bytesRead = x86UNIXRead(srcFd, buf, BufSize)) > 0)
 124       {
 125          // write data
 126          if (x86UNIXWrite(destFd, buf, bytesRead) == -1)
 127          {
 128             error = true;
 129             break;
 130          }
 131       }
 132
 133       if (bytesRead == -1)
 134          error = true;
 135    }
 136
 137    if (srcFd != -1)
 138       x86UNIXClose(srcFd);
 139    if (destFd != -1)
 140       x86UNIXClose(destFd);
 141
 142    if (error)
 143    {
 144       Con::errorf("Error copying file: %s, %s", src, dest);
 145       remove(dest);
 146    }
 147    return error;
 148 }
 149
 150bool dPathCopy(const char *fromName, const char *toName, bool nooverwrite)
 151{
 152   return CopyFile(fromName,toName);
 153}
 154
 155 //-----------------------------------------------------------------------------
 156 static char sgPrefDir[MaxPath];
 157 static bool sgPrefDirInitialized = false;
 158
 159 // get the "pref dir", which is where game output files are stored.  the pref
 160 // dir is ~/PREF_DIR_ROOT/PREF_DIR_GAME_NAME
 161 static const char* GetPrefDir()
 162 {
 163    if (sgPrefDirInitialized)
 164       return sgPrefDir;
 165
 166    if (sUseRedirect)
 167    {
 168       const char *home = getenv("HOME");
 169       AssertFatal(home, "HOME environment variable must be set");
 170
 171       dSprintf(sgPrefDir, MaxPath, "%s/%s/%s",
 172          home, PREF_DIR_ROOT, PREF_DIR_GAME_NAME);
 173    }
 174    else
 175    {
 176       getcwd(sgPrefDir, MaxPath);
 177    }
 178
 179    sgPrefDirInitialized = true;
 180    return sgPrefDir;
 181 }
 182
 183 //------------------------------------------------------------------------------
 184 // munge the case of the specified pathName.  This means try to find the actual
 185 // filename in with case-insensitive matching on the specified pathName, and
 186 // store the actual found name.
 187 static void MungeCase(char* pathName, S32 pathNameSize)
 188 {
 189    char tempBuf[MaxPath];
 190    dStrncpy(tempBuf, pathName, pathNameSize);
 191
 192    AssertFatal(pathName[0] == '/', "PATH must be absolute");
 193
 194    struct stat filestat;
 195    const int MaxPathEl = 200;
 196    char *currChar = pathName;
 197    char testPath[MaxPath];
 198    char pathEl[MaxPathEl];
 199    bool done = false;
 200
 201    dStrncpy(tempBuf, "/", MaxPath);
 202    currChar++;
 203
 204    while (!done)
 205    {
 206       char* termChar = dStrchr(currChar, '/');
 207       if (termChar == NULL)
 208          termChar = dStrchr(currChar, '\0');
 209       AssertFatal(termChar, "Can't find / or NULL terminator");
 210
 211       S32 pathElLen = (termChar - currChar);
 212       dStrncpy(pathEl, currChar, pathElLen);
 213       pathEl[pathElLen] = '\0';
 214       dStrncpy(testPath, tempBuf, MaxPath);
 215       dStrcat(testPath, pathEl, MaxPath);
 216       if (stat(testPath, &filestat) != -1)
 217       {
 218          dStrncpy(tempBuf, testPath, MaxPath);
 219       }
 220       else
 221       {
 222          DIR *dir = opendir(tempBuf);
 223          struct dirent* ent;
 224          bool foundMatch = false;
 225          while (dir != NULL && (ent = readdir(dir)) != NULL)
 226          {
 227             if (dStricmp(pathEl, ent->d_name) == 0)
 228             {
 229                foundMatch = true;
 230                dStrcat(tempBuf, ent->d_name, MaxPath);
 231                break;
 232             }
 233          }
 234
 235          if (!foundMatch)
 236             dStrncpy(tempBuf, testPath, MaxPath);
 237          if (dir)
 238             closedir(dir);
 239       }
 240       if (*termChar == '/')
 241       {
 242          dStrcat(tempBuf, "/", MaxPath);
 243          termChar++;
 244          currChar = termChar;
 245       }
 246       else
 247          done = true;
 248    }
 249
 250    dStrncpy(pathName, tempBuf, pathNameSize);
 251 }
 252
 253 //-----------------------------------------------------------------------------
 254 // Returns true if the pathname exists, false otherwise.  If isFile is true,
 255 // the pathname is assumed to be a file path, and only the directory part
 256 // will be examined (everything before last /)
 257 bool DirExists(char* pathname, bool isFile)
 258 {
 259    static char testpath[20000];
 260    dStrncpy(testpath, pathname, sizeof(testpath));
 261    if (isFile)
 262    {
 263       // find the last / and make it into null
 264       char* lastSlash = dStrrchr(testpath, '/');
 265       if (lastSlash != NULL)
 266          *lastSlash = 0;
 267    }
 268    return Platform::isDirectory(testpath);
 269 }
 270
 271 //-----------------------------------------------------------------------------
 272 // Munge the specified path.
 273 static void MungePath(char* dest, S32 destSize,
 274    const char* src, const char* absolutePrefix)
 275 {
 276    char tempBuf[MaxPath];
 277    dStrncpy(dest, src, MaxPath);
 278
 279    // translate all \ to /
 280    ForwardSlash(dest);
 281
 282    // if it is relative, make it absolute with the absolutePrefix
 283    if (dest[0] != '/')
 284    {
 285       AssertFatal(absolutePrefix, "Absolute Prefix must not be NULL");
 286
 287       dSprintf(tempBuf, MaxPath, "%s/%s",
 288          absolutePrefix, dest);
 289
 290       // copy the result back into dest
 291       dStrncpy(dest, tempBuf, destSize);
 292    }
 293
 294    // if the path exists, we're done
 295    struct stat filestat;
 296    if (stat(dest, &filestat) != -1)
 297       return;
 298
 299    // otherwise munge the case of the path
 300    MungeCase(dest, destSize);
 301 }
 302
 303 //-----------------------------------------------------------------------------
 304 enum
 305 {
 306    TOUCH,
 307    DELETE
 308 };
 309
 310 //-----------------------------------------------------------------------------
 311 // perform a modification on the specified file.  allowed modifications are
 312 // specified in the enum above.
 313 bool ModifyFile(const char * name, S32 modType)
 314 {
 315    if(!name || (dStrlen(name) >= MaxPath) || dStrstr(name, "../") != NULL)
 316       return(false);
 317
 318    // if its absolute skip it
 319    if (name[0]=='/' || name[0]=='\\')
 320       return(false);
 321
 322    // only modify files in home directory
 323    char prefPathName[MaxPath];
 324    MungePath(prefPathName, MaxPath, name, GetPrefDir());
 325
 326    if (modType == TOUCH)
 327       return(utime(prefPathName, 0) != -1);
 328    else if (modType == DELETE)
 329       return (remove(prefPathName) == 0);
 330    else
 331       AssertFatal(false, "Unknown File Mod type");
 332    return false;
 333 }
 334
 335 //-----------------------------------------------------------------------------
 336 static bool RecurseDumpPath(const char *path, const char* relativePath, const char *pattern, Vector<Platform::FileInfo> &fileVector, S32 currentDepth = 0, S32 recurseDepth = -1)
 337 {
 338    char search[1024];
 339
 340    dSprintf(search, sizeof(search), "%s", path, pattern);
 341
 342    DIR *directory = opendir(search);
 343
 344    if (directory == NULL)
 345       return false;
 346
 347    struct dirent *fEntry;
 348    fEntry = readdir(directory);    // read the first "file" in the directory
 349
 350    if (fEntry == NULL)
 351    {
 352       closedir(directory);
 353       return false;
 354    }
 355
 356    do
 357    {
 358       char filename[BUFSIZ+1];
 359       struct stat fStat;
 360
 361       dSprintf(filename, sizeof(filename), "%s/%s", search, fEntry->d_name); // "construct" the file name
 362       stat(filename, &fStat); // get the file stats
 363
 364       if ( (fStat.st_mode & S_IFMT) == S_IFDIR )
 365       {
 366          // Directory
 367          // skip . and .. directories
 368          if (dStrcmp(fEntry->d_name, ".") == 0 || dStrcmp(fEntry->d_name, "..") == 0)
 369             continue;
 370
 371      // skip excluded directories
 372      if( Platform::isExcludedDirectory(fEntry->d_name))
 373         continue;
 374
 375
 376          char child[MaxPath];
 377          dSprintf(child, sizeof(child), "%s/%s", path, fEntry->d_name);
 378          char* childRelative = NULL;
 379          char childRelativeBuf[MaxPath];
 380          if (relativePath)
 381          {
 382             dSprintf(childRelativeBuf, sizeof(childRelativeBuf), "%s/%s",
 383                relativePath, fEntry->d_name);
 384             childRelative = childRelativeBuf;
 385          }
 386          if (currentDepth < recurseDepth || recurseDepth == -1 )
 387            RecurseDumpPath(child, childRelative, pattern, fileVector, currentDepth+1, recurseDepth);
 388       }
 389       else
 390       {
 391          // File
 392
 393          // add it to the list
 394          fileVector.increment();
 395          Platform::FileInfo& rInfo = fileVector.last();
 396
 397          if (relativePath)
 398             rInfo.pFullPath = StringTable->insert(relativePath);
 399          else
 400             rInfo.pFullPath = StringTable->insert(path);
 401          rInfo.pFileName = StringTable->insert(fEntry->d_name);
 402          rInfo.fileSize  = fStat.st_size;
 403          //dPrintf("Adding file: %s/%s\n", rInfo.pFullPath, rInfo.pFileName);
 404       }
 405
 406    } while( (fEntry = readdir(directory)) != NULL );
 407
 408    closedir(directory);
 409    return true;
 410 }
 411
 412 //-----------------------------------------------------------------------------
 413 bool dFileDelete(const char * name)
 414 {
 415    return ModifyFile(name, DELETE);
 416 }
 417
 418 //-----------------------------------------------------------------------------
 419 bool dFileTouch(const char * name)
 420 {
 421    return ModifyFile(name, TOUCH);
 422 }
 423
 424 bool dFileRename(const char *oldName, const char *newName)
 425 {
 426    AssertFatal( oldName != NULL && newName != NULL, "dFileRename - NULL file name" );
 427
 428    // only modify files in home directory
 429    TempAlloc<char> oldPrefPathName(MaxPath);
 430    TempAlloc<char> newPrefPathName(MaxPath);
 431    MungePath(oldPrefPathName, MaxPath, oldName, GetPrefDir());
 432    MungePath(newPrefPathName, MaxPath, newName, GetPrefDir());
 433
 434    return rename(oldPrefPathName, newPrefPathName) == 0;
 435 }
 436
 437 //-----------------------------------------------------------------------------
 438 // Constructors & Destructor
 439 //-----------------------------------------------------------------------------
 440
 441 //-----------------------------------------------------------------------------
 442 // After construction, the currentStatus will be Closed and the capabilities
 443 // will be 0.
 444 //-----------------------------------------------------------------------------
 445 File::File()
 446 : currentStatus(Closed), capability(0)
 447 {
 448 //    AssertFatal(sizeof(int) == sizeof(void *), "File::File: cannot cast void* to int");
 449
 450     handle = (void *)NULL;
 451 }
 452
 453 //-----------------------------------------------------------------------------
 454 // insert a copy constructor here... (currently disabled)
 455 //-----------------------------------------------------------------------------
 456
 457 //-----------------------------------------------------------------------------
 458 // Destructor
 459 //-----------------------------------------------------------------------------
 460 File::~File()
 461 {
 462     close();
 463     handle = (void *)NULL;
 464 }
 465
 466 //-----------------------------------------------------------------------------
 467 // Open a file in the mode specified by openMode (Read, Write, or ReadWrite).
 468 // Truncate the file if the mode is either Write or ReadWrite and truncate is
 469 // true.
 470 //
 471 // Sets capability appropriate to the openMode.
 472 // Returns the currentStatus of the file.
 473 //-----------------------------------------------------------------------------
 474 File::FileStatus File::open(const char *filename, const AccessMode openMode)
 475 {
 476    AssertFatal(NULL != filename, "File::open: NULL filename");
 477    AssertWarn(NULL == handle, "File::open: handle already valid");
 478
 479    // Close the file if it was already open...
 480    if (Closed != currentStatus)
 481       close();
 482
 483    char prefPathName[MaxPath];
 484    char gamePathName[MaxPath];
 485    char cwd[MaxPath];
 486    getcwd(cwd, MaxPath);
 487    MungePath(prefPathName, MaxPath, filename, GetPrefDir());
 488    MungePath(gamePathName, MaxPath, filename, cwd);
 489
 490    int oflag;
 491    struct stat filestat;
 492    handle = (void *)dRealMalloc(sizeof(int));
 493
 494    switch (openMode)
 495    {
 496       case Read:
 497          oflag = O_RDONLY;
 498          break;
 499       case Write:
 500          oflag = O_WRONLY | O_CREAT | O_TRUNC;
 501          break;
 502       case ReadWrite:
 503          oflag = O_RDWR | O_CREAT;
 504          // if the file does not exist copy it before reading/writing
 505          if (stat(prefPathName, &filestat) == -1)
 506             bool ret = CopyFile(gamePathName, prefPathName);
 507          break;
 508       case WriteAppend:
 509          oflag = O_WRONLY | O_CREAT | O_APPEND;
 510          // if the file does not exist copy it before appending
 511          if (stat(prefPathName, &filestat) == -1)
 512              bool ret = CopyFile(gamePathName, prefPathName);
 513          break;
 514       default:
 515          AssertFatal(false, "File::open: bad access mode");    // impossible
 516    }
 517
 518    // if we are writing, make sure output path exists
 519    if (openMode == Write || openMode == ReadWrite || openMode == WriteAppend)
 520        Platform::createPath(prefPathName);
 521
 522    int fd = -1;
 523    fd = x86UNIXOpen(prefPathName, oflag);
 524    if (fd == -1 && openMode == Read)
 525       // for read only files we can use the gamePathName
 526       fd = x86UNIXOpen(gamePathName, oflag);
 527
 528    dMemcpy(handle, &fd, sizeof(int));
 529
 530 #ifdef DEBUG
 531 //   fprintf(stdout,"fd = %d, handle = %d\n", fd, *((int *)handle));
 532 #endif
 533
 534    if (*((int *)handle) == -1)
 535    {
 536       // handle not created successfully
 537       Con::errorf("Can't open file: %s", filename);
 538       return setStatus();
 539    }
 540    else
 541    {
 542       // successfully created file, so set the file capabilities...
 543       switch (openMode)
 544       {
 545          case Read:
 546             capability = U32(FileRead);
 547             break;
 548          case Write:
 549          case WriteAppend:
 550             capability = U32(FileWrite);
 551             break;
 552          case ReadWrite:
 553             capability = U32(FileRead)  |
 554                U32(FileWrite);
 555             break;
 556          default:
 557             AssertFatal(false, "File::open: bad access mode");
 558       }
 559       return currentStatus = Ok;                                // success!
 560    }
 561 }
 562
 563 //-----------------------------------------------------------------------------
 564 // Get the current position of the file pointer.
 565 //-----------------------------------------------------------------------------
 566 U32 File::getPosition() const
 567 {
 568     AssertFatal(Closed != currentStatus, "File::getPosition: file closed");
 569     AssertFatal(NULL != handle, "File::getPosition: invalid file handle");
 570
 571 #ifdef DEBUG
 572 //   fprintf(stdout, "handle = %d\n",*((int *)handle));fflush(stdout);
 573 #endif
 574     return (U32) lseek(*((int *)handle), 0, SEEK_CUR);
 575 }
 576
 577 //-----------------------------------------------------------------------------
 578 // Set the position of the file pointer.
 579 // Absolute and relative positioning is supported via the absolutePos
 580 // parameter.
 581 //
 582 // If positioning absolutely, position MUST be positive - an IOError results if
 583 // position is negative.
 584 // Position can be negative if positioning relatively, however positioning
 585 // before the start of the file is an IOError.
 586 //
 587 // Returns the currentStatus of the file.
 588 //-----------------------------------------------------------------------------
 589 File::FileStatus File::setPosition(S32 position, bool absolutePos)
 590 {
 591     AssertFatal(Closed != currentStatus, "File::setPosition: file closed");
 592     AssertFatal(NULL != handle, "File::setPosition: invalid file handle");
 593
 594     if (Ok != currentStatus && EOS != currentStatus)
 595         return currentStatus;
 596
 597     U32 finalPos = 0;
 598     switch (absolutePos)
 599     {
 600     case true:                                                    // absolute position
 601         AssertFatal(0 <= position, "File::setPosition: negative absolute position");
 602
 603         // position beyond EOS is OK
 604         finalPos = lseek(*((int *)handle), position, SEEK_SET);
 605         break;
 606     case false:                                                    // relative position
 607         AssertFatal((getPosition() >= (U32)abs(position) && 0 > position) || 0 <= position, "File::setPosition: negative relative position");
 608
 609         // position beyond EOS is OK
 610         finalPos = lseek(*((int *)handle), position, SEEK_CUR);
 611   break;
 612     }
 613
 614     if (0xffffffff == finalPos)
 615         return setStatus();                                        // unsuccessful
 616     else if (finalPos >= getSize())
 617         return currentStatus = EOS;                                // success, at end of file
 618     else
 619         return currentStatus = Ok;                                // success!
 620 }
 621
 622 //-----------------------------------------------------------------------------
 623 // Get the size of the file in bytes.
 624 // It is an error to query the file size for a Closed file, or for one with an
 625 // error status.
 626 //-----------------------------------------------------------------------------
 627 U32 File::getSize() const
 628 {
 629     AssertWarn(Closed != currentStatus, "File::getSize: file closed");
 630     AssertFatal(NULL != handle, "File::getSize: invalid file handle");
 631
 632     if (Ok == currentStatus || EOS == currentStatus)
 633     {
 634   long  currentOffset = getPosition();                  // keep track of our current position
 635   long  fileSize;
 636   lseek(*((int *)handle), 0, SEEK_END);                     // seek to the end of the file
 637   fileSize = getPosition();                               // get the file size
 638   lseek(*((int *)handle), currentOffset, SEEK_SET);         // seek back to our old offset
 639         return fileSize;                                        // success!
 640     }
 641     else
 642         return 0;                                               // unsuccessful
 643 }
 644
 645 //-----------------------------------------------------------------------------
 646 // Flush the file.
 647 // It is an error to flush a read-only file.
 648 // Returns the currentStatus of the file.
 649 //-----------------------------------------------------------------------------
 650 File::FileStatus File::flush()
 651 {
 652     AssertFatal(Closed != currentStatus, "File::flush: file closed");
 653     AssertFatal(NULL != handle, "File::flush: invalid file handle");
 654     AssertFatal(true == hasCapability(FileWrite), "File::flush: cannot flush a read-only file");
 655
 656     if (fsync(*((int *)handle)) == 0)
 657         return currentStatus = Ok;                                // success!
 658     else
 659         return setStatus();                                       // unsuccessful
 660 }
 661
 662 //-----------------------------------------------------------------------------
 663 // Close the File.
 664 //
 665 // Returns the currentStatus
 666 //-----------------------------------------------------------------------------
 667 File::FileStatus File::close()
 668 {
 669    // if the handle is non-NULL, close it if necessary and free it
 670    if (NULL != handle)
 671    {
 672       // make a local copy of the handle value and
 673       // free the handle
 674       int handleVal = *((int *)handle);
 675       dRealFree(handle);
 676       handle = (void *)NULL;
 677
 678       // close the handle if it is valid
 679       if (handleVal != -1 && x86UNIXClose(handleVal) != 0)
 680          return setStatus();                                    // unsuccessful
 681    }
 682    // Set the status to closed
 683    return currentStatus = Closed;
 684 }
 685
 686 //-----------------------------------------------------------------------------
 687 // Self-explanatory.
 688 //-----------------------------------------------------------------------------
 689 File::FileStatus File::getStatus() const
 690 {
 691     return currentStatus;
 692 }
 693
 694 //-----------------------------------------------------------------------------
 695 // Sets and returns the currentStatus when an error has been encountered.
 696 //-----------------------------------------------------------------------------
 697 File::FileStatus File::setStatus()
 698 {
 699    Con::printf("File IO error: %s", strerror(errno));
 700    return currentStatus = IOError;
 701 }
 702
 703 //-----------------------------------------------------------------------------
 704 // Sets and returns the currentStatus to status.
 705 //-----------------------------------------------------------------------------
 706 File::FileStatus File::setStatus(File::FileStatus status)
 707 {
 708     return currentStatus = status;
 709 }
 710
 711 //-----------------------------------------------------------------------------
 712 // Read from a file.
 713 // The number of bytes to read is passed in size, the data is returned in src.
 714 // The number of bytes read is available in bytesRead if a non-Null pointer is
 715 // provided.
 716 //-----------------------------------------------------------------------------
 717 File::FileStatus File::read(U32 size, char *dst, U32 *bytesRead)
 718 {
 719 #ifdef DEBUG
 720 //   fprintf(stdout,"reading %d bytes\n",size);fflush(stdout);
 721 #endif
 722     AssertFatal(Closed != currentStatus, "File::read: file closed");
 723     AssertFatal(NULL != handle, "File::read: invalid file handle");
 724     AssertFatal(NULL != dst, "File::read: NULL destination pointer");
 725     AssertFatal(true == hasCapability(FileRead), "File::read: file lacks capability");
 726     AssertWarn(0 != size, "File::read: size of zero");
 727
 728 /* show stats for this file */
 729 #ifdef DEBUG
 730 //struct stat st;
 731 //fstat(*((int *)handle), &st);
 732 //fprintf(stdout,"file size = %d\n", st.st_size);
 733 #endif
 734 /****************************/
 735
 736     if (Ok != currentStatus || 0 == size)
 737         return currentStatus;
 738     else
 739     {
 740         long lastBytes;
 741         long *bytes = (NULL == bytesRead) ? &lastBytes : (long *)bytesRead;
 742         if ( (*((U32 *)bytes) = x86UNIXRead(*((int *)handle), dst, size)) == -1)
 743         {
 744 #ifdef DEBUG
 745 //   fprintf(stdout,"unsuccessful: %d\n", *((U32 *)bytes));fflush(stdout);
 746 #endif
 747            return setStatus();                                    // unsuccessful
 748         } else {
 749 //            dst[*((U32 *)bytes)] = '\0';
 750             if (*((U32 *)bytes) != size || *((U32 *)bytes) == 0) {
 751 #ifdef DEBUG
 752 //  fprintf(stdout,"end of stream: %d\n", *((U32 *)bytes));fflush(stdout);
 753 #endif
 754                 return currentStatus = EOS;                        // end of stream
 755             }
 756         }
 757     }
 758 //    dst[*bytesRead] = '\0';
 759 #ifdef DEBUG
 760 //fprintf(stdout, "We read:\n");
 761 //fprintf(stdout, "====================================================\n");
 762 //fprintf(stdout, "%s\n",dst);
 763 //fprintf(stdout, "====================================================\n");
 764 //fprintf(stdout,"read ok: %d\n", *bytesRead);fflush(stdout);
 765 #endif
 766     return currentStatus = Ok;                                    // successfully read size bytes
 767 }
 768
 769 //-----------------------------------------------------------------------------
 770 // Write to a file.
 771 // The number of bytes to write is passed in size, the data is passed in src.
 772 // The number of bytes written is available in bytesWritten if a non-Null
 773 // pointer is provided.
 774 //-----------------------------------------------------------------------------
 775 File::FileStatus File::write(U32 size, const char *src, U32 *bytesWritten)
 776 {
 777    // JMQ: despite the U32 parameters, the maximum filesize supported by this
 778    // function is probably the max value of S32, due to the unix syscall
 779    // api.
 780    AssertFatal(Closed != currentStatus, "File::write: file closed");
 781    AssertFatal(NULL != handle, "File::write: invalid file handle");
 782    AssertFatal(NULL != src, "File::write: NULL source pointer");
 783    AssertFatal(true == hasCapability(FileWrite), "File::write: file lacks capability");
 784    AssertWarn(0 != size, "File::write: size of zero");
 785
 786    if ((Ok != currentStatus && EOS != currentStatus) || 0 == size)
 787       return currentStatus;
 788    else
 789    {
 790       S32 numWritten = x86UNIXWrite(*((int *)handle), src, size);
 791       if (numWritten < 0)
 792          return setStatus();
 793
 794       if (bytesWritten)
 795          *bytesWritten = static_cast<U32>(numWritten);
 796       return currentStatus = Ok;
 797    }
 798 }
 799
 800 //-----------------------------------------------------------------------------
 801 // Self-explanatory.  JMQ: No explanation needed.  Move along.  These aren't
 802 // the droids you're looking for.
 803 //-----------------------------------------------------------------------------
 804 bool File::hasCapability(Capability cap) const
 805 {
 806     return (0 != (U32(cap) & capability));
 807 }
 808
 809 //-----------------------------------------------------------------------------
 810 S32 Platform::compareFileTimes(const FileTime &a, const FileTime &b)
 811 {
 812    if(a > b)
 813       return 1;
 814    if(a < b)
 815       return -1;
 816    return 0;
 817 }
 818
 819 //-----------------------------------------------------------------------------
 820 static bool GetFileTimes(const char *filePath, FileTime *createTime, FileTime *modifyTime)
 821 {
 822    struct stat fStat;
 823
 824    if (stat(filePath, &fStat) == -1)
 825       return false;
 826
 827    if(createTime)
 828    {
 829       // no where does SysV/BSD UNIX keep a record of a file's
 830       // creation time.  instead of creation time I'll just use
 831       // changed time for now.
 832       *createTime = fStat.st_ctime;
 833    }
 834    if(modifyTime)
 835    {
 836       *modifyTime = fStat.st_mtime;
 837    }
 838
 839    return true;
 840 }
 841
 842 //-----------------------------------------------------------------------------
 843 bool Platform::getFileTimes(const char *filePath, FileTime *createTime, FileTime *modifyTime)
 844 {
 845    char pathName[MaxPath];
 846
 847    // if it starts with cwd, we need to strip that off so that we can look for
 848    // the file in the pref dir
 849    char cwd[MaxPath];
 850    getcwd(cwd, MaxPath);
 851    if (dStrstr(filePath, cwd) == filePath)
 852       filePath = filePath + dStrlen(cwd) + 1;
 853
 854    // if its relative, first look in the pref dir
 855    if (filePath[0] != '/' && filePath[0] != '\\')
 856    {
 857       MungePath(pathName, MaxPath, filePath, GetPrefDir());
 858       if (GetFileTimes(pathName, createTime, modifyTime))
 859          return true;
 860    }
 861
 862    // here if the path is absolute or not in the pref dir
 863    MungePath(pathName, MaxPath, filePath, cwd);
 864    return GetFileTimes(pathName, createTime, modifyTime);
 865 }
 866
 867 //-----------------------------------------------------------------------------
 868 bool Platform::createPath(const char *file)
 869 {
 870    char pathbuf[MaxPath];
 871    const char *dir;
 872    pathbuf[0] = 0;
 873    U32 pathLen = 0;
 874
 875    // all paths should be created in home directory
 876    char prefPathName[MaxPath];
 877    MungePath(prefPathName, MaxPath, file, GetPrefDir());
 878    file = prefPathName;
 879
 880    // does the directory exist already?
 881    if (DirExists(prefPathName, true)) // true means that the path is a filepath
 882       return true;
 883
 884    while((dir = dStrchr(file, '/')) != NULL)
 885    {
 886       dStrncpy(pathbuf + pathLen, file, dir - file);
 887       pathbuf[pathLen + dir-file] = 0;
 888       bool ret = mkdir(pathbuf, 0700);
 889       pathLen += dir - file;
 890       pathbuf[pathLen++] = '/';
 891       file = dir + 1;
 892    }
 893    return true;
 894 }
 895
 896 // JMQ: Platform:cdFileExists in unimplemented
 897 //------------------------------------------------------------------------------
 898 // bool Platform::cdFileExists(const char *filePath, const char *volumeName,
 899 //    S32 serialNum)
 900 // {
 901 // }
 902
 903 //-----------------------------------------------------------------------------
 904 bool Platform::dumpPath(const char *path, Vector<Platform::FileInfo> &fileVector, int depth)
 905 {
 906    const char* pattern = "*";
 907
 908    // if it is not absolute, dump the pref dir first
 909    if (path[0] != '/' && path[0] != '\\')
 910    {
 911       char prefPathName[MaxPath];
 912       MungePath(prefPathName, MaxPath, path, GetPrefDir());
 913       RecurseDumpPath(prefPathName, path, pattern, fileVector, 0, depth);
 914    }
 915
 916    // munge the requested path and dump it
 917    char mungedPath[MaxPath];
 918    char cwd[MaxPath];
 919    getcwd(cwd, MaxPath);
 920    MungePath(mungedPath, MaxPath, path, cwd);
 921    return RecurseDumpPath(mungedPath, path, pattern, fileVector, 0, depth);
 922 }
 923
 924 //-----------------------------------------------------------------------------
 925 StringTableEntry Platform::getCurrentDirectory()
 926 {
 927    char cwd_buf[2048];
 928    getcwd(cwd_buf, 2047);
 929    return StringTable->insert(cwd_buf);
 930 }
 931
 932 //-----------------------------------------------------------------------------
 933 bool Platform::setCurrentDirectory(StringTableEntry newDir)
 934 {
 935    if (Platform::getWebDeployment())
 936       return true;
 937
 938    TempAlloc< UTF8> buf( dStrlen( newDir ) + 2 );
 939
 940    dStrcpy( buf, newDir, buf.size );
 941
 942    ForwardSlash( buf );
 943    return chdir( buf ) == 0;
 944 }
 945
 946 //-----------------------------------------------------------------------------
 947 const char *Platform::getUserDataDirectory()
 948 {
 949    return StringTable->insert( GetPrefDir() );
 950 }
 951
 952 //-----------------------------------------------------------------------------
 953 StringTableEntry Platform::getUserHomeDirectory()
 954 {
 955    char *home = getenv( "HOME" );
 956    return StringTable->insert( home );
 957 }
 958
 959 StringTableEntry Platform::getExecutablePath()
 960{
 961   if( !sBinPathName[0] )
 962   {
 963      const char *cpath;
 964      if( (cpath = torque_getexecutablepath()) )
 965      {
 966         dStrncpy(sBinPathName, cpath, sizeof(sBinPathName));
 967         chdir(sBinPathName);
 968      }
 969      else
 970      {
 971         getcwd(sBinPathName, sizeof(sBinPathName)-1);
 972      }
 973   }
 974
 975   return StringTable->insert(sBinPathName);
 976}
 977
 978 //-----------------------------------------------------------------------------
 979 bool Platform::isFile(const char *pFilePath)
 980 {
 981    if (!pFilePath || !*pFilePath)
 982       return false;
 983    // Get file info
 984    struct stat fStat;
 985    if (stat(pFilePath, &fStat) < 0)
 986    {
 987       // Since file does not exist on disk see if it exists in a zip file loaded
 988       return Torque::FS::IsFile(pFilePath);
 989    }
 990
 991    // if the file is a "regular file" then true
 992    if ( (fStat.st_mode & S_IFMT) == S_IFREG)
 993       return true;
 994    // must be some other file (directory, device, etc.)
 995    return false;
 996 }
 997
 998 //-----------------------------------------------------------------------------
 999 S32 Platform::getFileSize(const char *pFilePath)
1000 {
1001   if (!pFilePath || !*pFilePath)
1002     return -1;
1003   // Get the file info
1004   struct stat fStat;
1005   if (stat(pFilePath, &fStat) < 0)
1006     return -1;
1007   // if the file is a "regular file" then return the size
1008   if ( (fStat.st_mode & S_IFMT) == S_IFREG)
1009     return fStat.st_size;
1010   // Must be something else or we can't read the file.
1011   return -1;
1012 }
1013
1014 //-----------------------------------------------------------------------------
1015 bool Platform::isDirectory(const char *pDirPath)
1016 {
1017    if (!pDirPath || !*pDirPath)
1018       return false;
1019
1020    // Get file info
1021    struct stat fStat;
1022    if (stat(pDirPath, &fStat) < 0)
1023       return false;
1024
1025    // if the file is a Directory then true
1026    if ( (fStat.st_mode & S_IFMT) == S_IFDIR)
1027       return true;
1028
1029    return false;
1030 }
1031
1032 //-----------------------------------------------------------------------------
1033 bool Platform::isSubDirectory(const char *pParent, const char *pDir)
1034 {
1035    if (!pParent || !*pDir)
1036       return false;
1037
1038    // this is somewhat of a brute force method but we need to be 100% sure
1039    // that the user cannot enter things like ../dir or /dir etc,...
1040    DIR *directory;
1041
1042    directory = opendir(pParent);
1043    if (directory == NULL)
1044       return false;
1045
1046    struct dirent *fEntry;
1047    fEntry = readdir(directory);
1048    if ( fEntry == NULL )
1049    {
1050       closedir(directory);
1051       return false;
1052    }
1053
1054    do
1055    {
1056       char dirBuf[MaxPath];
1057       struct stat fStat;
1058
1059       dSprintf(dirBuf, sizeof(dirBuf), "%s/%s", pParent, fEntry->d_name);
1060       if (stat(dirBuf, &fStat) < 0)
1061          continue;
1062       // if it is a directory...
1063       if ( (fStat.st_mode & S_IFMT) == S_IFDIR)
1064       {
1065          // and the names match
1066          if (dStrcmp(pDir, fEntry->d_name ) == 0)
1067          {
1068             // then we have a real sub directory
1069             closedir(directory);
1070             return true;
1071          }
1072       }
1073    } while( (fEntry = readdir(directory)) != NULL );
1074
1075    closedir(directory);
1076    return false;
1077 }
1078
1079 //-----------------------------------------------------------------------------
1080
1081
1082 // This is untested -- BJG
1083
1084 bool Platform::fileTimeToString(FileTime * time, char * string, U32 strLen)
1085 {
1086    if(!time || !string)
1087       return(false);
1088
1089    dSprintf(string, strLen, "%ld", *time);
1090    return(true);
1091 }
1092
1093 bool Platform::stringToFileTime(const char * string, FileTime * time)
1094 {
1095    if(!time || !string)
1096       return(false);
1097
1098    *time = dAtoi(string);
1099
1100    return(true);
1101 }
1102
1103 bool Platform::hasSubDirectory(const char *pPath)
1104 {
1105   if (!pPath)
1106     return false;
1107
1108   struct dirent *d;
1109   DIR           *dip;
1110   dip = opendir(pPath);
1111   if (dip == NULL)
1112     return false;
1113
1114   while (d = readdir(dip))
1115     {
1116   bool isDir = false;
1117   if (d->d_type == DT_UNKNOWN)
1118   {
1119      char child [1024];
1120      if ((pPath[dStrlen(pPath) - 1] == '/'))
1121         dSprintf(child, 1024, "%s%s", pPath, d->d_name);
1122      else
1123         dSprintf(child, 1024, "%s/%s", pPath, d->d_name);
1124      isDir = Platform::isDirectory (child);
1125   }
1126   else if (d->d_type & DT_DIR)
1127      isDir = true;
1128      if( isDir )
1129      {
1130      // Skip the . and .. directories
1131      if (dStrcmp(d->d_name, ".") == 0 ||<a href="/coding/file/stringfunctions_8cpp/#stringfunctions_8cpp_1ab5428a869cb0ee9387cd27db91f1d82b">dStrcmp</a>(d->d_name, "..") == 0)
1132          continue;
1133      if (Platform::isExcludedDirectory(d->d_name))
1134          continue;
1135        Platform::clearExcludedDirectories();
1136        closedir(dip);
1137        return true;
1138   }
1139     }
1140   closedir(dip);
1141   Platform::clearExcludedDirectories();
1142   return false;
1143 }
1144
1145 bool Platform::fileDelete(const char * name)
1146 {
1147   return ModifyFile(name, DELETE);
1148 }
1149
1150 static bool recurseDumpDirectories(const char *basePath, const char *subPath, Vector<StringTableEntry> &directoryVector, S32 currentDepth, S32 recurseDepth, bool noBasePath)
1151 {
1152   char Path[1024];
1153   DIR *dip;
1154   struct dirent *d;
1155
1156   dsize_t trLen = basePath ? dStrlen(basePath) : 0;
1157   dsize_t subtrLen = subPath ? dStrlen(subPath) : 0;
1158   char trail = trLen > 0 ? basePath[trLen - 1] : '\0';
1159   char subTrail = subtrLen > 0 ? subPath[subtrLen - 1] : '\0';
1160   char subLead = subtrLen > 0 ? subPath[0] : '\0';
1161
1162   if (trail == '/')
1163   {
1164      if (subPath && (dStrncmp(subPath, "", 1) != 0))
1165      {
1166         if (subTrail == '/')
1167            dSprintf(Path, 1024, "%s%s", basePath, subPath);
1168         else
1169            dSprintf(Path, 1024, "%s%s/", basePath, subPath);
1170      }
1171      else
1172         dSprintf(Path, 1024, "%s", basePath);
1173   }
1174   else
1175   {
1176      if (subPath && (dStrncmp(subPath, "", 1) != 0))
1177      {
1178         if (subTrail == '/')
1179            dSprintf(Path, 1024, "%s%s", basePath, subPath);
1180         else
1181            dSprintf(Path, 1024, "%s%s/", basePath, subPath);
1182      }
1183      else
1184         dSprintf(Path, 1024, "%s/", basePath);
1185   }
1186
1187   dip = opendir(Path);
1188   if (dip == NULL)
1189     return false;
1190
1191   //////////////////////////////////////////////////////////////////////////
1192   // add path to our return list ( provided it is valid )
1193   //////////////////////////////////////////////////////////////////////////
1194   if (!Platform::isExcludedDirectory(subPath))
1195   {
1196      if (noBasePath)
1197      {
1198         // We have a path and it's not an empty string or an excluded directory
1199         if ( (subPath && (dStrncmp (subPath, "", 1) != 0)) )
1200            directoryVector.push_back(StringTable->insert(subPath));
1201      }
1202      else
1203      {
1204         if ( (subPath && (dStrncmp(subPath, "", 1) != 0)) )
1205         {
1206            char szPath[1024];
1207            dMemset(szPath, 0, 1024);
1208            if (trail == '/')
1209            {
1210               if ((basePath[dStrlen(basePath) - 1]) != '/')
1211                  dSprintf(szPath, 1024, "%s%s", basePath, &subPath[1]);
1212               else
1213                  dSprintf(szPath, 1024, "%s%s", basePath, subPath);
1214            }
1215            else
1216            {
1217               if ((basePath[dStrlen(basePath) - 1]) != '/')
1218                  dSprintf(szPath, 1024, "%s%s", basePath, subPath);
1219               else
1220                  dSprintf(szPath, 1024, "%s/%s", basePath, subPath);
1221            }
1222
1223            directoryVector.push_back(StringTable->insert(szPath));
1224         }
1225         else
1226            directoryVector.push_back(StringTable->insert(basePath));
1227      }
1228   }
1229   //////////////////////////////////////////////////////////////////////////
1230   // Iterate through and grab valid directories
1231   //////////////////////////////////////////////////////////////////////////
1232
1233   while (d = readdir(dip))
1234   {
1235      bool  isDir;
1236      isDir = false;
1237      if (d->d_type == DT_UNKNOWN)
1238      {
1239         char child [1024];
1240         if ((Path[dStrlen(Path) - 1] == '/'))
1241            dSprintf(child, 1024, "%s%s", Path, d->d_name);
1242         else
1243            dSprintf(child, 1024, "%s/%s", Path, d->d_name);
1244         isDir = Platform::isDirectory (child);
1245      }
1246      else if (d->d_type & DT_DIR)
1247      isDir = true;
1248
1249       if ( isDir )
1250   {
1251     if (dStrcmp(d->d_name, ".") == 0 ||
1252         dStrcmp(d->d_name, "..") == 0)
1253       continue;
1254     if (Platform::isExcludedDirectory(d->d_name))
1255       continue;
1256     if ( (subPath && (dStrncmp(subPath, "", 1) != 0)) )
1257       {
1258         char child[1024];
1259         if ((subPath[dStrlen(subPath) - 1] == '/'))
1260      dSprintf(child, 1024, "%s%s", subPath, d->d_name);
1261         else
1262      dSprintf(child, 1024, "%s/%s", subPath, d->d_name);
1263         if (currentDepth < recurseDepth || recurseDepth == -1 )
1264      recurseDumpDirectories(basePath, child, directoryVector,
1265                   currentDepth + 1, recurseDepth,
1266                   noBasePath);
1267       }
1268     else
1269       {
1270         char child[1024];
1271         if ( (basePath[dStrlen(basePath) - 1]) == '/')
1272      dStrcpy (child, d->d_name, 1024);
1273         else
1274      dSprintf(child, 1024, "/%s", d->d_name);
1275         if (currentDepth < recurseDepth || recurseDepth == -1)
1276      recurseDumpDirectories(basePath, child, directoryVector,
1277                   currentDepth + 1, recurseDepth,
1278                   noBasePath);
1279       }
1280   }
1281     }
1282   closedir(dip);
1283   return true;
1284 }
1285
1286 bool Platform::dumpDirectories(const char *path, Vector<StringTableEntry> &directoryVector, S32 depth, bool noBasePath)
1287 {
1288   bool retVal = recurseDumpDirectories(path, "", directoryVector, 0, depth, noBasePath);
1289   clearExcludedDirectories();
1290   return retVal;
1291 }
1292
1293StringTableEntry Platform::getExecutableName()
1294{
1295   return StringTable->insert(sBinName);
1296}
1297
1298extern "C"
1299{
1300   void setExePathName(const char* exePathName)
1301   {
1302      if (exePathName == NULL)
1303         sBinPathName[0] = '\0';
1304      else
1305         dStrncpy(sBinPathName, exePathName, sizeof(sBinPathName));
1306
1307      // set the base exe name field
1308      char *binName = dStrrchr(sBinPathName, '/');
1309      if( !binName )
1310         binName = sBinPathName;
1311      else
1312         *binName++ = '\0';
1313      sBinName = binName;
1314   }
1315}
1316