//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $Workfile: $ // $NoKeywords: $ //===========================================================================// #include "pch_tier0.h" #include "tier0/icommandline.h" #include #include #include #include #include "tier0/dbg.h" #include "tier0/memdbgon.h" #ifdef POSIX #include #define _MAX_PATH PATH_MAX #endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" static const int MAX_PARAMETER_LEN = 128; //----------------------------------------------------------------------------- // Purpose: Implements ICommandLine //----------------------------------------------------------------------------- class CCommandLine : public ICommandLine { public: // Construction CCommandLine( void ); virtual ~CCommandLine( void ); // Implements ICommandLine virtual void CreateCmdLine( const char *commandline ); virtual void CreateCmdLine( int argc, char **argv ); virtual const char *GetCmdLine( void ) const; virtual const char *CheckParm( const char *psz, const char **ppszValue = 0 ) const; // A bool return of whether param exists, useful for just checking if param that is just a flag is set virtual bool HasParm( const char *psz ) const; virtual void RemoveParm( const char *parm ); virtual void AppendParm( const char *pszParm, const char *pszValues ); virtual int ParmCount() const; virtual int FindParm( const char *psz ) const; virtual const char* GetParm( int nIndex ) const; virtual const char *ParmValue( const char *psz, const char *pDefaultVal = NULL ) const OVERRIDE; virtual int ParmValue( const char *psz, int nDefaultVal ) const OVERRIDE; virtual float ParmValue( const char *psz, float flDefaultVal ) const OVERRIDE; virtual const char *ParmValueByIndex( int nIndex, const char *pDefaultVal = 0 ) const OVERRIDE; virtual void SetParm( int nIndex, char const *pParm ); private: enum { MAX_PARAMETER_LEN = 128, MAX_PARAMETERS = 256, }; // When the commandline contains @name, it reads the parameters from that file void LoadParametersFromFile( const char *&pSrc, char *&pDst, int maxDestLen, bool bInQuotes ); // Parse command line... void ParseCommandLine(); // Frees the command line arguments void CleanUpParms(); // Adds an argument.. void AddArgument( const char *pFirst, const char *pLast ); // Copy of actual command line char *m_pszCmdLine; // Pointers to each argument... int m_nParmCount; char *m_ppParms[MAX_PARAMETERS]; }; //----------------------------------------------------------------------------- // Instance singleton and expose interface to rest of code //----------------------------------------------------------------------------- static CCommandLine g_CmdLine; ICommandLine *CommandLine() { return &g_CmdLine; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CCommandLine::CCommandLine( void ) { m_pszCmdLine = NULL; m_nParmCount = 0; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CCommandLine::~CCommandLine( void ) { CleanUpParms(); delete[] m_pszCmdLine; } //----------------------------------------------------------------------------- // Read commandline from file instead... //----------------------------------------------------------------------------- void CCommandLine::LoadParametersFromFile( const char *&pSrc, char *&pDst, int maxDestLen, bool bInQuotes ) { // Suck out the file name char szFileName[ _MAX_PATH ]; char *pOut; char *pDestStart = pDst; if ( maxDestLen < 3 ) return; // Skip the @ sign pSrc++; pOut = szFileName; char terminatingChar = ' '; if ( bInQuotes ) terminatingChar = '\"'; while ( *pSrc && *pSrc != terminatingChar ) { *pOut++ = *pSrc++; if ( (pOut - szFileName) >= (_MAX_PATH-1) ) break; } *pOut = '\0'; // Skip the space after the file name if ( *pSrc ) pSrc++; // Now read in parameters from file FILE *fp = fopen( szFileName, "r" ); if ( fp ) { char c; c = (char)fgetc( fp ); while ( c != EOF ) { // Turn return characters into spaces if ( c == '\n' ) c = ' '; *pDst++ = c; // Don't go past the end, and allow for our terminating space character AND a terminating null character. if ( (pDst - pDestStart) >= (maxDestLen-2) ) break; // Get the next character, if there are more c = (char)fgetc( fp ); } // Add a terminating space character *pDst++ = ' '; fclose( fp ); } else { printf( "Parameter file '%s' not found, skipping...", szFileName ); } } //----------------------------------------------------------------------------- // Creates a command line from the arguments passed in //----------------------------------------------------------------------------- void CCommandLine::CreateCmdLine( int argc, char **argv ) { char cmdline[ 2048 ]; cmdline[ 0 ] = 0; char *dest = cmdline; size_t size = sizeof( cmdline ); const char *space = ""; for ( int i = 0; i < argc; ++i ) { // We need room for: space, arg, 2 quotes, and a nil. Assert( strlen( space ) + strlen( argv[ i ] ) + 2 + 1 <= size ); if ( size ) { _snprintf( dest, size, "%s\"%s\"", space, argv[ i ] ); dest[ size - 1 ] = 0; } size_t len = strlen( dest ); size -= len; dest += len; space = " "; } CreateCmdLine( cmdline ); } //----------------------------------------------------------------------------- // Purpose: Create a command line from the passed in string // Note that if you pass in a @filename, then the routine will read settings // from a file instead of the command line //----------------------------------------------------------------------------- void CCommandLine::CreateCmdLine( const char *commandline ) { if ( m_pszCmdLine ) { delete[] m_pszCmdLine; } char szFull[ 4096 ]; szFull[0] = '\0'; char *pDst = szFull; const char *pSrc = commandline; bool bInQuotes = false; const char *pInQuotesStart = 0; while ( *pSrc ) { // Is this an unslashed quote? if ( *pSrc == '"' ) { if ( pSrc == commandline || ( pSrc[-1] != '/' && pSrc[-1] != '\\' ) ) { bInQuotes = !bInQuotes; pInQuotesStart = pSrc + 1; } } if ( *pSrc == '@' ) { if ( pSrc == commandline || (!bInQuotes && isspace( pSrc[-1] )) || (bInQuotes && pSrc == pInQuotesStart) ) { LoadParametersFromFile( pSrc, pDst, sizeof( szFull ) - (pDst - szFull), bInQuotes ); continue; } } // Don't go past the end. if ( (pDst - szFull) >= (sizeof( szFull ) - 1) ) break; *pDst++ = *pSrc++; } *pDst = '\0'; int len = strlen( szFull ) + 1; m_pszCmdLine = new char[len]; memcpy( m_pszCmdLine, szFull, len ); ParseCommandLine(); } //----------------------------------------------------------------------------- // Finds a string in another string with a case insensitive test //----------------------------------------------------------------------------- static char * _stristr( char * pStr, const char * pSearch ) { AssertValidStringPtr(pStr); AssertValidStringPtr(pSearch); if (!pStr || !pSearch) return 0; char* pLetter = pStr; // Check the entire string while (*pLetter != 0) { // Skip over non-matches if (tolower((unsigned char)*pLetter) == tolower((unsigned char)*pSearch)) { // Check for match char const* pMatch = pLetter + 1; char const* pTest = pSearch + 1; while (*pTest != 0) { // We've run off the end; don't bother. if (*pMatch == 0) return 0; if (tolower((unsigned char)*pMatch) != tolower((unsigned char)*pTest)) break; ++pMatch; ++pTest; } // Found a match! if (*pTest == 0) return pLetter; } ++pLetter; } return 0; } //----------------------------------------------------------------------------- // Purpose: Remove specified string ( and any args attached to it ) from command line // Input : *pszParm - //----------------------------------------------------------------------------- void CCommandLine::RemoveParm( const char *pszParm ) { if ( !m_pszCmdLine ) return; // Search for first occurrence of pszParm char *p, *found; char *pnextparam; int n; int curlen; p = m_pszCmdLine; while ( *p ) { curlen = strlen( p ); found = _stristr( p, pszParm ); if ( !found ) break; pnextparam = found + 1; bool bHadQuote = false; if ( found > m_pszCmdLine && found[-1] == '\"' ) bHadQuote = true; while ( pnextparam && *pnextparam && (*pnextparam != ' ') && (*pnextparam != '\"') ) pnextparam++; if ( pnextparam && ( static_cast( pnextparam - found ) > strlen( pszParm ) ) ) { p = pnextparam; continue; } while ( pnextparam && *pnextparam && (*pnextparam != '-') && (*pnextparam != '+') ) pnextparam++; if ( bHadQuote ) { found--; } if ( pnextparam && *pnextparam ) { // We are either at the end of the string, or at the next param. Just chop out the current param. n = curlen - ( pnextparam - p ); // # of characters after this param. memmove( found, pnextparam, n ); found[n] = '\0'; } else { // Clear out rest of string. n = pnextparam - found; memset( found, 0, n ); } } // Strip and trailing ' ' characters left over. while ( 1 ) { int len = strlen( m_pszCmdLine ); if ( len == 0 || m_pszCmdLine[ len - 1 ] != ' ' ) break; m_pszCmdLine[len - 1] = '\0'; } ParseCommandLine(); } //----------------------------------------------------------------------------- // Purpose: Append parameter and argument values to command line // Input : *pszParm - // *pszValues - //----------------------------------------------------------------------------- void CCommandLine::AppendParm( const char *pszParm, const char *pszValues ) { int nNewLength = 0; char *pCmdString; nNewLength = strlen( pszParm ); // Parameter. if ( pszValues ) nNewLength += strlen( pszValues ) + 1; // Values + leading space character. nNewLength++; // Terminal 0; if ( !m_pszCmdLine ) { m_pszCmdLine = new char[ nNewLength ]; strcpy( m_pszCmdLine, pszParm ); if ( pszValues ) { strcat( m_pszCmdLine, " " ); strcat( m_pszCmdLine, pszValues ); } ParseCommandLine(); return; } // Remove any remnants from the current Cmd Line. RemoveParm( pszParm ); nNewLength += strlen( m_pszCmdLine ) + 1 + 1; pCmdString = new char[ nNewLength ]; memset( pCmdString, 0, nNewLength ); strcpy ( pCmdString, m_pszCmdLine ); // Copy old command line. strcat ( pCmdString, " " ); // Put in a space strcat ( pCmdString, pszParm ); if ( pszValues ) { strcat( pCmdString, " " ); strcat( pCmdString, pszValues ); } // Kill off the old one delete[] m_pszCmdLine; // Point at the new command line. m_pszCmdLine = pCmdString; ParseCommandLine(); } //----------------------------------------------------------------------------- // Purpose: Return current command line // Output : const char //----------------------------------------------------------------------------- const char *CCommandLine::GetCmdLine( void ) const { return m_pszCmdLine; } //----------------------------------------------------------------------------- // Purpose: Search for the parameter in the current commandline // Input : *psz - // **ppszValue - // Output : char //----------------------------------------------------------------------------- const char *CCommandLine::CheckParm( const char *psz, const char **ppszValue ) const { if ( ppszValue ) *ppszValue = NULL; int i = FindParm( psz ); if ( i == 0 ) return NULL; if ( ppszValue ) { if ( (i+1) >= m_nParmCount ) { *ppszValue = NULL; } else { *ppszValue = m_ppParms[i+1]; } } return m_ppParms[i]; } //----------------------------------------------------------------------------- // Adds an argument.. //----------------------------------------------------------------------------- void CCommandLine::AddArgument( const char *pFirst, const char *pLast ) { if ( pLast <= pFirst ) return; if ( m_nParmCount >= MAX_PARAMETERS ) Error( "CCommandLine::AddArgument: exceeded %d parameters", MAX_PARAMETERS ); size_t nLen = pLast - pFirst + 1; m_ppParms[m_nParmCount] = new char[nLen]; memcpy( m_ppParms[m_nParmCount], pFirst, nLen - 1 ); m_ppParms[m_nParmCount][nLen - 1] = 0; ++m_nParmCount; } //----------------------------------------------------------------------------- // Parse command line... //----------------------------------------------------------------------------- void CCommandLine::ParseCommandLine() { CleanUpParms(); if (!m_pszCmdLine) return; const char *pChar = m_pszCmdLine; while ( *pChar && isspace(*pChar) ) { ++pChar; } bool bInQuotes = false; const char *pFirstLetter = NULL; for ( ; *pChar; ++pChar ) { if ( bInQuotes ) { if ( *pChar != '\"' ) continue; AddArgument( pFirstLetter, pChar ); pFirstLetter = NULL; bInQuotes = false; continue; } // Haven't started a word yet... if ( !pFirstLetter ) { if ( *pChar == '\"' ) { bInQuotes = true; pFirstLetter = pChar + 1; continue; } if ( isspace( *pChar ) ) continue; pFirstLetter = pChar; continue; } // Here, we're in the middle of a word. Look for the end of it. if ( isspace( *pChar ) ) { AddArgument( pFirstLetter, pChar ); pFirstLetter = NULL; } } if ( pFirstLetter ) { AddArgument( pFirstLetter, pChar ); } } //----------------------------------------------------------------------------- // Individual command line arguments //----------------------------------------------------------------------------- void CCommandLine::CleanUpParms() { for ( int i = 0; i < m_nParmCount; ++i ) { delete [] m_ppParms[i]; m_ppParms[i] = NULL; } m_nParmCount = 0; } //----------------------------------------------------------------------------- // Returns individual command line arguments //----------------------------------------------------------------------------- int CCommandLine::ParmCount() const { return m_nParmCount; } int CCommandLine::FindParm( const char *psz ) const { // Start at 1 so as to not search the exe name for ( int i = 1; i < m_nParmCount; ++i ) { if ( !_stricmp( psz, m_ppParms[i] ) ) return i; } return 0; } bool CCommandLine::HasParm( const char *psz ) const { return ( FindParm( psz ) != 0 ); } const char* CCommandLine::GetParm( int nIndex ) const { Assert( (nIndex >= 0) && (nIndex < m_nParmCount) ); if ( (nIndex < 0) || (nIndex >= m_nParmCount) ) return ""; return m_ppParms[nIndex]; } void CCommandLine::SetParm( int nIndex, char const *pParm ) { if ( pParm ) { Assert( (nIndex >= 0) && (nIndex < m_nParmCount) ); if ( (nIndex >= 0) && (nIndex < m_nParmCount) ) { if ( m_ppParms[nIndex] ) delete[] m_ppParms[nIndex]; m_ppParms[nIndex] = strdup( pParm ); } } } //----------------------------------------------------------------------------- // Returns the argument after the one specified, or the default if not found //----------------------------------------------------------------------------- const char *CCommandLine::ParmValue( const char *psz, const char *pDefaultVal ) const { int nIndex = FindParm( psz ); if (( nIndex == 0 ) || (nIndex == m_nParmCount - 1)) return pDefaultVal; // Probably another cmdline parameter instead of a valid arg if it starts with '+' or '-' if ( m_ppParms[nIndex + 1][0] == '-' || m_ppParms[nIndex + 1][0] == '+' ) return pDefaultVal; return m_ppParms[nIndex + 1]; } int CCommandLine::ParmValue( const char *psz, int nDefaultVal ) const { int nIndex = FindParm( psz ); if (( nIndex == 0 ) || (nIndex == m_nParmCount - 1)) return nDefaultVal; // Probably another cmdline parameter instead of a valid arg if it starts with '+' or '-' if ( m_ppParms[nIndex + 1][0] == '-' || m_ppParms[nIndex + 1][0] == '+' ) return nDefaultVal; return atoi( m_ppParms[nIndex + 1] ); } float CCommandLine::ParmValue( const char *psz, float flDefaultVal ) const { int nIndex = FindParm( psz ); if (( nIndex == 0 ) || (nIndex == m_nParmCount - 1)) return flDefaultVal; // Probably another cmdline parameter instead of a valid arg if it starts with '+' or '-' if ( m_ppParms[nIndex + 1][0] == '-' || m_ppParms[nIndex + 1][0] == '+' ) return flDefaultVal; return atof( m_ppParms[nIndex + 1] ); } const char *CCommandLine::ParmValueByIndex( int nIndex, const char *pDefaultVal ) const { if (( nIndex == 0 ) || (nIndex == m_nParmCount - 1)) return pDefaultVal; // Probably another cmdline parameter instead of a valid arg if it starts with '+' or '-' if ( m_ppParms[nIndex + 1][0] == '-' || m_ppParms[nIndex + 1][0] == '+' ) return pDefaultVal; return m_ppParms[nIndex + 1]; }