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.

676 lines
16 KiB

  1. //===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $Workfile: $
  6. // $NoKeywords: $
  7. //===========================================================================//
  8. #include "pch_tier0.h"
  9. #include "tier0/icommandline.h"
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include <ctype.h>
  14. #include "tier0/dbg.h"
  15. #include "tier0_strtools.h"
  16. #include "tier1/strtools.h" // this is included for the definition of V_isspace()
  17. #ifdef PLATFORM_POSIX
  18. #include <limits.h>
  19. #define _MAX_PATH PATH_MAX
  20. #endif
  21. // memdbgon must be the last include file in a .cpp file!!!
  22. #include "tier0/memdbgon.h"
  23. static const int MAX_PARAMETER_LEN = 128;
  24. //-----------------------------------------------------------------------------
  25. // Purpose: Implements ICommandLine
  26. //-----------------------------------------------------------------------------
  27. class CCommandLine : public ICommandLine
  28. {
  29. public:
  30. // Construction
  31. CCommandLine( void );
  32. virtual ~CCommandLine( void );
  33. // Implements ICommandLine
  34. virtual void CreateCmdLine( const char *commandline );
  35. virtual void CreateCmdLine( int argc, char **argv );
  36. virtual const char *GetCmdLine( void ) const;
  37. virtual const char *CheckParm( const char *psz, const char **ppszValue = 0 ) const;
  38. // A bool return of whether param exists, useful for just checking if param that is just a flag is set
  39. virtual bool HasParm( const char *psz ) const;
  40. virtual void RemoveParm( const char *parm );
  41. virtual void AppendParm( const char *pszParm, const char *pszValues );
  42. virtual int ParmCount() const;
  43. virtual int FindParm( const char *psz ) const;
  44. virtual const char* GetParm( int nIndex ) const;
  45. virtual const char *ParmValue( const char *psz, const char *pDefaultVal = NULL ) const;
  46. virtual int ParmValue( const char *psz, int nDefaultVal ) const;
  47. virtual float ParmValue( const char *psz, float flDefaultVal ) const;
  48. virtual void SetParm( int nIndex, char const *pParm );
  49. virtual const char **GetParms() const { return (const char**)m_ppParms; }
  50. private:
  51. enum
  52. {
  53. MAX_PARAMETER_LEN = 128,
  54. MAX_PARAMETERS = 256,
  55. };
  56. // When the commandline contains @name, it reads the parameters from that file
  57. void LoadParametersFromFile( const char *&pSrc, char *&pDst, intp maxDestLen, bool bInQuotes );
  58. // Parse command line...
  59. void ParseCommandLine();
  60. // Frees the command line arguments
  61. void CleanUpParms();
  62. // Adds an argument..
  63. void AddArgument( const char *pFirst, const char *pLast );
  64. // Copy of actual command line
  65. char *m_pszCmdLine;
  66. // Pointers to each argument...
  67. int m_nParmCount;
  68. char *m_ppParms[MAX_PARAMETERS];
  69. };
  70. //-----------------------------------------------------------------------------
  71. // Instance singleton and expose interface to rest of code
  72. //-----------------------------------------------------------------------------
  73. static CCommandLine g_CmdLine;
  74. ICommandLine *CommandLine()
  75. {
  76. return &g_CmdLine;
  77. }
  78. //-----------------------------------------------------------------------------
  79. // Purpose:
  80. //-----------------------------------------------------------------------------
  81. CCommandLine::CCommandLine( void )
  82. {
  83. m_pszCmdLine = NULL;
  84. m_nParmCount = 0;
  85. }
  86. //-----------------------------------------------------------------------------
  87. // Purpose:
  88. //-----------------------------------------------------------------------------
  89. CCommandLine::~CCommandLine( void )
  90. {
  91. CleanUpParms();
  92. delete[] m_pszCmdLine;
  93. }
  94. //-----------------------------------------------------------------------------
  95. // Read commandline from file instead...
  96. //-----------------------------------------------------------------------------
  97. void CCommandLine::LoadParametersFromFile( const char *&pSrc, char *&pDst, intp maxDestLen, bool bInQuotes )
  98. {
  99. // Suck out the file name
  100. char szFileName[ MAX_PATH ];
  101. char *pOut;
  102. char *pDestStart = pDst;
  103. if ( maxDestLen < 3 )
  104. return;
  105. // Skip the @ sign
  106. pSrc++;
  107. pOut = szFileName;
  108. char terminatingChar = ' ';
  109. if ( bInQuotes )
  110. terminatingChar = '\"';
  111. while ( *pSrc && *pSrc != terminatingChar )
  112. {
  113. *pOut++ = *pSrc++;
  114. if ( (pOut - szFileName) >= (MAX_PATH-1) )
  115. break;
  116. }
  117. *pOut = '\0';
  118. // Skip the space after the file name
  119. if ( *pSrc )
  120. pSrc++;
  121. // Now read in parameters from file
  122. FILE *fp = fopen( szFileName, "r" );
  123. if ( fp )
  124. {
  125. char c;
  126. c = (char)fgetc( fp );
  127. while ( c != EOF )
  128. {
  129. // Turn return characters into spaces
  130. if ( c == '\n' )
  131. c = ' ';
  132. *pDst++ = c;
  133. // Don't go past the end, and allow for our terminating space character AND a terminating null character.
  134. if ( (pDst - pDestStart) >= (maxDestLen-2) )
  135. break;
  136. // Get the next character, if there are more
  137. c = (char)fgetc( fp );
  138. }
  139. // Add a terminating space character
  140. *pDst++ = ' ';
  141. fclose( fp );
  142. }
  143. else
  144. {
  145. printf( "Parameter file '%s' not found, skipping...", szFileName );
  146. }
  147. }
  148. //-----------------------------------------------------------------------------
  149. // Creates a command line from the arguments passed in
  150. //-----------------------------------------------------------------------------
  151. void CCommandLine::CreateCmdLine( int argc, char **argv )
  152. {
  153. char cmdline[2048];
  154. cmdline[0] = 0;
  155. const int MAX_CHARS = sizeof(cmdline) - 1;
  156. cmdline[MAX_CHARS] = 0;
  157. for ( int i = 0; i < argc; ++i )
  158. {
  159. strncat( cmdline, "\"", MAX_CHARS );
  160. strncat( cmdline, argv[i], MAX_CHARS );
  161. strncat( cmdline, "\"", MAX_CHARS );
  162. strncat( cmdline, " ", MAX_CHARS );
  163. }
  164. CreateCmdLine( cmdline );
  165. }
  166. //-----------------------------------------------------------------------------
  167. // Purpose: Create a command line from the passed in string
  168. // Note that if you pass in a @filename, then the routine will read settings
  169. // from a file instead of the command line
  170. //-----------------------------------------------------------------------------
  171. void CCommandLine::CreateCmdLine( const char *commandline )
  172. {
  173. const bool bNoAutoArgs = (Plat_GetEnv("autoargs")) == nullptr;
  174. if ( m_pszCmdLine )
  175. {
  176. delete[] m_pszCmdLine;
  177. }
  178. char szFull[ 4096 ];
  179. char *pDst = szFull;
  180. const char *pSrc = commandline;
  181. bool bInQuotes = false;
  182. const char *pInQuotesStart = 0;
  183. while ( *pSrc )
  184. {
  185. // Is this an unslashed quote?
  186. if ( *pSrc == '"' )
  187. {
  188. if ( pSrc == commandline || ( pSrc[-1] != '/' && pSrc[-1] != '\\' ) )
  189. {
  190. bInQuotes = !bInQuotes;
  191. pInQuotesStart = pSrc + 1;
  192. }
  193. }
  194. if ( !bNoAutoArgs && *pSrc == '@' )
  195. {
  196. if ( pSrc == commandline || (!bInQuotes && V_isspace( pSrc[-1] )) || (bInQuotes && pSrc == pInQuotesStart) )
  197. {
  198. LoadParametersFromFile( pSrc, pDst, sizeof( szFull ) - (pDst - szFull), bInQuotes );
  199. continue;
  200. }
  201. }
  202. // Don't go past the end.
  203. if ( (pDst - szFull) >= (sizeof( szFull ) - 1) )
  204. break;
  205. *pDst++ = *pSrc++;
  206. }
  207. *pDst = '\0';
  208. size_t len = strlen( szFull ) + 1;
  209. m_pszCmdLine = new char[len];
  210. memcpy( m_pszCmdLine, szFull, len );
  211. #if defined( POSIX )
  212. Plat_SetCommandLine( m_pszCmdLine );
  213. #endif
  214. ParseCommandLine();
  215. }
  216. //-----------------------------------------------------------------------------
  217. // Finds a string in another string with a case insensitive test
  218. //-----------------------------------------------------------------------------
  219. static char * _stristr( char * pStr, const char * pSearch )
  220. {
  221. AssertValidStringPtr(pStr);
  222. AssertValidStringPtr(pSearch);
  223. if (!pStr || !pSearch)
  224. return 0;
  225. char* pLetter = pStr;
  226. // Check the entire string
  227. while (*pLetter != 0)
  228. {
  229. // Skip over non-matches
  230. if (tolower((unsigned char)*pLetter) == tolower((unsigned char)*pSearch))
  231. {
  232. // Check for match
  233. char const* pMatch = pLetter + 1;
  234. char const* pTest = pSearch + 1;
  235. while (*pTest != 0)
  236. {
  237. // We've run off the end; don't bother.
  238. if (*pMatch == 0)
  239. return 0;
  240. if (tolower((unsigned char)*pMatch) != tolower((unsigned char)*pTest))
  241. break;
  242. ++pMatch;
  243. ++pTest;
  244. }
  245. // Found a match!
  246. if (*pTest == 0)
  247. return pLetter;
  248. }
  249. ++pLetter;
  250. }
  251. return 0;
  252. }
  253. //-----------------------------------------------------------------------------
  254. // Purpose: Remove specified string ( and any args attached to it ) from command line
  255. // Input : *pszParm -
  256. //-----------------------------------------------------------------------------
  257. void CCommandLine::RemoveParm( const char *pszParm )
  258. {
  259. if ( !m_pszCmdLine )
  260. return;
  261. // Search for first occurrence of pszParm
  262. char *p, *found;
  263. char *pnextparam;
  264. intp n;
  265. size_t curlen;
  266. p = m_pszCmdLine;
  267. while ( *p )
  268. {
  269. curlen = strlen( p );
  270. found = _stristr( p, pszParm );
  271. if ( !found )
  272. break;
  273. pnextparam = found + 1;
  274. bool bHadQuote = false;
  275. if ( found > m_pszCmdLine && found[-1] == '\"' )
  276. bHadQuote = true;
  277. while ( pnextparam && *pnextparam && (*pnextparam != ' ') && (*pnextparam != '\"') )
  278. pnextparam++;
  279. if ( pnextparam && ( static_cast<size_t>( pnextparam - found ) > strlen( pszParm ) ) )
  280. {
  281. p = pnextparam;
  282. continue;
  283. }
  284. while ( pnextparam && *pnextparam && (*pnextparam != '-') && (*pnextparam != '+') )
  285. pnextparam++;
  286. if ( bHadQuote )
  287. {
  288. found--;
  289. }
  290. if ( pnextparam && *pnextparam )
  291. {
  292. // We are either at the end of the string, or at the next param. Just chop out the current param.
  293. n = curlen - ( pnextparam - p ); // # of characters after this param.
  294. memmove( found, pnextparam, n );
  295. found[n] = '\0';
  296. }
  297. else
  298. {
  299. // Clear out rest of string.
  300. n = pnextparam - found;
  301. memset( found, 0, n );
  302. }
  303. }
  304. // Strip and trailing ' ' characters left over.
  305. while ( 1 )
  306. {
  307. intp len = strlen( m_pszCmdLine );
  308. if ( len == 0 || m_pszCmdLine[ len - 1 ] != ' ' )
  309. break;
  310. m_pszCmdLine[len - 1] = '\0';
  311. }
  312. ParseCommandLine();
  313. }
  314. //-----------------------------------------------------------------------------
  315. // Purpose: Append parameter and argument values to command line
  316. // Input : *pszParm -
  317. // *pszValues -
  318. //-----------------------------------------------------------------------------
  319. void CCommandLine::AppendParm( const char *pszParm, const char *pszValues )
  320. {
  321. intp nNewLength = 0;
  322. char *pCmdString;
  323. nNewLength = strlen( pszParm ); // Parameter.
  324. if ( pszValues )
  325. nNewLength += strlen( pszValues ) + 1; // Values + leading space character.
  326. nNewLength++; // Terminal 0;
  327. if ( !m_pszCmdLine )
  328. {
  329. m_pszCmdLine = new char[ nNewLength ];
  330. strcpy( m_pszCmdLine, pszParm );
  331. if ( pszValues )
  332. {
  333. strcat( m_pszCmdLine, " " );
  334. strcat( m_pszCmdLine, pszValues );
  335. }
  336. ParseCommandLine();
  337. return;
  338. }
  339. // Remove any remnants from the current Cmd Line.
  340. RemoveParm( pszParm );
  341. nNewLength += strlen( m_pszCmdLine ) + 1 + 1;
  342. pCmdString = new char[ nNewLength ];
  343. memset( pCmdString, 0, nNewLength );
  344. strcpy ( pCmdString, m_pszCmdLine ); // Copy old command line.
  345. strcat ( pCmdString, " " ); // Put in a space
  346. strcat ( pCmdString, pszParm );
  347. if ( pszValues )
  348. {
  349. strcat( pCmdString, " " );
  350. strcat( pCmdString, pszValues );
  351. }
  352. // Kill off the old one
  353. delete[] m_pszCmdLine;
  354. // Point at the new command line.
  355. m_pszCmdLine = pCmdString;
  356. ParseCommandLine();
  357. }
  358. //-----------------------------------------------------------------------------
  359. // Purpose: Return current command line
  360. // Output : const char
  361. //-----------------------------------------------------------------------------
  362. const char *CCommandLine::GetCmdLine( void ) const
  363. {
  364. return m_pszCmdLine;
  365. }
  366. //-----------------------------------------------------------------------------
  367. // Purpose: Search for the parameter in the current commandline
  368. // Input : *psz -
  369. // **ppszValue -
  370. // Output : char
  371. //-----------------------------------------------------------------------------
  372. const char *CCommandLine::CheckParm( const char *psz, const char **ppszValue ) const
  373. {
  374. if ( ppszValue )
  375. *ppszValue = NULL;
  376. int i = FindParm( psz );
  377. if ( i == 0 )
  378. return NULL;
  379. if ( ppszValue )
  380. {
  381. if ( (i+1) >= m_nParmCount )
  382. {
  383. *ppszValue = NULL;
  384. }
  385. else
  386. {
  387. *ppszValue = m_ppParms[i+1];
  388. }
  389. }
  390. return m_ppParms[i];
  391. }
  392. //-----------------------------------------------------------------------------
  393. // Adds an argument..
  394. //-----------------------------------------------------------------------------
  395. void CCommandLine::AddArgument( const char *pFirst, const char *pLast )
  396. {
  397. if ( pLast == pFirst )
  398. return;
  399. if ( m_nParmCount >= MAX_PARAMETERS )
  400. Error( "CCommandLine::AddArgument: exceeded %d parameters", MAX_PARAMETERS );
  401. size_t nLen = ( pLast - pFirst ) + 1;
  402. m_ppParms[m_nParmCount] = new char[nLen];
  403. memcpy( m_ppParms[m_nParmCount], pFirst, nLen - 1 );
  404. m_ppParms[m_nParmCount][nLen - 1] = 0;
  405. ++m_nParmCount;
  406. }
  407. //-----------------------------------------------------------------------------
  408. // Parse command line...
  409. //-----------------------------------------------------------------------------
  410. void CCommandLine::ParseCommandLine()
  411. {
  412. CleanUpParms();
  413. if (!m_pszCmdLine)
  414. return;
  415. const char *pChar = m_pszCmdLine;
  416. while ( *pChar && V_isspace(*pChar) )
  417. {
  418. ++pChar;
  419. }
  420. bool bInQuotes = false;
  421. const char *pFirstLetter = NULL;
  422. for ( ; *pChar; ++pChar )
  423. {
  424. if ( bInQuotes )
  425. {
  426. if ( *pChar != '\"' )
  427. continue;
  428. AddArgument( pFirstLetter, pChar );
  429. pFirstLetter = NULL;
  430. bInQuotes = false;
  431. continue;
  432. }
  433. // Haven't started a word yet...
  434. if ( !pFirstLetter )
  435. {
  436. if ( *pChar == '\"' )
  437. {
  438. bInQuotes = true;
  439. pFirstLetter = pChar + 1;
  440. continue;
  441. }
  442. if ( V_isspace( *pChar ) )
  443. continue;
  444. pFirstLetter = pChar;
  445. continue;
  446. }
  447. // Here, we're in the middle of a word. Look for the end of it.
  448. if ( V_isspace( *pChar ) )
  449. {
  450. AddArgument( pFirstLetter, pChar );
  451. pFirstLetter = NULL;
  452. }
  453. }
  454. if ( pFirstLetter )
  455. {
  456. AddArgument( pFirstLetter, pChar );
  457. }
  458. }
  459. //-----------------------------------------------------------------------------
  460. // Individual command line arguments
  461. //-----------------------------------------------------------------------------
  462. void CCommandLine::CleanUpParms()
  463. {
  464. for ( int i = 0; i < m_nParmCount; ++i )
  465. {
  466. delete [] m_ppParms[i];
  467. m_ppParms[i] = NULL;
  468. }
  469. m_nParmCount = 0;
  470. }
  471. //-----------------------------------------------------------------------------
  472. // Returns individual command line arguments
  473. //-----------------------------------------------------------------------------
  474. int CCommandLine::ParmCount() const
  475. {
  476. return m_nParmCount;
  477. }
  478. int CCommandLine::FindParm( const char *psz ) const
  479. {
  480. // Start at 1 so as to not search the exe name
  481. for ( int i = 1; i < m_nParmCount; ++i )
  482. {
  483. if ( !V_tier0_stricmp( psz, m_ppParms[i] ) )
  484. return i;
  485. }
  486. return 0;
  487. }
  488. bool CCommandLine::HasParm( const char *psz ) const
  489. {
  490. return ( FindParm( psz ) != 0 );
  491. }
  492. const char* CCommandLine::GetParm( int nIndex ) const
  493. {
  494. Assert( (nIndex >= 0) && (nIndex < m_nParmCount) );
  495. if ( (nIndex < 0) || (nIndex >= m_nParmCount) )
  496. return "";
  497. return m_ppParms[nIndex];
  498. }
  499. void CCommandLine::SetParm( int nIndex, char const *pParm )
  500. {
  501. if ( pParm )
  502. {
  503. Assert( (nIndex >= 0) && (nIndex < m_nParmCount) );
  504. if ( (nIndex >= 0) && (nIndex < m_nParmCount) )
  505. {
  506. if ( m_ppParms[nIndex] )
  507. delete[] m_ppParms[nIndex];
  508. m_ppParms[nIndex] = strdup( pParm );
  509. }
  510. }
  511. }
  512. //-----------------------------------------------------------------------------
  513. // Returns the argument after the one specified, or the default if not found
  514. //-----------------------------------------------------------------------------
  515. const char *CCommandLine::ParmValue( const char *psz, const char *pDefaultVal ) const
  516. {
  517. int nIndex = FindParm( psz );
  518. if (( nIndex == 0 ) || (nIndex == m_nParmCount - 1))
  519. return pDefaultVal;
  520. // Probably another cmdline parameter instead of a valid arg if it starts with '+' or '-'
  521. if ( m_ppParms[nIndex + 1][0] == '-' || m_ppParms[nIndex + 1][0] == '+' )
  522. return pDefaultVal;
  523. return m_ppParms[nIndex + 1];
  524. }
  525. int CCommandLine::ParmValue( const char *psz, int nDefaultVal ) const
  526. {
  527. int nIndex = FindParm( psz );
  528. if (( nIndex == 0 ) || (nIndex == m_nParmCount - 1))
  529. return nDefaultVal;
  530. // Probably another cmdline parameter instead of a valid arg if it starts with '+' or '-'
  531. if ( m_ppParms[nIndex + 1][0] == '-' || m_ppParms[nIndex + 1][0] == '+' )
  532. return nDefaultVal;
  533. return atoi( m_ppParms[nIndex + 1] );
  534. }
  535. float CCommandLine::ParmValue( const char *psz, float flDefaultVal ) const
  536. {
  537. int nIndex = FindParm( psz );
  538. if (( nIndex == 0 ) || (nIndex == m_nParmCount - 1))
  539. return flDefaultVal;
  540. // Probably another cmdline parameter instead of a valid arg if it starts with '+' or '-'
  541. if ( m_ppParms[nIndex + 1][0] == '-' || m_ppParms[nIndex + 1][0] == '+' )
  542. return flDefaultVal;
  543. return atof( m_ppParms[nIndex + 1] );
  544. }