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, 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