1/* $NetBSD: enumeration.c,v 1.2 2010/12/04 23:08:34 christos Exp $ */ 2 3 4/* 5 * Id: 27595043d23170eb4bb8b9831fc54016944e00e8 6 * Time-stamp: "2008-07-27 12:28:01 bkorb" 7 * 8 * Automated Options Paged Usage module. 9 * 10 * This routine will run run-on options through a pager so the 11 * user may examine, print or edit them at their leisure. 12 * 13 * This file is part of AutoOpts, a companion to AutoGen. 14 * AutoOpts is free software. 15 * AutoOpts is copyright (c) 1992-2009 by Bruce Korb - all rights reserved 16 * 17 * AutoOpts is available under any one of two licenses. The license 18 * in use must be one of these two and the choice is under the control 19 * of the user of the license. 20 * 21 * The GNU Lesser General Public License, version 3 or later 22 * See the files "COPYING.lgplv3" and "COPYING.gplv3" 23 * 24 * The Modified Berkeley Software Distribution License 25 * See the file "COPYING.mbsd" 26 * 27 * These files have the following md5sums: 28 * 29 * 43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3 30 * 06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3 31 * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd 32 */ 33 34tSCC* pz_enum_err_fmt; 35 36/* = = = START-STATIC-FORWARD = = = */ 37/* static forward declarations maintained by mk-fwd */ 38static void 39enumError( 40 tOptions* pOpts, 41 tOptDesc* pOD, 42 tCC* const * paz_names, 43 int name_ct ); 44 45static uintptr_t 46findName( 47 tCC* pzName, 48 tOptions* pOpts, 49 tOptDesc* pOD, 50 tCC* const * paz_names, 51 unsigned int name_ct ); 52/* = = = END-STATIC-FORWARD = = = */ 53 54static void 55enumError( 56 tOptions* pOpts, 57 tOptDesc* pOD, 58 tCC* const * paz_names, 59 int name_ct ) 60{ 61 size_t max_len = 0; 62 size_t ttl_len = 0; 63 int ct_down = name_ct; 64 int hidden = 0; 65 66 /* 67 * A real "pOpts" pointer means someone messed up. Give a real error. 68 */ 69 if (pOpts > OPTPROC_EMIT_LIMIT) 70 fprintf(option_usage_fp, pz_enum_err_fmt, pOpts->pzProgName, 71 pOD->optArg.argString, pOD->pz_Name); 72 73 fprintf(option_usage_fp, zValidKeys, pOD->pz_Name); 74 75 /* 76 * If the first name starts with this funny character, then we have 77 * a first value with an unspellable name. You cannot specify it. 78 * So, we don't list it either. 79 */ 80 if (**paz_names == 0x7F) { 81 paz_names++; 82 hidden = 1; 83 ct_down = --name_ct; 84 } 85 86 /* 87 * Figure out the maximum length of any name, plus the total length 88 * of all the names. 89 */ 90 { 91 tCC * const * paz = paz_names; 92 93 do { 94 size_t len = strlen( *(paz++) ) + 1; 95 if (len > max_len) 96 max_len = len; 97 ttl_len += len; 98 } while (--ct_down > 0); 99 100 ct_down = name_ct; 101 } 102 103 /* 104 * IF any one entry is about 1/2 line or longer, print one per line 105 */ 106 if (max_len > 35) { 107 do { 108 fprintf( option_usage_fp, " %s\n", *(paz_names++) ); 109 } while (--ct_down > 0); 110 } 111 112 /* 113 * ELSE IF they all fit on one line, then do so. 114 */ 115 else if (ttl_len < 76) { 116 fputc( ' ', option_usage_fp ); 117 do { 118 fputc( ' ', option_usage_fp ); 119 fputs( *(paz_names++), option_usage_fp ); 120 } while (--ct_down > 0); 121 fputc( '\n', option_usage_fp ); 122 } 123 124 /* 125 * Otherwise, columnize the output 126 */ 127 else { 128 size_t ent_no = 0; 129 char zFmt[16]; /* format for all-but-last entries on a line */ 130 131 sprintf( zFmt, "%%-%ds", (int)max_len ); 132 max_len = 78 / max_len; /* max_len is now max entries on a line */ 133 fputs( " ", option_usage_fp ); 134 135 /* 136 * Loop through all but the last entry 137 */ 138 ct_down = name_ct; 139 while (--ct_down > 0) { 140 if (++ent_no == max_len) { 141 /* 142 * Last entry on a line. Start next line, too. 143 */ 144 fprintf( option_usage_fp, "%s\n ", *(paz_names++) ); 145 ent_no = 0; 146 } 147 148 else 149 fprintf(option_usage_fp, zFmt, *(paz_names++) ); 150 } 151 fprintf(option_usage_fp, "%s\n", *paz_names); 152 } 153 154 if (pOpts > OPTPROC_EMIT_LIMIT) { 155 fprintf(option_usage_fp, zIntRange, hidden, name_ct - 1 + hidden); 156 157 (*(pOpts->pUsageProc))( pOpts, EXIT_FAILURE ); 158 /* NOTREACHED */ 159 } 160 161 162 if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_MEMBERSHIP) { 163 fprintf(option_usage_fp, zLowerBits, name_ct); 164 fputs(zSetMemberSettings, option_usage_fp); 165 } else { 166 fprintf(option_usage_fp, zIntRange, hidden, name_ct - 1 + hidden); 167 } 168} 169 170 171static uintptr_t 172findName( 173 tCC* pzName, 174 tOptions* pOpts, 175 tOptDesc* pOD, 176 tCC* const * paz_names, 177 unsigned int name_ct ) 178{ 179 /* 180 * Return the matching index as a pointer sized integer. 181 * The result gets stashed in a char* pointer. 182 */ 183 uintptr_t res = name_ct; 184 size_t len = strlen( (const char*)pzName ); 185 uintptr_t idx; 186 187 if (IS_DEC_DIGIT_CHAR(*pzName)) { 188 char * pz = (char *)(void *)(intptr_t)pzName; 189 unsigned long val = strtoul(pz, &pz, 0); 190 if ((*pz == NUL) && (val < name_ct)) 191 return (uintptr_t)val; 192 enumError(pOpts, pOD, paz_names, (int)name_ct); 193 return name_ct; 194 } 195 196 /* 197 * Look for an exact match, but remember any partial matches. 198 * Multiple partial matches means we have an ambiguous match. 199 */ 200 for (idx = 0; idx < name_ct; idx++) { 201 if (strncmp((const char*)paz_names[idx], (const char*)pzName, len) == 0) { 202 if (paz_names[idx][len] == NUL) 203 return idx; /* full match */ 204 205 res = (res != name_ct) ? (uintptr_t)~0 : idx; /* save partial match */ 206 } 207 } 208 209 if (res < name_ct) 210 return res; /* partial match */ 211 212 pz_enum_err_fmt = (res == name_ct) ? zNoKey : zAmbigKey; 213 option_usage_fp = stderr; 214 enumError(pOpts, pOD, paz_names, (int)name_ct); 215 return name_ct; 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))(OPTPROC_RETURN_VALNAME, &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 (uintptr_t)OPTPROC_EMIT_USAGE: 277 /* 278 * print the list of enumeration names. 279 */ 280 enumError(pOpts, pOD, paz_names, (int)name_ct); 281 break; 282 283 case (uintptr_t)OPTPROC_EMIT_SHELL: 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 (uintptr_t)OPTPROC_RETURN_VALNAME: 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 pOD->optArg.argString = 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 (uintptr_t)OPTPROC_EMIT_USAGE: 354 /* 355 * print the list of enumeration names. 356 */ 357 enumError(OPTPROC_EMIT_USAGE, pOD, paz_names, (int)name_ct ); 358 return; 359 360 case (uintptr_t)OPTPROC_EMIT_SHELL: 361 { 362 /* 363 * print the name string. 364 */ 365 size_t ix = 0; 366 uintptr_t bits = (uintptr_t)pOD->optCookie; 367 size_t len = 0; 368 369 bits &= ((uintptr_t)1 << (uintptr_t)name_ct) - (uintptr_t)1; 370 371 while (bits != 0) { 372 if (bits & 1) { 373 if (len++ > 0) fputs( " | ", stdout ); 374 fputs(paz_names[ix], stdout); 375 } 376 if (++ix >= name_ct) break; 377 bits >>= 1; 378 } 379 return; 380 } 381 382 case (uintptr_t)OPTPROC_RETURN_VALNAME: 383 { 384 char* pz; 385 uintptr_t bits = (uintptr_t)pOD->optCookie; 386 size_t ix = 0; 387 size_t len = 5; 388 389 bits &= ((uintptr_t)1 << (uintptr_t)name_ct) - (uintptr_t)1; 390 391 /* 392 * Replace the enumeration value with the name string. 393 * First, determine the needed length, then allocate and fill in. 394 */ 395 while (bits != 0) { 396 if (bits & 1) 397 len += strlen( paz_names[ix]) + 8; 398 if (++ix >= name_ct) break; 399 bits >>= 1; 400 } 401 402 pOD->optArg.argString = pz = AGALOC(len, "enum name"); 403 404 /* 405 * Start by clearing all the bits. We want to turn off any defaults 406 * because we will be restoring to current state, not adding to 407 * the default set of bits. 408 */ 409 strcpy( pz, "none" ); 410 pz += 4; 411 bits = (uintptr_t)pOD->optCookie; 412 bits &= ((uintptr_t)1 << (uintptr_t)name_ct) - (uintptr_t)1; 413 ix = 0; 414 415 while (bits != 0) { 416 if (bits & 1) { 417 strcpy( pz, " + " ); 418 strcpy( pz+3, paz_names[ix]); 419 pz += strlen( paz_names[ix]) + 3; 420 } 421 if (++ix >= name_ct) break; 422 bits >>= 1; 423 } 424 return; 425 } 426 427 default: 428 break; 429 } 430 431 if ((pOD->fOptState & OPTST_RESET) != 0) 432 return; 433 434 { 435 tCC* pzArg = pOD->optArg.argString; 436 uintptr_t res; 437 if ((pzArg == NULL) || (*pzArg == NUL)) { 438 pOD->optCookie = (void*)0; 439 return; 440 } 441 442 res = (uintptr_t)pOD->optCookie; 443 for (;;) { 444 tSCC zSpn[] = " ,|+\t\r\f\n"; 445 int iv, len; 446 447 pzArg += strspn( pzArg, zSpn ); 448 iv = (*pzArg == '!'); 449 if (iv) 450 pzArg += strspn( pzArg+1, zSpn ) + 1; 451 452 len = strcspn( pzArg, zSpn ); 453 if (len == 0) 454 break; 455 456 if ((len == 3) && (strncmp(pzArg, zAll, (size_t)3) == 0)) { 457 if (iv) 458 res = 0; 459 else res = ~0UL; 460 } 461 else if ((len == 4) && (strncmp(pzArg, zNone, (size_t)4) == 0)) { 462 if (! iv) 463 res = 0; 464 } 465 else do { 466 char* pz; 467 uintptr_t bit = strtoul( pzArg, &pz, 0 ); 468 469 if (pz != pzArg + len) { 470 char z[ AO_NAME_SIZE ]; 471 tCC* p; 472 unsigned int shift_ct; 473 474 if (*pz != NUL) { 475 if (len >= AO_NAME_LIMIT) 476 break; 477 strncpy( z, pzArg, (size_t)len ); 478 z[len] = NUL; 479 p = z; 480 } else { 481 p = pzArg; 482 } 483 484 shift_ct = findName(p, pOpts, pOD, paz_names, name_ct); 485 if (shift_ct >= name_ct) { 486 pOD->optCookie = (void*)0; 487 return; 488 } 489 bit = 1UL << shift_ct; 490 } 491 if (iv) 492 res &= ~bit; 493 else res |= bit; 494 } while (0); 495 496 if (pzArg[len] == NUL) 497 break; 498 pzArg += len + 1; 499 } 500 if (name_ct < (8 * sizeof( uintptr_t ))) { 501 res &= (1UL << name_ct) - 1UL; 502 } 503 504 pOD->optCookie = (void*)res; 505 } 506} 507 508/* 509 * Local Variables: 510 * mode: C 511 * c-file-style: "stroustrup" 512 * indent-tabs-mode: nil 513 * End: 514 * end of autoopts/enumeration.c */ 515