/contrib/ntp/libopts/enumeration.c
C | 498 lines | 270 code | 55 blank | 173 comment | 80 complexity | 8da93fde6382cac4008082a0dc7b442f MD5 | raw file
1 2/* 3 * $Id: enumeration.c,v 4.17 2007/02/04 17:44:12 bkorb Exp $ 4 * Time-stamp: "2007-01-13 10:22:35 bkorb" 5 * 6 * Automated Options Paged Usage module. 7 * 8 * This routine will run run-on options through a pager so the 9 * user may examine, print or edit them at their leisure. 10 */ 11 12/* 13 * Automated Options copyright 1992-2007 Bruce Korb 14 * 15 * Automated Options is free software. 16 * You may redistribute it and/or modify it under the terms of the 17 * GNU General Public License, as published by the Free Software 18 * Foundation; either version 2, or (at your option) any later version. 19 * 20 * Automated Options is distributed in the hope that it will be useful, 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * GNU General Public License for more details. 24 * 25 * You should have received a copy of the GNU General Public License 26 * along with Automated Options. See the file "COPYING". If not, 27 * write to: The Free Software Foundation, Inc., 28 * 51 Franklin Street, Fifth Floor, 29 * Boston, MA 02110-1301, USA. 30 * 31 * As a special exception, Bruce Korb gives permission for additional 32 * uses of the text contained in his release of AutoOpts. 33 * 34 * The exception is that, if you link the AutoOpts library with other 35 * files to produce an executable, this does not by itself cause the 36 * resulting executable to be covered by the GNU General Public License. 37 * Your use of that executable is in no way restricted on account of 38 * linking the AutoOpts library code into it. 39 * 40 * This exception does not however invalidate any other reasons why 41 * the executable file might be covered by the GNU General Public License. 42 * 43 * This exception applies only to the code released by Bruce Korb under 44 * the name AutoOpts. If you copy code from other sources under the 45 * General Public License into a copy of AutoOpts, as the General Public 46 * License permits, the exception does not apply to the code that you add 47 * in this way. To avoid misleading anyone as to the status of such 48 * modified files, you must delete this exception notice from them. 49 * 50 * If you write modifications of your own for AutoOpts, it is your choice 51 * whether to permit this exception to apply to your modifications. 52 * If you do not wish that, delete this exception notice. 53 */ 54 55tSCC* pz_enum_err_fmt; 56 57/* = = = START-STATIC-FORWARD = = = */ 58/* static forward declarations maintained by :mkfwd */ 59static void 60enumError( 61 tOptions* pOpts, 62 tOptDesc* pOD, 63 tCC* const * paz_names, 64 int name_ct ); 65 66static uintptr_t 67findName( 68 tCC* pzName, 69 tOptions* pOpts, 70 tOptDesc* pOD, 71 tCC* const * paz_names, 72 unsigned int name_ct ); 73/* = = = END-STATIC-FORWARD = = = */ 74 75static void 76enumError( 77 tOptions* pOpts, 78 tOptDesc* pOD, 79 tCC* const * paz_names, 80 int name_ct ) 81{ 82 size_t max_len = 0; 83 size_t ttl_len = 0; 84 85 if (pOpts != NULL) 86 fprintf( option_usage_fp, pz_enum_err_fmt, pOpts->pzProgName, 87 pOD->optArg.argString, pOD->pz_Name ); 88 89 fprintf( option_usage_fp, zValidKeys, pOD->pz_Name ); 90 91 if (**paz_names == 0x7F) { 92 paz_names++; 93 name_ct--; 94 } 95 96 /* 97 * Figure out the maximum length of any name, plus the total length 98 * of all the names. 99 */ 100 { 101 tCC * const * paz = paz_names; 102 int ct = name_ct; 103 104 do { 105 size_t len = strlen( *(paz++) ) + 1; 106 if (len > max_len) 107 max_len = len; 108 ttl_len += len; 109 } while (--ct > 0); 110 } 111 112 /* 113 * IF any one entry is about 1/2 line or longer, print one per line 114 */ 115 if (max_len > 35) { 116 do { 117 fprintf( option_usage_fp, " %s\n", *(paz_names++) ); 118 } while (--name_ct > 0); 119 } 120 121 /* 122 * ELSE IF they all fit on one line, then do so. 123 */ 124 else if (ttl_len < 76) { 125 fputc( ' ', option_usage_fp ); 126 do { 127 fputc( ' ', option_usage_fp ); 128 fputs( *(paz_names++), option_usage_fp ); 129 } while (--name_ct > 0); 130 fputc( '\n', option_usage_fp ); 131 } 132 133 /* 134 * Otherwise, columnize the output 135 */ 136 else { 137 int ent_no = 0; 138 char zFmt[16]; /* format for all-but-last entries on a line */ 139 140 sprintf( zFmt, "%%-%ds", (int)max_len ); 141 max_len = 78 / max_len; /* max_len is now max entries on a line */ 142 fputs( " ", option_usage_fp ); 143 144 /* 145 * Loop through all but the last entry 146 */ 147 while (--name_ct > 0) { 148 if (++ent_no == max_len) { 149 /* 150 * Last entry on a line. Start next line, too. 151 */ 152 fprintf( option_usage_fp, "%s\n ", *(paz_names++) ); 153 ent_no = 0; 154 } 155 156 else 157 fprintf( option_usage_fp, zFmt, *(paz_names++) ); 158 } 159 fprintf( option_usage_fp, "%s\n", *paz_names ); 160 } 161 162 /* 163 * IF we do not have a pOpts pointer, then this output is being requested 164 * by the usage procedure. Let's not re-invoke it recursively. 165 */ 166 if (pOpts != NULL) 167 (*(pOpts->pUsageProc))( pOpts, EXIT_FAILURE ); 168 if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_MEMBERSHIP) 169 fputs( zSetMemberSettings, option_usage_fp ); 170} 171 172 173static uintptr_t 174findName( 175 tCC* pzName, 176 tOptions* pOpts, 177 tOptDesc* pOD, 178 tCC* const * paz_names, 179 unsigned int name_ct ) 180{ 181 uintptr_t res = name_ct; 182 size_t len = strlen( (char*)pzName ); 183 uintptr_t idx; 184 /* 185 * Look for an exact match, but remember any partial matches. 186 * Multiple partial matches means we have an ambiguous match. 187 */ 188 for (idx = 0; idx < name_ct; idx++) { 189 if (strncmp( (char*)paz_names[idx], (char*)pzName, len) == 0) { 190 if (paz_names[idx][len] == NUL) 191 return idx; /* full match */ 192 193 if (res != name_ct) { 194 pz_enum_err_fmt = zAmbigKey; 195 option_usage_fp = stderr; 196 enumError( pOpts, pOD, paz_names, (int)name_ct ); 197 } 198 res = idx; /* save partial match */ 199 } 200 } 201 202 /* 203 * no partial match -> error 204 */ 205 if (res == name_ct) { 206 pz_enum_err_fmt = zNoKey; 207 option_usage_fp = stderr; 208 enumError( pOpts, pOD, paz_names, (int)name_ct ); 209 } 210 211 /* 212 * Return the matching index as a char* pointer. 213 * The result gets stashed in a char* pointer, so it will have to fit. 214 */ 215 return res; 216} 217 218 219/*=export_func optionKeywordName 220 * what: Convert between enumeration values and strings 221 * private: 222 * 223 * arg: tOptDesc*, pOD, enumeration option description 224 * arg: unsigned int, enum_val, the enumeration value to map 225 * 226 * ret_type: char const* 227 * ret_desc: the enumeration name from const memory 228 * 229 * doc: This converts an enumeration value into the matching string. 230=*/ 231char const* 232optionKeywordName( 233 tOptDesc* pOD, 234 unsigned int enum_val ) 235{ 236 tOptDesc od; 237 238 od.optArg.argEnum = enum_val; 239 (*(pOD->pOptProc))( (void*)(2UL), &od ); 240 return od.optArg.argString; 241} 242 243 244/*=export_func optionEnumerationVal 245 * what: Convert from a string to an enumeration value 246 * private: 247 * 248 * arg: tOptions*, pOpts, the program options descriptor 249 * arg: tOptDesc*, pOD, enumeration option description 250 * arg: char const * const *, paz_names, list of enumeration names 251 * arg: unsigned int, name_ct, number of names in list 252 * 253 * ret_type: uintptr_t 254 * ret_desc: the enumeration value 255 * 256 * doc: This converts the optArg.argString string from the option description 257 * into the index corresponding to an entry in the name list. 258 * This will match the generated enumeration value. 259 * Full matches are always accepted. Partial matches are accepted 260 * if there is only one partial match. 261=*/ 262uintptr_t 263optionEnumerationVal( 264 tOptions* pOpts, 265 tOptDesc* pOD, 266 tCC * const * paz_names, 267 unsigned int name_ct ) 268{ 269 uintptr_t res = 0UL; 270 271 /* 272 * IF the program option descriptor pointer is invalid, 273 * then it is some sort of special request. 274 */ 275 switch ((uintptr_t)pOpts) { 276 case 0UL: 277 /* 278 * print the list of enumeration names. 279 */ 280 enumError( pOpts, pOD, paz_names, (int)name_ct ); 281 break; 282 283 case 1UL: 284 { 285 unsigned int ix = pOD->optArg.argEnum; 286 /* 287 * print the name string. 288 */ 289 if (ix >= name_ct) 290 printf( "INVALID-%d", ix ); 291 else 292 fputs( paz_names[ ix ], stdout ); 293 294 break; 295 } 296 297 case 2UL: 298 { 299 tSCC zInval[] = "*INVALID*"; 300 unsigned int ix = pOD->optArg.argEnum; 301 /* 302 * Replace the enumeration value with the name string. 303 */ 304 if (ix >= name_ct) 305 return (uintptr_t)zInval; 306 307 res = (uintptr_t)paz_names[ ix ]; 308 break; 309 } 310 311 default: 312 res = findName( pOD->optArg.argString, pOpts, pOD, paz_names, name_ct ); 313 314 if (pOD->fOptState & OPTST_ALLOC_ARG) { 315 AGFREE(pOD->optArg.argString); 316 pOD->fOptState &= ~OPTST_ALLOC_ARG; 317 pOD->optArg.argString = NULL; 318 } 319 } 320 321 return res; 322} 323 324 325/*=export_func optionSetMembers 326 * what: Convert between bit flag values and strings 327 * private: 328 * 329 * arg: tOptions*, pOpts, the program options descriptor 330 * arg: tOptDesc*, pOD, enumeration option description 331 * arg: char const * const *, 332 * paz_names, list of enumeration names 333 * arg: unsigned int, name_ct, number of names in list 334 * 335 * doc: This converts the optArg.argString string from the option description 336 * into the index corresponding to an entry in the name list. 337 * This will match the generated enumeration value. 338 * Full matches are always accepted. Partial matches are accepted 339 * if there is only one partial match. 340=*/ 341void 342optionSetMembers( 343 tOptions* pOpts, 344 tOptDesc* pOD, 345 tCC* const * paz_names, 346 unsigned int name_ct ) 347{ 348 /* 349 * IF the program option descriptor pointer is invalid, 350 * then it is some sort of special request. 351 */ 352 switch ((uintptr_t)pOpts) { 353 case 0UL: 354 /* 355 * print the list of enumeration names. 356 */ 357 enumError( pOpts, pOD, paz_names, (int)name_ct ); 358 return; 359 360 case 1UL: 361 { 362 /* 363 * print the name string. 364 */ 365 uintptr_t bits = (uintptr_t)pOD->optCookie; 366 uintptr_t res = 0; 367 size_t len = 0; 368 369 while (bits != 0) { 370 if (bits & 1) { 371 if (len++ > 0) fputs( " | ", stdout ); 372 fputs( paz_names[ res ], stdout ); 373 } 374 if (++res >= name_ct) break; 375 bits >>= 1; 376 } 377 return; 378 } 379 380 case 2UL: 381 { 382 char* pz; 383 uintptr_t bits = (uintptr_t)pOD->optCookie; 384 uintptr_t res = 0; 385 size_t len = 0; 386 387 /* 388 * Replace the enumeration value with the name string. 389 * First, determine the needed length, then allocate and fill in. 390 */ 391 while (bits != 0) { 392 if (bits & 1) 393 len += strlen( paz_names[ res ]) + 8; 394 if (++res >= name_ct) break; 395 bits >>= 1; 396 } 397 398 pOD->optArg.argString = pz = AGALOC( len, "enum name" ); 399 400 /* 401 * Start by clearing all the bits. We want to turn off any defaults 402 * because we will be restoring to current state, not adding to 403 * the default set of bits. 404 */ 405 strcpy( pz, "none" ); 406 pz += 4; 407 bits = (uintptr_t)pOD->optCookie; 408 res = 0; 409 while (bits != 0) { 410 if (bits & 1) { 411 strcpy( pz, " + " ); 412 strcpy( pz+3, paz_names[ res ]); 413 pz += strlen( paz_names[ res ]) + 3; 414 } 415 if (++res >= name_ct) break; 416 bits >>= 1; 417 } 418 return; 419 } 420 421 default: 422 break; 423 } 424 425 { 426 tCC* pzArg = pOD->optArg.argString; 427 uintptr_t res; 428 if ((pzArg == NULL) || (*pzArg == NUL)) { 429 pOD->optCookie = (void*)0; 430 return; 431 } 432 433 res = (uintptr_t)pOD->optCookie; 434 for (;;) { 435 tSCC zSpn[] = " ,|+\t\r\f\n"; 436 int iv, len; 437 438 pzArg += strspn( pzArg, zSpn ); 439 iv = (*pzArg == '!'); 440 if (iv) 441 pzArg += strspn( pzArg+1, zSpn ) + 1; 442 443 len = strcspn( pzArg, zSpn ); 444 if (len == 0) 445 break; 446 447 if ((len == 3) && (strncmp(pzArg, zAll, (size_t)3) == 0)) { 448 if (iv) 449 res = 0; 450 else res = ~0UL; 451 } 452 else if ((len == 4) && (strncmp(pzArg, zNone, (size_t)4) == 0)) { 453 if (! iv) 454 res = 0; 455 } 456 else do { 457 char* pz; 458 uintptr_t bit = strtoul( pzArg, &pz, 0 ); 459 460 if (pz != pzArg + len) { 461 char z[ AO_NAME_SIZE ]; 462 tCC* p; 463 if (*pz != NUL) { 464 if (len >= AO_NAME_LIMIT) 465 break; 466 strncpy( z, pzArg, (size_t)len ); 467 z[len] = NUL; 468 p = z; 469 } else { 470 p = pzArg; 471 } 472 473 bit = 1UL << findName(p, pOpts, pOD, paz_names, name_ct); 474 } 475 if (iv) 476 res &= ~bit; 477 else res |= bit; 478 } while (0); 479 480 if (pzArg[len] == NUL) 481 break; 482 pzArg += len + 1; 483 } 484 if (name_ct < (8 * sizeof( uintptr_t ))) { 485 res &= (1UL << name_ct) - 1UL; 486 } 487 488 pOD->optCookie = (void*)res; 489 } 490} 491 492/* 493 * Local Variables: 494 * mode: C 495 * c-file-style: "stroustrup" 496 * indent-tabs-mode: nil 497 * End: 498 * end of autoopts/enumeration.c */