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