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
)
79 return (char *)opt
->arg
;
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
= (char *)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
);
179 l
= (char *)_free(l
);
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
);
217 left
= (char *)malloc(nb
);
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
= (char *)malloc((help
? strlen(help
) : 0) +
246 strlen(defs
) + sizeof(" "));
251 strcpy(te
, help
); te
+= strlen(te
);
255 defs
= (char *)_free(defs
);
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
);
326 left
= (char *)_free(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@*/
357 defs
= (char *)_free(defs
);
358 /*@=dependenttrans@*/
359 left
= (char *)_free(left
);
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((const struct poptOption
*)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(
465 (const struct poptOption
*)opt
->arg
);
466 if (sub_transdom
== NULL
)
467 sub_transdom
= translation_domain
;
470 fprintf(fp
, "\n%s\n", D_(sub_transdom
, opt
->descrip
));
472 singleTableHelp(con
, fp
, (const struct poptOption
*)opt
->arg
, left
, sub_transdom
);
478 * @param fp output file handle
480 static int showHelpIntro(poptContext con
, FILE * fp
)
481 /*@globals fileSystem @*/
482 /*@modifies *fp, fileSystem @*/
487 fprintf(fp
, POPT_("Usage:"));
488 if (!(con
->flags
& POPT_CONTEXT_KEEP_FIRST
)) {
490 /*@-nullderef@*/ /* LCL: wazzup? */
491 fn
= con
->optionStack
->argv
[0];
494 if (fn
== NULL
) return len
;
495 if (strchr(fn
, '/')) fn
= strrchr(fn
, '/') + 1;
496 fprintf(fp
, " %s", fn
);
497 len
+= strlen(fn
) + 1;
503 void poptPrintHelp(poptContext con
, FILE * fp
, /*@unused@*/ int flags
)
507 (void) showHelpIntro(con
, fp
);
509 fprintf(fp
, " %s\n", con
->otherHelp
);
511 fprintf(fp
, " %s\n", POPT_("[OPTION...]"));
513 leftColWidth
= maxArgWidth(con
->options
, NULL
);
514 singleTableHelp(con
, fp
, con
->options
, leftColWidth
, NULL
);
518 * @param fp output file handle
520 * @param opt option(s)
521 * @param translation_domain translation domain
523 static int singleOptionUsage(FILE * fp
, int cursor
,
524 const struct poptOption
* opt
,
525 /*@null@*/ const char *translation_domain
)
526 /*@globals fileSystem @*/
527 /*@modifies *fp, fileSystem @*/
530 char shortStr
[2] = { '\0', '\0' };
531 const char * item
= shortStr
;
532 const char * argDescrip
= getArgDescrip(opt
, translation_domain
);
534 if (opt
->shortName
!= '\0' && opt
->longName
!= NULL
) {
536 if (!(opt
->argInfo
& POPT_ARGFLAG_ONEDASH
)) len
++;
537 len
+= strlen(opt
->longName
);
538 } else if (opt
->shortName
!= '\0') {
540 shortStr
[0] = opt
->shortName
;
542 } else if (opt
->longName
) {
543 len
+= strlen(opt
->longName
);
544 if (!(opt
->argInfo
& POPT_ARGFLAG_ONEDASH
)) len
++;
545 item
= opt
->longName
;
548 if (len
== 4) return cursor
;
551 len
+= strlen(argDescrip
) + 1;
553 if ((cursor
+ len
) > 79) {
558 if (opt
->longName
&& opt
->shortName
) {
559 fprintf(fp
, " [-%c|-%s%s%s%s]",
560 opt
->shortName
, ((opt
->argInfo
& POPT_ARGFLAG_ONEDASH
) ? "" : "-"),
562 (argDescrip
? " " : ""),
563 (argDescrip
? argDescrip
: ""));
565 fprintf(fp
, " [-%s%s%s%s]",
566 ((opt
->shortName
|| (opt
->argInfo
& POPT_ARGFLAG_ONEDASH
)) ? "" : "-"),
568 (argDescrip
? (opt
->shortName
!= '\0' ? " " : "=") : ""),
569 (argDescrip
? argDescrip
: ""));
572 return cursor
+ len
+ 1;
576 * Display popt alias and exec usage.
577 * @param fp output file handle
579 * @param item alias/exec array
580 * @param nitems no. of ara/exec entries
581 * @param translation_domain translation domain
583 static int itemUsage(FILE * fp
, int cursor
, poptItem item
, int nitems
,
584 /*@null@*/ const char * translation_domain
)
585 /*@globals fileSystem @*/
586 /*@modifies *fp, fileSystem @*/
590 /*@-branchstate@*/ /* FIX: W2DO? */
592 for (i
= 0; i
< nitems
; i
++, item
++) {
593 const struct poptOption
* opt
;
595 if ((opt
->argInfo
& POPT_ARG_MASK
) == POPT_ARG_INTL_DOMAIN
) {
596 translation_domain
= (const char *)opt
->arg
;
597 } else if ((opt
->longName
|| opt
->shortName
) &&
598 !(opt
->argInfo
& POPT_ARGFLAG_DOC_HIDDEN
)) {
599 cursor
= singleOptionUsage(fp
, cursor
, opt
, translation_domain
);
608 * Keep track of option tables already processed.
610 typedef struct poptDone_s
{
617 * Display usage text for a table of options.
619 * @param fp output file handle
621 * @param opt option(s)
622 * @param translation_domain translation domain
623 * @param done tables already processed
626 static int singleTableUsage(poptContext con
, FILE * fp
, int cursor
,
627 /*@null@*/ const struct poptOption
* opt
,
628 /*@null@*/ const char * translation_domain
,
629 /*@null@*/ poptDone done
)
630 /*@globals fileSystem @*/
631 /*@modifies *fp, done, fileSystem @*/
633 /*@-branchstate@*/ /* FIX: W2DO? */
635 for (; (opt
->longName
|| opt
->shortName
|| opt
->arg
) ; opt
++) {
636 if ((opt
->argInfo
& POPT_ARG_MASK
) == POPT_ARG_INTL_DOMAIN
) {
637 translation_domain
= (const char *)opt
->arg
;
638 } else if ((opt
->argInfo
& POPT_ARG_MASK
) == POPT_ARG_INCLUDE_TABLE
) {
641 for (i
= 0; i
< done
->nopts
; i
++) {
643 const void * that
= done
->opts
[i
];
645 if (that
== NULL
|| that
!= opt
->arg
)
646 /*@innercontinue@*/ continue;
647 /*@innerbreak@*/ break;
649 /* Skip if this table has already been processed. */
650 if (opt
->arg
== NULL
|| i
< done
->nopts
)
653 if (done
->nopts
< done
->maxopts
)
654 done
->opts
[done
->nopts
++] = (const void *) opt
->arg
;
657 cursor
= singleTableUsage(con
, fp
, cursor
, (const struct poptOption
*)opt
->arg
,
658 translation_domain
, done
);
659 } else if ((opt
->longName
|| opt
->shortName
) &&
660 !(opt
->argInfo
& POPT_ARGFLAG_DOC_HIDDEN
)) {
661 cursor
= singleOptionUsage(fp
, cursor
, opt
, translation_domain
);
670 * Return concatenated short options for display.
671 * @todo Sub-tables should be recursed.
672 * @param opt option(s)
673 * @param fp output file handle
674 * @retval str concatenation of short options
675 * @return length of display string
677 static int showShortOptions(const struct poptOption
* opt
, FILE * fp
,
678 /*@null@*/ char * str
)
679 /*@globals fileSystem @*/
680 /*@modifies *str, *fp, fileSystem @*/
681 /*@requires maxRead(str) >= 0 @*/
683 /* bufsize larger then the ascii set, lazy alloca on top level call. */
684 char * s
= (str
!= NULL
? str
: (char *)memset(alloca(300), 0, 300));
689 for (; (opt
->longName
|| opt
->shortName
|| opt
->arg
); opt
++) {
690 if (opt
->shortName
&& !(opt
->argInfo
& POPT_ARG_MASK
))
691 s
[strlen(s
)] = opt
->shortName
;
692 else if ((opt
->argInfo
& POPT_ARG_MASK
) == POPT_ARG_INCLUDE_TABLE
)
693 if (opt
->arg
) /* XXX program error */
694 len
= showShortOptions(
695 (const struct poptOption
*)opt
->arg
, fp
, s
);
699 /* On return to top level, print the short options, return print length. */
700 if (s
== str
&& *s
!= '\0') {
701 fprintf(fp
, " [-%s]", s
);
702 len
= strlen(s
) + sizeof(" [-]")-1;
707 void poptPrintUsage(poptContext con
, FILE * fp
, /*@unused@*/ int flags
)
709 poptDone done
= (poptDone
)memset(alloca(sizeof(*done
)), 0, sizeof(*done
));
714 cursor
= done
->maxopts
* sizeof(*done
->opts
);
716 done
->opts
= (const void **)memset(alloca(cursor
), 0, cursor
);
717 done
->opts
[done
->nopts
++] = (const void *) con
->options
;
720 cursor
= showHelpIntro(con
, fp
);
721 cursor
+= showShortOptions(con
->options
, fp
, NULL
);
722 cursor
= singleTableUsage(con
, fp
, cursor
, con
->options
, NULL
, done
);
723 cursor
= itemUsage(fp
, cursor
, con
->aliases
, con
->numAliases
, NULL
);
724 cursor
= itemUsage(fp
, cursor
, con
->execs
, con
->numExecs
, NULL
);
726 if (con
->otherHelp
) {
727 cursor
+= strlen(con
->otherHelp
) + 1;
728 if (cursor
> 79) fprintf(fp
, "\n ");
729 fprintf(fp
, " %s", con
->otherHelp
);
735 void poptSetOtherOptionHelp(poptContext con
, const char * text
)
737 con
->otherHelp
= (const char *)_free(con
->otherHelp
);
738 con
->otherHelp
= xstrdup(text
);