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;
|
|
// 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;
|
|
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 );
|
|
|
|
virtual const char **GetParms() const { return (const char**)m_ppParms; }
|
|
|
|
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 )
|
|
{
|
|
const bool bNoAutoArgs = (Plat_GetEnv("autoargs")) == nullptr;
|
|
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 ( !bNoAutoArgs && *pSrc == '@' )
|
|
{
|
|
if ( pSrc == commandline || (!bInQuotes && V_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';
|
|
|
|
size_t len = strlen( szFull ) + 1;
|
|
m_pszCmdLine = new char[len];
|
|
memcpy( m_pszCmdLine, szFull, len );
|
|
|
|
#if defined( POSIX )
|
|
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;
|
|
}
|
|
|
|
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] );
|
|
}
|