IPv6 support is now merged.
[rsync.git] / popt / popthelp.c
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
10 static 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
21 struct 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 *const
30 getTableTranslationDomain(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 *const
45 getArgDescrip(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
56 static 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
107 out:
108     free(left);
109 }
110
111 static 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
140 static 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
170 static 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
185 void 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
198 static 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
234 static 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
255 static 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
282 void 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
298 void poptSetOtherOptionHelp(poptContext con, const char * text) {
299     if (con->otherHelp) xfree(con->otherHelp);
300     con->otherHelp = xstrdup(text);
301 }