popthelp.c 25 KB

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