static void
removeBackslashes( char* pzSrc );
static char const*
scanQuotedString( char const* pzTxt );
static tOptionValue*
addStringValue( void** pp, char const* pzName, size_t nameLen,
char const* pzValue, size_t dataLen );
static tOptionValue*
addBoolValue( void** pp, char const* pzName, size_t nameLen,
char const* pzValue, size_t dataLen );
static tOptionValue*
addNumberValue( void** pp, char const* pzName, size_t nameLen,
char const* pzValue, size_t dataLen );
static tOptionValue*
addNestedValue( void** pp, char const* pzName, size_t nameLen,
char* pzValue, size_t dataLen );
static char const*
scanNameEntry(char const* pzName, tOptionValue* pRes);
static char const*
scanXmlEntry( char const* pzName, tOptionValue* pRes );
static void
unloadNestedArglist( tArgList* pAL );
static void
sortNestedList( tArgList* pAL );
static void
removeBackslashes( char* pzSrc )
{
char* pzD = strchr(pzSrc, '\n');
if (pzD == NULL)
return;
*--pzD = '\n';
for (;;) {
char ch = ((*pzD++) = *(pzSrc++));
switch (ch) {
case '\n': *--pzD = ch; break;
case NUL: return;
default:
;
}
}
}
static char const*
scanQuotedString( char const* pzTxt )
{
char q = *(pzTxt++);
for (;;) {
char ch = *(pzTxt++);
if (ch == NUL)
return pzTxt-1;
if (ch == q)
return pzTxt;
if (ch == '\\') {
ch = *(pzTxt++);
if (ch == NUL)
return pzTxt - 2;
if ((ch == q) || (ch == '\\')) {
if (*(pzTxt++) == NUL)
return pzTxt-1;
}
}
}
}
static tOptionValue*
addStringValue( void** pp, char const* pzName, size_t nameLen,
char const* pzValue, size_t dataLen )
{
tOptionValue* pNV;
size_t sz = nameLen + dataLen + sizeof(*pNV);
pNV = AGALOC( sz, "option name/str value pair" );
if (pNV == NULL)
return NULL;
if (pzValue == NULL) {
pNV->valType = OPARG_TYPE_NONE;
pNV->pzName = pNV->v.strVal;
} else {
pNV->valType = OPARG_TYPE_STRING;
if (dataLen > 0)
memcpy( pNV->v.strVal, pzValue, dataLen );
pNV->v.strVal[dataLen] = NUL;
pNV->pzName = pNV->v.strVal + dataLen + 1;
}
memcpy( pNV->pzName, pzName, nameLen );
pNV->pzName[ nameLen ] = NUL;
addArgListEntry( pp, pNV );
return pNV;
}
static tOptionValue*
addBoolValue( void** pp, char const* pzName, size_t nameLen,
char const* pzValue, size_t dataLen )
{
tOptionValue* pNV;
size_t sz = nameLen + sizeof(*pNV) + 1;
pNV = AGALOC( sz, "option name/bool value pair" );
if (pNV == NULL)
return NULL;
while (isspace( (int)*pzValue ) && (dataLen > 0)) {
dataLen--; pzValue++;
}
if (dataLen == 0)
pNV->v.boolVal = 0;
else if (isdigit( (int)*pzValue ))
pNV->v.boolVal = atoi( pzValue );
else switch (*pzValue) {
case 'f':
case 'F':
case 'n':
case 'N':
pNV->v.boolVal = 0; break;
default:
pNV->v.boolVal = 1;
}
pNV->valType = OPARG_TYPE_BOOLEAN;
pNV->pzName = (char*)(pNV + 1);
memcpy( pNV->pzName, pzName, nameLen );
pNV->pzName[ nameLen ] = NUL;
addArgListEntry( pp, pNV );
return pNV;
}
static tOptionValue*
addNumberValue( void** pp, char const* pzName, size_t nameLen,
char const* pzValue, size_t dataLen )
{
tOptionValue* pNV;
size_t sz = nameLen + sizeof(*pNV) + 1;
pNV = AGALOC( sz, "option name/bool value pair" );
if (pNV == NULL)
return NULL;
while (isspace( (int)*pzValue ) && (dataLen > 0)) {
dataLen--; pzValue++;
}
if (dataLen == 0)
pNV->v.boolVal = 0;
else
pNV->v.boolVal = atoi( pzValue );
pNV->valType = OPARG_TYPE_NUMERIC;
pNV->pzName = (char*)(pNV + 1);
memcpy( pNV->pzName, pzName, nameLen );
pNV->pzName[ nameLen ] = NUL;
addArgListEntry( pp, pNV );
return pNV;
}
static tOptionValue*
addNestedValue( void** pp, char const* pzName, size_t nameLen,
char* pzValue, size_t dataLen )
{
tOptionValue* pNV;
if (dataLen == 0) {
size_t sz = nameLen + sizeof(*pNV) + 1;
pNV = AGALOC( sz, "empty nested value pair" );
if (pNV == NULL)
return NULL;
pNV->v.nestVal = NULL;
pNV->valType = OPARG_TYPE_HIERARCHY;
pNV->pzName = (char*)(pNV + 1);
memcpy( pNV->pzName, pzName, nameLen );
pNV->pzName[ nameLen ] = NUL;
} else {
pNV = optionLoadNested( pzValue, pzName, nameLen );
}
if (pNV != NULL)
addArgListEntry( pp, pNV );
return pNV;
}
static char const*
scanNameEntry(char const* pzName, tOptionValue* pRes)
{
tOptionValue* pNV;
char const * pzScan = pzName+1;
char const * pzVal;
size_t nameLen = 1;
size_t dataLen = 0;
while (ISNAMECHAR( (int)*pzScan )) { pzScan++; nameLen++; }
while (isspace( (int)*pzScan )) {
char ch = *(pzScan++);
if ((ch == '\n') || (ch == ',')) {
addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL,(size_t)0);
return pzScan - 1;
}
}
switch (*pzScan) {
case '=':
case ':':
while (isspace( (int)*++pzScan )) ;
switch (*pzScan) {
case ',': goto comma_char;
case '"':
case '\'': goto quote_char;
case NUL: goto nul_byte;
default: goto default_char;
}
case ',':
comma_char:
pzScan++;
case NUL:
nul_byte:
addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
break;
case '"':
case '\'':
quote_char:
pzVal = pzScan;
pzScan = scanQuotedString( pzScan );
dataLen = pzScan - pzVal;
pNV = addStringValue( &(pRes->v.nestVal), pzName, nameLen, pzVal,
dataLen );
if ((pNV != NULL) && (option_load_mode == OPTION_LOAD_COOKED))
ao_string_cook( pNV->v.strVal, NULL );
break;
default:
default_char:
pzVal = pzScan;
for (;;) {
char ch = *(pzScan++);
switch (ch) {
case NUL:
pzScan--;
dataLen = pzScan - pzVal;
goto string_done;
case '\n':
if ( (pzScan > pzVal + 2)
&& (pzScan[-2] == '\\')
&& (pzScan[ 0] != NUL))
continue;
case ',':
dataLen = (pzScan - pzVal) - 1;
string_done:
pNV = addStringValue( &(pRes->v.nestVal), pzName, nameLen,
pzVal, dataLen );
if (pNV != NULL)
removeBackslashes( pNV->v.strVal );
goto leave_scan_name;
}
}
break;
} leave_scan_name:;
return pzScan;
}
static char const*
scanXmlEntry( char const* pzName, tOptionValue* pRes )
{
size_t nameLen = 1, valLen = 0;
char const* pzScan = ++pzName;
char const* pzVal;
tOptionValue valu;
tOptionValue* pNewVal;
tOptionLoadMode save_mode = option_load_mode;
if (! isalpha((int)*pzName)) {
switch (*pzName) {
default:
pzName = NULL;
break;
case '!':
pzName = strstr( pzName, "-->" );
if (pzName != NULL)
pzName += 3;
break;
case '?':
pzName = strchr( pzName, '>' );
if (pzName != NULL)
pzName++;
break;
}
return pzName;
}
while (isalpha( (int)*++pzScan )) nameLen++;
if (nameLen > 64)
return NULL;
valu.valType = OPARG_TYPE_STRING;
switch (*pzScan) {
case ' ':
case '\t':
pzScan = parseAttributes(
NULL, (char*)pzScan, &option_load_mode, &valu );
if (*pzScan == '>') {
pzScan++;
break;
}
if (*pzScan != '/') {
option_load_mode = save_mode;
return NULL;
}
case '/':
if (*++pzScan != '>') {
option_load_mode = save_mode;
return NULL;
}
addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
option_load_mode = save_mode;
return pzScan+2;
default:
option_load_mode = save_mode;
return NULL;
case '>':
pzScan++;
break;
}
pzVal = pzScan;
{
char z[68];
char* pzD = z;
int ct = nameLen;
char const* pzS = pzName;
*(pzD++) = '<';
*(pzD++) = '/';
do {
*(pzD++) = *(pzS++);
} while (--ct > 0);
*(pzD++) = '>';
*pzD = NUL;
pzScan = strstr( pzScan, z );
if (pzScan == NULL) {
option_load_mode = save_mode;
return NULL;
}
valLen = (pzScan - pzVal);
pzScan += nameLen + 3;
while (isspace( (int)*pzScan )) pzScan++;
}
switch (valu.valType) {
case OPARG_TYPE_NONE:
addStringValue( &(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
break;
case OPARG_TYPE_STRING:
pNewVal = addStringValue(
&(pRes->v.nestVal), pzName, nameLen, pzVal, valLen);
if (option_load_mode == OPTION_LOAD_KEEP)
break;
mungeString( pNewVal->v.strVal, option_load_mode );
break;
case OPARG_TYPE_BOOLEAN:
addBoolValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen );
break;
case OPARG_TYPE_NUMERIC:
addNumberValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen );
break;
case OPARG_TYPE_HIERARCHY:
{
char* pz = AGALOC( valLen+1, "hierarchical scan" );
if (pz == NULL)
break;
memcpy( pz, pzVal, valLen );
pz[valLen] = NUL;
addNestedValue( &(pRes->v.nestVal), pzName, nameLen, pz, valLen );
AGFREE(pz);
break;
}
case OPARG_TYPE_ENUMERATION:
case OPARG_TYPE_MEMBERSHIP:
default:
break;
}
option_load_mode = save_mode;
return pzScan;
}
static void
unloadNestedArglist( tArgList* pAL )
{
int ct = pAL->useCt;
tCC** ppNV = pAL->apzArgs;
while (ct-- > 0) {
tOptionValue* pNV = (tOptionValue*)(void*)*(ppNV++);
if (pNV->valType == OPARG_TYPE_HIERARCHY)
unloadNestedArglist( pNV->v.nestVal );
AGFREE( pNV );
}
AGFREE( (void*)pAL );
}
void
optionUnloadNested( tOptionValue const * pOV )
{
if (pOV == NULL) return;
if (pOV->valType != OPARG_TYPE_HIERARCHY) {
errno = EINVAL;
return;
}
unloadNestedArglist( pOV->v.nestVal );
AGFREE( (void*)pOV );
}
static void
sortNestedList( tArgList* pAL )
{
int ix;
int lm = pAL->useCt;
for (ix = 0; ++ix < lm;) {
int iy = ix-1;
tOptionValue* pNewNV = (tOptionValue*)(void*)(pAL->apzArgs[ix]);
tOptionValue* pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[iy]);
while (strcmp( pOldNV->pzName, pNewNV->pzName ) > 0) {
pAL->apzArgs[iy+1] = (void*)pOldNV;
pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[--iy]);
if (iy < 0)
break;
}
pAL->apzArgs[iy+1] = (void*)pNewNV;
}
}
LOCAL tOptionValue*
optionLoadNested(char const* pzTxt, char const* pzName, size_t nameLen)
{
tOptionValue* pRes;
tArgList* pAL;
if (pzTxt == NULL) {
errno = EINVAL;
return NULL;
}
while (isspace( (int)*pzTxt )) pzTxt++;
if (*pzTxt == NUL) {
errno = ENOENT;
return NULL;
}
pRes = AGALOC( sizeof(*pRes) + nameLen + 1, "nested args" );
if (pRes == NULL) {
errno = ENOMEM;
return NULL;
}
pRes->valType = OPARG_TYPE_HIERARCHY;
pRes->pzName = (char*)(pRes + 1);
memcpy( pRes->pzName, pzName, nameLen );
pRes->pzName[ nameLen ] = NUL;
pAL = AGALOC( sizeof(*pAL), "nested arg list" );
if (pAL == NULL) {
AGFREE( pRes );
return NULL;
}
pRes->v.nestVal = pAL;
pAL->useCt = 0;
pAL->allocCt = MIN_ARG_ALLOC_CT;
do {
while (isspace( (int)*pzTxt )) pzTxt++;
if (isalpha( (int)*pzTxt )) {
pzTxt = scanNameEntry( pzTxt, pRes );
}
else switch (*pzTxt) {
case NUL: goto scan_done;
case '<': pzTxt = scanXmlEntry( pzTxt, pRes );
if (*pzTxt == ',') pzTxt++; break;
case '#': pzTxt = strchr( pzTxt, '\n' ); break;
default: goto woops;
}
} while (pzTxt != NULL); scan_done:;
pAL = pRes->v.nestVal;
if (pAL->useCt != 0) {
sortNestedList( pAL );
return pRes;
}
woops:
AGFREE( pRes->v.nestVal );
AGFREE( pRes );
return NULL;
}
void
optionNestedVal( tOptions* pOpts, tOptDesc* pOD )
{
tOptionValue* pOV = optionLoadNested(
pOD->optArg.argString, pOD->pz_Name, strlen(pOD->pz_Name));
if (pOV != NULL)
addArgListEntry( &(pOD->optCookie), (void*)pOV );
}