OSDN Git Service

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