📄 nested.c
字号:
/* * $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 voidremoveBackslashes( 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 voidunloadNestedArglist( tArgList* pAL );static voidsortNestedList( tArgList* pAL );/* = = = END-STATIC-FORWARD = = = *//* removeBackslashes * * This function assumes that all newline characters were preceeded by * backslashes that need removal. */static voidremoveBackslashes( 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -