tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }