4 struct toy_context toys;
5 char libbuf[4096], toybuf[4096];
6 void show_help(void) {;}
7 void toy_exec(char *argv[]) {;}
9 // Parse config files into data structures.
13 int enabled, help_indent;
15 struct double_list *help;
20 while (isspace(*s)) s++;
25 char *keyword(char *name, char *line)
27 int len = strlen(name);
30 if (strncmp(name, line, len)) return 0;
32 if (*line && !isspace(*line)) return 0;
38 char *dlist_zap(struct double_list **help)
40 struct double_list *dd = dlist_pop(help);
48 int zap_blank_lines(struct double_list **help)
55 s = trim((*help)->data);
59 free(dlist_zap(help));
65 // Collect "-a blah" description lines following a blank line (or start).
66 // Returns array of removed lines with *len entries (0 for none).
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.
72 // If no prefix, *help NULL. If no postfix, *from == *help
73 // if no dashlines returned *from == *help.
75 char **grab_dashlines(struct double_list **help, struct double_list **from,
78 struct double_list *dd;
83 zap_blank_lines(help);
86 // Find start of dash block. Must be at start or after blank line.
88 s = trim((*from)->data);
89 if (*s == '-' && s[1] != '-' && !count) break;
94 *from = (*from)->next;
95 if (*from == *help) return 0;
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));
106 // Count number of dashlines, copy out to array, zap trailing whitespace
107 // If *help was at start of dashblock, move it with *from
110 if (*help == *from) *help = 0;
112 if (*trim(dd->data) != '-') break;
114 if (*from == (dd = dd->next)) break;
117 list = xmalloc(sizeof(char *)*count);
119 while (count) list[--count] = dlist_zap(from);
124 void parse(char *filename)
126 FILE *fp = xfopen(filename, "r");
127 struct symbol *new = 0;
130 char *s, *line = NULL;
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;
141 // source or config keyword at left edge?
142 if (*line && !isspace(*line)) {
143 if ((s = keyword("config", line))) {
144 new = xzalloc(sizeof(struct symbol));
148 } else if ((s = keyword("source", line))) parse(s);
154 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++;
161 else if ((s = keyword("depends", line)) && (s = keyword("on", s)))
163 else if (keyword("help", line)) sym->help_indent = -1;
169 int charsort(void *a, void *b)
171 char *aa = a, *bb = b;
173 if (*aa < *bb) return -1;
174 if (*aa > *bb) return 1;
178 int dashsort(char **a, char **b)
180 char *aa = *a, *bb = *b;
182 if (aa[1] < bb[1]) return -1;
183 if (aa[1] > bb[1]) return 1;
187 int dashlinesort(char **a, char **b)
189 return strcmp(*a, *b);
192 int main(int argc, char *argv[])
197 fprintf(stderr, "usage: config2help Config.in .config\n");
205 fp = xfopen(argv[2], "r");
210 if (getline(&line, &len, fp) < 1) break;
211 if (!strncmp("CONFIG_", line, 7)) {
215 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') {
225 // Collate help according to usage, depends, and .config
227 // Loop through each entry, finding duplicate enabled "usage:" names
228 // This is in reverse order, so last entry gets collated with previous
229 // entry until we run out of matching pairs.
231 struct symbol *throw = 0, *catch;
232 char *this, *that, *cusage, *tusage, *name;
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;
238 if (catch->help && (that = keyword("usage:", catch->help->data))) {
239 struct double_list *cfrom, *tfrom, *anchor;
240 char *try, **cdashlines, **tdashlines;
243 // Align usage: lines, finding a matching pair so we can suck help
244 // text out of throw into catch, copying from this to that
245 if (!throw) name = that;
246 else if (strncmp(name, that, len) || !isspace(that[len])) continue;
248 while (!isspace(*that) && *that) that++;
249 if (!throw) len = that-name;
258 // Grab option description lines to collate from catch and throw
259 tusage = dlist_zap(&throw->help);
260 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;
265 // If we've got both, collate and alphebetize
266 if (cdashlines && tdashlines) {
267 char **new = xmalloc(sizeof(char *)*(clen+tlen));
269 memcpy(new, cdashlines, sizeof(char *)*clen);
270 memcpy(new+clen, tdashlines, sizeof(char *)*tlen);
273 qsort(new, clen+tlen, sizeof(char *), (void *)dashlinesort);
276 // If just one, make sure it's in catch.
277 } else if (tdashlines) cdashlines = tdashlines;
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(""));
284 dlist_add(&cfrom, 0);
285 anchor = cfrom->prev;
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(""));
293 dlist_add(&cfrom, 0);
294 anchor = cfrom->prev;
297 // Splice sorted lines back in place
301 for (clen = 0; clen < tlen; clen++)
302 dlist_add(&cfrom, cdashlines[clen]);
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);
309 // zap whitespace at end of catch help text
310 while (!*trim(anchor->prev->data)) {
311 anchor = anchor->prev;
312 free(dlist_zap(&anchor));
315 // Append trailing lines.
316 while (tfrom) dlist_add(&anchor, dlist_zap(&tfrom));
318 // Collate first [-abc] option block in usage: lines
320 if (*this == '[' && this[1] == '-' && this[2] != '-' &&
321 *that == '[' && that[1] == '-' && that[2] != '-')
323 char *from = this+2, *to = that+2;
324 int ff = strcspn(from, " ]"), tt = strcspn(to, " ]");
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);
334 // The list is definitely no longer empty, so discard placeholder.
335 if (!anchor->data) dlist_zap(&anchor);
337 // Add new collated line (and whitespace).
338 dlist_add(&anchor, xmprintf("%*cusage: %.*s %s%s%s%s",
339 catch->help_indent, ' ', len, name, try ? try : "",
340 this, *this ? " " : "", that));
342 dlist_add(&anchor, strdup(""));
347 throw->help = anchor->prev->prev;
350 this = throw->help->data + throw->help_indent + 8 + len;
359 // Print out help #defines
361 struct double_list *dd;
365 char *s = xstrdup(sym->name);
367 for (i = 0; s[i]; i++) s[i] = tolower(s[i]);
368 printf("#define help_%s \"", s);
373 i = sym->help_indent;
375 // Trim leading whitespace
377 while (isspace(*s) && i) {
381 for (i=0; s[i]; i++) {
382 if (s[i] == '"' || s[i] == '\\') putchar('\\');
388 if (dd == sym->help) break;