Make hard-linking work when a device has an st_dev of 0.
[rsync/rsync.git] / 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_WCHAR_HACK*/
14 #ifdef  POPT_WCHAR_HACK
15 #include <wchar.h>                      /* for mbsrtowcs */
16 /*@access mbstate_t @*/
17 #endif
18 #include "poptint.h"
19
20 /*@access poptContext@*/
21
22 /**
23  * Display arguments.
24  * @param con           context
25  * @param foo           (unused)
26  * @param key           option(s)
27  * @param arg           (unused)
28  * @param data          (unused)
29  */
30 static void displayArgs(poptContext con,
31                 /*@unused@*/ UNUSED(enum poptCallbackReason foo),
32                 struct poptOption * key, 
33                 /*@unused@*/ UNUSED(const char * arg), /*@unused@*/ UNUSED(void * data))
34         /*@globals fileSystem@*/
35         /*@modifies fileSystem@*/
36 {
37     if (key->shortName == '?')
38         poptPrintHelp(con, stdout, 0);
39     else
40         poptPrintUsage(con, stdout, 0);
41     exit(0);
42 }
43
44 #ifdef  NOTYET
45 /*@unchecked@*/
46 static int show_option_defaults = 0;
47 #endif
48
49 /**
50  * Empty table marker to enable displaying popt alias/exec options.
51  */
52 /*@observer@*/ /*@unchecked@*/
53 struct poptOption poptAliasOptions[] = {
54     POPT_TABLEEND
55 };
56
57 /**
58  * Auto help table options.
59  */
60 /*@-castfcnptr@*/
61 /*@observer@*/ /*@unchecked@*/
62 struct poptOption poptHelpOptions[] = {
63   { NULL, '\0', POPT_ARG_CALLBACK, (void *)&displayArgs, '\0', NULL, NULL },
64   { "help", '?', 0, NULL, '?', N_("Show this help message"), NULL },
65   { "usage", '\0', 0, NULL, 'u', N_("Display brief usage message"), NULL },
66     POPT_TABLEEND
67 } ;
68
69 /*@observer@*/ /*@unchecked@*/
70 static struct poptOption poptHelpOptions2[] = {
71 /*@-readonlytrans@*/
72   { NULL, '\0', POPT_ARG_INTL_DOMAIN, PACKAGE, 0, NULL, NULL},
73 /*@=readonlytrans@*/
74   { NULL, '\0', POPT_ARG_CALLBACK, (void *)&displayArgs, '\0', NULL, NULL },
75   { "help", '?', 0, NULL, '?', N_("Show this help message"), NULL },
76   { "usage", '\0', 0, NULL, 'u', N_("Display brief usage message"), NULL },
77 #ifdef  NOTYET
78   { "defaults", '\0', POPT_ARG_NONE, &show_option_defaults, 0,
79         N_("Display option defaults in message"), NULL },
80 #endif
81     POPT_TABLEEND
82 } ;
83
84 /*@observer@*/ /*@unchecked@*/
85 struct poptOption * poptHelpOptionsI18N = poptHelpOptions2;
86 /*@=castfcnptr@*/
87
88 /**
89  * @param table         option(s)
90  */
91 /*@observer@*/ /*@null@*/ static const char *
92 getTableTranslationDomain(/*@null@*/ const struct poptOption *table)
93         /*@*/
94 {
95     const struct poptOption *opt;
96
97     if (table != NULL)
98     for (opt = table; opt->longName || opt->shortName || opt->arg; opt++) {
99         if (opt->argInfo == POPT_ARG_INTL_DOMAIN)
100             return opt->arg;
101     }
102     return NULL;
103 }
104
105 /**
106  * @param opt           option(s)
107  * @param translation_domain    translation domain
108  */
109 /*@observer@*/ /*@null@*/ static const char *
110 getArgDescrip(const struct poptOption * opt,
111                 /*@-paramuse@*/ /* FIX: i18n macros disabled with lclint */
112                 /*@null@*/ UNUSED(const char * translation_domain))
113                 /*@=paramuse@*/
114         /*@*/
115 {
116     if (!(opt->argInfo & POPT_ARG_MASK)) return NULL;
117
118     if (opt == (poptHelpOptions + 1) || opt == (poptHelpOptions + 2))
119         if (opt->argDescrip) return POPT_(opt->argDescrip);
120
121     if (opt->argDescrip) return D_(translation_domain, opt->argDescrip);
122
123     switch (opt->argInfo & POPT_ARG_MASK) {
124     /*case POPT_ARG_NONE:       return POPT_("NONE");*/ /* impossible */
125 #ifdef  DYING
126     case POPT_ARG_VAL:          return POPT_("VAL");
127 #else
128     case POPT_ARG_VAL:          return NULL;
129 #endif
130     case POPT_ARG_INT:          return POPT_("INT");
131     case POPT_ARG_LONG:         return POPT_("LONG");
132     case POPT_ARG_STRING:       return POPT_("STRING");
133     case POPT_ARG_FLOAT:        return POPT_("FLOAT");
134     case POPT_ARG_DOUBLE:       return POPT_("DOUBLE");
135     default:                    return POPT_("ARG");
136     }
137 }
138
139 /**
140  * Display default value for an option.
141  * @param lineLength    display positions remaining
142  * @param opt           option(s)
143  * @param translation_domain    translation domain
144  * @return
145  */
146 static /*@only@*/ /*@null@*/ char *
147 singleOptionDefaultValue(size_t lineLength,
148                 const struct poptOption * opt,
149                 /*@-paramuse@*/ /* FIX: i18n macros disabled with lclint */
150                 /*@null@*/ UNUSED(const char * translation_domain))
151                 /*@=paramuse@*/
152         /*@*/
153 {
154     const char * defstr = D_(translation_domain, "default");
155     size_t limit, bufsize = 4*lineLength + 1;
156     char * le = malloc(bufsize);
157     char * l = le;
158
159     if (le == NULL) return NULL;        /* XXX can't happen */
160 /*@-boundswrite@*/
161     *le++ = '(';
162     le += strlcpy(le, defstr, bufsize - 3);
163     *le++ = ':';
164     *le++ = ' ';
165     limit = bufsize - (le - l) - 1; /* -1 for closing paren */
166     if (opt->arg)       /* XXX programmer error */
167     switch (opt->argInfo & POPT_ARG_MASK) {
168     case POPT_ARG_VAL:
169     case POPT_ARG_INT:
170     {   long aLong = *((int *)opt->arg);
171         le += snprintf(le, limit, "%ld", aLong);
172     }   break;
173     case POPT_ARG_LONG:
174     {   long aLong = *((long *)opt->arg);
175         le += snprintf(le, limit, "%ld", aLong);
176     }   break;
177     case POPT_ARG_FLOAT:
178     {   double aDouble = *((float *)opt->arg);
179         le += snprintf(le, limit, "%g", aDouble);
180     }   break;
181     case POPT_ARG_DOUBLE:
182     {   double aDouble = *((double *)opt->arg);
183         le += snprintf(le, limit, "%g", aDouble);
184     }   break;
185     case POPT_ARG_STRING:
186     {   const char * s = *(const char **)opt->arg;
187         if (s == NULL) {
188             le += strlcpy(le, "null", limit);
189         } else {
190             size_t len;
191             limit -= 2; /* make room for quotes */
192             *le++ = '"';
193             len = strlcpy(le, s, limit);
194             if (len >= limit) {
195                 le += limit - 3 - 1;
196                 *le++ = '.'; *le++ = '.'; *le++ = '.';
197             } else
198                 le += len;
199             *le++ = '"';
200         }
201     }   break;
202     case POPT_ARG_NONE:
203     default:
204         l = _free(l);
205         return NULL;
206         /*@notreached@*/ break;
207     }
208     *le++ = ')';
209     *le = '\0';
210 /*@=boundswrite@*/
211
212     return l;
213 }
214
215 /**
216  * Display help text for an option.
217  * @param fp            output file handle
218  * @param maxLeftCol    largest argument display width
219  * @param opt           option(s)
220  * @param translation_domain    translation domain
221  */
222 static void singleOptionHelp(FILE * fp, size_t maxLeftCol, 
223                 const struct poptOption * opt,
224                 /*@null@*/ UNUSED(const char * translation_domain))
225         /*@globals fileSystem @*/
226         /*@modifies *fp, fileSystem @*/
227 {
228     size_t indentLength = maxLeftCol + 5;
229     size_t lineLength = 79 - indentLength;
230     const char * help = D_(translation_domain, opt->descrip);
231     const char * argDescrip = getArgDescrip(opt, translation_domain);
232     size_t helpLength;
233     char * defs = NULL;
234     char * left;
235     size_t lelen, limit;
236     size_t nb = maxLeftCol + 1;
237     int displaypad = 0;
238
239     /* Make sure there's more than enough room in target buffer. */
240     if (opt->longName)  nb += strlen(opt->longName);
241     if (argDescrip)     nb += strlen(argDescrip);
242
243 /*@-boundswrite@*/
244     left = malloc(nb);
245     if (left == NULL) return;   /* XXX can't happen */
246     left[0] = '\0';
247     left[maxLeftCol] = '\0';
248
249     if (opt->longName && opt->shortName)
250         snprintf(left, nb, "-%c, %s%s", opt->shortName,
251                 ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"),
252                 opt->longName);
253     else if (opt->shortName != '\0') 
254         snprintf(left, nb, "-%c", opt->shortName);
255     else if (opt->longName)
256         snprintf(left, nb, "%s%s",
257                 ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"),
258                 opt->longName);
259     if (!*left) goto out;
260
261     if (argDescrip) {
262         char * le = left + strlen(left);
263
264         if (opt->argInfo & POPT_ARGFLAG_OPTIONAL)
265             *le++ = '[';
266
267         /* Choose type of output */
268         /*@-branchstate@*/
269         if (opt->argInfo & POPT_ARGFLAG_SHOW_DEFAULT) {
270             defs = singleOptionDefaultValue(lineLength, opt, translation_domain);
271             if (defs) {
272                 size_t bufsize = (help ? strlen(help) : 0) + sizeof " " + strlen(defs);
273                 char * t = malloc(bufsize);
274                 if (t) {
275                     snprintf(t, bufsize, "%s %s", help ? help : "", defs);
276                     defs = _free(defs);
277                 }
278                 defs = t;
279             }
280         }
281         /*@=branchstate@*/
282
283         if (opt->argDescrip == NULL) {
284             switch (opt->argInfo & POPT_ARG_MASK) {
285             case POPT_ARG_NONE:
286                 break;
287             case POPT_ARG_VAL:
288 #ifdef  NOTNOW  /* XXX pug ugly nerdy output */
289             {   long aLong = opt->val;
290                 int ops = (opt->argInfo & POPT_ARGFLAG_LOGICALOPS);
291                 int negate = (opt->argInfo & POPT_ARGFLAG_NOT);
292
293                 /* Don't bother displaying typical values */
294                 if (!ops && (aLong == 0L || aLong == 1L || aLong == -1L))
295                     break;
296                 *le++ = '[';
297                 switch (ops) {
298                 case POPT_ARGFLAG_OR:
299                     *le++ = '|';
300                     /*@innerbreak@*/ break;
301                 case POPT_ARGFLAG_AND:
302                     *le++ = '&';
303                     /*@innerbreak@*/ break;
304                 case POPT_ARGFLAG_XOR:
305                     *le++ = '^';
306                     /*@innerbreak@*/ break;
307                 default:
308                     /*@innerbreak@*/ break;
309                 }
310                 *le++ = (opt->longName != NULL ? '=' : ' ');
311                 if (negate) *le++ = '~';
312                 /*@-formatconst@*/
313                 limit = nb - (le - left);
314                 lelen = snprintf(le, limit, (ops ? "0x%lx" : "%ld"), aLong);
315                 le += lelen >= limit ? limit - 1 : lelen;
316                 /*@=formatconst@*/
317                 *le++ = ']';
318             }
319 #endif
320                 break;
321             case POPT_ARG_INT:
322             case POPT_ARG_LONG:
323             case POPT_ARG_FLOAT:
324             case POPT_ARG_DOUBLE:
325             case POPT_ARG_STRING:
326                 *le++ = (opt->longName != NULL ? '=' : ' ');
327                 limit = nb - (le - left);
328                 lelen = strlcpy(le, argDescrip, limit);
329                 le += lelen >= limit ? limit - 1 : lelen;
330                 break;
331             default:
332                 break;
333             }
334         } else {
335
336             *le++ = '=';
337             limit = nb - (le - left);
338             lelen = strlcpy(le, argDescrip, limit);
339             if (lelen >= limit)
340                 lelen = limit - 1;
341             le += lelen;
342
343 #ifdef  POPT_WCHAR_HACK
344             {   const char * scopy = argDescrip;
345                 mbstate_t t;
346                 size_t n;
347
348                 memset ((void *)&t, '\0', sizeof (t));  /* In initial state.  */
349                 /* Determine number of characters.  */
350                 n = mbsrtowcs (NULL, &scopy, strlen(scopy), &t);
351
352                 displaypad = (int) (lelen-n);
353             }
354 #endif
355         }
356         if (opt->argInfo & POPT_ARGFLAG_OPTIONAL)
357             *le++ = ']';
358         *le = '\0';
359     }
360 /*@=boundswrite@*/
361
362     if (help)
363         fprintf(fp,"  %-*s   ", (int)maxLeftCol+displaypad, left);
364     else {
365         fprintf(fp,"  %s\n", left); 
366         goto out;
367     }
368
369     left = _free(left);
370 /*@-branchstate@*/
371     if (defs) {
372         help = defs;
373         defs = NULL;
374     }
375 /*@=branchstate@*/
376
377     helpLength = strlen(help);
378 /*@-boundsread@*/
379     while (helpLength > lineLength) {
380         const char * ch;
381         char format[16];
382
383         ch = help + lineLength - 1;
384         while (ch > help && !isSpace(ch)) ch--;
385         if (ch == help) break;          /* give up */
386         while (ch > (help + 1) && isSpace(ch)) ch--;
387         ch++;
388
389         snprintf(format, sizeof format, "%%.%ds\n%%%ds", (int) (ch - help), (int) indentLength);
390         /*@-formatconst@*/
391         fprintf(fp, format, help, " ");
392         /*@=formatconst@*/
393         help = ch;
394         while (isSpace(help) && *help) help++;
395         helpLength = strlen(help);
396     }
397 /*@=boundsread@*/
398
399     if (helpLength) fprintf(fp, "%s\n", help);
400
401 out:
402     /*@-dependenttrans@*/
403     defs = _free(defs);
404     /*@=dependenttrans@*/
405     left = _free(left);
406 }
407
408 /**
409  * Find display width for longest argument string.
410  * @param opt           option(s)
411  * @param translation_domain    translation domain
412  * @return              display width
413  */
414 static size_t maxArgWidth(const struct poptOption * opt,
415                        /*@null@*/ UNUSED(const char * translation_domain))
416         /*@*/
417 {
418     size_t max = 0;
419     size_t len = 0;
420     const char * s;
421     
422     if (opt != NULL)
423     while (opt->longName || opt->shortName || opt->arg) {
424         if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
425             if (opt->arg)       /* XXX program error */
426             len = maxArgWidth(opt->arg, translation_domain);
427             if (len > max) max = len;
428         } else if (!(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
429             len = sizeof("  ")-1;
430             if (opt->shortName != '\0') len += sizeof("-X")-1;
431             if (opt->shortName != '\0' && opt->longName) len += sizeof(", ")-1;
432             if (opt->longName) {
433                 len += ((opt->argInfo & POPT_ARGFLAG_ONEDASH)
434                         ? sizeof("-")-1 : sizeof("--")-1);
435                 len += strlen(opt->longName);
436             }
437
438             s = getArgDescrip(opt, translation_domain);
439
440 #ifdef POPT_WCHAR_HACK
441             /* XXX Calculate no. of display characters. */
442             if (s) {
443                 const char * scopy = s;
444                 mbstate_t t;
445                 size_t n;
446
447 /*@-boundswrite@*/
448                 memset ((void *)&t, '\0', sizeof (t));  /* In initial state.  */
449 /*@=boundswrite@*/
450                 /* Determine number of characters.  */
451                 n = mbsrtowcs (NULL, &scopy, strlen(scopy), &t);
452                 len += sizeof("=")-1 + n;
453             }
454 #else
455             if (s)
456                 len += sizeof("=")-1 + strlen(s);
457 #endif
458
459             if (opt->argInfo & POPT_ARGFLAG_OPTIONAL) len += sizeof("[]")-1;
460             if (len > max) max = len;
461         }
462
463         opt++;
464     }
465     
466     return max;
467 }
468
469 /**
470  * Display popt alias and exec help.
471  * @param fp            output file handle
472  * @param items         alias/exec array
473  * @param nitems        no. of alias/exec entries
474  * @param left          largest argument display width
475  * @param translation_domain    translation domain
476  */
477 static void itemHelp(FILE * fp,
478                 /*@null@*/ poptItem items, int nitems, size_t left,
479                 /*@null@*/ UNUSED(const char * translation_domain))
480         /*@globals fileSystem @*/
481         /*@modifies *fp, fileSystem @*/
482 {
483     poptItem item;
484     int i;
485
486     if (items != NULL)
487     for (i = 0, item = items; i < nitems; i++, item++) {
488         const struct poptOption * opt;
489         opt = &item->option;
490         if ((opt->longName || opt->shortName) && 
491             !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
492             singleOptionHelp(fp, left, opt, translation_domain);
493     }
494 }
495
496 /**
497  * Display help text for a table of options.
498  * @param con           context
499  * @param fp            output file handle
500  * @param table         option(s)
501  * @param left          largest argument display width
502  * @param translation_domain    translation domain
503  */
504 static void singleTableHelp(poptContext con, FILE * fp,
505                 /*@null@*/ const struct poptOption * table, size_t left,
506                 /*@null@*/ UNUSED(const char * translation_domain))
507         /*@globals fileSystem @*/
508         /*@modifies *fp, fileSystem @*/
509 {
510     const struct poptOption * opt;
511     const char *sub_transdom;
512
513     if (table == poptAliasOptions) {
514         itemHelp(fp, con->aliases, con->numAliases, left, NULL);
515         itemHelp(fp, con->execs, con->numExecs, left, NULL);
516         return;
517     }
518
519     if (table != NULL)
520     for (opt = table; (opt->longName || opt->shortName || opt->arg); opt++) {
521         if ((opt->longName || opt->shortName) && 
522             !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
523             singleOptionHelp(fp, left, opt, translation_domain);
524     }
525
526     if (table != NULL)
527     for (opt = table; (opt->longName || opt->shortName || opt->arg); opt++) {
528         if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_INCLUDE_TABLE)
529             continue;
530         sub_transdom = getTableTranslationDomain(opt->arg);
531         if (sub_transdom == NULL)
532             sub_transdom = translation_domain;
533             
534         if (opt->descrip)
535             fprintf(fp, "\n%s\n", D_(sub_transdom, opt->descrip));
536
537         singleTableHelp(con, fp, opt->arg, left, sub_transdom);
538     }
539 }
540
541 /**
542  * @param con           context
543  * @param fp            output file handle
544  */
545 static int showHelpIntro(poptContext con, FILE * fp)
546         /*@globals fileSystem @*/
547         /*@modifies *fp, fileSystem @*/
548 {
549     int len = 6;
550     const char * fn;
551
552     fprintf(fp, POPT_("Usage:"));
553     if (!(con->flags & POPT_CONTEXT_KEEP_FIRST)) {
554 /*@-boundsread@*/
555         /*@-nullderef -type@*/  /* LCL: wazzup? */
556         fn = con->optionStack->argv[0];
557         /*@=nullderef =type@*/
558 /*@=boundsread@*/
559         if (fn == NULL) return len;
560         if (strchr(fn, '/')) fn = strrchr(fn, '/') + 1;
561         fprintf(fp, " %s", fn);
562         len += strlen(fn) + 1;
563     }
564
565     return len;
566 }
567
568 void poptPrintHelp(poptContext con, FILE * fp, /*@unused@*/ UNUSED(int flags))
569 {
570     size_t leftColWidth;
571
572     (void) showHelpIntro(con, fp);
573     if (con->otherHelp)
574         fprintf(fp, " %s\n", con->otherHelp);
575     else
576         fprintf(fp, " %s\n", POPT_("[OPTION...]"));
577
578     leftColWidth = maxArgWidth(con->options, NULL);
579     singleTableHelp(con, fp, con->options, leftColWidth, NULL);
580 }
581
582 /**
583  * Display usage text for an option.
584  * @param fp            output file handle
585  * @param cursor        current display position
586  * @param opt           option(s)
587  * @param translation_domain    translation domain
588  */
589 static size_t singleOptionUsage(FILE * fp, size_t cursor, 
590                 const struct poptOption * opt,
591                 /*@null@*/ const char *translation_domain)
592         /*@globals fileSystem @*/
593         /*@modifies *fp, fileSystem @*/
594 {
595     size_t len = 4;
596     char shortStr[2] = { '\0', '\0' };
597     const char * item = shortStr;
598     const char * argDescrip = getArgDescrip(opt, translation_domain);
599
600     if (opt->shortName != '\0' && opt->longName != NULL) {
601         len += 2;
602         if (!(opt->argInfo & POPT_ARGFLAG_ONEDASH)) len++;
603         len += strlen(opt->longName);
604     } else if (opt->shortName != '\0') {
605         len++;
606         shortStr[0] = opt->shortName;
607         shortStr[1] = '\0';
608     } else if (opt->longName) {
609         len += strlen(opt->longName);
610         if (!(opt->argInfo & POPT_ARGFLAG_ONEDASH)) len++;
611         item = opt->longName;
612     }
613
614     if (len == 4) return cursor;
615
616 #ifdef POPT_WCHAR_HACK
617     /* XXX Calculate no. of display characters. */
618     if (argDescrip) {
619         const char * scopy = argDescrip;
620         mbstate_t t;
621         size_t n;
622
623 /*@-boundswrite@*/
624         memset ((void *)&t, '\0', sizeof (t));  /* In initial state.  */
625 /*@=boundswrite@*/
626         /* Determine number of characters.  */
627         n = mbsrtowcs (NULL, &scopy, strlen(scopy), &t);
628         len += sizeof("=")-1 + n;
629     }
630 #else
631     if (argDescrip) 
632         len += sizeof("=")-1 + strlen(argDescrip);
633 #endif
634
635     if ((cursor + len) > 79) {
636         fprintf(fp, "\n       ");
637         cursor = 7;
638     } 
639
640     if (opt->longName && opt->shortName) {
641         fprintf(fp, " [-%c|-%s%s%s%s]",
642             opt->shortName, ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "" : "-"),
643             opt->longName,
644             (argDescrip ? " " : ""),
645             (argDescrip ? argDescrip : ""));
646     } else {
647         fprintf(fp, " [-%s%s%s%s]",
648             ((opt->shortName || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) ? "" : "-"),
649             item,
650             (argDescrip ? (opt->shortName != '\0' ? " " : "=") : ""),
651             (argDescrip ? argDescrip : ""));
652     }
653
654     return cursor + len + 1;
655 }
656
657 /**
658  * Display popt alias and exec usage.
659  * @param fp            output file handle
660  * @param cursor        current display position
661  * @param item          alias/exec array
662  * @param nitems        no. of ara/exec entries
663  * @param translation_domain    translation domain
664  */
665 static size_t itemUsage(FILE * fp, size_t cursor,
666                 /*@null@*/ poptItem item, int nitems,
667                 /*@null@*/ UNUSED(const char * translation_domain))
668         /*@globals fileSystem @*/
669         /*@modifies *fp, fileSystem @*/
670 {
671     int i;
672
673     /*@-branchstate@*/          /* FIX: W2DO? */
674     if (item != NULL)
675     for (i = 0; i < nitems; i++, item++) {
676         const struct poptOption * opt;
677         opt = &item->option;
678         if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN) {
679             translation_domain = (const char *)opt->arg;
680         } else if ((opt->longName || opt->shortName) &&
681                  !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
682             cursor = singleOptionUsage(fp, cursor, opt, translation_domain);
683         }
684     }
685     /*@=branchstate@*/
686
687     return cursor;
688 }
689
690 /**
691  * Keep track of option tables already processed.
692  */
693 typedef struct poptDone_s {
694     int nopts;
695     int maxopts;
696     const void ** opts;
697 } * poptDone;
698
699 /**
700  * Display usage text for a table of options.
701  * @param con           context
702  * @param fp            output file handle
703  * @param cursor        current display position
704  * @param opt           option(s)
705  * @param translation_domain    translation domain
706  * @param done          tables already processed
707  * @return
708  */
709 static size_t singleTableUsage(poptContext con, FILE * fp, size_t cursor,
710                 /*@null@*/ const struct poptOption * opt,
711                 /*@null@*/ UNUSED(const char * translation_domain),
712                 /*@null@*/ poptDone done)
713         /*@globals fileSystem @*/
714         /*@modifies *fp, done, fileSystem @*/
715 {
716     /*@-branchstate@*/          /* FIX: W2DO? */
717     if (opt != NULL)
718     for (; (opt->longName || opt->shortName || opt->arg) ; opt++) {
719         if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN) {
720             translation_domain = (const char *)opt->arg;
721         } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
722             if (done) {
723                 int i = 0;
724                 for (i = 0; i < done->nopts; i++) {
725 /*@-boundsread@*/
726                     const void * that = done->opts[i];
727 /*@=boundsread@*/
728                     if (that == NULL || that != opt->arg)
729                         /*@innercontinue@*/ continue;
730                     /*@innerbreak@*/ break;
731                 }
732                 /* Skip if this table has already been processed. */
733                 if (opt->arg == NULL || i < done->nopts)
734                     continue;
735 /*@-boundswrite@*/
736                 if (done->nopts < done->maxopts)
737                     done->opts[done->nopts++] = (const void *) opt->arg;
738 /*@=boundswrite@*/
739             }
740             cursor = singleTableUsage(con, fp, cursor, opt->arg,
741                         translation_domain, done);
742         } else if ((opt->longName || opt->shortName) &&
743                  !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
744             cursor = singleOptionUsage(fp, cursor, opt, translation_domain);
745         }
746     }
747     /*@=branchstate@*/
748
749     return cursor;
750 }
751
752 /**
753  * Return concatenated short options for display.
754  * @todo Sub-tables should be recursed.
755  * @param opt           option(s)
756  * @param fp            output file handle
757  * @retval str          concatenation of short options
758  * @return              length of display string
759  */
760 static int showShortOptions(const struct poptOption * opt, FILE * fp,
761                 /*@null@*/ char * str)
762         /*@globals fileSystem @*/
763         /*@modifies *str, *fp, fileSystem @*/
764         /*@requires maxRead(str) >= 0 @*/
765 {
766     /* bufsize larger then the ascii set, lazy alloca on top level call. */
767     char * s = (str != NULL ? str : memset(alloca(300), 0, 300));
768     int len = 0;
769
770     if (s == NULL)
771         return 0;
772
773 /*@-boundswrite@*/
774     if (opt != NULL)
775     for (; (opt->longName || opt->shortName || opt->arg); opt++) {
776         if (opt->shortName && !(opt->argInfo & POPT_ARG_MASK))
777             s[strlen(s)] = opt->shortName;
778         else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE)
779             if (opt->arg)       /* XXX program error */
780                 len = showShortOptions(opt->arg, fp, s);
781     } 
782 /*@=boundswrite@*/
783
784     /* On return to top level, print the short options, return print length. */
785     if (s == str && *s != '\0') {
786         fprintf(fp, " [-%s]", s);
787         len = strlen(s) + sizeof(" [-]")-1;
788     }
789     return len;
790 }
791
792 void poptPrintUsage(poptContext con, FILE * fp, /*@unused@*/ UNUSED(int flags))
793 {
794     poptDone done = memset(alloca(sizeof(*done)), 0, sizeof(*done));
795     size_t cursor;
796
797     done->nopts = 0;
798     done->maxopts = 64;
799     cursor = done->maxopts * sizeof(*done->opts);
800 /*@-boundswrite@*/
801     done->opts = memset(alloca(cursor), 0, cursor);
802     /*@-keeptrans@*/
803     done->opts[done->nopts++] = (const void *) con->options;
804     /*@=keeptrans@*/
805 /*@=boundswrite@*/
806
807     cursor = showHelpIntro(con, fp);
808     cursor += showShortOptions(con->options, fp, NULL);
809     cursor = singleTableUsage(con, fp, cursor, con->options, NULL, done);
810     cursor = itemUsage(fp, cursor, con->aliases, con->numAliases, NULL);
811     cursor = itemUsage(fp, cursor, con->execs, con->numExecs, NULL);
812
813     if (con->otherHelp) {
814         cursor += strlen(con->otherHelp) + 1;
815         if (cursor > 79) fprintf(fp, "\n       ");
816         fprintf(fp, " %s", con->otherHelp);
817     }
818
819     fprintf(fp, "\n");
820 }
821
822 void poptSetOtherOptionHelp(poptContext con, const char * text)
823 {
824     con->otherHelp = _free(con->otherHelp);
825     con->otherHelp = xstrdup(text);
826 }