1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
5 * \file popt/popthelp.c
8 /* (C) 1998-2002 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. */
19 * @param key option(s)
21 * @param data (unused)
23 static void displayArgs(poptContext con
,
24 /*@unused@*/ enum poptCallbackReason foo
,
25 struct poptOption
* key
,
26 /*@unused@*/ const char * arg
, /*@unused@*/ void * data
)
27 /*@globals fileSystem@*/
28 /*@modifies fileSystem@*/
30 if (key
->shortName
== '?')
31 poptPrintHelp(con
, stdout
, 0);
33 poptPrintUsage(con
, stdout
, 0);
39 static int show_option_defaults
= 0;
43 * Empty table marker to enable displaying popt alias/exec options.
45 /*@observer@*/ /*@unchecked@*/
46 struct poptOption poptAliasOptions
[] = {
51 * Auto help table options.
54 /*@observer@*/ /*@unchecked@*/
55 struct poptOption poptHelpOptions
[] = {
56 { NULL
, '\0', POPT_ARG_CALLBACK
, (void *)&displayArgs
, '\0', NULL
, NULL
},
57 { "help", '?', 0, NULL
, '?', N_("Show this help message"), NULL
},
58 { "usage", '\0', 0, NULL
, 'u', N_("Display brief usage message"), NULL
},
60 { "defaults", '\0', POPT_ARG_NONE
, &show_option_defaults
, 0,
61 N_("Display option defaults in message"), NULL
},
68 * @param table option(s)
70 /*@observer@*/ /*@null@*/ static const char *
71 getTableTranslationDomain(/*@null@*/ const struct poptOption
*table
)
74 const struct poptOption
*opt
;
77 for (opt
= table
; opt
->longName
|| opt
->shortName
|| opt
->arg
; opt
++) {
78 if (opt
->argInfo
== POPT_ARG_INTL_DOMAIN
)
85 * @param opt option(s)
86 * @param translation_domain translation domain
88 /*@observer@*/ /*@null@*/ static const char *
89 getArgDescrip(const struct poptOption
* opt
,
90 /*@-paramuse@*/ /* FIX: i18n macros disabled with lclint */
91 /*@null@*/ const char * translation_domain
)
95 if (!(opt
->argInfo
& POPT_ARG_MASK
)) return NULL
;
97 if (opt
== (poptHelpOptions
+ 1) || opt
== (poptHelpOptions
+ 2))
98 if (opt
->argDescrip
) return POPT_(opt
->argDescrip
);
100 if (opt
->argDescrip
) return D_(translation_domain
, opt
->argDescrip
);
102 switch (opt
->argInfo
& POPT_ARG_MASK
) {
103 case POPT_ARG_NONE
: return POPT_("NONE");
105 case POPT_ARG_VAL
: return POPT_("VAL");
107 case POPT_ARG_VAL
: return NULL
;
109 case POPT_ARG_INT
: return POPT_("INT");
110 case POPT_ARG_LONG
: return POPT_("LONG");
111 case POPT_ARG_STRING
: return POPT_("STRING");
112 case POPT_ARG_FLOAT
: return POPT_("FLOAT");
113 case POPT_ARG_DOUBLE
: return POPT_("DOUBLE");
114 default: return POPT_("ARG");
119 * Display default value for an option.
121 * @param opt option(s)
122 * @param translation_domain translation domain
125 static /*@only@*/ /*@null@*/ char *
126 singleOptionDefaultValue(int lineLength
,
127 const struct poptOption
* opt
,
128 /*@-paramuse@*/ /* FIX: i18n macros disabled with lclint */
129 /*@null@*/ const char * translation_domain
)
133 const char * defstr
= D_(translation_domain
, "default");
134 char * le
= malloc(4*lineLength
+ 1);
137 if (le
== NULL
) return NULL
; /* XXX can't happen */
141 strcpy(le
, defstr
); le
+= strlen(le
);
144 if (opt
->arg
) /* XXX programmer error */
145 switch (opt
->argInfo
& POPT_ARG_MASK
) {
148 { long aLong
= *((int *)opt
->arg
);
149 le
+= sprintf(le
, "%ld", aLong
);
152 { long aLong
= *((long *)opt
->arg
);
153 le
+= sprintf(le
, "%ld", aLong
);
156 { double aDouble
= *((float *)opt
->arg
);
157 le
+= sprintf(le
, "%g", aDouble
);
159 case POPT_ARG_DOUBLE
:
160 { double aDouble
= *((double *)opt
->arg
);
161 le
+= sprintf(le
, "%g", aDouble
);
163 case POPT_ARG_STRING
:
164 { const char * s
= *(const char **)opt
->arg
;
166 strcpy(le
, "null"); le
+= strlen(le
);
168 size_t slen
= 4*lineLength
- (le
- l
) - sizeof("\"...\")");
170 strncpy(le
, s
, slen
); le
[slen
] = '\0'; le
+= strlen(le
);
171 if (slen
< strlen(s
)) {
172 strcpy(le
, "..."); le
+= strlen(le
);
181 /*@notreached@*/ break;
191 * Display help text for an option.
192 * @param fp output file handle
194 * @param opt option(s)
195 * @param translation_domain translation domain
197 static void singleOptionHelp(FILE * fp
, int maxLeftCol
,
198 const struct poptOption
* opt
,
199 /*@null@*/ const char * translation_domain
)
200 /*@globals fileSystem @*/
201 /*@modifies *fp, fileSystem @*/
203 int indentLength
= maxLeftCol
+ 5;
204 int lineLength
= 79 - indentLength
;
205 const char * help
= D_(translation_domain
, opt
->descrip
);
206 const char * argDescrip
= getArgDescrip(opt
, translation_domain
);
210 int nb
= maxLeftCol
+ 1;
212 /* Make sure there's more than enough room in target buffer. */
213 if (opt
->longName
) nb
+= strlen(opt
->longName
);
214 if (argDescrip
) nb
+= strlen(argDescrip
);
218 if (left
== NULL
) return; /* XXX can't happen */
220 left
[maxLeftCol
] = '\0';
222 if (opt
->longName
&& opt
->shortName
)
223 sprintf(left
, "-%c, %s%s", opt
->shortName
,
224 ((opt
->argInfo
& POPT_ARGFLAG_ONEDASH
) ? "-" : "--"),
226 else if (opt
->shortName
!= '\0')
227 sprintf(left
, "-%c", opt
->shortName
);
228 else if (opt
->longName
)
229 sprintf(left
, "%s%s",
230 ((opt
->argInfo
& POPT_ARGFLAG_ONEDASH
) ? "-" : "--"),
232 if (!*left
) goto out
;
235 char * le
= left
+ strlen(left
);
237 if (opt
->argInfo
& POPT_ARGFLAG_OPTIONAL
)
240 /* Choose type of output */
242 if (opt
->argInfo
& POPT_ARGFLAG_SHOW_DEFAULT
) {
243 defs
= singleOptionDefaultValue(lineLength
, opt
, translation_domain
);
245 char * t
= malloc((help
? strlen(help
) : 0) +
246 strlen(defs
) + sizeof(" "));
251 strcpy(te
, help
); te
+= strlen(te
);
262 if (opt
->argDescrip
== NULL
) {
263 switch (opt
->argInfo
& POPT_ARG_MASK
) {
267 #ifdef NOTNOW /* XXX pug ugly nerdy output */
268 { long aLong
= opt
->val
;
269 int ops
= (opt
->argInfo
& POPT_ARGFLAG_LOGICALOPS
);
270 int negate
= (opt
->argInfo
& POPT_ARGFLAG_NOT
);
272 /* Don't bother displaying typical values */
273 if (!ops
&& (aLong
== 0L || aLong
== 1L || aLong
== -1L))
277 case POPT_ARGFLAG_OR
:
279 /*@innerbreak@*/ break;
280 case POPT_ARGFLAG_AND
:
282 /*@innerbreak@*/ break;
283 case POPT_ARGFLAG_XOR
:
285 /*@innerbreak@*/ break;
287 /*@innerbreak@*/ break;
290 if (negate
) *le
++ = '~';
292 le
+= sprintf(le
, (ops
? "0x%lx" : "%ld"), aLong
);
301 case POPT_ARG_DOUBLE
:
302 case POPT_ARG_STRING
:
304 strcpy(le
, argDescrip
); le
+= strlen(le
);
311 strcpy(le
, argDescrip
); le
+= strlen(le
);
313 if (opt
->argInfo
& POPT_ARGFLAG_OPTIONAL
)
320 fprintf(fp
," %-*s ", maxLeftCol
, left
);
322 fprintf(fp
," %s\n", left
);
328 help
= defs
; defs
= NULL
;
331 helpLength
= strlen(help
);
333 while (helpLength
> lineLength
) {
337 ch
= help
+ lineLength
- 1;
338 while (ch
> help
&& !isspace(*ch
)) ch
--;
339 if (ch
== help
) break; /* give up */
340 while (ch
> (help
+ 1) && isspace(*ch
)) ch
--;
343 sprintf(format
, "%%.%ds\n%%%ds", (int) (ch
- help
), indentLength
);
345 fprintf(fp
, format
, help
, " ");
348 while (isspace(*help
) && *help
) help
++;
349 helpLength
= strlen(help
);
353 if (helpLength
) fprintf(fp
, "%s\n", help
);
356 /*@-dependenttrans@*/
358 /*@=dependenttrans@*/
363 * @param opt option(s)
364 * @param translation_domain translation domain
366 static int maxArgWidth(const struct poptOption
* opt
,
367 /*@null@*/ const char * translation_domain
)
375 while (opt
->longName
|| opt
->shortName
|| opt
->arg
) {
376 if ((opt
->argInfo
& POPT_ARG_MASK
) == POPT_ARG_INCLUDE_TABLE
) {
377 if (opt
->arg
) /* XXX program error */
378 len
= maxArgWidth(opt
->arg
, translation_domain
);
379 if (len
> max
) max
= len
;
380 } else if (!(opt
->argInfo
& POPT_ARGFLAG_DOC_HIDDEN
)) {
382 if (opt
->shortName
!= '\0') len
+= sizeof("-X")-1;
383 if (opt
->shortName
!= '\0' && opt
->longName
) len
+= sizeof(", ")-1;
385 len
+= ((opt
->argInfo
& POPT_ARGFLAG_ONEDASH
)
386 ? sizeof("-")-1 : sizeof("--")-1);
387 len
+= strlen(opt
->longName
);
390 s
= getArgDescrip(opt
, translation_domain
);
392 len
+= sizeof("=")-1 + strlen(s
);
393 if (opt
->argInfo
& POPT_ARGFLAG_OPTIONAL
) len
+= sizeof("[]")-1;
394 if (len
> max
) max
= len
;
404 * Display popt alias and exec help.
405 * @param fp output file handle
406 * @param items alias/exec array
407 * @param nitems no. of alias/exec entries
409 * @param translation_domain translation domain
411 static void itemHelp(FILE * fp
,
412 /*@null@*/ poptItem items
, int nitems
, int left
,
413 /*@null@*/ const char * translation_domain
)
414 /*@globals fileSystem @*/
415 /*@modifies *fp, fileSystem @*/
421 for (i
= 0, item
= items
; i
< nitems
; i
++, item
++) {
422 const struct poptOption
* opt
;
424 if ((opt
->longName
|| opt
->shortName
) &&
425 !(opt
->argInfo
& POPT_ARGFLAG_DOC_HIDDEN
))
426 singleOptionHelp(fp
, left
, opt
, translation_domain
);
431 * Display help text for a table of options.
433 * @param fp output file handle
434 * @param table option(s)
436 * @param translation_domain translation domain
438 static void singleTableHelp(poptContext con
, FILE * fp
,
439 /*@null@*/ const struct poptOption
* table
, int left
,
440 /*@null@*/ const char * translation_domain
)
441 /*@globals fileSystem @*/
442 /*@modifies *fp, fileSystem @*/
444 const struct poptOption
* opt
;
445 const char *sub_transdom
;
447 if (table
== poptAliasOptions
) {
448 itemHelp(fp
, con
->aliases
, con
->numAliases
, left
, NULL
);
449 itemHelp(fp
, con
->execs
, con
->numExecs
, left
, NULL
);
454 for (opt
= table
; (opt
->longName
|| opt
->shortName
|| opt
->arg
); opt
++) {
455 if ((opt
->longName
|| opt
->shortName
) &&
456 !(opt
->argInfo
& POPT_ARGFLAG_DOC_HIDDEN
))
457 singleOptionHelp(fp
, left
, opt
, translation_domain
);
461 for (opt
= table
; (opt
->longName
|| opt
->shortName
|| opt
->arg
); opt
++) {
462 if ((opt
->argInfo
& POPT_ARG_MASK
) != POPT_ARG_INCLUDE_TABLE
)
464 sub_transdom
= getTableTranslationDomain(opt
->arg
);
465 if (sub_transdom
== NULL
)
466 sub_transdom
= translation_domain
;
469 fprintf(fp
, "\n%s\n", D_(sub_transdom
, opt
->descrip
));
471 singleTableHelp(con
, fp
, opt
->arg
, left
, sub_transdom
);
477 * @param fp output file handle
479 static int showHelpIntro(poptContext con
, FILE * fp
)
480 /*@globals fileSystem @*/
481 /*@modifies *fp, fileSystem @*/
486 fprintf(fp
, POPT_("Usage:"));
487 if (!(con
->flags
& POPT_CONTEXT_KEEP_FIRST
)) {
489 /*@-nullderef@*/ /* LCL: wazzup? */
490 fn
= con
->optionStack
->argv
[0];
493 if (fn
== NULL
) return len
;
494 if (strchr(fn
, '/')) fn
= strrchr(fn
, '/') + 1;
495 fprintf(fp
, " %s", fn
);
496 len
+= strlen(fn
) + 1;
502 void poptPrintHelp(poptContext con
, FILE * fp
, /*@unused@*/ int flags
)
506 (void) showHelpIntro(con
, fp
);
508 fprintf(fp
, " %s\n", con
->otherHelp
);
510 fprintf(fp
, " %s\n", POPT_("[OPTION...]"));
512 leftColWidth
= maxArgWidth(con
->options
, NULL
);
513 singleTableHelp(con
, fp
, con
->options
, leftColWidth
, NULL
);
517 * @param fp output file handle
519 * @param opt option(s)
520 * @param translation_domain translation domain
522 static int singleOptionUsage(FILE * fp
, int cursor
,
523 const struct poptOption
* opt
,
524 /*@null@*/ const char *translation_domain
)
525 /*@globals fileSystem @*/
526 /*@modifies *fp, fileSystem @*/
529 char shortStr
[2] = { '\0', '\0' };
530 const char * item
= shortStr
;
531 const char * argDescrip
= getArgDescrip(opt
, translation_domain
);
533 if (opt
->shortName
!= '\0' && opt
->longName
!= NULL
) {
535 if (!(opt
->argInfo
& POPT_ARGFLAG_ONEDASH
)) len
++;
536 len
+= strlen(opt
->longName
);
537 } else if (opt
->shortName
!= '\0') {
539 shortStr
[0] = opt
->shortName
;
541 } else if (opt
->longName
) {
542 len
+= strlen(opt
->longName
);
543 if (!(opt
->argInfo
& POPT_ARGFLAG_ONEDASH
)) len
++;
544 item
= opt
->longName
;
547 if (len
== 4) return cursor
;
550 len
+= strlen(argDescrip
) + 1;
552 if ((cursor
+ len
) > 79) {
557 if (opt
->longName
&& opt
->shortName
) {
558 fprintf(fp
, " [-%c|-%s%s%s%s]",
559 opt
->shortName
, ((opt
->argInfo
& POPT_ARGFLAG_ONEDASH
) ? "" : "-"),
561 (argDescrip
? " " : ""),
562 (argDescrip
? argDescrip
: ""));
564 fprintf(fp
, " [-%s%s%s%s]",
565 ((opt
->shortName
|| (opt
->argInfo
& POPT_ARGFLAG_ONEDASH
)) ? "" : "-"),
567 (argDescrip
? (opt
->shortName
!= '\0' ? " " : "=") : ""),
568 (argDescrip
? argDescrip
: ""));
571 return cursor
+ len
+ 1;
575 * Display popt alias and exec usage.
576 * @param fp output file handle
578 * @param item alias/exec array
579 * @param nitems no. of ara/exec entries
580 * @param translation_domain translation domain
582 static int itemUsage(FILE * fp
, int cursor
, poptItem item
, int nitems
,
583 /*@null@*/ const char * translation_domain
)
584 /*@globals fileSystem @*/
585 /*@modifies *fp, fileSystem @*/
589 /*@-branchstate@*/ /* FIX: W2DO? */
591 for (i
= 0; i
< nitems
; i
++, item
++) {
592 const struct poptOption
* opt
;
594 if ((opt
->argInfo
& POPT_ARG_MASK
) == POPT_ARG_INTL_DOMAIN
) {
595 translation_domain
= (const char *)opt
->arg
;
596 } else if ((opt
->longName
|| opt
->shortName
) &&
597 !(opt
->argInfo
& POPT_ARGFLAG_DOC_HIDDEN
)) {
598 cursor
= singleOptionUsage(fp
, cursor
, opt
, translation_domain
);
607 * Keep track of option tables already processed.
609 typedef struct poptDone_s
{
616 * Display usage text for a table of options.
618 * @param fp output file handle
620 * @param opt option(s)
621 * @param translation_domain translation domain
622 * @param done tables already processed
625 static int singleTableUsage(poptContext con
, FILE * fp
, int cursor
,
626 /*@null@*/ const struct poptOption
* opt
,
627 /*@null@*/ const char * translation_domain
,
628 /*@null@*/ poptDone done
)
629 /*@globals fileSystem @*/
630 /*@modifies *fp, done, fileSystem @*/
632 /*@-branchstate@*/ /* FIX: W2DO? */
634 for (; (opt
->longName
|| opt
->shortName
|| opt
->arg
) ; opt
++) {
635 if ((opt
->argInfo
& POPT_ARG_MASK
) == POPT_ARG_INTL_DOMAIN
) {
636 translation_domain
= (const char *)opt
->arg
;
637 } else if ((opt
->argInfo
& POPT_ARG_MASK
) == POPT_ARG_INCLUDE_TABLE
) {
640 for (i
= 0; i
< done
->nopts
; i
++) {
642 const void * that
= done
->opts
[i
];
644 if (that
== NULL
|| that
!= opt
->arg
)
645 /*@innercontinue@*/ continue;
646 /*@innerbreak@*/ break;
648 /* Skip if this table has already been processed. */
649 if (opt
->arg
== NULL
|| i
< done
->nopts
)
652 if (done
->nopts
< done
->maxopts
)
653 done
->opts
[done
->nopts
++] = (const void *) opt
->arg
;
656 cursor
= singleTableUsage(con
, fp
, cursor
, opt
->arg
,
657 translation_domain
, done
);
658 } else if ((opt
->longName
|| opt
->shortName
) &&
659 !(opt
->argInfo
& POPT_ARGFLAG_DOC_HIDDEN
)) {
660 cursor
= singleOptionUsage(fp
, cursor
, opt
, translation_domain
);
669 * Return concatenated short options for display.
670 * @todo Sub-tables should be recursed.
671 * @param opt option(s)
672 * @param fp output file handle
673 * @retval str concatenation of short options
674 * @return length of display string
676 static int showShortOptions(const struct poptOption
* opt
, FILE * fp
,
677 /*@null@*/ char * str
)
678 /*@globals fileSystem @*/
679 /*@modifies *str, *fp, fileSystem @*/
680 /*@requires maxRead(str) >= 0 @*/
682 /* bufsize larger then the ascii set, lazy alloca on top level call. */
683 char * s
= (str
!= NULL
? str
: memset(alloca(300), 0, 300));
688 for (; (opt
->longName
|| opt
->shortName
|| opt
->arg
); opt
++) {
689 if (opt
->shortName
&& !(opt
->argInfo
& POPT_ARG_MASK
))
690 s
[strlen(s
)] = opt
->shortName
;
691 else if ((opt
->argInfo
& POPT_ARG_MASK
) == POPT_ARG_INCLUDE_TABLE
)
692 if (opt
->arg
) /* XXX program error */
693 len
= showShortOptions(opt
->arg
, fp
, s
);
697 /* On return to top level, print the short options, return print length. */
698 if (s
== str
&& *s
!= '\0') {
699 fprintf(fp
, " [-%s]", s
);
700 len
= strlen(s
) + sizeof(" [-]")-1;
705 void poptPrintUsage(poptContext con
, FILE * fp
, /*@unused@*/ int flags
)
707 poptDone done
= memset(alloca(sizeof(*done
)), 0, sizeof(*done
));
712 cursor
= done
->maxopts
* sizeof(*done
->opts
);
714 done
->opts
= memset(alloca(cursor
), 0, cursor
);
715 done
->opts
[done
->nopts
++] = (const void *) con
->options
;
718 cursor
= showHelpIntro(con
, fp
);
719 cursor
+= showShortOptions(con
->options
, fp
, NULL
);
720 cursor
= singleTableUsage(con
, fp
, cursor
, con
->options
, NULL
, done
);
721 cursor
= itemUsage(fp
, cursor
, con
->aliases
, con
->numAliases
, NULL
);
722 cursor
= itemUsage(fp
, cursor
, con
->execs
, con
->numExecs
, NULL
);
724 if (con
->otherHelp
) {
725 cursor
+= strlen(con
->otherHelp
) + 1;
726 if (cursor
> 79) fprintf(fp
, "\n ");
727 fprintf(fp
, " %s", con
->otherHelp
);
733 void poptSetOtherOptionHelp(poptContext con
, const char * text
)
735 con
->otherHelp
= _free(con
->otherHelp
);
736 con
->otherHelp
= xstrdup(text
);