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