mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-02-10 16:57:00 +08:00
updated libopts
This commit is contained in:
634
libopts/nested.c
634
libopts/nested.c
@@ -2,13 +2,17 @@
|
||||
/**
|
||||
* \file nested.c
|
||||
*
|
||||
* Time-stamp: "2012-03-04 13:30:07 bkorb"
|
||||
* Handle options with arguments that contain nested values.
|
||||
*
|
||||
* @addtogroup autoopts
|
||||
* @{
|
||||
*/
|
||||
/*
|
||||
* Automated Options Nested Values module.
|
||||
*
|
||||
* This file is part of AutoOpts, a companion to AutoGen.
|
||||
* AutoOpts is free software.
|
||||
* AutoOpts is Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
|
||||
* AutoOpts is Copyright (C) 1992-2013 by Bruce Korb - all rights reserved
|
||||
*
|
||||
* AutoOpts is available under any one of two licenses. The license
|
||||
* in use must be one of these two and the choice is under the control
|
||||
@@ -20,11 +24,11 @@
|
||||
* The Modified Berkeley Software Distribution License
|
||||
* See the file "COPYING.mbsd"
|
||||
*
|
||||
* These files have the following md5sums:
|
||||
* These files have the following sha256 sums:
|
||||
*
|
||||
* 43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3
|
||||
* 06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3
|
||||
* 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
|
||||
* 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3
|
||||
* 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3
|
||||
* 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
@@ -47,35 +51,44 @@ static xml_xlate_t const xml_xlate[] = {
|
||||
|
||||
/* = = = START-STATIC-FORWARD = = = */
|
||||
static void
|
||||
remove_continuation(char* pzSrc);
|
||||
remove_continuation(char * src);
|
||||
|
||||
static char const*
|
||||
scan_q_str(char const* pzTxt);
|
||||
|
||||
static tOptionValue *
|
||||
add_string(void ** pp, char const * pzName, size_t nameLen,
|
||||
add_string(void ** pp, char const * name, size_t nm_len,
|
||||
char const* pzValue, size_t dataLen);
|
||||
|
||||
static tOptionValue *
|
||||
add_bool(void ** pp, char const * pzName, size_t nameLen,
|
||||
char const* pzValue, size_t dataLen);
|
||||
add_bool(void ** pp, char const * name, size_t nm_len,
|
||||
char const * val, size_t d_len);
|
||||
|
||||
static tOptionValue*
|
||||
add_number(void** pp, char const* pzName, size_t nameLen,
|
||||
char const* pzValue, size_t dataLen);
|
||||
add_number(void** pp, char const* pzName, size_t nm_len,
|
||||
char const* val, size_t d_len);
|
||||
|
||||
static tOptionValue*
|
||||
add_nested(void** pp, char const* pzName, size_t nameLen,
|
||||
char* pzValue, size_t dataLen);
|
||||
add_nested(void** pp, char const* pzName, size_t nm_len,
|
||||
char* val, size_t d_len);
|
||||
|
||||
static char const *
|
||||
scan_name(char const* pzName, tOptionValue* pRes);
|
||||
|
||||
static char const*
|
||||
scan_xml(char const* pzName, tOptionValue* pRes);
|
||||
static char const *
|
||||
unnamed_xml(char const * txt);
|
||||
|
||||
static char const *
|
||||
scan_xml_name(char const * name, size_t * nm_len, tOptionValue * val);
|
||||
|
||||
static char const *
|
||||
find_end_xml(char const * src, size_t nm_len, char const * val, size_t * len);
|
||||
|
||||
static char const *
|
||||
scan_xml(char const * xml_name, tOptionValue * res_val);
|
||||
|
||||
static void
|
||||
sort_list(tArgList* pAL);
|
||||
sort_list(tArgList * arg_list);
|
||||
/* = = = END-STATIC-FORWARD = = = */
|
||||
|
||||
/**
|
||||
@@ -83,13 +96,13 @@ sort_list(tArgList* pAL);
|
||||
* characters, but trim out the backslash:
|
||||
*/
|
||||
static void
|
||||
remove_continuation(char* pzSrc)
|
||||
remove_continuation(char * src)
|
||||
{
|
||||
char* pzD;
|
||||
|
||||
do {
|
||||
while (*pzSrc == NL) pzSrc++;
|
||||
pzD = strchr(pzSrc, NL);
|
||||
while (*src == NL) src++;
|
||||
pzD = strchr(src, NL);
|
||||
if (pzD == NULL)
|
||||
return;
|
||||
|
||||
@@ -98,20 +111,20 @@ remove_continuation(char* pzSrc)
|
||||
* points to a newline character. It now becomes the source and
|
||||
* pzD goes to the previous character.
|
||||
*/
|
||||
pzSrc = pzD--;
|
||||
src = pzD--;
|
||||
if (*pzD != '\\')
|
||||
pzD++;
|
||||
} while (pzD == pzSrc);
|
||||
} while (pzD == src);
|
||||
|
||||
/*
|
||||
* Start shifting text.
|
||||
*/
|
||||
for (;;) {
|
||||
char ch = ((*pzD++) = *(pzSrc++));
|
||||
char ch = ((*pzD++) = *(src++));
|
||||
switch (ch) {
|
||||
case NUL: return;
|
||||
case '\\':
|
||||
if (*pzSrc == NL)
|
||||
if (*src == NL)
|
||||
--pzD; /* rewrite on next iteration */
|
||||
}
|
||||
}
|
||||
@@ -158,11 +171,11 @@ scan_q_str(char const* pzTxt)
|
||||
* Associate a name with either a string or no value.
|
||||
*/
|
||||
static tOptionValue *
|
||||
add_string(void ** pp, char const * pzName, size_t nameLen,
|
||||
add_string(void ** pp, char const * name, size_t nm_len,
|
||||
char const* pzValue, size_t dataLen)
|
||||
{
|
||||
tOptionValue* pNV;
|
||||
size_t sz = nameLen + dataLen + sizeof(*pNV);
|
||||
size_t sz = nm_len + dataLen + sizeof(*pNV);
|
||||
|
||||
pNV = AGALOC(sz, "option name/str value pair");
|
||||
if (pNV == NULL)
|
||||
@@ -175,14 +188,14 @@ add_string(void ** pp, char const * pzName, size_t nameLen,
|
||||
} else {
|
||||
pNV->valType = OPARG_TYPE_STRING;
|
||||
if (dataLen > 0) {
|
||||
char const * pzSrc = pzValue;
|
||||
char const * src = pzValue;
|
||||
char * pzDst = pNV->v.strVal;
|
||||
int ct = dataLen;
|
||||
int ct = (int)dataLen;
|
||||
do {
|
||||
int ch = *(pzSrc++) & 0xFF;
|
||||
int ch = *(src++) & 0xFF;
|
||||
if (ch == NUL) goto data_copy_done;
|
||||
if (ch == '&')
|
||||
ch = get_special_char(&pzSrc, &ct);
|
||||
ch = get_special_char(&src, &ct);
|
||||
*(pzDst++) = (char)ch;
|
||||
} while (--ct > 0);
|
||||
data_copy_done:
|
||||
@@ -195,8 +208,8 @@ add_string(void ** pp, char const * pzName, size_t nameLen,
|
||||
pNV->pzName = pNV->v.strVal + dataLen + 1;
|
||||
}
|
||||
|
||||
memcpy(pNV->pzName, pzName, nameLen);
|
||||
pNV->pzName[ nameLen ] = NUL;
|
||||
memcpy(pNV->pzName, name, nm_len);
|
||||
pNV->pzName[ nm_len ] = NUL;
|
||||
addArgListEntry(pp, pNV);
|
||||
return pNV;
|
||||
}
|
||||
@@ -205,95 +218,95 @@ add_string(void ** pp, char const * pzName, size_t nameLen,
|
||||
* Associate a name with either a string or no value.
|
||||
*/
|
||||
static tOptionValue *
|
||||
add_bool(void ** pp, char const * pzName, size_t nameLen,
|
||||
char const* pzValue, size_t dataLen)
|
||||
add_bool(void ** pp, char const * name, size_t nm_len,
|
||||
char const * val, size_t d_len)
|
||||
{
|
||||
tOptionValue * pNV;
|
||||
tOptionValue * new_val;
|
||||
|
||||
{
|
||||
size_t sz = nameLen + sizeof(tOptionValue) + 1;
|
||||
pNV = AGALOC(sz, "name/bool value");
|
||||
size_t sz = nm_len + sizeof(tOptionValue) + 1;
|
||||
new_val = AGALOC(sz, "name/bool value");
|
||||
}
|
||||
|
||||
{
|
||||
char * p = SPN_WHITESPACE_CHARS(pzValue);
|
||||
dataLen -= p - pzValue;
|
||||
pzValue = p;
|
||||
char * p = SPN_WHITESPACE_CHARS(val);
|
||||
d_len -= (unsigned long)(p - val);
|
||||
val = p;
|
||||
}
|
||||
|
||||
if (dataLen == 0)
|
||||
pNV->v.boolVal = 0;
|
||||
if (d_len == 0)
|
||||
new_val->v.boolVal = 0;
|
||||
|
||||
else if (IS_DEC_DIGIT_CHAR(*pzValue))
|
||||
pNV->v.boolVal = atoi(pzValue);
|
||||
else if (IS_DEC_DIGIT_CHAR(*val))
|
||||
new_val->v.boolVal = (unsigned)atoi(val);
|
||||
|
||||
else pNV->v.boolVal = ! IS_FALSE_TYPE_CHAR(*pzValue);
|
||||
else new_val->v.boolVal = ! IS_FALSE_TYPE_CHAR(*val);
|
||||
|
||||
pNV->valType = OPARG_TYPE_BOOLEAN;
|
||||
pNV->pzName = (char*)(pNV + 1);
|
||||
memcpy(pNV->pzName, pzName, nameLen);
|
||||
pNV->pzName[ nameLen ] = NUL;
|
||||
addArgListEntry(pp, pNV);
|
||||
return pNV;
|
||||
new_val->valType = OPARG_TYPE_BOOLEAN;
|
||||
new_val->pzName = (char*)(new_val + 1);
|
||||
memcpy(new_val->pzName, name, nm_len);
|
||||
new_val->pzName[ nm_len ] = NUL;
|
||||
addArgListEntry(pp, new_val);
|
||||
return new_val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a name with either a string or no value.
|
||||
*/
|
||||
static tOptionValue*
|
||||
add_number(void** pp, char const* pzName, size_t nameLen,
|
||||
char const* pzValue, size_t dataLen)
|
||||
add_number(void** pp, char const* pzName, size_t nm_len,
|
||||
char const* val, size_t d_len)
|
||||
{
|
||||
tOptionValue* pNV;
|
||||
size_t sz = nameLen + sizeof(*pNV) + 1;
|
||||
tOptionValue* new_val;
|
||||
size_t sz = nm_len + sizeof(*new_val) + 1;
|
||||
|
||||
pNV = AGALOC(sz, "option name/bool value pair");
|
||||
if (pNV == NULL)
|
||||
new_val = AGALOC(sz, "bool val");
|
||||
if (new_val == NULL)
|
||||
return NULL;
|
||||
while (IS_WHITESPACE_CHAR(*pzValue) && (dataLen > 0)) {
|
||||
dataLen--; pzValue++;
|
||||
while (IS_WHITESPACE_CHAR(*val) && (d_len > 0)) {
|
||||
d_len--; val++;
|
||||
}
|
||||
if (dataLen == 0)
|
||||
pNV->v.longVal = 0;
|
||||
if (d_len == 0)
|
||||
new_val->v.longVal = 0;
|
||||
else
|
||||
pNV->v.longVal = strtol(pzValue, 0, 0);
|
||||
new_val->v.longVal = strtol(val, 0, 0);
|
||||
|
||||
pNV->valType = OPARG_TYPE_NUMERIC;
|
||||
pNV->pzName = (char*)(pNV + 1);
|
||||
memcpy(pNV->pzName, pzName, nameLen);
|
||||
pNV->pzName[ nameLen ] = NUL;
|
||||
addArgListEntry(pp, pNV);
|
||||
return pNV;
|
||||
new_val->valType = OPARG_TYPE_NUMERIC;
|
||||
new_val->pzName = (char*)(new_val + 1);
|
||||
memcpy(new_val->pzName, pzName, nm_len);
|
||||
new_val->pzName[ nm_len ] = NUL;
|
||||
addArgListEntry(pp, new_val);
|
||||
return new_val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a name with either a string or no value.
|
||||
*/
|
||||
static tOptionValue*
|
||||
add_nested(void** pp, char const* pzName, size_t nameLen,
|
||||
char* pzValue, size_t dataLen)
|
||||
add_nested(void** pp, char const* pzName, size_t nm_len,
|
||||
char* val, size_t d_len)
|
||||
{
|
||||
tOptionValue* pNV;
|
||||
tOptionValue* new_val;
|
||||
|
||||
if (dataLen == 0) {
|
||||
size_t sz = nameLen + sizeof(*pNV) + 1;
|
||||
pNV = AGALOC(sz, "empty nested value pair");
|
||||
if (pNV == NULL)
|
||||
if (d_len == 0) {
|
||||
size_t sz = nm_len + sizeof(*new_val) + 1;
|
||||
new_val = AGALOC(sz, "empty nest");
|
||||
if (new_val == 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;
|
||||
new_val->v.nestVal = NULL;
|
||||
new_val->valType = OPARG_TYPE_HIERARCHY;
|
||||
new_val->pzName = (char*)(new_val + 1);
|
||||
memcpy(new_val->pzName, pzName, nm_len);
|
||||
new_val->pzName[ nm_len ] = NUL;
|
||||
|
||||
} else {
|
||||
pNV = optionLoadNested(pzValue, pzName, nameLen);
|
||||
new_val = optionLoadNested(val, pzName, nm_len);
|
||||
}
|
||||
|
||||
if (pNV != NULL)
|
||||
addArgListEntry(pp, pNV);
|
||||
if (new_val != NULL)
|
||||
addArgListEntry(pp, new_val);
|
||||
|
||||
return pNV;
|
||||
return new_val;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -303,11 +316,11 @@ add_nested(void** pp, char const* pzName, size_t nameLen,
|
||||
static char const *
|
||||
scan_name(char const* pzName, tOptionValue* pRes)
|
||||
{
|
||||
tOptionValue* pNV;
|
||||
tOptionValue* new_val;
|
||||
char const * pzScan = pzName+1; /* we know first char is a name char */
|
||||
char const * pzVal;
|
||||
size_t nameLen = 1;
|
||||
size_t dataLen = 0;
|
||||
size_t nm_len = 1;
|
||||
size_t d_len = 0;
|
||||
|
||||
/*
|
||||
* Scan over characters that name a value. These names may not end
|
||||
@@ -316,7 +329,7 @@ scan_name(char const* pzName, tOptionValue* pRes)
|
||||
pzScan = SPN_VALUE_NAME_CHARS(pzName + 1);
|
||||
if (pzScan[-1] == ':')
|
||||
pzScan--;
|
||||
nameLen = pzScan - pzName;
|
||||
nm_len = (size_t)(pzScan - pzName);
|
||||
|
||||
pzScan = SPN_HORIZ_WHITE_CHARS(pzScan);
|
||||
|
||||
@@ -336,18 +349,18 @@ scan_name(char const* pzName, tOptionValue* pRes)
|
||||
/* FALLTHROUGH */
|
||||
|
||||
case NUL:
|
||||
add_string(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
|
||||
add_string(&(pRes->v.nestVal), pzName, nm_len, NULL, (size_t)0);
|
||||
break;
|
||||
|
||||
case '"':
|
||||
case '\'':
|
||||
pzVal = pzScan;
|
||||
pzScan = scan_q_str(pzScan);
|
||||
dataLen = pzScan - pzVal;
|
||||
pNV = add_string(&(pRes->v.nestVal), pzName, nameLen, pzVal,
|
||||
dataLen);
|
||||
if ((pNV != NULL) && (option_load_mode == OPTION_LOAD_COOKED))
|
||||
ao_string_cook(pNV->v.strVal, NULL);
|
||||
d_len = (size_t)(pzScan - pzVal);
|
||||
new_val = add_string(&(pRes->v.nestVal), pzName, nm_len, pzVal,
|
||||
d_len);
|
||||
if ((new_val != NULL) && (option_load_mode == OPTION_LOAD_COOKED))
|
||||
ao_string_cook(new_val->v.strVal, NULL);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -362,7 +375,7 @@ scan_name(char const* pzName, tOptionValue* pRes)
|
||||
switch (ch) {
|
||||
case NUL:
|
||||
pzScan--;
|
||||
dataLen = pzScan - pzVal;
|
||||
d_len = (size_t)(pzScan - pzVal);
|
||||
goto string_done;
|
||||
/* FALLTHROUGH */
|
||||
|
||||
@@ -374,12 +387,12 @@ scan_name(char const* pzName, tOptionValue* pRes)
|
||||
/* FALLTHROUGH */
|
||||
|
||||
case ',':
|
||||
dataLen = (pzScan - pzVal) - 1;
|
||||
d_len = (size_t)(pzScan - pzVal) - 1;
|
||||
string_done:
|
||||
pNV = add_string(&(pRes->v.nestVal), pzName, nameLen,
|
||||
pzVal, dataLen);
|
||||
if (pNV != NULL)
|
||||
remove_continuation(pNV->v.strVal);
|
||||
new_val = add_string(&(pRes->v.nestVal), pzName, nm_len,
|
||||
pzVal, d_len);
|
||||
if (new_val != NULL)
|
||||
remove_continuation(new_val->v.strVal);
|
||||
goto leave_scan_name;
|
||||
}
|
||||
}
|
||||
@@ -389,140 +402,194 @@ scan_name(char const* pzName, tOptionValue* pRes)
|
||||
return pzScan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Some xml element that does not start with a name.
|
||||
* The next character must be either '!' (introducing a comment),
|
||||
* or '?' (introducing an XML meta-marker of some sort).
|
||||
* We ignore these and indicate an error (NULL result) otherwise.
|
||||
*
|
||||
* @param[in] txt the text within an xml bracket
|
||||
* @returns the address of the character after the closing marker, or NULL.
|
||||
*/
|
||||
static char const *
|
||||
unnamed_xml(char const * txt)
|
||||
{
|
||||
switch (*txt) {
|
||||
default:
|
||||
txt = NULL;
|
||||
break;
|
||||
|
||||
case '!':
|
||||
txt = strstr(txt, "-->");
|
||||
if (txt != NULL)
|
||||
txt += 3;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
txt = strchr(txt, '>');
|
||||
if (txt != NULL)
|
||||
txt++;
|
||||
break;
|
||||
}
|
||||
return txt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan off the xml element name, and the rest of the header, too.
|
||||
* Set the value type to NONE if it ends with "/>".
|
||||
*
|
||||
* @param[in] name the first name character (alphabetic)
|
||||
* @param[out] nm_len the length of the name
|
||||
* @param[out] val set valType field to STRING or NONE.
|
||||
*
|
||||
* @returns the scan resumption point, or NULL on error
|
||||
*/
|
||||
static char const *
|
||||
scan_xml_name(char const * name, size_t * nm_len, tOptionValue * val)
|
||||
{
|
||||
char const * scan = SPN_VALUE_NAME_CHARS(name + 1);
|
||||
*nm_len = (size_t)(scan - name);
|
||||
if (*nm_len > 64)
|
||||
return NULL;
|
||||
val->valType = OPARG_TYPE_STRING;
|
||||
|
||||
if (IS_WHITESPACE_CHAR(*scan)) {
|
||||
/*
|
||||
* There are attributes following the name. Parse 'em.
|
||||
*/
|
||||
scan = SPN_WHITESPACE_CHARS(scan);
|
||||
scan = parse_attrs(NULL, scan, &option_load_mode, val);
|
||||
if (scan == NULL)
|
||||
return NULL; /* oops */
|
||||
}
|
||||
|
||||
if (! IS_END_XML_TOKEN_CHAR(*scan))
|
||||
return NULL; /* oops */
|
||||
|
||||
if (*scan == '/') {
|
||||
/*
|
||||
* Single element XML entries get inserted as an empty string.
|
||||
*/
|
||||
if (*++scan != '>')
|
||||
return NULL;
|
||||
val->valType = OPARG_TYPE_NONE;
|
||||
}
|
||||
return scan+1;
|
||||
}
|
||||
|
||||
/**
|
||||
* We've found a closing '>' without a preceding '/', thus we must search
|
||||
* the text for '<name/>' where "name" is the name of the XML element.
|
||||
*
|
||||
* @param[in] name the start of the name in the element header
|
||||
* @param[in] nm_len the length of that name
|
||||
* @param[out] len the length of the value (string between header and
|
||||
* the trailer/tail.
|
||||
* @returns the character after the trailer, or NULL if not found.
|
||||
*/
|
||||
static char const *
|
||||
find_end_xml(char const * src, size_t nm_len, char const * val, size_t * len)
|
||||
{
|
||||
char z[72] = "</";
|
||||
char * dst = z + 2;
|
||||
|
||||
do {
|
||||
*(dst++) = *(src++);
|
||||
} while (--nm_len > 0); /* nm_len is known to be 64 or less */
|
||||
*(dst++) = '>';
|
||||
*dst = NUL;
|
||||
|
||||
{
|
||||
char const * res = strstr(val, z);
|
||||
|
||||
if (res != NULL) {
|
||||
char const * end = (option_load_mode != OPTION_LOAD_KEEP)
|
||||
? SPN_WHITESPACE_BACK(val, res)
|
||||
: res;
|
||||
*len = (size_t)(end - val); /* includes trailing white space */
|
||||
res = SPN_WHITESPACE_CHARS(res + (dst - z));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We've found a '<' character. We ignore this if it is a comment or a
|
||||
* directive. If it is something else, then whatever it is we are looking
|
||||
* at is bogus. Returning NULL stops processing.
|
||||
*
|
||||
* @param[in] xml_name the name of an xml bracket (usually)
|
||||
* @param[in,out] res_val the option data derived from the XML element
|
||||
*
|
||||
* @returns the place to resume scanning input
|
||||
*/
|
||||
static char const*
|
||||
scan_xml(char const* pzName, tOptionValue* pRes)
|
||||
static char const *
|
||||
scan_xml(char const * xml_name, tOptionValue * res_val)
|
||||
{
|
||||
size_t nameLen;
|
||||
size_t valLen;
|
||||
char const* pzScan = ++pzName;
|
||||
char const* pzVal;
|
||||
tOptionValue valu;
|
||||
tOptionValue* pNewVal;
|
||||
size_t nm_len, v_len;
|
||||
char const * scan;
|
||||
char const * val_str;
|
||||
tOptionValue valu;
|
||||
tOptionLoadMode save_mode = option_load_mode;
|
||||
|
||||
if (! IS_VAR_FIRST_CHAR(*pzName)) {
|
||||
switch (*pzName) {
|
||||
default:
|
||||
pzName = NULL;
|
||||
break;
|
||||
if (! IS_VAR_FIRST_CHAR(*++xml_name))
|
||||
return unnamed_xml(xml_name);
|
||||
|
||||
case '!':
|
||||
pzName = strstr(pzName, "-->");
|
||||
if (pzName != NULL)
|
||||
pzName += 3;
|
||||
break;
|
||||
/*
|
||||
* "scan_xml_name()" may change "option_load_mode".
|
||||
*/
|
||||
val_str = scan_xml_name(xml_name, &nm_len, &valu);
|
||||
if (val_str == NULL)
|
||||
goto bail_scan_xml;
|
||||
|
||||
case '?':
|
||||
pzName = strchr(pzName, '>');
|
||||
if (pzName != NULL)
|
||||
pzName++;
|
||||
break;
|
||||
}
|
||||
return pzName;
|
||||
if (valu.valType == OPARG_TYPE_NONE)
|
||||
scan = val_str;
|
||||
else {
|
||||
if (option_load_mode != OPTION_LOAD_KEEP)
|
||||
val_str = SPN_WHITESPACE_CHARS(val_str);
|
||||
scan = find_end_xml(xml_name, nm_len, val_str, &v_len);
|
||||
if (scan == NULL)
|
||||
goto bail_scan_xml;
|
||||
}
|
||||
|
||||
pzScan = SPN_VALUE_NAME_CHARS(pzName+1);
|
||||
nameLen = pzScan - pzName;
|
||||
if (nameLen > 64)
|
||||
return NULL;
|
||||
valu.valType = OPARG_TYPE_STRING;
|
||||
|
||||
switch (*pzScan) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
pzScan = parse_attrs(
|
||||
NULL, (char*)pzScan, &option_load_mode, &valu );
|
||||
if (*pzScan == '>') {
|
||||
pzScan++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (*pzScan != '/') {
|
||||
option_load_mode = save_mode;
|
||||
return NULL;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
|
||||
case '/':
|
||||
if (*++pzScan != '>') {
|
||||
option_load_mode = save_mode;
|
||||
return NULL;
|
||||
}
|
||||
add_string(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
|
||||
option_load_mode = save_mode;
|
||||
return pzScan+1;
|
||||
|
||||
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;
|
||||
pzScan = SPN_WHITESPACE_CHARS(pzScan);
|
||||
}
|
||||
/*
|
||||
* "scan" now points to where the scan is to resume after returning.
|
||||
* It either points after "/>" at the end of the XML element header,
|
||||
* or it points after the "</name>" tail based on the name in the header.
|
||||
*/
|
||||
|
||||
switch (valu.valType) {
|
||||
case OPARG_TYPE_NONE:
|
||||
add_string(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0);
|
||||
add_string(&(res_val->v.nestVal), xml_name, nm_len, NULL, 0);
|
||||
break;
|
||||
|
||||
case OPARG_TYPE_STRING:
|
||||
pNewVal = add_string(
|
||||
&(pRes->v.nestVal), pzName, nameLen, pzVal, valLen);
|
||||
{
|
||||
tOptionValue * new_val = add_string(
|
||||
&(res_val->v.nestVal), xml_name, nm_len, val_str, v_len);
|
||||
|
||||
if (option_load_mode != OPTION_LOAD_KEEP)
|
||||
munge_str(new_val->v.strVal, option_load_mode);
|
||||
|
||||
if (option_load_mode == OPTION_LOAD_KEEP)
|
||||
break;
|
||||
mungeString(pNewVal->v.strVal, option_load_mode);
|
||||
break;
|
||||
}
|
||||
|
||||
case OPARG_TYPE_BOOLEAN:
|
||||
add_bool(&(pRes->v.nestVal), pzName, nameLen, pzVal, valLen);
|
||||
add_bool(&(res_val->v.nestVal), xml_name, nm_len, val_str, v_len);
|
||||
break;
|
||||
|
||||
case OPARG_TYPE_NUMERIC:
|
||||
add_number(&(pRes->v.nestVal), pzName, nameLen, pzVal, valLen);
|
||||
add_number(&(res_val->v.nestVal), xml_name, nm_len, val_str, v_len);
|
||||
break;
|
||||
|
||||
case OPARG_TYPE_HIERARCHY:
|
||||
{
|
||||
char* pz = AGALOC(valLen+1, "hierarchical scan");
|
||||
char * pz = AGALOC(v_len+1, "h scan");
|
||||
if (pz == NULL)
|
||||
break;
|
||||
memcpy(pz, pzVal, valLen);
|
||||
pz[valLen] = NUL;
|
||||
add_nested(&(pRes->v.nestVal), pzName, nameLen, pz, valLen);
|
||||
memcpy(pz, val_str, v_len);
|
||||
pz[v_len] = NUL;
|
||||
add_nested(&(res_val->v.nestVal), xml_name, nm_len, pz, v_len);
|
||||
AGFREE(pz);
|
||||
break;
|
||||
}
|
||||
@@ -534,7 +601,11 @@ scan_xml(char const* pzName, tOptionValue* pRes)
|
||||
}
|
||||
|
||||
option_load_mode = save_mode;
|
||||
return pzScan;
|
||||
return scan;
|
||||
|
||||
bail_scan_xml:
|
||||
option_load_mode = save_mode;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -545,19 +616,19 @@ scan_xml(char const* pzName, tOptionValue* pRes)
|
||||
* knowing what they are doing.
|
||||
*/
|
||||
LOCAL void
|
||||
unload_arg_list(tArgList* pAL)
|
||||
unload_arg_list(tArgList * arg_list)
|
||||
{
|
||||
int ct = pAL->useCt;
|
||||
tCC** ppNV = pAL->apzArgs;
|
||||
int ct = arg_list->useCt;
|
||||
char const ** pnew_val = arg_list->apzArgs;
|
||||
|
||||
while (ct-- > 0) {
|
||||
tOptionValue* pNV = (tOptionValue*)(void*)*(ppNV++);
|
||||
if (pNV->valType == OPARG_TYPE_HIERARCHY)
|
||||
unload_arg_list(pNV->v.nestVal);
|
||||
AGFREE(pNV);
|
||||
tOptionValue* new_val = (tOptionValue*)(void*)*(pnew_val++);
|
||||
if (new_val->valType == OPARG_TYPE_HIERARCHY)
|
||||
unload_arg_list(new_val->v.nestVal);
|
||||
AGFREE(new_val);
|
||||
}
|
||||
|
||||
AGFREE((void*)pAL);
|
||||
AGFREE((void*)arg_list);
|
||||
}
|
||||
|
||||
/*=export_func optionUnloadNested
|
||||
@@ -571,17 +642,17 @@ unload_arg_list(tArgList* pAL)
|
||||
* @pxref{libopts-configFileLoad}).
|
||||
=*/
|
||||
void
|
||||
optionUnloadNested(tOptionValue const * pOV)
|
||||
optionUnloadNested(tOptionValue const * opt_val)
|
||||
{
|
||||
if (pOV == NULL) return;
|
||||
if (pOV->valType != OPARG_TYPE_HIERARCHY) {
|
||||
if (opt_val == NULL) return;
|
||||
if (opt_val->valType != OPARG_TYPE_HIERARCHY) {
|
||||
errno = EINVAL;
|
||||
return;
|
||||
}
|
||||
|
||||
unload_arg_list(pOV->v.nestVal);
|
||||
unload_arg_list(opt_val->v.nestVal);
|
||||
|
||||
AGFREE((void*)pOV);
|
||||
AGFREE((void*)opt_val);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -590,27 +661,27 @@ optionUnloadNested(tOptionValue const * pOV)
|
||||
* Typically, we also hope the input is sorted.
|
||||
*/
|
||||
static void
|
||||
sort_list(tArgList* pAL)
|
||||
sort_list(tArgList * arg_list)
|
||||
{
|
||||
int ix;
|
||||
int lm = pAL->useCt;
|
||||
int lm = arg_list->useCt;
|
||||
|
||||
/*
|
||||
* This loop iterates "useCt" - 1 times.
|
||||
*/
|
||||
for (ix = 0; ++ix < lm;) {
|
||||
int iy = ix-1;
|
||||
tOptionValue* pNewNV = (tOptionValue*)(void*)(pAL->apzArgs[ix]);
|
||||
tOptionValue* pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[iy]);
|
||||
tOptionValue * new_v = C(tOptionValue *, arg_list->apzArgs[ix]);
|
||||
tOptionValue * old_v = C(tOptionValue *, arg_list->apzArgs[iy]);
|
||||
|
||||
/*
|
||||
* For as long as the new entry precedes the "old" entry,
|
||||
* move the old pointer. Stop before trying to extract the
|
||||
* "-1" entry.
|
||||
*/
|
||||
while (strcmp(pOldNV->pzName, pNewNV->pzName) > 0) {
|
||||
pAL->apzArgs[iy+1] = (void*)pOldNV;
|
||||
pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[--iy]);
|
||||
while (strcmp(old_v->pzName, new_v->pzName) > 0) {
|
||||
arg_list->apzArgs[iy+1] = (void*)old_v;
|
||||
old_v = (tOptionValue*)(void*)(arg_list->apzArgs[--iy]);
|
||||
if (iy < 0)
|
||||
break;
|
||||
}
|
||||
@@ -619,7 +690,7 @@ sort_list(tArgList* pAL)
|
||||
* Always store the pointer. Sometimes it is redundant,
|
||||
* but the redundancy is cheaper than a test and branch sequence.
|
||||
*/
|
||||
pAL->apzArgs[iy+1] = (void*)pNewNV;
|
||||
arg_list->apzArgs[iy+1] = (void*)new_v;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -627,9 +698,9 @@ sort_list(tArgList* pAL)
|
||||
* private:
|
||||
*
|
||||
* what: parse a hierarchical option argument
|
||||
* arg: + char const* + pzTxt + the text to scan +
|
||||
* arg: + char const* + pzName + the name for the text +
|
||||
* arg: + size_t + nameLen + the length of "name" +
|
||||
* arg: + char const * + pzTxt + the text to scan +
|
||||
* arg: + char const * + pzName + the name for the text +
|
||||
* arg: + size_t + nm_len + the length of "name" +
|
||||
*
|
||||
* ret_type: tOptionValue*
|
||||
* ret_desc: An allocated, compound value structure
|
||||
@@ -649,65 +720,57 @@ sort_list(tArgList* pAL)
|
||||
* @code{ENOMSG} no configuration values were found
|
||||
* @end itemize
|
||||
=*/
|
||||
LOCAL tOptionValue*
|
||||
optionLoadNested(char const* pzTxt, char const* pzName, size_t nameLen)
|
||||
LOCAL tOptionValue *
|
||||
optionLoadNested(char const * text, char const * name, size_t nm_len)
|
||||
{
|
||||
tOptionValue* pRes;
|
||||
tOptionValue* res_val;
|
||||
|
||||
/*
|
||||
* Make sure we have some data and we have space to put what we find.
|
||||
*/
|
||||
if (pzTxt == NULL) {
|
||||
if (text == NULL) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
pzTxt = SPN_WHITESPACE_CHARS(pzTxt);
|
||||
if (*pzTxt == NUL) {
|
||||
text = SPN_WHITESPACE_CHARS(text);
|
||||
if (*text == NUL) {
|
||||
errno = ENOMSG;
|
||||
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;
|
||||
res_val = AGALOC(sizeof(*res_val) + nm_len + 1, "nest args");
|
||||
res_val->valType = OPARG_TYPE_HIERARCHY;
|
||||
res_val->pzName = (char*)(res_val + 1);
|
||||
memcpy(res_val->pzName, name, nm_len);
|
||||
res_val->pzName[nm_len] = NUL;
|
||||
|
||||
{
|
||||
tArgList * pAL = AGALOC(sizeof(*pAL), "nested arg list");
|
||||
if (pAL == NULL) {
|
||||
AGFREE(pRes);
|
||||
return NULL;
|
||||
}
|
||||
tArgList * arg_list = AGALOC(sizeof(*arg_list), "nest arg l");
|
||||
|
||||
pRes->v.nestVal = pAL;
|
||||
pAL->useCt = 0;
|
||||
pAL->allocCt = MIN_ARG_ALLOC_CT;
|
||||
res_val->v.nestVal = arg_list;
|
||||
arg_list->useCt = 0;
|
||||
arg_list->allocCt = MIN_ARG_ALLOC_CT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan until we hit a NUL.
|
||||
*/
|
||||
do {
|
||||
pzTxt = SPN_WHITESPACE_CHARS(pzTxt);
|
||||
if (IS_VAR_FIRST_CHAR(*pzTxt))
|
||||
pzTxt = scan_name(pzTxt, pRes);
|
||||
text = SPN_WHITESPACE_CHARS(text);
|
||||
if (IS_VAR_FIRST_CHAR(*text))
|
||||
text = scan_name(text, res_val);
|
||||
|
||||
else switch (*pzTxt) {
|
||||
else switch (*text) {
|
||||
case NUL: goto scan_done;
|
||||
case '<': pzTxt = scan_xml(pzTxt, pRes);
|
||||
if (pzTxt == NULL) goto woops;
|
||||
if (*pzTxt == ',') pzTxt++; break;
|
||||
case '#': pzTxt = strchr(pzTxt, NL); break;
|
||||
case '<': text = scan_xml(text, res_val);
|
||||
if (text == NULL) goto woops;
|
||||
if (*text == ',') text++; break;
|
||||
case '#': text = strchr(text, NL); break;
|
||||
default: goto woops;
|
||||
}
|
||||
} while (pzTxt != NULL); scan_done:;
|
||||
} while (text != NULL); scan_done:;
|
||||
|
||||
{
|
||||
tArgList * al = pRes->v.nestVal;
|
||||
tArgList * al = res_val->v.nestVal;
|
||||
if (al->useCt == 0) {
|
||||
errno = ENOMSG;
|
||||
goto woops;
|
||||
@@ -716,11 +779,11 @@ optionLoadNested(char const* pzTxt, char const* pzName, size_t nameLen)
|
||||
sort_list(al);
|
||||
}
|
||||
|
||||
return pRes;
|
||||
return res_val;
|
||||
|
||||
woops:
|
||||
AGFREE(pRes->v.nestVal);
|
||||
AGFREE(pRes);
|
||||
AGFREE(res_val->v.nestVal);
|
||||
AGFREE(res_val);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -728,45 +791,45 @@ optionLoadNested(char const* pzTxt, char const* pzName, size_t nameLen)
|
||||
* private:
|
||||
*
|
||||
* what: parse a hierarchical option argument
|
||||
* arg: + tOptions* + pOpts + program options descriptor +
|
||||
* arg: + tOptDesc* + pOptDesc + the descriptor for this arg +
|
||||
* arg: + tOptions* + opts + program options descriptor +
|
||||
* arg: + tOptDesc* + od + the descriptor for this arg +
|
||||
*
|
||||
* doc:
|
||||
* Nested value was found on the command line
|
||||
=*/
|
||||
void
|
||||
optionNestedVal(tOptions* pOpts, tOptDesc* pOD)
|
||||
optionNestedVal(tOptions * opts, tOptDesc * od)
|
||||
{
|
||||
if (pOpts < OPTPROC_EMIT_LIMIT)
|
||||
if (opts < OPTPROC_EMIT_LIMIT)
|
||||
return;
|
||||
|
||||
if (pOD->fOptState & OPTST_RESET) {
|
||||
tArgList* pAL = pOD->optCookie;
|
||||
int ct;
|
||||
tCC ** av;
|
||||
if (od->fOptState & OPTST_RESET) {
|
||||
tArgList * arg_list = od->optCookie;
|
||||
int ct;
|
||||
char const ** av;
|
||||
|
||||
if (pAL == NULL)
|
||||
if (arg_list == NULL)
|
||||
return;
|
||||
ct = pAL->useCt;
|
||||
av = pAL->apzArgs;
|
||||
ct = arg_list->useCt;
|
||||
av = arg_list->apzArgs;
|
||||
|
||||
while (--ct >= 0) {
|
||||
void * p = (void *)*(av++);
|
||||
optionUnloadNested((tOptionValue const *)p);
|
||||
}
|
||||
|
||||
AGFREE(pOD->optCookie);
|
||||
AGFREE(od->optCookie);
|
||||
|
||||
} else {
|
||||
tOptionValue* pOV = optionLoadNested(
|
||||
pOD->optArg.argString, pOD->pz_Name, strlen(pOD->pz_Name));
|
||||
tOptionValue * opt_val = optionLoadNested(
|
||||
od->optArg.argString, od->pz_Name, strlen(od->pz_Name));
|
||||
|
||||
if (pOV != NULL)
|
||||
addArgListEntry(&(pOD->optCookie), (void*)pOV);
|
||||
if (opt_val != NULL)
|
||||
addArgListEntry(&(od->optCookie), (void*)opt_val);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* get_special_char
|
||||
*/
|
||||
LOCAL int
|
||||
@@ -789,7 +852,7 @@ get_special_char(char const ** ppz, int * ct)
|
||||
retch = (int)strtoul(pz, (char **)&pz, base);
|
||||
if (*pz != ';')
|
||||
return '&';
|
||||
base = ++pz - *ppz;
|
||||
base = (int)(++pz - *ppz);
|
||||
if (base > *ct)
|
||||
return '&';
|
||||
|
||||
@@ -804,7 +867,7 @@ get_special_char(char const ** ppz, int * ct)
|
||||
|
||||
for (;;) {
|
||||
if ( (*ct >= xlatp->xml_len)
|
||||
&& (strncmp(pz, xlatp->xml_txt, xlatp->xml_len) == 0)) {
|
||||
&& (strncmp(pz, xlatp->xml_txt, (size_t)xlatp->xml_len) == 0)) {
|
||||
*ppz += xlatp->xml_len;
|
||||
*ct -= xlatp->xml_len;
|
||||
return xlatp->xml_ch;
|
||||
@@ -818,7 +881,7 @@ get_special_char(char const ** ppz, int * ct)
|
||||
return '&';
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* emit_special_char
|
||||
*/
|
||||
LOCAL void
|
||||
@@ -840,7 +903,8 @@ emit_special_char(FILE * fp, int ch)
|
||||
fprintf(fp, XML_HEX_BYTE_FMT, (ch & 0xFF));
|
||||
}
|
||||
|
||||
/*
|
||||
/** @}
|
||||
*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-file-style: "stroustrup"
|
||||
|
||||
Reference in New Issue
Block a user