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 #include <stdint.h>
12 
13 #include "system.h"
14 #include "poptint.h"
15 
16 /**
17  * Display arguments.
18  * @param con		context
19  * @param foo		(unused)
20  * @param key		option(s)
21  * @param arg		(unused)
22  * @param data		(unused)
23  */
displayArgs(poptContext con,enum poptCallbackReason foo,struct poptOption * key,const char * arg,void * data)24 static void displayArgs(poptContext con,
25 		/*@unused@*/ enum poptCallbackReason foo,
26 		struct poptOption * key,
27 		/*@unused@*/ const char * arg, /*@unused@*/ void * data)
28 	/*@globals fileSystem@*/
29 	/*@modifies fileSystem@*/
30 {
31     if (key->shortName == '?')
32 	poptPrintHelp(con, stdout, 0);
33     else
34 	poptPrintUsage(con, stdout, 0);
35     exit(0);
36 }
37 
38 #ifdef	NOTYET
39 /*@unchecked@*/
40 static int show_option_defaults = 0;
41 #endif
42 
43 /**
44  * Empty table marker to enable displaying popt alias/exec options.
45  */
46 /*@observer@*/ /*@unchecked@*/
47 struct poptOption poptAliasOptions[] = {
48     POPT_TABLEEND
49 };
50 
51 /**
52  * Auto help table options.
53  */
54 /*@-castfcnptr@*/
55 /*@observer@*/ /*@unchecked@*/
56 struct poptOption poptHelpOptions[] = {
57   { NULL, '\0', POPT_ARG_CALLBACK, (void *)&displayArgs, '\0', NULL, NULL },
58   { "help", '?', 0, NULL, '?', N_("Show this help message"), NULL },
59   { "usage", '\0', 0, NULL, 'u', N_("Display brief usage message"), NULL },
60 #ifdef	NOTYET
61   { "defaults", '\0', POPT_ARG_NONE, &show_option_defaults, 0,
62 	N_("Display option defaults in message"), NULL },
63 #endif
64     POPT_TABLEEND
65 } ;
66 /*@=castfcnptr@*/
67 
68 /**
69  * @param table		option(s)
70  */
71 /*@observer@*/ /*@null@*/ static const char *
getTableTranslationDomain(const struct poptOption * table)72 getTableTranslationDomain(/*@null@*/ const struct poptOption *table)
73 	/*@*/
74 {
75     const struct poptOption *opt;
76 
77     if (table != NULL)
78     for (opt = table; opt->longName || opt->shortName || opt->arg; opt++) {
79 	if (opt->argInfo == POPT_ARG_INTL_DOMAIN)
80 	    return opt->arg;
81     }
82     return NULL;
83 }
84 
85 /**
86  * @param opt		option(s)
87  * @param translation_domain	translation domain
88  */
89 /*@observer@*/ /*@null@*/ static const char *
getArgDescrip(const struct poptOption * opt,const char * translation_domain)90 getArgDescrip(const struct poptOption * opt,
91 		/*@-paramuse@*/ /* FIX: i18n macros disabled with lclint */
92 		/*@null@*/ const char * translation_domain)
93 		/*@=paramuse@*/
94 	/*@*/
95 {
96     if (!(opt->argInfo & POPT_ARG_MASK)) return NULL;
97 
98     if (opt == (poptHelpOptions + 1) || opt == (poptHelpOptions + 2))
99 	if (opt->argDescrip) return POPT_(opt->argDescrip);
100 
101     if (opt->argDescrip) return D_(translation_domain, opt->argDescrip);
102 
103     switch (opt->argInfo & POPT_ARG_MASK) {
104     case POPT_ARG_NONE:		return POPT_("NONE");
105 #ifdef	DYING
106     case POPT_ARG_VAL:		return POPT_("VAL");
107 #else
108     case POPT_ARG_VAL:		return NULL;
109 #endif
110     case POPT_ARG_INT:		return POPT_("INT");
111     case POPT_ARG_LONG:		return POPT_("LONG");
112     case POPT_ARG_STRING:	return POPT_("STRING");
113     case POPT_ARG_FLOAT:	return POPT_("FLOAT");
114     case POPT_ARG_DOUBLE:	return POPT_("DOUBLE");
115     default:			return POPT_("ARG");
116     }
117 }
118 
119 /**
120  * Display default value for an option.
121  * @param lineLength
122  * @param opt		option(s)
123  * @param translation_domain	translation domain
124  * @return
125  */
126 static /*@only@*/ /*@null@*/ char *
singleOptionDefaultValue(int lineLength,const struct poptOption * opt,const char * translation_domain)127 singleOptionDefaultValue(int lineLength,
128 		const struct poptOption * opt,
129 		/*@-paramuse@*/ /* FIX: i18n macros disabled with lclint */
130 		/*@null@*/ const char * translation_domain)
131 		/*@=paramuse@*/
132 	/*@*/
133 {
134     const char * defstr = D_(translation_domain, "default");
135     char * le = malloc(4*lineLength + 1);
136     char * l = le;
137 
138     if (le == NULL) return NULL;	/* XXX can't happen */
139 /*@-boundswrite@*/
140     *le = '\0';
141     *le++ = '(';
142     strcpy(le, defstr);	le += strlen(le);
143     *le++ = ':';
144     *le++ = ' ';
145     if (opt->arg)	/* XXX programmer error */
146     switch (opt->argInfo & POPT_ARG_MASK) {
147     case POPT_ARG_VAL:
148     case POPT_ARG_INT:
149     {	long aLong = *((int *)opt->arg);
150 	le += sprintf(le, "%ld", aLong);
151     }	break;
152     case POPT_ARG_LONG:
153     {	long aLong = *((long *)opt->arg);
154 	le += sprintf(le, "%ld", aLong);
155     }	break;
156     case POPT_ARG_FLOAT:
157     {	double aDouble = *((float *)opt->arg);
158 	le += sprintf(le, "%g", aDouble);
159     }	break;
160     case POPT_ARG_DOUBLE:
161     {	double aDouble = *((double *)opt->arg);
162 	le += sprintf(le, "%g", aDouble);
163     }	break;
164     case POPT_ARG_STRING:
165     {	const char * s = *(const char **)opt->arg;
166 	if (s == NULL) {
167 	    strcpy(le, "null");	le += strlen(le);
168 	} else {
169 	    size_t slen = 4*lineLength - (le - l) - sizeof("\"...\")");
170 	    *le++ = '"';
171 	    strncpy(le, s, slen); le[slen] = '\0'; le += strlen(le);
172 	    if (slen < strlen(s)) {
173 		strcpy(le, "...");	le += strlen(le);
174 	    }
175 	    *le++ = '"';
176 	}
177     }	break;
178     case POPT_ARG_NONE:
179     default:
180 	l = _free(l);
181 	return NULL;
182 	/*@notreached@*/ break;
183     }
184     *le++ = ')';
185     *le = '\0';
186 /*@=boundswrite@*/
187 
188     return l;
189 }
190 
191 /**
192  * Display help text for an option.
193  * @param fp		output file handle
194  * @param maxLeftCol
195  * @param opt		option(s)
196  * @param translation_domain	translation domain
197  */
singleOptionHelp(FILE * fp,int maxLeftCol,const struct poptOption * opt,const char * translation_domain)198 static void singleOptionHelp(FILE * fp, int maxLeftCol,
199 		const struct poptOption * opt,
200 		/*@null@*/ const char * translation_domain)
201 	/*@globals fileSystem @*/
202 	/*@modifies *fp, fileSystem @*/
203 {
204     int indentLength = maxLeftCol + 5;
205     int lineLength = 79 - indentLength;
206     const char * help = D_(translation_domain, opt->descrip);
207     const char * argDescrip = getArgDescrip(opt, translation_domain);
208     int helpLength;
209     char * defs = NULL;
210     char * left;
211     int nb = maxLeftCol + 1;
212 
213     /* Make sure there's more than enough room in target buffer. */
214     if (opt->longName)	nb += strlen(opt->longName);
215     if (argDescrip)	nb += strlen(argDescrip);
216 
217 /*@-boundswrite@*/
218     left = malloc(nb);
219     if (left == NULL) return;	/* XXX can't happen */
220     left[0] = '\0';
221     left[maxLeftCol] = '\0';
222 
223     if (opt->longName && opt->shortName)
224 	sprintf(left, "-%c, %s%s", opt->shortName,
225 		((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"),
226 		opt->longName);
227     else if (opt->shortName != '\0')
228 	sprintf(left, "-%c", opt->shortName);
229     else if (opt->longName)
230 	sprintf(left, "%s%s",
231 		((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"),
232 		opt->longName);
233     if (!*left) goto out;
234 
235     if (argDescrip) {
236 	char * le = left + strlen(left);
237 
238 	if (opt->argInfo & POPT_ARGFLAG_OPTIONAL)
239 	    *le++ = '[';
240 
241 	/* Choose type of output */
242 	/*@-branchstate@*/
243 	if (opt->argInfo & POPT_ARGFLAG_SHOW_DEFAULT) {
244 	    defs = singleOptionDefaultValue(lineLength, opt, translation_domain);
245 	    if (defs) {
246 		char * t = malloc((help ? strlen(help) : 0) +
247 				strlen(defs) + sizeof(" "));
248 		if (t) {
249 		    char * te = t;
250 		    *te = '\0';
251 		    if (help) {
252 			strcpy(te, help);	te += strlen(te);
253 		    }
254 		    *te++ = ' ';
255 		    strcpy(te, defs);
256 		    defs = _free(defs);
257 		}
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 = _free(left);
328     if (defs) {
329 	help = defs; defs = NULL;
330     }
331 
332     helpLength = strlen(help);
333 /*@-boundsread@*/
334     while (helpLength > lineLength) {
335 	const char * ch;
336 	char format[30];
337 
338 	ch = help + lineLength - 1;
339 	while (ch > help && !isspace((uint8_t)*ch)) ch--;
340 	if (ch == help) break;		/* give up */
341 	while (ch > (help + 1) && isspace((uint8_t)*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((uint8_t)*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 = _free(defs);
359     /*@=dependenttrans@*/
360     left = _free(left);
361 }
362 
363 /**
364  * @param opt		option(s)
365  * @param translation_domain	translation domain
366  */
maxArgWidth(const struct poptOption * opt,const char * translation_domain)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(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  */
itemHelp(FILE * fp,poptItem items,int nitems,int left,const char * translation_domain)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  */
singleTableHelp(poptContext con,FILE * fp,const struct poptOption * table,int left,const char * translation_domain)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(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, opt->arg, left, sub_transdom);
473     }
474 }
475 
476 /**
477  * @param con		context
478  * @param fp		output file handle
479  */
showHelpIntro(poptContext con,FILE * fp)480 static 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 
poptPrintHelp(poptContext con,FILE * fp,int flags)503 void 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  */
singleOptionUsage(FILE * fp,int cursor,const struct poptOption * opt,const char * translation_domain)523 static 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  */
itemUsage(FILE * fp,int cursor,poptItem item,int nitems,const char * translation_domain)583 static 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  */
610 typedef 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  */
singleTableUsage(poptContext con,FILE * fp,int cursor,const struct poptOption * opt,const char * translation_domain,poptDone done)626 static 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, 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  */
showShortOptions(const struct poptOption * opt,FILE * fp,char * str)677 static int showShortOptions(const struct poptOption * opt, FILE * fp,
678 		/*@null@*/ char * str)
679 	/*@globals fileSystem @*/
680 	/*@modifies *str, *fp, fileSystem @*/
681 {
682     size_t size = 300;	/* larger then the ascii set */
683     char * s = alloca(size);
684 
685     s[0] = '\0';
686     /*@-branchstate@*/		/* FIX: W2DO? */
687     if (str == NULL) {
688 	memset(s, 0, size);
689 	str = s;
690     }
691     /*@=branchstate@*/
692 
693 /*@-boundswrite@*/
694     if (opt != NULL)
695     for (; (opt->longName || opt->shortName || opt->arg); opt++) {
696 	if (opt->shortName && !(opt->argInfo & POPT_ARG_MASK))
697 	    str[strlen(str)] = opt->shortName;
698 	else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE)
699 	    if (opt->arg)	/* XXX program error */
700 		(void) showShortOptions(opt->arg, fp, str);
701     }
702 /*@=boundswrite@*/
703 
704     if (s != str || *s != '\0')
705 	return 0;
706 
707     fprintf(fp, " [-%s]", s);
708     return strlen(s) + 4;
709 }
710 
poptPrintUsage(poptContext con,FILE * fp,int flags)711 void poptPrintUsage(poptContext con, FILE * fp, /*@unused@*/ int flags)
712 {
713     poptDone done = memset(alloca(sizeof(*done)), 0, sizeof(*done));
714     int cursor;
715 
716     done->nopts = 0;
717     done->maxopts = 64;
718     cursor = done->maxopts * sizeof(*done->opts);
719 /*@-boundswrite@*/
720     done->opts = memset(alloca(cursor), 0, cursor);
721     done->opts[done->nopts++] = (const void *) con->options;
722 /*@=boundswrite@*/
723 
724     cursor = showHelpIntro(con, fp);
725     cursor += showShortOptions(con->options, fp, NULL);
726     cursor = singleTableUsage(con, fp, cursor, con->options, NULL, done);
727     cursor = itemUsage(fp, cursor, con->aliases, con->numAliases, NULL);
728     cursor = itemUsage(fp, cursor, con->execs, con->numExecs, NULL);
729 
730     if (con->otherHelp) {
731 	cursor += strlen(con->otherHelp) + 1;
732 	if (cursor > 79) fprintf(fp, "\n       ");
733 	fprintf(fp, " %s", con->otherHelp);
734     }
735 
736     fprintf(fp, "\n");
737 }
738 
poptSetOtherOptionHelp(poptContext con,const char * text)739 void poptSetOtherOptionHelp(poptContext con, const char * text)
740 {
741     con->otherHelp = _free(con->otherHelp);
742     con->otherHelp = xstrdup(text);
743 }
744 /*@=type@*/
745