1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2
3/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
4   file accompanying popt source distributions, available from
5   ftp://ftp.redhat.com/pub/code/popt */
6
7#include "system.h"
8#include "poptint.h"
9
10static void displayArgs(poptContext con,
11		/*@unused@*/ enum poptCallbackReason foo,
12		struct poptOption * key,
13		/*@unused@*/ const char * arg, /*@unused@*/ void * data) {
14    if (key->shortName== '?')
15	poptPrintHelp(con, stdout, 0);
16    else
17	poptPrintUsage(con, stdout, 0);
18    exit(0);
19}
20
21struct poptOption poptHelpOptions[] = {
22    { NULL, '\0', POPT_ARG_CALLBACK, (void *)&displayArgs, '\0', NULL, NULL },
23    { "help", '?', 0, NULL, '?', N_("Show this help message"), NULL },
24    { "usage", '\0', 0, NULL, 'u', N_("Display brief usage message"), NULL },
25    { NULL, '\0', 0, NULL, 0, NULL, NULL }
26} ;
27
28
29/*@observer@*/ /*@null@*/ static const char *
30getTableTranslationDomain(const struct poptOption *table)
31{
32  const struct poptOption *opt;
33
34  for(opt = table;
35      opt->longName || opt->shortName || opt->arg;
36      opt++) {
37    if(opt->argInfo == POPT_ARG_INTL_DOMAIN)
38      return opt->arg;
39  }
40
41  return NULL;
42}
43
44/*@observer@*/ /*@null@*/ static const char *
45getArgDescrip(const struct poptOption * opt, const char *translation_domain)
46{
47    if (!(opt->argInfo & POPT_ARG_MASK)) return NULL;
48
49    if (opt == (poptHelpOptions + 1) || opt == (poptHelpOptions + 2))
50	if (opt->argDescrip) return POPT_(opt->argDescrip);
51
52    if (opt->argDescrip) return D_(translation_domain, opt->argDescrip);
53    return POPT_("ARG");
54}
55
56static void singleOptionHelp(FILE * f, int maxLeftCol,
57			     const struct poptOption * opt,
58			     const char *translation_domain) {
59    int indentLength = maxLeftCol + 5;
60    int lineLength = 79 - indentLength;
61    const char * help = D_(translation_domain, opt->descrip);
62    int helpLength;
63    const char * ch;
64    char format[10];
65    char * left;
66    const char * argDescrip = getArgDescrip(opt, translation_domain);
67
68    left = malloc(maxLeftCol + 1);
69    *left = '\0';
70
71    if (opt->longName && opt->shortName)
72	sprintf(left, "-%c, --%s", opt->shortName, opt->longName);
73    else if (opt->shortName)
74	sprintf(left, "-%c", opt->shortName);
75    else if (opt->longName)
76	sprintf(left, "--%s", opt->longName);
77    if (!*left) return ;
78    if (argDescrip) {
79	strcat(left, "=");
80	strcat(left, argDescrip);
81    }
82
83    if (help)
84	fprintf(f,"  %-*s   ", maxLeftCol, left);
85    else {
86	fprintf(f,"  %s\n", left);
87	goto out;
88    }
89
90    helpLength = strlen(help);
91    while (helpLength > lineLength) {
92	ch = help + lineLength - 1;
93	while (ch > help && !isspace(*ch)) ch--;
94	if (ch == help) break;		/* give up */
95	while (ch > (help + 1) && isspace(*ch)) ch--;
96	ch++;
97
98	sprintf(format, "%%.%ds\n%%%ds", (int) (ch - help), indentLength);
99	fprintf(f, format, help, " ");
100	help = ch;
101	while (isspace(*help) && *help) help++;
102	helpLength = strlen(help);
103    }
104
105    if (helpLength) fprintf(f, "%s\n", help);
106
107out:
108    free(left);
109}
110
111static int maxArgWidth(const struct poptOption * opt,
112		       const char * translation_domain) {
113    int max = 0;
114    int this;
115    const char * s;
116
117    while (opt->longName || opt->shortName || opt->arg) {
118	if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
119	    this = maxArgWidth(opt->arg, translation_domain);
120	    if (this > max) max = this;
121	} else if (!(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
122	    this = opt->shortName ? 2 : 0;
123	    if (opt->longName) {
124		if (this) this += 2;
125		this += strlen(opt->longName) + 2;
126	    }
127
128	    s = getArgDescrip(opt, translation_domain);
129	    if (s)
130		this += strlen(s) + 1;
131	    if (this > max) max = this;
132	}
133
134	opt++;
135    }
136
137    return max;
138}
139
140static void singleTableHelp(FILE * f, const struct poptOption * table,
141			    int left,
142			    const char *translation_domain) {
143    const struct poptOption * opt;
144    const char *sub_transdom;
145
146    opt = table;
147    while (opt->longName || opt->shortName || opt->arg) {
148	if ((opt->longName || opt->shortName) &&
149	    !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
150	    singleOptionHelp(f, left, opt, translation_domain);
151	opt++;
152    }
153
154    opt = table;
155    while (opt->longName || opt->shortName || opt->arg) {
156	if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
157	    sub_transdom = getTableTranslationDomain(opt->arg);
158	    if(!sub_transdom)
159		sub_transdom = translation_domain;
160
161	    if (opt->descrip)
162		fprintf(f, "\n%s\n", D_(sub_transdom, opt->descrip));
163
164	    singleTableHelp(f, opt->arg, left, sub_transdom);
165	}
166	opt++;
167    }
168}
169
170static int showHelpIntro(poptContext con, FILE * f) {
171    int len = 6;
172    const char * fn;
173
174    fprintf(f, POPT_("Usage:"));
175    if (!(con->flags & POPT_CONTEXT_KEEP_FIRST)) {
176	fn = con->optionStack->argv[0];
177	if (strchr(fn, '/')) fn = strchr(fn, '/') + 1;
178	fprintf(f, " %s", fn);
179	len += strlen(fn) + 1;
180    }
181
182    return len;
183}
184
185void poptPrintHelp(poptContext con, FILE * f, /*@unused@*/ int flags) {
186    int leftColWidth;
187
188    showHelpIntro(con, f);
189    if (con->otherHelp)
190	fprintf(f, " %s\n", con->otherHelp);
191    else
192	fprintf(f, " %s\n", POPT_("[OPTION...]"));
193
194    leftColWidth = maxArgWidth(con->options, NULL);
195    singleTableHelp(f, con->options, leftColWidth, NULL);
196}
197
198static int singleOptionUsage(FILE * f, int cursor,
199			     const struct poptOption * opt,
200			     const char *translation_domain) {
201    int len = 3;
202    char shortStr[2] = { '\0', '\0' };
203    const char * item = shortStr;
204    const char * argDescrip = getArgDescrip(opt, translation_domain);
205
206    if (opt->shortName) {
207	if (!(opt->argInfo & POPT_ARG_MASK))
208	    return cursor;	/* we did these already */
209	len++;
210	*shortStr = opt->shortName;
211	shortStr[1] = '\0';
212    } else if (opt->longName) {
213	len += 1 + strlen(opt->longName);
214	item = opt->longName;
215    }
216
217    if (len == 3) return cursor;
218
219    if (argDescrip)
220	len += strlen(argDescrip) + 1;
221
222    if ((cursor + len) > 79) {
223	fprintf(f, "\n       ");
224	cursor = 7;
225    }
226
227    fprintf(f, " [-%s%s%s%s]", opt->shortName ? "" : "-", item,
228	    argDescrip ? (opt->shortName ? " " : "=") : "",
229	    argDescrip ? argDescrip : "");
230
231    return cursor + len + 1;
232}
233
234static int singleTableUsage(FILE * f, int cursor, const struct poptOption * table,
235		     const char *translation_domain) {
236    const struct poptOption * opt;
237
238    opt = table;
239    while (opt->longName || opt->shortName || opt->arg) {
240        if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN)
241	    translation_domain = (const char *)opt->arg;
242	else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE)
243	    cursor = singleTableUsage(f, cursor, opt->arg,
244				      translation_domain);
245	else if ((opt->longName || opt->shortName) &&
246		 !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
247	  cursor = singleOptionUsage(f, cursor, opt, translation_domain);
248
249	opt++;
250    }
251
252    return cursor;
253}
254
255static int showShortOptions(const struct poptOption * opt, FILE * f,
256			    char * str) {
257    char s[300];		/* this is larger then the ascii set, so
258				   it should do just fine */
259
260    s[0] = '\0';
261    if (str == NULL) {
262	memset(s, 0, sizeof(s));
263	str = s;
264    }
265
266    while (opt->longName || opt->shortName || opt->arg) {
267	if (opt->shortName && !(opt->argInfo & POPT_ARG_MASK))
268	    str[strlen(str)] = opt->shortName;
269	else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE)
270	    showShortOptions(opt->arg, f, str);
271
272	opt++;
273    }
274
275    if (s != str || !*s)
276	return 0;
277
278    fprintf(f, " [-%s]", s);
279    return strlen(s) + 4;
280}
281
282void poptPrintUsage(poptContext con, FILE * f, /*@unused@*/ int flags) {
283    int cursor;
284
285    cursor = showHelpIntro(con, f);
286    cursor += showShortOptions(con->options, f, NULL);
287    singleTableUsage(f, cursor, con->options, NULL);
288
289    if (con->otherHelp) {
290	cursor += strlen(con->otherHelp) + 1;
291	if (cursor > 79) fprintf(f, "\n       ");
292	fprintf(f, " %s", con->otherHelp);
293    }
294
295    fprintf(f, "\n");
296}
297
298void poptSetOtherOptionHelp(poptContext con, const char * text) {
299    if (con->otherHelp) xfree(con->otherHelp);
300    con->otherHelp = xstrdup(text);
301}
302