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.

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