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
676 lines
16 KiB
//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======// |
|
// |
|
// Purpose: |
|
// |
|
// $Workfile: $ |
|
// $NoKeywords: $ |
|
//===========================================================================// |
|
|
|
#include "pch_tier0.h" |
|
#include "tier0/icommandline.h" |
|
|
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <ctype.h> |
|
#include "tier0/dbg.h" |
|
#include "tier0_strtools.h" |
|
#include "tier1/strtools.h" // this is included for the definition of V_isspace() |
|
|
|
#ifdef PLATFORM_POSIX |
|
#include <limits.h> |
|
#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; |
|
|
|
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; |
|
virtual int ParmValue( const char *psz, int nDefaultVal ) const; |
|
virtual float ParmValue( const char *psz, float flDefaultVal ) const; |
|
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, intp 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, intp 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; |
|
const int MAX_CHARS = sizeof(cmdline) - 1; |
|
cmdline[MAX_CHARS] = 0; |
|
for ( int i = 0; i < argc; ++i ) |
|
{ |
|
strncat( cmdline, "\"", MAX_CHARS ); |
|
strncat( cmdline, argv[i], MAX_CHARS ); |
|
strncat( cmdline, "\"", MAX_CHARS ); |
|
strncat( cmdline, " ", MAX_CHARS ); |
|
} |
|
|
|
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 ]; |
|
|
|
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 && V_isspace( pSrc[-1] )) || (bInQuotes && pSrc == pInQuotesStart) ) |
|
{ |
|
LoadParametersFromFile( pSrc, pDst, sizeof( szFull ) - (pDst - szFull), bInQuotes ); |
|
if ( bInQuotes ) |
|
{ |
|
// Back up over the opening quote which has already been copied to pDst. |
|
// Otherwise we end up with an orphaned single quote which causes later |
|
// parsing problems. |
|
--pDst; |
|
Assert( *pDst == '\"' ); |
|
} |
|
// The opening quote, if any, is now gone. |
|
bInQuotes = false; |
|
continue; |
|
} |
|
} |
|
|
|
// Don't go past the end. |
|
if ( (pDst - szFull) >= (sizeof( szFull ) - 1) ) |
|
break; |
|
|
|
*pDst++ = *pSrc++; |
|
} |
|
|
|
*pDst = '\0'; |
|
|
|
size_t len = strlen( szFull ) + 1; |
|
m_pszCmdLine = new char[len]; |
|
memcpy( m_pszCmdLine, szFull, len ); |
|
|
|
#if defined( PLATFORM_PS3 ) |
|
Plat_SetCommandLine( m_pszCmdLine ); |
|
#endif |
|
|
|
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; |
|
intp n; |
|
size_t 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<size_t>( 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 ) |
|
{ |
|
intp 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 ) |
|
{ |
|
intp 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 && V_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 ( V_isspace( *pChar ) ) |
|
continue; |
|
|
|
pFirstLetter = pChar; |
|
continue; |
|
} |
|
|
|
// Here, we're in the middle of a word. Look for the end of it. |
|
if ( V_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 ( !V_tier0_stricmp( psz, m_ppParms[i] ) ) |
|
return i; |
|
} |
|
return 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] ); |
|
}
|
|
|