1// Take three word input lines on stdin (the three space separated words are
2// command name, option string with current config, option string from
3// allyesconfig; space separated, the last two are and double quotes)
4// and produce flag #defines to stdout.
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 <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <errno.h>
13
14struct flag {
15  struct flag *next;
16  char *command;
17  struct flag *lopt;
18};
19
20// replace chopped out USE_BLAH() sections with low-ascii characters
21// showing how many flags got skipped
22
23char *mark_gaps(char *flags, char *all)
24{
25  char *n, *new, c;
26
27  // Shell feeds in " " for blank args, leading space not meaningful.
28  while (isspace(*flags)) flags++;
29  while (isspace(*all)) all++;
30
31  n = new = strdup(all);
32  while (*all) {
33    if (*flags == *all) {
34      *(new++) = *(all++);
35      *flags++;
36      continue;
37    }
38
39    c = *(all++);
40    if (strchr("?&^-:#|@*; ", c));
41    else if (strchr("=<>", c)) while (isdigit(*all)) all++;
42    else if (c == '(') while(*(all++) != ')');
43    else *(new++) = 1;
44  }
45  *new = 0;
46
47  return n;
48}
49
50// Break down a command string into struct flag list.
51
52struct flag *digest(char *string)
53{
54  struct flag *list = NULL;
55  char *err = string;
56
57  while (*string) {
58    // Groups must be at end.
59    if (*string == '[') break;
60
61    // Longopts
62    if (*string == '(') {
63      struct flag *new = calloc(sizeof(struct flag), 1);
64
65      new->command = ++string;
66
67      // Attach longopt to previous short opt, if any.
68      if (list && list->command) {
69        new->next = list->lopt;
70        list->lopt = new;
71      } else {
72        struct flag *blank = calloc(sizeof(struct flag), 1);
73
74        blank->next = list;
75        blank->lopt = new;
76        list = blank;
77      }
78      // An empty longopt () would break this.
79      while (*++string != ')') if (*string == '-') *string = '_';
80      *(string++) = 0;
81      continue;
82    }
83
84    if (strchr("?&^-:#|@*; ", *string)) string++;
85    else if (strchr("=<>", *string)) {
86      if (!isdigit(string[1])) {
87        fprintf(stderr, "%c without number in '%s'", *string, err);
88        exit(1);
89      }
90      while (isdigit(*++string)) {
91        if (!list) {
92           string++;
93           break;
94        }
95      }
96    } else {
97      struct flag *new = calloc(sizeof(struct flag), 1);
98
99      new->command = string++;
100      new->next = list;
101      list = new;
102    }
103  }
104
105  return list;
106}
107
108int main(int argc, char *argv[])
109{
110  char command[256], flags[1023], allflags[1024];
111  char *out, *outbuf = malloc(1024*1024);
112
113  // Yes, the output buffer is 1 megabyte with no bounds checking.
114  // See "intentionally crappy", above.
115  if (!(out = outbuf)) return 1;
116
117  printf("#ifdef FORCE_FLAGS\n#define FORCED_FLAG 1\n"
118         "#else\n#define FORCED_FLAG 0\n#endif\n\n");
119
120  for (;;) {
121    struct flag *flist, *aflist, *offlist;
122    char *gaps, *mgaps, c;
123    unsigned bit;
124
125    *command = *flags = *allflags = 0;
126    bit = fscanf(stdin, "%255s \"%1023[^\"]\" \"%1023[^\"]\"\n",
127                    command, flags, allflags);
128
129    if (getenv("DEBUG"))
130      fprintf(stderr, "command=%s, flags=%s, allflags=%s\n",
131        command, flags, allflags);
132
133    if (!*command) break;
134    if (bit != 3) {
135      fprintf(stderr, "\nError in %s (duplicate command?)\n", command);
136      exit(1);
137    }
138
139    bit = 0;
140    printf("// %s %s %s\n", command, flags, allflags);
141    mgaps = mark_gaps(flags, allflags);
142    for (gaps = mgaps; *gaps == 1; gaps++);
143    if (*gaps) c = '"';
144    else {
145      c = ' ';
146      gaps = "0";
147    }
148    printf("#undef OPTSTR_%s\n#define OPTSTR_%s %c%s%c\n",
149            command, command, c, gaps, c);
150    free(mgaps);
151
152    flist = digest(flags);
153    offlist = aflist = digest(allflags);
154
155    printf("#ifdef CLEANUP_%s\n#undef CLEANUP_%s\n#undef FOR_%s\n",
156           command, command, command);
157
158    while (offlist) {
159      struct flag *f = offlist->lopt;
160      while (f) {
161        printf("#undef FLAG_%s\n", f->command);
162        f = f->next;
163      }
164      if (offlist->command) printf("#undef FLAG_%c\n", *offlist->command);
165      offlist = offlist->next;
166    }
167    printf("#endif\n\n");
168
169    sprintf(out, "#ifdef FOR_%s\n#ifndef TT\n#define TT this.%s\n#endif\n",
170            command, command);
171    out += strlen(out);
172
173    while (aflist) {
174      if (aflist->lopt) {
175        if (flist && flist->lopt &&
176            !strcmp(flist->lopt->command, aflist->lopt->command))
177        {
178          sprintf(out, "#define FLAG_%s (1<<%d)\n", flist->lopt->command, bit);
179          flist->lopt = flist->lopt->next;
180        } else sprintf(out, "#define FLAG_%s (FORCED_FLAG<<%d)\n",
181                       aflist->lopt->command, bit);
182        aflist->lopt = aflist->lopt->next;
183        if (!aflist->command) {
184          aflist = aflist->next;
185          bit++;
186          if (flist) flist = flist->next;
187        }
188      } else if (aflist->command) {
189        if (flist && (!flist->command || *aflist->command == *flist->command)) {
190          if (aflist->command)
191            sprintf(out, "#define FLAG_%c (1<<%d)\n", *aflist->command, bit);
192          flist = flist->next;
193        } else sprintf(out, "#define FLAG_%c (FORCED_FLAG<<%d)\n",
194                       *aflist->command, bit);
195        bit++;
196        aflist = aflist->next;
197      }
198      out += strlen(out);
199    }
200    out = stpcpy(out, "#endif\n\n");
201  }
202
203  if (fflush(0) && ferror(stdout)) return 1;
204
205  out = outbuf;
206  while (*out) {
207    int i = write(1, outbuf, strlen(outbuf));
208
209    if (i<0) return 1;
210    out += i;
211  }
212
213  return 0;
214}
215