toybox/scripts/mkflags.c
<<
>>
Prefs
   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
  16struct flag {
  17  struct flag *next;
  18  char *command;
  19  struct flag *lopt;
  20};
  21
  22int 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
  38char *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
  85struct 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
 144void 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
 158int 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