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