Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

554 lines
12 KiB

  1. //========= Copyright � 1996-2006, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: VPC
  4. //
  5. //=====================================================================================//
  6. #include "vpc.h"
  7. #ifdef STEAM
  8. #include "tier1/utlintrusivelist.h"
  9. #endif
  10. #ifdef POSIX
  11. #define _O_RDONLY O_RDONLY
  12. #define _open open
  13. #include <sys/errno.h>
  14. #define _lseek lseek
  15. #define _read read
  16. #define _close close
  17. #define _stat stat
  18. #else
  19. #include <io.h>
  20. #include <ShellAPI.h>
  21. #endif
  22. CXMLWriter::CXMLWriter()
  23. {
  24. m_fp = NULL;
  25. m_b2010Format = false;
  26. }
  27. bool CXMLWriter::Open( const char *pFilename, bool b2010Format )
  28. {
  29. m_b2010Format = b2010Format;
  30. m_fp = fopen( pFilename, "wt" );
  31. if ( !m_fp )
  32. return false;
  33. if ( b2010Format )
  34. {
  35. Write( "\xEF\xBB\xBF<?xml version=\"1.0\" encoding=\"utf-8\"?>" );
  36. }
  37. else
  38. {
  39. // 2005 format
  40. Write( "<?xml version=\"1.0\" encoding=\"Windows-1252\"?>\n" );
  41. }
  42. return true;
  43. }
  44. void CXMLWriter::Close()
  45. {
  46. if ( !m_fp )
  47. return;
  48. fclose( m_fp );
  49. m_fp = NULL;
  50. }
  51. void CXMLWriter::PushNode( const char *pName )
  52. {
  53. Indent();
  54. char *pNewName = strdup( pName );
  55. m_Nodes.Push( pNewName );
  56. fprintf( m_fp, "<%s%s\n", pName, m_Nodes.Count() == 2 ? ">" : "" );
  57. }
  58. void CXMLWriter::PushNode( const char *pName, const char *pString )
  59. {
  60. Indent();
  61. char *pNewName = strdup( pName );
  62. m_Nodes.Push( pNewName );
  63. fprintf( m_fp, "<%s%s%s>\n", pName, pString ? " " : "", pString ? pString : "" );
  64. }
  65. void CXMLWriter::WriteLineNode( const char *pName, const char *pExtra, const char *pString )
  66. {
  67. Indent();
  68. fprintf( m_fp, "<%s%s>%s</%s>\n", pName, pExtra ? pExtra : "", pString, pName );
  69. }
  70. void CXMLWriter::PopNode( bool bEmitLabel )
  71. {
  72. char *pName;
  73. m_Nodes.Pop( pName );
  74. Indent();
  75. if ( bEmitLabel )
  76. {
  77. fprintf( m_fp, "</%s>\n", pName );
  78. }
  79. else
  80. {
  81. fprintf( m_fp, "/>\n", pName );
  82. }
  83. free( pName );
  84. }
  85. void CXMLWriter::Write( const char *p )
  86. {
  87. if ( m_fp )
  88. {
  89. Indent();
  90. fprintf( m_fp, "%s\n", p );
  91. }
  92. }
  93. CUtlString CXMLWriter::FixupXMLString( const char *pInput )
  94. {
  95. struct XMLFixup_t
  96. {
  97. const char *m_pFrom;
  98. const char *m_pTo;
  99. bool m_b2010Only;
  100. };
  101. // these tokens are not allowed in xml vcproj and be be escaped per msdev docs
  102. XMLFixup_t xmlFixups[] =
  103. {
  104. {"\"", "&quot;", false},
  105. {"\'", "&apos;", false},
  106. {"\n", "&#x0D;&#x0A;", false},
  107. {">", "&gt;", false},
  108. {"<", "&lt;", false},
  109. {"$(InputFileName)", "%(Filename)%(Extension)", true},
  110. {"$(InputName)", "%(Filename)", true},
  111. {"$(InputPath)", "%(FullPath)", true},
  112. {"$(InputDir)", "%(RootDir)%(Directory)", true},
  113. };
  114. bool bNeedsFixups = false;
  115. CUtlVector< bool > needsFixups;
  116. CUtlString outString;
  117. needsFixups.SetCount( ARRAYSIZE( xmlFixups ) );
  118. for ( int i = 0; i < ARRAYSIZE( xmlFixups ); i++ )
  119. {
  120. needsFixups[i] = false;
  121. if ( !m_b2010Format && xmlFixups[i].m_b2010Only )
  122. continue;
  123. if ( V_stristr( pInput, xmlFixups[i].m_pFrom ) )
  124. {
  125. needsFixups[i] = true;
  126. bNeedsFixups = true;
  127. }
  128. }
  129. if ( !bNeedsFixups )
  130. {
  131. outString = pInput;
  132. }
  133. else
  134. {
  135. int flip = 0;
  136. char bigBuffer[2][8192];
  137. V_strncpy( bigBuffer[flip], pInput, sizeof( bigBuffer[0] ) );
  138. for ( int i = 0; i < ARRAYSIZE( xmlFixups ); i++ )
  139. {
  140. if ( !needsFixups[i] )
  141. continue;
  142. if ( !V_StrSubst( bigBuffer[flip], xmlFixups[i].m_pFrom, xmlFixups[i].m_pTo, bigBuffer[flip ^ 1], sizeof( bigBuffer[0] ), false ) )
  143. {
  144. g_pVPC->VPCError( "XML overflow - Increase big buffer" );
  145. }
  146. flip ^= 1;
  147. }
  148. outString = bigBuffer[flip];
  149. }
  150. return outString;
  151. }
  152. void CXMLWriter::Indent()
  153. {
  154. for ( int i = 0; i < m_Nodes.Count(); i++ )
  155. {
  156. if ( m_b2010Format )
  157. {
  158. fprintf( m_fp, " " );
  159. }
  160. else
  161. {
  162. fprintf( m_fp, "\t" );
  163. }
  164. }
  165. }
  166. //-----------------------------------------------------------------------------
  167. // Sys_LoadFile
  168. //
  169. //-----------------------------------------------------------------------------
  170. int Sys_LoadFile( const char* filename, void** bufferptr, bool bText )
  171. {
  172. int handle;
  173. long length;
  174. char* buffer;
  175. *bufferptr = NULL;
  176. if ( !Sys_Exists( filename ) )
  177. return ( -1 );
  178. int flags = _O_RDONLY;
  179. #if !defined( POSIX )
  180. flags |= (bText ? _O_TEXT : _O_BINARY);
  181. #endif
  182. handle = _open( filename, flags );
  183. if ( handle == -1 )
  184. Sys_Error( "Sys_LoadFile(): Error opening %s: %s", filename, strerror( errno ) );
  185. length = _lseek( handle, 0, SEEK_END );
  186. _lseek( handle, 0, SEEK_SET );
  187. buffer = ( char* )malloc( length+1 );
  188. int bytesRead = _read( handle, buffer, length );
  189. if ( !bText && ( bytesRead != length ) )
  190. Sys_Error( "Sys_LoadFile(): read truncated failure" );
  191. _close( handle );
  192. // text mode is truncated, add null for parsing
  193. buffer[bytesRead] = '\0';
  194. *bufferptr = ( void* )buffer;
  195. return ( length );
  196. }
  197. //-----------------------------------------------------------------------------
  198. // Sys_FileLength
  199. //-----------------------------------------------------------------------------
  200. long Sys_FileLength( const char* filename, bool bText )
  201. {
  202. long length;
  203. if ( filename )
  204. {
  205. int flags = _O_RDONLY;
  206. #if !defined( POSIX )
  207. flags |= (bText ? _O_TEXT : _O_BINARY);
  208. #endif
  209. int handle = _open( filename, flags );
  210. if ( handle == -1 )
  211. {
  212. // file does not exist
  213. return ( -1 );
  214. }
  215. length = _lseek( handle, 0, SEEK_END );
  216. _close( handle );
  217. }
  218. else
  219. {
  220. return ( -1 );
  221. }
  222. return ( length );
  223. }
  224. //-----------------------------------------------------------------------------
  225. // Sys_StripPath
  226. //
  227. // Removes path portion from a fully qualified name, leaving filename and extension.
  228. //-----------------------------------------------------------------------------
  229. void Sys_StripPath( const char* inpath, char* outpath )
  230. {
  231. const char* src;
  232. src = inpath + strlen( inpath );
  233. while ( ( src != inpath ) && ( *( src-1 ) != '\\' ) && ( *( src-1 ) != '/' ) && ( *( src-1 ) != ':' ) )
  234. src--;
  235. strcpy( outpath,src );
  236. }
  237. //-----------------------------------------------------------------------------
  238. // Sys_Exists
  239. //
  240. // Returns TRUE if file exists.
  241. //-----------------------------------------------------------------------------
  242. bool Sys_Exists( const char* filename )
  243. {
  244. FILE* test;
  245. if ( ( test = fopen( filename, "rb" ) ) == NULL )
  246. return ( false );
  247. fclose( test );
  248. return ( true );
  249. }
  250. //-----------------------------------------------------------------------------
  251. // Sys_FileInfo
  252. //-----------------------------------------------------------------------------
  253. bool Sys_FileInfo( const char *pFilename, int64 &nFileSize, int64 &nModifyTime )
  254. {
  255. struct _stat statData;
  256. int rt = _stat( pFilename, &statData );
  257. if ( rt != 0 )
  258. return false;
  259. nFileSize = statData.st_size;
  260. nModifyTime = statData.st_mtime;
  261. return true;
  262. }
  263. //-----------------------------------------------------------------------------
  264. // Ignores allowable trailing characters.
  265. //-----------------------------------------------------------------------------
  266. bool Sys_StringToBool( const char *pString )
  267. {
  268. if ( !V_strnicmp( pString, "no", 2 ) ||
  269. !V_strnicmp( pString, "off", 3 ) ||
  270. !V_strnicmp( pString, "false", 5 ) ||
  271. !V_strnicmp( pString, "not set", 7 ) ||
  272. !V_strnicmp( pString, "disabled", 8 ) ||
  273. !V_strnicmp( pString, "0", 1 ) )
  274. {
  275. // false
  276. return false;
  277. }
  278. else if ( !V_strnicmp( pString, "yes", 3 ) ||
  279. !V_strnicmp( pString, "on", 2 ) ||
  280. !V_strnicmp( pString, "true", 4 ) ||
  281. !V_strnicmp( pString, "set", 3 ) ||
  282. !V_strnicmp( pString, "enabled", 7 ) ||
  283. !V_strnicmp( pString, "1", 1 ) )
  284. {
  285. // true
  286. return true;
  287. }
  288. else
  289. {
  290. // unknown boolean expression
  291. g_pVPC->VPCSyntaxError( "Unknown boolean expression '%s'", pString );
  292. }
  293. // assume false
  294. return false;
  295. }
  296. bool Sys_ReplaceString( const char *pStream, const char *pSearch, const char *pReplace, char *pOutBuff, int outBuffSize )
  297. {
  298. const char *pFind;
  299. const char *pStart = pStream;
  300. char *pOut = pOutBuff;
  301. int len;
  302. bool bReplaced = false;
  303. while ( 1 )
  304. {
  305. // find sub string
  306. pFind = V_stristr( pStart, pSearch );
  307. if ( !pFind )
  308. {
  309. /// end of string
  310. len = strlen( pStart );
  311. pFind = pStart + len;
  312. memcpy( pOut, pStart, len );
  313. pOut += len;
  314. break;
  315. }
  316. else
  317. {
  318. bReplaced = true;
  319. }
  320. // copy up to sub string
  321. len = pFind - pStart;
  322. memcpy( pOut, pStart, len );
  323. pOut += len;
  324. // substitute new string
  325. len = strlen( pReplace );
  326. memcpy( pOut, pReplace, len );
  327. pOut += len;
  328. // advance past sub string
  329. pStart = pFind + strlen( pSearch );
  330. }
  331. *pOut = '\0';
  332. return bReplaced;
  333. }
  334. //--------------------------------------------------------------------------------
  335. // string match with wildcards.
  336. // '?' = match any char
  337. //--------------------------------------------------------------------------------
  338. bool Sys_StringPatternMatch( char const *pSrcPattern, char const *pString )
  339. {
  340. for (;;)
  341. {
  342. char nPat = *(pSrcPattern++);
  343. char nString= *(pString++);
  344. if ( !( ( nPat == nString ) || ( ( nPat == '?' ) && nString ) ) )
  345. return false;
  346. if ( !nString )
  347. return true; // end of string
  348. }
  349. }
  350. bool Sys_EvaluateEnvironmentExpression( const char *pExpression, const char *pDefault, char *pOutBuff, int nOutBuffSize )
  351. {
  352. char *pEnvVarName = (char*)StringAfterPrefix( pExpression, "$env(" );
  353. if ( !pEnvVarName )
  354. {
  355. // not an environment specification
  356. return false;
  357. }
  358. char *pLastChar = &pEnvVarName[ V_strlen( pEnvVarName ) - 1 ];
  359. if ( !*pEnvVarName || *pLastChar != ')' )
  360. {
  361. g_pVPC->VPCSyntaxError( "$env() must have a closing ')' in \"%s\"\n", pExpression );
  362. }
  363. // get the contents of the $env( blah..blah ) expressions
  364. // handles expresions that could have whitepsaces
  365. g_pVPC->GetScript().PushScript( pExpression, pEnvVarName );
  366. const char *pToken = g_pVPC->GetScript().GetToken( false );
  367. g_pVPC->GetScript().PopScript();
  368. if ( pToken && pToken[0] )
  369. {
  370. const char *pResolve = getenv( pToken );
  371. if ( !pResolve )
  372. {
  373. // not defined, use default
  374. pResolve = pDefault ? pDefault : "";
  375. }
  376. V_strncpy( pOutBuff, pResolve, nOutBuffSize );
  377. }
  378. return true;
  379. }
  380. //-----------------------------------------------------------------------------
  381. // Given some arbitrary case filename, provides what the OS thinks it is.
  382. // Windows specific. Returns false if file cannot be resolved (i.e. does not exist).
  383. //-----------------------------------------------------------------------------
  384. bool Sys_GetActualFilenameCase( const char *pFilename, char *pOutputBuffer, int nOutputBufferSize )
  385. {
  386. #if defined( _WINDOWS )
  387. char filenameBuffer[MAX_PATH];
  388. V_strncpy( filenameBuffer, pFilename, sizeof( filenameBuffer ) );
  389. V_FixSlashes( filenameBuffer );
  390. V_RemoveDotSlashes( filenameBuffer );
  391. int nFilenameLength = V_strlen( filenameBuffer );
  392. CUtlString actualFilename;
  393. // march along filename, resolving up to next seperator
  394. int nLastComponentStart = 0;
  395. bool bAddSeparator = false;
  396. int i = 0;
  397. while ( i < nFilenameLength )
  398. {
  399. // cannot resolve these, emit as-is
  400. if ( !V_strnicmp( filenameBuffer + i, ".\\", 2 ) )
  401. {
  402. i += 2;
  403. actualFilename += CUtlString( ".\\" );
  404. continue;
  405. }
  406. // cannot resolve these, emit as-is
  407. if ( !V_strnicmp( filenameBuffer + i, "..\\", 3 ) )
  408. {
  409. i += 3;
  410. actualFilename += CUtlString( "..\\" );
  411. continue;
  412. }
  413. // skip until path separator
  414. while ( i < nFilenameLength && filenameBuffer[i] != '\\' )
  415. {
  416. ++i;
  417. }
  418. bool bFoundSeparator = ( i < nFilenameLength );
  419. // truncate at separator, windows resolves each component in pieces
  420. filenameBuffer[i] = 0;
  421. SHFILEINFOA info = {0};
  422. HRESULT hr = SHGetFileInfoA( filenameBuffer, 0, &info, sizeof( info ), SHGFI_DISPLAYNAME );
  423. if ( SUCCEEDED( hr ) )
  424. {
  425. // reassemble based on actual component
  426. if ( bAddSeparator )
  427. {
  428. actualFilename += CUtlString( "\\" );
  429. }
  430. actualFilename += CUtlString( info.szDisplayName );
  431. }
  432. else
  433. {
  434. return false;
  435. }
  436. // restore path separator
  437. if ( bFoundSeparator )
  438. {
  439. filenameBuffer[i] = '\\';
  440. }
  441. ++i;
  442. nLastComponentStart = i;
  443. bAddSeparator = true;
  444. }
  445. V_strncpy( pOutputBuffer, actualFilename.Get(), nOutputBufferSize );
  446. return true;
  447. #else
  448. return false;
  449. #endif
  450. }
  451. //-----------------------------------------------------------------------------
  452. // Given some arbitrary case filename, determine if OS version matches.
  453. //-----------------------------------------------------------------------------
  454. bool Sys_IsFilenameCaseConsistent( const char *pFilename, char *pOutputBuffer, int nOutputBufferSize )
  455. {
  456. V_strncpy( pOutputBuffer, pFilename, nOutputBufferSize );
  457. // normalize the provided filename separators
  458. CUtlString filename = pFilename;
  459. V_FixSlashes( filename.Get() );
  460. V_RemoveDotSlashes( filename.Get() );
  461. if ( !Sys_GetActualFilenameCase( filename.Get(), pOutputBuffer, nOutputBufferSize ) )
  462. return false;
  463. if ( !V_strcmp( filename.Get(), pOutputBuffer ) )
  464. return true;
  465. return false;
  466. }