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 */
23static 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@*/
39static int show_option_defaults = 0;
40#endif
41
42/**
43 * Empty table marker to enable displaying popt alias/exec options.
44 */
45/*@observer@*/ /*@unchecked@*/
46struct poptOption poptAliasOptions[] = {
47    POPT_TABLEEND
48};
49
50/**
51 * Auto help table options.
52 */
53/*@-castfcnptr@*/
54/*@observer@*/ /*@unchecked@*/
55struct 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 *
71getTableTranslationDomain(/*@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 *
89getArgDescrip(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 */
125static /*@only@*/ /*@null@*/ char *
126singleOptionDefaultValue(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 */
197static 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);	te += strlen(te);
252		    }
253		    *te++ = ' ';
254		    strcpy(te, defs);
255		    defs = (char *)_free(defs);
256		}
257		defs = t;
258	    }
259	}
260	/*@=branchstate@*/
261
262	if (opt->argDescrip == NULL) {
263	    switch (opt->argInfo & POPT_ARG_MASK) {
264	    case POPT_ARG_NONE:
265		break;
266	    case POPT_ARG_VAL:
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);
271
272		/* Don't bother displaying typical values */
273		if (!ops && (aLong == 0L || aLong == 1L || aLong == -1L))
274		    break;
275		*le++ = '[';
276		switch (ops) {
277		case POPT_ARGFLAG_OR:
278		    *le++ = '|';
279		    /*@innerbreak@*/ break;
280		case POPT_ARGFLAG_AND:
281		    *le++ = '&';
282		    /*@innerbreak@*/ break;
283		case POPT_ARGFLAG_XOR:
284		    *le++ = '^';
285		    /*@innerbreak@*/ break;
286		default:
287		    /*@innerbreak@*/ break;
288		}
289		*le++ = '=';
290		if (negate) *le++ = '~';
291		/*@-formatconst@*/
292		le += sprintf(le, (ops ? "0x%lx" : "%ld"), aLong);
293		/*@=formatconst@*/
294		*le++ = ']';
295	    }
296#endif
297		break;
298	    case POPT_ARG_INT:
299	    case POPT_ARG_LONG:
300	    case POPT_ARG_FLOAT:
301	    case POPT_ARG_DOUBLE:
302	    case POPT_ARG_STRING:
303		*le++ = '=';
304		strcpy(le, argDescrip);		le += strlen(le);
305		break;
306	    default:
307		break;
308	    }
309	} else {
310	    *le++ = '=';
311	    strcpy(le, argDescrip);		le += strlen(le);
312	}
313	if (opt->argInfo & POPT_ARGFLAG_OPTIONAL)
314	    *le++ = ']';
315	*le = '\0';
316    }
317/*@=boundswrite@*/
318
319    if (help)
320	fprintf(fp,"  %-*s   ", maxLeftCol, left);
321    else {
322	fprintf(fp,"  %s\n", left);
323	goto out;
324    }
325
326    left = (char *)_free(left);
327    if (defs) {
328	help = defs; defs = NULL;
329    }
330
331    helpLength = strlen(help);
332/*@-boundsread@*/
333    while (helpLength > lineLength) {
334	const char * ch;
335	char format[16];
336
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--;
341	ch++;
342
343	sprintf(format, "%%.%ds\n%%%ds", (int) (ch - help), indentLength);
344	/*@-formatconst@*/
345	fprintf(fp, format, help, " ");
346	/*@=formatconst@*/
347	help = ch;
348	while (isspace(*help) && *help) help++;
349	helpLength = strlen(help);
350    }
351/*@=boundsread@*/
352
353    if (helpLength) fprintf(fp, "%s\n", help);
354
355out:
356    /*@-dependenttrans@*/
357    defs = (char *)_free(defs);
358    /*@=dependenttrans@*/
359    left = (char *)_free(left);
360}
361
362/**
363 * @param opt		option(s)
364 * @param translation_domain	translation domain
365 */
366static int maxArgWidth(const struct poptOption * opt,
367		       /*@null@*/ const char * translation_domain)
368	/*@*/
369{
370    int max = 0;
371    int len = 0;
372    const char * s;
373
374    if (opt != NULL)
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)) {
381	    len = sizeof("  ")-1;
382	    if (opt->shortName != '\0') len += sizeof("-X")-1;
383	    if (opt->shortName != '\0' && opt->longName) len += sizeof(", ")-1;
384	    if (opt->longName) {
385		len += ((opt->argInfo & POPT_ARGFLAG_ONEDASH)
386			? sizeof("-")-1 : sizeof("--")-1);
387		len += strlen(opt->longName);
388	    }
389
390	    s = getArgDescrip(opt, translation_domain);
391	    if (s)
392		len += sizeof("=")-1 + strlen(s);
393	    if (opt->argInfo & POPT_ARGFLAG_OPTIONAL) len += sizeof("[]")-1;
394	    if (len > max) max = len;
395	}
396
397	opt++;
398    }
399
400    return max;
401}
402
403/**
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
408 * @param left
409 * @param translation_domain	translation domain
410 */
411static 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 @*/
416{
417    poptItem item;
418    int i;
419
420    if (items != NULL)
421    for (i = 0, item = items; i < nitems; i++, item++) {
422	const struct poptOption * opt;
423	opt = &item->option;
424	if ((opt->longName || opt->shortName) &&
425	    !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
426	    singleOptionHelp(fp, left, opt, translation_domain);
427    }
428}
429
430/**
431 * Display help text for a table of options.
432 * @param con		context
433 * @param fp		output file handle
434 * @param table		option(s)
435 * @param left
436 * @param translation_domain	translation domain
437 */
438static 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 @*/
443{
444    const struct poptOption * opt;
445    const char *sub_transdom;
446
447    if (table == poptAliasOptions) {
448	itemHelp(fp, con->aliases, con->numAliases, left, NULL);
449	itemHelp(fp, con->execs, con->numExecs, left, NULL);
450	return;
451    }
452
453    if (table != 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);
458    }
459
460    if (table != NULL)
461    for (opt = table; (opt->longName || opt->shortName || opt->arg); opt++) {
462	if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_INCLUDE_TABLE)
463	    continue;
464	sub_transdom = getTableTranslationDomain(
465	    (const struct poptOption *)opt->arg);
466	if (sub_transdom == NULL)
467	    sub_transdom = translation_domain;
468
469	if (opt->descrip)
470	    fprintf(fp, "\n%s\n", D_(sub_transdom, opt->descrip));
471
472	singleTableHelp(con, fp, (const struct poptOption *)opt->arg, left, sub_transdom);
473    }
474}
475
476/**
477 * @param con		context
478 * @param fp		output file handle
479 */
480static int showHelpIntro(poptContext con, FILE * fp)
481	/*@globals fileSystem @*/
482	/*@modifies *fp, fileSystem @*/
483{
484    int len = 6;
485    const char * fn;
486
487    fprintf(fp, POPT_("Usage:"));
488    if (!(con->flags & POPT_CONTEXT_KEEP_FIRST)) {
489/*@-boundsread@*/
490	/*@-nullderef@*/	/* LCL: wazzup? */
491	fn = con->optionStack->argv[0];
492	/*@=nullderef@*/
493/*@=boundsread@*/
494	if (fn == NULL) return len;
495	if (strchr(fn, '/')) fn = strrchr(fn, '/') + 1;
496	fprintf(fp, " %s", fn);
497	len += strlen(fn) + 1;
498    }
499
500    return len;
501}
502
503void poptPrintHelp(poptContext con, FILE * fp, /*@unused@*/ int flags)
504{
505    int leftColWidth;
506
507    (void) showHelpIntro(con, fp);
508    if (con->otherHelp)
509	fprintf(fp, " %s\n", con->otherHelp);
510    else
511	fprintf(fp, " %s\n", POPT_("[OPTION...]"));
512
513    leftColWidth = maxArgWidth(con->options, NULL);
514    singleTableHelp(con, fp, con->options, leftColWidth, NULL);
515}
516
517/**
518 * @param fp		output file handle
519 * @param cursor
520 * @param opt		option(s)
521 * @param translation_domain	translation domain
522 */
523static int singleOptionUsage(FILE * fp, int cursor,
524		const struct poptOption * opt,
525		/*@null@*/ const char *translation_domain)
526	/*@globals fileSystem @*/
527	/*@modifies *fp, fileSystem @*/
528{
529    int len = 4;
530    char shortStr[2] = { '\0', '\0' };
531    const char * item = shortStr;
532    const char * argDescrip = getArgDescrip(opt, translation_domain);
533
534    if (opt->shortName != '\0' && opt->longName != NULL) {
535	len += 2;
536	if (!(opt->argInfo & POPT_ARGFLAG_ONEDASH)) len++;
537	len += strlen(opt->longName);
538    } else if (opt->shortName != '\0') {
539	len++;
540	shortStr[0] = opt->shortName;
541	shortStr[1] = '\0';
542    } else if (opt->longName) {
543	len += strlen(opt->longName);
544	if (!(opt->argInfo & POPT_ARGFLAG_ONEDASH)) len++;
545	item = opt->longName;
546    }
547
548    if (len == 4) return cursor;
549
550    if (argDescrip)
551	len += strlen(argDescrip) + 1;
552
553    if ((cursor + len) > 79) {
554	fprintf(fp, "\n       ");
555	cursor = 7;
556    }
557
558    if (opt->longName && opt->shortName) {
559	fprintf(fp, " [-%c|-%s%s%s%s]",
560	    opt->shortName, ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "" : "-"),
561	    opt->longName,
562	    (argDescrip ? " " : ""),
563	    (argDescrip ? argDescrip : ""));
564    } else {
565	fprintf(fp, " [-%s%s%s%s]",
566	    ((opt->shortName || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) ? "" : "-"),
567	    item,
568	    (argDescrip ? (opt->shortName != '\0' ? " " : "=") : ""),
569	    (argDescrip ? argDescrip : ""));
570    }
571
572    return cursor + len + 1;
573}
574
575/**
576 * Display popt alias and exec usage.
577 * @param fp		output file handle
578 * @param cursor
579 * @param item		alias/exec array
580 * @param nitems	no. of ara/exec entries
581 * @param translation_domain	translation domain
582 */
583static int itemUsage(FILE * fp, int cursor, poptItem item, int nitems,
584		/*@null@*/ const char * translation_domain)
585	/*@globals fileSystem @*/
586	/*@modifies *fp, fileSystem @*/
587{
588    int i;
589
590    /*@-branchstate@*/		/* FIX: W2DO? */
591    if (item != NULL)
592    for (i = 0; i < nitems; i++, item++) {
593	const struct poptOption * opt;
594	opt = &item->option;
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);
600	}
601    }
602    /*@=branchstate@*/
603
604    return cursor;
605}
606
607/**
608 * Keep track of option tables already processed.
609 */
610typedef struct poptDone_s {
611    int nopts;
612    int maxopts;
613    const void ** opts;
614} * poptDone;
615
616/**
617 * Display usage text for a table of options.
618 * @param con		context
619 * @param fp		output file handle
620 * @param cursor
621 * @param opt		option(s)
622 * @param translation_domain	translation domain
623 * @param done		tables already processed
624 * @return
625 */
626static 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 @*/
632{
633    /*@-branchstate@*/		/* FIX: W2DO? */
634    if (opt != NULL)
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) {
639	    if (done) {
640		int i = 0;
641		for (i = 0; i < done->nopts; i++) {
642/*@-boundsread@*/
643		    const void * that = done->opts[i];
644/*@=boundsread@*/
645		    if (that == NULL || that != opt->arg)
646			/*@innercontinue@*/ continue;
647		    /*@innerbreak@*/ break;
648		}
649		/* Skip if this table has already been processed. */
650		if (opt->arg == NULL || i < done->nopts)
651		    continue;
652/*@-boundswrite@*/
653		if (done->nopts < done->maxopts)
654		    done->opts[done->nopts++] = (const void *) opt->arg;
655/*@=boundswrite@*/
656	    }
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);
662	}
663    }
664    /*@=branchstate@*/
665
666    return cursor;
667}
668
669/**
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
676 */
677static 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 @*/
682{
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));
685    int len = 0;
686
687/*@-boundswrite@*/
688    if (opt != NULL)
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);
696    }
697/*@=boundswrite@*/
698
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;
703    }
704    return len;
705}
706
707void poptPrintUsage(poptContext con, FILE * fp, /*@unused@*/ int flags)
708{
709    poptDone done = (poptDone)memset(alloca(sizeof(*done)), 0, sizeof(*done));
710    int cursor;
711
712    done->nopts = 0;
713    done->maxopts = 64;
714    cursor = done->maxopts * sizeof(*done->opts);
715/*@-boundswrite@*/
716    done->opts = (const void **)memset(alloca(cursor), 0, cursor);
717    done->opts[done->nopts++] = (const void *) con->options;
718/*@=boundswrite@*/
719
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);
725
726    if (con->otherHelp) {
727	cursor += strlen(con->otherHelp) + 1;
728	if (cursor > 79) fprintf(fp, "\n       ");
729	fprintf(fp, " %s", con->otherHelp);
730    }
731
732    fprintf(fp, "\n");
733}
734
735void poptSetOtherOptionHelp(poptContext con, const char * text)
736{
737    con->otherHelp = (const char *)_free(con->otherHelp);
738    con->otherHelp = xstrdup(text);
739}
740/*@=type@*/
741