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