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 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 = 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 = _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 = 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 = 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 = _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 = _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 = _free(defs);
358    /*@=dependenttrans@*/
359    left = _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(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(opt->arg);
465	if (sub_transdom == NULL)
466	    sub_transdom = translation_domain;
467
468	if (opt->descrip)
469	    fprintf(fp, "\n%s\n", D_(sub_transdom, opt->descrip));
470
471	singleTableHelp(con, fp, opt->arg, left, sub_transdom);
472    }
473}
474
475/**
476 * @param con		context
477 * @param fp		output file handle
478 */
479static int showHelpIntro(poptContext con, FILE * fp)
480	/*@globals fileSystem @*/
481	/*@modifies *fp, fileSystem @*/
482{
483    int len = 6;
484    const char * fn;
485
486    fprintf(fp, POPT_("Usage:"));
487    if (!(con->flags & POPT_CONTEXT_KEEP_FIRST)) {
488/*@-boundsread@*/
489	/*@-nullderef@*/	/* LCL: wazzup? */
490	fn = con->optionStack->argv[0];
491	/*@=nullderef@*/
492/*@=boundsread@*/
493	if (fn == NULL) return len;
494	if (strchr(fn, '/')) fn = strrchr(fn, '/') + 1;
495	fprintf(fp, " %s", fn);
496	len += strlen(fn) + 1;
497    }
498
499    return len;
500}
501
502void poptPrintHelp(poptContext con, FILE * fp, /*@unused@*/ int flags)
503{
504    int leftColWidth;
505
506    (void) showHelpIntro(con, fp);
507    if (con->otherHelp)
508	fprintf(fp, " %s\n", con->otherHelp);
509    else
510	fprintf(fp, " %s\n", POPT_("[OPTION...]"));
511
512    leftColWidth = maxArgWidth(con->options, NULL);
513    singleTableHelp(con, fp, con->options, leftColWidth, NULL);
514}
515
516/**
517 * @param fp		output file handle
518 * @param cursor
519 * @param opt		option(s)
520 * @param translation_domain	translation domain
521 */
522static int singleOptionUsage(FILE * fp, int cursor,
523		const struct poptOption * opt,
524		/*@null@*/ const char *translation_domain)
525	/*@globals fileSystem @*/
526	/*@modifies *fp, fileSystem @*/
527{
528    int len = 4;
529    char shortStr[2] = { '\0', '\0' };
530    const char * item = shortStr;
531    const char * argDescrip = getArgDescrip(opt, translation_domain);
532
533    if (opt->shortName != '\0' && opt->longName != NULL) {
534	len += 2;
535	if (!(opt->argInfo & POPT_ARGFLAG_ONEDASH)) len++;
536	len += strlen(opt->longName);
537    } else if (opt->shortName != '\0') {
538	len++;
539	shortStr[0] = opt->shortName;
540	shortStr[1] = '\0';
541    } else if (opt->longName) {
542	len += strlen(opt->longName);
543	if (!(opt->argInfo & POPT_ARGFLAG_ONEDASH)) len++;
544	item = opt->longName;
545    }
546
547    if (len == 4) return cursor;
548
549    if (argDescrip)
550	len += strlen(argDescrip) + 1;
551
552    if ((cursor + len) > 79) {
553	fprintf(fp, "\n       ");
554	cursor = 7;
555    }
556
557    if (opt->longName && opt->shortName) {
558	fprintf(fp, " [-%c|-%s%s%s%s]",
559	    opt->shortName, ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "" : "-"),
560	    opt->longName,
561	    (argDescrip ? " " : ""),
562	    (argDescrip ? argDescrip : ""));
563    } else {
564	fprintf(fp, " [-%s%s%s%s]",
565	    ((opt->shortName || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) ? "" : "-"),
566	    item,
567	    (argDescrip ? (opt->shortName != '\0' ? " " : "=") : ""),
568	    (argDescrip ? argDescrip : ""));
569    }
570
571    return cursor + len + 1;
572}
573
574/**
575 * Display popt alias and exec usage.
576 * @param fp		output file handle
577 * @param cursor
578 * @param item		alias/exec array
579 * @param nitems	no. of ara/exec entries
580 * @param translation_domain	translation domain
581 */
582static int itemUsage(FILE * fp, int cursor, poptItem item, int nitems,
583		/*@null@*/ const char * translation_domain)
584	/*@globals fileSystem @*/
585	/*@modifies *fp, fileSystem @*/
586{
587    int i;
588
589    /*@-branchstate@*/		/* FIX: W2DO? */
590    if (item != NULL)
591    for (i = 0; i < nitems; i++, item++) {
592	const struct poptOption * opt;
593	opt = &item->option;
594        if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN) {
595	    translation_domain = (const char *)opt->arg;
596	} else if ((opt->longName || opt->shortName) &&
597		 !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
598	    cursor = singleOptionUsage(fp, cursor, opt, translation_domain);
599	}
600    }
601    /*@=branchstate@*/
602
603    return cursor;
604}
605
606/**
607 * Keep track of option tables already processed.
608 */
609typedef struct poptDone_s {
610    int nopts;
611    int maxopts;
612    const void ** opts;
613} * poptDone;
614
615/**
616 * Display usage text for a table of options.
617 * @param con		context
618 * @param fp		output file handle
619 * @param cursor
620 * @param opt		option(s)
621 * @param translation_domain	translation domain
622 * @param done		tables already processed
623 * @return
624 */
625static int singleTableUsage(poptContext con, FILE * fp, int cursor,
626		/*@null@*/ const struct poptOption * opt,
627		/*@null@*/ const char * translation_domain,
628		/*@null@*/ poptDone done)
629	/*@globals fileSystem @*/
630	/*@modifies *fp, done, fileSystem @*/
631{
632    /*@-branchstate@*/		/* FIX: W2DO? */
633    if (opt != NULL)
634    for (; (opt->longName || opt->shortName || opt->arg) ; opt++) {
635        if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN) {
636	    translation_domain = (const char *)opt->arg;
637	} else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
638	    if (done) {
639		int i = 0;
640		for (i = 0; i < done->nopts; i++) {
641/*@-boundsread@*/
642		    const void * that = done->opts[i];
643/*@=boundsread@*/
644		    if (that == NULL || that != opt->arg)
645			/*@innercontinue@*/ continue;
646		    /*@innerbreak@*/ break;
647		}
648		/* Skip if this table has already been processed. */
649		if (opt->arg == NULL || i < done->nopts)
650		    continue;
651/*@-boundswrite@*/
652		if (done->nopts < done->maxopts)
653		    done->opts[done->nopts++] = (const void *) opt->arg;
654/*@=boundswrite@*/
655	    }
656	    cursor = singleTableUsage(con, fp, cursor, opt->arg,
657			translation_domain, done);
658	} else if ((opt->longName || opt->shortName) &&
659		 !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
660	    cursor = singleOptionUsage(fp, cursor, opt, translation_domain);
661	}
662    }
663    /*@=branchstate@*/
664
665    return cursor;
666}
667
668/**
669 * Return concatenated short options for display.
670 * @todo Sub-tables should be recursed.
671 * @param opt		option(s)
672 * @param fp		output file handle
673 * @retval str		concatenation of short options
674 * @return		length of display string
675 */
676static int showShortOptions(const struct poptOption * opt, FILE * fp,
677		/*@null@*/ char * str)
678	/*@globals fileSystem @*/
679	/*@modifies *str, *fp, fileSystem @*/
680	/*@requires maxRead(str) >= 0 @*/
681{
682    /* bufsize larger then the ascii set, lazy alloca on top level call. */
683    char * s = (str != NULL ? str : memset(alloca(300), 0, 300));
684    int len = 0;
685
686/*@-boundswrite@*/
687    if (opt != NULL)
688    for (; (opt->longName || opt->shortName || opt->arg); opt++) {
689	if (opt->shortName && !(opt->argInfo & POPT_ARG_MASK))
690	    s[strlen(s)] = opt->shortName;
691	else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE)
692	    if (opt->arg)	/* XXX program error */
693		len = showShortOptions(opt->arg, fp, s);
694    }
695/*@=boundswrite@*/
696
697    /* On return to top level, print the short options, return print length. */
698    if (s == str && *s != '\0') {
699	fprintf(fp, " [-%s]", s);
700	len = strlen(s) + sizeof(" [-]")-1;
701    }
702    return len;
703}
704
705void poptPrintUsage(poptContext con, FILE * fp, /*@unused@*/ int flags)
706{
707    poptDone done = memset(alloca(sizeof(*done)), 0, sizeof(*done));
708    int cursor;
709
710    done->nopts = 0;
711    done->maxopts = 64;
712    cursor = done->maxopts * sizeof(*done->opts);
713/*@-boundswrite@*/
714    done->opts = memset(alloca(cursor), 0, cursor);
715    done->opts[done->nopts++] = (const void *) con->options;
716/*@=boundswrite@*/
717
718    cursor = showHelpIntro(con, fp);
719    cursor += showShortOptions(con->options, fp, NULL);
720    cursor = singleTableUsage(con, fp, cursor, con->options, NULL, done);
721    cursor = itemUsage(fp, cursor, con->aliases, con->numAliases, NULL);
722    cursor = itemUsage(fp, cursor, con->execs, con->numExecs, NULL);
723
724    if (con->otherHelp) {
725	cursor += strlen(con->otherHelp) + 1;
726	if (cursor > 79) fprintf(fp, "\n       ");
727	fprintf(fp, " %s", con->otherHelp);
728    }
729
730    fprintf(fp, "\n");
731}
732
733void poptSetOtherOptionHelp(poptContext con, const char * text)
734{
735    con->otherHelp = _free(con->otherHelp);
736    con->otherHelp = xstrdup(text);
737}
738/*@=type@*/
739