Mark a couple unused variables.
[rsync.git] / popt / popthelp.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2
3 /*@-type@*/
4 /** \ingroup popt
5  * \file popt/popthelp.c
6  */
7
8 /* (C) 1998-2000 Red Hat, Inc. -- Licensing details are in the COPYING
9    file accompanying popt source distributions, available from 
10    ftp://ftp.rpm.org/pub/rpm/dist. */
11
12 #include "system.h"
13 #include "poptint.h"
14
15 /**
16  * @param con           context
17  * @param key           option(s)
18  */
19 static void displayArgs(poptContext con,
20                 /*@unused@*/ UNUSED(enum poptCallbackReason foo),
21                 struct poptOption * key, 
22                 /*@unused@*/ UNUSED(const char * arg), /*@unused@*/ UNUSED(void * data))
23         /*@globals fileSystem@*/
24         /*@modifies fileSystem@*/
25 {
26     if (key->shortName == '?')
27         poptPrintHelp(con, stdout, 0);
28     else
29         poptPrintUsage(con, stdout, 0);
30     exit(0);
31 }
32
33 #ifdef  NOTYET
34 /*@unchecked@*/
35 static int show_option_defaults = 0;
36 #endif
37
38 /**
39  * Empty table marker to enable displaying popt alias/exec options.
40  */
41 /*@observer@*/ /*@unchecked@*/
42 struct poptOption poptAliasOptions[] = {
43     POPT_TABLEEND
44 };
45
46 /**
47  * Auto help table options.
48  */
49 /*@-castfcnptr@*/
50 /*@observer@*/ /*@unchecked@*/
51 struct poptOption poptHelpOptions[] = {
52   { NULL, '\0', POPT_ARG_CALLBACK, (void *)&displayArgs, '\0', NULL, NULL },
53   { "help", '?', 0, NULL, '?', N_("Show this help message"), NULL },
54   { "usage", '\0', 0, NULL, 'u', N_("Display brief usage message"), NULL },
55 #ifdef  NOTYET
56   { "defaults", '\0', POPT_ARG_NONE, &show_option_defaults, 0,
57         N_("Display option defaults in message"), NULL },
58 #endif
59     POPT_TABLEEND
60 } ;
61 /*@=castfcnptr@*/
62
63 /**
64  * @param table         option(s)
65  */
66 /*@observer@*/ /*@null@*/ static const char *
67 getTableTranslationDomain(/*@null@*/ const struct poptOption *table)
68         /*@*/
69 {
70     const struct poptOption *opt;
71
72     if (table != NULL)
73     for (opt = table; opt->longName || opt->shortName || opt->arg; opt++) {
74         if (opt->argInfo == POPT_ARG_INTL_DOMAIN)
75             return opt->arg;
76     }
77     return NULL;
78 }
79
80 /**
81  * @param opt           option(s)
82  * @param translation_domain    translation domain
83  */
84 /*@observer@*/ /*@null@*/ static const char *
85 getArgDescrip(const struct poptOption * opt,
86                 /*@-paramuse@*/         /* FIX: wazzup? */
87                 /*@null@*/ UNUSED(const char * translation_domain))
88                 /*@=paramuse@*/
89         /*@*/
90 {
91     if (!(opt->argInfo & POPT_ARG_MASK)) return NULL;
92
93     if (opt == (poptHelpOptions + 1) || opt == (poptHelpOptions + 2))
94         if (opt->argDescrip) return POPT_(opt->argDescrip);
95
96     if (opt->argDescrip) return D_(translation_domain, opt->argDescrip);
97
98     switch (opt->argInfo & POPT_ARG_MASK) {
99     case POPT_ARG_NONE:         return POPT_("NONE");
100     case POPT_ARG_VAL:          return POPT_("VAL");
101     case POPT_ARG_INT:          return POPT_("INT");
102     case POPT_ARG_LONG:         return POPT_("LONG");
103     case POPT_ARG_STRING:       return POPT_("STRING");
104     case POPT_ARG_FLOAT:        return POPT_("FLOAT");
105     case POPT_ARG_DOUBLE:       return POPT_("DOUBLE");
106     default:                    return POPT_("ARG");
107     }
108 }
109
110 /**
111  * @param opt           option(s)
112  * @param translation_domain    translation domain
113  */
114 static /*@only@*/ /*@null@*/ char *
115 singleOptionDefaultValue(int lineLength,
116                 const struct poptOption * opt,
117                 /*@-paramuse@*/ /* FIX: i18n macros disable with lclint */
118                 /*@null@*/ UNUSED(const char * translation_domain))
119                 /*@=paramuse@*/
120         /*@*/
121 {
122     const char * defstr = D_(translation_domain, "default");
123     char * le = malloc(4*lineLength + 1);
124     char * l = le;
125
126     if (le == NULL) return NULL;        /* XXX can't happen */
127     *le = '\0';
128     *le++ = '(';
129     strcpy(le, defstr); le += strlen(le);
130     *le++ = ':';
131     *le++ = ' ';
132     if (opt->arg)       /* XXX programmer error */
133     switch (opt->argInfo & POPT_ARG_MASK) {
134     case POPT_ARG_VAL:
135     case POPT_ARG_INT:
136     {   long aLong = *((int *)opt->arg);
137         sprintf(le, "%ld", aLong);
138         le += strlen(le);
139     }   break;
140     case POPT_ARG_LONG:
141     {   long aLong = *((long *)opt->arg);
142         sprintf(le, "%ld", aLong);
143         le += strlen(le);
144     }   break;
145     case POPT_ARG_FLOAT:
146     {   double aDouble = *((float *)opt->arg);
147         sprintf(le, "%g", aDouble);
148         le += strlen(le);
149     }   break;
150     case POPT_ARG_DOUBLE:
151     {   double aDouble = *((double *)opt->arg);
152         sprintf(le, "%g", aDouble);
153         le += strlen(le);
154     }   break;
155     case POPT_ARG_STRING:
156     {   const char * s = *(const char **)opt->arg;
157         if (s == NULL) {
158             strcpy(le, "null"); le += strlen(le);
159         } else {
160             size_t slen = 4*lineLength - (le - l) - sizeof("\"...\")");
161             *le++ = '"';
162             strncpy(le, s, slen); le[slen] = '\0'; le += strlen(le);    
163             if (slen < strlen(s)) {
164                 strcpy(le, "...");      le += strlen(le);
165             }
166             *le++ = '"';
167         }
168     }   break;
169     case POPT_ARG_NONE:
170     default:
171         l = _free(l);
172         return NULL;
173         /*@notreached@*/ break;
174     }
175     *le++ = ')';
176     *le = '\0';
177
178     return l;
179 }
180
181 /**
182  * @param fp            output file handle
183  * @param opt           option(s)
184  * @param translation_domain    translation domain
185  */
186 static void singleOptionHelp(FILE * fp, int maxLeftCol, 
187                 const struct poptOption * opt,
188                 /*@null@*/ const char * translation_domain)
189         /*@globals fileSystem @*/
190         /*@modifies *fp, fileSystem @*/
191 {
192     int indentLength = maxLeftCol + 5;
193     int lineLength = 79 - indentLength;
194     const unsigned char * help = D_(translation_domain, opt->descrip);
195     const char * argDescrip = getArgDescrip(opt, translation_domain);
196     int helpLength;
197     unsigned char * defs = NULL;
198     unsigned char * left;
199     int nb = maxLeftCol + 1;
200
201     /* Make sure there's more than enough room in target buffer. */
202     if (opt->longName)  nb += strlen(opt->longName);
203     if (argDescrip)     nb += strlen(argDescrip);
204
205     left = malloc(nb);
206     if (left == NULL) return;   /* XXX can't happen */
207     left[0] = '\0';
208     left[maxLeftCol] = '\0';
209
210     if (opt->longName && opt->shortName)
211         sprintf(left, "-%c, %s%s", opt->shortName,
212                 ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"),
213                 opt->longName);
214     else if (opt->shortName != '\0') 
215         sprintf(left, "-%c", opt->shortName);
216     else if (opt->longName)
217         sprintf(left, "%s%s",
218                 ((opt->argInfo & POPT_ARGFLAG_ONEDASH) ? "-" : "--"),
219                 opt->longName);
220     if (!*left) goto out;
221     if (argDescrip) {
222         char * le = left + strlen(left);
223
224         if (opt->argInfo & POPT_ARGFLAG_OPTIONAL)
225             *le++ = '[';
226
227         /* Choose type of output */
228         /*@-branchstate@*/
229         if (opt->argInfo & POPT_ARGFLAG_SHOW_DEFAULT) {
230             defs = singleOptionDefaultValue(lineLength, opt, translation_domain);
231             if (defs) {
232                 char * t = malloc((help ? strlen(help) : 0) +
233                                 strlen(defs) + sizeof(" "));
234                 if (t) {
235                     char * te = t;
236                     *te = '\0';
237                     if (help) {
238                         strcpy(te, help);       te += strlen(te);
239                     }
240                     *te++ = ' ';
241                     strcpy(te, defs);
242                     defs = _free(defs);
243                 }
244                 defs = t;
245             }
246         }
247         /*@=branchstate@*/
248
249         if (opt->argDescrip == NULL) {
250             switch (opt->argInfo & POPT_ARG_MASK) {
251             case POPT_ARG_NONE:
252                 break;
253             case POPT_ARG_VAL:
254             {   long aLong = opt->val;
255                 int ops = (opt->argInfo & POPT_ARGFLAG_LOGICALOPS);
256                 int negate = (opt->argInfo & POPT_ARGFLAG_NOT);
257
258                 /* Don't bother displaying typical values */
259                 if (!ops && (aLong == 0L || aLong == 1L || aLong == -1L))
260                     break;
261                 *le++ = '[';
262                 switch (ops) {
263                 case POPT_ARGFLAG_OR:
264                     *le++ = '|';
265                     /*@innerbreak@*/ break;
266                 case POPT_ARGFLAG_AND:
267                     *le++ = '&';
268                     /*@innerbreak@*/ break;
269                 case POPT_ARGFLAG_XOR:
270                     *le++ = '^';
271                     /*@innerbreak@*/ break;
272                 default:
273                     /*@innerbreak@*/ break;
274                 }
275                 *le++ = '=';
276                 if (negate) *le++ = '~';
277                 /*@-formatconst@*/
278                 sprintf(le, (ops ? "0x%lx" : "%ld"), aLong);
279                 le += strlen(le);
280                 /*@=formatconst@*/
281                 *le++ = ']';
282             }   break;
283             case POPT_ARG_INT:
284             case POPT_ARG_LONG:
285             case POPT_ARG_FLOAT:
286             case POPT_ARG_DOUBLE:
287             case POPT_ARG_STRING:
288                 *le++ = '=';
289                 strcpy(le, argDescrip);         le += strlen(le);
290                 break;
291             default:
292                 break;
293             }
294         } else {
295             *le++ = '=';
296             strcpy(le, argDescrip);             le += strlen(le);
297         }
298         if (opt->argInfo & POPT_ARGFLAG_OPTIONAL)
299             *le++ = ']';
300         *le = '\0';
301     }
302
303     if (help)
304         fprintf(fp,"  %-*s   ", maxLeftCol, left);
305     else {
306         fprintf(fp,"  %s\n", left); 
307         goto out;
308     }
309
310     left = _free(left);
311     if (defs) {
312         help = defs; defs = NULL;
313     }
314
315     helpLength = strlen(help);
316     while (helpLength > lineLength) {
317         const unsigned char * ch;
318         char format[10];
319
320         ch = help + lineLength - 1;
321         while (ch > help && !isspace(*ch)) ch--;
322         if (ch == help) break;          /* give up */
323         while (ch > (help + 1) && isspace(*ch)) ch--;
324         ch++;
325
326         sprintf(format, "%%.%ds\n%%%ds", (int) (ch - help), indentLength);
327         /*@-formatconst@*/
328         fprintf(fp, format, help, " ");
329         /*@=formatconst@*/
330         help = ch;
331         while (isspace(*help) && *help) help++;
332         helpLength = strlen(help);
333     }
334
335     if (helpLength) fprintf(fp, "%s\n", help);
336
337 out:
338     /*@-dependenttrans@*/
339     defs = _free(defs);
340     /*@=dependenttrans@*/
341     left = _free(left);
342 }
343
344 /**
345  * @param opt           option(s)
346  * @param translation_domain    translation domain
347  */
348 static int maxArgWidth(const struct poptOption * opt,
349                        /*@null@*/ const char * translation_domain)
350         /*@*/
351 {
352     int max = 0;
353     int len = 0;
354     const char * s;
355     
356     if (opt != NULL)
357     while (opt->longName || opt->shortName || opt->arg) {
358         if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
359             if (opt->arg)       /* XXX program error */
360             len = maxArgWidth(opt->arg, translation_domain);
361             if (len > max) max = len;
362         } else if (!(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
363             len = sizeof("  ")-1;
364             if (opt->shortName != '\0') len += sizeof("-X")-1;
365             if (opt->shortName != '\0' && opt->longName) len += sizeof(", ")-1;
366             if (opt->longName) {
367                 len += ((opt->argInfo & POPT_ARGFLAG_ONEDASH)
368                         ? sizeof("-")-1 : sizeof("--")-1);
369                 len += strlen(opt->longName);
370             }
371
372             s = getArgDescrip(opt, translation_domain);
373             if (s)
374                 len += sizeof("=")-1 + strlen(s);
375             if (opt->argInfo & POPT_ARGFLAG_OPTIONAL) len += sizeof("[]")-1;
376             if (len > max) max = len;
377         }
378
379         opt++;
380     }
381     
382     return max;
383 }
384
385 /**
386  * Display popt alias and exec help.
387  * @param fp            output file handle
388  * @param items         alias/exec array
389  * @param nitems        no. of alias/exec entries
390  * @param translation_domain    translation domain
391  */
392 static void itemHelp(FILE * fp,
393                 /*@null@*/ poptItem items, int nitems, int left,
394                 /*@null@*/ const char * translation_domain)
395         /*@globals fileSystem @*/
396         /*@modifies *fp, fileSystem @*/
397 {
398     poptItem item;
399     int i;
400
401     if (items != NULL)
402     for (i = 0, item = items; i < nitems; i++, item++) {
403         const struct poptOption * opt;
404         opt = &item->option;
405         if ((opt->longName || opt->shortName) && 
406             !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
407             singleOptionHelp(fp, left, opt, translation_domain);
408     }
409 }
410
411 /**
412  * @param fp            output file handle
413  * @param table         option(s)
414  * @param translation_domain    translation domain
415  */
416 static void singleTableHelp(poptContext con, FILE * fp,
417                 /*@null@*/ const struct poptOption * table, int left,
418                 /*@null@*/ const char * translation_domain)
419         /*@globals fileSystem @*/
420         /*@modifies *fp, fileSystem @*/
421 {
422     const struct poptOption * opt;
423     const char *sub_transdom;
424
425     if (table == poptAliasOptions) {
426         itemHelp(fp, con->aliases, con->numAliases, left, NULL);
427         itemHelp(fp, con->execs, con->numExecs, left, NULL);
428         return;
429     }
430
431     if (table != NULL)
432     for (opt = table; (opt->longName || opt->shortName || opt->arg); opt++) {
433         if ((opt->longName || opt->shortName) && 
434             !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
435             singleOptionHelp(fp, left, opt, translation_domain);
436     }
437
438     if (table != NULL)
439     for (opt = table; (opt->longName || opt->shortName || opt->arg); opt++) {
440         if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_INCLUDE_TABLE)
441             continue;
442         sub_transdom = getTableTranslationDomain(opt->arg);
443         if (sub_transdom == NULL)
444             sub_transdom = translation_domain;
445             
446         if (opt->descrip)
447             fprintf(fp, "\n%s\n", D_(sub_transdom, opt->descrip));
448
449         singleTableHelp(con, fp, opt->arg, left, sub_transdom);
450     }
451 }
452
453 /**
454  * @param con           context
455  * @param fp            output file handle
456  */
457 static int showHelpIntro(poptContext con, FILE * fp)
458         /*@globals fileSystem @*/
459         /*@modifies *fp, fileSystem @*/
460 {
461     int len = 6;
462     const char * fn;
463
464     fprintf(fp, POPT_("Usage:"));
465     if (!(con->flags & POPT_CONTEXT_KEEP_FIRST)) {
466         /*@-nullderef@*/        /* LCL: wazzup? */
467         fn = con->optionStack->argv[0];
468         /*@=nullderef@*/
469         if (fn == NULL) return len;
470         if (strchr(fn, '/')) fn = strrchr(fn, '/') + 1;
471         fprintf(fp, " %s", fn);
472         len += strlen(fn) + 1;
473     }
474
475     return len;
476 }
477
478 void poptPrintHelp(poptContext con, FILE * fp, /*@unused@*/ UNUSED(int flags))
479 {
480     int leftColWidth;
481
482     (void) showHelpIntro(con, fp);
483     if (con->otherHelp)
484         fprintf(fp, " %s\n", con->otherHelp);
485     else
486         fprintf(fp, " %s\n", POPT_("[OPTION...]"));
487
488     leftColWidth = maxArgWidth(con->options, NULL);
489     singleTableHelp(con, fp, con->options, leftColWidth, NULL);
490 }
491
492 /**
493  * @param fp            output file handle
494  * @param opt           option(s)
495  * @param translation_domain    translation domain
496  */
497 static int singleOptionUsage(FILE * fp, int cursor, 
498                 const struct poptOption * opt,
499                 /*@null@*/ const char *translation_domain)
500         /*@globals fileSystem @*/
501         /*@modifies *fp, fileSystem @*/
502 {
503     int len = 3;
504     char shortStr[2] = { '\0', '\0' };
505     const char * item = shortStr;
506     const char * argDescrip = getArgDescrip(opt, translation_domain);
507
508     if (opt->shortName!= '\0' ) {
509         if (!(opt->argInfo & POPT_ARG_MASK)) 
510             return cursor;      /* we did these already */
511         len++;
512         shortStr[0] = opt->shortName;
513         shortStr[1] = '\0';
514     } else if (opt->longName) {
515         len += 1 + strlen(opt->longName);
516         item = opt->longName;
517     }
518
519     if (len == 3) return cursor;
520
521     if (argDescrip) 
522         len += strlen(argDescrip) + 1;
523
524     if ((cursor + len) > 79) {
525         fprintf(fp, "\n       ");
526         cursor = 7;
527     } 
528
529     fprintf(fp, " [-%s%s%s%s]",
530         ((opt->shortName || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) ? "" : "-"),
531         item,
532         (argDescrip ? (opt->shortName != '\0' ? " " : "=") : ""),
533         (argDescrip ? argDescrip : ""));
534
535     return cursor + len + 1;
536 }
537
538 /**
539  * Display popt alias and exec usage.
540  * @param fp            output file handle
541  * @param item          alias/exec array
542  * @param nitems        no. of ara/exec entries
543  * @param translation_domain    translation domain
544  */
545 static int itemUsage(FILE * fp, int cursor, poptItem item, int nitems,
546                 /*@null@*/ const char * translation_domain)
547         /*@globals fileSystem @*/
548         /*@modifies *fp, fileSystem @*/
549 {
550     int i;
551
552     /*@-branchstate@*/          /* FIX: W2DO? */
553     if (item != NULL)
554     for (i = 0; i < nitems; i++, item++) {
555         const struct poptOption * opt;
556         opt = &item->option;
557         if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN) {
558             translation_domain = (const char *)opt->arg;
559         } else if ((opt->longName || opt->shortName) &&
560                  !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
561             cursor = singleOptionUsage(fp, cursor, opt, translation_domain);
562         }
563     }
564     /*@=branchstate@*/
565
566     return cursor;
567 }
568
569 /**
570  * @param fp            output file handle
571  * @param opt           option(s)
572  * @param translation_domain    translation domain
573  */
574 static int singleTableUsage(poptContext con, FILE * fp,
575                 int cursor, const struct poptOption * opt,
576                 /*@null@*/ const char * translation_domain)
577         /*@globals fileSystem @*/
578         /*@modifies *fp, fileSystem @*/
579 {
580     /*@-branchstate@*/          /* FIX: W2DO? */
581     if (opt != NULL)
582     for (; (opt->longName || opt->shortName || opt->arg) ; opt++) {
583         if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN) {
584             translation_domain = (const char *)opt->arg;
585         } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
586             if (opt->arg)       /* XXX program error */
587             cursor = singleTableUsage(con, fp, cursor, opt->arg,
588                         translation_domain);
589         } else if ((opt->longName || opt->shortName) &&
590                  !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
591             cursor = singleOptionUsage(fp, cursor, opt, translation_domain);
592         }
593     }
594     /*@=branchstate@*/
595
596     return cursor;
597 }
598
599 /**
600  * Return concatenated short options for display.
601  * @param opt           option(s)
602  * @param fp            output file handle
603  * @retval str          concatenation of short options
604  * @return              length of display string
605  */
606 static int showShortOptions(const struct poptOption * opt, FILE * fp,
607                 /*@null@*/ char * str)
608         /*@globals fileSystem @*/
609         /*@modifies *str, *fp, fileSystem @*/
610 {
611     char * s = alloca(300);     /* larger then the ascii set */
612
613     s[0] = '\0';
614     /*@-branchstate@*/          /* FIX: W2DO? */
615     if (str == NULL) {
616         memset(s, 0, sizeof(s));
617         str = s;
618     }
619     /*@=branchstate@*/
620
621     if (opt != NULL)
622     for (; (opt->longName || opt->shortName || opt->arg); opt++) {
623         if (opt->shortName && !(opt->argInfo & POPT_ARG_MASK))
624             str[strlen(str)] = opt->shortName;
625         else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE)
626             if (opt->arg)       /* XXX program error */
627                 (void) showShortOptions(opt->arg, fp, str);
628     } 
629
630     if (s != str || *s != '\0')
631         return 0;
632
633     fprintf(fp, " [-%s]", s);
634     return strlen(s) + 4;
635 }
636
637 void poptPrintUsage(poptContext con, FILE * fp, /*@unused@*/ UNUSED(int flags))
638 {
639     int cursor;
640
641     cursor = showHelpIntro(con, fp);
642     cursor += showShortOptions(con->options, fp, NULL);
643     (void) singleTableUsage(con, fp, cursor, con->options, NULL);
644     (void) itemUsage(fp, cursor, con->aliases, con->numAliases, NULL);
645     (void) itemUsage(fp, cursor, con->execs, con->numExecs, NULL);
646
647     if (con->otherHelp) {
648         cursor += strlen(con->otherHelp) + 1;
649         if (cursor > 79) fprintf(fp, "\n       ");
650         fprintf(fp, " %s", con->otherHelp);
651     }
652
653     fprintf(fp, "\n");
654 }
655
656 void poptSetOtherOptionHelp(poptContext con, const char * text)
657 {
658     con->otherHelp = _free(con->otherHelp);
659     con->otherHelp = xstrdup(text);
660 }
661 /*@=type@*/