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 */
Path_GetExecutablePath()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 */
Path_GetWorkingDirectory()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. */
Path_SetWorkingDirectory(const std::string & sPath)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. */
Path_GetTemporaryDirectory()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 */
Path_StripFilename(const std::string & sPath,char slash)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. */
Path_StripDirectory(const std::string & sPath,char slash)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. */
Path_StripExtension(const std::string & sPath)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). */
Path_GetExtension(const std::string & sPath)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 
Path_IsAbsolute(const std::string & sPath)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 */
Path_MakeAbsolute(const std::string & sRelativePath,const std::string & sBasePath)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 */
Path_FixSlashes(const std::string & sPath,char slash)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 
Path_GetSlash()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 */
Path_Join(const std::string & first,const std::string & second,char 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 
Path_Join(const std::string & first,const std::string & second,const std::string & third,char slash)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 
Path_Join(const std::string & first,const std::string & second,const std::string & third,const std::string & fourth,char slash)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 
Path_Join(const std::string & first,const std::string & second,const std::string & third,const std::string & fourth,const std::string & fifth,char slash)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 
Path_RemoveTrailingSlash(const std::string & sRawPath,char slash)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 */
Path_Compact(const std::string & sRawPath,char slash)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). */
Path_IsSamePath(const std::string & sPath1,const std::string & sPath2)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 */
Path_GetThisModulePath()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 */
Path_IsDirectory(const std::string & sPath)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 */
Path_IsAppBundle(const std::string & sPath)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 //-----------------------------------------------------------------------------
Path_Exists(const std::string & sPath)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 //-----------------------------------------------------------------------------
Path_FindParentDirectoryRecursively(const std::string & strStartDirectory,const std::string & strDirectoryName)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 //-----------------------------------------------------------------------------
Path_FindParentSubDirectoryRecursively(const std::string & strStartDirectory,const std::string & strDirectoryName)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 //-----------------------------------------------------------------------------
Path_ReadBinaryFile(const std::string & strFilename,int * pSize)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 
Path_ReadBinaryFile(const std::string & strFilename,unsigned char * pBuffer,uint32_t unSize)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 
Path_WriteBinaryFile(const std::string & strFilename,unsigned char * pData,unsigned nSize)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 
Path_ReadTextFile(const std::string & strFilename)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 
Path_MakeWritable(const std::string & strFilename)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 
Path_WriteStringToTextFile(const std::string & strFilename,const char * pchData)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 
Path_WriteStringToTextFileAtomic(const std::string & strFilename,const char * pchData)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 README.mozilla 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 README.mozilla 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 // -----------------------------------------------------------------------------------------------------
GetUserDocumentsPath()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 // -----------------------------------------------------------------------------------------------------
Path_UnlinkFile(const std::string & strFilename)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 }
902