• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* config2.help.c - config2hep Config.in .config > help.h
2 
3    function parse() reads Config.in data into *sym list, then
4    we read .config and set sym->try on each enabled symbol.
5 
6 */
7 
8 #include <ctype.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <stdlib.h>
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <unistd.h>
15 #include <regex.h>
16 #include <inttypes.h>
17 #include <termios.h>
18 #include <poll.h>
19 #include <sys/socket.h>
20 
21 struct statvfs {int i;};
22 #include "lib/portability.h"
23 #include "lib/lib.h"
24 
25 // Humor toys.h (lie through our teeth, C's linker doesn't care).
26 char toys[4096], libbuf[4096], toybuf[4096];
show_help(FILE * out)27 void show_help(FILE *out) {;}
toy_exec(char * argv[])28 void toy_exec(char *argv[]) {;}
toy_init(void * which,char * argv[])29 void toy_init(void *which, char *argv[]) {;}
30 
31 // Parse config files into data structures.
32 
33 struct symbol {
34   struct symbol *next;
35   int enabled, help_indent;
36   char *name, *depends;
37   struct double_list *help;
38 } *sym;
39 
40 // remove leading spaces
skip_spaces(char * s)41 char *skip_spaces(char *s)
42 {
43   while (isspace(*s)) s++;
44 
45   return s;
46 }
47 
48 // if line starts with name (as whole word) return pointer after it, else NULL
keyword(char * name,char * line)49 char *keyword(char *name, char *line)
50 {
51   int len = strlen(name);
52 
53   line = skip_spaces(line);
54   if (strncmp(name, line, len)) return 0;
55   line += len;
56   if (*line && !isspace(*line)) return 0;
57   line = skip_spaces(line);
58 
59   return line;
60 }
61 
62 // dlist_pop() freeing wrapper structure for you.
dlist_zap(struct double_list ** help)63 char *dlist_zap(struct double_list **help)
64 {
65   struct double_list *dd = dlist_pop(help);
66   char *s = dd->data;
67 
68   free(dd);
69 
70   return s;
71 }
72 
zap_blank_lines(struct double_list ** help)73 int zap_blank_lines(struct double_list **help)
74 {
75   int got = 0;
76 
77   while (*help) {
78     char *s;
79 
80     s = skip_spaces((*help)->data);
81 
82     if (*s) break;
83     got++;
84     free(dlist_zap(help));
85   }
86 
87   return got;
88 }
89 
90 // Collect "-a blah" description lines following a blank line (or start).
91 // Returns array of removed lines with *len entries (0 for none).
92 
93 // Moves *help to new start of text (in case dash lines were at beginning).
94 // Sets *from to where dash lines removed from (in case they weren't).
95 // Discards blank lines before and after dashlines.
96 
97 // If no prefix, *help NULL. If no postfix, *from == *help
98 // if no dashlines returned *from == *help.
99 
grab_dashlines(struct double_list ** help,struct double_list ** from,int * len)100 char **grab_dashlines(struct double_list **help, struct double_list **from,
101                       int *len)
102 {
103   struct double_list *dd;
104   char *s, **list;
105   int count = 0;
106 
107   *len = 0;
108   zap_blank_lines(help);
109   *from = *help;
110 
111   // Find start of dash block. Must be at start or after blank line.
112   for (;;) {
113     s = skip_spaces((*from)->data);
114     if (*s == '-' && s[1] != '-' && !count) break;
115 
116     if (!*s) count = 0;
117     else count++;
118 
119     *from = (*from)->next;
120     if (*from == *help) return 0;
121   }
122 
123   // If there was whitespace before this, zap it. This can't take out *help
124   // because zap_blank_lines skipped blank lines, and we had to have at least
125   // one non-blank line (a dash line) to get this far.
126   while (!*skip_spaces((*from)->prev->data)) {
127     *from = (*from)->prev;
128     free(dlist_zap(from));
129   }
130 
131   // Count number of dashlines, copy out to array, zap trailing whitespace
132   // If *help was at start of dashblock, move it with *from
133   count = 0;
134   dd = *from;
135   if (*help == *from) *help = 0;
136   for (;;) {
137    if (*skip_spaces(dd->data) != '-') break;
138    count++;
139    if (*from == (dd = dd->next)) break;
140   }
141 
142   list = xmalloc(sizeof(char *)*count);
143   *len = count;
144   while (count) list[--count] = dlist_zap(from);
145 
146   return list;
147 }
148 
149 // Read Config.in (and includes) to populate global struct symbol *sym list.
parse(char * filename)150 void parse(char *filename)
151 {
152   FILE *fp = xfopen(filename, "r");
153   struct symbol *new = 0;
154 
155   for (;;) {
156     char *s, *line = NULL;
157     size_t len;
158 
159     // Read line, trim whitespace at right edge.
160     if (getline(&line, &len, fp) < 1) break;
161     s = line+strlen(line);
162     while (--s >= line) {
163       if (!isspace(*s)) break;
164       *s = 0;
165     }
166 
167     // source or config keyword at left edge?
168     if (*line && !isspace(*line)) {
169       if ((s = keyword("config", line))) {
170         new = xzalloc(sizeof(struct symbol));
171         new->next = sym;
172         new->name = s;
173         sym = new;
174       } else if ((s = keyword("source", line))) parse(s);
175 
176       continue;
177     }
178     if (!new) continue;
179 
180     if (sym && sym->help_indent) {
181       dlist_add(&(new->help), line);
182       if (sym->help_indent < 0) {
183         sym->help_indent = 0;
184         while (isspace(line[sym->help_indent])) sym->help_indent++;
185       }
186     }
187     else if ((s = keyword("depends", line)) && (s = keyword("on", s)))
188       new->depends = s;
189     else if (keyword("help", line)) sym->help_indent = -1;
190   }
191 
192   fclose(fp);
193 }
194 
charsort(void * a,void * b)195 int charsort(void *a, void *b)
196 {
197   char *aa = a, *bb = b;
198 
199   if (*aa < *bb) return -1;
200   if (*aa > *bb) return 1;
201   return 0;
202 }
203 
dashsort(char ** a,char ** b)204 int dashsort(char **a, char **b)
205 {
206   char *aa = *a, *bb = *b;
207 
208   if (aa[1] < bb[1]) return -1;
209   if (aa[1] > bb[1]) return 1;
210   return 0;
211 }
212 
dashlinesort(char ** a,char ** b)213 int dashlinesort(char **a, char **b)
214 {
215   return strcmp(*a, *b);
216 }
217 
218 // Three stages: read data, collate entries, output results.
219 
main(int argc,char * argv[])220 int main(int argc, char *argv[])
221 {
222   FILE *fp;
223 
224   if (argc != 3) {
225     fprintf(stderr, "usage: config2help Config.in .config\n");
226     exit(1);
227   }
228 
229   // Stage 1: read data. Read Config.in to global 'struct symbol *sym' list,
230   // then read .config to set "enabled" member of each enabled symbol.
231 
232   // Read Config.in
233   parse(argv[1]);
234 
235   // read .config
236   fp = xfopen(argv[2], "r");
237   for (;;) {
238     char *line = NULL;
239     size_t len;
240 
241     if (getline(&line, &len, fp) < 1) break;
242     if (!strncmp("CONFIG_", line, 7)) {
243       struct symbol *try;
244       char *s = line+7;
245 
246       for (try=sym; try; try=try->next) {
247         len = strlen(try->name);
248         if (!strncmp(try->name, s, len) && s[len]=='=' && s[len+1]=='y') {
249           try->enabled++;
250           break;
251         }
252       }
253     }
254   }
255 
256   // Stage 2: process data.
257 
258   // Collate help according to usage, depends, and .config
259 
260   // Loop through each entry, finding duplicate enabled "usage:" names
261   // This is in reverse order, so last entry gets collated with previous
262   // entry until we run out of matching pairs.
263   for (;;) {
264     struct symbol *throw = 0, *catch;
265     char *this, *that, *cusage, *tusage, *name;
266     int len;
267 
268     // find a usage: name and collate all enabled entries with that name
269     for (catch = sym; catch; catch = catch->next) {
270       if (catch->enabled != 1) continue;
271       if (catch->help && (that = keyword("usage:", catch->help->data))) {
272         struct double_list *cfrom, *tfrom, *anchor;
273         char *try, **cdashlines, **tdashlines;
274         int clen, tlen;
275 
276         // Align usage: lines, finding a matching pair so we can suck help
277         // text out of throw into catch, copying from this to that
278         if (!throw) name = that;
279         else if (strncmp(name, that, len) || !isspace(that[len])) continue;
280         catch->enabled++;
281         while (!isspace(*that) && *that) that++;
282         if (!throw) len = that-name;
283         that = skip_spaces(that);
284         if (!throw) {
285           throw = catch;
286           this = that;
287 
288           continue;
289         }
290 
291         // Grab option description lines to collate from catch and throw
292         tusage = dlist_zap(&throw->help);
293         tdashlines = grab_dashlines(&throw->help, &tfrom, &tlen);
294         cusage = dlist_zap(&catch->help);
295         cdashlines = grab_dashlines(&catch->help, &cfrom, &clen);
296         anchor = catch->help;
297 
298         // If we've got both, collate and alphebetize
299         if (cdashlines && tdashlines) {
300           char **new = xmalloc(sizeof(char *)*(clen+tlen));
301 
302           memcpy(new, cdashlines, sizeof(char *)*clen);
303           memcpy(new+clen, tdashlines, sizeof(char *)*tlen);
304           free(cdashlines);
305           free(tdashlines);
306           qsort(new, clen+tlen, sizeof(char *), (void *)dashlinesort);
307           cdashlines = new;
308 
309         // If just one, make sure it's in catch.
310         } else if (tdashlines) cdashlines = tdashlines;
311 
312         // If throw had a prefix, insert it before dashlines, with a
313         // blank line if catch had a prefix.
314         if (tfrom && tfrom != throw->help) {
315           if (throw->help || catch->help) dlist_add(&cfrom, strdup(""));
316           else {
317             dlist_add(&cfrom, 0);
318             anchor = cfrom->prev;
319           }
320           while (throw->help && throw->help != tfrom)
321             dlist_add(&cfrom, dlist_zap(&throw->help));
322           if (cfrom && cfrom->prev->data && *skip_spaces(cfrom->prev->data))
323             dlist_add(&cfrom, strdup(""));
324         }
325         if (!anchor) {
326           dlist_add(&cfrom, 0);
327           anchor = cfrom->prev;
328         }
329 
330         // Splice sorted lines back in place
331         if (cdashlines) {
332           tlen += clen;
333 
334           for (clen = 0; clen < tlen; clen++)
335             dlist_add(&cfrom, cdashlines[clen]);
336         }
337 
338         // If there were no dashlines, text would be considered prefix, so
339         // the list is definitely no longer empty, so discard placeholder.
340         if (!anchor->data) dlist_zap(&anchor);
341 
342         // zap whitespace at end of catch help text
343         while (!*skip_spaces(anchor->prev->data)) {
344           anchor = anchor->prev;
345           free(dlist_zap(&anchor));
346         }
347 
348         // Append trailing lines.
349         while (tfrom) dlist_add(&anchor, dlist_zap(&tfrom));
350 
351         // Collate first [-abc] option block in usage: lines
352         try = 0;
353         if (*this == '[' && this[1] == '-' && this[2] != '-' &&
354             *that == '[' && that[1] == '-' && that[2] != '-')
355         {
356           char *from = this+2, *to = that+2;
357           int ff = strcspn(from, " ]"), tt = strcspn(to, " ]");
358 
359           if (from[ff] == ']' && to[tt] == ']') {
360             try = xmprintf("[-%.*s%.*s] ", ff, from, tt, to);
361             qsort(try+2, ff+tt, 1, (void *)charsort);
362             this = skip_spaces(this+ff+3);
363             that = skip_spaces(that+tt+3);
364           }
365         }
366 
367         // The list is definitely no longer empty, so discard placeholder.
368         if (!anchor->data) dlist_zap(&anchor);
369 
370         // Add new collated line (and whitespace).
371         dlist_add(&anchor, xmprintf("%*cusage: %.*s %s%s%s%s",
372                   catch->help_indent, ' ', len, name, try ? try : "",
373                   this, *this ? " " : "", that));
374         free(try);
375         dlist_add(&anchor, strdup(""));
376         free(cusage);
377         free(tusage);
378         throw->enabled = 0;
379         throw = catch;
380         throw->help = anchor->prev->prev;
381 
382         throw = catch;
383         this = throw->help->data + throw->help_indent + 8 + len;
384       }
385     }
386 
387     // Did we find one?
388 
389     if (!throw) break;
390   }
391 
392   // Stage 3: output results to stdout.
393 
394   // Print out help #defines
395   while (sym) {
396     struct double_list *dd;
397 
398     if (sym->help) {
399       int i;
400       char *s = xstrdup(sym->name);
401 
402       for (i = 0; s[i]; i++) s[i] = tolower(s[i]);
403       printf("#define HELP_%s \"", s);
404       free(s);
405 
406       dd = sym->help;
407       for (;;) {
408         i = sym->help_indent;
409 
410         // Trim leading whitespace
411         s = dd->data;
412         while (isspace(*s) && i) {
413           s++;
414           i--;
415         }
416         for (i=0; s[i]; i++) {
417           if (s[i] == '"' || s[i] == '\\') putchar('\\');
418           putchar(s[i]);
419         }
420         putchar('\\');
421         putchar('n');
422         dd = dd->next;
423         if (dd == sym->help) break;
424       }
425       printf("\"\n\n");
426     }
427     sym = sym->next;
428   }
429 
430   return 0;
431 }
432