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