1181834Sroberto 2181834Sroberto/* 3181834Sroberto * $Id: nested.c,v 4.14 2007/02/04 17:44:12 bkorb Exp $ 4181834Sroberto * Time-stamp: "2007-01-26 11:04:35 bkorb" 5181834Sroberto * 6181834Sroberto * Automated Options Nested Values module. 7181834Sroberto */ 8181834Sroberto 9181834Sroberto/* 10181834Sroberto * Automated Options copyright 1992-2007 Bruce Korb 11181834Sroberto * 12181834Sroberto * Automated Options is free software. 13181834Sroberto * You may redistribute it and/or modify it under the terms of the 14181834Sroberto * GNU General Public License, as published by the Free Software 15181834Sroberto * Foundation; either version 2, or (at your option) any later version. 16181834Sroberto * 17181834Sroberto * Automated Options is distributed in the hope that it will be useful, 18181834Sroberto * but WITHOUT ANY WARRANTY; without even the implied warranty of 19181834Sroberto * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20181834Sroberto * GNU General Public License for more details. 21181834Sroberto * 22181834Sroberto * You should have received a copy of the GNU General Public License 23181834Sroberto * along with Automated Options. See the file "COPYING". If not, 24181834Sroberto * write to: The Free Software Foundation, Inc., 25181834Sroberto * 51 Franklin Street, Fifth Floor, 26181834Sroberto * Boston, MA 02110-1301, USA. 27181834Sroberto * 28181834Sroberto * As a special exception, Bruce Korb gives permission for additional 29181834Sroberto * uses of the text contained in his release of AutoOpts. 30181834Sroberto * 31181834Sroberto * The exception is that, if you link the AutoOpts library with other 32181834Sroberto * files to produce an executable, this does not by itself cause the 33181834Sroberto * resulting executable to be covered by the GNU General Public License. 34181834Sroberto * Your use of that executable is in no way restricted on account of 35181834Sroberto * linking the AutoOpts library code into it. 36181834Sroberto * 37181834Sroberto * This exception does not however invalidate any other reasons why 38181834Sroberto * the executable file might be covered by the GNU General Public License. 39181834Sroberto * 40181834Sroberto * This exception applies only to the code released by Bruce Korb under 41181834Sroberto * the name AutoOpts. If you copy code from other sources under the 42181834Sroberto * General Public License into a copy of AutoOpts, as the General Public 43181834Sroberto * License permits, the exception does not apply to the code that you add 44181834Sroberto * in this way. To avoid misleading anyone as to the status of such 45181834Sroberto * modified files, you must delete this exception notice from them. 46181834Sroberto * 47181834Sroberto * If you write modifications of your own for AutoOpts, it is your choice 48181834Sroberto * whether to permit this exception to apply to your modifications. 49181834Sroberto * If you do not wish that, delete this exception notice. 50181834Sroberto */ 51181834Sroberto/* = = = START-STATIC-FORWARD = = = */ 52181834Sroberto/* static forward declarations maintained by :mkfwd */ 53181834Srobertostatic void 54181834SrobertoremoveBackslashes( char* pzSrc ); 55181834Sroberto 56181834Srobertostatic char const* 57181834SrobertoscanQuotedString( char const* pzTxt ); 58181834Sroberto 59181834Srobertostatic tOptionValue* 60181834SrobertoaddStringValue( void** pp, char const* pzName, size_t nameLen, 61181834Sroberto char const* pzValue, size_t dataLen ); 62181834Sroberto 63181834Srobertostatic tOptionValue* 64181834SrobertoaddBoolValue( void** pp, char const* pzName, size_t nameLen, 65181834Sroberto char const* pzValue, size_t dataLen ); 66181834Sroberto 67181834Srobertostatic tOptionValue* 68181834SrobertoaddNumberValue( void** pp, char const* pzName, size_t nameLen, 69181834Sroberto char const* pzValue, size_t dataLen ); 70181834Sroberto 71181834Srobertostatic tOptionValue* 72181834SrobertoaddNestedValue( void** pp, char const* pzName, size_t nameLen, 73181834Sroberto char* pzValue, size_t dataLen ); 74181834Sroberto 75181834Srobertostatic char const* 76181834SrobertoscanNameEntry(char const* pzName, tOptionValue* pRes); 77181834Sroberto 78181834Srobertostatic char const* 79181834SrobertoscanXmlEntry( char const* pzName, tOptionValue* pRes ); 80181834Sroberto 81181834Srobertostatic void 82181834SrobertounloadNestedArglist( tArgList* pAL ); 83181834Sroberto 84181834Srobertostatic void 85181834SrobertosortNestedList( tArgList* pAL ); 86181834Sroberto/* = = = END-STATIC-FORWARD = = = */ 87181834Sroberto 88181834Sroberto/* removeBackslashes 89181834Sroberto * 90181834Sroberto * This function assumes that all newline characters were preceeded by 91181834Sroberto * backslashes that need removal. 92181834Sroberto */ 93181834Srobertostatic void 94181834SrobertoremoveBackslashes( char* pzSrc ) 95181834Sroberto{ 96181834Sroberto char* pzD = strchr(pzSrc, '\n'); 97181834Sroberto 98181834Sroberto if (pzD == NULL) 99181834Sroberto return; 100181834Sroberto *--pzD = '\n'; 101181834Sroberto 102181834Sroberto for (;;) { 103181834Sroberto char ch = ((*pzD++) = *(pzSrc++)); 104181834Sroberto switch (ch) { 105181834Sroberto case '\n': *--pzD = ch; break; 106181834Sroberto case NUL: return; 107181834Sroberto default: 108181834Sroberto ; 109181834Sroberto } 110181834Sroberto } 111181834Sroberto} 112181834Sroberto 113181834Sroberto 114181834Sroberto/* scanQuotedString 115181834Sroberto * 116181834Sroberto * Find the end of a quoted string, skipping escaped quote characters. 117181834Sroberto */ 118181834Srobertostatic char const* 119181834SrobertoscanQuotedString( char const* pzTxt ) 120181834Sroberto{ 121181834Sroberto char q = *(pzTxt++); /* remember the type of quote */ 122181834Sroberto 123181834Sroberto for (;;) { 124181834Sroberto char ch = *(pzTxt++); 125181834Sroberto if (ch == NUL) 126181834Sroberto return pzTxt-1; 127181834Sroberto 128181834Sroberto if (ch == q) 129181834Sroberto return pzTxt; 130181834Sroberto 131181834Sroberto if (ch == '\\') { 132181834Sroberto ch = *(pzTxt++); 133181834Sroberto /* 134181834Sroberto * IF the next character is NUL, drop the backslash, too. 135181834Sroberto */ 136181834Sroberto if (ch == NUL) 137181834Sroberto return pzTxt - 2; 138181834Sroberto 139181834Sroberto /* 140181834Sroberto * IF the quote character or the escape character were escaped, 141181834Sroberto * then skip both, as long as the string does not end. 142181834Sroberto */ 143181834Sroberto if ((ch == q) || (ch == '\\')) { 144181834Sroberto if (*(pzTxt++) == NUL) 145181834Sroberto return pzTxt-1; 146181834Sroberto } 147181834Sroberto } 148181834Sroberto } 149181834Sroberto} 150181834Sroberto 151181834Sroberto 152181834Sroberto/* addStringValue 153181834Sroberto * 154181834Sroberto * Associate a name with either a string or no value. 155181834Sroberto */ 156181834Srobertostatic tOptionValue* 157181834SrobertoaddStringValue( void** pp, char const* pzName, size_t nameLen, 158181834Sroberto char const* pzValue, size_t dataLen ) 159181834Sroberto{ 160181834Sroberto tOptionValue* pNV; 161181834Sroberto size_t sz = nameLen + dataLen + sizeof(*pNV); 162181834Sroberto 163181834Sroberto pNV = AGALOC( sz, "option name/str value pair" ); 164181834Sroberto if (pNV == NULL) 165181834Sroberto return NULL; 166181834Sroberto 167181834Sroberto if (pzValue == NULL) { 168181834Sroberto pNV->valType = OPARG_TYPE_NONE; 169181834Sroberto pNV->pzName = pNV->v.strVal; 170181834Sroberto 171181834Sroberto } else { 172181834Sroberto pNV->valType = OPARG_TYPE_STRING; 173181834Sroberto if (dataLen > 0) 174181834Sroberto memcpy( pNV->v.strVal, pzValue, dataLen ); 175181834Sroberto pNV->v.strVal[dataLen] = NUL; 176181834Sroberto pNV->pzName = pNV->v.strVal + dataLen + 1; 177181834Sroberto } 178181834Sroberto 179181834Sroberto memcpy( pNV->pzName, pzName, nameLen ); 180181834Sroberto pNV->pzName[ nameLen ] = NUL; 181181834Sroberto addArgListEntry( pp, pNV ); 182181834Sroberto return pNV; 183181834Sroberto} 184181834Sroberto 185181834Sroberto 186181834Sroberto/* addBoolValue 187181834Sroberto * 188181834Sroberto * Associate a name with either a string or no value. 189181834Sroberto */ 190181834Srobertostatic tOptionValue* 191181834SrobertoaddBoolValue( void** pp, char const* pzName, size_t nameLen, 192181834Sroberto char const* pzValue, size_t dataLen ) 193181834Sroberto{ 194181834Sroberto tOptionValue* pNV; 195181834Sroberto size_t sz = nameLen + sizeof(*pNV) + 1; 196181834Sroberto 197181834Sroberto pNV = AGALOC( sz, "option name/bool value pair" ); 198181834Sroberto if (pNV == NULL) 199181834Sroberto return NULL; 200181834Sroberto while (isspace( (int)*pzValue ) && (dataLen > 0)) { 201181834Sroberto dataLen--; pzValue++; 202181834Sroberto } 203181834Sroberto if (dataLen == 0) 204181834Sroberto pNV->v.boolVal = 0; 205181834Sroberto else if (isdigit( (int)*pzValue )) 206181834Sroberto pNV->v.boolVal = atoi( pzValue ); 207181834Sroberto else switch (*pzValue) { 208181834Sroberto case 'f': 209181834Sroberto case 'F': 210181834Sroberto case 'n': 211181834Sroberto case 'N': 212181834Sroberto pNV->v.boolVal = 0; break; 213181834Sroberto default: 214181834Sroberto pNV->v.boolVal = 1; 215181834Sroberto } 216181834Sroberto 217181834Sroberto pNV->valType = OPARG_TYPE_BOOLEAN; 218181834Sroberto pNV->pzName = (char*)(pNV + 1); 219181834Sroberto memcpy( pNV->pzName, pzName, nameLen ); 220181834Sroberto pNV->pzName[ nameLen ] = NUL; 221181834Sroberto addArgListEntry( pp, pNV ); 222181834Sroberto return pNV; 223181834Sroberto} 224181834Sroberto 225181834Sroberto 226181834Sroberto/* addNumberValue 227181834Sroberto * 228181834Sroberto * Associate a name with either a string or no value. 229181834Sroberto */ 230181834Srobertostatic tOptionValue* 231181834SrobertoaddNumberValue( void** pp, char const* pzName, size_t nameLen, 232181834Sroberto char const* pzValue, size_t dataLen ) 233181834Sroberto{ 234181834Sroberto tOptionValue* pNV; 235181834Sroberto size_t sz = nameLen + sizeof(*pNV) + 1; 236181834Sroberto 237181834Sroberto pNV = AGALOC( sz, "option name/bool value pair" ); 238181834Sroberto if (pNV == NULL) 239181834Sroberto return NULL; 240181834Sroberto while (isspace( (int)*pzValue ) && (dataLen > 0)) { 241181834Sroberto dataLen--; pzValue++; 242181834Sroberto } 243181834Sroberto if (dataLen == 0) 244181834Sroberto pNV->v.boolVal = 0; 245181834Sroberto else 246181834Sroberto pNV->v.boolVal = atoi( pzValue ); 247181834Sroberto 248181834Sroberto pNV->valType = OPARG_TYPE_NUMERIC; 249181834Sroberto pNV->pzName = (char*)(pNV + 1); 250181834Sroberto memcpy( pNV->pzName, pzName, nameLen ); 251181834Sroberto pNV->pzName[ nameLen ] = NUL; 252181834Sroberto addArgListEntry( pp, pNV ); 253181834Sroberto return pNV; 254181834Sroberto} 255181834Sroberto 256181834Sroberto 257181834Sroberto/* addNestedValue 258181834Sroberto * 259181834Sroberto * Associate a name with either a string or no value. 260181834Sroberto */ 261181834Srobertostatic tOptionValue* 262181834SrobertoaddNestedValue( void** pp, char const* pzName, size_t nameLen, 263181834Sroberto char* pzValue, size_t dataLen ) 264181834Sroberto{ 265181834Sroberto tOptionValue* pNV; 266181834Sroberto 267181834Sroberto if (dataLen == 0) { 268181834Sroberto size_t sz = nameLen + sizeof(*pNV) + 1; 269181834Sroberto pNV = AGALOC( sz, "empty nested value pair" ); 270181834Sroberto if (pNV == NULL) 271181834Sroberto return NULL; 272181834Sroberto pNV->v.nestVal = NULL; 273181834Sroberto pNV->valType = OPARG_TYPE_HIERARCHY; 274181834Sroberto pNV->pzName = (char*)(pNV + 1); 275181834Sroberto memcpy( pNV->pzName, pzName, nameLen ); 276181834Sroberto pNV->pzName[ nameLen ] = NUL; 277181834Sroberto 278181834Sroberto } else { 279181834Sroberto pNV = optionLoadNested( pzValue, pzName, nameLen ); 280181834Sroberto } 281181834Sroberto 282181834Sroberto if (pNV != NULL) 283181834Sroberto addArgListEntry( pp, pNV ); 284181834Sroberto 285181834Sroberto return pNV; 286181834Sroberto} 287181834Sroberto 288181834Sroberto 289181834Sroberto/* scanNameEntry 290181834Sroberto * 291181834Sroberto * We have an entry that starts with a name. Find the end of it, cook it 292181834Sroberto * (if called for) and create the name/value association. 293181834Sroberto */ 294181834Srobertostatic char const* 295181834SrobertoscanNameEntry(char const* pzName, tOptionValue* pRes) 296181834Sroberto{ 297181834Sroberto tOptionValue* pNV; 298181834Sroberto char const * pzScan = pzName+1; 299181834Sroberto char const * pzVal; 300181834Sroberto size_t nameLen = 1; 301181834Sroberto size_t dataLen = 0; 302181834Sroberto 303181834Sroberto while (ISNAMECHAR( (int)*pzScan )) { pzScan++; nameLen++; } 304181834Sroberto 305181834Sroberto while (isspace( (int)*pzScan )) { 306181834Sroberto char ch = *(pzScan++); 307181834Sroberto if ((ch == '\n') || (ch == ',')) { 308181834Sroberto addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL,(size_t)0); 309181834Sroberto return pzScan - 1; 310181834Sroberto } 311181834Sroberto } 312181834Sroberto 313181834Sroberto switch (*pzScan) { 314181834Sroberto case '=': 315181834Sroberto case ':': 316181834Sroberto while (isspace( (int)*++pzScan )) ; 317181834Sroberto switch (*pzScan) { 318181834Sroberto case ',': goto comma_char; 319181834Sroberto case '"': 320181834Sroberto case '\'': goto quote_char; 321181834Sroberto case NUL: goto nul_byte; 322181834Sroberto default: goto default_char; 323181834Sroberto } 324181834Sroberto 325181834Sroberto case ',': 326181834Sroberto comma_char: 327181834Sroberto pzScan++; 328181834Sroberto /* FALLTHROUGH */ 329181834Sroberto 330181834Sroberto case NUL: 331181834Sroberto nul_byte: 332181834Sroberto addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0); 333181834Sroberto break; 334181834Sroberto 335181834Sroberto case '"': 336181834Sroberto case '\'': 337181834Sroberto quote_char: 338181834Sroberto pzVal = pzScan; 339181834Sroberto pzScan = scanQuotedString( pzScan ); 340181834Sroberto dataLen = pzScan - pzVal; 341181834Sroberto pNV = addStringValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, 342181834Sroberto dataLen ); 343181834Sroberto if ((pNV != NULL) && (option_load_mode == OPTION_LOAD_COOKED)) 344181834Sroberto ao_string_cook( pNV->v.strVal, NULL ); 345181834Sroberto break; 346181834Sroberto 347181834Sroberto default: 348181834Sroberto default_char: 349181834Sroberto /* 350181834Sroberto * We have found some strange text value. It ends with a newline 351181834Sroberto * or a comma. 352181834Sroberto */ 353181834Sroberto pzVal = pzScan; 354181834Sroberto for (;;) { 355181834Sroberto char ch = *(pzScan++); 356181834Sroberto switch (ch) { 357181834Sroberto case NUL: 358181834Sroberto pzScan--; 359181834Sroberto dataLen = pzScan - pzVal; 360181834Sroberto goto string_done; 361181834Sroberto /* FALLTHROUGH */ 362181834Sroberto 363181834Sroberto case '\n': 364181834Sroberto if ( (pzScan > pzVal + 2) 365181834Sroberto && (pzScan[-2] == '\\') 366181834Sroberto && (pzScan[ 0] != NUL)) 367181834Sroberto continue; 368181834Sroberto /* FALLTHROUGH */ 369181834Sroberto 370181834Sroberto case ',': 371181834Sroberto dataLen = (pzScan - pzVal) - 1; 372181834Sroberto string_done: 373181834Sroberto pNV = addStringValue( &(pRes->v.nestVal), pzName, nameLen, 374181834Sroberto pzVal, dataLen ); 375181834Sroberto if (pNV != NULL) 376181834Sroberto removeBackslashes( pNV->v.strVal ); 377181834Sroberto goto leave_scan_name; 378181834Sroberto } 379181834Sroberto } 380181834Sroberto break; 381181834Sroberto } leave_scan_name:; 382181834Sroberto 383181834Sroberto return pzScan; 384181834Sroberto} 385181834Sroberto 386181834Sroberto 387181834Sroberto/* scanXmlEntry 388181834Sroberto * 389181834Sroberto * We've found a '<' character. We ignore this if it is a comment or a 390181834Sroberto * directive. If it is something else, then whatever it is we are looking 391181834Sroberto * at is bogus. Returning NULL stops processing. 392181834Sroberto */ 393181834Srobertostatic char const* 394181834SrobertoscanXmlEntry( char const* pzName, tOptionValue* pRes ) 395181834Sroberto{ 396181834Sroberto size_t nameLen = 1, valLen = 0; 397181834Sroberto char const* pzScan = ++pzName; 398181834Sroberto char const* pzVal; 399181834Sroberto tOptionValue valu; 400181834Sroberto tOptionValue* pNewVal; 401181834Sroberto tOptionLoadMode save_mode = option_load_mode; 402181834Sroberto 403181834Sroberto if (! isalpha((int)*pzName)) { 404181834Sroberto switch (*pzName) { 405181834Sroberto default: 406181834Sroberto pzName = NULL; 407181834Sroberto break; 408181834Sroberto 409181834Sroberto case '!': 410181834Sroberto pzName = strstr( pzName, "-->" ); 411181834Sroberto if (pzName != NULL) 412181834Sroberto pzName += 3; 413181834Sroberto break; 414181834Sroberto 415181834Sroberto case '?': 416181834Sroberto pzName = strchr( pzName, '>' ); 417181834Sroberto if (pzName != NULL) 418181834Sroberto pzName++; 419181834Sroberto break; 420181834Sroberto } 421181834Sroberto return pzName; 422181834Sroberto } 423181834Sroberto 424181834Sroberto while (isalpha( (int)*++pzScan )) nameLen++; 425181834Sroberto if (nameLen > 64) 426181834Sroberto return NULL; 427181834Sroberto valu.valType = OPARG_TYPE_STRING; 428181834Sroberto 429181834Sroberto switch (*pzScan) { 430181834Sroberto case ' ': 431181834Sroberto case '\t': 432181834Sroberto pzScan = parseAttributes( 433181834Sroberto NULL, (char*)pzScan, &option_load_mode, &valu ); 434181834Sroberto if (*pzScan == '>') { 435181834Sroberto pzScan++; 436181834Sroberto break; 437181834Sroberto } 438181834Sroberto 439181834Sroberto if (*pzScan != '/') { 440181834Sroberto option_load_mode = save_mode; 441181834Sroberto return NULL; 442181834Sroberto } 443181834Sroberto /* FALLTHROUGH */ 444181834Sroberto 445181834Sroberto case '/': 446181834Sroberto if (*++pzScan != '>') { 447181834Sroberto option_load_mode = save_mode; 448181834Sroberto return NULL; 449181834Sroberto } 450181834Sroberto addStringValue(&(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0); 451181834Sroberto option_load_mode = save_mode; 452181834Sroberto return pzScan+2; 453181834Sroberto 454181834Sroberto default: 455181834Sroberto option_load_mode = save_mode; 456181834Sroberto return NULL; 457181834Sroberto 458181834Sroberto case '>': 459181834Sroberto pzScan++; 460181834Sroberto break; 461181834Sroberto } 462181834Sroberto 463181834Sroberto pzVal = pzScan; 464181834Sroberto 465181834Sroberto { 466181834Sroberto char z[68]; 467181834Sroberto char* pzD = z; 468181834Sroberto int ct = nameLen; 469181834Sroberto char const* pzS = pzName; 470181834Sroberto 471181834Sroberto *(pzD++) = '<'; 472181834Sroberto *(pzD++) = '/'; 473181834Sroberto 474181834Sroberto do { 475181834Sroberto *(pzD++) = *(pzS++); 476181834Sroberto } while (--ct > 0); 477181834Sroberto *(pzD++) = '>'; 478181834Sroberto *pzD = NUL; 479181834Sroberto 480181834Sroberto pzScan = strstr( pzScan, z ); 481181834Sroberto if (pzScan == NULL) { 482181834Sroberto option_load_mode = save_mode; 483181834Sroberto return NULL; 484181834Sroberto } 485181834Sroberto valLen = (pzScan - pzVal); 486181834Sroberto pzScan += nameLen + 3; 487181834Sroberto while (isspace( (int)*pzScan )) pzScan++; 488181834Sroberto } 489181834Sroberto 490181834Sroberto switch (valu.valType) { 491181834Sroberto case OPARG_TYPE_NONE: 492181834Sroberto addStringValue( &(pRes->v.nestVal), pzName, nameLen, NULL, (size_t)0); 493181834Sroberto break; 494181834Sroberto 495181834Sroberto case OPARG_TYPE_STRING: 496181834Sroberto pNewVal = addStringValue( 497181834Sroberto &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen); 498181834Sroberto 499181834Sroberto if (option_load_mode == OPTION_LOAD_KEEP) 500181834Sroberto break; 501181834Sroberto mungeString( pNewVal->v.strVal, option_load_mode ); 502181834Sroberto break; 503181834Sroberto 504181834Sroberto case OPARG_TYPE_BOOLEAN: 505181834Sroberto addBoolValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen ); 506181834Sroberto break; 507181834Sroberto 508181834Sroberto case OPARG_TYPE_NUMERIC: 509181834Sroberto addNumberValue( &(pRes->v.nestVal), pzName, nameLen, pzVal, valLen ); 510181834Sroberto break; 511181834Sroberto 512181834Sroberto case OPARG_TYPE_HIERARCHY: 513181834Sroberto { 514181834Sroberto char* pz = AGALOC( valLen+1, "hierarchical scan" ); 515181834Sroberto if (pz == NULL) 516181834Sroberto break; 517181834Sroberto memcpy( pz, pzVal, valLen ); 518181834Sroberto pz[valLen] = NUL; 519181834Sroberto addNestedValue( &(pRes->v.nestVal), pzName, nameLen, pz, valLen ); 520181834Sroberto AGFREE(pz); 521181834Sroberto break; 522181834Sroberto } 523181834Sroberto 524181834Sroberto case OPARG_TYPE_ENUMERATION: 525181834Sroberto case OPARG_TYPE_MEMBERSHIP: 526181834Sroberto default: 527181834Sroberto break; 528181834Sroberto } 529181834Sroberto 530181834Sroberto option_load_mode = save_mode; 531181834Sroberto return pzScan; 532181834Sroberto} 533181834Sroberto 534181834Sroberto 535181834Sroberto/* unloadNestedArglist 536181834Sroberto * 537181834Sroberto * Deallocate a list of option arguments. This must have been gotten from 538181834Sroberto * a hierarchical option argument, not a stacked list of strings. It is 539181834Sroberto * an internal call, so it is not validated. The caller is responsible for 540181834Sroberto * knowing what they are doing. 541181834Sroberto */ 542181834Srobertostatic void 543181834SrobertounloadNestedArglist( tArgList* pAL ) 544181834Sroberto{ 545181834Sroberto int ct = pAL->useCt; 546181834Sroberto tCC** ppNV = pAL->apzArgs; 547181834Sroberto 548181834Sroberto while (ct-- > 0) { 549181834Sroberto tOptionValue* pNV = (tOptionValue*)(void*)*(ppNV++); 550181834Sroberto if (pNV->valType == OPARG_TYPE_HIERARCHY) 551181834Sroberto unloadNestedArglist( pNV->v.nestVal ); 552181834Sroberto AGFREE( pNV ); 553181834Sroberto } 554181834Sroberto 555181834Sroberto AGFREE( (void*)pAL ); 556181834Sroberto} 557181834Sroberto 558181834Sroberto 559181834Sroberto/*=export_func optionUnloadNested 560181834Sroberto * 561181834Sroberto * what: Deallocate the memory for a nested value 562181834Sroberto * arg: + tOptionValue const * + pOptVal + the hierarchical value + 563181834Sroberto * 564181834Sroberto * doc: 565181834Sroberto * A nested value needs to be deallocated. The pointer passed in should 566181834Sroberto * have been gotten from a call to @code{configFileLoad()} (See 567181834Sroberto * @pxref{libopts-configFileLoad}). 568181834Sroberto=*/ 569181834Srobertovoid 570181834SrobertooptionUnloadNested( tOptionValue const * pOV ) 571181834Sroberto{ 572181834Sroberto if (pOV == NULL) return; 573181834Sroberto if (pOV->valType != OPARG_TYPE_HIERARCHY) { 574181834Sroberto errno = EINVAL; 575181834Sroberto return; 576181834Sroberto } 577181834Sroberto 578181834Sroberto unloadNestedArglist( pOV->v.nestVal ); 579181834Sroberto 580181834Sroberto AGFREE( (void*)pOV ); 581181834Sroberto} 582181834Sroberto 583181834Sroberto 584181834Sroberto/* sortNestedList 585181834Sroberto * 586181834Sroberto * This is a _stable_ sort. The entries are sorted alphabetically, 587181834Sroberto * but within entries of the same name the ordering is unchanged. 588181834Sroberto * Typically, we also hope the input is sorted. 589181834Sroberto */ 590181834Srobertostatic void 591181834SrobertosortNestedList( tArgList* pAL ) 592181834Sroberto{ 593181834Sroberto int ix; 594181834Sroberto int lm = pAL->useCt; 595181834Sroberto 596181834Sroberto /* 597181834Sroberto * This loop iterates "useCt" - 1 times. 598181834Sroberto */ 599181834Sroberto for (ix = 0; ++ix < lm;) { 600181834Sroberto int iy = ix-1; 601181834Sroberto tOptionValue* pNewNV = (tOptionValue*)(void*)(pAL->apzArgs[ix]); 602181834Sroberto tOptionValue* pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[iy]); 603181834Sroberto 604181834Sroberto /* 605181834Sroberto * For as long as the new entry precedes the "old" entry, 606181834Sroberto * move the old pointer. Stop before trying to extract the 607181834Sroberto * "-1" entry. 608181834Sroberto */ 609181834Sroberto while (strcmp( pOldNV->pzName, pNewNV->pzName ) > 0) { 610181834Sroberto pAL->apzArgs[iy+1] = (void*)pOldNV; 611181834Sroberto pOldNV = (tOptionValue*)(void*)(pAL->apzArgs[--iy]); 612181834Sroberto if (iy < 0) 613181834Sroberto break; 614181834Sroberto } 615181834Sroberto 616181834Sroberto /* 617181834Sroberto * Always store the pointer. Sometimes it is redundant, 618181834Sroberto * but the redundancy is cheaper than a test and branch sequence. 619181834Sroberto */ 620181834Sroberto pAL->apzArgs[iy+1] = (void*)pNewNV; 621181834Sroberto } 622181834Sroberto} 623181834Sroberto 624181834Sroberto 625181834Sroberto/* optionLoadNested 626181834Sroberto * private: 627181834Sroberto * 628181834Sroberto * what: parse a hierarchical option argument 629181834Sroberto * arg: + char const* + pzTxt + the text to scan + 630181834Sroberto * arg: + char const* + pzName + the name for the text + 631181834Sroberto * arg: + size_t + nameLen + the length of "name" + 632181834Sroberto * 633181834Sroberto * ret_type: tOptionValue* 634181834Sroberto * ret_desc: An allocated, compound value structure 635181834Sroberto * 636181834Sroberto * doc: 637181834Sroberto * A block of text represents a series of values. It may be an 638181834Sroberto * entire configuration file, or it may be an argument to an 639181834Sroberto * option that takes a hierarchical value. 640181834Sroberto */ 641181834SrobertoLOCAL tOptionValue* 642181834SrobertooptionLoadNested(char const* pzTxt, char const* pzName, size_t nameLen) 643181834Sroberto{ 644181834Sroberto tOptionValue* pRes; 645181834Sroberto tArgList* pAL; 646181834Sroberto 647181834Sroberto /* 648181834Sroberto * Make sure we have some data and we have space to put what we find. 649181834Sroberto */ 650181834Sroberto if (pzTxt == NULL) { 651181834Sroberto errno = EINVAL; 652181834Sroberto return NULL; 653181834Sroberto } 654181834Sroberto while (isspace( (int)*pzTxt )) pzTxt++; 655181834Sroberto if (*pzTxt == NUL) { 656181834Sroberto errno = ENOENT; 657181834Sroberto return NULL; 658181834Sroberto } 659181834Sroberto pRes = AGALOC( sizeof(*pRes) + nameLen + 1, "nested args" ); 660181834Sroberto if (pRes == NULL) { 661181834Sroberto errno = ENOMEM; 662181834Sroberto return NULL; 663181834Sroberto } 664181834Sroberto pRes->valType = OPARG_TYPE_HIERARCHY; 665181834Sroberto pRes->pzName = (char*)(pRes + 1); 666181834Sroberto memcpy( pRes->pzName, pzName, nameLen ); 667181834Sroberto pRes->pzName[ nameLen ] = NUL; 668181834Sroberto 669181834Sroberto pAL = AGALOC( sizeof(*pAL), "nested arg list" ); 670181834Sroberto if (pAL == NULL) { 671181834Sroberto AGFREE( pRes ); 672181834Sroberto return NULL; 673181834Sroberto } 674181834Sroberto pRes->v.nestVal = pAL; 675181834Sroberto pAL->useCt = 0; 676181834Sroberto pAL->allocCt = MIN_ARG_ALLOC_CT; 677181834Sroberto 678181834Sroberto /* 679181834Sroberto * Scan until we hit a NUL. 680181834Sroberto */ 681181834Sroberto do { 682181834Sroberto while (isspace( (int)*pzTxt )) pzTxt++; 683181834Sroberto if (isalpha( (int)*pzTxt )) { 684181834Sroberto pzTxt = scanNameEntry( pzTxt, pRes ); 685181834Sroberto } 686181834Sroberto else switch (*pzTxt) { 687181834Sroberto case NUL: goto scan_done; 688181834Sroberto case '<': pzTxt = scanXmlEntry( pzTxt, pRes ); 689181834Sroberto if (*pzTxt == ',') pzTxt++; break; 690181834Sroberto case '#': pzTxt = strchr( pzTxt, '\n' ); break; 691181834Sroberto default: goto woops; 692181834Sroberto } 693181834Sroberto } while (pzTxt != NULL); scan_done:; 694181834Sroberto 695181834Sroberto pAL = pRes->v.nestVal; 696181834Sroberto if (pAL->useCt != 0) { 697181834Sroberto sortNestedList( pAL ); 698181834Sroberto return pRes; 699181834Sroberto } 700181834Sroberto 701181834Sroberto woops: 702181834Sroberto AGFREE( pRes->v.nestVal ); 703181834Sroberto AGFREE( pRes ); 704181834Sroberto return NULL; 705181834Sroberto} 706181834Sroberto 707181834Sroberto 708181834Sroberto/*=export_func optionNestedVal 709181834Sroberto * private: 710181834Sroberto * 711181834Sroberto * what: parse a hierarchical option argument 712181834Sroberto * arg: + tOptions* + pOpts + program options descriptor + 713181834Sroberto * arg: + tOptDesc* + pOptDesc + the descriptor for this arg + 714181834Sroberto * 715181834Sroberto * doc: 716181834Sroberto * Nested value was found on the command line 717181834Sroberto=*/ 718181834Srobertovoid 719181834SrobertooptionNestedVal( tOptions* pOpts, tOptDesc* pOD ) 720181834Sroberto{ 721181834Sroberto tOptionValue* pOV = optionLoadNested( 722181834Sroberto pOD->optArg.argString, pOD->pz_Name, strlen(pOD->pz_Name)); 723181834Sroberto 724181834Sroberto if (pOV != NULL) 725181834Sroberto addArgListEntry( &(pOD->optCookie), (void*)pOV ); 726181834Sroberto} 727181834Sroberto/* 728181834Sroberto * Local Variables: 729181834Sroberto * mode: C 730181834Sroberto * c-file-style: "stroustrup" 731181834Sroberto * indent-tabs-mode: nil 732181834Sroberto * End: 733181834Sroberto * end of autoopts/nested.c */ 734