third_party: Update popt to 1.16 release
[samba.git] / third_party / popt / popthelp.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2
3 /** \ingroup popt
4  * \file popt/popthelp.c
5  */
6
7 /* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING
8    file accompanying popt source distributions, available from 
9    ftp://ftp.rpm.org/pub/rpm/dist. */
10
11 #include "system.h"
12
13 #define        POPT_USE_TIOCGWINSZ
14 #ifdef POPT_USE_TIOCGWINSZ
15 #include <sys/ioctl.h>
16 #endif
17
18 #define POPT_WCHAR_HACK
19 #ifdef  POPT_WCHAR_HACK
20 #include <wchar.h>                      /* for mbsrtowcs */
21 /*@access mbstate_t @*/
22 #endif
23 #include "poptint.h"
24
25 /*@access poptContext@*/
26
27 /**
28  * Display arguments.
29  * @param con           context
30  * @param foo           (unused)
31  * @param key           option(s)
32  * @param arg           (unused)
33  * @param data          (unused)
34  */
35 /*@exits@*/
36 static void displayArgs(poptContext con,
37                 /*@unused@*/ UNUSED(enum poptCallbackReason foo),
38                 struct poptOption * key, 
39                 /*@unused@*/ UNUSED(const char * arg),
40                 /*@unused@*/ UNUSED(void * data))
41         /*@globals fileSystem@*/
42         /*@modifies fileSystem@*/
43 {
44     if (key->shortName == '?')
45         poptPrintHelp(con, stdout, 0);
46     else
47         poptPrintUsage(con, stdout, 0);
48
49 #if !defined(__LCLINT__)        /* XXX keep both splint & valgrind happy */
50     con = poptFreeContext(con);
51 #endif
52     exit(0);
53 }
54
55 #ifdef  NOTYET
56 /*@unchecked@*/
57 static int show_option_defaults = 0;
58 #endif
59
60 /**
61  * Empty table marker to enable displaying popt alias/exec options.
62  */
63 /*@observer@*/ /*@unchecked@*/
64 struct poptOption poptAliasOptions[] = {
65     POPT_TABLEEND
66 };
67
68 /**
69  * Auto help table options.
70  */
71 /*@-castfcnptr@*/
72 /*@observer@*/ /*@unchecked@*/
73 struct poptOption poptHelpOptions[] = {
74   { NULL, '\0', POPT_ARG_CALLBACK, (void *)displayArgs, 0, NULL, NULL },
75   { "help", '?', 0, NULL, (int)'?', N_("Show this help message"), NULL },
76   { "usage", '\0', 0, NULL, (int)'u', N_("Display brief usage message"), NULL },
77     POPT_TABLEEND
78 } ;
79
80 /*@observer@*/ /*@unchecked@*/
81 static struct poptOption poptHelpOptions2[] = {
82 /*@-readonlytrans@*/
83   { NULL, '\0', POPT_ARG_INTL_DOMAIN, PACKAGE, 0, NULL, NULL},
84 /*@=readonlytrans@*/
85   { NULL, '\0', POPT_ARG_CALLBACK, (void *)displayArgs, 0, NULL, NULL },
86   { "help", '?', 0, NULL, (int)'?', N_("Show this help message"), NULL },
87   { "usage", '\0', 0, NULL, (int)'u', N_("Display brief usage message"), NULL },
88 #ifdef  NOTYET
89   { "defaults", '\0', POPT_ARG_NONE, &show_option_defaults, 0,
90         N_("Display option defaults in message"), NULL },
91 #endif
92   { "", '\0',   0, NULL, 0, N_("Terminate options"), NULL },
93     POPT_TABLEEND
94 } ;
95
96 /*@observer@*/ /*@unchecked@*/
97 struct poptOption * poptHelpOptionsI18N = poptHelpOptions2;
98 /*@=castfcnptr@*/
99
100 #define        _POPTHELP_MAXLINE       ((size_t)79)
101
102 typedef struct columns_s {
103     size_t cur;
104     size_t max;
105 } * columns_t;
106
107 /**
108  * Return no. of columns in output window.
109  * @param fp           FILE
110  * @return             no. of columns
111  */
112 static size_t maxColumnWidth(FILE *fp)
113         /*@*/
114 {
115     size_t maxcols = _POPTHELP_MAXLINE;
116 #if defined(TIOCGWINSZ)
117     struct winsize ws;
118     int fdno = fileno(fp ? fp : stdout);
119
120     memset(&ws, 0, sizeof(ws));
121     if (fdno >= 0 && !ioctl(fdno, (unsigned long)TIOCGWINSZ, &ws)) {
122         size_t ws_col = (size_t)ws.ws_col;
123         if (ws_col > maxcols && ws_col < (size_t)256)
124             maxcols = ws_col - 1;
125     }
126 #endif
127     return maxcols;
128 }
129
130 /**
131  * Determine number of display characters in a string.
132  * @param s             string
133  * @return              no. of display characters.
134  */
135 static inline size_t stringDisplayWidth(const char *s)
136         /*@*/
137 {
138     size_t n = strlen(s);
139 #ifdef  POPT_WCHAR_HACK
140     mbstate_t t;
141
142     memset ((void *)&t, 0, sizeof (t)); /* In initial state.  */
143     /* Determine number of display characters.  */
144     n = mbsrtowcs (NULL, &s, n, &t);
145 #else
146     n = 0;
147     for (; *s; s = POPT_next_char(s))
148         n++;
149 #endif
150
151     return n;
152 }
153
154 /**
155  * @param opt           option(s)
156  */
157 /*@observer@*/ /*@null@*/ static const char *
158 getTableTranslationDomain(/*@null@*/ const struct poptOption *opt)
159         /*@*/
160 {
161     if (opt != NULL)
162     for (; opt->longName || opt->shortName || opt->arg; opt++) {
163         if (opt->argInfo == POPT_ARG_INTL_DOMAIN)
164             return opt->arg;
165     }
166     return NULL;
167 }
168
169 /**
170  * @param opt           option(s)
171  * @param translation_domain    translation domain
172  */
173 /*@observer@*/ /*@null@*/ static const char *
174 getArgDescrip(const struct poptOption * opt,
175                 /*@-paramuse@*/ /* FIX: i18n macros disabled with lclint */
176                 /*@null@*/ const char * translation_domain)
177                 /*@=paramuse@*/
178         /*@*/
179 {
180     if (!poptArgType(opt)) return NULL;
181
182     if (poptArgType(opt) == POPT_ARG_MAINCALL)
183         return opt->argDescrip;
184     if (poptArgType(opt) == POPT_ARG_ARGV)
185         return opt->argDescrip;
186
187     if (opt->argDescrip) {
188         /* Some strings need popt library, not application, i18n domain. */
189         if (opt == (poptHelpOptions + 1)
190          || opt == (poptHelpOptions + 2)
191          || !strcmp(opt->argDescrip, N_("Help options:"))
192          || !strcmp(opt->argDescrip, N_("Options implemented via popt alias/exec:")))
193             return POPT_(opt->argDescrip);
194
195         /* Use the application i18n domain. */
196         return D_(translation_domain, opt->argDescrip);
197     }
198
199     switch (poptArgType(opt)) {
200     case POPT_ARG_NONE:         return POPT_("NONE");
201 #ifdef  DYING
202     case POPT_ARG_VAL:          return POPT_("VAL");
203 #else
204     case POPT_ARG_VAL:          return NULL;
205 #endif
206     case POPT_ARG_INT:          return POPT_("INT");
207     case POPT_ARG_SHORT:        return POPT_("SHORT");
208     case POPT_ARG_LONG:         return POPT_("LONG");
209     case POPT_ARG_LONGLONG:     return POPT_("LONGLONG");
210     case POPT_ARG_STRING:       return POPT_("STRING");
211     case POPT_ARG_FLOAT:        return POPT_("FLOAT");
212     case POPT_ARG_DOUBLE:       return POPT_("DOUBLE");
213     case POPT_ARG_MAINCALL:     return NULL;
214     case POPT_ARG_ARGV:         return NULL;
215     default:                    return POPT_("ARG");
216     }
217 }
218
219 /**
220  * Display default value for an option.
221  * @param lineLength    display positions remaining
222  * @param opt           option(s)
223  * @param translation_domain    translation domain
224  * @return
225  */
226 static /*@only@*/ /*@null@*/ char *
227 singleOptionDefaultValue(size_t lineLength,
228                 const struct poptOption * opt,
229                 /*@-paramuse@*/ /* FIX: i18n macros disabled with lclint */
230                 /*@null@*/ const char * translation_domain)
231                 /*@=paramuse@*/
232         /*@*/
233 {
234     const char * defstr = D_(translation_domain, "default");
235     char * le = malloc(4*lineLength + 1);
236     char * l = le;
237
238     if (le == NULL) return NULL;        /* XXX can't happen */
239     *le = '\0';
240     *le++ = '(';
241     le = stpcpy(le, defstr);
242     *le++ = ':';
243     *le++ = ' ';
244   if (opt->arg) {       /* XXX programmer error */
245     poptArg arg = { .ptr = opt->arg };
246     switch (poptArgType(opt)) {
247     case POPT_ARG_VAL:
248     case POPT_ARG_INT:
249         le += sprintf(le, "%d", arg.intp[0]);
250         break;
251     case POPT_ARG_SHORT:
252         le += sprintf(le, "%hd", arg.shortp[0]);
253         break;
254     case POPT_ARG_LONG:
255         le += sprintf(le, "%ld", arg.longp[0]);
256         break;
257     case POPT_ARG_LONGLONG:
258         le += sprintf(le, "%lld", arg.longlongp[0]);
259         break;
260     case POPT_ARG_FLOAT:
261     {   double aDouble = (double) arg.floatp[0];
262         le += sprintf(le, "%g", aDouble);
263     }   break;
264     case POPT_ARG_DOUBLE:
265         le += sprintf(le, "%g", arg.doublep[0]);
266         break;
267     case POPT_ARG_MAINCALL:
268         le += sprintf(le, "%p", opt->arg);
269         break;
270     case POPT_ARG_ARGV:
271         le += sprintf(le, "%p", opt->arg);
272         break;
273     case POPT_ARG_STRING:
274     {   const char * s = arg.argv[0];
275         if (s == NULL)
276             le = stpcpy(le, "null");
277         else {
278             size_t limit = 4*lineLength - (le - l) - sizeof("\"\")");
279             size_t slen;
280             *le++ = '"';
281             strncpy(le, s, limit); le[limit] = '\0'; le += (slen = strlen(le));
282             if (slen == limit && s[limit])
283                 le[-1] = le[-2] = le[-3] = '.';
284             *le++ = '"';
285         }
286     }   break;
287     case POPT_ARG_NONE:
288     default:
289         l = _free(l);
290         return NULL;
291         /*@notreached@*/ break;
292     }
293   }
294     *le++ = ')';
295     *le = '\0';
296
297     return l;
298 }
299
300 /**
301  * Display help text for an option.
302  * @param fp            output file handle
303  * @param columns       output display width control
304  * @param opt           option(s)
305  * @param translation_domain    translation domain
306  */
307 static void singleOptionHelp(FILE * fp, columns_t columns,
308                 const struct poptOption * opt,
309                 /*@null@*/ const char * translation_domain)
310         /*@globals fileSystem @*/
311         /*@modifies fp, fileSystem @*/
312 {
313     size_t maxLeftCol = columns->cur;
314     size_t indentLength = maxLeftCol + 5;
315     size_t lineLength = columns->max - indentLength;
316     const char * help = D_(translation_domain, opt->descrip);
317     const char * argDescrip = getArgDescrip(opt, translation_domain);
318     /* Display shortName iff printable non-space. */
319     int prtshort = (int)(isprint((int)opt->shortName) && opt->shortName != ' ');
320     size_t helpLength;
321     char * defs = NULL;
322     char * left;
323     size_t nb = maxLeftCol + 1;
324     int displaypad = 0;
325     int xx;
326
327     /* Make sure there's more than enough room in target buffer. */
328     if (opt->longName)  nb += strlen(opt->longName);
329     if (F_ISSET(opt, TOGGLE)) nb += sizeof("[no]") - 1;
330     if (argDescrip)     nb += strlen(argDescrip);
331
332     left = malloc(nb);
333     if (left == NULL) return;   /* XXX can't happen */
334     left[0] = '\0';
335     left[maxLeftCol] = '\0';
336
337 #define prtlong (opt->longName != NULL) /* XXX splint needs a clue */
338     if (!(prtshort || prtlong))
339         goto out;
340     if (prtshort && prtlong) {
341         char *dash = F_ISSET(opt, ONEDASH) ? "-" : "--";
342         left[0] = '-';
343         left[1] = opt->shortName;
344         (void) stpcpy(stpcpy(stpcpy(left+2, ", "), dash), opt->longName);
345     } else if (prtshort) {
346         left[0] = '-';
347         left[1] = opt->shortName;
348         left[2] = '\0';
349     } else if (prtlong) {
350         /* XXX --long always padded for alignment with/without "-X, ". */
351         char *dash = poptArgType(opt) == POPT_ARG_MAINCALL ? ""
352                    : (F_ISSET(opt, ONEDASH) ? "-" : "--");
353         const char *longName = opt->longName;
354         const char *toggle;
355         if (F_ISSET(opt, TOGGLE)) {
356             toggle = "[no]";
357             if (longName[0] == 'n' && longName[1] == 'o') {
358                 longName += sizeof("no") - 1;
359                 if (longName[0] == '-')
360                     longName++;
361             }
362         } else
363             toggle = "";
364         (void) stpcpy(stpcpy(stpcpy(stpcpy(left, "    "), dash), toggle), longName);
365     }
366 #undef  prtlong
367
368     if (argDescrip) {
369         char * le = left + strlen(left);
370
371         if (F_ISSET(opt, OPTIONAL))
372             *le++ = '[';
373
374         /* Choose type of output */
375         if (F_ISSET(opt, SHOW_DEFAULT)) {
376             defs = singleOptionDefaultValue(lineLength, opt, translation_domain);
377             if (defs) {
378                 char * t = malloc((help ? strlen(help) : 0) +
379                                 strlen(defs) + sizeof(" "));
380                 if (t) {
381                     char * te = t;
382                     if (help)
383                         te = stpcpy(te, help);
384                     *te++ = ' ';
385                     strcpy(te, defs);
386                     defs = _free(defs);
387                     defs = t;
388                 }
389             }
390         }
391
392         if (opt->argDescrip == NULL) {
393             switch (poptArgType(opt)) {
394             case POPT_ARG_NONE:
395                 break;
396             case POPT_ARG_VAL:
397 #ifdef  NOTNOW  /* XXX pug ugly nerdy output */
398             {   long aLong = opt->val;
399                 int ops = F_ISSET(opt, LOGICALOPS);
400                 int negate = F_ISSET(opt, NOT);
401
402                 /* Don't bother displaying typical values */
403                 if (!ops && (aLong == 0L || aLong == 1L || aLong == -1L))
404                     break;
405                 *le++ = '[';
406                 switch (ops) {
407                 case POPT_ARGFLAG_OR:
408                     *le++ = '|';
409                     /*@innerbreak@*/ break;
410                 case POPT_ARGFLAG_AND:
411                     *le++ = '&';
412                     /*@innerbreak@*/ break;
413                 case POPT_ARGFLAG_XOR:
414                     *le++ = '^';
415                     /*@innerbreak@*/ break;
416                 default:
417                     /*@innerbreak@*/ break;
418                 }
419                 *le++ = (opt->longName != NULL ? '=' : ' ');
420                 if (negate) *le++ = '~';
421                 /*@-formatconst@*/
422                 le += sprintf(le, (ops ? "0x%lx" : "%ld"), aLong);
423                 /*@=formatconst@*/
424                 *le++ = ']';
425             }
426 #endif
427                 break;
428             case POPT_ARG_INT:
429             case POPT_ARG_SHORT:
430             case POPT_ARG_LONG:
431             case POPT_ARG_LONGLONG:
432             case POPT_ARG_FLOAT:
433             case POPT_ARG_DOUBLE:
434             case POPT_ARG_STRING:
435                 *le++ = (opt->longName != NULL ? '=' : ' ');
436                 le = stpcpy(le, argDescrip);
437                 break;
438             default:
439                 break;
440             }
441         } else {
442             char *leo;
443
444             /* XXX argDescrip[0] determines "--foo=bar" or "--foo bar". */
445             if (!strchr(" =(", argDescrip[0]))
446                 *le++ = ((poptArgType(opt) == POPT_ARG_MAINCALL) ? ' ' :
447                          (poptArgType(opt) == POPT_ARG_ARGV) ? ' ' : '=');
448             le = stpcpy(leo = le, argDescrip);
449
450             /* Adjust for (possible) wide characters. */
451             displaypad = (int)((le - leo) - stringDisplayWidth(argDescrip));
452         }
453         if (F_ISSET(opt, OPTIONAL))
454             *le++ = ']';
455         *le = '\0';
456     }
457
458     if (help)
459         xx = POPT_fprintf(fp,"  %-*s   ", (int)(maxLeftCol+displaypad), left);
460     else {
461         xx = POPT_fprintf(fp,"  %s\n", left);
462         goto out;
463     }
464
465     left = _free(left);
466     if (defs)
467         help = defs;
468
469     helpLength = strlen(help);
470     while (helpLength > lineLength) {
471         const char * ch;
472         char format[16];
473
474         ch = help + lineLength - 1;
475         while (ch > help && !_isspaceptr(ch))
476             ch = POPT_prev_char(ch);
477         if (ch == help) break;          /* give up */
478         while (ch > (help + 1) && _isspaceptr(ch))
479             ch = POPT_prev_char (ch);
480         ch = POPT_next_char(ch);
481
482         /*
483          *  XXX strdup is necessary to add NUL terminator so that an unknown
484          *  no. of (possible) multi-byte characters can be displayed.
485          */
486         {   char * fmthelp = xstrdup(help);
487             if (fmthelp) {
488                 fmthelp[ch - help] = '\0';
489                 sprintf(format, "%%s\n%%%ds", (int) indentLength);
490                 /*@-formatconst@*/
491                 xx = POPT_fprintf(fp, format, fmthelp, " ");
492                 /*@=formatconst@*/
493                 free(fmthelp);
494             }
495         }
496
497         help = ch;
498         while (_isspaceptr(help) && *help)
499             help = POPT_next_char(help);
500         helpLength = strlen(help);
501     }
502
503     if (helpLength) fprintf(fp, "%s\n", help);
504     help = NULL;
505
506 out:
507     /*@-dependenttrans@*/
508     defs = _free(defs);
509     /*@=dependenttrans@*/
510     left = _free(left);
511 }
512
513 /**
514  * Find display width for longest argument string.
515  * @param opt           option(s)
516  * @param translation_domain    translation domain
517  * @return              display width
518  */
519 static size_t maxArgWidth(const struct poptOption * opt,
520                        /*@null@*/ const char * translation_domain)
521         /*@*/
522 {
523     size_t max = 0;
524     size_t len = 0;
525     const char * argDescrip;
526     
527     if (opt != NULL)
528     while (opt->longName || opt->shortName || opt->arg) {
529         if (poptArgType(opt) == POPT_ARG_INCLUDE_TABLE) {
530             if (opt->arg)       /* XXX program error */
531                 len = maxArgWidth(opt->arg, translation_domain);
532             if (len > max) max = len;
533         } else if (!F_ISSET(opt, DOC_HIDDEN)) {
534             len = sizeof("  ")-1;
535             /* XXX --long always padded for alignment with/without "-X, ". */
536             len += sizeof("-X, ")-1;
537             if (opt->longName) {
538                 len += (F_ISSET(opt, ONEDASH) ? sizeof("-") : sizeof("--")) - 1;
539                 len += strlen(opt->longName);
540             }
541
542             argDescrip = getArgDescrip(opt, translation_domain);
543
544             if (argDescrip) {
545
546                 /* XXX argDescrip[0] determines "--foo=bar" or "--foo bar". */
547                 if (!strchr(" =(", argDescrip[0])) len += sizeof("=")-1;
548
549                 /* Adjust for (possible) wide characters. */
550                 len += stringDisplayWidth(argDescrip);
551             }
552
553             if (F_ISSET(opt, OPTIONAL)) len += sizeof("[]")-1;
554             if (len > max) max = len;
555         }
556         opt++;
557     }
558     
559     return max;
560 }
561
562 /**
563  * Display popt alias and exec help.
564  * @param fp            output file handle
565  * @param items         alias/exec array
566  * @param nitems        no. of alias/exec entries
567  * @param columns       output display width control
568  * @param translation_domain    translation domain
569  */
570 static void itemHelp(FILE * fp,
571                 /*@null@*/ poptItem items, int nitems,
572                 columns_t columns,
573                 /*@null@*/ const char * translation_domain)
574         /*@globals fileSystem @*/
575         /*@modifies fp, fileSystem @*/
576 {
577     poptItem item;
578     int i;
579
580     if (items != NULL)
581     for (i = 0, item = items; i < nitems; i++, item++) {
582         const struct poptOption * opt;
583         opt = &item->option;
584         if ((opt->longName || opt->shortName) && !F_ISSET(opt, DOC_HIDDEN))
585             singleOptionHelp(fp, columns, opt, translation_domain);
586     }
587 }
588
589 /**
590  * Display help text for a table of options.
591  * @param con           context
592  * @param fp            output file handle
593  * @param table         option(s)
594  * @param columns       output display width control
595  * @param translation_domain    translation domain
596  */
597 static void singleTableHelp(poptContext con, FILE * fp,
598                 /*@null@*/ const struct poptOption * table,
599                 columns_t columns,
600                 /*@null@*/ const char * translation_domain)
601         /*@globals fileSystem @*/
602         /*@modifies fp, columns->cur, fileSystem @*/
603 {
604     const struct poptOption * opt;
605     const char *sub_transdom;
606     int xx;
607
608     if (table == poptAliasOptions) {
609         itemHelp(fp, con->aliases, con->numAliases, columns, NULL);
610         itemHelp(fp, con->execs, con->numExecs, columns, NULL);
611         return;
612     }
613
614     if (table != NULL)
615     for (opt = table; opt->longName || opt->shortName || opt->arg; opt++) {
616         if ((opt->longName || opt->shortName) && !F_ISSET(opt, DOC_HIDDEN))
617             singleOptionHelp(fp, columns, opt, translation_domain);
618     }
619
620     if (table != NULL)
621     for (opt = table; opt->longName || opt->shortName || opt->arg; opt++) {
622         if (poptArgType(opt) != POPT_ARG_INCLUDE_TABLE)
623             continue;
624         sub_transdom = getTableTranslationDomain(opt->arg);
625         if (sub_transdom == NULL)
626             sub_transdom = translation_domain;
627             
628         /* If no popt aliases/execs, skip poptAliasOption processing. */
629         if (opt->arg == poptAliasOptions && !(con->numAliases || con->numExecs))
630             continue;
631         if (opt->descrip)
632             xx = POPT_fprintf(fp, "\n%s\n", D_(sub_transdom, opt->descrip));
633
634         singleTableHelp(con, fp, opt->arg, columns, sub_transdom);
635     }
636 }
637
638 /**
639  * @param con           context
640  * @param fp            output file handle
641  */
642 static size_t showHelpIntro(poptContext con, FILE * fp)
643         /*@globals fileSystem @*/
644         /*@modifies fp, fileSystem @*/
645 {
646     size_t len = (size_t)6;
647     int xx;
648
649     xx = POPT_fprintf(fp, POPT_("Usage:"));
650     if (!(con->flags & POPT_CONTEXT_KEEP_FIRST)) {
651         struct optionStackEntry * os = con->optionStack;
652         const char * fn = (os->argv ? os->argv[0] : NULL);
653         if (fn == NULL) return len;
654         if (strchr(fn, '/')) fn = strrchr(fn, '/') + 1;
655         /* XXX POPT_fprintf not needed for argv[0] display. */
656         fprintf(fp, " %s", fn);
657         len += strlen(fn) + 1;
658     }
659
660     return len;
661 }
662
663 void poptPrintHelp(poptContext con, FILE * fp, /*@unused@*/ UNUSED(int flags))
664 {
665     columns_t columns = calloc((size_t)1, sizeof(*columns));
666     int xx;
667
668     (void) showHelpIntro(con, fp);
669     if (con->otherHelp)
670         xx = POPT_fprintf(fp, " %s\n", con->otherHelp);
671     else
672         xx = POPT_fprintf(fp, " %s\n", POPT_("[OPTION...]"));
673
674     if (columns) {
675         columns->cur = maxArgWidth(con->options, NULL);
676         columns->max = maxColumnWidth(fp);
677         singleTableHelp(con, fp, con->options, columns, NULL);
678         free(columns);
679     }
680 }
681
682 /**
683  * Display usage text for an option.
684  * @param fp            output file handle
685  * @param columns       output display width control
686  * @param opt           option(s)
687  * @param translation_domain    translation domain
688  */
689 static size_t singleOptionUsage(FILE * fp, columns_t columns,
690                 const struct poptOption * opt,
691                 /*@null@*/ const char *translation_domain)
692         /*@globals fileSystem @*/
693         /*@modifies fp, columns->cur, fileSystem @*/
694 {
695     size_t len = sizeof(" []")-1;
696     const char * argDescrip = getArgDescrip(opt, translation_domain);
697     /* Display shortName iff printable non-space. */
698     int prtshort = (int)(isprint((int)opt->shortName) && opt->shortName != ' ');
699
700 #define prtlong (opt->longName != NULL) /* XXX splint needs a clue */
701     if (!(prtshort || prtlong))
702         return columns->cur;
703
704     len = sizeof(" []")-1;
705     if (prtshort)
706         len += sizeof("-c")-1;
707     if (prtlong) {
708         if (prtshort) len += sizeof("|")-1;
709         len += (F_ISSET(opt, ONEDASH) ? sizeof("-") : sizeof("--")) - 1;
710         len += strlen(opt->longName);
711     }
712
713     if (argDescrip) {
714
715         /* XXX argDescrip[0] determines "--foo=bar" or "--foo bar". */
716         if (!strchr(" =(", argDescrip[0])) len += sizeof("=")-1;
717
718         /* Adjust for (possible) wide characters. */
719         len += stringDisplayWidth(argDescrip);
720     }
721
722     if ((columns->cur + len) > columns->max) {
723         fprintf(fp, "\n       ");
724         columns->cur = (size_t)7;
725     } 
726
727     fprintf(fp, " [");
728     if (prtshort)
729         fprintf(fp, "-%c", opt->shortName);
730     if (prtlong)
731         fprintf(fp, "%s%s%s",
732                 (prtshort ? "|" : ""),
733                 (F_ISSET(opt, ONEDASH) ? "-" : "--"),
734                 opt->longName);
735 #undef  prtlong
736
737     if (argDescrip) {
738         /* XXX argDescrip[0] determines "--foo=bar" or "--foo bar". */
739         if (!strchr(" =(", argDescrip[0])) fprintf(fp, "=");
740         fprintf(fp, "%s", argDescrip);
741     }
742     fprintf(fp, "]");
743
744     return columns->cur + len + 1;
745 }
746
747 /**
748  * Display popt alias and exec usage.
749  * @param fp            output file handle
750  * @param columns       output display width control
751  * @param item          alias/exec array
752  * @param nitems        no. of ara/exec entries
753  * @param translation_domain    translation domain
754  */
755 static size_t itemUsage(FILE * fp, columns_t columns,
756                 /*@null@*/ poptItem item, int nitems,
757                 /*@null@*/ const char * translation_domain)
758         /*@globals fileSystem @*/
759         /*@modifies fp, columns->cur, fileSystem @*/
760 {
761     int i;
762
763     if (item != NULL)
764     for (i = 0; i < nitems; i++, item++) {
765         const struct poptOption * opt;
766         opt = &item->option;
767         if (poptArgType(opt) == POPT_ARG_INTL_DOMAIN) {
768             translation_domain = (const char *)opt->arg;
769         } else
770         if ((opt->longName || opt->shortName) && !F_ISSET(opt, DOC_HIDDEN)) {
771             columns->cur = singleOptionUsage(fp, columns, opt, translation_domain);
772         }
773     }
774
775     return columns->cur;
776 }
777
778 /**
779  * Keep track of option tables already processed.
780  */
781 typedef struct poptDone_s {
782     int nopts;
783     int maxopts;
784 /*@null@*/
785     const void ** opts;
786 } * poptDone;
787
788 /**
789  * Display usage text for a table of options.
790  * @param con           context
791  * @param fp            output file handle
792  * @param columns       output display width control
793  * @param opt           option(s)
794  * @param translation_domain    translation domain
795  * @param done          tables already processed
796  * @return
797  */
798 static size_t singleTableUsage(poptContext con, FILE * fp, columns_t columns,
799                 /*@null@*/ const struct poptOption * opt,
800                 /*@null@*/ const char * translation_domain,
801                 /*@null@*/ poptDone done)
802         /*@globals fileSystem @*/
803         /*@modifies fp, columns->cur, done, fileSystem @*/
804 {
805     if (opt != NULL)
806     for (; (opt->longName || opt->shortName || opt->arg) ; opt++) {
807         if (poptArgType(opt) == POPT_ARG_INTL_DOMAIN) {
808             translation_domain = (const char *)opt->arg;
809         } else
810         if (poptArgType(opt) == POPT_ARG_INCLUDE_TABLE) {
811             if (done) {
812                 int i = 0;
813                 if (done->opts != NULL)
814                 for (i = 0; i < done->nopts; i++) {
815                     const void * that = done->opts[i];
816                     if (that == NULL || that != opt->arg)
817                         /*@innercontinue@*/ continue;
818                     /*@innerbreak@*/ break;
819                 }
820                 /* Skip if this table has already been processed. */
821                 if (opt->arg == NULL || i < done->nopts)
822                     continue;
823                 if (done->opts != NULL && done->nopts < done->maxopts)
824                     done->opts[done->nopts++] = (const void *) opt->arg;
825             }
826             columns->cur = singleTableUsage(con, fp, columns, opt->arg,
827                         translation_domain, done);
828         } else
829         if ((opt->longName || opt->shortName) && !F_ISSET(opt, DOC_HIDDEN)) {
830             columns->cur = singleOptionUsage(fp, columns, opt, translation_domain);
831         }
832     }
833
834     return columns->cur;
835 }
836
837 /**
838  * Return concatenated short options for display.
839  * @todo Sub-tables should be recursed.
840  * @param opt           option(s)
841  * @param fp            output file handle
842  * @retval str          concatenation of short options
843  * @return              length of display string
844  */
845 static size_t showShortOptions(const struct poptOption * opt, FILE * fp,
846                 /*@null@*/ char * str)
847         /*@globals fileSystem @*/
848         /*@modifies str, *fp, fileSystem @*/
849         /*@requires maxRead(str) >= 0 @*/
850 {
851     /* bufsize larger then the ascii set, lazy allocation on top level call. */
852     size_t nb = (size_t)300;
853     char * s = (str != NULL ? str : calloc((size_t)1, nb));
854     size_t len = (size_t)0;
855
856     if (s == NULL)
857         return 0;
858
859     if (opt != NULL)
860     for (; (opt->longName || opt->shortName || opt->arg); opt++) {
861         if (!F_ISSET(opt, DOC_HIDDEN) && opt->shortName && !poptArgType(opt))
862         {
863             /* Display shortName iff unique printable non-space. */
864             if (!strchr(s, opt->shortName) && isprint((int)opt->shortName)
865              && opt->shortName != ' ')
866                 s[strlen(s)] = opt->shortName;
867         } else if (poptArgType(opt) == POPT_ARG_INCLUDE_TABLE)
868             if (opt->arg)       /* XXX program error */
869                 len = showShortOptions(opt->arg, fp, s);
870     } 
871
872     /* On return to top level, print the short options, return print length. */
873     if (s != str && *s != '\0') {
874         fprintf(fp, " [-%s]", s);
875         len = strlen(s) + sizeof(" [-]")-1;
876     }
877 /*@-temptrans@*/        /* LCL: local s, not str arg, is being freed. */
878     if (s != str)
879         free(s);
880 /*@=temptrans@*/
881     return len;
882 }
883
884 void poptPrintUsage(poptContext con, FILE * fp, /*@unused@*/ UNUSED(int flags))
885 {
886     columns_t columns = calloc((size_t)1, sizeof(*columns));
887     struct poptDone_s done_buf;
888     poptDone done = &done_buf;
889
890     memset(done, 0, sizeof(*done));
891     done->nopts = 0;
892     done->maxopts = 64;
893   if (columns) {
894     columns->cur = done->maxopts * sizeof(*done->opts);
895     columns->max = maxColumnWidth(fp);
896     done->opts = calloc((size_t)1, columns->cur);
897     /*@-keeptrans@*/
898     if (done->opts != NULL)
899         done->opts[done->nopts++] = (const void *) con->options;
900     /*@=keeptrans@*/
901
902     columns->cur = showHelpIntro(con, fp);
903     columns->cur += showShortOptions(con->options, fp, NULL);
904     columns->cur = singleTableUsage(con, fp, columns, con->options, NULL, done);
905     columns->cur = itemUsage(fp, columns, con->aliases, con->numAliases, NULL);
906     columns->cur = itemUsage(fp, columns, con->execs, con->numExecs, NULL);
907
908     if (con->otherHelp) {
909         columns->cur += strlen(con->otherHelp) + 1;
910         if (columns->cur > columns->max) fprintf(fp, "\n       ");
911         fprintf(fp, " %s", con->otherHelp);
912     }
913
914     fprintf(fp, "\n");
915     if (done->opts != NULL)
916         free(done->opts);
917     free(columns);
918   }
919 }
920
921 void poptSetOtherOptionHelp(poptContext con, const char * text)
922 {
923     con->otherHelp = _free(con->otherHelp);
924     con->otherHelp = xstrdup(text);
925 }