• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 
3 /** \ingroup popt
4  * @file
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_USE_TIOCGWINSZ
14 #ifdef POPT_USE_TIOCGWINSZ
15 #include <sys/ioctl.h>
16 #endif
17 
18 #ifdef HAVE_MBSRTOWCS
19 #include <wchar.h>			/* for mbsrtowcs */
20 #endif
21 #include "poptint.h"
22 
23 
24 /**
25  * Display arguments.
26  * @param con		context
27  * @param foo		(unused)
28  * @param key		option(s)
29  * @param arg		(unused)
30  * @param data		(unused)
31  */
32 NORETURN
displayArgs(poptContext con,UNUSED (enum poptCallbackReason foo),struct poptOption * key,UNUSED (const char * arg),UNUSED (void * data))33 static void displayArgs(poptContext con,
34 		UNUSED(enum poptCallbackReason foo),
35 		struct poptOption * key,
36 		UNUSED(const char * arg),
37 		UNUSED(void * data))
38 {
39     if (key->shortName == '?')
40 	poptPrintHelp(con, stdout, 0);
41     else
42 	poptPrintUsage(con, stdout, 0);
43 
44     poptFreeContext(con);
45     exit(0);
46 }
47 
48 #ifdef	NOTYET
49 static int show_option_defaults = 0;
50 #endif
51 
52 /**
53  * Empty table marker to enable displaying popt alias/exec options.
54  */
55 struct poptOption poptAliasOptions[] = {
56     POPT_TABLEEND
57 };
58 
59 /**
60  * Auto help table options.
61  */
62 struct poptOption poptHelpOptions[] = {
63   { NULL, '\0', POPT_ARG_CALLBACK, (void *)displayArgs, 0, NULL, NULL },
64   { "help", '?', 0, NULL, (int)'?', N_("Show this help message"), NULL },
65   { "usage", '\0', 0, NULL, (int)'u', N_("Display brief usage message"), NULL },
66     POPT_TABLEEND
67 } ;
68 
69 static struct poptOption poptHelpOptions2[] = {
70   { NULL, '\0', POPT_ARG_INTL_DOMAIN, (void *)PACKAGE, 0, NULL, NULL},
71   { NULL, '\0', POPT_ARG_CALLBACK, (void *)displayArgs, 0, NULL, NULL },
72   { "help", '?', 0, NULL, (int)'?', N_("Show this help message"), NULL },
73   { "usage", '\0', 0, NULL, (int)'u', N_("Display brief usage message"), NULL },
74 #ifdef	NOTYET
75   { "defaults", '\0', POPT_ARG_NONE, &show_option_defaults, 0,
76 	N_("Display option defaults in message"), NULL },
77 #endif
78   { NULL, '\0',	0, NULL, 0, N_("Terminate options"), NULL },
79     POPT_TABLEEND
80 } ;
81 
82 struct poptOption * poptHelpOptionsI18N = poptHelpOptions2;
83 
84 #define        _POPTHELP_MAXLINE       ((size_t)79)
85 
86 typedef struct columns_s {
87     size_t cur;
88     size_t max;
89 } * columns_t;
90 
91 /**
92  * Return no. of columns in output window.
93  * @param fp           FILE
94  * @return             no. of columns
95  */
maxColumnWidth(FILE * fp)96 static size_t maxColumnWidth(FILE *fp)
97 {
98     size_t maxcols = _POPTHELP_MAXLINE;
99 #if defined(TIOCGWINSZ)
100     struct winsize ws;
101     int fdno = fileno(fp ? fp : stdout);
102 
103     memset(&ws, 0, sizeof(ws));
104     if (fdno >= 0 && !ioctl(fdno, (unsigned long)TIOCGWINSZ, &ws)) {
105 	size_t ws_col = (size_t)ws.ws_col;
106 	if (ws_col > maxcols && ws_col < (size_t)256)
107 	    maxcols = ws_col - 1;
108     }
109 #endif
110     return maxcols;
111 }
112 
113 /**
114  * Determine number of display characters in a string.
115  * @param s		string
116  * @return		no. of display characters.
117  */
stringDisplayWidth(const char * s)118 static inline size_t stringDisplayWidth(const char *s)
119 {
120     size_t n = strlen(s);
121 #ifdef HAVE_MBSRTOWCS
122     mbstate_t t;
123 
124     memset ((void *)&t, 0, sizeof (t));	/* In initial state.  */
125     /* Determine number of display characters.  */
126     n = mbsrtowcs (NULL, &s, n, &t);
127 #else
128     n = 0;
129     for (; *s; s = POPT_next_char(s))
130 	n++;
131 #endif
132 
133     return n;
134 }
135 
136 /**
137  * @param opt		option(s)
138  */
139 static const char *
getTableTranslationDomain(const struct poptOption * opt)140 getTableTranslationDomain(const struct poptOption *opt)
141 {
142     if (opt != NULL)
143     for (; opt->longName || opt->shortName || opt->arg; opt++) {
144 	if (opt->argInfo == POPT_ARG_INTL_DOMAIN)
145 	    return opt->arg;
146     }
147     return NULL;
148 }
149 
150 /**
151  * @param opt		option(s)
152  * @param translation_domain	translation domain
153  */
154 static const char *
getArgDescrip(const struct poptOption * opt,const char * translation_domain)155 getArgDescrip(const struct poptOption * opt,
156 		/* FIX: i18n macros disabled with lclint */
157 		const char * translation_domain)
158 {
159     if (!poptArgType(opt)) return NULL;
160 
161     if (poptArgType(opt) == POPT_ARG_MAINCALL)
162 	return opt->argDescrip;
163     if (poptArgType(opt) == POPT_ARG_ARGV)
164 	return opt->argDescrip;
165 
166     if (opt->argDescrip) {
167 	/* Some strings need popt library, not application, i18n domain. */
168 	if (opt == (poptHelpOptions + 1)
169 	 || opt == (poptHelpOptions + 2)
170 	 || !strcmp(opt->argDescrip, N_("Help options:"))
171 	 || !strcmp(opt->argDescrip, N_("Options implemented via popt alias/exec:")))
172 	    return POPT_(opt->argDescrip);
173 
174 	/* Use the application i18n domain. */
175 	return D_(translation_domain, opt->argDescrip);
176     }
177 
178     switch (poptArgType(opt)) {
179     case POPT_ARG_NONE:		return POPT_("NONE");
180 #ifdef	DYING
181     case POPT_ARG_VAL:		return POPT_("VAL");
182 #else
183     case POPT_ARG_VAL:		return NULL;
184 #endif
185     case POPT_ARG_INT:		return POPT_("INT");
186     case POPT_ARG_SHORT:	return POPT_("SHORT");
187     case POPT_ARG_LONG:		return POPT_("LONG");
188     case POPT_ARG_LONGLONG:	return POPT_("LONGLONG");
189     case POPT_ARG_STRING:	return POPT_("STRING");
190     case POPT_ARG_FLOAT:	return POPT_("FLOAT");
191     case POPT_ARG_DOUBLE:	return POPT_("DOUBLE");
192     case POPT_ARG_MAINCALL:	return NULL;
193     case POPT_ARG_ARGV:		return NULL;
194     default:			return POPT_("ARG");
195     }
196 }
197 
198 /**
199  * Display default value for an option.
200  * @param lineLength	display positions remaining
201  * @param opt		option(s)
202  * @param translation_domain	translation domain
203  * @return
204  */
205 static char *
singleOptionDefaultValue(size_t lineLength,const struct poptOption * opt,const char * translation_domain)206 singleOptionDefaultValue(size_t lineLength,
207 		const struct poptOption * opt,
208 		/* FIX: i18n macros disabled with lclint */
209 		const char * translation_domain)
210 {
211     const char * defstr = D_(translation_domain, "default");
212     char * le = malloc(4*lineLength + 1);
213     char * l = le;
214 
215     if (le == NULL) return NULL;	/* XXX can't happen */
216     *le = '\0';
217     *le++ = '(';
218     le = stpcpy(le, defstr);
219     *le++ = ':';
220     *le++ = ' ';
221   if (opt->arg) {	/* XXX programmer error */
222     poptArg arg = { .ptr = opt->arg };
223     switch (poptArgType(opt)) {
224     case POPT_ARG_VAL:
225     case POPT_ARG_INT:
226 	le += sprintf(le, "%d", arg.intp[0]);
227 	break;
228     case POPT_ARG_SHORT:
229 	le += sprintf(le, "%hd", arg.shortp[0]);
230 	break;
231     case POPT_ARG_LONG:
232 	le += sprintf(le, "%ld", arg.longp[0]);
233 	break;
234     case POPT_ARG_LONGLONG:
235 	le += sprintf(le, "%lld", arg.longlongp[0]);
236 	break;
237     case POPT_ARG_FLOAT:
238     {	double aDouble = (double) arg.floatp[0];
239 	le += sprintf(le, "%g", aDouble);
240     }	break;
241     case POPT_ARG_DOUBLE:
242 	le += sprintf(le, "%g", arg.doublep[0]);
243 	break;
244     case POPT_ARG_MAINCALL:
245 	le += sprintf(le, "%p", opt->arg);
246 	break;
247     case POPT_ARG_ARGV:
248 	le += sprintf(le, "%p", opt->arg);
249 	break;
250     case POPT_ARG_STRING:
251     {	const char * s = arg.argv[0];
252 	if (s == NULL)
253 	    le = stpcpy(le, "null");
254 	else {
255 	    size_t limit = 4*lineLength - (le - l) - sizeof("\"\")");
256 	    size_t slen;
257 	    *le++ = '"';
258 	    strncpy(le, s, limit); le[limit] = '\0'; le += (slen = strlen(le));
259 	    if (slen == limit && s[limit])
260 		le[-1] = le[-2] = le[-3] = '.';
261 	    *le++ = '"';
262 	}
263     }	break;
264     case POPT_ARG_NONE:
265     default:
266 	l = _free(l);
267 	return NULL;
268 	break;
269     }
270   }
271     *le++ = ')';
272     *le = '\0';
273 
274     return l;
275 }
276 
277 /**
278  * Display help text for an option.
279  * @param fp		output file handle
280  * @param columns	output display width control
281  * @param opt		option(s)
282  * @param translation_domain	translation domain
283  */
singleOptionHelp(FILE * fp,columns_t columns,const struct poptOption * opt,const char * translation_domain)284 static void singleOptionHelp(FILE * fp, columns_t columns,
285 		const struct poptOption * opt,
286 		const char * translation_domain)
287 {
288     size_t maxLeftCol = columns->cur;
289     size_t indentLength = maxLeftCol + 5;
290     size_t lineLength = columns->max - indentLength;
291     const char * help = D_(translation_domain, opt->descrip);
292     const char * argDescrip = getArgDescrip(opt, translation_domain);
293     /* Display shortName iff printable non-space. */
294     int prtshort = (int)(isprint((int)opt->shortName) && opt->shortName != ' ');
295     size_t helpLength;
296     char * defs = NULL;
297     char * left;
298     size_t nb = maxLeftCol + 1;
299     int displaypad = 0;
300 
301     /* Make sure there's more than enough room in target buffer. */
302     if (opt->longName)	nb += strlen(opt->longName);
303     if (F_ISSET(opt, TOGGLE)) nb += sizeof("[no]") - 1;
304     if (argDescrip)	nb += strlen(argDescrip);
305 
306     left = malloc(nb);
307     if (left == NULL) return;	/* XXX can't happen */
308     left[0] = '\0';
309     left[maxLeftCol] = '\0';
310 
311 #define	prtlong	(opt->longName != NULL)	/* XXX splint needs a clue */
312     if (!(prtshort || prtlong))
313 	goto out;
314     if (prtshort && prtlong) {
315 	const char *dash = F_ISSET(opt, ONEDASH) ? "-" : "--";
316 	left[0] = '-';
317 	left[1] = opt->shortName;
318 	(void) stpcpy(stpcpy(stpcpy(left+2, ", "), dash), opt->longName);
319     } else if (prtshort) {
320 	left[0] = '-';
321 	left[1] = opt->shortName;
322 	left[2] = '\0';
323     } else if (prtlong) {
324 	/* XXX --long always padded for alignment with/without "-X, ". */
325 	const char *dash = poptArgType(opt) == POPT_ARG_MAINCALL ? ""
326 			 : (F_ISSET(opt, ONEDASH) ? "-" : "--");
327 	const char *longName = opt->longName;
328 	const char *toggle;
329 	if (F_ISSET(opt, TOGGLE)) {
330 	    toggle = "[no]";
331 	    if (longName[0] == 'n' && longName[1] == 'o') {
332 		longName += sizeof("no") - 1;
333 		if (longName[0] == '-')
334 		    longName++;
335 	    }
336 	} else
337 	    toggle = "";
338 	(void) stpcpy(stpcpy(stpcpy(stpcpy(left, "    "), dash), toggle), longName);
339     }
340 #undef	prtlong
341 
342     if (argDescrip) {
343 	char * le = left + strlen(left);
344 
345 	if (F_ISSET(opt, OPTIONAL))
346 	    *le++ = '[';
347 
348 	/* Choose type of output */
349 	if (F_ISSET(opt, SHOW_DEFAULT)) {
350 	    defs = singleOptionDefaultValue(lineLength, opt, translation_domain);
351 	    if (defs) {
352 		char * t = malloc((help ? strlen(help) : 0) +
353 				strlen(defs) + sizeof(" "));
354 		if (t) {
355 		    char * te = t;
356 		    if (help)
357 			te = stpcpy(te, help);
358 		    *te++ = ' ';
359 		    strcpy(te, defs);
360 		    defs = _free(defs);
361 		    defs = t;
362 		}
363 	    }
364 	}
365 
366 	if (opt->argDescrip == NULL) {
367 	    switch (poptArgType(opt)) {
368 	    case POPT_ARG_NONE:
369 		break;
370 	    case POPT_ARG_VAL:
371 #ifdef	NOTNOW	/* XXX pug ugly nerdy output */
372 	    {	long aLong = opt->val;
373 		int ops = F_ISSET(opt, LOGICALOPS);
374 		int negate = F_ISSET(opt, NOT);
375 
376 		/* Don't bother displaying typical values */
377 		if (!ops && (aLong == 0L || aLong == 1L || aLong == -1L))
378 		    break;
379 		*le++ = '[';
380 		switch (ops) {
381 		case POPT_ARGFLAG_OR:
382 		    *le++ = '|';
383 		    break;
384 		case POPT_ARGFLAG_AND:
385 		    *le++ = '&';
386 		    break;
387 		case POPT_ARGFLAG_XOR:
388 		    *le++ = '^';
389 		    break;
390 		default:
391 		    break;
392 		}
393 		*le++ = (opt->longName != NULL ? '=' : ' ');
394 		if (negate) *le++ = '~';
395 		le += sprintf(le, (ops ? "0x%lx" : "%ld"), aLong);
396 		*le++ = ']';
397 	    }
398 #endif
399 		break;
400 	    case POPT_ARG_INT:
401 	    case POPT_ARG_SHORT:
402 	    case POPT_ARG_LONG:
403 	    case POPT_ARG_LONGLONG:
404 	    case POPT_ARG_FLOAT:
405 	    case POPT_ARG_DOUBLE:
406 	    case POPT_ARG_STRING:
407 		*le++ = (opt->longName != NULL ? '=' : ' ');
408 		le = stpcpy(le, argDescrip);
409 		break;
410 	    default:
411 		break;
412 	    }
413 	} else {
414 	    char *leo;
415 
416 	    /* XXX argDescrip[0] determines "--foo=bar" or "--foo bar". */
417 	    if (!strchr(" =(", argDescrip[0]))
418 		*le++ = ((poptArgType(opt) == POPT_ARG_MAINCALL) ? ' ' :
419 			 (poptArgType(opt) == POPT_ARG_ARGV) ? ' ' :
420 			 opt->longName == NULL ? ' ' : '=');
421 	    le = stpcpy(leo = le, argDescrip);
422 
423 	    /* Adjust for (possible) wide characters. */
424 	    displaypad = (int)((le - leo) - stringDisplayWidth(argDescrip));
425 	}
426 	if (F_ISSET(opt, OPTIONAL))
427 	    *le++ = ']';
428 	*le = '\0';
429     }
430 
431     if (help)
432 	POPT_fprintf(fp,"  %-*s   ", (int)(maxLeftCol+displaypad), left);
433     else {
434 	POPT_fprintf(fp,"  %s\n", left);
435 	goto out;
436     }
437 
438     left = _free(left);
439     if (defs)
440 	help = defs;
441 
442     helpLength = strlen(help);
443     while (helpLength > lineLength) {
444 	const char * ch;
445 	char format[16];
446 
447 	ch = help + lineLength - 1;
448 	while (ch > help && !_isspaceptr(ch))
449 	    ch = POPT_prev_char(ch);
450 	if (ch == help) break;		/* give up */
451 	while (ch > (help + 1) && _isspaceptr(ch))
452 	    ch = POPT_prev_char (ch);
453 	ch = POPT_next_char(ch);
454 
455 	/*
456 	 *  XXX strdup is necessary to add NUL terminator so that an unknown
457 	 *  no. of (possible) multi-byte characters can be displayed.
458 	 */
459 	{   char * fmthelp = xstrdup(help);
460 	    if (fmthelp) {
461 		fmthelp[ch - help] = '\0';
462 		sprintf(format, "%%s\n%%%ds", (int) indentLength);
463 		POPT_fprintf(fp, format, fmthelp, " ");
464 		free(fmthelp);
465 	    }
466 	}
467 
468 	help = ch;
469 	while (_isspaceptr(help) && *help)
470 	    help = POPT_next_char(help);
471 	helpLength = strlen(help);
472     }
473 
474     if (helpLength) fprintf(fp, "%s\n", help);
475     help = NULL;
476 
477 out:
478     defs = _free(defs);
479     left = _free(left);
480 }
481 
482 /**
483  * Find display width for longest argument string.
484  * @param opt		option(s)
485  * @param translation_domain	translation domain
486  * @return		display width
487  */
maxArgWidth(const struct poptOption * opt,const char * translation_domain)488 static size_t maxArgWidth(const struct poptOption * opt,
489 		       const char * translation_domain)
490 {
491     size_t max = 0;
492     size_t len = 0;
493     const char * argDescrip;
494 
495     if (opt != NULL)
496     while (opt->longName || opt->shortName || opt->arg) {
497 	if (poptArgType(opt) == POPT_ARG_INCLUDE_TABLE) {
498 	    void * arg = opt->arg;
499 	    /* XXX sick hack to preserve pretense of ABI. */
500 	    if (arg == poptHelpOptions)
501 		arg = poptHelpOptionsI18N;
502 	    if (arg)	/* XXX program error */
503 		len = maxArgWidth(arg, translation_domain);
504 	    if (len > max) max = len;
505 	} else if (!F_ISSET(opt, DOC_HIDDEN)) {
506 	    len = sizeof("  ")-1;
507 	    /* XXX --long always padded for alignment with/without "-X, ". */
508 	    len += sizeof("-X, ")-1;
509 	    if (opt->longName) {
510 		len += (F_ISSET(opt, ONEDASH) ? sizeof("-") : sizeof("--")) - 1;
511 		len += strlen(opt->longName);
512 	    }
513 
514 	    argDescrip = getArgDescrip(opt, translation_domain);
515 
516 	    if (argDescrip) {
517 
518 		/* XXX argDescrip[0] determines "--foo=bar" or "--foo bar". */
519 		if (!strchr(" =(", argDescrip[0])) len += sizeof("=")-1;
520 
521 		/* Adjust for (possible) wide characters. */
522 		len += stringDisplayWidth(argDescrip);
523 	    }
524 
525 	    if (F_ISSET(opt, OPTIONAL)) len += sizeof("[]")-1;
526 	    if (len > max) max = len;
527 	}
528 	opt++;
529     }
530 
531     return max;
532 }
533 
534 /**
535  * Display popt alias and exec help.
536  * @param fp		output file handle
537  * @param items		alias/exec array
538  * @param nitems	no. of alias/exec entries
539  * @param columns	output display width control
540  * @param translation_domain	translation domain
541  */
itemHelp(FILE * fp,poptItem items,int nitems,columns_t columns,const char * translation_domain)542 static void itemHelp(FILE * fp,
543 		poptItem items, int nitems,
544 		columns_t columns,
545 		const char * translation_domain)
546 {
547     poptItem item;
548     int i;
549 
550     if (items != NULL)
551     for (i = 0, item = items; i < nitems; i++, item++) {
552 	const struct poptOption * opt;
553 	opt = &item->option;
554 	if ((opt->longName || opt->shortName) && !F_ISSET(opt, DOC_HIDDEN))
555 	    singleOptionHelp(fp, columns, opt, translation_domain);
556     }
557 }
558 
559 /**
560  * Display help text for a table of options.
561  * @param con		context
562  * @param fp		output file handle
563  * @param table		option(s)
564  * @param columns	output display width control
565  * @param translation_domain	translation domain
566  */
singleTableHelp(poptContext con,FILE * fp,const struct poptOption * table,columns_t columns,const char * translation_domain)567 static void singleTableHelp(poptContext con, FILE * fp,
568 		const struct poptOption * table,
569 		columns_t columns,
570 		const char * translation_domain)
571 {
572     const struct poptOption * opt;
573     const char *sub_transdom;
574 
575     if (table == poptAliasOptions) {
576 	itemHelp(fp, con->aliases, con->numAliases, columns, NULL);
577 	itemHelp(fp, con->execs, con->numExecs, columns, NULL);
578 	return;
579     }
580 
581     if (table != NULL)
582     for (opt = table; opt->longName || opt->shortName || opt->arg; opt++) {
583 	if ((opt->longName || opt->shortName) && !F_ISSET(opt, DOC_HIDDEN))
584 	    singleOptionHelp(fp, columns, opt, translation_domain);
585     }
586 
587     if (table != NULL)
588     for (opt = table; opt->longName || opt->shortName || opt->arg; opt++) {
589 	void * arg = opt->arg;
590 	if (poptArgType(opt) != POPT_ARG_INCLUDE_TABLE)
591 	    continue;
592 	/* XXX sick hack to preserve pretense of ABI. */
593 	if (arg == poptHelpOptions)
594 	    arg = poptHelpOptionsI18N;
595 	sub_transdom = getTableTranslationDomain(arg);
596 	if (sub_transdom == NULL)
597 	    sub_transdom = translation_domain;
598 
599 	/* If no popt aliases/execs, skip poptAliasOption processing. */
600 	if (arg == poptAliasOptions && !(con->numAliases || con->numExecs))
601 	    continue;
602 	if (opt->descrip)
603 	    POPT_fprintf(fp, "\n%s\n", D_(sub_transdom, opt->descrip));
604 
605 	singleTableHelp(con, fp, arg, columns, sub_transdom);
606     }
607 }
608 
609 /**
610  * @param con		context
611  * @param fp		output file handle
612  */
showHelpIntro(poptContext con,FILE * fp)613 static size_t showHelpIntro(poptContext con, FILE * fp)
614 {
615     const char *usage_str = POPT_("Usage:");
616     size_t len = strlen(usage_str);
617     POPT_fprintf(fp, "%s", usage_str);
618 
619     if (!(con->flags & POPT_CONTEXT_KEEP_FIRST)) {
620 	struct optionStackEntry * os = con->optionStack;
621 	const char * fn = (os->argv ? os->argv[0] : NULL);
622 	if (fn == NULL) return len;
623 	if (strchr(fn, '/')) fn = strrchr(fn, '/') + 1;
624 	/* XXX POPT_fprintf not needed for argv[0] display. */
625 	fprintf(fp, " %s", fn);
626 	len += strlen(fn) + 1;
627     }
628 
629     return len;
630 }
631 
poptPrintHelp(poptContext con,FILE * fp,UNUSED (int flags))632 void poptPrintHelp(poptContext con, FILE * fp, UNUSED(int flags))
633 {
634     columns_t columns = calloc((size_t)1, sizeof(*columns));
635 
636     (void) showHelpIntro(con, fp);
637     if (con->otherHelp)
638 	POPT_fprintf(fp, " %s\n", con->otherHelp);
639     else
640 	POPT_fprintf(fp, " %s\n", POPT_("[OPTION...]"));
641 
642     if (columns) {
643 	columns->cur = maxArgWidth(con->options, NULL);
644 	columns->max = maxColumnWidth(fp);
645 	singleTableHelp(con, fp, con->options, columns, NULL);
646 	free(columns);
647     }
648 }
649 
650 /**
651  * Display usage text for an option.
652  * @param fp		output file handle
653  * @param columns	output display width control
654  * @param opt		option(s)
655  * @param translation_domain	translation domain
656  */
singleOptionUsage(FILE * fp,columns_t columns,const struct poptOption * opt,const char * translation_domain)657 static size_t singleOptionUsage(FILE * fp, columns_t columns,
658 		const struct poptOption * opt,
659 		const char *translation_domain)
660 {
661     size_t len = sizeof(" []")-1;
662     const char * argDescrip = getArgDescrip(opt, translation_domain);
663     /* Display shortName iff printable non-space. */
664     int prtshort = (int)(isprint((int)opt->shortName) && opt->shortName != ' ');
665 
666 #define	prtlong	(opt->longName != NULL)	/* XXX splint needs a clue */
667     if (!(prtshort || prtlong))
668 	return columns->cur;
669 
670     len = sizeof(" []")-1;
671     if (prtshort)
672 	len += sizeof("-c")-1;
673     if (prtlong) {
674 	if (prtshort) len += sizeof("|")-1;
675 	len += (F_ISSET(opt, ONEDASH) ? sizeof("-") : sizeof("--")) - 1;
676 	len += strlen(opt->longName);
677     }
678 
679     if (argDescrip) {
680 
681 	/* XXX argDescrip[0] determines "--foo=bar" or "--foo bar". */
682 	if (!strchr(" =(", argDescrip[0])) len += sizeof("=")-1;
683 
684 	/* Adjust for (possible) wide characters. */
685 	len += stringDisplayWidth(argDescrip);
686     }
687 
688     if ((columns->cur + len) > columns->max) {
689 	fprintf(fp, "\n       ");
690 	columns->cur = (size_t)7;
691     }
692 
693     fprintf(fp, " [");
694     if (prtshort)
695 	fprintf(fp, "-%c", opt->shortName);
696     if (prtlong)
697 	fprintf(fp, "%s%s%s",
698 		(prtshort ? "|" : ""),
699 		(F_ISSET(opt, ONEDASH) ? "-" : "--"),
700 		opt->longName);
701 #undef	prtlong
702 
703     if (argDescrip) {
704 	/* XXX argDescrip[0] determines "--foo=bar" or "--foo bar". */
705 	if (!strchr(" =(", argDescrip[0])) fputc(opt->longName == NULL ? ' ' : '=', fp);
706 	fprintf(fp, "%s", argDescrip);
707     }
708     fprintf(fp, "]");
709 
710     return columns->cur + len + 1;
711 }
712 
713 /**
714  * Display popt alias and exec usage.
715  * @param fp		output file handle
716  * @param columns	output display width control
717  * @param item		alias/exec array
718  * @param nitems	no. of ara/exec entries
719  * @param translation_domain	translation domain
720  */
itemUsage(FILE * fp,columns_t columns,poptItem item,int nitems,const char * translation_domain)721 static size_t itemUsage(FILE * fp, columns_t columns,
722 		poptItem item, int nitems,
723 		const char * translation_domain)
724 {
725     int i;
726 
727     if (item != NULL)
728     for (i = 0; i < nitems; i++, item++) {
729 	const struct poptOption * opt;
730 	opt = &item->option;
731         if (poptArgType(opt) == POPT_ARG_INTL_DOMAIN) {
732 	    translation_domain = (const char *)opt->arg;
733 	} else
734 	if ((opt->longName || opt->shortName) && !F_ISSET(opt, DOC_HIDDEN)) {
735 	    columns->cur = singleOptionUsage(fp, columns, opt, translation_domain);
736 	}
737     }
738 
739     return columns->cur;
740 }
741 
742 /**
743  * Keep track of option tables already processed.
744  */
745 typedef struct poptDone_s {
746     int nopts;
747     int maxopts;
748     const void ** opts;
749 } * poptDone;
750 
751 /**
752  * Display usage text for a table of options.
753  * @param con		context
754  * @param fp		output file handle
755  * @param columns	output display width control
756  * @param opt		option(s)
757  * @param translation_domain	translation domain
758  * @param done		tables already processed
759  * @return
760  */
singleTableUsage(poptContext con,FILE * fp,columns_t columns,const struct poptOption * opt,const char * translation_domain,poptDone done)761 static size_t singleTableUsage(poptContext con, FILE * fp, columns_t columns,
762 		const struct poptOption * opt,
763 		const char * translation_domain,
764 		poptDone done)
765 {
766     if (opt != NULL)
767     for (; (opt->longName || opt->shortName || opt->arg) ; opt++) {
768         if (poptArgType(opt) == POPT_ARG_INTL_DOMAIN) {
769 	    translation_domain = (const char *)opt->arg;
770 	} else
771 	if (poptArgType(opt) == POPT_ARG_INCLUDE_TABLE) {
772 	    void * arg = opt->arg;
773 	    /* XXX sick hack to preserve pretense of ABI. */
774 	    if (arg == poptHelpOptions)
775 		arg = poptHelpOptionsI18N;
776 	    if (done) {
777 		int i = 0;
778 		if (done->opts != NULL)
779 		for (i = 0; i < done->nopts; i++) {
780 		    const void * that = done->opts[i];
781 		    if (that == NULL || that != arg)
782 			continue;
783 		    break;
784 		}
785 		/* Skip if this table has already been processed. */
786 		if (arg == NULL || i < done->nopts)
787 		    continue;
788 		if (done->opts != NULL && done->nopts < done->maxopts)
789 		    done->opts[done->nopts++] = (const void *) arg;
790 	    }
791 	    columns->cur = singleTableUsage(con, fp, columns, opt->arg,
792 			translation_domain, done);
793 	} else
794 	if ((opt->longName || opt->shortName) && !F_ISSET(opt, DOC_HIDDEN)) {
795 	    columns->cur = singleOptionUsage(fp, columns, opt, translation_domain);
796 	}
797     }
798 
799     return columns->cur;
800 }
801 
802 /**
803  * Return concatenated short options for display.
804  * @todo Sub-tables should be recursed.
805  * @param opt		option(s)
806  * @param fp		output file handle
807  * @retval str		concatenation of short options
808  * @return		length of display string
809  */
showShortOptions(const struct poptOption * opt,FILE * fp,char * str)810 static size_t showShortOptions(const struct poptOption * opt, FILE * fp,
811 		char * str)
812 {
813     /* bufsize larger then the ascii set, lazy allocation on top level call. */
814     size_t nb = (size_t)300;
815     char * s = (str != NULL ? str : calloc((size_t)1, nb));
816     size_t len = (size_t)0;
817 
818     if (s == NULL)
819 	return 0;
820 
821     if (opt != NULL)
822     for (; (opt->longName || opt->shortName || opt->arg); opt++) {
823 	if (!F_ISSET(opt, DOC_HIDDEN) && opt->shortName && !poptArgType(opt))
824 	{
825 	    /* Display shortName iff unique printable non-space. */
826 	    if (!strchr(s, opt->shortName) && isprint((int)opt->shortName)
827 	     && opt->shortName != ' ')
828 		s[strlen(s)] = opt->shortName;
829 	} else if (poptArgType(opt) == POPT_ARG_INCLUDE_TABLE) {
830 	    void * arg = opt->arg;
831 	    /* XXX sick hack to preserve pretense of ABI. */
832 	    if (arg == poptHelpOptions)
833 		arg = poptHelpOptionsI18N;
834 	    if (arg)	/* XXX program error */
835 		len = showShortOptions(arg, fp, s);
836 	}
837     }
838 
839     /* On return to top level, print the short options, return print length. */
840     if (s != str && *s != '\0') {
841 	fprintf(fp, " [-%s]", s);
842 	len = strlen(s) + sizeof(" [-]")-1;
843     }
844     if (s != str)
845 	free(s);
846     return len;
847 }
848 
poptPrintUsage(poptContext con,FILE * fp,UNUSED (int flags))849 void poptPrintUsage(poptContext con, FILE * fp, UNUSED(int flags))
850 {
851     columns_t columns = calloc((size_t)1, sizeof(*columns));
852     struct poptDone_s done_buf;
853     poptDone done = &done_buf;
854 
855     memset(done, 0, sizeof(*done));
856     done->nopts = 0;
857     done->maxopts = 64;
858   if (columns) {
859     columns->cur = done->maxopts * sizeof(*done->opts);
860     columns->max = maxColumnWidth(fp);
861     done->opts = calloc((size_t)1, columns->cur);
862     if (done->opts != NULL)
863 	done->opts[done->nopts++] = (const void *) con->options;
864 
865     columns->cur = showHelpIntro(con, fp);
866     columns->cur += showShortOptions(con->options, fp, NULL);
867     columns->cur = singleTableUsage(con, fp, columns, con->options, NULL, done);
868     columns->cur = itemUsage(fp, columns, con->aliases, con->numAliases, NULL);
869     columns->cur = itemUsage(fp, columns, con->execs, con->numExecs, NULL);
870 
871     if (con->otherHelp) {
872 	columns->cur += strlen(con->otherHelp) + 1;
873 	if (columns->cur > columns->max) fprintf(fp, "\n       ");
874 	fprintf(fp, " %s", con->otherHelp);
875     }
876 
877     fprintf(fp, "\n");
878     if (done->opts != NULL)
879 	free(done->opts);
880     free(columns);
881   }
882 }
883 
poptSetOtherOptionHelp(poptContext con,const char * text)884 void poptSetOtherOptionHelp(poptContext con, const char * text)
885 {
886     con->otherHelp = _free(con->otherHelp);
887     con->otherHelp = xstrdup(text);
888 }
889