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