pathtools_public.cpp (23356B)
1 //========= Copyright Valve Corporation ============// 2 #include "strtools_public.h" 3 #include "pathtools_public.h" 4 5 #if defined( _WIN32) 6 #include <windows.h> 7 #include <direct.h> 8 #include <shobjidl.h> 9 #include <knownfolders.h> 10 #include <shlobj.h> 11 #include <share.h> 12 13 #undef GetEnvironmentVariable 14 #else 15 #include <dlfcn.h> 16 #include <stdio.h> 17 #include <unistd.h> 18 #include <stdlib.h> 19 #include <alloca.h> 20 #endif 21 22 #if defined OSX 23 #include <Foundation/Foundation.h> 24 #include <AppKit/AppKit.h> 25 #include <mach-o/dyld.h> 26 #define _S_IFDIR S_IFDIR // really from tier0/platform.h which we dont have yet 27 #endif 28 29 #include <sys/stat.h> 30 31 #include <algorithm> 32 33 /** Returns the path (including filename) to the current executable */ 34 std::string Path_GetExecutablePath() 35 { 36 #if defined( _WIN32 ) 37 wchar_t *pwchPath = new wchar_t[MAX_UNICODE_PATH]; 38 char *pchPath = new char[MAX_UNICODE_PATH_IN_UTF8]; 39 ::GetModuleFileNameW( NULL, pwchPath, MAX_UNICODE_PATH ); 40 WideCharToMultiByte( CP_UTF8, 0, pwchPath, -1, pchPath, MAX_UNICODE_PATH_IN_UTF8, NULL, NULL ); 41 delete[] pwchPath; 42 43 std::string sPath = pchPath; 44 delete[] pchPath; 45 return sPath; 46 #elif defined( OSX ) 47 char rchPath[1024]; 48 uint32_t nBuff = sizeof( rchPath ); 49 bool bSuccess = _NSGetExecutablePath(rchPath, &nBuff) == 0; 50 rchPath[nBuff-1] = '\0'; 51 if( bSuccess ) 52 return rchPath; 53 else 54 return ""; 55 #elif defined LINUX 56 char rchPath[1024]; 57 size_t nBuff = sizeof( rchPath ); 58 ssize_t nRead = readlink("/proc/self/exe", rchPath, nBuff-1 ); 59 if ( nRead != -1 ) 60 { 61 rchPath[ nRead ] = 0; 62 return rchPath; 63 } 64 else 65 { 66 return ""; 67 } 68 #else 69 AssertMsg( false, "Implement Plat_GetExecutablePath" ); 70 return ""; 71 #endif 72 73 } 74 75 /** Returns the path of the current working directory */ 76 std::string Path_GetWorkingDirectory() 77 { 78 std::string sPath; 79 #if defined( _WIN32 ) 80 wchar_t buf[MAX_UNICODE_PATH]; 81 sPath = UTF16to8( _wgetcwd( buf, MAX_UNICODE_PATH ) ); 82 #else 83 char buf[ 1024 ]; 84 sPath = getcwd( buf, sizeof( buf ) ); 85 #endif 86 return sPath; 87 } 88 89 /** Sets the path of the current working directory. Returns true if this was successful. */ 90 bool Path_SetWorkingDirectory( const std::string & sPath ) 91 { 92 bool bSuccess; 93 #if defined( _WIN32 ) 94 std::wstring wsPath = UTF8to16( sPath.c_str() ); 95 bSuccess = 0 == _wchdir( wsPath.c_str() ); 96 #else 97 bSuccess = 0 == chdir( sPath.c_str() ); 98 #endif 99 return bSuccess; 100 } 101 102 /** Gets the path to a temporary directory. */ 103 std::string Path_GetTemporaryDirectory() 104 { 105 #if defined( _WIN32 ) 106 wchar_t buf[MAX_UNICODE_PATH]; 107 if ( GetTempPathW( MAX_UNICODE_PATH, buf ) == 0 ) 108 return Path_GetWorkingDirectory(); 109 return UTF16to8( buf ); 110 #else 111 const char *pchTmpDir = getenv( "TMPDIR" ); 112 if ( pchTmpDir == NULL ) 113 { 114 return ""; 115 } 116 return pchTmpDir; 117 #endif 118 } 119 120 /** Returns the specified path without its filename */ 121 std::string Path_StripFilename( const std::string & sPath, char slash ) 122 { 123 if( slash == 0 ) 124 slash = Path_GetSlash(); 125 126 std::string::size_type n = sPath.find_last_of( slash ); 127 if( n == std::string::npos ) 128 return sPath; 129 else 130 return std::string( sPath.begin(), sPath.begin() + n ); 131 } 132 133 /** returns just the filename from the provided full or relative path. */ 134 std::string Path_StripDirectory( const std::string & sPath, char slash ) 135 { 136 if( slash == 0 ) 137 slash = Path_GetSlash(); 138 139 std::string::size_type n = sPath.find_last_of( slash ); 140 if( n == std::string::npos ) 141 return sPath; 142 else 143 return std::string( sPath.begin() + n + 1, sPath.end() ); 144 } 145 146 /** returns just the filename with no extension of the provided filename. 147 * If there is a path the path is left intact. */ 148 std::string Path_StripExtension( const std::string & sPath ) 149 { 150 for( std::string::const_reverse_iterator i = sPath.rbegin(); i != sPath.rend(); i++ ) 151 { 152 if( *i == '.' ) 153 { 154 return std::string( sPath.begin(), i.base() - 1 ); 155 } 156 157 // if we find a slash there is no extension 158 if( *i == '\\' || *i == '/' ) 159 break; 160 } 161 162 // we didn't find an extension 163 return sPath; 164 } 165 166 /** returns just extension of the provided filename (if any). */ 167 std::string Path_GetExtension( const std::string & sPath ) 168 { 169 for ( std::string::const_reverse_iterator i = sPath.rbegin(); i != sPath.rend(); i++ ) 170 { 171 if ( *i == '.' ) 172 { 173 return std::string( i.base(), sPath.end() ); 174 } 175 176 // if we find a slash there is no extension 177 if ( *i == '\\' || *i == '/' ) 178 break; 179 } 180 181 // we didn't find an extension 182 return ""; 183 } 184 185 bool Path_IsAbsolute( const std::string & sPath ) 186 { 187 if( sPath.empty() ) 188 return false; 189 190 #if defined( WIN32 ) 191 if ( sPath.size() < 3 ) // must be c:\x or \\x at least 192 return false; 193 194 if ( sPath[1] == ':' ) // drive letter plus slash, but must test both slash cases 195 { 196 if ( sPath[2] == '\\' || sPath[2] == '/' ) 197 return true; 198 } 199 else if ( sPath[0] == '\\' && sPath[1] == '\\' ) // UNC path 200 return true; 201 #else 202 if( sPath[0] == '\\' || sPath[0] == '/' ) // any leading slash 203 return true; 204 #endif 205 206 return false; 207 } 208 209 210 /** Makes an absolute path from a relative path and a base path */ 211 std::string Path_MakeAbsolute( const std::string & sRelativePath, const std::string & sBasePath ) 212 { 213 if( Path_IsAbsolute( sRelativePath ) ) 214 return Path_Compact( sRelativePath ); 215 else 216 { 217 if( !Path_IsAbsolute( sBasePath ) ) 218 return ""; 219 220 std::string sCompacted = Path_Compact( Path_Join( sBasePath, sRelativePath ) ); 221 if( Path_IsAbsolute( sCompacted ) ) 222 return sCompacted; 223 else 224 return ""; 225 } 226 } 227 228 229 /** Fixes the directory separators for the current platform */ 230 std::string Path_FixSlashes( const std::string & sPath, char slash ) 231 { 232 if( slash == 0 ) 233 slash = Path_GetSlash(); 234 235 std::string sFixed = sPath; 236 for( std::string::iterator i = sFixed.begin(); i != sFixed.end(); i++ ) 237 { 238 if( *i == '/' || *i == '\\' ) 239 *i = slash; 240 } 241 242 return sFixed; 243 } 244 245 246 char Path_GetSlash() 247 { 248 #if defined(_WIN32) 249 return '\\'; 250 #else 251 return '/'; 252 #endif 253 } 254 255 /** Jams two paths together with the right kind of slash */ 256 std::string Path_Join( const std::string & first, const std::string & second, char slash ) 257 { 258 if( slash == 0 ) 259 slash = Path_GetSlash(); 260 261 // only insert a slash if we don't already have one 262 std::string::size_type nLen = first.length(); 263 if( !nLen ) 264 return second; 265 #if defined(_WIN32) 266 if( first.back() == '\\' || first.back() == '/' ) 267 nLen--; 268 #else 269 char last_char = first[first.length()-1]; 270 if (last_char == '\\' || last_char == '/') 271 nLen--; 272 #endif 273 274 return first.substr( 0, nLen ) + std::string( 1, slash ) + second; 275 } 276 277 278 std::string Path_Join( const std::string & first, const std::string & second, const std::string & third, char slash ) 279 { 280 return Path_Join( Path_Join( first, second, slash ), third, slash ); 281 } 282 283 std::string Path_Join( const std::string & first, const std::string & second, const std::string & third, const std::string &fourth, char slash ) 284 { 285 return Path_Join( Path_Join( Path_Join( first, second, slash ), third, slash ), fourth, slash ); 286 } 287 288 std::string Path_Join( 289 const std::string & first, 290 const std::string & second, 291 const std::string & third, 292 const std::string & fourth, 293 const std::string & fifth, 294 char slash ) 295 { 296 return Path_Join( Path_Join( Path_Join( Path_Join( first, second, slash ), third, slash ), fourth, slash ), fifth, slash ); 297 } 298 299 300 std::string Path_RemoveTrailingSlash( const std::string & sRawPath, char slash ) 301 { 302 if ( slash == 0 ) 303 slash = Path_GetSlash(); 304 305 std::string sPath = sRawPath; 306 std::string::size_type nCurrent = sRawPath.length(); 307 if ( nCurrent == 0 ) 308 return sPath; 309 310 int nLastFound = -1; 311 nCurrent--; 312 while( nCurrent != 0 ) 313 { 314 if ( sRawPath[ nCurrent ] == slash ) 315 { 316 nLastFound = (int)nCurrent; 317 nCurrent--; 318 } 319 else 320 { 321 break; 322 } 323 } 324 325 if ( nLastFound >= 0 ) 326 { 327 sPath.erase( nLastFound, std::string::npos ); 328 } 329 330 return sPath; 331 } 332 333 334 /** Removes redundant <dir>/.. elements in the path. Returns an empty path if the 335 * specified path has a broken number of directories for its number of ..s */ 336 std::string Path_Compact( const std::string & sRawPath, char slash ) 337 { 338 if( slash == 0 ) 339 slash = Path_GetSlash(); 340 341 std::string sPath = Path_FixSlashes( sRawPath, slash ); 342 std::string sSlashString( 1, slash ); 343 344 // strip out all /./ 345 for( std::string::size_type i = 0; (i + 3) < sPath.length(); ) 346 { 347 if( sPath[ i ] == slash && sPath[ i+1 ] == '.' && sPath[ i+2 ] == slash ) 348 { 349 sPath.replace( i, 3, sSlashString ); 350 } 351 else 352 { 353 ++i; 354 } 355 } 356 357 358 // get rid of trailing /. but leave the path separator 359 if( sPath.length() > 2 ) 360 { 361 std::string::size_type len = sPath.length(); 362 if( sPath[ len-1 ] == '.' && sPath[ len-2 ] == slash ) 363 { 364 sPath.pop_back(); 365 //Not sure why the following line of code was used for a while. It causes problems with strlen. 366 //sPath[len-1] = 0; // for now, at least 367 } 368 } 369 370 // get rid of leading ./ 371 if( sPath.length() > 2 ) 372 { 373 if( sPath[ 0 ] == '.' && sPath[ 1 ] == slash ) 374 { 375 sPath.replace( 0, 2, "" ); 376 } 377 } 378 379 // each time we encounter .. back up until we've found the previous directory name 380 // then get rid of both 381 std::string::size_type i = 0; 382 while( i < sPath.length() ) 383 { 384 if( i > 0 && sPath.length() - i >= 2 385 && sPath[i] == '.' 386 && sPath[i+1] == '.' 387 && ( i + 2 == sPath.length() || sPath[ i+2 ] == slash ) 388 && sPath[ i-1 ] == slash ) 389 { 390 // check if we've hit the start of the string and have a bogus path 391 if( i == 1 ) 392 return ""; 393 394 // find the separator before i-1 395 std::string::size_type iDirStart = i-2; 396 while( iDirStart > 0 && sPath[ iDirStart - 1 ] != slash ) 397 --iDirStart; 398 399 // remove everything from iDirStart to i+2 400 sPath.replace( iDirStart, (i - iDirStart) + 3, "" ); 401 402 // start over 403 i = 0; 404 } 405 else 406 { 407 ++i; 408 } 409 } 410 411 return sPath; 412 } 413 414 415 /** Returns true if these two paths are the same without respect for internal . or .. 416 * sequences, slash type, or case (on case-insensitive platforms). */ 417 bool Path_IsSamePath( const std::string & sPath1, const std::string & sPath2 ) 418 { 419 std::string sCompact1 = Path_Compact( sPath1 ); 420 std::string sCompact2 = Path_Compact( sPath2 ); 421 #if defined(WIN32) 422 return !stricmp( sCompact1.c_str(), sCompact2.c_str() ); 423 #else 424 return !strcmp( sCompact1.c_str(), sCompact2.c_str() ); 425 #endif 426 } 427 428 429 /** Returns the path to the current DLL or exe */ 430 std::string Path_GetThisModulePath() 431 { 432 // gets the path of vrclient.dll itself 433 #ifdef WIN32 434 HMODULE hmodule = NULL; 435 436 ::GetModuleHandleEx( GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast<LPCTSTR>(Path_GetThisModulePath), &hmodule ); 437 438 wchar_t *pwchPath = new wchar_t[MAX_UNICODE_PATH]; 439 char *pchPath = new char[ MAX_UNICODE_PATH_IN_UTF8 ]; 440 ::GetModuleFileNameW( hmodule, pwchPath, MAX_UNICODE_PATH ); 441 WideCharToMultiByte( CP_UTF8, 0, pwchPath, -1, pchPath, MAX_UNICODE_PATH_IN_UTF8, NULL, NULL ); 442 delete[] pwchPath; 443 444 std::string sPath = pchPath; 445 delete [] pchPath; 446 return sPath; 447 448 #elif defined( OSX ) || defined( LINUX ) 449 // get the addr of a function in vrclient.so and then ask the dlopen system about it 450 Dl_info info; 451 dladdr( (void *)Path_GetThisModulePath, &info ); 452 return info.dli_fname; 453 #endif 454 455 } 456 457 458 /** returns true if the specified path exists and is a directory */ 459 bool Path_IsDirectory( const std::string & sPath ) 460 { 461 std::string sFixedPath = Path_FixSlashes( sPath ); 462 if( sFixedPath.empty() ) 463 return false; 464 char cLast = sFixedPath[ sFixedPath.length() - 1 ]; 465 if( cLast == '/' || cLast == '\\' ) 466 sFixedPath.erase( sFixedPath.end() - 1, sFixedPath.end() ); 467 468 // see if the specified path actually exists. 469 470 #if defined(POSIX) 471 struct stat buf; 472 if ( stat( sFixedPath.c_str(), &buf ) == -1 ) 473 { 474 return false; 475 } 476 477 #if defined( LINUX ) || defined( OSX ) 478 return S_ISDIR( buf.st_mode ); 479 #else 480 return (buf.st_mode & _S_IFDIR) != 0; 481 #endif 482 483 #else 484 struct _stat buf; 485 std::wstring wsFixedPath = UTF8to16( sFixedPath.c_str() ); 486 if ( _wstat( wsFixedPath.c_str(), &buf ) == -1 ) 487 { 488 return false; 489 } 490 491 return (buf.st_mode & _S_IFDIR) != 0; 492 #endif 493 } 494 495 /** returns true if the specified path represents an app bundle */ 496 bool Path_IsAppBundle( const std::string & sPath ) 497 { 498 #if defined(OSX) 499 @autoreleasepool { 500 NSBundle *bundle = [ NSBundle bundleWithPath: [ NSString stringWithUTF8String:sPath.c_str() ] ]; 501 bool bisAppBundle = ( nullptr != bundle ); 502 return bisAppBundle; 503 } 504 #else 505 return false; 506 #endif 507 } 508 509 //----------------------------------------------------------------------------- 510 // Purpose: returns true if the the path exists 511 //----------------------------------------------------------------------------- 512 bool Path_Exists( const std::string & sPath ) 513 { 514 std::string sFixedPath = Path_FixSlashes( sPath ); 515 if( sFixedPath.empty() ) 516 return false; 517 518 #if defined( WIN32 ) 519 struct _stat buf; 520 std::wstring wsFixedPath = UTF8to16( sFixedPath.c_str() ); 521 if ( _wstat( wsFixedPath.c_str(), &buf ) == -1 ) 522 { 523 return false; 524 } 525 #else 526 struct stat buf; 527 if ( stat ( sFixedPath.c_str(), &buf ) == -1) 528 { 529 return false; 530 } 531 #endif 532 533 return true; 534 } 535 536 537 //----------------------------------------------------------------------------- 538 // Purpose: helper to find a directory upstream from a given path 539 //----------------------------------------------------------------------------- 540 std::string Path_FindParentDirectoryRecursively( const std::string &strStartDirectory, const std::string &strDirectoryName ) 541 { 542 std::string strFoundPath = ""; 543 std::string strCurrentPath = Path_FixSlashes( strStartDirectory ); 544 if ( strCurrentPath.length() == 0 ) 545 return ""; 546 547 bool bExists = Path_Exists( strCurrentPath ); 548 std::string strCurrentDirectoryName = Path_StripDirectory( strCurrentPath ); 549 if ( bExists && stricmp( strCurrentDirectoryName.c_str(), strDirectoryName.c_str() ) == 0 ) 550 return strCurrentPath; 551 552 while( bExists && strCurrentPath.length() != 0 ) 553 { 554 strCurrentPath = Path_StripFilename( strCurrentPath ); 555 strCurrentDirectoryName = Path_StripDirectory( strCurrentPath ); 556 bExists = Path_Exists( strCurrentPath ); 557 if ( bExists && stricmp( strCurrentDirectoryName.c_str(), strDirectoryName.c_str() ) == 0 ) 558 return strCurrentPath; 559 } 560 561 return ""; 562 } 563 564 565 //----------------------------------------------------------------------------- 566 // Purpose: helper to find a subdirectory upstream from a given path 567 //----------------------------------------------------------------------------- 568 std::string Path_FindParentSubDirectoryRecursively( const std::string &strStartDirectory, const std::string &strDirectoryName ) 569 { 570 std::string strFoundPath = ""; 571 std::string strCurrentPath = Path_FixSlashes( strStartDirectory ); 572 if ( strCurrentPath.length() == 0 ) 573 return ""; 574 575 bool bExists = Path_Exists( strCurrentPath ); 576 while( bExists && strCurrentPath.length() != 0 ) 577 { 578 strCurrentPath = Path_StripFilename( strCurrentPath ); 579 bExists = Path_Exists( strCurrentPath ); 580 581 if( Path_Exists( Path_Join( strCurrentPath, strDirectoryName ) ) ) 582 { 583 strFoundPath = Path_Join( strCurrentPath, strDirectoryName ); 584 break; 585 } 586 } 587 return strFoundPath; 588 } 589 590 591 //----------------------------------------------------------------------------- 592 // Purpose: reading and writing files in the vortex directory 593 //----------------------------------------------------------------------------- 594 unsigned char * Path_ReadBinaryFile( const std::string &strFilename, int *pSize ) 595 { 596 FILE *f; 597 #if defined( POSIX ) 598 f = fopen( strFilename.c_str(), "rb" ); 599 #else 600 std::wstring wstrFilename = UTF8to16( strFilename.c_str() ); 601 // the open operation needs to be sharable, therefore use of _wfsopen instead of _wfopen_s 602 f = _wfsopen( wstrFilename.c_str(), L"rb", _SH_DENYNO ); 603 #endif 604 605 unsigned char* buf = NULL; 606 607 if ( f != NULL ) 608 { 609 fseek(f, 0, SEEK_END); 610 int size = ftell(f); 611 fseek(f, 0, SEEK_SET); 612 613 buf = new unsigned char[size]; 614 if (buf && fread(buf, size, 1, f) == 1) 615 { 616 if (pSize) 617 *pSize = size; 618 } 619 else 620 { 621 delete[] buf; 622 buf = 0; 623 } 624 625 fclose(f); 626 } 627 628 return buf; 629 } 630 631 uint32_t Path_ReadBinaryFile( const std::string &strFilename, unsigned char *pBuffer, uint32_t unSize ) 632 { 633 FILE *f; 634 #if defined( POSIX ) 635 f = fopen( strFilename.c_str(), "rb" ); 636 #else 637 std::wstring wstrFilename = UTF8to16( strFilename.c_str() ); 638 errno_t err = _wfopen_s( &f, wstrFilename.c_str(), L"rb" ); 639 if ( err != 0 ) 640 { 641 f = NULL; 642 } 643 #endif 644 645 uint32_t unSizeToReturn = 0; 646 647 if ( f != NULL ) 648 { 649 fseek( f, 0, SEEK_END ); 650 uint32_t size = (uint32_t)ftell( f ); 651 fseek( f, 0, SEEK_SET ); 652 653 if ( size > unSize || !pBuffer ) 654 { 655 unSizeToReturn = (uint32_t)size; 656 } 657 else 658 { 659 if ( fread( pBuffer, size, 1, f ) == 1 ) 660 { 661 unSizeToReturn = (uint32_t)size; 662 } 663 } 664 665 fclose( f ); 666 } 667 668 return unSizeToReturn; 669 } 670 671 bool Path_WriteBinaryFile(const std::string &strFilename, unsigned char *pData, unsigned nSize) 672 { 673 FILE *f; 674 #if defined( POSIX ) 675 f = fopen(strFilename.c_str(), "wb"); 676 #else 677 std::wstring wstrFilename = UTF8to16( strFilename.c_str() ); 678 errno_t err = _wfopen_s( &f, wstrFilename.c_str(), L"wb" ); 679 if (err != 0) 680 { 681 f = NULL; 682 } 683 #endif 684 685 size_t written = 0; 686 if (f != NULL) { 687 written = fwrite(pData, sizeof(unsigned char), nSize, f); 688 fclose(f); 689 } 690 691 return written == nSize ? true : false; 692 } 693 694 std::string Path_ReadTextFile( const std::string &strFilename ) 695 { 696 // doing it this way seems backwards, but I don't 697 // see an easy way to do this with C/C++ style IO 698 // that isn't worse... 699 int size; 700 unsigned char* buf = Path_ReadBinaryFile( strFilename, &size ); 701 if (!buf) 702 return ""; 703 704 // convert CRLF -> LF 705 size_t outsize = 1; 706 for (int i=1; i < size; i++) 707 { 708 if (buf[i] == '\n' && buf[i-1] == '\r') // CRLF 709 buf[outsize-1] = '\n'; // ->LF 710 else 711 buf[outsize++] = buf[i]; // just copy 712 } 713 714 std::string ret((char *)buf, outsize); 715 delete[] buf; 716 return ret; 717 } 718 719 720 bool Path_MakeWritable( const std::string &strFilename ) 721 { 722 #if defined ( _WIN32 ) 723 std::wstring wstrFilename = UTF8to16( strFilename.c_str() ); 724 725 DWORD dwAttrs = GetFileAttributesW( wstrFilename.c_str() ); 726 if ( dwAttrs != INVALID_FILE_ATTRIBUTES && ( dwAttrs & FILE_ATTRIBUTE_READONLY ) ) 727 { 728 return SetFileAttributesW( wstrFilename.c_str(), dwAttrs & ~FILE_ATTRIBUTE_READONLY ); 729 } 730 #else 731 struct stat sb; 732 733 if ( stat( strFilename.c_str(), &sb ) == 0 && !( sb.st_mode & S_IWUSR ) ) 734 { 735 return ( chmod( strFilename.c_str(), sb.st_mode | S_IWUSR ) == 0 ); 736 } 737 #endif 738 739 return true; 740 } 741 742 bool Path_WriteStringToTextFile( const std::string &strFilename, const char *pchData ) 743 { 744 FILE *f; 745 #if defined( POSIX ) 746 f = fopen( strFilename.c_str(), "w" ); 747 #else 748 std::wstring wstrFilename = UTF8to16( strFilename.c_str() ); 749 errno_t err = _wfopen_s( &f, wstrFilename.c_str(), L"w" ); 750 if ( err != 0 ) 751 { 752 f = NULL; 753 } 754 #endif 755 756 bool ok = false; 757 758 if ( f != NULL ) 759 { 760 ok = fputs( pchData, f) >= 0; 761 fclose(f); 762 } 763 764 return ok; 765 } 766 767 bool Path_WriteStringToTextFileAtomic( const std::string &strFilename, const char *pchData ) 768 { 769 std::string strTmpFilename = strFilename + ".tmp"; 770 771 if ( !Path_WriteStringToTextFile( strTmpFilename, pchData ) ) 772 return false; 773 774 // Platform specific atomic file replacement 775 #if defined( _WIN32 ) 776 std::wstring wsFilename = UTF8to16( strFilename.c_str() ); 777 std::wstring wsTmpFilename = UTF8to16( strTmpFilename.c_str() ); 778 if ( !::ReplaceFileW( wsFilename.c_str(), wsTmpFilename.c_str(), nullptr, 0, 0, 0 ) ) 779 { 780 // if we couldn't ReplaceFile, try a non-atomic write as a fallback 781 if ( !Path_WriteStringToTextFile( strFilename, pchData ) ) 782 return false; 783 } 784 #elif defined( POSIX ) 785 if ( rename( strTmpFilename.c_str(), strFilename.c_str() ) == -1 ) 786 return false; 787 #else 788 #error Do not know how to write atomic file 789 #endif 790 791 return true; 792 } 793 794 795 #if defined(WIN32) 796 #define FILE_URL_PREFIX "file:///" 797 #else 798 #define FILE_URL_PREFIX "file://" 799 #endif 800 801 // Mozilla: see mozilla.patch for more details 802 // ---------------------------------------------------------------------------------------------------------------------------- 803 // Purpose: Turns a path to a file on disk into a URL (or just returns the value if it's already a URL) 804 // ---------------------------------------------------------------------------------------------------------------------------- 805 // std::string Path_FilePathToUrl( const std::string & sRelativePath, const std::string & sBasePath ) 806 // { 807 // if ( StringHasPrefix( sRelativePath, "http://" ) 808 // || StringHasPrefix( sRelativePath, "https://" ) 809 // || StringHasPrefix( sRelativePath, "vr-input-workshop://" ) 810 // || StringHasPrefix( sRelativePath, "file://" ) 811 // ) 812 // { 813 // return sRelativePath; 814 // } 815 // else 816 // { 817 // std::string sAbsolute = Path_MakeAbsolute( sRelativePath, sBasePath ); 818 // if ( sAbsolute.empty() ) 819 // return sAbsolute; 820 // sAbsolute = Path_FixSlashes( sAbsolute, '/' ); 821 822 // size_t unBufferSize = sAbsolute.length() * 3; 823 // char *pchBuffer = (char *)alloca( unBufferSize ); 824 // V_URLEncodeFullPath( pchBuffer, (int)unBufferSize, sAbsolute.c_str(), (int)sAbsolute.length() ); 825 826 // return std::string( FILE_URL_PREFIX ) + pchBuffer; 827 // } 828 // } 829 830 // Mozilla: see mozilla.patch for more details 831 // ----------------------------------------------------------------------------------------------------- 832 // Purpose: Strips off file:// off a URL and returns the path. For other kinds of URLs an empty string is returned 833 // ----------------------------------------------------------------------------------------------------- 834 // std::string Path_UrlToFilePath( const std::string & sFileUrl ) 835 // { 836 // if ( !strnicmp( sFileUrl.c_str(), FILE_URL_PREFIX, strlen( FILE_URL_PREFIX ) ) ) 837 // { 838 // char *pchBuffer = (char *)alloca( sFileUrl.length() ); 839 // V_URLDecodeNoPlusForSpace( pchBuffer, (int)sFileUrl.length(), 840 // sFileUrl.c_str() + strlen( FILE_URL_PREFIX ), (int)( sFileUrl.length() - strlen( FILE_URL_PREFIX ) ) ); 841 842 // return Path_FixSlashes( pchBuffer ); 843 // } 844 // else 845 // { 846 // return ""; 847 // } 848 // } 849 850 851 // ----------------------------------------------------------------------------------------------------- 852 // Purpose: Returns the root of the directory the system wants us to store user documents in 853 // ----------------------------------------------------------------------------------------------------- 854 std::string GetUserDocumentsPath() 855 { 856 #if defined( WIN32 ) 857 WCHAR rwchPath[MAX_PATH]; 858 859 if ( !SUCCEEDED( SHGetFolderPathW( NULL, CSIDL_MYDOCUMENTS | CSIDL_FLAG_CREATE, NULL, 0, rwchPath ) ) ) 860 { 861 return ""; 862 } 863 864 // Convert the path to UTF-8 and store in the output 865 std::string sUserPath = UTF16to8( rwchPath ); 866 867 return sUserPath; 868 #elif defined( OSX ) 869 @autoreleasepool { 870 NSArray *paths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES ); 871 if ( [paths count] == 0 ) 872 { 873 return ""; 874 } 875 876 return [[paths objectAtIndex:0] UTF8String]; 877 } 878 #elif defined( LINUX ) 879 // @todo: not solved/changed as part of OSX - still not real - just removed old class based steam cut and paste 880 const char *pchHome = getenv( "HOME" ); 881 if ( pchHome == NULL ) 882 { 883 return ""; 884 } 885 return pchHome; 886 #endif 887 } 888 889 890 // ----------------------------------------------------------------------------------------------------- 891 // Purpose: deletes / unlinks a single file 892 // ----------------------------------------------------------------------------------------------------- 893 bool Path_UnlinkFile( const std::string &strFilename ) 894 { 895 #if defined( WIN32 ) 896 std::wstring wsFilename = UTF8to16( strFilename.c_str() ); 897 return ( 0 != DeleteFileW( wsFilename.c_str() ) ); 898 #else 899 return ( 0 == unlink( strFilename.c_str() ) ); 900 #endif 901 }