Team Fortress 2 Source Code as on 22/4/2020
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.

696 lines
17 KiB

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