blob: 6ec5e84112d5ffc5c4c60e9d4eb473b1a725266c [file] [log] [blame]
Rob Landley187649d2016-02-10 23:06:12 -06001//#include "toys.h"
Rob Landley86cafe12014-01-03 18:23:09 -06002
Mike Frysingerbef3a512016-02-29 13:13:38 -06003#include <ctype.h>
Rob Landley187649d2016-02-10 23:06:12 -06004#include <stdio.h>
5#include <string.h>
6#include <stdlib.h>
7#include <sys/types.h>
8#include <sys/stat.h>
9#include <unistd.h>
10#include <regex.h>
11#include <inttypes.h>
12#include <termios.h>
13#include <poll.h>
Rob Landley3879cd92016-02-25 17:16:32 -060014#include <sys/socket.h>
Rob Landley187649d2016-02-10 23:06:12 -060015struct statvfs {int i;};
16#include "lib/portability.h"
17#include "lib/lib.h"
18
19// Humor toys.h (lie through our teeth, C's linker doesn't care).
20char toys[4096], libbuf[4096], toybuf[4096];
Rob Landleye5354ca2015-09-11 16:35:14 -050021void show_help(FILE *out) {;}
Rob Landley86cafe12014-01-03 18:23:09 -060022void toy_exec(char *argv[]) {;}
Rob Landley187649d2016-02-10 23:06:12 -060023void toy_init(void *which, char *argv[]) {;}
Rob Landley86cafe12014-01-03 18:23:09 -060024
25// Parse config files into data structures.
26
27struct symbol {
28 struct symbol *next;
Rob Landleyc049bca2014-01-20 17:26:50 -060029 int enabled, help_indent;
Rob Landley86cafe12014-01-03 18:23:09 -060030 char *name, *depends;
31 struct double_list *help;
32} *sym;
33
Rob Landley187649d2016-02-10 23:06:12 -060034// remove leading spaces
Rob Landleyc049bca2014-01-20 17:26:50 -060035char *trim(char *s)
36{
37 while (isspace(*s)) s++;
38
39 return s;
40}
41
Rob Landley187649d2016-02-10 23:06:12 -060042// if line starts with name (as whole word) return pointer after it, else NULL
Rob Landley86cafe12014-01-03 18:23:09 -060043char *keyword(char *name, char *line)
44{
45 int len = strlen(name);
46
Rob Landleyc049bca2014-01-20 17:26:50 -060047 line = trim(line);
Rob Landley86cafe12014-01-03 18:23:09 -060048 if (strncmp(name, line, len)) return 0;
49 line += len;
50 if (*line && !isspace(*line)) return 0;
Rob Landleyc049bca2014-01-20 17:26:50 -060051 line = trim(line);
Rob Landley86cafe12014-01-03 18:23:09 -060052
53 return line;
54}
55
Rob Landley187649d2016-02-10 23:06:12 -060056// dlist_pop() freeing wrapper structure for you.
Rob Landleyc049bca2014-01-20 17:26:50 -060057char *dlist_zap(struct double_list **help)
58{
59 struct double_list *dd = dlist_pop(help);
60 char *s = dd->data;
61
62 free(dd);
Rob Landley04320502014-01-29 23:47:53 -060063
Rob Landleyc049bca2014-01-20 17:26:50 -060064 return s;
65}
66
Rob Landley04320502014-01-29 23:47:53 -060067int zap_blank_lines(struct double_list **help)
Rob Landleyc049bca2014-01-20 17:26:50 -060068{
Rob Landley04320502014-01-29 23:47:53 -060069 int got = 0;
70
71 while (*help) {
72 char *s;
73
74 s = trim((*help)->data);
Rob Landleyc049bca2014-01-20 17:26:50 -060075
76 if (*s) break;
Rob Landley04320502014-01-29 23:47:53 -060077 got++;
Rob Landleyc049bca2014-01-20 17:26:50 -060078 free(dlist_zap(help));
79 }
Rob Landley04320502014-01-29 23:47:53 -060080
81 return got;
82}
83
84// Collect "-a blah" description lines following a blank line (or start).
85// Returns array of removed lines with *len entries (0 for none).
86
87// Moves *help to new start of text (in case dash lines were at beginning).
88// Sets *from to where dash lines removed from (in case they weren't).
89// Discards blank lines before and after dashlines.
90
91// If no prefix, *help NULL. If no postfix, *from == *help
92// if no dashlines returned *from == *help.
93
94char **grab_dashlines(struct double_list **help, struct double_list **from,
95 int *len)
96{
97 struct double_list *dd;
98 char *s, **list;
99 int count = 0;
100
101 *len = 0;
102 zap_blank_lines(help);
103 *from = *help;
104
105 // Find start of dash block. Must be at start or after blank line.
106 for (;;) {
107 s = trim((*from)->data);
108 if (*s == '-' && s[1] != '-' && !count) break;
109
110 if (!*s) count = 0;
111 else count++;
112
113 *from = (*from)->next;
114 if (*from == *help) return 0;
115 }
116
117 // If there was whitespace before this, zap it. This can't take out *help
118 // because zap_blank_lines skipped blank lines, and we had to have at least
119 // one non-blank line (a dash line) to get this far.
120 while (!*trim((*from)->prev->data)) {
121 *from = (*from)->prev;
122 free(dlist_zap(from));
123 }
124
125 // Count number of dashlines, copy out to array, zap trailing whitespace
126 // If *help was at start of dashblock, move it with *from
127 count = 0;
128 dd = *from;
129 if (*help == *from) *help = 0;
130 for (;;) {
131 if (*trim(dd->data) != '-') break;
132 count++;
133 if (*from == (dd = dd->next)) break;
134 }
135
136 list = xmalloc(sizeof(char *)*count);
137 *len = count;
138 while (count) list[--count] = dlist_zap(from);
139
140 return list;
Rob Landleyc049bca2014-01-20 17:26:50 -0600141}
142
Rob Landley86cafe12014-01-03 18:23:09 -0600143void parse(char *filename)
144{
145 FILE *fp = xfopen(filename, "r");
146 struct symbol *new = 0;
Rob Landley86cafe12014-01-03 18:23:09 -0600147
148 for (;;) {
149 char *s, *line = NULL;
150 size_t len;
151
152 // Read line, trim whitespace at right edge.
153 if (getline(&line, &len, fp) < 1) break;
154 s = line+strlen(line);
155 while (--s >= line) {
156 if (!isspace(*s)) break;
157 *s = 0;
158 }
159
160 // source or config keyword at left edge?
161 if (*line && !isspace(*line)) {
Rob Landley86cafe12014-01-03 18:23:09 -0600162 if ((s = keyword("config", line))) {
163 new = xzalloc(sizeof(struct symbol));
164 new->next = sym;
165 new->name = s;
166 sym = new;
167 } else if ((s = keyword("source", line))) parse(s);
168
169 continue;
170 }
171 if (!new) continue;
172
Rob Landleyc049bca2014-01-20 17:26:50 -0600173 if (sym && sym->help_indent) {
174 dlist_add(&(new->help), line);
175 if (sym->help_indent < 0) {
176 sym->help_indent = 0;
177 while (isspace(line[sym->help_indent])) sym->help_indent++;
178 }
179 }
Rob Landley86cafe12014-01-03 18:23:09 -0600180 else if ((s = keyword("depends", line)) && (s = keyword("on", s)))
181 new->depends = s;
Rob Landleyc049bca2014-01-20 17:26:50 -0600182 else if (keyword("help", line)) sym->help_indent = -1;
Rob Landley86cafe12014-01-03 18:23:09 -0600183 }
184
185 fclose(fp);
186}
187
Rob Landleyc049bca2014-01-20 17:26:50 -0600188int charsort(void *a, void *b)
189{
190 char *aa = a, *bb = b;
191
192 if (*aa < *bb) return -1;
193 if (*aa > *bb) return 1;
194 return 0;
195}
196
Rob Landley04320502014-01-29 23:47:53 -0600197int dashsort(char **a, char **b)
Rob Landleyc049bca2014-01-20 17:26:50 -0600198{
Rob Landley04320502014-01-29 23:47:53 -0600199 char *aa = *a, *bb = *b;
Rob Landleyc049bca2014-01-20 17:26:50 -0600200
201 if (aa[1] < bb[1]) return -1;
202 if (aa[1] > bb[1]) return 1;
203 return 0;
204}
205
Rob Landley04320502014-01-29 23:47:53 -0600206int dashlinesort(char **a, char **b)
207{
208 return strcmp(*a, *b);
209}
210
Rob Landley86cafe12014-01-03 18:23:09 -0600211int main(int argc, char *argv[])
212{
213 FILE *fp;
Rob Landley86cafe12014-01-03 18:23:09 -0600214
215 if (argc != 3) {
216 fprintf(stderr, "usage: config2help Config.in .config\n");
217 exit(1);
218 }
219
220 // Read Config.in
221 parse(argv[1]);
222
223 // read .config
224 fp = xfopen(argv[2], "r");
225 for (;;) {
226 char *line = NULL;
227 size_t len;
228
229 if (getline(&line, &len, fp) < 1) break;
230 if (!strncmp("CONFIG_", line, 7)) {
Rob Landley2ded8332014-01-15 09:38:31 -0600231 struct symbol *try;
232 char *s = line+7;
233
Rob Landley86cafe12014-01-03 18:23:09 -0600234 for (try=sym; try; try=try->next) {
235 len = strlen(try->name);
236 if (!strncmp(try->name, s, len) && s[len]=='=' && s[len+1]=='y') {
237 try->enabled++;
238 break;
239 }
240 }
241 }
242 }
243
Rob Landley2ded8332014-01-15 09:38:31 -0600244 // Collate help according to usage, depends, and .config
245
246 // Loop through each entry, finding duplicate enabled "usage:" names
Rob Landley04320502014-01-29 23:47:53 -0600247 // This is in reverse order, so last entry gets collated with previous
248 // entry until we run out of matching pairs.
Rob Landley2ded8332014-01-15 09:38:31 -0600249 for (;;) {
250 struct symbol *throw = 0, *catch;
Rob Landleyc049bca2014-01-20 17:26:50 -0600251 char *this, *that, *cusage, *tusage, *name;
Rob Landley2ded8332014-01-15 09:38:31 -0600252 int len;
253
254 // find a usage: name and collate all enabled entries with that name
255 for (catch = sym; catch; catch = catch->next) {
256 if (catch->enabled != 1) continue;
Rob Landleyc049bca2014-01-20 17:26:50 -0600257 if (catch->help && (that = keyword("usage:", catch->help->data))) {
Rob Landley04320502014-01-29 23:47:53 -0600258 struct double_list *cfrom, *tfrom, *anchor;
259 char *try, **cdashlines, **tdashlines;
260 int clen, tlen;
Rob Landley2ded8332014-01-15 09:38:31 -0600261
Rob Landley04320502014-01-29 23:47:53 -0600262 // Align usage: lines, finding a matching pair so we can suck help
263 // text out of throw into catch, copying from this to that
Rob Landleyc049bca2014-01-20 17:26:50 -0600264 if (!throw) name = that;
265 else if (strncmp(name, that, len) || !isspace(that[len])) continue;
266 catch->enabled++;
267 while (!isspace(*that) && *that) that++;
268 if (!throw) len = that-name;
269 that = trim(that);
Rob Landley2ded8332014-01-15 09:38:31 -0600270 if (!throw) {
271 throw = catch;
Rob Landleyc049bca2014-01-20 17:26:50 -0600272 this = that;
Rob Landley2ded8332014-01-15 09:38:31 -0600273
274 continue;
275 }
276
Rob Landley04320502014-01-29 23:47:53 -0600277 // Grab option description lines to collate from catch and throw
Rob Landleyc049bca2014-01-20 17:26:50 -0600278 tusage = dlist_zap(&throw->help);
Rob Landley04320502014-01-29 23:47:53 -0600279 tdashlines = grab_dashlines(&throw->help, &tfrom, &tlen);
280 cusage = dlist_zap(&catch->help);
281 cdashlines = grab_dashlines(&catch->help, &cfrom, &clen);
282 anchor = catch->help;
Rob Landley2ded8332014-01-15 09:38:31 -0600283
Rob Landley04320502014-01-29 23:47:53 -0600284 // If we've got both, collate and alphebetize
285 if (cdashlines && tdashlines) {
286 char **new = xmalloc(sizeof(char *)*(clen+tlen));
Rob Landleyc049bca2014-01-20 17:26:50 -0600287
Rob Landley04320502014-01-29 23:47:53 -0600288 memcpy(new, cdashlines, sizeof(char *)*clen);
289 memcpy(new+clen, tdashlines, sizeof(char *)*tlen);
290 free(cdashlines);
291 free(tdashlines);
292 qsort(new, clen+tlen, sizeof(char *), (void *)dashlinesort);
293 cdashlines = new;
294
295 // If just one, make sure it's in catch.
296 } else if (tdashlines) cdashlines = tdashlines;
297
298 // If throw had a prefix, insert it before dashlines, with a
299 // blank line if catch had a prefix.
300 if (tfrom && tfrom != throw->help) {
301 if (throw->help || catch->help) dlist_add(&cfrom, strdup(""));
302 else {
303 dlist_add(&cfrom, 0);
304 anchor = cfrom->prev;
305 }
306 while (throw->help && throw->help != tfrom)
307 dlist_add(&cfrom, dlist_zap(&throw->help));
308 if (cfrom && cfrom->prev->data && *trim(cfrom->prev->data))
309 dlist_add(&cfrom, strdup(""));
310 }
311 if (!anchor) {
312 dlist_add(&cfrom, 0);
313 anchor = cfrom->prev;
314 }
315
316 // Splice sorted lines back in place
317 if (cdashlines) {
318 tlen += clen;
319
320 for (clen = 0; clen < tlen; clen++)
321 dlist_add(&cfrom, cdashlines[clen]);
322 }
323
324 // If there were no dashlines, text would be considered prefix, so
325 // the list is definitely no longer empty, so discard placeholder.
326 if (!anchor->data) dlist_zap(&anchor);
327
328 // zap whitespace at end of catch help text
329 while (!*trim(anchor->prev->data)) {
330 anchor = anchor->prev;
331 free(dlist_zap(&anchor));
332 }
333
334 // Append trailing lines.
335 while (tfrom) dlist_add(&anchor, dlist_zap(&tfrom));
336
337 // Collate first [-abc] option block in usage: lines
Rob Landleyc049bca2014-01-20 17:26:50 -0600338 try = 0;
339 if (*this == '[' && this[1] == '-' && this[2] != '-' &&
340 *that == '[' && that[1] == '-' && that[2] != '-')
341 {
342 char *from = this+2, *to = that+2;
343 int ff = strcspn(from, " ]"), tt = strcspn(to, " ]");
344
345 if (from[ff] == ']' && to[tt] == ']') {
346 try = xmprintf("[-%.*s%.*s] ", ff, from, tt, to);
347 qsort(try+2, ff+tt, 1, (void *)charsort);
348 this = trim(this+ff+3);
349 that = trim(that+tt+3);
350 }
351 }
352
Rob Landley04320502014-01-29 23:47:53 -0600353 // The list is definitely no longer empty, so discard placeholder.
354 if (!anchor->data) dlist_zap(&anchor);
355
Rob Landleyc049bca2014-01-20 17:26:50 -0600356 // Add new collated line (and whitespace).
Rob Landley04320502014-01-29 23:47:53 -0600357 dlist_add(&anchor, xmprintf("%*cusage: %.*s %s%s%s%s",
Rob Landleyc049bca2014-01-20 17:26:50 -0600358 catch->help_indent, ' ', len, name, try ? try : "",
359 this, *this ? " " : "", that));
Rob Landley04320502014-01-29 23:47:53 -0600360 free(try);
361 dlist_add(&anchor, strdup(""));
Rob Landleyc049bca2014-01-20 17:26:50 -0600362 free(cusage);
363 free(tusage);
Rob Landley2ded8332014-01-15 09:38:31 -0600364 throw->enabled = 0;
Rob Landley04320502014-01-29 23:47:53 -0600365 throw = catch;
366 throw->help = anchor->prev->prev;
Rob Landley2ded8332014-01-15 09:38:31 -0600367
Rob Landley2ded8332014-01-15 09:38:31 -0600368 throw = catch;
Rob Landleyc049bca2014-01-20 17:26:50 -0600369 this = throw->help->data + throw->help_indent + 8 + len;
Rob Landley2ded8332014-01-15 09:38:31 -0600370 }
371 }
372
373 // Did we find one?
374
375 if (!throw) break;
Rob Landley2ded8332014-01-15 09:38:31 -0600376 }
377
Rob Landley86cafe12014-01-03 18:23:09 -0600378 // Print out help #defines
379 while (sym) {
380 struct double_list *dd;
381
382 if (sym->help) {
Rob Landleyc049bca2014-01-20 17:26:50 -0600383 int i;
Rob Landley2ded8332014-01-15 09:38:31 -0600384 char *s = xstrdup(sym->name);
Rob Landley86cafe12014-01-03 18:23:09 -0600385
Rob Landley86cafe12014-01-03 18:23:09 -0600386 for (i = 0; s[i]; i++) s[i] = tolower(s[i]);
Rob Landley187649d2016-02-10 23:06:12 -0600387 printf("#define HELP_%s \"", s);
Rob Landley86cafe12014-01-03 18:23:09 -0600388 free(s);
389
Rob Landley86cafe12014-01-03 18:23:09 -0600390 dd = sym->help;
Rob Landley86cafe12014-01-03 18:23:09 -0600391 for (;;) {
Rob Landleyc049bca2014-01-20 17:26:50 -0600392 i = sym->help_indent;
Rob Landley86cafe12014-01-03 18:23:09 -0600393
394 // Trim leading whitespace
395 s = dd->data;
396 while (isspace(*s) && i) {
397 s++;
398 i--;
399 }
400 for (i=0; s[i]; i++) {
401 if (s[i] == '"' || s[i] == '\\') putchar('\\');
402 putchar(s[i]);
403 }
Rob Landley2ded8332014-01-15 09:38:31 -0600404 putchar('\\');
405 putchar('n');
Rob Landley86cafe12014-01-03 18:23:09 -0600406 dd = dd->next;
407 if (dd == sym->help) break;
408 }
Rob Landleye6314da2014-04-07 12:53:24 -0500409 printf("\"\n\n");
Rob Landley86cafe12014-01-03 18:23:09 -0600410 }
411 sym = sym->next;
412 }
Rob Landley1fb3ae72014-02-16 11:09:23 -0600413
414 return 0;
Rob Landley86cafe12014-01-03 18:23:09 -0600415}