]> mj.ucw.cz Git - libucw.git/blob - ucw/opt-help.c
Extended types: Cleaned up unit name parsing
[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   const struct opt_item *item = opt->item;
32
33   if (!item->help)
34     return;
35
36   bool force_col1 = 0;
37   if (item->cls == OPT_CL_HELP) {
38     if (item->flags & OPT_HELP_COL) {
39       force_col1 = 1;
40     } else {
41       struct help_line *l = GARY_PUSH(h->lines);
42       l->extra = item->help;
43       return;
44     }
45   }
46
47   if (item->letter >= OPT_POSITIONAL_TAIL)
48     return;
49
50   struct help_line *first = GARY_PUSH(h->lines);
51   char *text = mp_strdup(h->pool, item->help);
52   struct help_line *l = first;
53   while (text) {
54     char *eol = strchr(text, '\n');
55     if (eol)
56       *eol++ = 0;
57
58     int field = (l == first && !force_col1 ? 1 : 0);
59     char *f = text;
60     while (f) {
61       char *tab = strchr(f, '\t');
62       if (tab)
63         *tab++ = 0;
64       if (field < 3)
65         l->fields[field++] = f;
66       f = tab;
67     }
68
69     text = eol;
70     if (text)
71       l = GARY_PUSH(h->lines);
72   }
73
74   if (item->name) {
75     char *val = first->fields[1] ? : "";
76     if (opt->flags & OPT_REQUIRED_VALUE)
77       val = mp_printf(h->pool, "=%s", val);
78     else if (!(opt->flags & OPT_NO_VALUE))
79       val = mp_printf(h->pool, "[=%s]", val);
80     first->fields[1] = mp_printf(h->pool, "--%s%s", item->name, val);
81   }
82
83   if (item->letter) {
84     if (item->name)
85       first->fields[0] = mp_printf(h->pool, "-%c, ", item->letter);
86     else {
87       char *val = first->fields[1] ? : "";
88       if (!(opt->flags & OPT_REQUIRED_VALUE) && !(opt->flags & OPT_NO_VALUE))
89         val = mp_printf(h->pool, "[%s]", val);
90       first->fields[0] = mp_printf(h->pool, "-%c%s", item->letter, val);
91       first->fields[1] = NULL;
92     }
93   }
94 }
95
96 static void opt_help_scan(struct help *h, const struct opt_section *sec)
97 {
98   for (const struct opt_item * item = sec->opt; item->cls != OPT_CL_END; item++) {
99     if (item->cls == OPT_CL_SECTION)
100       opt_help_scan(h, item->u.section);
101     else if (item->cls == OPT_CL_HOOK)
102       ;
103     else {
104       struct opt_precomputed opt;
105       opt_precompute(&opt, item);
106       opt_help_scan_item(h, &opt);
107     }
108   }
109 }
110
111 void opt_help(const struct opt_section * sec) {
112   // Prepare help text
113   struct help h;
114   h.pool = mp_new(4096);
115   GARY_INIT_ZERO(h.lines, 0);
116   opt_help_scan(&h, sec);
117
118   // Calculate natural width of each column
119   uint n = GARY_SIZE(h.lines);
120   uint widths[3] = { 0, 0, 0 };
121   for (uint i=0; i<n; i++) {
122     struct help_line *l = &h.lines[i];
123     for (uint f=0; f<3; f++) {
124       if (!l->fields[f])
125         l->fields[f] = "";
126       uint w = strlen(l->fields[f]);
127       widths[f] = MAX(widths[f], w);
128     }
129   }
130   if (widths[0] > 4) {
131     /*
132      *  This is tricky: if there are short options, which have an argument,
133      *  but no long variant, we are willing to let column 0 overflow to column 1.
134      */
135     widths[1] = MAX(widths[1], widths[0] - 4);
136     widths[0] = 4;
137   }
138   widths[1] += 4;
139
140   // Print columns
141   for (uint i=0; i<n; i++) {
142     struct help_line *l = &h.lines[i];
143     if (l->extra)
144       puts(l->extra);
145     else {
146       int t = 0;
147       for (uint f=0; f<3; f++) {
148         t += widths[f];
149         t -= printf("%s", l->fields[f]);
150         while (t > 0) {
151           putchar(' ');
152           t--;
153         }
154       }
155       putchar('\n');
156     }
157   }
158
159   // Clean up
160   GARY_FREE(h.lines);
161   mp_delete(h.pool);
162 }
163
164 void opt_handle_help(const struct opt_item * opt UNUSED, const char * value UNUSED, void * data)
165 {
166   struct opt_context *oc = data;
167   opt_help(oc->options);
168   exit(0);
169 }