]> mj.ucw.cz Git - libucw.git/blob - ucw/opt-help.c
4166cc5f717e627e41e993b0a7e7d53dd83f5885
[libucw.git] / ucw / opt-help.c
1 /*
2  *      UCW Library -- Formatting of command-line option help
3  *
4  *      (c) 2013 Jan Moskyto Matejka <mq@ucw.cz>
5  *      (c) 2014 Martin Mares <mj@ucw.cz>
6  *
7  *      This software may be freely distributed and used according to the terms
8  *      of the GNU Lesser General Public License.
9  */
10
11 #include <ucw/lib.h>
12 #include <ucw/opt.h>
13 #include <ucw/opt-internal.h>
14 #include <ucw/mempool.h>
15 #include <ucw/gary.h>
16
17 #include <string.h>
18
19 struct help {
20   struct mempool *pool;
21   struct help_line *lines;                      // A growing array of lines
22 };
23
24 struct help_line {
25   const char *extra;
26   char *fields[3];
27 };
28
29 static void opt_help_scan_item(struct help *h, struct opt_precomputed *opt)
30 {
31   struct opt_item *item = opt->item;
32
33   if (opt->flags & OPT_NO_HELP)
34     return;
35
36   if (item->cls == OPT_CL_HELP) {
37     struct help_line *l = GARY_PUSH(h->lines, 1);
38     l->extra = item->help ? : "";
39     return;
40   }
41
42   if (item->letter >= OPT_POSITIONAL_TAIL)
43     return;
44
45   struct help_line *first = GARY_PUSH(h->lines, 1);
46   if (item->help) {
47     char *text = mp_strdup(h->pool, item->help);
48     struct help_line *l = first;
49     while (text) {
50       char *eol = strchr(text, '\n');
51       if (eol)
52         *eol++ = 0;
53
54       int field = (l == first ? 1 : 0);
55       char *f = text;
56       while (f) {
57         char *tab = strchr(f, '\t');
58         if (tab)
59           *tab++ = 0;
60         if (field < 3)
61           l->fields[field++] = f;
62         f = tab;
63       }
64
65       text = eol;
66       if (text)
67         l = GARY_PUSH(h->lines, 1);
68     }
69   }
70
71   if (item->name) {
72     char *val = first->fields[1] ? : "";
73     if (opt->flags & OPT_REQUIRED_VALUE)
74       val = mp_printf(h->pool, "=%s", val);
75     else if (!(opt->flags & OPT_NO_VALUE))
76       val = mp_printf(h->pool, "[=%s]", val);
77     first->fields[1] = mp_printf(h->pool, "--%s%s", item->name, val);
78   }
79
80   if (item->letter) {
81     if (item->name)
82       first->fields[0] = mp_printf(h->pool, "-%c, ", item->letter);
83     else {
84       char *val = first->fields[1] ? : "";
85       if (!(opt->flags & OPT_REQUIRED_VALUE) && !(opt->flags & OPT_NO_VALUE))
86         val = mp_printf(h->pool, "[%s]", val);
87       first->fields[0] = mp_printf(h->pool, "-%c%s", item->letter, val);
88       first->fields[1] = NULL;
89     }
90   }
91 }
92
93 static void opt_help_scan(struct help *h, const struct opt_section *sec)
94 {
95   for (struct opt_item * item = sec->opt; item->cls != OPT_CL_END; item++) {
96     if (item->cls == OPT_CL_SECTION)
97       opt_help_scan(h, item->u.section);
98     else if (item->cls == OPT_CL_HOOK)
99       ;
100     else {
101       struct opt_precomputed opt;
102       opt_precompute(&opt, item);
103       opt_help_scan_item(h, &opt);
104     }
105   }
106 }
107
108 void opt_help(const struct opt_section * sec) {
109   // Prepare help text
110   struct help h;
111   h.pool = mp_new(4096);
112   GARY_INIT_ZERO(h.lines, 0);
113   opt_help_scan(&h, sec);
114
115   // Calculate natural width of each column
116   uns n = GARY_SIZE(h.lines);
117   uns widths[3] = { 0, 0, 0 };
118   for (uns i=0; i<n; i++) {
119     struct help_line *l = &h.lines[i];
120     for (uns f=0; f<3; f++) {
121       if (!l->fields[f])
122         l->fields[f] = "";
123       uns w = strlen(l->fields[f]);
124       widths[f] = MAX(widths[f], w);
125     }
126   }
127   if (widths[0] > 4) {
128     /*
129      *  This is tricky: if there are short options, which have an argument,
130      *  but no long variant, we are willing to let column 0 overflow to column 1.
131      */
132     widths[1] = MAX(widths[1], widths[0] - 4);
133     widths[0] = 4;
134   }
135   widths[1] += 4;
136
137   // Print columns
138   for (uns i=0; i<n; i++) {
139     struct help_line *l = &h.lines[i];
140     if (l->extra)
141       puts(l->extra);
142     else {
143       int t = 0;
144       for (uns f=0; f<3; f++) {
145         t += widths[f];
146         t -= printf("%s", l->fields[f]);
147         while (t > 0) {
148           putchar(' ');
149           t--;
150         }
151       }
152       putchar('\n');
153     }
154   }
155
156   // Clean up
157   GARY_FREE(h.lines);
158   mp_delete(h.pool);
159 }
160
161 void opt_handle_help(struct opt_item * opt UNUSED, const char * value UNUSED, void * data)
162 {
163   struct opt_context *oc = data;
164   opt_help(oc->options);
165   exit(0);
166 }