blob: 1c2bcdcb124c4d1c7243c8a18f14875cf84752df [file] [log] [blame]
Rob Landley86cafe12014-01-03 18:23:09 -06001#include "toys.h"
2
3// Humor toys.h
4struct toy_context toys;
5char libbuf[4096], toybuf[4096];
6void show_help(void) {;}
7void toy_exec(char *argv[]) {;}
8
9// Parse config files into data structures.
10
11struct symbol {
12 struct symbol *next;
Rob Landleyc049bca2014-01-20 17:26:50 -060013 int enabled, help_indent;
Rob Landley86cafe12014-01-03 18:23:09 -060014 char *name, *depends;
15 struct double_list *help;
16} *sym;
17
Rob Landleyc049bca2014-01-20 17:26:50 -060018char *trim(char *s)
19{
20 while (isspace(*s)) s++;
21
22 return s;
23}
24
Rob Landley86cafe12014-01-03 18:23:09 -060025char *keyword(char *name, char *line)
26{
27 int len = strlen(name);
28
Rob Landleyc049bca2014-01-20 17:26:50 -060029 line = trim(line);
Rob Landley86cafe12014-01-03 18:23:09 -060030 if (strncmp(name, line, len)) return 0;
31 line += len;
32 if (*line && !isspace(*line)) return 0;
Rob Landleyc049bca2014-01-20 17:26:50 -060033 line = trim(line);
Rob Landley86cafe12014-01-03 18:23:09 -060034
35 return line;
36}
37
Rob Landleyc049bca2014-01-20 17:26:50 -060038char *dlist_zap(struct double_list **help)
39{
40 struct double_list *dd = dlist_pop(help);
41 char *s = dd->data;
42
43 free(dd);
Rob Landley04320502014-01-29 23:47:53 -060044
Rob Landleyc049bca2014-01-20 17:26:50 -060045 return s;
46}
47
Rob Landley04320502014-01-29 23:47:53 -060048int zap_blank_lines(struct double_list **help)
Rob Landleyc049bca2014-01-20 17:26:50 -060049{
Rob Landley04320502014-01-29 23:47:53 -060050 int got = 0;
51
52 while (*help) {
53 char *s;
54
55 s = trim((*help)->data);
Rob Landleyc049bca2014-01-20 17:26:50 -060056
57 if (*s) break;
Rob Landley04320502014-01-29 23:47:53 -060058 got++;
Rob Landleyc049bca2014-01-20 17:26:50 -060059 free(dlist_zap(help));
60 }
Rob Landley04320502014-01-29 23:47:53 -060061
62 return got;
63}
64
65// Collect "-a blah" description lines following a blank line (or start).
66// Returns array of removed lines with *len entries (0 for none).
67
68// Moves *help to new start of text (in case dash lines were at beginning).
69// Sets *from to where dash lines removed from (in case they weren't).
70// Discards blank lines before and after dashlines.
71
72// If no prefix, *help NULL. If no postfix, *from == *help
73// if no dashlines returned *from == *help.
74
75char **grab_dashlines(struct double_list **help, struct double_list **from,
76 int *len)
77{
78 struct double_list *dd;
79 char *s, **list;
80 int count = 0;
81
82 *len = 0;
83 zap_blank_lines(help);
84 *from = *help;
85
86 // Find start of dash block. Must be at start or after blank line.
87 for (;;) {
88 s = trim((*from)->data);
89 if (*s == '-' && s[1] != '-' && !count) break;
90
91 if (!*s) count = 0;
92 else count++;
93
94 *from = (*from)->next;
95 if (*from == *help) return 0;
96 }
97
98 // If there was whitespace before this, zap it. This can't take out *help
99 // because zap_blank_lines skipped blank lines, and we had to have at least
100 // one non-blank line (a dash line) to get this far.
101 while (!*trim((*from)->prev->data)) {
102 *from = (*from)->prev;
103 free(dlist_zap(from));
104 }
105
106 // Count number of dashlines, copy out to array, zap trailing whitespace
107 // If *help was at start of dashblock, move it with *from
108 count = 0;
109 dd = *from;
110 if (*help == *from) *help = 0;
111 for (;;) {
112 if (*trim(dd->data) != '-') break;
113 count++;
114 if (*from == (dd = dd->next)) break;
115 }
116
117 list = xmalloc(sizeof(char *)*count);
118 *len = count;
119 while (count) list[--count] = dlist_zap(from);
120
121 return list;
Rob Landleyc049bca2014-01-20 17:26:50 -0600122}
123
Rob Landley86cafe12014-01-03 18:23:09 -0600124void parse(char *filename)
125{
126 FILE *fp = xfopen(filename, "r");
127 struct symbol *new = 0;
Rob Landley86cafe12014-01-03 18:23:09 -0600128
129 for (;;) {
130 char *s, *line = NULL;
131 size_t len;
132
133 // Read line, trim whitespace at right edge.
134 if (getline(&line, &len, fp) < 1) break;
135 s = line+strlen(line);
136 while (--s >= line) {
137 if (!isspace(*s)) break;
138 *s = 0;
139 }
140
141 // source or config keyword at left edge?
142 if (*line && !isspace(*line)) {
Rob Landley86cafe12014-01-03 18:23:09 -0600143 if ((s = keyword("config", line))) {
144 new = xzalloc(sizeof(struct symbol));
145 new->next = sym;
146 new->name = s;
147 sym = new;
148 } else if ((s = keyword("source", line))) parse(s);
149
150 continue;
151 }
152 if (!new) continue;
153
Rob Landleyc049bca2014-01-20 17:26:50 -0600154 if (sym && sym->help_indent) {
155 dlist_add(&(new->help), line);
156 if (sym->help_indent < 0) {
157 sym->help_indent = 0;
158 while (isspace(line[sym->help_indent])) sym->help_indent++;
159 }
160 }
Rob Landley86cafe12014-01-03 18:23:09 -0600161 else if ((s = keyword("depends", line)) && (s = keyword("on", s)))
162 new->depends = s;
Rob Landleyc049bca2014-01-20 17:26:50 -0600163 else if (keyword("help", line)) sym->help_indent = -1;
Rob Landley86cafe12014-01-03 18:23:09 -0600164 }
165
166 fclose(fp);
167}
168
Rob Landleyc049bca2014-01-20 17:26:50 -0600169int charsort(void *a, void *b)
170{
171 char *aa = a, *bb = b;
172
173 if (*aa < *bb) return -1;
174 if (*aa > *bb) return 1;
175 return 0;
176}
177
Rob Landley04320502014-01-29 23:47:53 -0600178int dashsort(char **a, char **b)
Rob Landleyc049bca2014-01-20 17:26:50 -0600179{
Rob Landley04320502014-01-29 23:47:53 -0600180 char *aa = *a, *bb = *b;
Rob Landleyc049bca2014-01-20 17:26:50 -0600181
182 if (aa[1] < bb[1]) return -1;
183 if (aa[1] > bb[1]) return 1;
184 return 0;
185}
186
Rob Landley04320502014-01-29 23:47:53 -0600187int dashlinesort(char **a, char **b)
188{
189 return strcmp(*a, *b);
190}
191
Rob Landley86cafe12014-01-03 18:23:09 -0600192int main(int argc, char *argv[])
193{
194 FILE *fp;
Rob Landley86cafe12014-01-03 18:23:09 -0600195
196 if (argc != 3) {
197 fprintf(stderr, "usage: config2help Config.in .config\n");
198 exit(1);
199 }
200
201 // Read Config.in
202 parse(argv[1]);
203
204 // read .config
205 fp = xfopen(argv[2], "r");
206 for (;;) {
207 char *line = NULL;
208 size_t len;
209
210 if (getline(&line, &len, fp) < 1) break;
211 if (!strncmp("CONFIG_", line, 7)) {
Rob Landley2ded8332014-01-15 09:38:31 -0600212 struct symbol *try;
213 char *s = line+7;
214
Rob Landley86cafe12014-01-03 18:23:09 -0600215 for (try=sym; try; try=try->next) {
216 len = strlen(try->name);
217 if (!strncmp(try->name, s, len) && s[len]=='=' && s[len+1]=='y') {
218 try->enabled++;
219 break;
220 }
221 }
222 }
223 }
224
Rob Landley2ded8332014-01-15 09:38:31 -0600225 // Collate help according to usage, depends, and .config
226
227 // Loop through each entry, finding duplicate enabled "usage:" names
Rob Landley04320502014-01-29 23:47:53 -0600228 // This is in reverse order, so last entry gets collated with previous
229 // entry until we run out of matching pairs.
Rob Landley2ded8332014-01-15 09:38:31 -0600230 for (;;) {
231 struct symbol *throw = 0, *catch;
Rob Landleyc049bca2014-01-20 17:26:50 -0600232 char *this, *that, *cusage, *tusage, *name;
Rob Landley2ded8332014-01-15 09:38:31 -0600233 int len;
234
235 // find a usage: name and collate all enabled entries with that name
236 for (catch = sym; catch; catch = catch->next) {
237 if (catch->enabled != 1) continue;
Rob Landleyc049bca2014-01-20 17:26:50 -0600238 if (catch->help && (that = keyword("usage:", catch->help->data))) {
Rob Landley04320502014-01-29 23:47:53 -0600239 struct double_list *cfrom, *tfrom, *anchor;
240 char *try, **cdashlines, **tdashlines;
241 int clen, tlen;
Rob Landley2ded8332014-01-15 09:38:31 -0600242
Rob Landley04320502014-01-29 23:47:53 -0600243 // Align usage: lines, finding a matching pair so we can suck help
244 // text out of throw into catch, copying from this to that
Rob Landleyc049bca2014-01-20 17:26:50 -0600245 if (!throw) name = that;
246 else if (strncmp(name, that, len) || !isspace(that[len])) continue;
247 catch->enabled++;
248 while (!isspace(*that) && *that) that++;
249 if (!throw) len = that-name;
250 that = trim(that);
Rob Landley2ded8332014-01-15 09:38:31 -0600251 if (!throw) {
252 throw = catch;
Rob Landleyc049bca2014-01-20 17:26:50 -0600253 this = that;
Rob Landley2ded8332014-01-15 09:38:31 -0600254
255 continue;
256 }
257
Rob Landley04320502014-01-29 23:47:53 -0600258 // Grab option description lines to collate from catch and throw
Rob Landleyc049bca2014-01-20 17:26:50 -0600259 tusage = dlist_zap(&throw->help);
Rob Landley04320502014-01-29 23:47:53 -0600260 tdashlines = grab_dashlines(&throw->help, &tfrom, &tlen);
261 cusage = dlist_zap(&catch->help);
262 cdashlines = grab_dashlines(&catch->help, &cfrom, &clen);
263 anchor = catch->help;
Rob Landley2ded8332014-01-15 09:38:31 -0600264
Rob Landley04320502014-01-29 23:47:53 -0600265 // If we've got both, collate and alphebetize
266 if (cdashlines && tdashlines) {
267 char **new = xmalloc(sizeof(char *)*(clen+tlen));
Rob Landleyc049bca2014-01-20 17:26:50 -0600268
Rob Landley04320502014-01-29 23:47:53 -0600269 memcpy(new, cdashlines, sizeof(char *)*clen);
270 memcpy(new+clen, tdashlines, sizeof(char *)*tlen);
271 free(cdashlines);
272 free(tdashlines);
273 qsort(new, clen+tlen, sizeof(char *), (void *)dashlinesort);
274 cdashlines = new;
275
276 // If just one, make sure it's in catch.
277 } else if (tdashlines) cdashlines = tdashlines;
278
279 // If throw had a prefix, insert it before dashlines, with a
280 // blank line if catch had a prefix.
281 if (tfrom && tfrom != throw->help) {
282 if (throw->help || catch->help) dlist_add(&cfrom, strdup(""));
283 else {
284 dlist_add(&cfrom, 0);
285 anchor = cfrom->prev;
286 }
287 while (throw->help && throw->help != tfrom)
288 dlist_add(&cfrom, dlist_zap(&throw->help));
289 if (cfrom && cfrom->prev->data && *trim(cfrom->prev->data))
290 dlist_add(&cfrom, strdup(""));
291 }
292 if (!anchor) {
293 dlist_add(&cfrom, 0);
294 anchor = cfrom->prev;
295 }
296
297 // Splice sorted lines back in place
298 if (cdashlines) {
299 tlen += clen;
300
301 for (clen = 0; clen < tlen; clen++)
302 dlist_add(&cfrom, cdashlines[clen]);
303 }
304
305 // If there were no dashlines, text would be considered prefix, so
306 // the list is definitely no longer empty, so discard placeholder.
307 if (!anchor->data) dlist_zap(&anchor);
308
309 // zap whitespace at end of catch help text
310 while (!*trim(anchor->prev->data)) {
311 anchor = anchor->prev;
312 free(dlist_zap(&anchor));
313 }
314
315 // Append trailing lines.
316 while (tfrom) dlist_add(&anchor, dlist_zap(&tfrom));
317
318 // Collate first [-abc] option block in usage: lines
Rob Landleyc049bca2014-01-20 17:26:50 -0600319 try = 0;
320 if (*this == '[' && this[1] == '-' && this[2] != '-' &&
321 *that == '[' && that[1] == '-' && that[2] != '-')
322 {
323 char *from = this+2, *to = that+2;
324 int ff = strcspn(from, " ]"), tt = strcspn(to, " ]");
325
326 if (from[ff] == ']' && to[tt] == ']') {
327 try = xmprintf("[-%.*s%.*s] ", ff, from, tt, to);
328 qsort(try+2, ff+tt, 1, (void *)charsort);
329 this = trim(this+ff+3);
330 that = trim(that+tt+3);
331 }
332 }
333
Rob Landley04320502014-01-29 23:47:53 -0600334 // The list is definitely no longer empty, so discard placeholder.
335 if (!anchor->data) dlist_zap(&anchor);
336
Rob Landleyc049bca2014-01-20 17:26:50 -0600337 // Add new collated line (and whitespace).
Rob Landley04320502014-01-29 23:47:53 -0600338 dlist_add(&anchor, xmprintf("%*cusage: %.*s %s%s%s%s",
Rob Landleyc049bca2014-01-20 17:26:50 -0600339 catch->help_indent, ' ', len, name, try ? try : "",
340 this, *this ? " " : "", that));
Rob Landley04320502014-01-29 23:47:53 -0600341 free(try);
342 dlist_add(&anchor, strdup(""));
Rob Landleyc049bca2014-01-20 17:26:50 -0600343 free(cusage);
344 free(tusage);
Rob Landley2ded8332014-01-15 09:38:31 -0600345 throw->enabled = 0;
Rob Landley04320502014-01-29 23:47:53 -0600346 throw = catch;
347 throw->help = anchor->prev->prev;
Rob Landley2ded8332014-01-15 09:38:31 -0600348
Rob Landley2ded8332014-01-15 09:38:31 -0600349 throw = catch;
Rob Landleyc049bca2014-01-20 17:26:50 -0600350 this = throw->help->data + throw->help_indent + 8 + len;
Rob Landley2ded8332014-01-15 09:38:31 -0600351 }
352 }
353
354 // Did we find one?
355
356 if (!throw) break;
Rob Landley2ded8332014-01-15 09:38:31 -0600357 }
358
Rob Landley86cafe12014-01-03 18:23:09 -0600359 // Print out help #defines
360 while (sym) {
361 struct double_list *dd;
362
363 if (sym->help) {
Rob Landleyc049bca2014-01-20 17:26:50 -0600364 int i;
Rob Landley2ded8332014-01-15 09:38:31 -0600365 char *s = xstrdup(sym->name);
Rob Landley86cafe12014-01-03 18:23:09 -0600366
Rob Landley86cafe12014-01-03 18:23:09 -0600367 for (i = 0; s[i]; i++) s[i] = tolower(s[i]);
368 printf("#define help_%s \"", s);
369 free(s);
370
Rob Landley86cafe12014-01-03 18:23:09 -0600371 dd = sym->help;
Rob Landley86cafe12014-01-03 18:23:09 -0600372 for (;;) {
Rob Landleyc049bca2014-01-20 17:26:50 -0600373 i = sym->help_indent;
Rob Landley86cafe12014-01-03 18:23:09 -0600374
375 // Trim leading whitespace
376 s = dd->data;
377 while (isspace(*s) && i) {
378 s++;
379 i--;
380 }
381 for (i=0; s[i]; i++) {
382 if (s[i] == '"' || s[i] == '\\') putchar('\\');
383 putchar(s[i]);
384 }
Rob Landley2ded8332014-01-15 09:38:31 -0600385 putchar('\\');
386 putchar('n');
Rob Landley86cafe12014-01-03 18:23:09 -0600387 dd = dd->next;
388 if (dd == sym->help) break;
389 }
Rob Landleye6314da2014-04-07 12:53:24 -0500390 printf("\"\n\n");
Rob Landley86cafe12014-01-03 18:23:09 -0600391 }
392 sym = sym->next;
393 }
Rob Landley1fb3ae72014-02-16 11:09:23 -0600394
395 return 0;
Rob Landley86cafe12014-01-03 18:23:09 -0600396}