/** * @param opt option(s) * @param translation_domain translation domain */ /*@observer@*/ /*@null@*/ static const char * getArgDescrip(const struct poptOption * opt, /*@-paramuse@*/ /* FIX: i18n macros disabled with lclint */ /*@null@*/ const char * translation_domain) /*@=paramuse@*/ /*@*/ { if (!poptArgType(opt)) return NULL; if (poptArgType(opt) == POPT_ARG_MAINCALL) return opt->argDescrip; if (poptArgType(opt) == POPT_ARG_ARGV) return opt->argDescrip; if (opt->argDescrip) { /* Some strings need popt library, not application, i18n domain. */ if (opt == (poptHelpOptions + 1) || opt == (poptHelpOptions + 2) || !strcmp(opt->argDescrip, N_("Help options:")) || !strcmp(opt->argDescrip, N_("Options implemented via popt alias/exec:"))) return POPT_(opt->argDescrip); /* Use the application i18n domain. */ return D_(translation_domain, opt->argDescrip); } switch (poptArgType(opt)) { case POPT_ARG_NONE: return POPT_("NONE"); #ifdef DYING case POPT_ARG_VAL: return POPT_("VAL"); #else case POPT_ARG_VAL: return NULL; #endif case POPT_ARG_INT: return POPT_("INT"); case POPT_ARG_SHORT: return POPT_("SHORT"); case POPT_ARG_LONG: return POPT_("LONG"); case POPT_ARG_LONGLONG: return POPT_("LONGLONG"); case POPT_ARG_STRING: return POPT_("STRING"); case POPT_ARG_FLOAT: return POPT_("FLOAT"); case POPT_ARG_DOUBLE: return POPT_("DOUBLE"); case POPT_ARG_MAINCALL: return NULL; case POPT_ARG_ARGV: return NULL; default: return POPT_("ARG"); } }
/** * Return concatenated short options for display. * @todo Sub-tables should be recursed. * @param opt option(s) * @param fp output file handle * @retval str concatenation of short options * @return length of display string */ static size_t showShortOptions(const struct poptOption * opt, FILE * fp, /*@null@*/ char * str) /*@globals fileSystem @*/ /*@modifies str, *fp, fileSystem @*/ /*@requires maxRead(str) >= 0 @*/ { /* bufsize larger then the ascii set, lazy allocation on top level call. */ size_t nb = (size_t)300; char * s = (str != NULL ? str : (char*) calloc((size_t)1, nb)); size_t len = (size_t)0; assert(s); /* XXX can't happen */ if (s == NULL) return 0; if (opt != NULL) for (; (opt->longName || opt->shortName || opt->arg); opt++) { if (!F_ISSET(opt, DOC_HIDDEN) && opt->shortName && !poptArgType(opt)) { /* Display shortName iff unique printable non-space. */ if (!strchr(s, opt->shortName) && isprint((int)opt->shortName) && opt->shortName != ' ') s[strlen(s)] = opt->shortName; } else if (poptArgType(opt) == POPT_ARG_INCLUDE_TABLE) if (opt->arg) /* XXX program error */ len = showShortOptions(opt->arg, fp, s); } /* On return to top level, print the short options, return print length. */ if (s != str && *s != '\0') { fprintf(fp, " [-%s]", s); len = strlen(s) + sizeof(" [-]")-1; } /*@-temptrans@*/ /* LCL: local s, not str arg, is being freed. */ if (s != str) free(s); /*@=temptrans@*/ return len; }
/** * Display usage text for a table of options. * @param con context * @param fp output file handle * @param columns output display width control * @param opt option(s) * @param translation_domain translation domain * @param done tables already processed * @return */ static size_t singleTableUsage(poptContext con, FILE * fp, columns_t columns, /*@null@*/ const struct poptOption * opt, /*@null@*/ const char * translation_domain, /*@null@*/ poptDone done) /*@globals fileSystem @*/ /*@modifies fp, columns->cur, done, fileSystem @*/ { if (opt != NULL) for (; (opt->longName || opt->shortName || opt->arg) ; opt++) { if (poptArgType(opt) == POPT_ARG_INTL_DOMAIN) { translation_domain = (const char *)opt->arg; } else if (poptArgType(opt) == POPT_ARG_INCLUDE_TABLE) { if (done) { int i = 0; if (done->opts != NULL) for (i = 0; i < done->nopts; i++) { const void * that = done->opts[i]; if (that == NULL || that != opt->arg) /*@innercontinue@*/ continue; /*@innerbreak@*/ break; } /* Skip if this table has already been processed. */ if (opt->arg == NULL || i < done->nopts) continue; if (done->opts != NULL && done->nopts < done->maxopts) done->opts[done->nopts++] = (const void *) opt->arg; } columns->cur = singleTableUsage(con, fp, columns, opt->arg, translation_domain, done); } else if ((opt->longName || opt->shortName) && !F_ISSET(opt, DOC_HIDDEN)) { columns->cur = singleOptionUsage(fp, columns, opt, translation_domain); } } return columns->cur; }
/** * Find display width for longest argument string. * @param opt option(s) * @param translation_domain translation domain * @return display width */ static size_t maxArgWidth(const struct poptOption * opt, /*@null@*/ const char * translation_domain) /*@*/ { size_t max = 0; const char * argDescrip; if (opt != NULL) while (opt->longName || opt->shortName || opt->arg) { size_t len = 0; if (poptArgType(opt) == POPT_ARG_INCLUDE_TABLE) { if (opt->arg) /* XXX program error */ len = maxArgWidth(opt->arg, translation_domain); if (len > max) max = len; } else if (!F_ISSET(opt, DOC_HIDDEN)) { len = sizeof(" ")-1; /* XXX --long always padded for alignment with/without "-X, ". */ len += sizeof("-X, ")-1; if (opt->longName) { len += (F_ISSET(opt, ONEDASH) ? sizeof("-") : sizeof("--")) - 1; len += strlen(opt->longName); } argDescrip = getArgDescrip(opt, translation_domain); if (argDescrip) { /* XXX argDescrip[0] determines "--foo=bar" or "--foo bar". */ if (!strchr(" =(", argDescrip[0])) len += sizeof("=")-1; /* Adjust for (possible) wide characters. */ len += stringDisplayWidth(argDescrip); } if (F_ISSET(opt, OPTIONAL)) len += sizeof("[]")-1; if (len > max) max = len; } opt++; } return max; }
/** * Display help text for a table of options. * @param con context * @param fp output file handle * @param table option(s) * @param columns output display width control * @param translation_domain translation domain */ static void singleTableHelp(poptContext con, FILE * fp, /*@null@*/ const struct poptOption * table, columns_t columns, /*@null@*/ const char * translation_domain) /*@globals fileSystem @*/ /*@modifies fp, columns->cur, fileSystem @*/ { const struct poptOption * opt; const char *sub_transdom; if (table == poptAliasOptions) { itemHelp(fp, con->aliases, con->numAliases, columns, NULL); itemHelp(fp, con->execs, con->numExecs, columns, NULL); return; } if (table != NULL) for (opt = table; opt->longName || opt->shortName || opt->arg; opt++) { if ((opt->longName || opt->shortName) && !F_ISSET(opt, DOC_HIDDEN)) singleOptionHelp(fp, columns, opt, translation_domain); } if (table != NULL) for (opt = table; opt->longName || opt->shortName || opt->arg; opt++) { if (poptArgType(opt) != POPT_ARG_INCLUDE_TABLE) continue; sub_transdom = getTableTranslationDomain(opt->arg); if (sub_transdom == NULL) sub_transdom = translation_domain; /* If no popt aliases/execs, skip poptAliasOption processing. */ if (opt->arg == poptAliasOptions && !(con->numAliases || con->numExecs)) continue; if (opt->descrip) { POPT_fprintf(fp, "\n%s\n", D_(sub_transdom, opt->descrip)); /* XXX: unchecked */ } singleTableHelp(con, fp, opt->arg, columns, sub_transdom); } }
/** * Display popt alias and exec usage. * @param fp output file handle * @param columns output display width control * @param item alias/exec array * @param nitems no. of ara/exec entries * @param translation_domain translation domain */ static size_t itemUsage(FILE * fp, columns_t columns, /*@null@*/ poptItem item, int nitems, /*@null@*/ const char * translation_domain) /*@globals fileSystem @*/ /*@modifies fp, columns->cur, fileSystem @*/ { int i; if (item != NULL) for (i = 0; i < nitems; i++, item++) { const struct poptOption * opt; opt = &item->option; if (poptArgType(opt) == POPT_ARG_INTL_DOMAIN) { translation_domain = (const char *)opt->arg; } else if ((opt->longName || opt->shortName) && !F_ISSET(opt, DOC_HIDDEN)) { columns->cur = singleOptionUsage(fp, columns, opt, translation_domain); } } return columns->cur; }
/** * Display help text for an option. * @param fp output file handle * @param columns output display width control * @param opt option(s) * @param translation_domain translation domain */ static void singleOptionHelp(FILE * fp, columns_t columns, const struct poptOption * opt, /*@null@*/ const char * translation_domain) /*@globals fileSystem @*/ /*@modifies fp, fileSystem @*/ { size_t maxLeftCol = columns->cur; size_t indentLength = maxLeftCol + 5; size_t lineLength = columns->max - indentLength; const char * help = D_(translation_domain, opt->descrip); const char * argDescrip = getArgDescrip(opt, translation_domain); /* Display shortName iff printable non-space. */ int prtshort = (int)(isprint((int)opt->shortName) && opt->shortName != ' '); size_t helpLength; char * defs = NULL; char * left; size_t nb = maxLeftCol + 1; int displaypad = 0; /* Make sure there's more than enough room in target buffer. */ if (opt->longName) nb += strlen(opt->longName); if (F_ISSET(opt, TOGGLE)) nb += sizeof("[no]") - 1; if (argDescrip) nb += strlen(argDescrip); left = (char*) xmalloc(nb); assert(left); /* XXX can't happen */ if (left == NULL) return; left[0] = '\0'; left[maxLeftCol] = '\0'; #define prtlong (opt->longName != NULL) /* XXX splint needs a clue */ if (!(prtshort || prtlong)) goto out; if (prtshort && prtlong) { char *dash = F_ISSET(opt, ONEDASH) ? "-" : "--"; left[0] = '-'; left[1] = opt->shortName; (void) stpcpy(stpcpy(stpcpy(left+2, ", "), dash), opt->longName); } else if (prtshort) { left[0] = '-'; left[1] = opt->shortName; left[2] = '\0'; } else if (prtlong) { /* XXX --long always padded for alignment with/without "-X, ". */ char *dash = poptArgType(opt) == POPT_ARG_MAINCALL ? "" : (F_ISSET(opt, ONEDASH) ? "-" : "--"); const char *longName = opt->longName; const char *toggle; if (F_ISSET(opt, TOGGLE)) { toggle = "[no]"; if (longName[0] == 'n' && longName[1] == 'o') { longName += sizeof("no") - 1; if (longName[0] == '-') longName++; } } else toggle = ""; (void) stpcpy(stpcpy(stpcpy(stpcpy(left, " "), dash), toggle), longName); } #undef prtlong if (argDescrip) { char * le = left + strlen(left); if (F_ISSET(opt, OPTIONAL)) *le++ = '['; /* Choose type of output */ if (F_ISSET(opt, SHOW_DEFAULT)) { defs = singleOptionDefaultValue(lineLength, opt, translation_domain); if (defs) { char * t = (char*) xmalloc((help ? strlen(help) : 0) + strlen(defs) + sizeof(" ")); assert(t); /* XXX can't happen */ if (t) { char * te = t; if (help) te = stpcpy(te, help); *te++ = ' '; strcpy(te, defs); defs = _free(defs); defs = t; } } } if (opt->argDescrip == NULL) { switch (poptArgType(opt)) { case POPT_ARG_NONE: break; case POPT_ARG_VAL: #ifdef NOTNOW /* XXX pug ugly nerdy output */ { long aLong = opt->val; int ops = F_ISSET(opt, LOGICALOPS); int negate = F_ISSET(opt, NOT); /* Don't bother displaying typical values */ if (!ops && (aLong == 0L || aLong == 1L || aLong == -1L)) break; *le++ = '['; switch (ops) { case POPT_ARGFLAG_OR: *le++ = '|'; /*@innerbreak@*/ break; case POPT_ARGFLAG_AND: *le++ = '&'; /*@innerbreak@*/ break; case POPT_ARGFLAG_XOR: *le++ = '^'; /*@innerbreak@*/ break; default: /*@innerbreak@*/ break; } *le++ = (opt->longName != NULL ? '=' : ' '); if (negate) *le++ = '~'; /*@-formatconst@*/ le += sprintf(le, (ops ? "0x%lx" : "%ld"), aLong); /*@=formatconst@*/ *le++ = ']'; } #endif break; case POPT_ARG_INT: case POPT_ARG_SHORT: case POPT_ARG_LONG: case POPT_ARG_LONGLONG: case POPT_ARG_FLOAT: case POPT_ARG_DOUBLE: case POPT_ARG_STRING: *le++ = (opt->longName != NULL ? '=' : ' '); le = stpcpy(le, argDescrip); break; default: break; } } else { char *leo; /* XXX argDescrip[0] determines "--foo=bar" or "--foo bar". */ if (!strchr(" =(", argDescrip[0])) *le++ = ((poptArgType(opt) == POPT_ARG_MAINCALL) ? ' ' : (poptArgType(opt) == POPT_ARG_ARGV) ? ' ' : '='); le = stpcpy(leo = le, argDescrip); /* Adjust for (possible) wide characters. */ displaypad = (int)((le - leo) - stringDisplayWidth(argDescrip)); } if (F_ISSET(opt, OPTIONAL)) *le++ = ']'; *le = '\0'; } if (help) POPT_fprintf(fp," %-*s ", (int)(maxLeftCol+displaypad), left); else { POPT_fprintf(fp," %s\n", left); goto out; } left = _free(left); if (defs) help = defs; helpLength = strlen(help); while (helpLength > lineLength) { const char * ch; char format[16]; ch = help + lineLength - 1; while (ch > help && !_isspaceptr(ch)) ch = POPT_prev_char(ch); if (ch == help) break; /* give up */ while (ch > (help + 1) && _isspaceptr(ch)) ch = POPT_prev_char (ch); ch = POPT_next_char(ch); /* * XXX strdup is necessary to add NUL terminator so that an unknown * no. of (possible) multi-byte characters can be displayed. */ { char * fmthelp = xstrdup(help); if (fmthelp) { fmthelp[ch - help] = '\0'; sprintf(format, "%%s\n%%%ds", (int) indentLength); /*@-formatconst@*/ POPT_fprintf(fp, format, fmthelp, " "); /*@=formatconst@*/ free(fmthelp); } } help = ch; while (_isspaceptr(help) && *help) help = POPT_next_char(help); helpLength = strlen(help); } if (helpLength) fprintf(fp, "%s\n", help); help = NULL; out: /*@-dependenttrans@*/ defs = _free(defs); /*@=dependenttrans@*/ left = _free(left); }
/** * Display default value for an option. * @param lineLength display positions remaining * @param opt option(s) * @param translation_domain translation domain * @return */ static /*@only@*/ /*@null@*/ char * singleOptionDefaultValue(size_t lineLength, const struct poptOption * opt, /*@-paramuse@*/ /* FIX: i18n macros disabled with lclint */ /*@null@*/ const char * translation_domain) /*@=paramuse@*/ /*@*/ { const char * defstr = D_(translation_domain, "default"); char * le = (char*) xmalloc(4*lineLength + 1); char * l = le; assert(le); /* XXX can't happen */ if (le == NULL) return NULL; *le = '\0'; *le++ = '('; le = stpcpy(le, defstr); *le++ = ':'; *le++ = ' '; if (opt->arg) { /* XXX programmer error */ poptArg arg; arg.ptr = opt->arg; switch (poptArgType(opt)) { case POPT_ARG_VAL: case POPT_ARG_INT: le += sprintf(le, "%d", arg.intp[0]); break; case POPT_ARG_SHORT: le += sprintf(le, "%hd", arg.shortp[0]); break; case POPT_ARG_LONG: le += sprintf(le, "%ld", arg.longp[0]); break; case POPT_ARG_LONGLONG: #if defined(_MSC_VER) || defined(__MINGW32__) le += sprintf(le, "%I64d", arg.longlongp[0]); #else le += sprintf(le, "%lld", arg.longlongp[0]); #endif break; case POPT_ARG_FLOAT: { double aDouble = (double) arg.floatp[0]; le += sprintf(le, "%g", aDouble); } break; case POPT_ARG_DOUBLE: le += sprintf(le, "%g", arg.doublep[0]); break; case POPT_ARG_MAINCALL: le += sprintf(le, "%p", opt->arg); break; case POPT_ARG_ARGV: le += sprintf(le, "%p", opt->arg); break; case POPT_ARG_STRING: { const char * s = arg.argv[0]; if (s == NULL) le = stpcpy(le, "null"); else { size_t limit = 4*lineLength - (le - l) - sizeof("\"\")"); size_t slen; *le++ = '"'; strncpy(le, s, limit); le[limit] = '\0'; le += (slen = strlen(le)); if (slen == limit && s[limit]) le[-1] = le[-2] = le[-3] = '.'; *le++ = '"'; } } break; case POPT_ARG_NONE: default: l = _free(l); return NULL; /*@notreached@*/ break; } } *le++ = ')'; *le = '\0'; return l; }