x86UNIXFileio.cpp
Engine/source/platformX86UNIX/x86UNIXFileio.cpp
Public Enumerations
enum
_Anonymous_ { TOUCH DELETE }
Public Variables
Public Functions
bool
dFileDelete(const char * name)
bool
dFileRename(const char * oldName, const char * newName)
bool
dFileTouch(const char * name)
bool
DirExists(char * pathname, bool isFile)
ForwardSlash(char * str)
bool
GetFileTimes(const char * filePath, FileTime * createTime, FileTime * modifyTime)
const char *
bool
ModifyFile(const char * name, S32 modType)
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)
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
@99
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)
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/cinterface.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); 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); 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, "/"); 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) 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 RecurseDumpPath(child, childRelative, pattern, fileVector); 387 } 388 else 389 { 390 // File 391 392 // add it to the list 393 fileVector.increment(); 394 Platform::FileInfo& rInfo = fileVector.last(); 395 396 if (relativePath) 397 rInfo.pFullPath = StringTable->insert(relativePath); 398 else 399 rInfo.pFullPath = StringTable->insert(path); 400 rInfo.pFileName = StringTable->insert(fEntry->d_name); 401 rInfo.fileSize = fStat.st_size; 402 //dPrintf("Adding file: %s/%s\n", rInfo.pFullPath, rInfo.pFileName); 403 } 404 405 } while( (fEntry = readdir(directory)) != NULL ); 406 407 closedir(directory); 408 return true; 409 } 410 411 //----------------------------------------------------------------------------- 412 bool dFileDelete(const char * name) 413 { 414 return ModifyFile(name, DELETE); 415 } 416 417 //----------------------------------------------------------------------------- 418 bool dFileTouch(const char * name) 419 { 420 return ModifyFile(name, TOUCH); 421 } 422 423 bool dFileRename(const char *oldName, const char *newName) 424 { 425 AssertFatal( oldName != NULL && newName != NULL, "dFileRename - NULL file name" ); 426 427 // only modify files in home directory 428 TempAlloc<char> oldPrefPathName(MaxPath); 429 TempAlloc<char> newPrefPathName(MaxPath); 430 MungePath(oldPrefPathName, MaxPath, oldName, GetPrefDir()); 431 MungePath(newPrefPathName, MaxPath, newName, GetPrefDir()); 432 433 return rename(oldPrefPathName, newPrefPathName) == 0; 434 } 435 436 //----------------------------------------------------------------------------- 437 // Constructors & Destructor 438 //----------------------------------------------------------------------------- 439 440 //----------------------------------------------------------------------------- 441 // After construction, the currentStatus will be Closed and the capabilities 442 // will be 0. 443 //----------------------------------------------------------------------------- 444 File::File() 445 : currentStatus(Closed), capability(0) 446 { 447 // AssertFatal(sizeof(int) == sizeof(void *), "File::File: cannot cast void* to int"); 448 449 handle = (void *)NULL; 450 } 451 452 //----------------------------------------------------------------------------- 453 // insert a copy constructor here... (currently disabled) 454 //----------------------------------------------------------------------------- 455 456 //----------------------------------------------------------------------------- 457 // Destructor 458 //----------------------------------------------------------------------------- 459 File::~File() 460 { 461 close(); 462 handle = (void *)NULL; 463 } 464 465 //----------------------------------------------------------------------------- 466 // Open a file in the mode specified by openMode (Read, Write, or ReadWrite). 467 // Truncate the file if the mode is either Write or ReadWrite and truncate is 468 // true. 469 // 470 // Sets capability appropriate to the openMode. 471 // Returns the currentStatus of the file. 472 //----------------------------------------------------------------------------- 473 File::FileStatus File::open(const char *filename, const AccessMode openMode) 474 { 475 AssertFatal(NULL != filename, "File::open: NULL filename"); 476 AssertWarn(NULL == handle, "File::open: handle already valid"); 477 478 // Close the file if it was already open... 479 if (Closed != currentStatus) 480 close(); 481 482 char prefPathName[MaxPath]; 483 char gamePathName[MaxPath]; 484 char cwd[MaxPath]; 485 getcwd(cwd, MaxPath); 486 MungePath(prefPathName, MaxPath, filename, GetPrefDir()); 487 MungePath(gamePathName, MaxPath, filename, cwd); 488 489 int oflag; 490 struct stat filestat; 491 handle = (void *)dRealMalloc(sizeof(int)); 492 493 switch (openMode) 494 { 495 case Read: 496 oflag = O_RDONLY; 497 break; 498 case Write: 499 oflag = O_WRONLY | O_CREAT | O_TRUNC; 500 break; 501 case ReadWrite: 502 oflag = O_RDWR | O_CREAT; 503 // if the file does not exist copy it before reading/writing 504 if (stat(prefPathName, &filestat) == -1) 505 bool ret = CopyFile(gamePathName, prefPathName); 506 break; 507 case WriteAppend: 508 oflag = O_WRONLY | O_CREAT | O_APPEND; 509 // if the file does not exist copy it before appending 510 if (stat(prefPathName, &filestat) == -1) 511 bool ret = CopyFile(gamePathName, prefPathName); 512 break; 513 default: 514 AssertFatal(false, "File::open: bad access mode"); // impossible 515 } 516 517 // if we are writing, make sure output path exists 518 if (openMode == Write || openMode == ReadWrite || openMode == WriteAppend) 519 Platform::createPath(prefPathName); 520 521 int fd = -1; 522 fd = x86UNIXOpen(prefPathName, oflag); 523 if (fd == -1 && openMode == Read) 524 // for read only files we can use the gamePathName 525 fd = x86UNIXOpen(gamePathName, oflag); 526 527 dMemcpy(handle, &fd, sizeof(int)); 528 529 #ifdef DEBUG 530 // fprintf(stdout,"fd = %d, handle = %d\n", fd, *((int *)handle)); 531 #endif 532 533 if (*((int *)handle) == -1) 534 { 535 // handle not created successfully 536 Con::errorf("Can't open file: %s", filename); 537 return setStatus(); 538 } 539 else 540 { 541 // successfully created file, so set the file capabilities... 542 switch (openMode) 543 { 544 case Read: 545 capability = U32(FileRead); 546 break; 547 case Write: 548 case WriteAppend: 549 capability = U32(FileWrite); 550 break; 551 case ReadWrite: 552 capability = U32(FileRead) | 553 U32(FileWrite); 554 break; 555 default: 556 AssertFatal(false, "File::open: bad access mode"); 557 } 558 return currentStatus = Ok; // success! 559 } 560 } 561 562 //----------------------------------------------------------------------------- 563 // Get the current position of the file pointer. 564 //----------------------------------------------------------------------------- 565 U32 File::getPosition() const 566 { 567 AssertFatal(Closed != currentStatus, "File::getPosition: file closed"); 568 AssertFatal(NULL != handle, "File::getPosition: invalid file handle"); 569 570 #ifdef DEBUG 571 // fprintf(stdout, "handle = %d\n",*((int *)handle));fflush(stdout); 572 #endif 573 return (U32) lseek(*((int *)handle), 0, SEEK_CUR); 574 } 575 576 //----------------------------------------------------------------------------- 577 // Set the position of the file pointer. 578 // Absolute and relative positioning is supported via the absolutePos 579 // parameter. 580 // 581 // If positioning absolutely, position MUST be positive - an IOError results if 582 // position is negative. 583 // Position can be negative if positioning relatively, however positioning 584 // before the start of the file is an IOError. 585 // 586 // Returns the currentStatus of the file. 587 //----------------------------------------------------------------------------- 588 File::FileStatus File::setPosition(S32 position, bool absolutePos) 589 { 590 AssertFatal(Closed != currentStatus, "File::setPosition: file closed"); 591 AssertFatal(NULL != handle, "File::setPosition: invalid file handle"); 592 593 if (Ok != currentStatus && EOS != currentStatus) 594 return currentStatus; 595 596 U32 finalPos = 0; 597 switch (absolutePos) 598 { 599 case true: // absolute position 600 AssertFatal(0 <= position, "File::setPosition: negative absolute position"); 601 602 // position beyond EOS is OK 603 finalPos = lseek(*((int *)handle), position, SEEK_SET); 604 break; 605 case false: // relative position 606 AssertFatal((getPosition() >= (U32)abs(position) && 0 > position) || 0 <= position, "File::setPosition: negative relative position"); 607 608 // position beyond EOS is OK 609 finalPos = lseek(*((int *)handle), position, SEEK_CUR); 610 break; 611 } 612 613 if (0xffffffff == finalPos) 614 return setStatus(); // unsuccessful 615 else if (finalPos >= getSize()) 616 return currentStatus = EOS; // success, at end of file 617 else 618 return currentStatus = Ok; // success! 619 } 620 621 //----------------------------------------------------------------------------- 622 // Get the size of the file in bytes. 623 // It is an error to query the file size for a Closed file, or for one with an 624 // error status. 625 //----------------------------------------------------------------------------- 626 U32 File::getSize() const 627 { 628 AssertWarn(Closed != currentStatus, "File::getSize: file closed"); 629 AssertFatal(NULL != handle, "File::getSize: invalid file handle"); 630 631 if (Ok == currentStatus || EOS == currentStatus) 632 { 633 long currentOffset = getPosition(); // keep track of our current position 634 long fileSize; 635 lseek(*((int *)handle), 0, SEEK_END); // seek to the end of the file 636 fileSize = getPosition(); // get the file size 637 lseek(*((int *)handle), currentOffset, SEEK_SET); // seek back to our old offset 638 return fileSize; // success! 639 } 640 else 641 return 0; // unsuccessful 642 } 643 644 //----------------------------------------------------------------------------- 645 // Flush the file. 646 // It is an error to flush a read-only file. 647 // Returns the currentStatus of the file. 648 //----------------------------------------------------------------------------- 649 File::FileStatus File::flush() 650 { 651 AssertFatal(Closed != currentStatus, "File::flush: file closed"); 652 AssertFatal(NULL != handle, "File::flush: invalid file handle"); 653 AssertFatal(true == hasCapability(FileWrite), "File::flush: cannot flush a read-only file"); 654 655 if (fsync(*((int *)handle)) == 0) 656 return currentStatus = Ok; // success! 657 else 658 return setStatus(); // unsuccessful 659 } 660 661 //----------------------------------------------------------------------------- 662 // Close the File. 663 // 664 // Returns the currentStatus 665 //----------------------------------------------------------------------------- 666 File::FileStatus File::close() 667 { 668 // if the handle is non-NULL, close it if necessary and free it 669 if (NULL != handle) 670 { 671 // make a local copy of the handle value and 672 // free the handle 673 int handleVal = *((int *)handle); 674 dRealFree(handle); 675 handle = (void *)NULL; 676 677 // close the handle if it is valid 678 if (handleVal != -1 && x86UNIXClose(handleVal) != 0) 679 return setStatus(); // unsuccessful 680 } 681 // Set the status to closed 682 return currentStatus = Closed; 683 } 684 685 //----------------------------------------------------------------------------- 686 // Self-explanatory. 687 //----------------------------------------------------------------------------- 688 File::FileStatus File::getStatus() const 689 { 690 return currentStatus; 691 } 692 693 //----------------------------------------------------------------------------- 694 // Sets and returns the currentStatus when an error has been encountered. 695 //----------------------------------------------------------------------------- 696 File::FileStatus File::setStatus() 697 { 698 Con::printf("File IO error: %s", strerror(errno)); 699 return currentStatus = IOError; 700 } 701 702 //----------------------------------------------------------------------------- 703 // Sets and returns the currentStatus to status. 704 //----------------------------------------------------------------------------- 705 File::FileStatus File::setStatus(File::FileStatus status) 706 { 707 return currentStatus = status; 708 } 709 710 //----------------------------------------------------------------------------- 711 // Read from a file. 712 // The number of bytes to read is passed in size, the data is returned in src. 713 // The number of bytes read is available in bytesRead if a non-Null pointer is 714 // provided. 715 //----------------------------------------------------------------------------- 716 File::FileStatus File::read(U32 size, char *dst, U32 *bytesRead) 717 { 718 #ifdef DEBUG 719 // fprintf(stdout,"reading %d bytes\n",size);fflush(stdout); 720 #endif 721 AssertFatal(Closed != currentStatus, "File::read: file closed"); 722 AssertFatal(NULL != handle, "File::read: invalid file handle"); 723 AssertFatal(NULL != dst, "File::read: NULL destination pointer"); 724 AssertFatal(true == hasCapability(FileRead), "File::read: file lacks capability"); 725 AssertWarn(0 != size, "File::read: size of zero"); 726 727 /* show stats for this file */ 728 #ifdef DEBUG 729 //struct stat st; 730 //fstat(*((int *)handle), &st); 731 //fprintf(stdout,"file size = %d\n", st.st_size); 732 #endif 733 /****************************/ 734 735 if (Ok != currentStatus || 0 == size) 736 return currentStatus; 737 else 738 { 739 long lastBytes; 740 long *bytes = (NULL == bytesRead) ? &lastBytes : (long *)bytesRead; 741 if ( (*((U32 *)bytes) = x86UNIXRead(*((int *)handle), dst, size)) == -1) 742 { 743 #ifdef DEBUG 744 // fprintf(stdout,"unsuccessful: %d\n", *((U32 *)bytes));fflush(stdout); 745 #endif 746 return setStatus(); // unsuccessful 747 } else { 748 // dst[*((U32 *)bytes)] = '\0'; 749 if (*((U32 *)bytes) != size || *((U32 *)bytes) == 0) { 750 #ifdef DEBUG 751 // fprintf(stdout,"end of stream: %d\n", *((U32 *)bytes));fflush(stdout); 752 #endif 753 return currentStatus = EOS; // end of stream 754 } 755 } 756 } 757 // dst[*bytesRead] = '\0'; 758 #ifdef DEBUG 759 //fprintf(stdout, "We read:\n"); 760 //fprintf(stdout, "====================================================\n"); 761 //fprintf(stdout, "%s\n",dst); 762 //fprintf(stdout, "====================================================\n"); 763 //fprintf(stdout,"read ok: %d\n", *bytesRead);fflush(stdout); 764 #endif 765 return currentStatus = Ok; // successfully read size bytes 766 } 767 768 //----------------------------------------------------------------------------- 769 // Write to a file. 770 // The number of bytes to write is passed in size, the data is passed in src. 771 // The number of bytes written is available in bytesWritten if a non-Null 772 // pointer is provided. 773 //----------------------------------------------------------------------------- 774 File::FileStatus File::write(U32 size, const char *src, U32 *bytesWritten) 775 { 776 // JMQ: despite the U32 parameters, the maximum filesize supported by this 777 // function is probably the max value of S32, due to the unix syscall 778 // api. 779 AssertFatal(Closed != currentStatus, "File::write: file closed"); 780 AssertFatal(NULL != handle, "File::write: invalid file handle"); 781 AssertFatal(NULL != src, "File::write: NULL source pointer"); 782 AssertFatal(true == hasCapability(FileWrite), "File::write: file lacks capability"); 783 AssertWarn(0 != size, "File::write: size of zero"); 784 785 if ((Ok != currentStatus && EOS != currentStatus) || 0 == size) 786 return currentStatus; 787 else 788 { 789 S32 numWritten = x86UNIXWrite(*((int *)handle), src, size); 790 if (numWritten < 0) 791 return setStatus(); 792 793 if (bytesWritten) 794 *bytesWritten = static_cast<U32>(numWritten); 795 return currentStatus = Ok; 796 } 797 } 798 799 //----------------------------------------------------------------------------- 800 // Self-explanatory. JMQ: No explanation needed. Move along. These aren't 801 // the droids you're looking for. 802 //----------------------------------------------------------------------------- 803 bool File::hasCapability(Capability cap) const 804 { 805 return (0 != (U32(cap) & capability)); 806 } 807 808 //----------------------------------------------------------------------------- 809 S32 Platform::compareFileTimes(const FileTime &a, const FileTime &b) 810 { 811 if(a > b) 812 return 1; 813 if(a < b) 814 return -1; 815 return 0; 816 } 817 818 //----------------------------------------------------------------------------- 819 static bool GetFileTimes(const char *filePath, FileTime *createTime, FileTime *modifyTime) 820 { 821 struct stat fStat; 822 823 if (stat(filePath, &fStat) == -1) 824 return false; 825 826 if(createTime) 827 { 828 // no where does SysV/BSD UNIX keep a record of a file's 829 // creation time. instead of creation time I'll just use 830 // changed time for now. 831 *createTime = fStat.st_ctime; 832 } 833 if(modifyTime) 834 { 835 *modifyTime = fStat.st_mtime; 836 } 837 838 return true; 839 } 840 841 //----------------------------------------------------------------------------- 842 bool Platform::getFileTimes(const char *filePath, FileTime *createTime, FileTime *modifyTime) 843 { 844 char pathName[MaxPath]; 845 846 // if it starts with cwd, we need to strip that off so that we can look for 847 // the file in the pref dir 848 char cwd[MaxPath]; 849 getcwd(cwd, MaxPath); 850 if (dStrstr(filePath, cwd) == filePath) 851 filePath = filePath + dStrlen(cwd) + 1; 852 853 // if its relative, first look in the pref dir 854 if (filePath[0] != '/' && filePath[0] != '\\') 855 { 856 MungePath(pathName, MaxPath, filePath, GetPrefDir()); 857 if (GetFileTimes(pathName, createTime, modifyTime)) 858 return true; 859 } 860 861 // here if the path is absolute or not in the pref dir 862 MungePath(pathName, MaxPath, filePath, cwd); 863 return GetFileTimes(pathName, createTime, modifyTime); 864 } 865 866 //----------------------------------------------------------------------------- 867 bool Platform::createPath(const char *file) 868 { 869 char pathbuf[MaxPath]; 870 const char *dir; 871 pathbuf[0] = 0; 872 U32 pathLen = 0; 873 874 // all paths should be created in home directory 875 char prefPathName[MaxPath]; 876 MungePath(prefPathName, MaxPath, file, GetPrefDir()); 877 file = prefPathName; 878 879 // does the directory exist already? 880 if (DirExists(prefPathName, true)) // true means that the path is a filepath 881 return true; 882 883 while((dir = dStrchr(file, '/')) != NULL) 884 { 885 dStrncpy(pathbuf + pathLen, file, dir - file); 886 pathbuf[pathLen + dir-file] = 0; 887 bool ret = mkdir(pathbuf, 0700); 888 pathLen += dir - file; 889 pathbuf[pathLen++] = '/'; 890 file = dir + 1; 891 } 892 return true; 893 } 894 895 // JMQ: Platform:cdFileExists in unimplemented 896 //------------------------------------------------------------------------------ 897 // bool Platform::cdFileExists(const char *filePath, const char *volumeName, 898 // S32 serialNum) 899 // { 900 // } 901 902 //----------------------------------------------------------------------------- 903 bool Platform::dumpPath(const char *path, Vector<Platform::FileInfo> &fileVector, int depth) 904 { 905 const char* pattern = "*"; 906 907 // if it is not absolute, dump the pref dir first 908 if (path[0] != '/' && path[0] != '\\') 909 { 910 char prefPathName[MaxPath]; 911 MungePath(prefPathName, MaxPath, path, GetPrefDir()); 912 RecurseDumpPath(prefPathName, path, pattern, fileVector); 913 } 914 915 // munge the requested path and dump it 916 char mungedPath[MaxPath]; 917 char cwd[MaxPath]; 918 getcwd(cwd, MaxPath); 919 MungePath(mungedPath, MaxPath, path, cwd); 920 return RecurseDumpPath(mungedPath, path, pattern, fileVector); 921 } 922 923 //----------------------------------------------------------------------------- 924 StringTableEntry Platform::getCurrentDirectory() 925 { 926 char cwd_buf[2048]; 927 getcwd(cwd_buf, 2047); 928 return StringTable->insert(cwd_buf); 929 } 930 931 //----------------------------------------------------------------------------- 932 bool Platform::setCurrentDirectory(StringTableEntry newDir) 933 { 934 if (Platform::getWebDeployment()) 935 return true; 936 937 TempAlloc< UTF8> buf( dStrlen( newDir ) + 2 ); 938 939 dStrcpy( buf, newDir ); 940 941 ForwardSlash( buf ); 942 return chdir( buf ) == 0; 943 } 944 945 //----------------------------------------------------------------------------- 946 const char *Platform::getUserDataDirectory() 947 { 948 return StringTable->insert( GetPrefDir() ); 949 } 950 951 //----------------------------------------------------------------------------- 952 StringTableEntry Platform::getUserHomeDirectory() 953 { 954 char *home = getenv( "HOME" ); 955 return StringTable->insert( home ); 956 } 957 958 StringTableEntry Platform::getExecutablePath() 959{ 960 if( !sBinPathName[0] ) 961 { 962 const char *cpath; 963 if( (cpath = torque_getexecutablepath()) ) 964 { 965 dStrncpy(sBinPathName, cpath, sizeof(sBinPathName)); 966 chdir(sBinPathName); 967 } 968 else 969 { 970 getcwd(sBinPathName, sizeof(sBinPathName)-1); 971 } 972 } 973 974 return StringTable->insert(sBinPathName); 975} 976 977 //----------------------------------------------------------------------------- 978 bool Platform::isFile(const char *pFilePath) 979 { 980 if (!pFilePath || !*pFilePath) 981 return false; 982 // Get file info 983 struct stat fStat; 984 if (stat(pFilePath, &fStat) < 0) 985 { 986 // Since file does not exist on disk see if it exists in a zip file loaded 987 return Torque::FS::IsFile(pFilePath); 988 } 989 990 // if the file is a "regular file" then true 991 if ( (fStat.st_mode & S_IFMT) == S_IFREG) 992 return true; 993 // must be some other file (directory, device, etc.) 994 return false; 995 } 996 997 //----------------------------------------------------------------------------- 998 S32 Platform::getFileSize(const char *pFilePath) 999 { 1000 if (!pFilePath || !*pFilePath) 1001 return -1; 1002 // Get the file info 1003 struct stat fStat; 1004 if (stat(pFilePath, &fStat) < 0) 1005 return -1; 1006 // if the file is a "regular file" then return the size 1007 if ( (fStat.st_mode & S_IFMT) == S_IFREG) 1008 return fStat.st_size; 1009 // Must be something else or we can't read the file. 1010 return -1; 1011 } 1012 1013 //----------------------------------------------------------------------------- 1014 bool Platform::isDirectory(const char *pDirPath) 1015 { 1016 if (!pDirPath || !*pDirPath) 1017 return false; 1018 1019 // Get file info 1020 struct stat fStat; 1021 if (stat(pDirPath, &fStat) < 0) 1022 return false; 1023 1024 // if the file is a Directory then true 1025 if ( (fStat.st_mode & S_IFMT) == S_IFDIR) 1026 return true; 1027 1028 return false; 1029 } 1030 1031 //----------------------------------------------------------------------------- 1032 bool Platform::isSubDirectory(const char *pParent, const char *pDir) 1033 { 1034 if (!pParent || !*pDir) 1035 return false; 1036 1037 // this is somewhat of a brute force method but we need to be 100% sure 1038 // that the user cannot enter things like ../dir or /dir etc,... 1039 DIR *directory; 1040 1041 directory = opendir(pParent); 1042 if (directory == NULL) 1043 return false; 1044 1045 struct dirent *fEntry; 1046 fEntry = readdir(directory); 1047 if ( fEntry == NULL ) 1048 { 1049 closedir(directory); 1050 return false; 1051 } 1052 1053 do 1054 { 1055 char dirBuf[MaxPath]; 1056 struct stat fStat; 1057 1058 dSprintf(dirBuf, sizeof(dirBuf), "%s/%s", pParent, fEntry->d_name); 1059 if (stat(dirBuf, &fStat) < 0) 1060 continue; 1061 // if it is a directory... 1062 if ( (fStat.st_mode & S_IFMT) == S_IFDIR) 1063 { 1064 // and the names match 1065 if (dStrcmp(pDir, fEntry->d_name ) == 0) 1066 { 1067 // then we have a real sub directory 1068 closedir(directory); 1069 return true; 1070 } 1071 } 1072 } while( (fEntry = readdir(directory)) != NULL ); 1073 1074 closedir(directory); 1075 return false; 1076 } 1077 1078 //----------------------------------------------------------------------------- 1079 1080 1081 // This is untested -- BJG 1082 1083 bool Platform::fileTimeToString(FileTime * time, char * string, U32 strLen) 1084 { 1085 if(!time || !string) 1086 return(false); 1087 1088 dSprintf(string, strLen, "%ld", *time); 1089 return(true); 1090 } 1091 1092 bool Platform::stringToFileTime(const char * string, FileTime * time) 1093 { 1094 if(!time || !string) 1095 return(false); 1096 1097 *time = dAtoi(string); 1098 1099 return(true); 1100 } 1101 1102 bool Platform::hasSubDirectory(const char *pPath) 1103 { 1104 if (!pPath) 1105 return false; 1106 1107 struct dirent *d; 1108 DIR *dip; 1109 dip = opendir(pPath); 1110 if (dip == NULL) 1111 return false; 1112 1113 while (d = readdir(dip)) 1114 { 1115 bool isDir = false; 1116 if (d->d_type == DT_UNKNOWN) 1117 { 1118 char child [1024]; 1119 if ((pPath[dStrlen(pPath) - 1] == '/')) 1120 dSprintf(child, 1024, "%s%s", pPath, d->d_name); 1121 else 1122 dSprintf(child, 1024, "%s/%s", pPath, d->d_name); 1123 isDir = Platform::isDirectory (child); 1124 } 1125 else if (d->d_type & DT_DIR) 1126 isDir = true; 1127 if( isDir ) 1128 { 1129 // Skip the . and .. directories 1130 if (dStrcmp(d->d_name, ".") == 0 ||<a href="/coding/file/stringfunctions_8cpp/#stringfunctions_8cpp_1ab5428a869cb0ee9387cd27db91f1d82b">dStrcmp</a>(d->d_name, "..") == 0) 1131 continue; 1132 if (Platform::isExcludedDirectory(d->d_name)) 1133 continue; 1134 Platform::clearExcludedDirectories(); 1135 closedir(dip); 1136 return true; 1137 } 1138 } 1139 closedir(dip); 1140 Platform::clearExcludedDirectories(); 1141 return false; 1142 } 1143 1144 bool Platform::fileDelete(const char * name) 1145 { 1146 return ModifyFile(name, DELETE); 1147 } 1148 1149 static bool recurseDumpDirectories(const char *basePath, const char *subPath, Vector<StringTableEntry> &directoryVector, S32 currentDepth, S32 recurseDepth, bool noBasePath) 1150 { 1151 char Path[1024]; 1152 DIR *dip; 1153 struct dirent *d; 1154 1155 dsize_t trLen = basePath ? dStrlen(basePath) : 0; 1156 dsize_t subtrLen = subPath ? dStrlen(subPath) : 0; 1157 char trail = trLen > 0 ? basePath[trLen - 1] : '\0'; 1158 char subTrail = subtrLen > 0 ? subPath[subtrLen - 1] : '\0'; 1159 char subLead = subtrLen > 0 ? subPath[0] : '\0'; 1160 1161 if (trail == '/') 1162 { 1163 if (subPath && (dStrncmp(subPath, "", 1) != 0)) 1164 { 1165 if (subTrail == '/') 1166 dSprintf(Path, 1024, "%s%s", basePath, subPath); 1167 else 1168 dSprintf(Path, 1024, "%s%s/", basePath, subPath); 1169 } 1170 else 1171 dSprintf(Path, 1024, "%s", basePath); 1172 } 1173 else 1174 { 1175 if (subPath && (dStrncmp(subPath, "", 1) != 0)) 1176 { 1177 if (subTrail == '/') 1178 dSprintf(Path, 1024, "%s%s", basePath, subPath); 1179 else 1180 dSprintf(Path, 1024, "%s%s/", basePath, subPath); 1181 } 1182 else 1183 dSprintf(Path, 1024, "%s/", basePath); 1184 } 1185 1186 dip = opendir(Path); 1187 if (dip == NULL) 1188 return false; 1189 1190 ////////////////////////////////////////////////////////////////////////// 1191 // add path to our return list ( provided it is valid ) 1192 ////////////////////////////////////////////////////////////////////////// 1193 if (!Platform::isExcludedDirectory(subPath)) 1194 { 1195 if (noBasePath) 1196 { 1197 // We have a path and it's not an empty string or an excluded directory 1198 if ( (subPath && (dStrncmp (subPath, "", 1) != 0)) ) 1199 directoryVector.push_back(StringTable->insert(subPath)); 1200 } 1201 else 1202 { 1203 if ( (subPath && (dStrncmp(subPath, "", 1) != 0)) ) 1204 { 1205 char szPath[1024]; 1206 dMemset(szPath, 0, 1024); 1207 if (trail == '/') 1208 { 1209 if ((basePath[dStrlen(basePath) - 1]) != '/') 1210 dSprintf(szPath, 1024, "%s%s", basePath, &subPath[1]); 1211 else 1212 dSprintf(szPath, 1024, "%s%s", basePath, subPath); 1213 } 1214 else 1215 { 1216 if ((basePath[dStrlen(basePath) - 1]) != '/') 1217 dSprintf(szPath, 1024, "%s%s", basePath, subPath); 1218 else 1219 dSprintf(szPath, 1024, "%s/%s", basePath, subPath); 1220 } 1221 1222 directoryVector.push_back(StringTable->insert(szPath)); 1223 } 1224 else 1225 directoryVector.push_back(StringTable->insert(basePath)); 1226 } 1227 } 1228 ////////////////////////////////////////////////////////////////////////// 1229 // Iterate through and grab valid directories 1230 ////////////////////////////////////////////////////////////////////////// 1231 1232 while (d = readdir(dip)) 1233 { 1234 bool isDir; 1235 isDir = false; 1236 if (d->d_type == DT_UNKNOWN) 1237 { 1238 char child [1024]; 1239 if ((Path[dStrlen(Path) - 1] == '/')) 1240 dSprintf(child, 1024, "%s%s", Path, d->d_name); 1241 else 1242 dSprintf(child, 1024, "%s/%s", Path, d->d_name); 1243 isDir = Platform::isDirectory (child); 1244 } 1245 else if (d->d_type & DT_DIR) 1246 isDir = true; 1247 1248 if ( isDir ) 1249 { 1250 if (dStrcmp(d->d_name, ".") == 0 || 1251 dStrcmp(d->d_name, "..") == 0) 1252 continue; 1253 if (Platform::isExcludedDirectory(d->d_name)) 1254 continue; 1255 if ( (subPath && (dStrncmp(subPath, "", 1) != 0)) ) 1256 { 1257 char child[1024]; 1258 if ((subPath[dStrlen(subPath) - 1] == '/')) 1259 dSprintf(child, 1024, "%s%s", subPath, d->d_name); 1260 else 1261 dSprintf(child, 1024, "%s/%s", subPath, d->d_name); 1262 if (currentDepth < recurseDepth || recurseDepth == -1 ) 1263 recurseDumpDirectories(basePath, child, directoryVector, 1264 currentDepth + 1, recurseDepth, 1265 noBasePath); 1266 } 1267 else 1268 { 1269 char child[1024]; 1270 if ( (basePath[dStrlen(basePath) - 1]) == '/') 1271 dStrcpy (child, d->d_name); 1272 else 1273 dSprintf(child, 1024, "/%s", d->d_name); 1274 if (currentDepth < recurseDepth || recurseDepth == -1) 1275 recurseDumpDirectories(basePath, child, directoryVector, 1276 currentDepth + 1, recurseDepth, 1277 noBasePath); 1278 } 1279 } 1280 } 1281 closedir(dip); 1282 return true; 1283 } 1284 1285 bool Platform::dumpDirectories(const char *path, Vector<StringTableEntry> &directoryVector, S32 depth, bool noBasePath) 1286 { 1287 bool retVal = recurseDumpDirectories(path, "", directoryVector, 0, depth, noBasePath); 1288 clearExcludedDirectories(); 1289 return retVal; 1290 } 1291 1292StringTableEntry Platform::getExecutableName() 1293{ 1294 return StringTable->insert(sBinName); 1295} 1296 1297extern "C" 1298{ 1299 void setExePathName(const char* exePathName) 1300 { 1301 if (exePathName == NULL) 1302 sBinPathName[0] = '\0'; 1303 else 1304 dStrncpy(sBinPathName, exePathName, sizeof(sBinPathName)); 1305 1306 // set the base exe name field 1307 char *binName = dStrrchr(sBinPathName, '/'); 1308 if( !binName ) 1309 binName = sBinPathName; 1310 else 1311 *binName++ = '\0'; 1312 sBinName = binName; 1313 } 1314} 1315
