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