1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ 2 3/*@-type@*/ 4/** \ingroup popt 5 * \file popt/popthelp.c 6 */ 7 8/* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING 9 file accompanying popt source distributions, available from 10 ftp://ftp.rpm.org/pub/rpm/dist. */ 11 12#include "system.h" 13#include "poptint.h" 14 15/** 16 * @param con context 17 * @param key option(s) 18 */ 19static void displayArgs(poptContext con, 20 /*@unused@*/ UNUSED(enum poptCallbackReason foo), 21 struct poptOption * key, 22 /*@unused@*/ UNUSED(const char * arg), /*@unused@*/ UNUSED(void * data)) 23 /*@globals fileSystem@*/ 24 /*@modifies fileSystem@*/ 25{ 26 if (key->shortName == '?') 27 poptPrintHelp(con, stdout, 0); 28 else 29 poptPrintUsage(con, stdout, 0); 30 exit(0); 31} 32 33#ifdef NOTYET 34/*@unchecked@*/ 35static int show_option_defaults = 0; 36#endif 37 38/** 39 * Empty table marker to enable displaying popt alias/exec options. 40 */ 41/*@observer@*/ /*@unchecked@*/ 42struct poptOption poptAliasOptions[] = { 43 POPT_TABLEEND 44}; 45 46/** 47 * Auto help table options. 48 */ 49/*@-castfcnptr@*/ 50/*@observer@*/ /*@unchecked@*/ 51struct poptOption poptHelpOptions[] = { 52 { NULL, '\0', POPT_ARG_CALLBACK, (void *)&displayArgs, '\0', NULL, NULL }, 53 { "help", '?', 0, NULL, '?', N_("Show this help message"), NULL }, 54 { "usage", '\0', 0, NULL, 'u', N_("Display brief usage message"), NULL }, 55#ifdef NOTYET 56 { "defaults", '\0', POPT_ARG_NONE, &show_option_defaults, 0, 57 N_("Display option defaults in message"), NULL }, 58#endif 59 POPT_TABLEEND 60} ; 61/*@=castfcnptr@*/ 62 63/** 64 * @param table option(s) 65 */ 66/*@observer@*/ /*@null@*/ static const char * 67getTableTranslationDomain(/*@null@*/ const struct poptOption *table) 68 /*@*/ 69{ 70 const struct poptOption *opt; 71 72 if (table != NULL) 73 for (opt = table; opt->longName || opt->shortName || opt->arg; opt++) { 74 if (opt->argInfo == POPT_ARG_INTL_DOMAIN) 75 return opt->arg; 76 } 77 return NULL; 78} 79 80/** 81 * @param opt option(s) 82 * @param translation_domain translation domain 83 */ 84/*@observer@*/ /*@null@*/ static const char * 85getArgDescrip(const struct poptOption * opt, 86 /*@-paramuse@*/ /* FIX: wazzup? */ 87 /*@null@*/ UNUSED(const char * translation_domain)) 88 /*@=paramuse@*/ 89 /*@*/ 90{ 91 if (!(opt->argInfo & POPT_ARG_MASK)) return NULL; 92 93 if (opt == (poptHelpOptions + 1) || opt == (poptHelpOptions + 2)) 94 if (opt->argDescrip) return POPT_(opt->argDescrip); 95 96 if (opt->argDescrip) return D_(translation_domain, opt->argDescrip); 97 98 switch (opt->argInfo & POPT_ARG_MASK) { 99 case POPT_ARG_NONE: return POPT_("NONE"); 100 case POPT_ARG_VAL: return POPT_("VAL"); 101 case POPT_ARG_INT: return POPT_("INT"); 102 case POPT_ARG_LONG: return POPT_("LONG"); 103 case POPT_ARG_STRING: return POPT_("STRING"); 104 case POPT_ARG_FLOAT: return POPT_("FLOAT"); 105 case POPT_ARG_DOUBLE: return POPT_("DOUBLE"); 106 default: return POPT_("ARG"); 107 } 108} 109 110/** 111 * @param opt option(s) 112 * @param translation_domain translation domain 113 */ 114static /*@only@*/ /*@null@*/ char * 115singleOptionDefaultValue(int lineLength, 116 const struct poptOption * opt, 117 /*@-paramuse@*/ /* FIX: i18n macros disable with lclint */ 118 /*@null@*/ UNUSED(const char * translation_domain)) 119 /*@=paramuse@*/ 120 /*@*/ 121{ 122 const char * defstr = D_(translation_domain, "default"); 123 char * le = malloc(4*lineLength + 1); 124 char * l = le; 125 126 if (le == NULL) return NULL; /* XXX can't happen */ 127 *le = '\0'; 128 *le++ = '('; 129 strcpy(le, defstr); le += strlen(le); 130 *le++ = ':'; 131 *le++ = ' '; 132 if (opt->arg) /* XXX programmer error */ 133 switch (opt->argInfo & POPT_ARG_MASK) { 134 case POPT_ARG_VAL: 135 case POPT_ARG_INT: 136 { long aLong = *((int *)opt->arg); 137 sprintf(le, "%ld", aLong); 138 le += strlen(le); 139 } break; 140 case POPT_ARG_LONG: 141 { long aLong = *((long *)opt->arg); 142 sprintf(le, "%ld", aLong); 143 le += strlen(le); 144 } break; 145 case POPT_ARG_FLOAT: 146 { double aDouble = *((float *)opt->arg); 147 sprintf(le, "%g", aDouble); 148 le += strlen(le); 149 } break; 150 case POPT_ARG_DOUBLE: 151 { double aDouble = *((double *)opt->arg); 152 sprintf(le, "%g", aDouble); 153 le += strlen(le); 154 } break; 155 case POPT_ARG_STRING: 156 { const char * s = *(const char **)opt->arg; 157 if (s == NULL) { 158 strcpy(le, "null"); le += strlen(le); 159 } else { 160 size_t slen = 4*lineLength - (le - l) - sizeof("\"...\")"); 161 *le++ = '"'; 162 strncpy(le, s, slen); le[slen] = '\0'; le += strlen(le); 163 if (slen < strlen(s)) { 164 strcpy(le, "..."); le += strlen(le); 165 } 166 *le++ = '"'; 167 } 168 } break; 169 case POPT_ARG_NONE: 170 default: 171 l = _free(l); 172 return NULL; 173 /*@notreached@*/ break; 174 } 175 *le++ = ')'; 176 *le = '\0'; 177 178 return l; 179} 180 181/** 182 * @param fp output file handle 183 * @param opt option(s) 184 * @param translation_domain translation domain 185 */ 186static void singleOptionHelp(FILE * fp, int maxLeftCol, 187 const struct poptOption * opt, 188 /*@null@*/ const char * translation_domain) 189 /*@globals fileSystem @*/ 190 /*@modifies *fp, fileSystem @*/ 191{ 192 int indentLength = maxLeftCol + 5; 193 int lineLength = 79 - indentLength; 194 const unsigned char * help = D_(translation_domain, opt->descrip); 195 const char * argDescrip = getArgDescrip(opt, translation_domain); 196 int helpLength; 197 unsigned char * defs = NULL; 198 unsigned char * left; 199 int nb = maxLeftCol + 1; 200 201 /* Make sure there's more than enough room in target buffer. */ 202 if (opt->longName) nb += strlen(opt->longName); 203 if (argDescrip) nb += strlen(argDescrip); 204 205 left = malloc(nb); 206 if (left == NULL) return; /* XXX can't happen */ 207 left[0] = '\0'; 208 left[maxLeftCol] = '\0'; 209 210 if (opt->longName && opt->shortName) 211 sprintf(left, "-%c, %s%s", opt->shortName, 212 ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"), 213 opt->longName); 214 else if (opt->shortName != '\0') 215 sprintf(left, "-%c", opt->shortName); 216 else if (opt->longName) 217 sprintf(left, "%s%s", 218 ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"), 219 opt->longName); 220 if (!*left) goto out; 221 if (argDescrip) { 222 char * le = left + strlen(left); 223 224 if (opt->argInfo & POPT_ARGFLAG_OPTIONAL) 225 *le++ = '['; 226 227 /* Choose type of output */ 228 /*@-branchstate@*/ 229 if (opt->argInfo & POPT_ARGFLAG_SHOW_DEFAULT) { 230 defs = singleOptionDefaultValue(lineLength, opt, translation_domain); 231 if (defs) { 232 char * t = malloc((help ? strlen(help) : 0) + 233 strlen(defs) + sizeof(" ")); 234 if (t) { 235 char * te = t; 236 *te = '\0'; 237 if (help) { 238 strcpy(te, help); te += strlen(te); 239 } 240 *te++ = ' '; 241 strcpy(te, defs); 242 defs = _free(defs); 243 } 244 defs = t; 245 } 246 } 247 /*@=branchstate@*/ 248 249 if (opt->argDescrip == NULL) { 250 switch (opt->argInfo & POPT_ARG_MASK) { 251 case POPT_ARG_NONE: 252 break; 253 case POPT_ARG_VAL: 254 { long aLong = opt->val; 255 int ops = (opt->argInfo & POPT_ARGFLAG_LOGICALOPS); 256 int negate = (opt->argInfo & POPT_ARGFLAG_NOT); 257 258 /* Don't bother displaying typical values */ 259 if (!ops && (aLong == 0L || aLong == 1L || aLong == -1L)) 260 break; 261 *le++ = '['; 262 switch (ops) { 263 case POPT_ARGFLAG_OR: 264 *le++ = '|'; 265 /*@innerbreak@*/ break; 266 case POPT_ARGFLAG_AND: 267 *le++ = '&'; 268 /*@innerbreak@*/ break; 269 case POPT_ARGFLAG_XOR: 270 *le++ = '^'; 271 /*@innerbreak@*/ break; 272 default: 273 /*@innerbreak@*/ break; 274 } 275 *le++ = '='; 276 if (negate) *le++ = '~'; 277 /*@-formatconst@*/ 278 sprintf(le, (ops ? "0x%lx" : "%ld"), aLong); 279 le += strlen(le); 280 /*@=formatconst@*/ 281 *le++ = ']'; 282 } break; 283 case POPT_ARG_INT: 284 case POPT_ARG_LONG: 285 case POPT_ARG_FLOAT: 286 case POPT_ARG_DOUBLE: 287 case POPT_ARG_STRING: 288 *le++ = '='; 289 strcpy(le, argDescrip); le += strlen(le); 290 break; 291 default: 292 break; 293 } 294 } else { 295 *le++ = '='; 296 strcpy(le, argDescrip); le += strlen(le); 297 } 298 if (opt->argInfo & POPT_ARGFLAG_OPTIONAL) 299 *le++ = ']'; 300 *le = '\0'; 301 } 302 303 if (help) 304 fprintf(fp," %-*s ", maxLeftCol, left); 305 else { 306 fprintf(fp," %s\n", left); 307 goto out; 308 } 309 310 left = _free(left); 311 if (defs) { 312 help = defs; defs = NULL; 313 } 314 315 helpLength = strlen(help); 316 while (helpLength > lineLength) { 317 const unsigned char * ch; 318 char format[10]; 319 320 ch = help + lineLength - 1; 321 while (ch > help && !isspace(*ch)) ch--; 322 if (ch == help) break; /* give up */ 323 while (ch > (help + 1) && isspace(*ch)) ch--; 324 ch++; 325 326 sprintf(format, "%%.%ds\n%%%ds", (int) (ch - help), indentLength); 327 /*@-formatconst@*/ 328 fprintf(fp, format, help, " "); 329 /*@=formatconst@*/ 330 help = ch; 331 while (isspace(*help) && *help) help++; 332 helpLength = strlen(help); 333 } 334 335 if (helpLength) fprintf(fp, "%s\n", help); 336 337out: 338 /*@-dependenttrans@*/ 339 defs = _free(defs); 340 /*@=dependenttrans@*/ 341 left = _free(left); 342} 343 344/** 345 * @param opt option(s) 346 * @param translation_domain translation domain 347 */ 348static int maxArgWidth(const struct poptOption * opt, 349 /*@null@*/ const char * translation_domain) 350 /*@*/ 351{ 352 int max = 0; 353 int len = 0; 354 const char * s; 355 356 if (opt != NULL) 357 while (opt->longName || opt->shortName || opt->arg) { 358 if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) { 359 if (opt->arg) /* XXX program error */ 360 len = maxArgWidth(opt->arg, translation_domain); 361 if (len > max) max = len; 362 } else if (!(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) { 363 len = sizeof(" ")-1; 364 if (opt->shortName != '\0') len += sizeof("-X")-1; 365 if (opt->shortName != '\0' && opt->longName) len += sizeof(", ")-1; 366 if (opt->longName) { 367 len += ((opt->argInfo & POPT_ARGFLAG_ONEDASH) 368 ? sizeof("-")-1 : sizeof("--")-1); 369 len += strlen(opt->longName); 370 } 371 372 s = getArgDescrip(opt, translation_domain); 373 if (s) 374 len += sizeof("=")-1 + strlen(s); 375 if (opt->argInfo & POPT_ARGFLAG_OPTIONAL) len += sizeof("[]")-1; 376 if (len > max) max = len; 377 } 378 379 opt++; 380 } 381 382 return max; 383} 384 385/** 386 * Display popt alias and exec help. 387 * @param fp output file handle 388 * @param items alias/exec array 389 * @param nitems no. of alias/exec entries 390 * @param translation_domain translation domain 391 */ 392static void itemHelp(FILE * fp, 393 /*@null@*/ poptItem items, int nitems, int left, 394 /*@null@*/ const char * translation_domain) 395 /*@globals fileSystem @*/ 396 /*@modifies *fp, fileSystem @*/ 397{ 398 poptItem item; 399 int i; 400 401 if (items != NULL) 402 for (i = 0, item = items; i < nitems; i++, item++) { 403 const struct poptOption * opt; 404 opt = &item->option; 405 if ((opt->longName || opt->shortName) && 406 !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) 407 singleOptionHelp(fp, left, opt, translation_domain); 408 } 409} 410 411/** 412 * @param fp output file handle 413 * @param table option(s) 414 * @param translation_domain translation domain 415 */ 416static void singleTableHelp(poptContext con, FILE * fp, 417 /*@null@*/ const struct poptOption * table, int left, 418 /*@null@*/ const char * translation_domain) 419 /*@globals fileSystem @*/ 420 /*@modifies *fp, fileSystem @*/ 421{ 422 const struct poptOption * opt; 423 const char *sub_transdom; 424 425 if (table == poptAliasOptions) { 426 itemHelp(fp, con->aliases, con->numAliases, left, NULL); 427 itemHelp(fp, con->execs, con->numExecs, left, NULL); 428 return; 429 } 430 431 if (table != NULL) 432 for (opt = table; (opt->longName || opt->shortName || opt->arg); opt++) { 433 if ((opt->longName || opt->shortName) && 434 !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) 435 singleOptionHelp(fp, left, opt, translation_domain); 436 } 437 438 if (table != NULL) 439 for (opt = table; (opt->longName || opt->shortName || opt->arg); opt++) { 440 if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_INCLUDE_TABLE) 441 continue; 442 sub_transdom = getTableTranslationDomain(opt->arg); 443 if (sub_transdom == NULL) 444 sub_transdom = translation_domain; 445 446 if (opt->descrip) 447 fprintf(fp, "\n%s\n", D_(sub_transdom, opt->descrip)); 448 449 singleTableHelp(con, fp, opt->arg, left, sub_transdom); 450 } 451} 452 453/** 454 * @param con context 455 * @param fp output file handle 456 */ 457static int showHelpIntro(poptContext con, FILE * fp) 458 /*@globals fileSystem @*/ 459 /*@modifies *fp, fileSystem @*/ 460{ 461 int len = 6; 462 const char * fn; 463 464 fprintf(fp, POPT_("Usage:")); 465 if (!(con->flags & POPT_CONTEXT_KEEP_FIRST)) { 466 /*@-nullderef@*/ /* LCL: wazzup? */ 467 fn = con->optionStack->argv[0]; 468 /*@=nullderef@*/ 469 if (fn == NULL) return len; 470 if (strchr(fn, '/')) fn = strrchr(fn, '/') + 1; 471 fprintf(fp, " %s", fn); 472 len += strlen(fn) + 1; 473 } 474 475 return len; 476} 477 478void poptPrintHelp(poptContext con, FILE * fp, /*@unused@*/ UNUSED(int flags)) 479{ 480 int leftColWidth; 481 482 (void) showHelpIntro(con, fp); 483 if (con->otherHelp) 484 fprintf(fp, " %s\n", con->otherHelp); 485 else 486 fprintf(fp, " %s\n", POPT_("[OPTION...]")); 487 488 leftColWidth = maxArgWidth(con->options, NULL); 489 singleTableHelp(con, fp, con->options, leftColWidth, NULL); 490} 491 492/** 493 * @param fp output file handle 494 * @param opt option(s) 495 * @param translation_domain translation domain 496 */ 497static int singleOptionUsage(FILE * fp, int cursor, 498 const struct poptOption * opt, 499 /*@null@*/ const char *translation_domain) 500 /*@globals fileSystem @*/ 501 /*@modifies *fp, fileSystem @*/ 502{ 503 int len = 3; 504 char shortStr[2] = { '\0', '\0' }; 505 const char * item = shortStr; 506 const char * argDescrip = getArgDescrip(opt, translation_domain); 507 508 if (opt->shortName!= '\0' ) { 509 if (!(opt->argInfo & POPT_ARG_MASK)) 510 return cursor; /* we did these already */ 511 len++; 512 shortStr[0] = opt->shortName; 513 shortStr[1] = '\0'; 514 } else if (opt->longName) { 515 len += 1 + strlen(opt->longName); 516 item = opt->longName; 517 } 518 519 if (len == 3) return cursor; 520 521 if (argDescrip) 522 len += strlen(argDescrip) + 1; 523 524 if ((cursor + len) > 79) { 525 fprintf(fp, "\n "); 526 cursor = 7; 527 } 528 529 fprintf(fp, " [-%s%s%s%s]", 530 ((opt->shortName || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) ? "" : "-"), 531 item, 532 (argDescrip ? (opt->shortName != '\0' ? " " : "=") : ""), 533 (argDescrip ? argDescrip : "")); 534 535 return cursor + len + 1; 536} 537 538/** 539 * Display popt alias and exec usage. 540 * @param fp output file handle 541 * @param item alias/exec array 542 * @param nitems no. of ara/exec entries 543 * @param translation_domain translation domain 544 */ 545static int itemUsage(FILE * fp, int cursor, poptItem item, int nitems, 546 /*@null@*/ const char * translation_domain) 547 /*@globals fileSystem @*/ 548 /*@modifies *fp, fileSystem @*/ 549{ 550 int i; 551 552 /*@-branchstate@*/ /* FIX: W2DO? */ 553 if (item != NULL) 554 for (i = 0; i < nitems; i++, item++) { 555 const struct poptOption * opt; 556 opt = &item->option; 557 if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN) { 558 translation_domain = (const char *)opt->arg; 559 } else if ((opt->longName || opt->shortName) && 560 !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) { 561 cursor = singleOptionUsage(fp, cursor, opt, translation_domain); 562 } 563 } 564 /*@=branchstate@*/ 565 566 return cursor; 567} 568 569/** 570 * @param fp output file handle 571 * @param opt option(s) 572 * @param translation_domain translation domain 573 */ 574static int singleTableUsage(poptContext con, FILE * fp, 575 int cursor, const struct poptOption * opt, 576 /*@null@*/ const char * translation_domain) 577 /*@globals fileSystem @*/ 578 /*@modifies *fp, fileSystem @*/ 579{ 580 /*@-branchstate@*/ /* FIX: W2DO? */ 581 if (opt != NULL) 582 for (; (opt->longName || opt->shortName || opt->arg) ; opt++) { 583 if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN) { 584 translation_domain = (const char *)opt->arg; 585 } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) { 586 if (opt->arg) /* XXX program error */ 587 cursor = singleTableUsage(con, fp, cursor, opt->arg, 588 translation_domain); 589 } else if ((opt->longName || opt->shortName) && 590 !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) { 591 cursor = singleOptionUsage(fp, cursor, opt, translation_domain); 592 } 593 } 594 /*@=branchstate@*/ 595 596 return cursor; 597} 598 599/** 600 * Return concatenated short options for display. 601 * @param opt option(s) 602 * @param fp output file handle 603 * @retval str concatenation of short options 604 * @return length of display string 605 */ 606static int showShortOptions(const struct poptOption * opt, FILE * fp, 607 /*@null@*/ char * str) 608 /*@globals fileSystem @*/ 609 /*@modifies *str, *fp, fileSystem @*/ 610{ 611 char * s = alloca(300); /* larger then the ascii set */ 612 613 s[0] = '\0'; 614 /*@-branchstate@*/ /* FIX: W2DO? */ 615 if (str == NULL) { 616 memset(s, 0, sizeof(s)); 617 str = s; 618 } 619 /*@=branchstate@*/ 620 621 if (opt != NULL) 622 for (; (opt->longName || opt->shortName || opt->arg); opt++) { 623 if (opt->shortName && !(opt->argInfo & POPT_ARG_MASK)) 624 str[strlen(str)] = opt->shortName; 625 else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) 626 if (opt->arg) /* XXX program error */ 627 (void) showShortOptions(opt->arg, fp, str); 628 } 629 630 if (s != str || *s != '\0') 631 return 0; 632 633 fprintf(fp, " [-%s]", s); 634 return strlen(s) + 4; 635} 636 637void poptPrintUsage(poptContext con, FILE * fp, /*@unused@*/ UNUSED(int flags)) 638{ 639 int cursor; 640 641 cursor = showHelpIntro(con, fp); 642 cursor += showShortOptions(con->options, fp, NULL); 643 (void) singleTableUsage(con, fp, cursor, con->options, NULL); 644 (void) itemUsage(fp, cursor, con->aliases, con->numAliases, NULL); 645 (void) itemUsage(fp, cursor, con->execs, con->numExecs, NULL); 646 647 if (con->otherHelp) { 648 cursor += strlen(con->otherHelp) + 1; 649 if (cursor > 79) fprintf(fp, "\n "); 650 fprintf(fp, " %s", con->otherHelp); 651 } 652 653 fprintf(fp, "\n"); 654} 655 656void poptSetOtherOptionHelp(poptContext con, const char * text) 657{ 658 con->otherHelp = _free(con->otherHelp); 659 con->otherHelp = xstrdup(text); 660} 661/*@=type@*/ 662