1 2/* 3 * $Id: 27595043d23170eb4bb8b9831fc54016944e00e8 $ 4 * Time-stamp: "2008-07-27 12:28:01 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 * This file is part of AutoOpts, a companion to AutoGen. 12 * AutoOpts is free software. 13 * AutoOpts is copyright (c) 1992-2009 by Bruce Korb - all rights reserved 14 * 15 * AutoOpts is available under any one of two licenses. The license 16 * in use must be one of these two and the choice is under the control 17 * of the user of the license. 18 * 19 * The GNU Lesser General Public License, version 3 or later 20 * See the files "COPYING.lgplv3" and "COPYING.gplv3" 21 * 22 * The Modified Berkeley Software Distribution License 23 * See the file "COPYING.mbsd" 24 * 25 * These files have the following md5sums: 26 * 27 * 43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3 28 * 06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3 29 * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd 30 */ 31 32tSCC* pz_enum_err_fmt; 33 34/* = = = START-STATIC-FORWARD = = = */ 35/* static forward declarations maintained by mk-fwd */ 36static void 37enumError( 38 tOptions* pOpts, 39 tOptDesc* pOD, 40 tCC* const * paz_names, 41 int name_ct ); 42 43static uintptr_t 44findName( 45 tCC* pzName, 46 tOptions* pOpts, 47 tOptDesc* pOD, 48 tCC* const * paz_names, 49 unsigned int name_ct ); 50/* = = = END-STATIC-FORWARD = = = */ 51 52static void 53enumError( 54 tOptions* pOpts, 55 tOptDesc* pOD, 56 tCC* const * paz_names, 57 int name_ct ) 58{ 59 size_t max_len = 0; 60 size_t ttl_len = 0; 61 int ct_down = name_ct; 62 int hidden = 0; 63 64 /* 65 * A real "pOpts" pointer means someone messed up. Give a real error. 66 */ 67 if (pOpts > OPTPROC_EMIT_LIMIT) 68 fprintf(option_usage_fp, pz_enum_err_fmt, pOpts->pzProgName, 69 pOD->optArg.argString, pOD->pz_Name); 70 71 fprintf(option_usage_fp, zValidKeys, pOD->pz_Name); 72 73 /* 74 * If the first name starts with this funny character, then we have 75 * a first value with an unspellable name. You cannot specify it. 76 * So, we don't list it either. 77 */ 78 if (**paz_names == 0x7F) { 79 paz_names++; 80 hidden = 1; 81 ct_down = --name_ct; 82 } 83 84 /* 85 * Figure out the maximum length of any name, plus the total length 86 * of all the names. 87 */ 88 { 89 tCC * const * paz = paz_names; 90 91 do { 92 size_t len = strlen( *(paz++) ) + 1; 93 if (len > max_len) 94 max_len = len; 95 ttl_len += len; 96 } while (--ct_down > 0); 97 98 ct_down = name_ct; 99 } 100 101 /* 102 * IF any one entry is about 1/2 line or longer, print one per line 103 */ 104 if (max_len > 35) { 105 do { 106 fprintf( option_usage_fp, " %s\n", *(paz_names++) ); 107 } while (--ct_down > 0); 108 } 109 110 /* 111 * ELSE IF they all fit on one line, then do so. 112 */ 113 else if (ttl_len < 76) { 114 fputc( ' ', option_usage_fp ); 115 do { 116 fputc( ' ', option_usage_fp ); 117 fputs( *(paz_names++), option_usage_fp ); 118 } while (--ct_down > 0); 119 fputc( '\n', option_usage_fp ); 120 } 121 122 /* 123 * Otherwise, columnize the output 124 */ 125 else { 126 int ent_no = 0; 127 char zFmt[16]; /* format for all-but-last entries on a line */ 128 129 sprintf( zFmt, "%%-%ds", (int)max_len ); 130 max_len = 78 / max_len; /* max_len is now max entries on a line */ 131 fputs( " ", option_usage_fp ); 132 133 /* 134 * Loop through all but the last entry 135 */ 136 ct_down = name_ct; 137 while (--ct_down > 0) { 138 if (++ent_no == max_len) { 139 /* 140 * Last entry on a line. Start next line, too. 141 */ 142 fprintf( option_usage_fp, "%s\n ", *(paz_names++) ); 143 ent_no = 0; 144 } 145 146 else 147 fprintf(option_usage_fp, zFmt, *(paz_names++) ); 148 } 149 fprintf(option_usage_fp, "%s\n", *paz_names); 150 } 151 152 if (pOpts > OPTPROC_EMIT_LIMIT) { 153 fprintf(option_usage_fp, zIntRange, hidden, name_ct - 1 + hidden); 154 155 (*(pOpts->pUsageProc))( pOpts, EXIT_FAILURE ); 156 /* NOTREACHED */ 157 } 158 159 160 if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_MEMBERSHIP) { 161 fprintf(option_usage_fp, zLowerBits, name_ct); 162 fputs(zSetMemberSettings, option_usage_fp); 163 } else { 164 fprintf(option_usage_fp, zIntRange, hidden, name_ct - 1 + hidden); 165 } 166} 167 168 169static uintptr_t 170findName( 171 tCC* pzName, 172 tOptions* pOpts, 173 tOptDesc* pOD, 174 tCC* const * paz_names, 175 unsigned int name_ct ) 176{ 177 /* 178 * Return the matching index as a pointer sized integer. 179 * The result gets stashed in a char* pointer. 180 */ 181 uintptr_t res = name_ct; 182 size_t len = strlen( (char*)pzName ); 183 uintptr_t idx; 184 185 if (IS_DEC_DIGIT_CHAR(*pzName)) { 186 char * pz = (char *)(void *)pzName; 187 unsigned long val = strtoul(pz, &pz, 0); 188 if ((*pz == NUL) && (val < name_ct)) 189 return (uintptr_t)val; 190 enumError(pOpts, pOD, paz_names, (int)name_ct); 191 return name_ct; 192 } 193 194 /* 195 * Look for an exact match, but remember any partial matches. 196 * Multiple partial matches means we have an ambiguous match. 197 */ 198 for (idx = 0; idx < name_ct; idx++) { 199 if (strncmp( (char*)paz_names[idx], (char*)pzName, len) == 0) { 200 if (paz_names[idx][len] == NUL) 201 return idx; /* full match */ 202 203 res = (res != name_ct) ? ~0 : idx; /* save partial match */ 204 } 205 } 206 207 if (res < name_ct) 208 return res; /* partial match */ 209 210 pz_enum_err_fmt = (res == name_ct) ? zNoKey : zAmbigKey; 211 option_usage_fp = stderr; 212 enumError(pOpts, pOD, paz_names, (int)name_ct); 213 return name_ct; 214} 215 216 217/*=export_func optionKeywordName 218 * what: Convert between enumeration values and strings 219 * private: 220 * 221 * arg: tOptDesc*, pOD, enumeration option description 222 * arg: unsigned int, enum_val, the enumeration value to map 223 * 224 * ret_type: char const* 225 * ret_desc: the enumeration name from const memory 226 * 227 * doc: This converts an enumeration value into the matching string. 228=*/ 229char const* 230optionKeywordName( 231 tOptDesc* pOD, 232 unsigned int enum_val ) 233{ 234 tOptDesc od; 235 236 od.optArg.argEnum = enum_val; 237 (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, &od ); 238 return od.optArg.argString; 239} 240 241 242/*=export_func optionEnumerationVal 243 * what: Convert from a string to an enumeration value 244 * private: 245 * 246 * arg: tOptions*, pOpts, the program options descriptor 247 * arg: tOptDesc*, pOD, enumeration option description 248 * arg: char const * const *, paz_names, list of enumeration names 249 * arg: unsigned int, name_ct, number of names in list 250 * 251 * ret_type: uintptr_t 252 * ret_desc: the enumeration value 253 * 254 * doc: This converts the optArg.argString string from the option description 255 * into the index corresponding to an entry in the name list. 256 * This will match the generated enumeration value. 257 * Full matches are always accepted. Partial matches are accepted 258 * if there is only one partial match. 259=*/ 260uintptr_t 261optionEnumerationVal( 262 tOptions* pOpts, 263 tOptDesc* pOD, 264 tCC * const * paz_names, 265 unsigned int name_ct ) 266{ 267 uintptr_t res = 0UL; 268 269 /* 270 * IF the program option descriptor pointer is invalid, 271 * then it is some sort of special request. 272 */ 273 switch ((uintptr_t)pOpts) { 274 case (uintptr_t)OPTPROC_EMIT_USAGE: 275 /* 276 * print the list of enumeration names. 277 */ 278 enumError(pOpts, pOD, paz_names, (int)name_ct); 279 break; 280 281 case (uintptr_t)OPTPROC_EMIT_SHELL: 282 { 283 unsigned int ix = pOD->optArg.argEnum; 284 /* 285 * print the name string. 286 */ 287 if (ix >= name_ct) 288 printf( "INVALID-%d", ix ); 289 else 290 fputs( paz_names[ ix ], stdout ); 291 292 break; 293 } 294 295 case (uintptr_t)OPTPROC_RETURN_VALNAME: 296 { 297 tSCC zInval[] = "*INVALID*"; 298 unsigned int ix = pOD->optArg.argEnum; 299 /* 300 * Replace the enumeration value with the name string. 301 */ 302 if (ix >= name_ct) 303 return (uintptr_t)zInval; 304 305 pOD->optArg.argString = paz_names[ix]; 306 break; 307 } 308 309 default: 310 res = findName(pOD->optArg.argString, pOpts, pOD, paz_names, name_ct); 311 312 if (pOD->fOptState & OPTST_ALLOC_ARG) { 313 AGFREE(pOD->optArg.argString); 314 pOD->fOptState &= ~OPTST_ALLOC_ARG; 315 pOD->optArg.argString = NULL; 316 } 317 } 318 319 return res; 320} 321 322 323/*=export_func optionSetMembers 324 * what: Convert between bit flag values and strings 325 * private: 326 * 327 * arg: tOptions*, pOpts, the program options descriptor 328 * arg: tOptDesc*, pOD, enumeration option description 329 * arg: char const * const *, 330 * paz_names, list of enumeration names 331 * arg: unsigned int, name_ct, number of names in list 332 * 333 * doc: This converts the optArg.argString string from the option description 334 * into the index corresponding to an entry in the name list. 335 * This will match the generated enumeration value. 336 * Full matches are always accepted. Partial matches are accepted 337 * if there is only one partial match. 338=*/ 339void 340optionSetMembers( 341 tOptions* pOpts, 342 tOptDesc* pOD, 343 tCC* const * paz_names, 344 unsigned int name_ct ) 345{ 346 /* 347 * IF the program option descriptor pointer is invalid, 348 * then it is some sort of special request. 349 */ 350 switch ((uintptr_t)pOpts) { 351 case (uintptr_t)OPTPROC_EMIT_USAGE: 352 /* 353 * print the list of enumeration names. 354 */ 355 enumError(OPTPROC_EMIT_USAGE, pOD, paz_names, (int)name_ct ); 356 return; 357 358 case (uintptr_t)OPTPROC_EMIT_SHELL: 359 { 360 /* 361 * print the name string. 362 */ 363 int ix = 0; 364 uintptr_t bits = (uintptr_t)pOD->optCookie; 365 size_t len = 0; 366 367 bits &= ((uintptr_t)1 << (uintptr_t)name_ct) - (uintptr_t)1; 368 369 while (bits != 0) { 370 if (bits & 1) { 371 if (len++ > 0) fputs( " | ", stdout ); 372 fputs(paz_names[ix], stdout); 373 } 374 if (++ix >= name_ct) break; 375 bits >>= 1; 376 } 377 return; 378 } 379 380 case (uintptr_t)OPTPROC_RETURN_VALNAME: 381 { 382 char* pz; 383 uintptr_t bits = (uintptr_t)pOD->optCookie; 384 int ix = 0; 385 size_t len = 5; 386 387 bits &= ((uintptr_t)1 << (uintptr_t)name_ct) - (uintptr_t)1; 388 389 /* 390 * Replace the enumeration value with the name string. 391 * First, determine the needed length, then allocate and fill in. 392 */ 393 while (bits != 0) { 394 if (bits & 1) 395 len += strlen( paz_names[ix]) + 8; 396 if (++ix >= name_ct) break; 397 bits >>= 1; 398 } 399 400 pOD->optArg.argString = pz = AGALOC(len, "enum name"); 401 402 /* 403 * Start by clearing all the bits. We want to turn off any defaults 404 * because we will be restoring to current state, not adding to 405 * the default set of bits. 406 */ 407 strcpy( pz, "none" ); 408 pz += 4; 409 bits = (uintptr_t)pOD->optCookie; 410 bits &= ((uintptr_t)1 << (uintptr_t)name_ct) - (uintptr_t)1; 411 ix = 0; 412 413 while (bits != 0) { 414 if (bits & 1) { 415 strcpy( pz, " + " ); 416 strcpy( pz+3, paz_names[ix]); 417 pz += strlen( paz_names[ix]) + 3; 418 } 419 if (++ix >= name_ct) break; 420 bits >>= 1; 421 } 422 return; 423 } 424 425 default: 426 break; 427 } 428 429 if ((pOD->fOptState & OPTST_RESET) != 0) 430 return; 431 432 { 433 tCC* pzArg = pOD->optArg.argString; 434 uintptr_t res; 435 if ((pzArg == NULL) || (*pzArg == NUL)) { 436 pOD->optCookie = (void*)0; 437 return; 438 } 439 440 res = (uintptr_t)pOD->optCookie; 441 for (;;) { 442 tSCC zSpn[] = " ,|+\t\r\f\n"; 443 int iv, len; 444 445 pzArg += strspn( pzArg, zSpn ); 446 iv = (*pzArg == '!'); 447 if (iv) 448 pzArg += strspn( pzArg+1, zSpn ) + 1; 449 450 len = strcspn( pzArg, zSpn ); 451 if (len == 0) 452 break; 453 454 if ((len == 3) && (strncmp(pzArg, zAll, (size_t)3) == 0)) { 455 if (iv) 456 res = 0; 457 else res = ~0UL; 458 } 459 else if ((len == 4) && (strncmp(pzArg, zNone, (size_t)4) == 0)) { 460 if (! iv) 461 res = 0; 462 } 463 else do { 464 char* pz; 465 uintptr_t bit = strtoul( pzArg, &pz, 0 ); 466 467 if (pz != pzArg + len) { 468 char z[ AO_NAME_SIZE ]; 469 tCC* p; 470 int shift_ct; 471 472 if (*pz != NUL) { 473 if (len >= AO_NAME_LIMIT) 474 break; 475 strncpy( z, pzArg, (size_t)len ); 476 z[len] = NUL; 477 p = z; 478 } else { 479 p = pzArg; 480 } 481 482 shift_ct = findName(p, pOpts, pOD, paz_names, name_ct); 483 if (shift_ct >= name_ct) { 484 pOD->optCookie = (void*)0; 485 return; 486 } 487 bit = 1UL << shift_ct; 488 } 489 if (iv) 490 res &= ~bit; 491 else res |= bit; 492 } while (0); 493 494 if (pzArg[len] == NUL) 495 break; 496 pzArg += len + 1; 497 } 498 if (name_ct < (8 * sizeof( uintptr_t ))) { 499 res &= (1UL << name_ct) - 1UL; 500 } 501 502 pOD->optCookie = (void*)res; 503 } 504} 505 506/* 507 * Local Variables: 508 * mode: C 509 * c-file-style: "stroustrup" 510 * indent-tabs-mode: nil 511 * End: 512 * end of autoopts/enumeration.c */ 513