csgo-2018-source/tier0/commandline.cpp
2021-07-24 21:11:47 -07:00

677 lines
16 KiB
C++

//===== 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] );
}