• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Take three word input lines on stdin and produce flag #defines to stdout.
2 // The three words on each input lnie are command name, option string with
3 // current config, option string from allyesconfig. The three are space
4 // separated and the last two are in double quotes.
5 
6 // This is intentionally crappy code because we control the inputs. It leaks
7 // memory like a sieve and segfaults if malloc returns null, but does the job.
8 
9 #include <unistd.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <errno.h>
14 #include <ctype.h>
15 
16 struct flag {
17   struct flag *next;
18   char *command;
19   struct flag *lopt;
20 };
21 
chrtype(char c)22 int chrtype(char c)
23 {
24   // Does this populate a GLOBALS() variable?
25   if (strchr("?&^-:#|@*; %", c)) return 1;
26 
27   // Is this followed by a numeric argument in optstr?
28   if (strchr("=<>", c)) return 2;
29 
30   return 0;
31 }
32 
33 // replace chopped out USE_BLAH() sections with low-ascii characters
34 // showing how many flags got skipped
35 
mark_gaps(char * flags,char * all)36 char *mark_gaps(char *flags, char *all)
37 {
38   char *n, *new, c;
39   int bare = 1;
40 
41   // Shell feeds in " " for blank args, leading space not meaningful.
42   while (isspace(*flags)) flags++;
43   while (isspace(*all)) all++;
44 
45   n = new = strdup(all);
46   while (*all) {
47     // --longopt parentheticals dealt with as a unit
48     if (*all == '(') {
49       int len = 0;
50 
51       while (all[len++] != ')');
52       if (strncmp(flags, all, len)) {
53         // bare longopts need their own skip placeholders
54         if (bare) *(new++) = 1;
55       } else {
56         memcpy(new, all, len);
57         new += len;
58         flags += len;
59       }
60       all += len;
61       continue;
62     }
63     c = *(all++);
64     if (bare) bare = chrtype(c);
65     if (*flags == c) {
66       *(new++) = c;
67       flags++;
68       continue;
69     }
70 
71     c = chrtype(c);
72     if (!c) *(new++) = 1;
73     else if (c==2) while (isdigit(*all)) all++;
74   }
75   *new = 0;
76 
77   return n;
78 }
79 
80 // Break down a command string into linked list of "struct flag".
81 
digest(char * string)82 struct flag *digest(char *string)
83 {
84   struct flag *list = NULL;
85   char *err = string, c;
86 
87   while (*string) {
88     // Groups must be at end.
89     if (*string == '[') break;
90 
91     // Longopts
92     if (*string == '(') {
93       struct flag *new = calloc(sizeof(struct flag), 1);
94 
95       new->command = ++string;
96 
97       // Attach longopt to previous short opt, if any.
98       if (list && list->command) {
99         new->next = list->lopt;
100         list->lopt = new;
101       } else {
102         struct flag *blank = calloc(sizeof(struct flag), 1);
103 
104         blank->next = list;
105         blank->lopt = new;
106         list = blank;
107       }
108       // An empty longopt () would break this.
109       while (*++string != ')') if (*string == '-') *string = '_';
110       *(string++) = 0;
111       continue;
112     }
113 
114     c = chrtype(*string);
115     if (c == 1) string++;
116     else if (c == 2) {
117       if (string[1]=='-') string++;
118       if (!isdigit(string[1])) {
119         fprintf(stderr, "%c without number in '%s'", *string, err);
120         exit(1);
121       }
122       while (isdigit(*++string)) {
123         if (!list) {
124            string++;
125            break;
126         }
127       }
128     } else {
129       struct flag *new = calloc(sizeof(struct flag), 1);
130 
131       new->command = string++;
132       new->next = list;
133       list = new;
134     }
135   }
136 
137   return list;
138 }
139 
140 // Parse C-style octal escape
octane(char * from)141 void octane(char *from)
142 {
143   unsigned char *to = (void *)from;
144 
145   while (*from) {
146     if (*from == '\\') {
147       *to = 0;
148       while (isdigit(*++from)) *to = (8**to)+*from-'0';
149       to++;
150     } else *to++ = *from++;
151   }
152   *to = 0;
153 }
154 
main(int argc,char * argv[])155 int main(int argc, char *argv[])
156 {
157   char command[256], flags[1023], allflags[1024];
158   char *out, *outbuf = malloc(1024*1024);
159 
160   // Yes, the output buffer is 1 megabyte with no bounds checking.
161   // See "intentionally crappy", above.
162   if (!(out = outbuf)) return 1;
163 
164   printf("#undef FORCED_FLAG\n#undef FORCED_FLAGLL\n"
165     "#ifdef FORCE_FLAGS\n#define FORCED_FLAG 1\n#define FORCED_FLAGLL 1LL\n"
166     "#else\n#define FORCED_FLAG 0\n#define FORCED_FLAGLL 0\n#endif\n\n");
167 
168   for (;;) {
169     struct flag *flist, *aflist, *offlist;
170     char *mgaps = 0;
171     unsigned bit;
172 
173     *command = *flags = *allflags = 0;
174     bit = fscanf(stdin, "%255s \"%1023[^\"]\" \"%1023[^\"]\"\n",
175                     command, flags, allflags);
176     octane(flags);
177     octane(allflags);
178 
179     if (getenv("DEBUG"))
180       fprintf(stderr, "command=%s, flags=%s, allflags=%s\n",
181         command, flags, allflags);
182 
183     if (!*command) break;
184     if (bit != 3) {
185       fprintf(stderr, "\nError in %s (see generated/flags.raw)\n", command);
186       exit(1);
187     }
188 
189     bit = 0;
190     printf("// %s %s %s\n", command, flags, allflags);
191     if (*flags != ' ') mgaps = mark_gaps(flags, allflags);
192     else if (*allflags != ' ') mgaps = allflags;
193     // If command disabled, use allflags for OLDTOY()
194     printf("#undef OPTSTR_%s\n#define OPTSTR_%s ", command, command);
195     if (mgaps) printf("\"%s\"\n", mgaps);
196     else printf("0\n");
197     if (mgaps != allflags) free(mgaps);
198 
199     flist = digest(flags);
200     offlist = aflist = digest(allflags);
201 
202     printf("#ifdef CLEANUP_%s\n#undef CLEANUP_%s\n#undef FOR_%s\n",
203            command, command, command);
204 
205     while (offlist) {
206       char *s = (char []){0, 0, 0, 0};
207 
208       if (!offlist->command) s = offlist->lopt->command;
209       else {
210         *s = *offlist->command;
211         if (127 < (unsigned char)*s) sprintf(s, "X%02X", 127&*s);
212       }
213       printf("#undef FLAG_%s\n", s);
214       offlist = offlist->next;
215     }
216     printf("#endif\n\n");
217 
218     sprintf(out, "#ifdef FOR_%s\n#ifndef TT\n#define TT this.%s\n#endif\n",
219             command, command);
220     out += strlen(out);
221 
222     while (aflist) {
223       char *llstr = bit>31 ? "LL" : "", *s = (char []){0, 0, 0, 0};
224       int enabled = 0;
225 
226       // Output flag macro for bare longopts
227       if (!aflist->command) {
228         s = aflist->lopt->command;
229         if (flist && flist->lopt &&
230             !strcmp(flist->lopt->command, aflist->lopt->command)) enabled++;
231       // Output normal flag macro
232       } else {
233         *s = *aflist->command;
234         if (127 < (unsigned char)*s) sprintf(s, "X%02X", 127&*s);
235         if (flist && flist->command && *aflist->command == *flist->command)
236           enabled++;
237       }
238       out += sprintf(out, "#define FLAG_%s (%s%s<<%d)\n",
239                        s, enabled ? "1" : "FORCED_FLAG", llstr, bit++);
240       aflist = aflist->next;
241       if (enabled) flist = flist->next;
242     }
243     out = stpcpy(out, "#endif\n\n");
244   }
245 
246   if (fflush(0) && ferror(stdout)) return 1;
247 
248   out = outbuf;
249   while (*out) {
250     int i = write(1, outbuf, strlen(outbuf));
251 
252     if (i<0) return 1;
253     out += i;
254   }
255 
256   return 0;
257 }
258