123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523 |
- /* config2.help.c - config2hep Config.in .config > help.h
- function parse() reads Config.in data into *sym list, then
- we read .config and set sym->try on each enabled symbol.
- */
- #include <ctype.h>
- #include <stdio.h>
- #include <string.h>
- #include <stdarg.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #include <regex.h>
- #include <inttypes.h>
- #include <termios.h>
- #include <poll.h>
- #include <sys/socket.h>
- //****************** functions copied from lib/*.c ********************
- struct double_list {
- struct double_list *next, *prev;
- char *data;
- };
- // Die unless we can allocate memory.
- void *xmalloc(size_t size)
- {
- void *ret = malloc(size);
- if (!ret) {
- fprintf(stderr, "xmalloc(%ld)", (long)size);
- exit(1);
- }
- return ret;
- }
- // Die unless we can allocate enough space to sprintf() into.
- char *xmprintf(char *format, ...)
- {
- va_list va, va2;
- int len;
- char *ret;
- va_start(va, format);
- va_copy(va2, va);
- // How long is it?
- len = vsnprintf(0, 0, format, va);
- len++;
- va_end(va);
- // Allocate and do the sprintf()
- ret = xmalloc(len);
- vsnprintf(ret, len, format, va2);
- va_end(va2);
- return ret;
- }
- // Die unless we can open/create a file, returning FILE *.
- FILE *xfopen(char *path, char *mode)
- {
- FILE *f = fopen(path, mode);
- if (!f) {
- fprintf(stderr, "No file %s", path);
- exit(1);
- }
- return f;
- }
- void *dlist_pop(void *list)
- {
- struct double_list **pdlist = (struct double_list **)list, *dlist = *pdlist;
- if (dlist->next == dlist) *pdlist = 0;
- else {
- dlist->next->prev = dlist->prev;
- dlist->prev->next = *pdlist = dlist->next;
- }
- return dlist;
- }
- void dlist_add_nomalloc(struct double_list **list, struct double_list *new)
- {
- if (*list) {
- new->next = *list;
- new->prev = (*list)->prev;
- (*list)->prev->next = new;
- (*list)->prev = new;
- } else *list = new->next = new->prev = new;
- }
- // Add an entry to the end of a doubly linked list
- struct double_list *dlist_add(struct double_list **list, char *data)
- {
- struct double_list *new = xmalloc(sizeof(struct double_list));
- new->data = data;
- dlist_add_nomalloc(list, new);
- return new;
- }
- //****************** end copies of lib/*.c *************
- // Parse config files into data structures.
- struct symbol {
- struct symbol *next;
- int enabled, help_indent;
- char *name, *depends;
- struct double_list *help;
- } *sym;
- // remove leading spaces
- char *skip_spaces(char *s)
- {
- while (isspace(*s)) s++;
- return s;
- }
- // if line starts with name (as whole word) return pointer after it, else NULL
- char *keyword(char *name, char *line)
- {
- int len = strlen(name);
- line = skip_spaces(line);
- if (strncmp(name, line, len)) return 0;
- line += len;
- if (*line && !isspace(*line)) return 0;
- line = skip_spaces(line);
- return line;
- }
- // dlist_pop() freeing wrapper structure for you.
- char *dlist_zap(struct double_list **help)
- {
- struct double_list *dd = dlist_pop(help);
- char *s = dd->data;
- free(dd);
-
- return s;
- }
- int zap_blank_lines(struct double_list **help)
- {
- int got = 0;
- while (*help) {
- char *s;
- s = skip_spaces((*help)->data);
- if (*s) break;
- got++;
- free(dlist_zap(help));
- }
- return got;
- }
- // Collect "-a blah" description lines following a blank line (or start).
- // Returns array of removed lines with *len entries (0 for none).
- // Moves *help to new start of text (in case dash lines were at beginning).
- // Sets *from to where dash lines removed from (in case they weren't).
- // Discards blank lines before and after dashlines.
- // If no prefix, *help NULL. If no postfix, *from == *help
- // if no dashlines returned *from == *help.
- char **grab_dashlines(struct double_list **help, struct double_list **from,
- int *len)
- {
- struct double_list *dd;
- char *s, **list;
- int count = 0;
- *len = 0;
- zap_blank_lines(help);
- *from = *help;
- // Find start of dash block. Must be at start or after blank line.
- for (;;) {
- s = skip_spaces((*from)->data);
- if (*s == '-' && s[1] != '-' && !count) break;
- if (!*s) count = 0;
- else count++;
- *from = (*from)->next;
- if (*from == *help) return 0;
- }
- // If there was whitespace before this, zap it. This can't take out *help
- // because zap_blank_lines skipped blank lines, and we had to have at least
- // one non-blank line (a dash line) to get this far.
- while (!*skip_spaces((*from)->prev->data)) {
- *from = (*from)->prev;
- free(dlist_zap(from));
- }
- // Count number of dashlines, copy out to array, zap trailing whitespace
- // If *help was at start of dashblock, move it with *from
- count = 0;
- dd = *from;
- if (*help == *from) *help = 0;
- for (;;) {
- if (*skip_spaces(dd->data) != '-') break;
- count++;
- if (*from == (dd = dd->next)) break;
- }
- list = xmalloc(sizeof(char *)*count);
- *len = count;
- while (count) list[--count] = dlist_zap(from);
- return list;
- }
- // Read Config.in (and includes) to populate global struct symbol *sym list.
- void parse(char *filename)
- {
- FILE *fp = xfopen(filename, "r");
- struct symbol *new = 0;
- for (;;) {
- char *s, *line = NULL;
- size_t len;
- // Read line, trim whitespace at right edge.
- if (getline(&line, &len, fp) < 1) break;
- s = line+strlen(line);
- while (--s >= line) {
- if (!isspace(*s)) break;
- *s = 0;
- }
- // source or config keyword at left edge?
- if (*line && !isspace(*line)) {
- if ((s = keyword("config", line))) {
- memset(new = xmalloc(sizeof(struct symbol)), 0, sizeof(struct symbol));
- new->next = sym;
- new->name = s;
- sym = new;
- } else if ((s = keyword("source", line))) parse(s);
- continue;
- }
- if (!new) continue;
- if (sym && sym->help_indent) {
- dlist_add(&(new->help), line);
- if (sym->help_indent < 0) {
- sym->help_indent = 0;
- while (isspace(line[sym->help_indent])) sym->help_indent++;
- }
- }
- else if ((s = keyword("depends", line)) && (s = keyword("on", s)))
- new->depends = s;
- else if (keyword("help", line)) sym->help_indent = -1;
- }
- fclose(fp);
- }
- int charsort(void *a, void *b)
- {
- char *aa = a, *bb = b;
- if (*aa < *bb) return -1;
- if (*aa > *bb) return 1;
- return 0;
- }
- int dashsort(char **a, char **b)
- {
- char *aa = *a, *bb = *b;
- if (aa[1] < bb[1]) return -1;
- if (aa[1] > bb[1]) return 1;
- return 0;
- }
- int dashlinesort(char **a, char **b)
- {
- return strcmp(*a, *b);
- }
- // Three stages: read data, collate entries, output results.
- int main(int argc, char *argv[])
- {
- FILE *fp;
- if (argc != 3) {
- fprintf(stderr, "usage: config2help Config.in .config\n");
- exit(1);
- }
- // Stage 1: read data. Read Config.in to global 'struct symbol *sym' list,
- // then read .config to set "enabled" member of each enabled symbol.
- // Read Config.in
- parse(argv[1]);
- // read .config
- fp = xfopen(argv[2], "r");
- for (;;) {
- char *line = NULL;
- size_t len;
- if (getline(&line, &len, fp) < 1) break;
- if (!strncmp("CONFIG_", line, 7)) {
- struct symbol *try;
- char *s = line+7;
- for (try=sym; try; try=try->next) {
- len = strlen(try->name);
- if (!strncmp(try->name, s, len) && s[len]=='=' && s[len+1]=='y') {
- try->enabled++;
- break;
- }
- }
- }
- }
- // Stage 2: process data.
- // Collate help according to usage, depends, and .config
- // Loop through each entry, finding duplicate enabled "usage:" names
- // This is in reverse order, so last entry gets collated with previous
- // entry until we run out of matching pairs.
- for (;;) {
- struct symbol *throw = 0, *catch;
- char *this, *that, *cusage, *tusage, *name = 0;
- int len;
- // find a usage: name and collate all enabled entries with that name
- for (catch = sym; catch; catch = catch->next) {
- if (catch->enabled != 1) continue;
- if (catch->help && (that = keyword("usage:", catch->help->data))) {
- struct double_list *cfrom, *tfrom, *anchor;
- char *try, **cdashlines, **tdashlines, *usage;
- int clen, tlen;
- // Align usage: lines, finding a matching pair so we can suck help
- // text out of throw into catch, copying from this to that
- if (!throw) usage = that;
- else if (strncmp(name, that, len) || !isspace(that[len])) continue;
- catch->enabled++;
- while (!isspace(*that) && *that) that++;
- if (!throw) len = that-usage;
- free(name);
- name = strndup(usage, len);
- that = skip_spaces(that);
- if (!throw) {
- throw = catch;
- this = that;
- continue;
- }
- // Grab option description lines to collate from catch and throw
- tusage = dlist_zap(&throw->help);
- tdashlines = grab_dashlines(&throw->help, &tfrom, &tlen);
- cusage = dlist_zap(&catch->help);
- cdashlines = grab_dashlines(&catch->help, &cfrom, &clen);
- anchor = catch->help;
- // If we've got both, collate and alphebetize
- if (cdashlines && tdashlines) {
- char **new = xmalloc(sizeof(char *)*(clen+tlen));
- memcpy(new, cdashlines, sizeof(char *)*clen);
- memcpy(new+clen, tdashlines, sizeof(char *)*tlen);
- free(cdashlines);
- free(tdashlines);
- qsort(new, clen+tlen, sizeof(char *), (void *)dashlinesort);
- cdashlines = new;
- // If just one, make sure it's in catch.
- } else if (tdashlines) cdashlines = tdashlines;
- // If throw had a prefix, insert it before dashlines, with a
- // blank line if catch had a prefix.
- if (tfrom && tfrom != throw->help) {
- if (throw->help || catch->help) dlist_add(&cfrom, strdup(""));
- else {
- dlist_add(&cfrom, 0);
- anchor = cfrom->prev;
- }
- while (throw->help && throw->help != tfrom)
- dlist_add(&cfrom, dlist_zap(&throw->help));
- if (cfrom && cfrom->prev->data && *skip_spaces(cfrom->prev->data))
- dlist_add(&cfrom, strdup(""));
- }
- if (!anchor) {
- dlist_add(&cfrom, 0);
- anchor = cfrom->prev;
- }
- // Splice sorted lines back in place
- if (cdashlines) {
- tlen += clen;
- for (clen = 0; clen < tlen; clen++)
- dlist_add(&cfrom, cdashlines[clen]);
- }
- // If there were no dashlines, text would be considered prefix, so
- // the list is definitely no longer empty, so discard placeholder.
- if (!anchor->data) dlist_zap(&anchor);
- // zap whitespace at end of catch help text
- while (!*skip_spaces(anchor->prev->data)) {
- anchor = anchor->prev;
- free(dlist_zap(&anchor));
- }
- // Append trailing lines.
- while (tfrom) dlist_add(&anchor, dlist_zap(&tfrom));
- // Collate first [-abc] option block in usage: lines
- try = 0;
- if (*this == '[' && this[1] == '-' && this[2] != '-' &&
- *that == '[' && that[1] == '-' && that[2] != '-')
- {
- char *from = this+2, *to = that+2;
- int ff = strcspn(from, " ]"), tt = strcspn(to, " ]");
- if (from[ff] == ']' && to[tt] == ']') {
- try = xmprintf("[-%.*s%.*s] ", ff, from, tt, to);
- qsort(try+2, ff+tt, 1, (void *)charsort);
- this = skip_spaces(this+ff+3);
- that = skip_spaces(that+tt+3);
- }
- }
- // The list is definitely no longer empty, so discard placeholder.
- if (!anchor->data) dlist_zap(&anchor);
- // Add new collated line (and whitespace).
- dlist_add(&anchor, xmprintf("%*cusage: %.*s %s%s%s%s",
- catch->help_indent, ' ', len, name, try ? try : "",
- this, *this ? " " : "", that));
- free(try);
- dlist_add(&anchor, strdup(""));
- free(cusage);
- free(tusage);
- throw->enabled = 0;
- throw = catch;
- throw->help = anchor->prev->prev;
- throw = catch;
- this = throw->help->data + throw->help_indent + 8 + len;
- }
- }
- // Did we find one?
- if (!throw) break;
- }
- // Stage 3: output results to stdout.
- // Print out help #defines
- while (sym) {
- struct double_list *dd;
- if (sym->help) {
- int i, blank;
- char *s;
- strcpy(s = xmalloc(strlen(sym->name)+1), sym->name);
- for (i = 0; s[i]; i++) s[i] = tolower(s[i]);
- printf("#define HELP_%s \"", s);
- free(s);
- dd = sym->help;
- blank = 0;
- for (;;) {
- // Trim leading whitespace
- s = dd->data;
- i = sym->help_indent;
- while (isspace(*s) && i--) s++;
- // Only one blank line between nonblank lines, not at start or end.
- if (!*s) blank = 2;
- else {
- while (blank--) {
- putchar('\\');
- putchar('n');
- }
- blank = 1;
- }
- for (i=0; s[i]; i++) {
- if (s[i] == '"' || s[i] == '\\') putchar('\\');
- putchar(s[i]);
- }
- dd = dd->next;
- if (dd == sym->help) break;
- }
- printf("\"\n\n");
- }
- sym = sym->next;
- }
- return 0;
- }
|