git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@1070 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2107 lines
45 KiB
C++
2107 lines
45 KiB
C++
//-------------------------------------------------------------------------
|
|
// Desc: Command line argument parser
|
|
// Tabs: 3
|
|
//
|
|
// Copyright (c) 2001, 2003, 2005-2007 Novell, Inc. All Rights Reserved.
|
|
//
|
|
// This library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
// License as published by the Free Software Foundation; version 2.1
|
|
// of the License.
|
|
//
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// Library Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; if not, contact Novell, Inc.
|
|
//
|
|
// To contact Novell about this file by physical or electronic mail,
|
|
// you may find current contact information at www.novell.com.
|
|
//
|
|
// $Id$
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "ftksys.h"
|
|
|
|
FSTATIC FLMBOOL matchesAtParams(
|
|
const char * pszStr);
|
|
|
|
FSTATIC FLMBOOL matchesEqualsAtParams(
|
|
const char * pszStr);
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
class F_Arg : public F_Object
|
|
{
|
|
private:
|
|
|
|
F_Arg(
|
|
const char * pszIdentifier,
|
|
const char * pszShortHelp,
|
|
FLMBOOL bCaseSensitive,
|
|
F_ARG_TYPE argType,
|
|
F_ARG_CONTENT_TYPE contentType);
|
|
|
|
virtual ~F_Arg();
|
|
|
|
const char * getIdentifier( void)
|
|
{
|
|
return( m_pszIdentifier);
|
|
}
|
|
|
|
FLMBOOL isPresent( void)
|
|
{
|
|
return( m_bIsPresent);
|
|
}
|
|
|
|
FLMUINT getValueCount( void)
|
|
{
|
|
return( m_uiValueCount);
|
|
}
|
|
|
|
FLMBOOL getCaseSensitive( void)
|
|
{
|
|
return( m_bCaseSensitive);
|
|
}
|
|
|
|
const char * getShortHelp( void)
|
|
{
|
|
return( m_pszShortHelp);
|
|
}
|
|
|
|
F_ARG_TYPE getArgType( void)
|
|
{
|
|
return( m_argType);
|
|
}
|
|
|
|
F_ARG_CONTENT_TYPE getContentType( void)
|
|
{
|
|
return( m_contentType);
|
|
}
|
|
|
|
F_ARG_VALIDATOR getValidator( void)
|
|
{
|
|
return( m_validator);
|
|
}
|
|
|
|
void * getValidatorData( void)
|
|
{
|
|
return( m_pvValidatorData);
|
|
}
|
|
|
|
F_Vector * getStringSet( void)
|
|
{
|
|
return( &m_stringSet);
|
|
}
|
|
|
|
FLMUINT getStringSetLen( void)
|
|
{
|
|
return( m_uiStringSetCount);
|
|
}
|
|
|
|
void getMinMax(
|
|
FLMUINT * puiMin,
|
|
FLMUINT * puiMax);
|
|
|
|
void getMinMax(
|
|
FLMINT * puiMin,
|
|
FLMINT * puiMax);
|
|
|
|
FLMUINT getUINT(
|
|
FLMUINT uiIndex);
|
|
|
|
FLMINT getINT(
|
|
FLMUINT uiIndex);
|
|
|
|
FLMBOOL getBOOL(
|
|
FLMUINT uiIndex);
|
|
|
|
const char * getString(
|
|
FLMUINT uiIndex);
|
|
|
|
void getString(
|
|
char * pszDestination,
|
|
FLMUINT uiDestinationBufferSize,
|
|
FLMUINT uiIndex);
|
|
|
|
void setPresent( void)
|
|
{
|
|
m_bIsPresent = TRUE;
|
|
}
|
|
|
|
RCODE addValue(
|
|
const char * pszVal);
|
|
|
|
const char * getValue(
|
|
FLMUINT uiIndex);
|
|
|
|
void setValidator(
|
|
F_ARG_VALIDATOR validator,
|
|
void * pvValidatorData)
|
|
{
|
|
m_validator = validator;
|
|
m_pvValidatorData = pvValidatorData;
|
|
}
|
|
|
|
void setMinMax(
|
|
FLMUINT uiMin,
|
|
FLMUINT uiMax)
|
|
{
|
|
m_uiMin = uiMin;
|
|
m_uiMax = uiMax;
|
|
}
|
|
|
|
void setMinMax(
|
|
FLMINT iMin,
|
|
FLMINT iMax)
|
|
{
|
|
m_iMin = iMin;
|
|
m_iMax = iMax;
|
|
}
|
|
|
|
RCODE addToStringSet(
|
|
const char * pszStr);
|
|
|
|
const char * m_pszIdentifier;
|
|
const char * m_pszShortHelp;
|
|
FLMBOOL m_bCaseSensitive;
|
|
F_ARG_TYPE m_argType;
|
|
F_ARG_CONTENT_TYPE m_contentType;
|
|
F_Vector m_valuesVec;
|
|
FLMUINT m_uiValueCount;
|
|
FLMBOOL m_bIsPresent;
|
|
F_ARG_VALIDATOR m_validator;
|
|
void * m_pvValidatorData;
|
|
FLMUINT m_uiMin;
|
|
FLMUINT m_uiMax;
|
|
FLMINT m_iMin;
|
|
FLMINT m_iMax;
|
|
F_Vector m_stringSet;
|
|
FLMUINT m_uiStringSetCount;
|
|
|
|
friend class F_ArgSet;
|
|
};
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
F_Arg::F_Arg(
|
|
const char * pszIdentifier,
|
|
const char * pszShortHelp,
|
|
FLMBOOL bCaseSensitive,
|
|
F_ARG_TYPE argType,
|
|
F_ARG_CONTENT_TYPE contentType)
|
|
{
|
|
m_pszIdentifier = pszIdentifier;
|
|
m_pszShortHelp = pszShortHelp;
|
|
m_bCaseSensitive = bCaseSensitive;
|
|
m_argType = argType;
|
|
m_contentType = contentType;
|
|
|
|
m_uiValueCount = 0;
|
|
m_bIsPresent = FALSE;
|
|
|
|
m_validator = NULL;
|
|
m_uiMin = 0xFFFFFFFF;
|
|
m_uiMax = 0xFFFFFFFF;
|
|
m_iMin = -1;
|
|
m_iMax = -1;
|
|
m_uiStringSetCount = 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
F_Arg::~F_Arg()
|
|
{
|
|
FLMUINT uiKill;
|
|
char * pszStr;
|
|
|
|
for( uiKill = 0; uiKill < m_uiValueCount; uiKill++)
|
|
{
|
|
pszStr = (char *)(m_valuesVec.getElementAt( uiKill));
|
|
|
|
if( pszStr)
|
|
{
|
|
f_free( &pszStr);
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
RCODE F_Arg::addValue(
|
|
const char * pszVal)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
char * pszNewVal = NULL;
|
|
|
|
switch( getContentType())
|
|
{
|
|
case F_ARG_CONTENT_SIGNED_INT:
|
|
{
|
|
// Allow escaped minus, i.e. foo \-1 to pass through a -1
|
|
// and not have it read as an option. Here, we detect that
|
|
// it made it through, so increment past the '-'
|
|
|
|
if( pszVal[ 0] == '\\')
|
|
{
|
|
pszVal++;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( RC_BAD( rc = f_alloc( f_strlen( pszVal) + 1, &pszNewVal)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_strcpy( pszNewVal, pszVal);
|
|
|
|
if( RC_BAD( rc = m_valuesVec.setElementAt( pszNewVal, m_uiValueCount)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pszNewVal = NULL;
|
|
m_uiValueCount++;
|
|
|
|
Exit:
|
|
|
|
if( pszNewVal)
|
|
{
|
|
f_free( &pszNewVal);
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
const char * F_Arg::getValue(
|
|
FLMUINT uiIndex)
|
|
{
|
|
f_assert( uiIndex < getValueCount());
|
|
|
|
return( (const char *)(m_valuesVec.getElementAt( uiIndex)));
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
RCODE F_Arg::addToStringSet(
|
|
const char * pszStr)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
|
|
if( RC_BAD( rc = m_stringSet.setElementAt(
|
|
(void *)pszStr, m_uiStringSetCount)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_uiStringSetCount++;
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
void F_Arg::getMinMax( FLMUINT * puiMin, FLMUINT * puiMax)
|
|
{
|
|
f_assert( getContentType() == F_ARG_CONTENT_UNSIGNED_INT);
|
|
f_assert( puiMin && puiMax);
|
|
|
|
*puiMin = m_uiMin;
|
|
*puiMax = m_uiMax;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
void F_Arg::getMinMax( FLMINT * piMin, FLMINT * piMax)
|
|
{
|
|
f_assert( getContentType() == F_ARG_CONTENT_SIGNED_INT);
|
|
f_assert( piMin && piMax);
|
|
|
|
*piMin = m_iMin;
|
|
*piMax = m_iMax;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
FLMUINT F_Arg::getUINT( FLMUINT uiIndex)
|
|
{
|
|
return( f_atoud( (const char *)m_valuesVec.getElementAt( uiIndex)));
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
FLMINT F_Arg::getINT( FLMUINT uiIndex)
|
|
{
|
|
return( f_atoi( (const char *)m_valuesVec.getElementAt( uiIndex)));
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
FLMBOOL F_Arg::getBOOL( FLMUINT uiIndex)
|
|
{
|
|
return( f_atobool( (const char *)m_valuesVec.getElementAt( uiIndex)));
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
const char * F_Arg::getString( FLMUINT uiIndex)
|
|
{
|
|
return( (const char *)(m_valuesVec.getElementAt( uiIndex)));
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
void F_Arg::getString(
|
|
char * pszDestination,
|
|
FLMUINT uiDestinationBufferSize,
|
|
FLMUINT uiIndex)
|
|
{
|
|
const char * pszStr = (const char *)m_valuesVec.getElementAt( uiIndex);
|
|
|
|
f_strncpy( pszDestination, pszStr, uiDestinationBufferSize - 1);
|
|
pszDestination[ uiDestinationBufferSize - 1] = 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
F_ArgSet::F_ArgSet()
|
|
{
|
|
m_uiArgVecIndex = 0;
|
|
m_uiArgc = 0;
|
|
m_pArgv = NULL;
|
|
m_pRepeatingArg = NULL;
|
|
m_uiOptionsVecLen = 0;
|
|
m_uiRequiredArgsVecLen = 0;
|
|
m_uiOptionalArgsVecLen = 0;
|
|
m_szExecBaseName[ 0] = 0;
|
|
m_pPrintfClient = NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
F_ArgSet::~F_ArgSet()
|
|
{
|
|
FLMUINT uiKill;
|
|
|
|
if( m_pArgv)
|
|
{
|
|
// Kill any dynamically allocated memory for the processed
|
|
// command line args
|
|
|
|
for( uiKill = 0; uiKill < m_uiArgc; uiKill++)
|
|
{
|
|
const char * pszStr = (const char *)m_pArgv->getElementAt( uiKill);
|
|
|
|
if( pszStr)
|
|
{
|
|
f_free( &pszStr);
|
|
}
|
|
}
|
|
|
|
m_pArgv->Release();
|
|
m_pArgv = NULL;
|
|
}
|
|
|
|
// Kill the dynamically allocated F_Arg objs
|
|
|
|
for( uiKill = 0; uiKill < m_uiArgVecIndex; uiKill++)
|
|
{
|
|
F_Arg * pArg = (F_Arg *)(m_argVec.getElementAt( uiKill));
|
|
|
|
if( pArg)
|
|
{
|
|
pArg->Release();
|
|
}
|
|
}
|
|
|
|
if( m_pPrintfClient)
|
|
{
|
|
m_pPrintfClient->Release();
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
RCODE FTKAPI F_ArgSet::setup(
|
|
IF_PrintfClient * pPrintfClient)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
|
|
if( (m_pPrintfClient = pPrintfClient) != NULL)
|
|
{
|
|
m_pPrintfClient->AddRef();
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
RCODE F_ArgSet::addArg(
|
|
const char * pszIdentifier,
|
|
const char * pszShortHelp,
|
|
FLMBOOL bCaseSensitive,
|
|
F_ARG_TYPE argType,
|
|
F_ARG_CONTENT_TYPE contentType
|
|
...)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
FLMUINT uiLoop;
|
|
const char * pszExistingStr;
|
|
F_Arg * pNewArg = NULL;
|
|
|
|
// Must have an identifier that's no longer than this, for the
|
|
// printUsage() to display on-screen correctly
|
|
|
|
f_assert( f_strlen( pszIdentifier) <= 16);
|
|
|
|
// Options with argumens need even shorter identifiers
|
|
|
|
if( (argType == F_ARG_OPTION) && (contentType != F_ARG_CONTENT_NONE))
|
|
{
|
|
f_assert( f_strlen( pszIdentifier) <= 12);
|
|
}
|
|
|
|
// Identifier cannot contain '='
|
|
|
|
f_assert( !f_strchr( pszIdentifier, '='));
|
|
|
|
// Validate that it's not there already based on the identifier
|
|
|
|
for( uiLoop = 0; uiLoop < m_uiArgVecIndex; uiLoop++)
|
|
{
|
|
pszExistingStr =
|
|
((F_Arg *)m_argVec.getElementAt( uiLoop))->getIdentifier();
|
|
|
|
if( f_strcmp( pszExistingStr, pszIdentifier) == 0)
|
|
{
|
|
// Duplicate arg, duplicate identifier found. If you are
|
|
// designing a utility and you got this error, it means you
|
|
// passed the same first argument to addArg() twice.
|
|
|
|
f_assert( 0);
|
|
}
|
|
}
|
|
|
|
// Enforce order of calling addArg() so it matches the way a utility
|
|
// is called on a command line. This is so a utility is coded up
|
|
// correctly, since the order matters.
|
|
|
|
if( argType == F_ARG_REQUIRED_ARG)
|
|
{
|
|
// You have to add required args first, then optionally optional
|
|
// args, then optionally a repeating arg. The rule is:
|
|
// required args -> optional args -> repeating arg.
|
|
//
|
|
// While all 3 of these types are optional in themselves, you
|
|
// can't have a required arg added after the next two, or
|
|
// an optional arg added after a repeating arg.
|
|
|
|
f_assert( (m_uiOptionalArgsVecLen == 0) && (!m_pRepeatingArg));
|
|
}
|
|
else if( argType == F_ARG_OPTIONAL_ARG)
|
|
{
|
|
// Can't add an optional arg after adding a repeating arg...the
|
|
// order matters!
|
|
|
|
f_assert( !m_pRepeatingArg);
|
|
}
|
|
|
|
// Can't have an embedded newline in the short help or it will
|
|
// mess up the word-wrapping in displayShortHelpLines
|
|
|
|
f_assert( !f_strchr( pszShortHelp, '\n'));
|
|
|
|
if( (pNewArg = f_new F_Arg( pszIdentifier, pszShortHelp, bCaseSensitive,
|
|
argType, contentType)) == NULL)
|
|
{
|
|
rc = RC_SET( NE_FLM_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
switch( contentType)
|
|
{
|
|
case F_ARG_CONTENT_VALIDATOR:
|
|
{
|
|
f_va_list args;
|
|
F_ARG_VALIDATOR validator;
|
|
void * pvValidatorData;
|
|
|
|
f_va_start( args, contentType);
|
|
|
|
validator = f_va_arg( args, F_ARG_VALIDATOR);
|
|
pvValidatorData = f_va_arg( args, void *);
|
|
pNewArg->setValidator( validator, pvValidatorData);
|
|
|
|
f_va_end( args);
|
|
break;
|
|
}
|
|
|
|
case F_ARG_CONTENT_SIGNED_INT:
|
|
{
|
|
f_va_list args;
|
|
FLMINT iMin;
|
|
FLMINT iMax;
|
|
|
|
f_va_start( args, contentType);
|
|
|
|
iMin = f_va_arg( args, FLMINT);
|
|
iMax = f_va_arg( args, FLMINT);
|
|
pNewArg->setMinMax( iMin, iMax);
|
|
|
|
f_va_end( args);
|
|
break;
|
|
}
|
|
|
|
case F_ARG_CONTENT_UNSIGNED_INT:
|
|
{
|
|
f_va_list args;
|
|
FLMUINT uiMin;
|
|
FLMUINT uiMax;
|
|
|
|
f_va_start( args, contentType);
|
|
|
|
uiMin = f_va_arg( args, FLMUINT);
|
|
uiMax = f_va_arg( args, FLMUINT);
|
|
|
|
f_assert( uiMin <= uiMax);
|
|
pNewArg->setMinMax( uiMin, uiMax);
|
|
|
|
f_va_end( args);
|
|
break;
|
|
}
|
|
|
|
case F_ARG_CONTENT_ALLOWED_STRING_SET:
|
|
{
|
|
f_va_list args;
|
|
|
|
f_va_start( args, contentType);
|
|
|
|
for( ;;)
|
|
{
|
|
const char * pszNext = f_va_arg( args, char *);
|
|
|
|
if ( !pszNext)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if( RC_BAD( rc = pNewArg->addToStringSet( pszNext)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
f_va_end( args);
|
|
}
|
|
|
|
case F_ARG_CONTENT_EXISTING_FILE:
|
|
case F_ARG_CONTENT_NONE:
|
|
case F_ARG_CONTENT_BOOL:
|
|
case F_ARG_CONTENT_STRING:
|
|
{
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
f_assert( 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Store the specific types of args in their own vector (and pointer
|
|
// in the case of m_pRepeatingArg)
|
|
|
|
switch( argType)
|
|
{
|
|
case F_ARG_OPTION:
|
|
{
|
|
if( RC_BAD( rc = m_optionsVec.setElementAt(
|
|
pNewArg, m_uiOptionsVecLen)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_uiOptionsVecLen++;
|
|
break;
|
|
}
|
|
|
|
case F_ARG_REQUIRED_ARG:
|
|
{
|
|
if( RC_BAD( rc = m_requiredArgsVec.setElementAt(
|
|
pNewArg, m_uiRequiredArgsVecLen)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_uiRequiredArgsVecLen++;
|
|
break;
|
|
}
|
|
|
|
case F_ARG_OPTIONAL_ARG:
|
|
{
|
|
if( RC_BAD( rc = m_optionalArgsVec.setElementAt(
|
|
pNewArg, m_uiOptionalArgsVecLen)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_uiOptionalArgsVecLen++;
|
|
break;
|
|
}
|
|
|
|
case F_ARG_REPEATING_ARG:
|
|
{
|
|
// Cannot have multiple repeating args
|
|
|
|
f_assert( !m_pRepeatingArg);
|
|
m_pRepeatingArg = pNewArg;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
f_assert( 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Store all args in a vector
|
|
|
|
if( RC_BAD( rc = m_argVec.setElementAt( pNewArg, m_uiArgVecIndex)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pNewArg = NULL;
|
|
m_uiArgVecIndex++;
|
|
|
|
Exit:
|
|
|
|
if( pNewArg)
|
|
{
|
|
pNewArg->Release();
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
F_Arg * F_ArgSet::getArg(
|
|
const char * pszIdentifier)
|
|
{
|
|
F_Arg * pArg;
|
|
FLMUINT uiLoop;
|
|
|
|
for( uiLoop = 0; uiLoop < m_uiArgVecIndex; uiLoop++)
|
|
{
|
|
pArg = (F_Arg *)(m_argVec.getElementAt( uiLoop));
|
|
|
|
if( f_strcmp( pArg->getIdentifier(), pszIdentifier) == 0)
|
|
{
|
|
return( pArg);
|
|
}
|
|
}
|
|
|
|
f_assert( 0);
|
|
return( NULL);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
FLMBOOL F_ArgSet::needMoreArgs(
|
|
F_Vector * pVec,
|
|
FLMUINT uiVecLen)
|
|
{
|
|
FLMUINT uiLoop;
|
|
|
|
f_assert( pVec);
|
|
|
|
for( uiLoop = 0; uiLoop < uiVecLen; uiLoop++)
|
|
{
|
|
F_Arg * pArg = ((F_Arg *)(pVec->getElementAt( uiLoop)));
|
|
|
|
// If at least one arg is non-present, we need some more.
|
|
|
|
if( !pArg->isPresent())
|
|
{
|
|
return( TRUE);
|
|
}
|
|
}
|
|
|
|
return( FALSE);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
void F_ArgSet::dump(
|
|
F_Vector * pVec,
|
|
FLMUINT uiVecLen)
|
|
{
|
|
FLMUINT uiLoop;
|
|
|
|
// Loop through the args and print out a table for easy reference to see
|
|
// what was set and what wasn't
|
|
|
|
for( uiLoop = 0; uiLoop < uiVecLen; uiLoop++)
|
|
{
|
|
F_Arg * pArg = (F_Arg *)(pVec->getElementAt( uiLoop));
|
|
|
|
f_printf( m_pPrintfClient, " ");
|
|
f_printf( m_pPrintfClient, pArg->getIdentifier());
|
|
f_printf( m_pPrintfClient, ": ");
|
|
|
|
if( pArg->isPresent())
|
|
{
|
|
f_printf( m_pPrintfClient, "values={");
|
|
|
|
for( FLMUINT uiVals = 0; uiVals < pArg->getValueCount(); uiVals++)
|
|
{
|
|
f_printf( m_pPrintfClient, pArg->getValue( uiVals));
|
|
|
|
if( uiVals != (pArg->getValueCount() - 1))
|
|
{
|
|
f_printf( m_pPrintfClient, ",");
|
|
}
|
|
}
|
|
|
|
f_printf( m_pPrintfClient, "}\n");
|
|
}
|
|
else
|
|
{
|
|
f_printf( m_pPrintfClient, "not supplied\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
RCODE F_ArgSet::parseOption(
|
|
const char * pszArg)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
const char * pszArgArg;
|
|
FLMBOOL bFoundMatch = FALSE;
|
|
FLMUINT uiTraverseOpts;
|
|
|
|
pszArg++;
|
|
pszArgArg = f_strchr( (const char *)pszArg, '=');
|
|
|
|
for( uiTraverseOpts = 0;
|
|
uiTraverseOpts < m_uiOptionsVecLen;
|
|
uiTraverseOpts++)
|
|
{
|
|
F_Arg * pArg = (F_Arg *)m_optionsVec.getElementAt( uiTraverseOpts);
|
|
FLMBOOL bCaseSensitive = pArg->getCaseSensitive();
|
|
|
|
if( (bCaseSensitive && (f_strcmp( pArg->getIdentifier(), pszArg) == 0)) ||
|
|
(!bCaseSensitive && (f_stricmp( pArg->getIdentifier(), pszArg) == 0)))
|
|
{
|
|
if( pArg->getContentType() != F_ARG_CONTENT_NONE)
|
|
{
|
|
f_printf( m_pPrintfClient, "ERROR: Option '");
|
|
f_printf( m_pPrintfClient, pArg->getIdentifier());
|
|
f_printf( m_pPrintfClient, "' requires argument of the form option=value");
|
|
|
|
printUsage();
|
|
|
|
rc = RC_SET( NE_FLM_FAILURE);
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
bFoundMatch = TRUE;
|
|
pArg->setPresent();
|
|
break;
|
|
}
|
|
}
|
|
else if( pszArgArg)
|
|
{
|
|
f_assert( pszArgArg[ 0] == '=');
|
|
|
|
FLMBOOL bIsMatch =
|
|
((bCaseSensitive &&
|
|
(f_strncmp( pArg->getIdentifier(), pszArg, pszArgArg - pszArg) == 0)) ||
|
|
(!bCaseSensitive &&
|
|
(f_strnicmp( pArg->getIdentifier(), pszArg, pszArgArg - pszArg) == 0)))
|
|
? TRUE
|
|
: FALSE;
|
|
|
|
if( bIsMatch)
|
|
{
|
|
pszArgArg++;
|
|
|
|
if( pArg->getContentType() == F_ARG_CONTENT_NONE)
|
|
{
|
|
f_printf( m_pPrintfClient,
|
|
"ERROR: Cannot give argument to option ");
|
|
f_printf( m_pPrintfClient, pArg->getIdentifier());
|
|
|
|
printUsage();
|
|
|
|
rc = RC_SET( NE_FLM_FAILURE);
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
if( RC_BAD( rc = pArg->addValue( pszArgArg)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pArg->setPresent();
|
|
bFoundMatch = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !bFoundMatch)
|
|
{
|
|
f_printf( m_pPrintfClient, "Unknown option ");
|
|
f_printf( m_pPrintfClient, pszArg);
|
|
f_printf( m_pPrintfClient, "\n");
|
|
|
|
printUsage();
|
|
|
|
rc = RC_SET( NE_FLM_FAILURE);
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
RCODE F_ArgSet::parseCommandLine(
|
|
FLMUINT uiArgc,
|
|
const char ** ppszArgv)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
FLMUINT uiLoop;
|
|
FLMUINT uiArgs;
|
|
FLMBOOL bNegative;
|
|
const char * pszExecStr = ppszArgv[ 0];
|
|
char szDir[ F_PATH_MAX_SIZE];
|
|
IF_FileSystem * pFileSystem = NULL;
|
|
|
|
// Set up members we'll need
|
|
|
|
if( !m_pArgv)
|
|
{
|
|
if( (m_pArgv = f_new F_Vector) == NULL)
|
|
{
|
|
rc = RC_SET( NE_FLM_MEM);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Read file exec basename into variable
|
|
|
|
if ( RC_BAD( rc = f_pathReduce( pszExecStr, szDir, m_szExecBaseName)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Copy inital args into member vars
|
|
|
|
for( uiLoop = 0; uiLoop < uiArgc; uiLoop++)
|
|
{
|
|
const char * pszCopyThis = ppszArgv[ uiLoop];
|
|
char * pszNewStr;
|
|
|
|
if( RC_BAD( rc = f_alloc( f_strlen( pszCopyThis) + 1, &pszNewStr)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_strcpy( pszNewStr, pszCopyThis);
|
|
|
|
if( RC_BAD( rc = m_pArgv->setElementAt( pszNewStr, uiLoop)))
|
|
{
|
|
f_free( &pszNewStr);
|
|
goto Exit;
|
|
}
|
|
|
|
m_uiArgc++;
|
|
}
|
|
|
|
// Pre-process params
|
|
|
|
if ( RC_BAD( rc = preProcessParams()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Now read through the args. They will all be well-formed at this
|
|
// point. Look for strings beginning with a hyphen to find options,
|
|
// and everything else is either a required, optional, or repeating
|
|
// arg. Ignore arg 0, which is the executable name.
|
|
|
|
for( uiLoop = 1; uiLoop < m_uiArgc; uiLoop++)
|
|
{
|
|
const char * pszArg = (const char *)m_pArgv->getElementAt( uiLoop);
|
|
|
|
f_assert( f_strlen( pszArg) > 0);
|
|
|
|
// Handle help strings first
|
|
|
|
if ( f_stricmp( pszArg, "-h") == 0 ||
|
|
f_stricmp( pszArg, "-help") == 0 ||
|
|
f_strcmp( pszArg, "-?") == 0 ||
|
|
f_strcmp( pszArg, "?") == 0)
|
|
{
|
|
printUsage();
|
|
|
|
rc = RC_SET( NE_FLM_FAILURE);
|
|
goto Exit;
|
|
}
|
|
else if( pszArg[ 0] == '-')
|
|
{
|
|
// Option-handling is sufficiently complex that we'll handle it
|
|
// elsewhere in its own function
|
|
|
|
if ( RC_BAD( rc = parseOption( pszArg)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if( (m_uiRequiredArgsVecLen > 0) &&
|
|
needMoreArgs( &m_requiredArgsVec, m_uiRequiredArgsVecLen))
|
|
{
|
|
// Required argument
|
|
|
|
FLMUINT uiIndex = 0;
|
|
F_Arg * pChosenArg;
|
|
|
|
for( ;;)
|
|
{
|
|
pChosenArg = (F_Arg *)m_requiredArgsVec.getElementAt( uiIndex);
|
|
|
|
if ( !pChosenArg->isPresent())
|
|
{
|
|
break;
|
|
}
|
|
|
|
uiIndex++;
|
|
}
|
|
|
|
if( RC_BAD( rc = pChosenArg->addValue( pszArg)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pChosenArg->setPresent();
|
|
}
|
|
else if( (m_uiOptionalArgsVecLen > 0) &&
|
|
needMoreArgs( &m_optionalArgsVec, m_uiOptionalArgsVecLen))
|
|
{
|
|
// Optional argument
|
|
|
|
FLMUINT uiIndex = 0;
|
|
F_Arg * pChosenArg;
|
|
|
|
for( ;;)
|
|
{
|
|
pChosenArg = (F_Arg *)m_optionalArgsVec.getElementAt( uiIndex);
|
|
|
|
if ( !pChosenArg->isPresent())
|
|
{
|
|
break;
|
|
}
|
|
|
|
uiIndex++;
|
|
}
|
|
|
|
if( RC_BAD( rc = pChosenArg->addValue( pszArg)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pChosenArg->setPresent();
|
|
}
|
|
else if ( m_pRepeatingArg)
|
|
{
|
|
m_pRepeatingArg->setPresent();
|
|
|
|
if( RC_BAD( rc = m_pRepeatingArg->addValue( pszArg)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
f_printf( m_pPrintfClient, "invalid extra argument '");
|
|
f_printf( m_pPrintfClient, pszArg);
|
|
f_printf( m_pPrintfClient, "'\n");
|
|
|
|
printUsage();
|
|
|
|
rc = RC_SET( NE_FLM_FAILURE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// We've read through all the args and set things appropriately. If
|
|
// we have any required args unset at this point, that's a user
|
|
// problem so let's report it and abort.
|
|
|
|
if( needMoreArgs( &m_requiredArgsVec, m_uiRequiredArgsVecLen))
|
|
{
|
|
for( uiLoop = 0; uiLoop < m_uiRequiredArgsVecLen; uiLoop++)
|
|
{
|
|
F_Arg * pArg = (F_Arg*)m_requiredArgsVec.getElementAt( uiLoop);
|
|
|
|
if ( !pArg->isPresent())
|
|
{
|
|
f_printf( m_pPrintfClient,
|
|
"ERROR: Did not pass required arg #%u <%s>\n",
|
|
uiLoop + 1, pArg->getIdentifier());
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
printUsage();
|
|
|
|
rc = RC_SET( NE_FLM_FAILURE);
|
|
goto Exit;
|
|
}
|
|
|
|
// Need to loop through all args and validate the inputs. We can use
|
|
// m_argVec for this (don't need to use the specific groups of args
|
|
// such as m_requiredArgsVec, m_optionalArgsVec, etc.)
|
|
|
|
for( uiLoop = 0; uiLoop < m_uiArgVecIndex; uiLoop++)
|
|
{
|
|
F_Arg * pArg = (F_Arg *)(m_argVec.getElementAt( uiLoop));
|
|
|
|
f_assert( pArg);
|
|
|
|
// If it's present, then it has a value we will want to validate
|
|
|
|
if( pArg->isPresent())
|
|
{
|
|
FLMBOOL bValidated = FALSE;
|
|
const char * pszStr = "";
|
|
|
|
switch( pArg->getContentType())
|
|
{
|
|
case F_ARG_CONTENT_NONE:
|
|
case F_ARG_CONTENT_STRING:
|
|
{
|
|
// Nothing to validate for these, they will always match
|
|
|
|
bValidated = TRUE;
|
|
break;
|
|
}
|
|
|
|
case F_ARG_CONTENT_BOOL:
|
|
{
|
|
for( uiArgs = 0; uiArgs < pArg->getValueCount(); uiArgs++)
|
|
{
|
|
pszStr = pArg->getValue( uiArgs);
|
|
f_atobool( pszStr, &bValidated);
|
|
|
|
if( !bValidated)
|
|
{
|
|
f_printf( m_pPrintfClient, "ERROR: Argument '");
|
|
f_printf( m_pPrintfClient, pszStr);
|
|
f_printf( m_pPrintfClient,
|
|
"' is not a valid representation of a boolean");
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case F_ARG_CONTENT_VALIDATOR:
|
|
{
|
|
for( uiArgs = 0; uiArgs < pArg->getValueCount(); uiArgs++)
|
|
{
|
|
pszStr = pArg->getValue( uiArgs);
|
|
|
|
if( (bValidated = pArg->getValidator()( pszStr,
|
|
pArg->getIdentifier(), m_pPrintfClient,
|
|
pArg->getValidatorData())) == FALSE)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case F_ARG_CONTENT_SIGNED_INT:
|
|
case F_ARG_CONTENT_UNSIGNED_INT:
|
|
{
|
|
for( uiArgs = 0; uiArgs < pArg->getValueCount(); uiArgs++)
|
|
{
|
|
pszStr = pArg->getValue( uiArgs);
|
|
|
|
if( (bValidated = f_isNumber( pszStr, &bNegative)) == FALSE)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if( pArg->getContentType() == F_ARG_CONTENT_UNSIGNED_INT &&
|
|
bNegative)
|
|
{
|
|
bValidated = FALSE;
|
|
break;
|
|
}
|
|
|
|
if( pArg->getContentType() == F_ARG_CONTENT_SIGNED_INT)
|
|
{
|
|
FLMINT iMax;
|
|
FLMINT iMin;
|
|
FLMINT iArg;
|
|
|
|
pArg->getMinMax( &iMin, &iMax);
|
|
iArg = f_atoi( pszStr);
|
|
|
|
if( (iArg > iMax) || (iArg < iMin))
|
|
{
|
|
f_printf( m_pPrintfClient,
|
|
"ERROR: Argument '%s' violates range "
|
|
"requirement of min=%d, max=%d\n",
|
|
pszStr, iMin, iMax);
|
|
|
|
bValidated = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FLMUINT uiMax;
|
|
FLMUINT uiMin;
|
|
FLMUINT uiArg;
|
|
|
|
pArg->getMinMax( &uiMin, &uiMax);
|
|
uiArg = f_atoud( pszStr);
|
|
|
|
if( (uiArg > uiMax) || (uiArg < uiMin))
|
|
{
|
|
f_printf( m_pPrintfClient,
|
|
"ERROR: Argument '%s' violates range "
|
|
"requirement of min=%u, max=%u\n",
|
|
pszStr, (unsigned)uiMin, (unsigned)uiMax);
|
|
|
|
bValidated = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case F_ARG_CONTENT_ALLOWED_STRING_SET:
|
|
{
|
|
for( uiArgs = 0; uiArgs < pArg->getValueCount(); uiArgs++)
|
|
{
|
|
FLMUINT uiStrSetLen = pArg->getStringSetLen();
|
|
FLMUINT uiStrSet;
|
|
FLMBOOL bMatched = FALSE;
|
|
F_Vector * pStringSet;
|
|
|
|
pszStr = pArg->getValue( uiArgs);
|
|
pStringSet = pArg->getStringSet();
|
|
|
|
for( uiStrSet = 0; uiStrSet < uiStrSetLen; uiStrSet++)
|
|
{
|
|
bMatched = (f_strcmp(
|
|
(const char *)pStringSet->getElementAt( uiStrSet),
|
|
pszStr) == 0) ? TRUE : FALSE;
|
|
if( bMatched)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
bValidated = bMatched;
|
|
|
|
if( !bValidated)
|
|
{
|
|
f_printf( m_pPrintfClient, "ERROR: '");
|
|
f_printf( m_pPrintfClient, pszStr);
|
|
f_printf( m_pPrintfClient,
|
|
"' is invalid. Must be a member of {");
|
|
|
|
for( uiStrSet = 0; uiStrSet < uiStrSetLen; uiStrSet++)
|
|
{
|
|
const char * pszNextMember = (const char *)pStringSet->getElementAt( uiStrSet);
|
|
|
|
f_printf( m_pPrintfClient, "'");
|
|
f_printf( m_pPrintfClient, pszNextMember);
|
|
f_printf( m_pPrintfClient, "'");
|
|
|
|
if( uiStrSet != (uiStrSetLen - 1))
|
|
{
|
|
f_printf( m_pPrintfClient, ",");
|
|
}
|
|
}
|
|
|
|
f_printf( m_pPrintfClient, "}\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case F_ARG_CONTENT_EXISTING_FILE:
|
|
{
|
|
if( RC_OK( FlmGetFileSystem( &pFileSystem)))
|
|
{
|
|
for( uiArgs = 0; uiArgs < pArg->getValueCount(); uiArgs++)
|
|
{
|
|
pszStr = pArg->getValue( uiArgs);
|
|
bValidated = RC_OK( pFileSystem->doesFileExist( pszStr));
|
|
|
|
if ( !bValidated)
|
|
{
|
|
f_printf( m_pPrintfClient, "ERROR: File ");
|
|
f_printf( m_pPrintfClient, pszStr);
|
|
f_printf( m_pPrintfClient, " does not exist\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
pFileSystem->Release();
|
|
pFileSystem = NULL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
f_assert( 0);
|
|
}
|
|
}
|
|
|
|
if( !bValidated)
|
|
{
|
|
f_printf( m_pPrintfClient, "ERROR: Argument '");
|
|
f_printf( m_pPrintfClient, pArg->getIdentifier());
|
|
f_printf( m_pPrintfClient, "' did not validate with value '");
|
|
f_printf( m_pPrintfClient, pszStr);
|
|
f_printf( m_pPrintfClient, "'\n");
|
|
|
|
printUsage();
|
|
|
|
rc = RC_SET( NE_FLM_FAILURE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if( pFileSystem)
|
|
{
|
|
pFileSystem->Release();
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Print out the short help lines, breaking up at word boundaries. This
|
|
method has hard-coded agreements with printUsage(), so be careful
|
|
when changing it.
|
|
****************************************************************************/
|
|
RCODE F_ArgSet::displayShortHelpLines(
|
|
const char * pszShortHelp,
|
|
FLMUINT uiCharsPerLine)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
char * pszClone = NULL;
|
|
FLMUINT uiLen = f_strlen( pszShortHelp);
|
|
FLMUINT uiLoop = 0;
|
|
FLMUINT uiLastGoodBreakingPos = 0;
|
|
|
|
if( RC_BAD( rc = f_strdup( pszShortHelp, &pszClone)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
for( ;;)
|
|
{
|
|
if( uiLoop > uiCharsPerLine)
|
|
{
|
|
if ( uiLastGoodBreakingPos == 0)
|
|
{
|
|
// This means that you made words that were too long in the
|
|
// short-help. You need to break up the words so that
|
|
// this doesn't happen.
|
|
|
|
f_assert( 0);
|
|
rc = RC_SET( NE_FLM_FAILURE);
|
|
goto Exit;
|
|
}
|
|
|
|
// Use uiLastGoodBreakingPos to print out a section
|
|
|
|
pszClone[ uiLastGoodBreakingPos] = 0;
|
|
|
|
f_printf( m_pPrintfClient, pszClone);
|
|
f_printf( m_pPrintfClient,
|
|
"\n ");
|
|
|
|
pszClone += f_strlen( pszClone) + 1;
|
|
uiLoop = 0;
|
|
uiLen = f_strlen( pszClone);
|
|
uiLastGoodBreakingPos = 0;
|
|
|
|
continue;
|
|
}
|
|
else if( uiLoop == (uiLen - 1))
|
|
{
|
|
f_printf( m_pPrintfClient, pszClone);
|
|
break;
|
|
}
|
|
else if( f_isWhitespace( pszClone[ uiLoop]))
|
|
{
|
|
uiLastGoodBreakingPos = uiLoop;
|
|
}
|
|
|
|
uiLoop++;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if( pszClone)
|
|
{
|
|
f_free( &pszClone);
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
void F_ArgSet::printUsage( void)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
FLMUINT uiLoop = 0;
|
|
|
|
f_printf( m_pPrintfClient, "Usage: ");
|
|
f_printf( m_pPrintfClient, m_szExecBaseName);
|
|
|
|
if( m_uiOptionsVecLen > 0)
|
|
{
|
|
f_printf( m_pPrintfClient, " [OPTIONS]");
|
|
}
|
|
|
|
if( m_uiRequiredArgsVecLen > 0)
|
|
{
|
|
f_printf( m_pPrintfClient, " ");
|
|
}
|
|
|
|
for( uiLoop = 0; uiLoop < m_uiRequiredArgsVecLen; uiLoop++)
|
|
{
|
|
F_Arg * pArg = (F_Arg*)m_requiredArgsVec.getElementAt( uiLoop);
|
|
|
|
f_printf( m_pPrintfClient, "<");
|
|
f_printf( m_pPrintfClient, pArg->getIdentifier());
|
|
f_printf( m_pPrintfClient, ">");
|
|
|
|
if( uiLoop != (m_uiRequiredArgsVecLen - 1))
|
|
{
|
|
f_printf( m_pPrintfClient, " ");
|
|
}
|
|
}
|
|
|
|
if( m_uiOptionalArgsVecLen > 0)
|
|
{
|
|
f_printf( m_pPrintfClient, " ");
|
|
}
|
|
|
|
for ( uiLoop = 0; uiLoop < m_uiOptionalArgsVecLen; uiLoop++)
|
|
{
|
|
F_Arg * pArg = (F_Arg*)m_optionalArgsVec.getElementAt( uiLoop);
|
|
|
|
f_printf( m_pPrintfClient, "[");
|
|
f_printf( m_pPrintfClient, pArg->getIdentifier());
|
|
f_printf( m_pPrintfClient, "]");
|
|
|
|
if( uiLoop != (m_uiOptionalArgsVecLen - 1))
|
|
{
|
|
f_printf( m_pPrintfClient, " ");
|
|
}
|
|
}
|
|
|
|
if ( m_pRepeatingArg)
|
|
{
|
|
f_printf( m_pPrintfClient, " [");
|
|
f_printf( m_pPrintfClient, m_pRepeatingArg->getIdentifier());
|
|
f_printf( m_pPrintfClient, "...]");
|
|
}
|
|
|
|
f_printf( m_pPrintfClient, "\n\n");
|
|
|
|
if( m_uiOptionsVecLen > 0)
|
|
{
|
|
// The following is fairly hard-coded to get the spacing right
|
|
|
|
f_printf( m_pPrintfClient,
|
|
"OPTIONS:\n"
|
|
"\n"
|
|
"identifier case sensitive description\n"
|
|
"----------------- -------------- ---------------------------------------\n");
|
|
|
|
// Show all the options
|
|
|
|
for( uiLoop = 0; uiLoop < m_uiOptionsVecLen; uiLoop++)
|
|
{
|
|
const char * pszHelpClone = NULL;
|
|
F_Arg * pArg = (F_Arg*)m_optionsVec.getElementAt( uiLoop);
|
|
const char * pszIdentifier = pArg->getIdentifier();
|
|
FLMUINT uiIdentifierLength = f_strlen( pszIdentifier);
|
|
|
|
f_printf( m_pPrintfClient, "-");
|
|
f_printf( m_pPrintfClient, pszIdentifier);
|
|
|
|
if( pArg->getContentType() != F_ARG_CONTENT_NONE)
|
|
{
|
|
const char * pszArgArgIndicator = "=ARG";
|
|
|
|
f_printf( m_pPrintfClient, pszArgArgIndicator);
|
|
uiIdentifierLength += f_strlen( pszArgArgIndicator);
|
|
}
|
|
|
|
m_pPrintfClient->outputChar( ' ', 16 - uiIdentifierLength);
|
|
|
|
f_printf( m_pPrintfClient, " ");
|
|
f_printf( m_pPrintfClient, (pArg->getCaseSensitive())
|
|
? "y"
|
|
: "n");
|
|
m_pPrintfClient->outputChar( ' ', 16);
|
|
|
|
if( RC_BAD( rc = f_strdup( pArg->getShortHelp(),
|
|
(char **)&pszHelpClone)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
rc = displayShortHelpLines( pszHelpClone, 39);
|
|
|
|
if( pszHelpClone)
|
|
{
|
|
f_free( &pszHelpClone);
|
|
pszHelpClone = NULL;
|
|
}
|
|
|
|
if( RC_BAD( rc))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_printf( m_pPrintfClient, "\n");
|
|
}
|
|
|
|
f_printf( m_pPrintfClient, "\n");
|
|
}
|
|
|
|
if( m_uiRequiredArgsVecLen > 0)
|
|
{
|
|
f_printf( m_pPrintfClient,
|
|
"REQUIRED args:\n"
|
|
"\n");
|
|
|
|
for( uiLoop = 0; uiLoop < m_uiRequiredArgsVecLen; uiLoop++)
|
|
{
|
|
F_Arg * pArg = (F_Arg*)m_requiredArgsVec.getElementAt( uiLoop);
|
|
F_StringAcc tempAcc;
|
|
FLMUINT uiPads;
|
|
|
|
if( RC_BAD( rc = tempAcc.appendTEXT( "<")))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = tempAcc.appendTEXT( pArg->getIdentifier())))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = tempAcc.appendTEXT( ">: ")))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
uiPads = 36 - f_strlen( tempAcc.getTEXT());
|
|
|
|
if( RC_BAD( rc = tempAcc.appendCHAR( ' ', uiPads)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_printf( m_pPrintfClient, tempAcc.getTEXT());
|
|
|
|
if( RC_BAD( rc = displayShortHelpLines( pArg->getShortHelp(), 39)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_printf( m_pPrintfClient, "\n");
|
|
}
|
|
|
|
f_printf( m_pPrintfClient, "\n\n");
|
|
}
|
|
|
|
if( m_uiOptionalArgsVecLen > 0)
|
|
{
|
|
f_printf( m_pPrintfClient,
|
|
"OPTIONAL args:\n"
|
|
"\n");
|
|
|
|
for( uiLoop = 0; uiLoop < m_uiOptionalArgsVecLen; uiLoop++)
|
|
{
|
|
F_Arg * pArg = (F_Arg*)m_optionalArgsVec.getElementAt( uiLoop);
|
|
F_StringAcc tempAcc;
|
|
FLMUINT uiPads;
|
|
|
|
if( RC_BAD( rc = tempAcc.appendTEXT( "[")))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = tempAcc.appendTEXT( pArg->getIdentifier())))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = tempAcc.appendTEXT( "]: ")))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
uiPads = 36 - f_strlen( tempAcc.getTEXT());
|
|
|
|
if( RC_BAD( rc = tempAcc.appendCHAR( ' ', uiPads)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_printf( m_pPrintfClient, tempAcc.getTEXT());
|
|
|
|
if( RC_BAD( rc = displayShortHelpLines( pArg->getShortHelp(), 39)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_printf( m_pPrintfClient, "\n");
|
|
}
|
|
|
|
f_printf( m_pPrintfClient, "\n\n");
|
|
}
|
|
|
|
if( m_pRepeatingArg)
|
|
{
|
|
F_StringAcc tempAcc;
|
|
FLMUINT uiPads;
|
|
|
|
f_printf( m_pPrintfClient,
|
|
"REPEATING arg:\n"
|
|
"\n");
|
|
|
|
if( RC_BAD( rc = tempAcc.appendTEXT( "[")))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = tempAcc.appendTEXT( m_pRepeatingArg->getIdentifier())))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = tempAcc.appendTEXT( "...]: ")))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
uiPads = 36 - f_strlen( tempAcc.getTEXT());
|
|
|
|
if( RC_BAD( rc = tempAcc.appendCHAR( ' ', uiPads)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_printf( m_pPrintfClient, tempAcc.getTEXT());
|
|
|
|
if( RC_BAD( rc = displayShortHelpLines(
|
|
m_pRepeatingArg->getShortHelp(), 39)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_printf( m_pPrintfClient, "\n\n");
|
|
}
|
|
|
|
Exit:
|
|
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Does the given arg begin with an '@'?
|
|
****************************************************************************/
|
|
FSTATIC FLMBOOL matchesAtParams(
|
|
const char * pszStr)
|
|
{
|
|
f_assert( pszStr);
|
|
f_assert( *pszStr);
|
|
|
|
return( (pszStr[ 0] == '@') ? TRUE : FALSE);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Does the given arg match an "=@filename" type argument
|
|
****************************************************************************/
|
|
FSTATIC FLMBOOL matchesEqualsAtParams(
|
|
const char * pszStr)
|
|
{
|
|
const char * pszStartPos;
|
|
|
|
f_assert( pszStr);
|
|
f_assert( *pszStr);
|
|
|
|
pszStartPos = f_strstr( pszStr, "=@");
|
|
|
|
if( !pszStartPos)
|
|
{
|
|
return( FALSE);
|
|
}
|
|
else
|
|
{
|
|
// Should have something after the @
|
|
|
|
if( (f_strlen( pszStartPos) > 2) && pszStartPos[ 2])
|
|
{
|
|
return( TRUE);
|
|
}
|
|
else
|
|
{
|
|
return( FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Are we done preprocessing the arg set or not for all the @'s?
|
|
****************************************************************************/
|
|
FLMBOOL F_ArgSet::needsPreprocessing( void)
|
|
{
|
|
FLMUINT uiLoop;
|
|
|
|
for( uiLoop = 0; uiLoop < m_uiArgc; uiLoop++)
|
|
{
|
|
if( matchesAtParams( (const char *)(m_pArgv->getElementAt( uiLoop))) ||
|
|
matchesEqualsAtParams( (const char *)(m_pArgv->getElementAt( uiLoop))))
|
|
{
|
|
return( TRUE);
|
|
}
|
|
}
|
|
|
|
return( FALSE);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Replace all @params.txt with whitespace-collapsed inserted arguments
|
|
****************************************************************************/
|
|
RCODE F_ArgSet::processAtParams(
|
|
FLMUINT uiInsertionPoint,
|
|
char * pszBuffer)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
F_Vector * pNewVec = NULL;
|
|
FLMUINT uiLoop;
|
|
FLMUINT uiOldVecIndex;
|
|
FLMUINT uiNewVecIndex = uiInsertionPoint;
|
|
FLMUINT uiLeftToDo;
|
|
|
|
// Set up a new vector and start copying/expanding things over
|
|
|
|
pNewVec = f_new F_Vector;
|
|
if( !pNewVec)
|
|
{
|
|
rc = RC_SET( NE_FLM_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
// Copy over the old items up to the insertion point
|
|
|
|
for( uiOldVecIndex = 0; uiOldVecIndex < uiInsertionPoint; uiOldVecIndex++)
|
|
{
|
|
const char * pszOldArg = (const char *)m_pArgv->getElementAt( uiOldVecIndex);
|
|
char * pszCopy;
|
|
|
|
if( RC_BAD( rc = f_strdup( pszOldArg, &pszCopy)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pNewVec->setElementAt( pszCopy, uiOldVecIndex)))
|
|
{
|
|
f_free( &pszCopy);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Handle empty file case
|
|
|
|
if( pszBuffer)
|
|
{
|
|
char * pszStartPos;
|
|
char * pszEndPos;
|
|
|
|
// Advance pszBuffer to first non-whitespace string
|
|
|
|
while( f_isWhitespace( *pszBuffer))
|
|
{
|
|
pszBuffer++;
|
|
}
|
|
|
|
pszStartPos = pszEndPos = pszBuffer;
|
|
|
|
while( *pszStartPos)
|
|
{
|
|
if( f_isWhitespace( *pszEndPos) || (pszEndPos[ 0] == 0))
|
|
{
|
|
FLMBYTE ucEndChar = pszEndPos[ 0];
|
|
char * pszCopy;
|
|
|
|
*pszEndPos = 0;
|
|
if( RC_BAD( rc = f_strdup( pszStartPos, &pszCopy)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pNewVec->setElementAt(
|
|
pszCopy, uiNewVecIndex)))
|
|
{
|
|
f_free( &pszCopy);
|
|
goto Exit;
|
|
}
|
|
|
|
uiNewVecIndex++;
|
|
|
|
// If not at end of file, advance through whitespace
|
|
|
|
if( ucEndChar != 0)
|
|
{
|
|
while( f_isWhitespace( *(++pszEndPos)))
|
|
{
|
|
; // Just advance it to next non-whitespace
|
|
}
|
|
}
|
|
|
|
pszStartPos = pszEndPos;
|
|
}
|
|
else
|
|
{
|
|
pszEndPos++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy the remaining args to the new vector
|
|
|
|
uiLeftToDo = m_uiArgc - (uiInsertionPoint+1);
|
|
|
|
for( uiLoop = 0; uiLoop < uiLeftToDo; uiLoop++)
|
|
{
|
|
char * pszCopy;
|
|
|
|
if( RC_BAD( rc = f_strdup( (const char *)
|
|
(m_pArgv->getElementAt( uiInsertionPoint + uiLoop + 1)), &pszCopy)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pNewVec->setElementAt( pszCopy, uiNewVecIndex)))
|
|
{
|
|
f_free( &pszCopy);
|
|
goto Exit;
|
|
}
|
|
|
|
uiNewVecIndex++;
|
|
}
|
|
|
|
// Switch over member variables to the new values
|
|
|
|
for( uiLoop = 0; uiLoop < m_uiArgc; uiLoop++)
|
|
{
|
|
char * pszFreeMe = (char *)(m_pArgv->getElementAt( uiLoop));
|
|
|
|
if( pszFreeMe)
|
|
{
|
|
f_free( &pszFreeMe);
|
|
}
|
|
}
|
|
|
|
m_pArgv->Release();
|
|
m_pArgv = pNewVec;
|
|
m_uiArgc = uiNewVecIndex;
|
|
|
|
Exit:
|
|
|
|
if( RC_BAD( rc))
|
|
{
|
|
FLMUINT uiClean;
|
|
|
|
for( uiClean = 0; uiClean < uiNewVecIndex; uiClean++)
|
|
{
|
|
char * pszStr = (char *)pNewVec->getElementAt( uiClean);
|
|
|
|
if( pszStr)
|
|
{
|
|
f_free( &pszStr);
|
|
}
|
|
}
|
|
|
|
if( pNewVec)
|
|
{
|
|
pNewVec->Release();
|
|
}
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Preprocess all @-style arguments
|
|
****************************************************************************/
|
|
RCODE F_ArgSet::preProcessParams( void)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
char * pszBuffer = NULL;
|
|
FLMUINT uiLoop = 0;
|
|
const FLMUINT uiMaxPreProcessingCycles = 64;
|
|
FLMUINT uiPreProcessingCycles = 0;
|
|
|
|
for( uiLoop = 0; uiLoop < m_uiArgc;)
|
|
{
|
|
const char * pszNextArg = (const char *)m_pArgv->getElementAt( uiLoop);
|
|
|
|
if( matchesAtParams( pszNextArg))
|
|
{
|
|
uiPreProcessingCycles++;
|
|
|
|
if( uiPreProcessingCycles >= uiMaxPreProcessingCycles)
|
|
{
|
|
// There's probably a cycle in the @-files if we're going this deep
|
|
|
|
f_printf( m_pPrintfClient, "ERROR: Cycle in @-files detected");
|
|
rc = RC_SET( NE_FLM_FAILURE);
|
|
goto Exit;
|
|
}
|
|
|
|
// pszNextArg has the form @params.txt
|
|
|
|
if( RC_BAD( rc = f_filetobuf(
|
|
(const char *)(pszNextArg + 1), &pszBuffer)))
|
|
{
|
|
f_printf( m_pPrintfClient, "ERROR: Reading @-file ");
|
|
f_printf( m_pPrintfClient, pszNextArg);
|
|
f_printf( m_pPrintfClient, "!");
|
|
goto Exit;
|
|
}
|
|
|
|
// f_filetobuf just allocated a pszBuffer with the
|
|
// file contents
|
|
|
|
rc = processAtParams( uiLoop, pszBuffer);
|
|
|
|
if( pszBuffer)
|
|
{
|
|
f_free( &pszBuffer);
|
|
}
|
|
|
|
if( RC_BAD( rc))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uiLoop++;
|
|
}
|
|
}
|
|
|
|
// Now do the -arg=@params style, once each
|
|
|
|
for( uiLoop = 0; uiLoop < m_uiArgc; uiLoop++)
|
|
{
|
|
const char * pszNextArg = (const char *)m_pArgv->getElementAt( uiLoop);
|
|
|
|
if( matchesEqualsAtParams( pszNextArg))
|
|
{
|
|
char * pszFile = f_strstr( pszNextArg, "=@");
|
|
char * pszFinalBuffer;
|
|
|
|
pszFile++;
|
|
pszFile[ 0] = 0;
|
|
pszFile++;
|
|
|
|
if( RC_BAD( rc = f_filetobuf( (const char *)pszFile, &pszBuffer)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = f_alloc( f_strlen( pszNextArg) +
|
|
f_strlen( pszBuffer) + 1, &pszFinalBuffer)))
|
|
{
|
|
f_free( &pszBuffer);
|
|
goto Exit;
|
|
}
|
|
|
|
// Replace existing -arg=@params.txt string
|
|
// with one of the form -arg=1 2 3 4 5, etc.
|
|
|
|
f_strcpy( pszFinalBuffer, pszNextArg);
|
|
f_strcat( pszFinalBuffer, pszBuffer);
|
|
f_free( &pszBuffer);
|
|
|
|
const char * pszOldStr = (const char *)m_pArgv->getElementAt( uiLoop);
|
|
|
|
if( pszOldStr)
|
|
{
|
|
f_free( &pszOldStr);
|
|
}
|
|
|
|
if( RC_BAD( rc = m_pArgv->setElementAt( pszFinalBuffer, uiLoop)))
|
|
{
|
|
f_free( &pszFinalBuffer);
|
|
goto Exit;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
FLMBOOL FTKAPI F_ArgSet::argIsPresent(
|
|
const char * pszIdentifier)
|
|
{
|
|
return( getArg( pszIdentifier)->isPresent());
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
FLMUINT FTKAPI F_ArgSet::getValueCount(
|
|
const char * pszIdentifier)
|
|
{
|
|
return( getArg( pszIdentifier)->getValueCount());
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
FLMUINT FTKAPI F_ArgSet::getUINT(
|
|
const char * pszIdentifier,
|
|
FLMUINT uiIndex)
|
|
{
|
|
return( getArg( pszIdentifier)->getUINT( uiIndex));
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
FLMINT FTKAPI F_ArgSet::getINT(
|
|
const char * pszIdentifier,
|
|
FLMUINT uiIndex)
|
|
{
|
|
return( getArg( pszIdentifier)->getINT( uiIndex));
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
FLMBOOL FTKAPI F_ArgSet::getBOOL(
|
|
const char * pszIdentifier,
|
|
FLMUINT uiIndex)
|
|
{
|
|
return( getArg( pszIdentifier)->getBOOL( uiIndex));
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
const char * FTKAPI F_ArgSet::getString(
|
|
const char * pszIdentifier,
|
|
FLMUINT uiIndex)
|
|
{
|
|
return( getArg( pszIdentifier)->getString( uiIndex));
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
void FTKAPI F_ArgSet::getString(
|
|
const char * pszIdentifier,
|
|
char * pszDestination,
|
|
FLMUINT uiDestinationBufferSize,
|
|
FLMUINT uiIndex)
|
|
{
|
|
getArg( pszIdentifier)->getString( pszDestination,
|
|
uiDestinationBufferSize, uiIndex);
|
|
}
|
|
|