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