OSDN Git Service

Cosmetic tweak: newline at the end of "make change".
[android-x86/external-toybox.git] / scripts / config2help.c
1 #include "toys.h"
2
3 // Humor toys.h
4 struct toy_context toys;
5 char libbuf[4096], toybuf[4096];
6 void show_help(void) {;}
7 void toy_exec(char *argv[]) {;}
8
9 // Parse config files into data structures.
10
11 struct symbol {
12   struct symbol *next;
13   int enabled, help_indent;
14   char *name, *depends;
15   struct double_list *help;
16 } *sym;
17
18 char *trim(char *s)
19 {
20   while (isspace(*s)) s++;
21
22   return s;
23 }
24
25 char *keyword(char *name, char *line)
26 {
27   int len = strlen(name);
28
29   line = trim(line);
30   if (strncmp(name, line, len)) return 0;
31   line += len;
32   if (*line && !isspace(*line)) return 0;
33   line = trim(line);
34
35   return line;
36 }
37
38 char *dlist_zap(struct double_list **help)
39 {
40   struct double_list *dd = dlist_pop(help);
41   char *s = dd->data;
42
43   free(dd);
44   
45   return s;
46 }
47
48 int zap_blank_lines(struct double_list **help)
49 {
50   int got = 0;
51
52   while (*help) {
53     char *s;
54
55     s = trim((*help)->data);
56
57     if (*s) break;
58     got++;
59     free(dlist_zap(help));
60   }
61
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
75 char **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;
122 }
123
124 void parse(char *filename)
125 {
126   FILE *fp = xfopen(filename, "r");
127   struct symbol *new = 0;
128
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)) {
143       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
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++;
159       }
160     }
161     else if ((s = keyword("depends", line)) && (s = keyword("on", s)))
162       new->depends = s;
163     else if (keyword("help", line)) sym->help_indent = -1;
164   }
165
166   fclose(fp);
167 }
168
169 int 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
178 int dashsort(char **a, char **b)
179 {
180   char *aa = *a, *bb = *b;
181
182   if (aa[1] < bb[1]) return -1;
183   if (aa[1] > bb[1]) return 1;
184   return 0;
185 }
186
187 int dashlinesort(char **a, char **b)
188 {
189   return strcmp(*a, *b);
190 }
191
192 int main(int argc, char *argv[])
193 {
194   FILE *fp;
195
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)) {
212       struct symbol *try;
213       char *s = line+7;
214
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') {
218           try->enabled++;
219           break;
220         } 
221       }
222     }
223   }
224
225   // Collate help according to usage, depends, and .config
226
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.
230   for (;;) {
231     struct symbol *throw = 0, *catch;
232     char *this, *that, *cusage, *tusage, *name;
233     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;
238       if (catch->help && (that = keyword("usage:", catch->help->data))) {
239         struct double_list *cfrom, *tfrom, *anchor;
240         char *try, **cdashlines, **tdashlines;
241         int clen, tlen;
242
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;
247         catch->enabled++;
248         while (!isspace(*that) && *that) that++;
249         if (!throw) len = that-name;
250         that = trim(that);
251         if (!throw) {
252           throw = catch;
253           this = that;
254
255           continue;
256         }
257
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;
264
265         // If we've got both, collate and alphebetize
266         if (cdashlines && tdashlines) {
267           char **new = xmalloc(sizeof(char *)*(clen+tlen));
268
269           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
319         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
334         // The list is definitely no longer empty, so discard placeholder.
335         if (!anchor->data) dlist_zap(&anchor);
336
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));
341         free(try);
342         dlist_add(&anchor, strdup(""));
343         free(cusage);
344         free(tusage);
345         throw->enabled = 0;
346         throw = catch;
347         throw->help = anchor->prev->prev;
348
349         throw = catch;
350         this = throw->help->data + throw->help_indent + 8 + len;
351       }
352     }
353
354     // Did we find one?
355
356     if (!throw) break;
357   }
358
359   // Print out help #defines
360   while (sym) {
361     struct double_list *dd;
362
363     if (sym->help) {
364       int i;
365       char *s = xstrdup(sym->name);
366
367       for (i = 0; s[i]; i++) s[i] = tolower(s[i]);
368       printf("#define help_%s \"", s);
369       free(s);
370
371       dd = sym->help;
372       for (;;) {
373         i = sym->help_indent;
374
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         }
385         putchar('\\');
386         putchar('n');
387         dd = dd->next;
388         if (dd == sym->help) break;
389       }
390       printf("\"\n\n");
391     }
392     sym = sym->next;
393   }
394
395   return 0;
396 }