Home | History | Annotate | Download | only in libpopt
      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  */
     30 static 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@*/
     46 static int show_option_defaults = 0;
     47 #endif
     48 
     49 /**
     50  * Empty table marker to enable displaying popt alias/exec options.
     51  */
     52 /*@observer@*/ /*@unchecked@*/
     53 struct poptOption poptAliasOptions[] = {
     54     POPT_TABLEEND
     55 };
     56 
     57 /**
     58  * Auto help table options.
     59  */
     60 /*@-castfcnptr@*/
     61 /*@observer@*/ /*@unchecked@*/
     62 struct 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@*/
     70 static 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@*/
     85 struct poptOption * poptHelpOptionsI18N = poptHelpOptions2;
     86 /*@=castfcnptr@*/
     87 
     88 /**
     89  * @param table		option(s)
     90  */
     91 /*@observer@*/ /*@null@*/ static const char *
     92 getTableTranslationDomain(/*@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 *
    110 getArgDescrip(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  */
    146 static /*@only@*/ /*@null@*/ char *
    147 singleOptionDefaultValue(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  */
    218 static 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 
    397 out:
    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  */
    410 static 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  */
    473 static 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  */
    500 static 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  */
    541 static 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 
    564 void 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  */
    585 static 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  */
    661 static 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  */
    689 typedef 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  */
    705 static 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  */
    756 static 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 
    785 void 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 
    815 void poptSetOtherOptionHelp(poptContext con, const char * text)
    816 {
    817     con->otherHelp = _free(con->otherHelp);
    818     con->otherHelp = xstrdup(text);
    819 }
    820