summaryrefslogtreecommitdiffstats
path: root/contrib/ntp/libopts/nested.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/ntp/libopts/nested.c')
-rw-r--r--contrib/ntp/libopts/nested.c733
1 files changed, 733 insertions, 0 deletions
diff --git a/contrib/ntp/libopts/nested.c b/contrib/ntp/libopts/nested.c
new file mode 100644
index 0000000..b39f8d1
--- /dev/null
+++ b/contrib/ntp/libopts/nested.c
@@ -0,0 +1,733 @@
+
+/*
+ * $Id: nested.c,v 4.14 2007/02/04 17:44:12 bkorb Exp $
+ * Time-stamp: "2007-01-26 11:04:35 bkorb"
+ *
+ * Automated Options Nested Values module.
+ */
+
+/*
+ * Automated Options copyright 1992-2007 Bruce Korb
+ *
+ * Automated Options is free software.
+ * You may redistribute it and/or modify it under the terms of the
+ * GNU General Public License, as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later version.
+ *
+ * Automated Options 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Automated Options. See the file "COPYING". If not,
+ * write to: The Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * As a special exception, Bruce Korb gives permission for additional
+ * uses of the text contained in his release of AutoOpts.
+ *
+ * The exception is that, if you link the AutoOpts library with other
+ * files to produce an executable, this does not by itself cause the
+ * resulting executable to be covered by the GNU General Public License.
+ * Your use of that executable is in no way restricted on account of
+ * linking the AutoOpts library code into it.
+ *
+ * This exception does not however invalidate any other reasons why
+ * the executable file might be covered by the GNU General Public License.
+ *
+ * This exception applies only to the code released by Bruce Korb under
+ * the name AutoOpts. If you copy code from other sources under the
+ * General Public License into a copy of AutoOpts, as the General Public
+ * License permits, the exception does not apply to the code that you add
+ * in this way. To avoid misleading anyone as to the status of such
+ * modified files, you must delete this exception notice from them.
+ *
+ * If you write modifications of your own for AutoOpts, it is your choice
+ * whether to permit this exception to apply to your modifications.
+ * If you do not wish that, delete this exception notice.
+ */
+/* = = = START-STATIC-FORWARD = = = */
+/* static forward declarations maintained by :mkfwd */
+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 );
+/* = = = END-STATIC-FORWARD = = = */
+
+/* removeBackslashes
+ *
+ * This function assumes that all newline characters were preceeded by
+ * backslashes that need removal.
+ */
+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:
+ ;
+ }
+ }
+}
+
+
+/* scanQuotedString
+ *
+ * Find the end of a quoted string, skipping escaped quote characters.
+ */
+static char const*
+scanQuotedString( char const* pzTxt )
+{
+ char q = *(pzTxt++); /* remember the type of quote */
+
+ for (;;) {
+ char ch = *(pzTxt++);
+ if (ch == NUL)
+ return pzTxt-1;
+
+ if (ch == q)
+ return pzTxt;
+
+ if (ch == '\\') {
+ ch = *(pzTxt++);
+ /*
+ * IF the next character is NUL, drop the backslash, too.
+ */
+ if (ch == NUL)
+ return pzTxt - 2;
+
+ /*
+ * IF the quote character or the escape character were escaped,
+ * then skip both, as long as the string does not end.
+ */
+ if ((ch == q) || (ch == '\\')) {
+ if (*(pzTxt++) == NUL)
+ return pzTxt-1;
+ }
+ }
+ }
+}
+
+
+/* addStringValue
+ *
+ * Associate a name with either a string or no value.
+ */
+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;
+}
+
+
+/* addBoolValue
+ *
+ * Associate a name with either a string or no value.
+ */
+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;
+}
+
+
+/* addNumberValue
+ *
+ * Associate a name with either a string or no value.
+ */
+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;
+}
+
+
+/* addNestedValue
+ *
+ * Associate a name with either a string or no value.
+ */
+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;
+}
+
+
+/* scanNameEntry
+ *
+ * We have an entry that starts with a name. Find the end of it, cook it
+ * (if called for) and create the name/value association.
+ */
+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++;
+ /* FALLTHROUGH */
+
+ 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:
+ /*
+ * We have found some strange text value. It ends with a newline
+ * or a comma.
+ */
+ pzVal = pzScan;
+ for (;;) {
+ char ch = *(pzScan++);
+ switch (ch) {
+ case NUL:
+ pzScan--;
+ dataLen = pzScan - pzVal;
+ goto string_done;
+ /* FALLTHROUGH */
+
+ case '\n':
+ if ( (pzScan > pzVal + 2)
+ && (pzScan[-2] == '\\')
+ && (pzScan[ 0] != NUL))
+ continue;
+ /* FALLTHROUGH */
+
+ 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;
+}
+
+
+/* scanXmlEntry
+ *
+ * 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.
+ */
+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;
+ }
+ /* FALLTHROUGH */
+
+ 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;
+}
+
+
+/* unloadNestedArglist
+ *
+ * Deallocate a list of option arguments. This must have been gotten from
+ * a hierarchical option argument, not a stacked list of strings. It is
+ * an internal call, so it is not validated. The caller is responsible for
+ * knowing what they are doing.
+ */
+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 );
+}
+
+
+/*=export_func optionUnloadNested
+ *
+ * what: Deallocate the memory for a nested value
+ * arg: + tOptionValue const * + pOptVal + the hierarchical value +
+ *
+ * doc:
+ * A nested value needs to be deallocated. The pointer passed in should
+ * have been gotten from a call to @code{configFileLoad()} (See
+ * @pxref{libopts-configFileLoad}).
+=*/
+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 );
+}
+
+
+/* sortNestedList
+ *
+ * This is a _stable_ sort. The entries are sorted alphabetically,
+ * but within entries of the same name the ordering is unchanged.
+ * Typically, we also hope the input is sorted.
+ */
+static void
+sortNestedList( tArgList* pAL )
+{
+ int ix;
+ int lm = pAL->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]);
+
+ /*
+ * 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]);
+ if (iy < 0)
+ break;
+ }
+
+ /*
+ * 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;
+ }
+}
+
+
+/* optionLoadNested
+ * 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" +
+ *
+ * ret_type: tOptionValue*
+ * ret_desc: An allocated, compound value structure
+ *
+ * doc:
+ * A block of text represents a series of values. It may be an
+ * entire configuration file, or it may be an argument to an
+ * option that takes a hierarchical value.
+ */
+LOCAL tOptionValue*
+optionLoadNested(char const* pzTxt, char const* pzName, size_t nameLen)
+{
+ tOptionValue* pRes;
+ tArgList* pAL;
+
+ /*
+ * Make sure we have some data and we have space to put what we find.
+ */
+ 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;
+
+ /*
+ * Scan until we hit a NUL.
+ */
+ 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;
+}
+
+
+/*=export_func optionNestedVal
+ * private:
+ *
+ * what: parse a hierarchical option argument
+ * arg: + tOptions* + pOpts + program options descriptor +
+ * arg: + tOptDesc* + pOptDesc + the descriptor for this arg +
+ *
+ * doc:
+ * Nested value was found on the command line
+=*/
+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 );
+}
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * end of autoopts/nested.c */
OpenPOWER on IntegriCloud