toybox/scripts/config2help.c
<<
>>
Prefs
   1/* config2.help.c - config2hep Config.in .config > help.h
   2
   3   function parse() reads Config.in data into *sym list, then
   4   we read .config and set sym->try on each enabled symbol.
   5
   6*/
   7
   8#include <ctype.h>
   9#include <stdio.h>
  10#include <string.h>
  11#include <stdarg.h>
  12#include <stdlib.h>
  13#include <sys/types.h>
  14#include <sys/stat.h>
  15#include <unistd.h>
  16#include <regex.h>
  17#include <inttypes.h>
  18#include <termios.h>
  19#include <poll.h>
  20#include <sys/socket.h>
  21
  22/****************** functions copied from lib/*.c ********************/
  23
  24struct double_list {
  25  struct double_list *next, *prev;
  26  char *data;
  27};
  28
  29// Die unless we can allocate memory.
  30void *xmalloc(size_t size)
  31{
  32  void *ret = malloc(size);
  33  if (!ret) {
  34    fprintf(stderr, "xmalloc(%ld)", (long)size);
  35    exit(1);
  36  }
  37
  38  return ret;
  39}
  40
  41// Die unless we can allocate enough space to sprintf() into.
  42char *xmprintf(char *format, ...)
  43{
  44  va_list va, va2;
  45  int len;
  46  char *ret;
  47
  48  va_start(va, format);
  49  va_copy(va2, va);
  50
  51  // How long is it?
  52  len = vsnprintf(0, 0, format, va);
  53  len++;
  54  va_end(va);
  55
  56  // Allocate and do the sprintf()
  57  ret = xmalloc(len);
  58  vsnprintf(ret, len, format, va2);
  59  va_end(va2);
  60
  61  return ret;
  62}
  63
  64// Die unless we can open/create a file, returning FILE *.
  65FILE *xfopen(char *path, char *mode)
  66{
  67  FILE *f = fopen(path, mode);
  68  if (!f) {
  69    fprintf(stderr, "No file %s", path);
  70    exit(1);
  71  }
  72  return f;
  73}
  74
  75void *dlist_pop(void *list)
  76{
  77  struct double_list **pdlist = (struct double_list **)list, *dlist = *pdlist;
  78
  79  if (dlist->next == dlist) *pdlist = 0;
  80  else {
  81    dlist->next->prev = dlist->prev;
  82    dlist->prev->next = *pdlist = dlist->next;
  83  }
  84
  85  return dlist;
  86}
  87
  88void dlist_add_nomalloc(struct double_list **list, struct double_list *new)
  89{
  90  if (*list) {
  91    new->next = *list;
  92    new->prev = (*list)->prev;
  93    (*list)->prev->next = new;
  94    (*list)->prev = new;
  95  } else *list = new->next = new->prev = new;
  96}
  97
  98
  99// Add an entry to the end of a doubly linked list
 100struct double_list *dlist_add(struct double_list **list, char *data)
 101{
 102  struct double_list *new = xmalloc(sizeof(struct double_list));
 103
 104  new->data = data;
 105  dlist_add_nomalloc(list, new);
 106
 107  return new;
 108}
 109
 110/****************** end copies of lib/*.c *************/
 111
 112// Parse config files into data structures.
 113
 114struct symbol {
 115  struct symbol *next;
 116  int enabled, help_indent;
 117  char *name, *depends;
 118  struct double_list *help;
 119} *sym;
 120
 121// remove leading spaces
 122char *skip_spaces(char *s)
 123{
 124  while (isspace(*s)) s++;
 125
 126  return s;
 127}
 128
 129// if line starts with name (as whole word) return pointer after it, else NULL
 130char *keyword(char *name, char *line)
 131{
 132  int len = strlen(name);
 133
 134  line = skip_spaces(line);
 135  if (strncmp(name, line, len)) return 0;
 136  line += len;
 137  if (*line && !isspace(*line)) return 0;
 138  line = skip_spaces(line);
 139
 140  return line;
 141}
 142
 143// dlist_pop() freeing wrapper structure for you.
 144char *dlist_zap(struct double_list **help)
 145{
 146  struct double_list *dd = dlist_pop(help);
 147  char *s = dd->data;
 148
 149  free(dd);
 150  
 151  return s;
 152}
 153
 154int zap_blank_lines(struct double_list **help)
 155{
 156  int got = 0;
 157
 158  while (*help) {
 159    char *s;
 160
 161    s = skip_spaces((*help)->data);
 162
 163    if (*s) break;
 164    got++;
 165    free(dlist_zap(help));
 166  }
 167
 168  return got;
 169}
 170
 171// Collect "-a blah" description lines following a blank line (or start).
 172// Returns array of removed lines with *len entries (0 for none).
 173
 174// Moves *help to new start of text (in case dash lines were at beginning).
 175// Sets *from to where dash lines removed from (in case they weren't).
 176// Discards blank lines before and after dashlines.
 177
 178// If no prefix, *help NULL. If no postfix, *from == *help
 179// if no dashlines returned *from == *help.
 180
 181char **grab_dashlines(struct double_list **help, struct double_list **from,
 182                      int *len)
 183{
 184  struct double_list *dd;
 185  char *s, **list;
 186  int count = 0;
 187
 188  *len = 0;
 189  zap_blank_lines(help);
 190  *from = *help;
 191
 192  // Find start of dash block. Must be at start or after blank line.
 193  for (;;) {
 194    s = skip_spaces((*from)->data);
 195    if (*s == '-' && s[1] != '-' && !count) break;
 196
 197    if (!*s) count = 0;
 198    else count++;
 199
 200    *from = (*from)->next;
 201    if (*from == *help) return 0;
 202  }
 203
 204  // If there was whitespace before this, zap it. This can't take out *help
 205  // because zap_blank_lines skipped blank lines, and we had to have at least
 206  // one non-blank line (a dash line) to get this far.
 207  while (!*skip_spaces((*from)->prev->data)) {
 208    *from = (*from)->prev;
 209    free(dlist_zap(from));
 210  }
 211
 212  // Count number of dashlines, copy out to array, zap trailing whitespace
 213  // If *help was at start of dashblock, move it with *from
 214  count = 0;
 215  dd = *from;
 216  if (*help == *from) *help = 0;
 217  for (;;) {
 218   if (*skip_spaces(dd->data) != '-') break;
 219   count++;
 220   if (*from == (dd = dd->next)) break;
 221  }
 222
 223  list = xmalloc(sizeof(char *)*count);
 224  *len = count;
 225  while (count) list[--count] = dlist_zap(from);
 226
 227  return list;
 228}
 229
 230// Read Config.in (and includes) to populate global struct symbol *sym list.
 231void parse(char *filename)
 232{
 233  FILE *fp = xfopen(filename, "r");
 234  struct symbol *new = 0;
 235
 236  for (;;) {
 237    char *s, *line = NULL;
 238    size_t len;
 239
 240    // Read line, trim whitespace at right edge.
 241    if (getline(&line, &len, fp) < 1) break;
 242    s = line+strlen(line);
 243    while (--s >= line) {
 244      if (!isspace(*s)) break;
 245      *s = 0;
 246    }
 247
 248    // source or config keyword at left edge?
 249    if (*line && !isspace(*line)) {
 250      if ((s = keyword("config", line))) {
 251        memset(new = xmalloc(sizeof(struct symbol)), 0, sizeof(struct symbol));
 252        new->next = sym;
 253        new->name = s;
 254        sym = new;
 255      } else if ((s = keyword("source", line))) parse(s);
 256
 257      continue;
 258    }
 259    if (!new) continue;
 260
 261    if (sym && sym->help_indent) {
 262      dlist_add(&(new->help), line);
 263      if (sym->help_indent < 0) {
 264        sym->help_indent = 0;
 265        while (isspace(line[sym->help_indent])) sym->help_indent++;
 266      }
 267    }
 268    else if ((s = keyword("depends", line)) && (s = keyword("on", s)))
 269      new->depends = s;
 270    else if (keyword("help", line)) sym->help_indent = -1;
 271  }
 272
 273  fclose(fp);
 274}
 275
 276int charsort(void *a, void *b)
 277{
 278  char *aa = a, *bb = b;
 279
 280  if (*aa < *bb) return -1;
 281  if (*aa > *bb) return 1;
 282  return 0;
 283}
 284
 285int dashsort(char **a, char **b)
 286{
 287  char *aa = *a, *bb = *b;
 288
 289  if (aa[1] < bb[1]) return -1;
 290  if (aa[1] > bb[1]) return 1;
 291  return 0;
 292}
 293
 294int dashlinesort(char **a, char **b)
 295{
 296  return strcmp(*a, *b);
 297}
 298
 299// Three stages: read data, collate entries, output results.
 300
 301int main(int argc, char *argv[])
 302{
 303  FILE *fp;
 304
 305  if (argc != 3) {
 306    fprintf(stderr, "usage: config2help Config.in .config\n");
 307    exit(1);
 308  }
 309
 310  // Stage 1: read data. Read Config.in to global 'struct symbol *sym' list,
 311  // then read .config to set "enabled" member of each enabled symbol.
 312
 313  // Read Config.in
 314  parse(argv[1]);
 315
 316  // read .config
 317  fp = xfopen(argv[2], "r");
 318  for (;;) {
 319    char *line = NULL;
 320    size_t len;
 321
 322    if (getline(&line, &len, fp) < 1) break;
 323    if (!strncmp("CONFIG_", line, 7)) {
 324      struct symbol *try;
 325      char *s = line+7;
 326
 327      for (try=sym; try; try=try->next) {
 328        len = strlen(try->name);
 329        if (!strncmp(try->name, s, len) && s[len]=='=' && s[len+1]=='y') {
 330          try->enabled++;
 331          break;
 332        } 
 333      }
 334    }
 335  }
 336
 337  // Stage 2: process data.
 338
 339  // Collate help according to usage, depends, and .config
 340
 341  // Loop through each entry, finding duplicate enabled "usage:" names
 342  // This is in reverse order, so last entry gets collated with previous
 343  // entry until we run out of matching pairs.
 344  for (;;) {
 345    struct symbol *throw = 0, *catch;
 346    char *this, *that, *cusage, *tusage, *name = 0;
 347    int len;
 348
 349    // find a usage: name and collate all enabled entries with that name
 350    for (catch = sym; catch; catch = catch->next) {
 351      if (catch->enabled != 1) continue;
 352      if (catch->help && (that = keyword("usage:", catch->help->data))) {
 353        struct double_list *cfrom, *tfrom, *anchor;
 354        char *try, **cdashlines, **tdashlines, *usage;
 355        int clen, tlen;
 356
 357        // Align usage: lines, finding a matching pair so we can suck help
 358        // text out of throw into catch, copying from this to that
 359        if (!throw) usage = that;
 360        else if (strncmp(name, that, len) || !isspace(that[len])) continue;
 361        catch->enabled++;
 362        while (!isspace(*that) && *that) that++;
 363        if (!throw) len = that-usage;
 364        free(name);
 365        name = strndup(usage, len);
 366        that = skip_spaces(that);
 367        if (!throw) {
 368          throw = catch;
 369          this = that;
 370
 371          continue;
 372        }
 373
 374        // Grab option description lines to collate from catch and throw
 375        tusage = dlist_zap(&throw->help);
 376        tdashlines = grab_dashlines(&throw->help, &tfrom, &tlen);
 377        cusage = dlist_zap(&catch->help);
 378        cdashlines = grab_dashlines(&catch->help, &cfrom, &clen);
 379        anchor = catch->help;
 380
 381        // If we've got both, collate and alphebetize
 382        if (cdashlines && tdashlines) {
 383          char **new = xmalloc(sizeof(char *)*(clen+tlen));
 384
 385          memcpy(new, cdashlines, sizeof(char *)*clen);
 386          memcpy(new+clen, tdashlines, sizeof(char *)*tlen);
 387          free(cdashlines);
 388          free(tdashlines);
 389          qsort(new, clen+tlen, sizeof(char *), (void *)dashlinesort);
 390          cdashlines = new;
 391
 392        // If just one, make sure it's in catch.
 393        } else if (tdashlines) cdashlines = tdashlines;
 394
 395        // If throw had a prefix, insert it before dashlines, with a
 396        // blank line if catch had a prefix.
 397        if (tfrom && tfrom != throw->help) {
 398          if (throw->help || catch->help) dlist_add(&cfrom, strdup(""));
 399          else {
 400            dlist_add(&cfrom, 0);
 401            anchor = cfrom->prev;
 402          }
 403          while (throw->help && throw->help != tfrom)
 404            dlist_add(&cfrom, dlist_zap(&throw->help));
 405          if (cfrom && cfrom->prev->data && *skip_spaces(cfrom->prev->data))
 406            dlist_add(&cfrom, strdup(""));
 407        }
 408        if (!anchor) {
 409          dlist_add(&cfrom, 0);
 410          anchor = cfrom->prev;
 411        }
 412
 413        // Splice sorted lines back in place
 414        if (cdashlines) {
 415          tlen += clen;
 416
 417          for (clen = 0; clen < tlen; clen++) 
 418            dlist_add(&cfrom, cdashlines[clen]);
 419        }
 420
 421        // If there were no dashlines, text would be considered prefix, so
 422        // the list is definitely no longer empty, so discard placeholder.
 423        if (!anchor->data) dlist_zap(&anchor);
 424
 425        // zap whitespace at end of catch help text
 426        while (!*skip_spaces(anchor->prev->data)) {
 427          anchor = anchor->prev;
 428          free(dlist_zap(&anchor));
 429        }
 430
 431        // Append trailing lines.
 432        while (tfrom) dlist_add(&anchor, dlist_zap(&tfrom));
 433
 434        // Collate first [-abc] option block in usage: lines
 435        try = 0;
 436        if (*this == '[' && this[1] == '-' && this[2] != '-' &&
 437            *that == '[' && that[1] == '-' && that[2] != '-')
 438        {
 439          char *from = this+2, *to = that+2;
 440          int ff = strcspn(from, " ]"), tt = strcspn(to, " ]");
 441
 442          if (from[ff] == ']' && to[tt] == ']') {
 443            try = xmprintf("[-%.*s%.*s] ", ff, from, tt, to);
 444            qsort(try+2, ff+tt, 1, (void *)charsort);
 445            this = skip_spaces(this+ff+3);
 446            that = skip_spaces(that+tt+3);
 447          }
 448        }
 449
 450        // The list is definitely no longer empty, so discard placeholder.
 451        if (!anchor->data) dlist_zap(&anchor);
 452
 453        // Add new collated line (and whitespace).
 454        dlist_add(&anchor, xmprintf("%*cusage: %.*s %s%s%s%s",
 455                  catch->help_indent, ' ', len, name, try ? try : "",
 456                  this, *this ? " " : "", that));
 457        free(try);
 458        dlist_add(&anchor, strdup(""));
 459        free(cusage);
 460        free(tusage);
 461        throw->enabled = 0;
 462        throw = catch;
 463        throw->help = anchor->prev->prev;
 464
 465        throw = catch;
 466        this = throw->help->data + throw->help_indent + 8 + len;
 467      }
 468    }
 469
 470    // Did we find one?
 471
 472    if (!throw) break;
 473  }
 474
 475  // Stage 3: output results to stdout.
 476
 477  // Print out help #defines
 478  while (sym) {
 479    struct double_list *dd;
 480
 481    if (sym->help) {
 482      int i;
 483      char *s;
 484
 485      strcpy(s = xmalloc(strlen(sym->name)+1), sym->name);
 486
 487      for (i = 0; s[i]; i++) s[i] = tolower(s[i]);
 488      printf("#define HELP_%s \"", s);
 489      free(s);
 490
 491      dd = sym->help;
 492      for (;;) {
 493        i = sym->help_indent;
 494
 495        // Trim leading whitespace
 496        s = dd->data;
 497        while (isspace(*s) && i) {
 498          s++;
 499          i--;
 500        }
 501        for (i=0; s[i]; i++) {
 502          if (s[i] == '"' || s[i] == '\\') putchar('\\');
 503          putchar(s[i]);
 504        }
 505        putchar('\\');
 506        putchar('n');
 507        dd = dd->next;
 508        if (dd == sym->help) break;
 509      }
 510      printf("\"\n\n");
 511    }
 512    sym = sym->next;
 513  }
 514
 515  return 0;
 516}
 517