From 185722d30ae7b1eb3f8bcdc6ddc69414c35e15fa Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Wed, 9 Apr 2025 10:04:36 -0500 Subject: [PATCH] Start of new kconfig plumbing, for now just replacing old config2help. --- scripts/config2help.c | 523 ------------------------------------------ scripts/kconfig.c | 184 +++++++++++++++ scripts/make.sh | 7 +- 3 files changed, 186 insertions(+), 528 deletions(-) delete mode 100644 scripts/config2help.c create mode 100644 scripts/kconfig.c diff --git a/scripts/config2help.c b/scripts/config2help.c deleted file mode 100644 index 0eff7087..00000000 --- a/scripts/config2help.c +++ /dev/null @@ -1,523 +0,0 @@ -/* 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//****************** 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; -} diff --git a/scripts/kconfig.c b/scripts/kconfig.c new file mode 100644 index 00000000..0614fb01 --- /dev/null +++ b/scripts/kconfig.c @@ -0,0 +1,184 @@ +#include +#include +#include +#include + +/* +mainmenu, source, comment +config + bool "string" + int "string" + default y/n/SYMBOL + depends [on] SYMBOL + help +menu/choice + prompt "string" + default, help + [config...] +endmenu/endchoice + +# bool without a string is invisible, no prompt just help text +*/ + +// Data we're collecting about each entry +struct kconfig { + struct kconfig *next; + char *symbol, *value, *type, *prompt, *def, *depend, *help; +}; + +// Skip/remove leading space, quotes, and escapes within quotes +char *trim(char *s) +{ + int len, in, out; + + while (isspace(*s)) s++; + len = strlen(s); + if (*s=='\"' && s[len-1]=='\"') { + s[--len]=0; + s++; + for (in = out = 0; in=line;) { + if (!isspace(*s)) break; + *s = 0; + } + + // Append help text? + s = line; + if (help) { + if (!kc) { fp = 0; break; } + for (ii = 0; *s==' ' || *s=='\t'; s++) { + if (*s=='\t') ii |= 7; + ii++; + if (*help && hindenthelp = help; + help = 0; + } else { + help = realloc(help, jj+strlen(s)+2); + if (jj) help[jj++]='\n'; + strcpy(help+jj, s); + + continue; + } + } + + // What's the keyword? + while (isspace(*s)) s++; + if (!*s || *s=='#') continue; + ss = s; + while (*s && !isspace(*s)) s++; + ss = strndup(ss, s-ss); + + // Include other file + if (!strcmp(ss, "source")) { + struct kconfig *kt; + + if (!(kt = walter(trim(s)))) { fp = 0; break; } + if (klist) kc = (kc->next) = kt; + else klist = kc = kt; + while (kc->next) kc = kc->next; + // start help block + } else if (!strcmp(ss, "help")) help = strdup(""); + // start a new config entry? + else if (strany(ss, (char *[]){"mainmenu", "menu", "comment", "config", + "menu", "choice", "endmenu", "endchoice", 0})) + { + struct kconfig *kt = calloc(sizeof(struct kconfig), 1); + + if (klist) kc = (kc->next = kt); + else klist = kc = kt; + if (!strcmp(ss, "config")) { + if (!*s) { fp = 0; break; } + else kt->symbol = strdup(trim(s)); + } else if (*s) kt->prompt = strdup(trim(s)); + bump(&kt->type, &ss); + } else if (!kc) { fp = 0; break; } + else if (!strcmp(ss, "default")) bump(&kc->def, &ss); + else if (strany(ss, (char *[]){"bool", "int", "prompt", 0})) { + if (*ss!='p') bump(&kc->type, &ss); + ss = strdup(trim(s)); + bump(&kc->prompt, &ss); + } else if (!strcmp(ss, "depends")) { + while (isspace(*s)) s++; + if (s[0]=='o' && s[1]=='n' && isspace(s[2])) s += 3; + ss = strdup(trim(s)); + bump(&kc->depend, &ss); + } else { dprintf(2, "%d %s\n", count, line); fp = 0; break; } + + free(ss); + } + + if (!fp) dprintf(2, "%s[%d]: bad %s\n", name, count, line ? : ""), exit(1); + fclose(fp); + + return klist; +} + +int main(int argc, char *argv[]) +{ + struct kconfig *kc = walter("Config.in"), *kk; + char *ss, *tt, *esc = "\n\\\""; + + if (argc==2 && !strcmp(argv[1], "-h")) for (kk = kc; kk; kk = kk->next) { + if (!kk->symbol || !kk->help) continue; + printf("#define HELP_"); + for (ss = kk->symbol; *ss; ss++) putchar(tolower(*ss)); + printf(" \""); + for (ss = kk->help; *ss; ss++) + if (!(tt = strchr(esc, *ss))) putchar(*ss); + else printf("\\%c", "n\\\""[tt-esc]); + printf("\"\n\n"); + } + + return 0; +} diff --git a/scripts/make.sh b/scripts/make.sh index 3f4b3070..ea2b0daa 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -226,11 +226,8 @@ while read i; do done > "$GENDIR"/tags.h || exit 1 # Create help.h, and zhelp.h if zcat enabled -hostcomp config2help -if isnewer help.h "$GENDIR"/Config.in -then - "$UNSTRIPPED"/config2help Config.in $KCONFIG_CONFIG > "$GENDIR"/help.h||exit 1 -fi +hostcomp kconfig +"$UNSTRIPPED"/kconfig -h > "$GENDIR"/help.h || exit 1 if grep -qx 'CONFIG_TOYBOX_ZHELP=y' "$KCONFIG_CONFIG" then -- 2.39.5