Commit | Line | Data |
---|---|---|
b348deae MP |
1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ |
2 | ||
cc248aae WD |
3 | /** \ingroup popt |
4 | * \file popt/popthelp.c | |
5 | */ | |
6 | ||
bc93ee84 | 7 | /* (C) 1998-2002 Red Hat, Inc. -- Licensing details are in the COPYING |
62402cb1 | 8 | file accompanying popt source distributions, available from |
cc248aae | 9 | ftp://ftp.rpm.org/pub/rpm/dist. */ |
62402cb1 | 10 | |
b348deae | 11 | #include "system.h" |
bc93ee84 | 12 | |
564782ba | 13 | /*#define POPT_WCHAR_HACK*/ |
bc93ee84 WD |
14 | #ifdef POPT_WCHAR_HACK |
15 | #include <wchar.h> /* for mbsrtowcs */ | |
16 | /*@access mbstate_t @*/ | |
17 | #endif | |
62402cb1 MP |
18 | #include "poptint.h" |
19 | ||
bc93ee84 WD |
20 | /*@access poptContext@*/ |
21 | ||
cc248aae | 22 | /** |
bc93ee84 | 23 | * Display arguments. |
cc248aae | 24 | * @param con context |
bc93ee84 | 25 | * @param foo (unused) |
cc248aae | 26 | * @param key option(s) |
bc93ee84 WD |
27 | * @param arg (unused) |
28 | * @param data (unused) | |
cc248aae | 29 | */ |
b348deae | 30 | static void displayArgs(poptContext con, |
73042aae | 31 | /*@unused@*/ UNUSED(enum poptCallbackReason foo), |
b348deae | 32 | struct poptOption * key, |
73042aae | 33 | /*@unused@*/ UNUSED(const char * arg), /*@unused@*/ UNUSED(void * data)) |
cc248aae WD |
34 | /*@globals fileSystem@*/ |
35 | /*@modifies fileSystem@*/ | |
36 | { | |
37 | if (key->shortName == '?') | |
b348deae | 38 | poptPrintHelp(con, stdout, 0); |
62402cb1 | 39 | else |
b348deae | 40 | poptPrintUsage(con, stdout, 0); |
62402cb1 MP |
41 | exit(0); |
42 | } | |
43 | ||
cc248aae WD |
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@*/ | |
62402cb1 | 62 | struct poptOption poptHelpOptions[] = { |
bc93ee84 WD |
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@*/ | |
cc248aae WD |
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 | |
62402cb1 | 82 | } ; |
bc93ee84 WD |
83 | |
84 | /*@observer@*/ /*@unchecked@*/ | |
85 | struct poptOption * poptHelpOptionsI18N = poptHelpOptions2; | |
cc248aae | 86 | /*@=castfcnptr@*/ |
62402cb1 | 87 | |
cc248aae WD |
88 | /** |
89 | * @param table option(s) | |
90 | */ | |
73042aae | 91 | /*@observer@*/ /*@null@*/ static const char * |
cc248aae WD |
92 | getTableTranslationDomain(/*@null@*/ const struct poptOption *table) |
93 | /*@*/ | |
b348deae | 94 | { |
cc248aae | 95 | const struct poptOption *opt; |
b348deae | 96 | |
cc248aae WD |
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; | |
b348deae MP |
103 | } |
104 | ||
cc248aae WD |
105 | /** |
106 | * @param opt option(s) | |
107 | * @param translation_domain translation domain | |
108 | */ | |
73042aae | 109 | /*@observer@*/ /*@null@*/ static const char * |
cc248aae | 110 | getArgDescrip(const struct poptOption * opt, |
bc93ee84 | 111 | /*@-paramuse@*/ /* FIX: i18n macros disabled with lclint */ |
1526b8b1 | 112 | /*@null@*/ UNUSED(const char * translation_domain)) |
cc248aae WD |
113 | /*@=paramuse@*/ |
114 | /*@*/ | |
b348deae | 115 | { |
62402cb1 MP |
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 | ||
b348deae | 121 | if (opt->argDescrip) return D_(translation_domain, opt->argDescrip); |
cc248aae WD |
122 | |
123 | switch (opt->argInfo & POPT_ARG_MASK) { | |
894e6299 | 124 | /*case POPT_ARG_NONE: return POPT_("NONE");*/ /* impossible */ |
bc93ee84 | 125 | #ifdef DYING |
cc248aae | 126 | case POPT_ARG_VAL: return POPT_("VAL"); |
bc93ee84 WD |
127 | #else |
128 | case POPT_ARG_VAL: return NULL; | |
129 | #endif | |
cc248aae WD |
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 | /** | |
bc93ee84 WD |
140 | * Display default value for an option. |
141 | * @param lineLength display positions remaining | |
cc248aae WD |
142 | * @param opt option(s) |
143 | * @param translation_domain translation domain | |
bc93ee84 | 144 | * @return |
cc248aae WD |
145 | */ |
146 | static /*@only@*/ /*@null@*/ char * | |
bc93ee84 | 147 | singleOptionDefaultValue(size_t lineLength, |
cc248aae | 148 | const struct poptOption * opt, |
bc93ee84 | 149 | /*@-paramuse@*/ /* FIX: i18n macros disabled with lclint */ |
1526b8b1 | 150 | /*@null@*/ UNUSED(const char * translation_domain)) |
cc248aae WD |
151 | /*@=paramuse@*/ |
152 | /*@*/ | |
153 | { | |
154 | const char * defstr = D_(translation_domain, "default"); | |
bc93ee84 WD |
155 | size_t limit, bufsize = 4*lineLength + 1; |
156 | char * le = malloc(bufsize); | |
cc248aae WD |
157 | char * l = le; |
158 | ||
159 | if (le == NULL) return NULL; /* XXX can't happen */ | |
bc93ee84 | 160 | /*@-boundswrite@*/ |
cc248aae | 161 | *le++ = '('; |
bc93ee84 | 162 | le += strlcpy(le, defstr, bufsize - 3); |
cc248aae WD |
163 | *le++ = ':'; |
164 | *le++ = ' '; | |
bc93ee84 | 165 | limit = bufsize - (le - l) - 1; /* -1 for closing paren */ |
cc248aae WD |
166 | if (opt->arg) /* XXX programmer error */ |
167 | switch (opt->argInfo & POPT_ARG_MASK) { | |
168 | case POPT_ARG_VAL: | |
169 | case POPT_ARG_INT: | |
170 | { long aLong = *((int *)opt->arg); | |
bc93ee84 | 171 | le += snprintf(le, limit, "%ld", aLong); |
cc248aae WD |
172 | } break; |
173 | case POPT_ARG_LONG: | |
174 | { long aLong = *((long *)opt->arg); | |
bc93ee84 | 175 | le += snprintf(le, limit, "%ld", aLong); |
cc248aae WD |
176 | } break; |
177 | case POPT_ARG_FLOAT: | |
178 | { double aDouble = *((float *)opt->arg); | |
bc93ee84 | 179 | le += snprintf(le, limit, "%g", aDouble); |
cc248aae WD |
180 | } break; |
181 | case POPT_ARG_DOUBLE: | |
182 | { double aDouble = *((double *)opt->arg); | |
bc93ee84 | 183 | le += snprintf(le, limit, "%g", aDouble); |
cc248aae WD |
184 | } break; |
185 | case POPT_ARG_STRING: | |
186 | { const char * s = *(const char **)opt->arg; | |
187 | if (s == NULL) { | |
bc93ee84 | 188 | le += strlcpy(le, "null", limit); |
cc248aae | 189 | } else { |
bc93ee84 WD |
190 | size_t len; |
191 | limit -= 2; /* make room for quotes */ | |
cc248aae | 192 | *le++ = '"'; |
bc93ee84 WD |
193 | len = strlcpy(le, s, limit); |
194 | if (len >= limit) { | |
195 | le += limit - 3 - 1; | |
196 | *le++ = '.'; *le++ = '.'; *le++ = '.'; | |
197 | } else | |
198 | le += len; | |
cc248aae WD |
199 | *le++ = '"'; |
200 | } | |
201 | } break; | |
202 | case POPT_ARG_NONE: | |
203 | default: | |
204 | l = _free(l); | |
205 | return NULL; | |
206 | /*@notreached@*/ break; | |
207 | } | |
208 | *le++ = ')'; | |
209 | *le = '\0'; | |
bc93ee84 | 210 | /*@=boundswrite@*/ |
cc248aae WD |
211 | |
212 | return l; | |
62402cb1 MP |
213 | } |
214 | ||
cc248aae | 215 | /** |
bc93ee84 | 216 | * Display help text for an option. |
cc248aae | 217 | * @param fp output file handle |
bc93ee84 | 218 | * @param maxLeftCol largest argument display width |
cc248aae WD |
219 | * @param opt option(s) |
220 | * @param translation_domain translation domain | |
221 | */ | |
bc93ee84 | 222 | static void singleOptionHelp(FILE * fp, size_t maxLeftCol, |
cc248aae | 223 | const struct poptOption * opt, |
bc93ee84 | 224 | /*@null@*/ UNUSED(const char * translation_domain)) |
cc248aae WD |
225 | /*@globals fileSystem @*/ |
226 | /*@modifies *fp, fileSystem @*/ | |
227 | { | |
bc93ee84 WD |
228 | size_t indentLength = maxLeftCol + 5; |
229 | size_t lineLength = 79 - indentLength; | |
230 | const char * help = D_(translation_domain, opt->descrip); | |
cc248aae | 231 | const char * argDescrip = getArgDescrip(opt, translation_domain); |
bc93ee84 WD |
232 | size_t helpLength; |
233 | char * defs = NULL; | |
234 | char * left; | |
235 | size_t lelen, limit; | |
236 | size_t nb = maxLeftCol + 1; | |
237 | int displaypad = 0; | |
cc248aae WD |
238 | |
239 | /* Make sure there's more than enough room in target buffer. */ | |
240 | if (opt->longName) nb += strlen(opt->longName); | |
241 | if (argDescrip) nb += strlen(argDescrip); | |
62402cb1 | 242 | |
bc93ee84 | 243 | /*@-boundswrite@*/ |
cc248aae WD |
244 | left = malloc(nb); |
245 | if (left == NULL) return; /* XXX can't happen */ | |
246 | left[0] = '\0'; | |
247 | left[maxLeftCol] = '\0'; | |
b348deae | 248 | |
62402cb1 | 249 | if (opt->longName && opt->shortName) |
bc93ee84 | 250 | snprintf(left, nb, "-%c, %s%s", opt->shortName, |
cc248aae WD |
251 | ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"), |
252 | opt->longName); | |
253 | else if (opt->shortName != '\0') | |
bc93ee84 | 254 | snprintf(left, nb, "-%c", opt->shortName); |
62402cb1 | 255 | else if (opt->longName) |
bc93ee84 | 256 | snprintf(left, nb, "%s%s", |
cc248aae WD |
257 | ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"), |
258 | opt->longName); | |
259 | if (!*left) goto out; | |
bc93ee84 | 260 | |
62402cb1 | 261 | if (argDescrip) { |
cc248aae WD |
262 | char * le = left + strlen(left); |
263 | ||
264 | if (opt->argInfo & POPT_ARGFLAG_OPTIONAL) | |
265 | *le++ = '['; | |
266 | ||
267 | /* Choose type of output */ | |
268 | /*@-branchstate@*/ | |
269 | if (opt->argInfo & POPT_ARGFLAG_SHOW_DEFAULT) { | |
270 | defs = singleOptionDefaultValue(lineLength, opt, translation_domain); | |
271 | if (defs) { | |
bc93ee84 WD |
272 | size_t bufsize = (help ? strlen(help) : 0) + sizeof " " + strlen(defs); |
273 | char * t = malloc(bufsize); | |
cc248aae | 274 | if (t) { |
bc93ee84 | 275 | snprintf(t, bufsize, "%s %s", help ? help : "", defs); |
cc248aae WD |
276 | defs = _free(defs); |
277 | } | |
278 | defs = t; | |
279 | } | |
280 | } | |
281 | /*@=branchstate@*/ | |
282 | ||
283 | if (opt->argDescrip == NULL) { | |
284 | switch (opt->argInfo & POPT_ARG_MASK) { | |
285 | case POPT_ARG_NONE: | |
286 | break; | |
287 | case POPT_ARG_VAL: | |
bc93ee84 | 288 | #ifdef NOTNOW /* XXX pug ugly nerdy output */ |
cc248aae WD |
289 | { long aLong = opt->val; |
290 | int ops = (opt->argInfo & POPT_ARGFLAG_LOGICALOPS); | |
291 | int negate = (opt->argInfo & POPT_ARGFLAG_NOT); | |
292 | ||
293 | /* Don't bother displaying typical values */ | |
294 | if (!ops && (aLong == 0L || aLong == 1L || aLong == -1L)) | |
295 | break; | |
296 | *le++ = '['; | |
297 | switch (ops) { | |
298 | case POPT_ARGFLAG_OR: | |
299 | *le++ = '|'; | |
300 | /*@innerbreak@*/ break; | |
301 | case POPT_ARGFLAG_AND: | |
302 | *le++ = '&'; | |
303 | /*@innerbreak@*/ break; | |
304 | case POPT_ARGFLAG_XOR: | |
305 | *le++ = '^'; | |
306 | /*@innerbreak@*/ break; | |
307 | default: | |
308 | /*@innerbreak@*/ break; | |
309 | } | |
bc93ee84 | 310 | *le++ = (opt->longName != NULL ? '=' : ' '); |
cc248aae WD |
311 | if (negate) *le++ = '~'; |
312 | /*@-formatconst@*/ | |
bc93ee84 WD |
313 | limit = nb - (le - left); |
314 | lelen = snprintf(le, limit, (ops ? "0x%lx" : "%ld"), aLong); | |
315 | le += lelen >= limit ? limit - 1 : lelen; | |
cc248aae WD |
316 | /*@=formatconst@*/ |
317 | *le++ = ']'; | |
bc93ee84 WD |
318 | } |
319 | #endif | |
320 | break; | |
cc248aae WD |
321 | case POPT_ARG_INT: |
322 | case POPT_ARG_LONG: | |
323 | case POPT_ARG_FLOAT: | |
324 | case POPT_ARG_DOUBLE: | |
325 | case POPT_ARG_STRING: | |
bc93ee84 WD |
326 | *le++ = (opt->longName != NULL ? '=' : ' '); |
327 | limit = nb - (le - left); | |
328 | lelen = strlcpy(le, argDescrip, limit); | |
329 | le += lelen >= limit ? limit - 1 : lelen; | |
cc248aae WD |
330 | break; |
331 | default: | |
332 | break; | |
333 | } | |
334 | } else { | |
bc93ee84 | 335 | |
cc248aae | 336 | *le++ = '='; |
bc93ee84 WD |
337 | limit = nb - (le - left); |
338 | lelen = strlcpy(le, argDescrip, limit); | |
339 | if (lelen >= limit) | |
340 | lelen = limit - 1; | |
341 | le += lelen; | |
342 | ||
343 | #ifdef POPT_WCHAR_HACK | |
344 | { const char * scopy = argDescrip; | |
345 | mbstate_t t; | |
346 | size_t n; | |
347 | ||
348 | memset ((void *)&t, '\0', sizeof (t)); /* In initial state. */ | |
349 | /* Determine number of characters. */ | |
350 | n = mbsrtowcs (NULL, &scopy, strlen(scopy), &t); | |
351 | ||
352 | displaypad = (int) (lelen-n); | |
353 | } | |
354 | #endif | |
cc248aae WD |
355 | } |
356 | if (opt->argInfo & POPT_ARGFLAG_OPTIONAL) | |
357 | *le++ = ']'; | |
358 | *le = '\0'; | |
62402cb1 | 359 | } |
bc93ee84 | 360 | /*@=boundswrite@*/ |
62402cb1 MP |
361 | |
362 | if (help) | |
564782ba | 363 | fprintf(fp," %-*s ", (int)maxLeftCol+displaypad, left); |
62402cb1 | 364 | else { |
cc248aae | 365 | fprintf(fp," %s\n", left); |
b348deae | 366 | goto out; |
62402cb1 MP |
367 | } |
368 | ||
cc248aae | 369 | left = _free(left); |
bc93ee84 | 370 | /*@-branchstate@*/ |
cc248aae | 371 | if (defs) { |
bc93ee84 WD |
372 | help = defs; |
373 | defs = NULL; | |
cc248aae | 374 | } |
bc93ee84 | 375 | /*@=branchstate@*/ |
cc248aae | 376 | |
62402cb1 | 377 | helpLength = strlen(help); |
bc93ee84 | 378 | /*@-boundsread@*/ |
62402cb1 | 379 | while (helpLength > lineLength) { |
bc93ee84 WD |
380 | const char * ch; |
381 | char format[16]; | |
cc248aae | 382 | |
62402cb1 | 383 | ch = help + lineLength - 1; |
a8facdc0 | 384 | while (ch > help && !isSpace(ch)) ch--; |
62402cb1 | 385 | if (ch == help) break; /* give up */ |
a8facdc0 | 386 | while (ch > (help + 1) && isSpace(ch)) ch--; |
62402cb1 MP |
387 | ch++; |
388 | ||
bc93ee84 | 389 | snprintf(format, sizeof format, "%%.%ds\n%%%ds", (int) (ch - help), (int) indentLength); |
cc248aae WD |
390 | /*@-formatconst@*/ |
391 | fprintf(fp, format, help, " "); | |
392 | /*@=formatconst@*/ | |
62402cb1 | 393 | help = ch; |
a8facdc0 | 394 | while (isSpace(help) && *help) help++; |
62402cb1 MP |
395 | helpLength = strlen(help); |
396 | } | |
bc93ee84 | 397 | /*@=boundsread@*/ |
62402cb1 | 398 | |
cc248aae | 399 | if (helpLength) fprintf(fp, "%s\n", help); |
b348deae MP |
400 | |
401 | out: | |
cc248aae WD |
402 | /*@-dependenttrans@*/ |
403 | defs = _free(defs); | |
404 | /*@=dependenttrans@*/ | |
405 | left = _free(left); | |
62402cb1 MP |
406 | } |
407 | ||
cc248aae | 408 | /** |
bc93ee84 | 409 | * Find display width for longest argument string. |
cc248aae WD |
410 | * @param opt option(s) |
411 | * @param translation_domain translation domain | |
bc93ee84 | 412 | * @return display width |
cc248aae | 413 | */ |
bc93ee84 WD |
414 | static size_t maxArgWidth(const struct poptOption * opt, |
415 | /*@null@*/ UNUSED(const char * translation_domain)) | |
cc248aae WD |
416 | /*@*/ |
417 | { | |
bc93ee84 WD |
418 | size_t max = 0; |
419 | size_t len = 0; | |
62402cb1 MP |
420 | const char * s; |
421 | ||
cc248aae | 422 | if (opt != NULL) |
62402cb1 MP |
423 | while (opt->longName || opt->shortName || opt->arg) { |
424 | if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) { | |
cc248aae WD |
425 | if (opt->arg) /* XXX program error */ |
426 | len = maxArgWidth(opt->arg, translation_domain); | |
427 | if (len > max) max = len; | |
62402cb1 | 428 | } else if (!(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) { |
cc248aae WD |
429 | len = sizeof(" ")-1; |
430 | if (opt->shortName != '\0') len += sizeof("-X")-1; | |
431 | if (opt->shortName != '\0' && opt->longName) len += sizeof(", ")-1; | |
62402cb1 | 432 | if (opt->longName) { |
cc248aae WD |
433 | len += ((opt->argInfo & POPT_ARGFLAG_ONEDASH) |
434 | ? sizeof("-")-1 : sizeof("--")-1); | |
435 | len += strlen(opt->longName); | |
62402cb1 MP |
436 | } |
437 | ||
b348deae | 438 | s = getArgDescrip(opt, translation_domain); |
bc93ee84 WD |
439 | |
440 | #ifdef POPT_WCHAR_HACK | |
441 | /* XXX Calculate no. of display characters. */ | |
442 | if (s) { | |
443 | const char * scopy = s; | |
444 | mbstate_t t; | |
445 | size_t n; | |
446 | ||
447 | /*@-boundswrite@*/ | |
448 | memset ((void *)&t, '\0', sizeof (t)); /* In initial state. */ | |
449 | /*@=boundswrite@*/ | |
450 | /* Determine number of characters. */ | |
451 | n = mbsrtowcs (NULL, &scopy, strlen(scopy), &t); | |
452 | len += sizeof("=")-1 + n; | |
453 | } | |
454 | #else | |
62402cb1 | 455 | if (s) |
cc248aae | 456 | len += sizeof("=")-1 + strlen(s); |
bc93ee84 WD |
457 | #endif |
458 | ||
cc248aae WD |
459 | if (opt->argInfo & POPT_ARGFLAG_OPTIONAL) len += sizeof("[]")-1; |
460 | if (len > max) max = len; | |
62402cb1 MP |
461 | } |
462 | ||
463 | opt++; | |
464 | } | |
465 | ||
466 | return max; | |
467 | } | |
468 | ||
cc248aae WD |
469 | /** |
470 | * Display popt alias and exec help. | |
471 | * @param fp output file handle | |
472 | * @param items alias/exec array | |
473 | * @param nitems no. of alias/exec entries | |
bc93ee84 | 474 | * @param left largest argument display width |
cc248aae WD |
475 | * @param translation_domain translation domain |
476 | */ | |
477 | static void itemHelp(FILE * fp, | |
bc93ee84 WD |
478 | /*@null@*/ poptItem items, int nitems, size_t left, |
479 | /*@null@*/ UNUSED(const char * translation_domain)) | |
cc248aae WD |
480 | /*@globals fileSystem @*/ |
481 | /*@modifies *fp, fileSystem @*/ | |
482 | { | |
483 | poptItem item; | |
484 | int i; | |
485 | ||
486 | if (items != NULL) | |
487 | for (i = 0, item = items; i < nitems; i++, item++) { | |
488 | const struct poptOption * opt; | |
489 | opt = &item->option; | |
490 | if ((opt->longName || opt->shortName) && | |
491 | !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) | |
492 | singleOptionHelp(fp, left, opt, translation_domain); | |
493 | } | |
494 | } | |
495 | ||
496 | /** | |
bc93ee84 WD |
497 | * Display help text for a table of options. |
498 | * @param con context | |
cc248aae WD |
499 | * @param fp output file handle |
500 | * @param table option(s) | |
bc93ee84 | 501 | * @param left largest argument display width |
cc248aae WD |
502 | * @param translation_domain translation domain |
503 | */ | |
504 | static void singleTableHelp(poptContext con, FILE * fp, | |
bc93ee84 WD |
505 | /*@null@*/ const struct poptOption * table, size_t left, |
506 | /*@null@*/ UNUSED(const char * translation_domain)) | |
cc248aae WD |
507 | /*@globals fileSystem @*/ |
508 | /*@modifies *fp, fileSystem @*/ | |
509 | { | |
62402cb1 | 510 | const struct poptOption * opt; |
b348deae | 511 | const char *sub_transdom; |
62402cb1 | 512 | |
cc248aae WD |
513 | if (table == poptAliasOptions) { |
514 | itemHelp(fp, con->aliases, con->numAliases, left, NULL); | |
515 | itemHelp(fp, con->execs, con->numExecs, left, NULL); | |
516 | return; | |
517 | } | |
518 | ||
519 | if (table != NULL) | |
520 | for (opt = table; (opt->longName || opt->shortName || opt->arg); opt++) { | |
62402cb1 MP |
521 | if ((opt->longName || opt->shortName) && |
522 | !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) | |
cc248aae | 523 | singleOptionHelp(fp, left, opt, translation_domain); |
62402cb1 MP |
524 | } |
525 | ||
cc248aae WD |
526 | if (table != NULL) |
527 | for (opt = table; (opt->longName || opt->shortName || opt->arg); opt++) { | |
528 | if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_INCLUDE_TABLE) | |
529 | continue; | |
530 | sub_transdom = getTableTranslationDomain(opt->arg); | |
531 | if (sub_transdom == NULL) | |
532 | sub_transdom = translation_domain; | |
b348deae | 533 | |
cc248aae WD |
534 | if (opt->descrip) |
535 | fprintf(fp, "\n%s\n", D_(sub_transdom, opt->descrip)); | |
b348deae | 536 | |
cc248aae | 537 | singleTableHelp(con, fp, opt->arg, left, sub_transdom); |
62402cb1 MP |
538 | } |
539 | } | |
540 | ||
cc248aae WD |
541 | /** |
542 | * @param con context | |
543 | * @param fp output file handle | |
544 | */ | |
545 | static int showHelpIntro(poptContext con, FILE * fp) | |
546 | /*@globals fileSystem @*/ | |
547 | /*@modifies *fp, fileSystem @*/ | |
548 | { | |
62402cb1 | 549 | int len = 6; |
b348deae | 550 | const char * fn; |
62402cb1 | 551 | |
cc248aae | 552 | fprintf(fp, POPT_("Usage:")); |
62402cb1 | 553 | if (!(con->flags & POPT_CONTEXT_KEEP_FIRST)) { |
bc93ee84 WD |
554 | /*@-boundsread@*/ |
555 | /*@-nullderef -type@*/ /* LCL: wazzup? */ | |
62402cb1 | 556 | fn = con->optionStack->argv[0]; |
bc93ee84 WD |
557 | /*@=nullderef =type@*/ |
558 | /*@=boundsread@*/ | |
cc248aae WD |
559 | if (fn == NULL) return len; |
560 | if (strchr(fn, '/')) fn = strrchr(fn, '/') + 1; | |
561 | fprintf(fp, " %s", fn); | |
62402cb1 MP |
562 | len += strlen(fn) + 1; |
563 | } | |
564 | ||
565 | return len; | |
566 | } | |
567 | ||
73042aae | 568 | void poptPrintHelp(poptContext con, FILE * fp, /*@unused@*/ UNUSED(int flags)) |
cc248aae | 569 | { |
bc93ee84 | 570 | size_t leftColWidth; |
62402cb1 | 571 | |
cc248aae | 572 | (void) showHelpIntro(con, fp); |
62402cb1 | 573 | if (con->otherHelp) |
cc248aae | 574 | fprintf(fp, " %s\n", con->otherHelp); |
62402cb1 | 575 | else |
cc248aae | 576 | fprintf(fp, " %s\n", POPT_("[OPTION...]")); |
62402cb1 | 577 | |
b348deae | 578 | leftColWidth = maxArgWidth(con->options, NULL); |
cc248aae | 579 | singleTableHelp(con, fp, con->options, leftColWidth, NULL); |
62402cb1 MP |
580 | } |
581 | ||
cc248aae | 582 | /** |
bc93ee84 | 583 | * Display usage text for an option. |
cc248aae | 584 | * @param fp output file handle |
bc93ee84 | 585 | * @param cursor current display position |
cc248aae WD |
586 | * @param opt option(s) |
587 | * @param translation_domain translation domain | |
588 | */ | |
bc93ee84 | 589 | static size_t singleOptionUsage(FILE * fp, size_t cursor, |
cc248aae WD |
590 | const struct poptOption * opt, |
591 | /*@null@*/ const char *translation_domain) | |
592 | /*@globals fileSystem @*/ | |
593 | /*@modifies *fp, fileSystem @*/ | |
594 | { | |
bc93ee84 | 595 | size_t len = 4; |
b348deae | 596 | char shortStr[2] = { '\0', '\0' }; |
62402cb1 | 597 | const char * item = shortStr; |
b348deae | 598 | const char * argDescrip = getArgDescrip(opt, translation_domain); |
62402cb1 | 599 | |
bc93ee84 WD |
600 | if (opt->shortName != '\0' && opt->longName != NULL) { |
601 | len += 2; | |
602 | if (!(opt->argInfo & POPT_ARGFLAG_ONEDASH)) len++; | |
603 | len += strlen(opt->longName); | |
604 | } else if (opt->shortName != '\0') { | |
62402cb1 | 605 | len++; |
cc248aae | 606 | shortStr[0] = opt->shortName; |
62402cb1 MP |
607 | shortStr[1] = '\0'; |
608 | } else if (opt->longName) { | |
bc93ee84 WD |
609 | len += strlen(opt->longName); |
610 | if (!(opt->argInfo & POPT_ARGFLAG_ONEDASH)) len++; | |
62402cb1 MP |
611 | item = opt->longName; |
612 | } | |
613 | ||
bc93ee84 | 614 | if (len == 4) return cursor; |
62402cb1 | 615 | |
bc93ee84 WD |
616 | #ifdef POPT_WCHAR_HACK |
617 | /* XXX Calculate no. of display characters. */ | |
618 | if (argDescrip) { | |
619 | const char * scopy = argDescrip; | |
620 | mbstate_t t; | |
621 | size_t n; | |
622 | ||
623 | /*@-boundswrite@*/ | |
624 | memset ((void *)&t, '\0', sizeof (t)); /* In initial state. */ | |
625 | /*@=boundswrite@*/ | |
626 | /* Determine number of characters. */ | |
627 | n = mbsrtowcs (NULL, &scopy, strlen(scopy), &t); | |
628 | len += sizeof("=")-1 + n; | |
629 | } | |
630 | #else | |
62402cb1 | 631 | if (argDescrip) |
bc93ee84 WD |
632 | len += sizeof("=")-1 + strlen(argDescrip); |
633 | #endif | |
62402cb1 MP |
634 | |
635 | if ((cursor + len) > 79) { | |
cc248aae | 636 | fprintf(fp, "\n "); |
62402cb1 MP |
637 | cursor = 7; |
638 | } | |
639 | ||
bc93ee84 WD |
640 | if (opt->longName && opt->shortName) { |
641 | fprintf(fp, " [-%c|-%s%s%s%s]", | |
642 | opt->shortName, ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "" : "-"), | |
643 | opt->longName, | |
644 | (argDescrip ? " " : ""), | |
645 | (argDescrip ? argDescrip : "")); | |
646 | } else { | |
647 | fprintf(fp, " [-%s%s%s%s]", | |
648 | ((opt->shortName || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) ? "" : "-"), | |
649 | item, | |
650 | (argDescrip ? (opt->shortName != '\0' ? " " : "=") : ""), | |
651 | (argDescrip ? argDescrip : "")); | |
652 | } | |
62402cb1 MP |
653 | |
654 | return cursor + len + 1; | |
655 | } | |
656 | ||
cc248aae WD |
657 | /** |
658 | * Display popt alias and exec usage. | |
659 | * @param fp output file handle | |
bc93ee84 | 660 | * @param cursor current display position |
cc248aae WD |
661 | * @param item alias/exec array |
662 | * @param nitems no. of ara/exec entries | |
663 | * @param translation_domain translation domain | |
664 | */ | |
bc93ee84 WD |
665 | static size_t itemUsage(FILE * fp, size_t cursor, |
666 | /*@null@*/ poptItem item, int nitems, | |
667 | /*@null@*/ UNUSED(const char * translation_domain)) | |
cc248aae WD |
668 | /*@globals fileSystem @*/ |
669 | /*@modifies *fp, fileSystem @*/ | |
670 | { | |
671 | int i; | |
672 | ||
673 | /*@-branchstate@*/ /* FIX: W2DO? */ | |
674 | if (item != NULL) | |
675 | for (i = 0; i < nitems; i++, item++) { | |
676 | const struct poptOption * opt; | |
677 | opt = &item->option; | |
678 | if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN) { | |
b348deae | 679 | translation_domain = (const char *)opt->arg; |
cc248aae WD |
680 | } else if ((opt->longName || opt->shortName) && |
681 | !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) { | |
682 | cursor = singleOptionUsage(fp, cursor, opt, translation_domain); | |
683 | } | |
684 | } | |
685 | /*@=branchstate@*/ | |
b348deae | 686 | |
cc248aae WD |
687 | return cursor; |
688 | } | |
689 | ||
690 | /** | |
bc93ee84 WD |
691 | * Keep track of option tables already processed. |
692 | */ | |
693 | typedef struct poptDone_s { | |
694 | int nopts; | |
695 | int maxopts; | |
696 | const void ** opts; | |
697 | } * poptDone; | |
698 | ||
699 | /** | |
700 | * Display usage text for a table of options. | |
701 | * @param con context | |
cc248aae | 702 | * @param fp output file handle |
bc93ee84 | 703 | * @param cursor current display position |
cc248aae WD |
704 | * @param opt option(s) |
705 | * @param translation_domain translation domain | |
bc93ee84 WD |
706 | * @param done tables already processed |
707 | * @return | |
cc248aae | 708 | */ |
bc93ee84 WD |
709 | static size_t singleTableUsage(poptContext con, FILE * fp, size_t cursor, |
710 | /*@null@*/ const struct poptOption * opt, | |
711 | /*@null@*/ UNUSED(const char * translation_domain), | |
712 | /*@null@*/ poptDone done) | |
cc248aae | 713 | /*@globals fileSystem @*/ |
bc93ee84 | 714 | /*@modifies *fp, done, fileSystem @*/ |
cc248aae WD |
715 | { |
716 | /*@-branchstate@*/ /* FIX: W2DO? */ | |
717 | if (opt != NULL) | |
718 | for (; (opt->longName || opt->shortName || opt->arg) ; opt++) { | |
719 | if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN) { | |
720 | translation_domain = (const char *)opt->arg; | |
721 | } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) { | |
bc93ee84 WD |
722 | if (done) { |
723 | int i = 0; | |
724 | for (i = 0; i < done->nopts; i++) { | |
725 | /*@-boundsread@*/ | |
726 | const void * that = done->opts[i]; | |
727 | /*@=boundsread@*/ | |
728 | if (that == NULL || that != opt->arg) | |
729 | /*@innercontinue@*/ continue; | |
730 | /*@innerbreak@*/ break; | |
731 | } | |
732 | /* Skip if this table has already been processed. */ | |
733 | if (opt->arg == NULL || i < done->nopts) | |
734 | continue; | |
735 | /*@-boundswrite@*/ | |
736 | if (done->nopts < done->maxopts) | |
737 | done->opts[done->nopts++] = (const void *) opt->arg; | |
738 | /*@=boundswrite@*/ | |
739 | } | |
cc248aae | 740 | cursor = singleTableUsage(con, fp, cursor, opt->arg, |
bc93ee84 | 741 | translation_domain, done); |
cc248aae WD |
742 | } else if ((opt->longName || opt->shortName) && |
743 | !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) { | |
744 | cursor = singleOptionUsage(fp, cursor, opt, translation_domain); | |
745 | } | |
62402cb1 | 746 | } |
cc248aae | 747 | /*@=branchstate@*/ |
62402cb1 MP |
748 | |
749 | return cursor; | |
750 | } | |
751 | ||
cc248aae WD |
752 | /** |
753 | * Return concatenated short options for display. | |
bc93ee84 | 754 | * @todo Sub-tables should be recursed. |
cc248aae WD |
755 | * @param opt option(s) |
756 | * @param fp output file handle | |
757 | * @retval str concatenation of short options | |
758 | * @return length of display string | |
759 | */ | |
760 | static int showShortOptions(const struct poptOption * opt, FILE * fp, | |
761 | /*@null@*/ char * str) | |
762 | /*@globals fileSystem @*/ | |
763 | /*@modifies *str, *fp, fileSystem @*/ | |
bc93ee84 | 764 | /*@requires maxRead(str) >= 0 @*/ |
cc248aae | 765 | { |
bc93ee84 WD |
766 | /* bufsize larger then the ascii set, lazy alloca on top level call. */ |
767 | char * s = (str != NULL ? str : memset(alloca(300), 0, 300)); | |
768 | int len = 0; | |
62402cb1 | 769 | |
894e6299 WD |
770 | if (s == NULL) |
771 | return 0; | |
772 | ||
bc93ee84 | 773 | /*@-boundswrite@*/ |
cc248aae WD |
774 | if (opt != NULL) |
775 | for (; (opt->longName || opt->shortName || opt->arg); opt++) { | |
62402cb1 | 776 | if (opt->shortName && !(opt->argInfo & POPT_ARG_MASK)) |
bc93ee84 | 777 | s[strlen(s)] = opt->shortName; |
62402cb1 | 778 | else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) |
cc248aae | 779 | if (opt->arg) /* XXX program error */ |
bc93ee84 | 780 | len = showShortOptions(opt->arg, fp, s); |
62402cb1 | 781 | } |
bc93ee84 | 782 | /*@=boundswrite@*/ |
62402cb1 | 783 | |
bc93ee84 WD |
784 | /* On return to top level, print the short options, return print length. */ |
785 | if (s == str && *s != '\0') { | |
786 | fprintf(fp, " [-%s]", s); | |
787 | len = strlen(s) + sizeof(" [-]")-1; | |
788 | } | |
789 | return len; | |
62402cb1 MP |
790 | } |
791 | ||
73042aae | 792 | void poptPrintUsage(poptContext con, FILE * fp, /*@unused@*/ UNUSED(int flags)) |
cc248aae | 793 | { |
bc93ee84 WD |
794 | poptDone done = memset(alloca(sizeof(*done)), 0, sizeof(*done)); |
795 | size_t cursor; | |
796 | ||
797 | done->nopts = 0; | |
798 | done->maxopts = 64; | |
799 | cursor = done->maxopts * sizeof(*done->opts); | |
800 | /*@-boundswrite@*/ | |
801 | done->opts = memset(alloca(cursor), 0, cursor); | |
802 | /*@-keeptrans@*/ | |
803 | done->opts[done->nopts++] = (const void *) con->options; | |
804 | /*@=keeptrans@*/ | |
805 | /*@=boundswrite@*/ | |
62402cb1 | 806 | |
cc248aae WD |
807 | cursor = showHelpIntro(con, fp); |
808 | cursor += showShortOptions(con->options, fp, NULL); | |
bc93ee84 WD |
809 | cursor = singleTableUsage(con, fp, cursor, con->options, NULL, done); |
810 | cursor = itemUsage(fp, cursor, con->aliases, con->numAliases, NULL); | |
811 | cursor = itemUsage(fp, cursor, con->execs, con->numExecs, NULL); | |
62402cb1 MP |
812 | |
813 | if (con->otherHelp) { | |
814 | cursor += strlen(con->otherHelp) + 1; | |
cc248aae WD |
815 | if (cursor > 79) fprintf(fp, "\n "); |
816 | fprintf(fp, " %s", con->otherHelp); | |
62402cb1 MP |
817 | } |
818 | ||
cc248aae | 819 | fprintf(fp, "\n"); |
62402cb1 MP |
820 | } |
821 | ||
cc248aae WD |
822 | void poptSetOtherOptionHelp(poptContext con, const char * text) |
823 | { | |
824 | con->otherHelp = _free(con->otherHelp); | |
b348deae | 825 | con->otherHelp = xstrdup(text); |
62402cb1 | 826 | } |