From 207cadacd3cef42fa918981423c951f49443f032 Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Thu, 3 Oct 2013 03:18:00 -0500 Subject: Switch flag generation from shell to C. This should actually generate FLAG_longopt 0 #defines for disabled bare longopts (ala ls without --color). Put temporary executables under "generated" (including instlist for install). --- Makefile | 15 ++++--- scripts/install.sh | 4 +- scripts/make.sh | 95 +++++++++++++++++---------------------- scripts/mkflags.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++ toys.h | 1 + 5 files changed, 181 insertions(+), 63 deletions(-) create mode 100644 scripts/mkflags.c diff --git a/Makefile b/Makefile index 7b583ed0..688926bf 100644 --- a/Makefile +++ b/Makefile @@ -25,16 +25,16 @@ baseline: toybox_unstripped bloatcheck: toybox_old toybox_unstripped @scripts/bloatcheck toybox_old toybox_unstripped -instlist: toybox - $(HOSTCC) -I . scripts/install.c -o instlist +generated/instlist: toybox + $(HOSTCC) -I . scripts/install.c -o generated/instlist -install_flat: instlist +install_flat: generated/instlist scripts/install.sh --symlink --force install: scripts/install.sh --long --symlink --force -uninstall_flat: instlist +uninstall_flat: generated/instlist scripts/install.sh --uninstall uninstall: @@ -42,9 +42,10 @@ uninstall: clean:: rm -rf toybox toybox_unstripped generated/config.h generated/Config.in \ - generated/newtoys.h generated/globals.h instlist testdir \ - generated/Config.probed generated/oldtoys.h \ - generated/portability.h .singleconfig + generated/newtoys.h generated/globals.h testdir \ + generated/Config.probed generated/oldtoys.h generated/flags.h \ + generated/portability.h .singleconfig generated/instlist \ + generated/mkflags distclean: clean rm -f toybox_old .config* generated/help.h diff --git a/scripts/install.sh b/scripts/install.sh index d9b56567..8891a314 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -35,8 +35,8 @@ done echo "Compile instlist..." -$DEBUG $HOSTCC -I . scripts/install.c -o instlist || exit 1 -COMMANDS="$(./instlist $LONG_PATH)" +$DEBUG $HOSTCC -I . scripts/install.c -o generated/instlist || exit 1 +COMMANDS="$(generated/instlist $LONG_PATH)" echo "Install commands..." diff --git a/scripts/make.sh b/scripts/make.sh index f5c4c273..61f2acfa 100755 --- a/scripts/make.sh +++ b/scripts/make.sh @@ -59,70 +59,57 @@ sed -n -e 's/^USE_[A-Z0-9_]*(/&/p' toys/*/*.c \ sed -n -e 's/.*(NEWTOY(\([^,]*\), *\(\("[^"]*"[^,]*\)*\),.*/#define OPTSTR_\1\t\2/p' \ generated/newtoys.h > generated/oldtoys.h -# Extract list of command letters from processed header file +if [ ! -e generated/mkflags ] +then + $HOSTCC scripts/mkflags.c -o generated/mkflags || exit 1 +fi -function getflags() -{ - FLX="$1" - shift - sed -n -e "s/.*TOY($FLX"',[ \t]*"\([^"]*\)"[ \t]*,.*)/\1/' \ - -e 't keep;d;:keep' -e 's/^[<>=][0-9]//' -e 's/[?&^]//' \ - -e 't keep' -e 's/[><=][0-9][0-9]*//g' -e 's/+.//g' \ - -e 's/\[[^]]*\]//g' -e 's/[-?^:&#|@* ;]//g' "$@" -e 'p' -} +echo generated/flags.h + +# Parse files through C preprocessor twice, once to get flags for current +# .config and once to get flags for allyesconfig +for I in A B +do + ( + # define macros and select header files with option string data + + echo "#define NEWTOY(aa,bb,cc) aa $I bb" + echo '#define OLDTOY(...)' + if [ "$I" == A ] + then + cat generated/config.h + else + sed '/USE_.*([^)]*)$/s/$/ __VA_ARGS__/' generated/config.h + fi + cat generated/newtoys.h + + # Run result through preprocessor, glue together " " gaps leftover from USE + # macros, delete comment lines, print any line with a quoted optstring, + # turn any non-quoted opstring (NULL or 0) into " " (because fscanf can't + # handle "" with nothing in it). + + ) | $CC -E - | \ + sed -n -e 's/" *"//g;/^#/d;s/"/"/p' -e 's/ *$//;s/ [^" ]*$/ " "/p' + +# Sort resulting line pairs and glue them together into triplets of +# command "flags" "allflags" +# to feed into mkflags C program that outputs actual flag macros + +done | sort | sed -n 's/ A / /;t skip;d;:skip;h;n;s/[^ ]* B //;H;g;s/\n/ /;p' |\ +generated/mkflags > generated/flags.h || exit 1 # Extract global structure definitions and flag definitions from toys/*/*.c function getglobals() { - # Run newtoys.h through the compiler's preprocessor to resolve USE macros - # against current config. - NEWTOYS="$(cat generated/config.h generated/newtoys.h | $CC -E - | sed 's/" *"//g')" - - # Grab allyesconfig for comparison - ALLTOYS="$((sed '/USE_.*([^)]*)$/s/$/ __VA_ARGS__/' generated/config.h && cat generated/newtoys.h) | $CC -E - | sed 's/" *"//g')" - for i in toys/*/*.c do NAME="$(echo $i | sed 's@.*/\(.*\)\.c@\1@')" + DATA="$(sed -n -e '/^GLOBALS(/,/^)/b got;b;:got' \ + -e 's/^GLOBALS(/struct '"$NAME"'_data {/' \ + -e 's/^)/};/' -e 'p' $i)" - echo -e "// $i\n" - sed -n -e '/^GLOBALS(/,/^)/b got;b;:got' \ - -e 's/^GLOBALS(/struct '"$NAME"'_data {/' \ - -e 's/^)/};/' -e 'p' $i - - LONGFLAGS="$(echo "$NEWTOYS" | getflags "$NAME" -e 's/\(\(([^)]*)\)*\).*/\1/' -e 's/(//g' -e 's/)/ /g')" - FLAGS="$(echo "$NEWTOYS" | getflags "$NAME" -e 's/([^)]*)//g')" - ZFLAGS="$(echo "$ALLTOYS" | getflags "$NAME" -e 's/([^)]*)//g' -e 's/[-'"$FLAGS"']//g')" - LONGFLAGLEN="$(echo "$LONGFLAGS" | wc -w)" - - echo "#ifdef FOR_${NAME}" - X=0 - # Provide values for --longopts with no corresponding short flags - for i in $LONGFLAGS - do - X=$(($X+1)) - echo -e "#define FLAG_$i\t(1<<$(($LONGFLAGLEN+${#FLAGS}-$X)))" - done - - # Provide values for active flags - X=0 - while [ $X -lt ${#FLAGS} ] - do - echo -ne "#define FLAG_${FLAGS:$X:1}\t" - X=$(($X+1)) - echo "(1<<$((${#FLAGS}-$X)))" - done - - # Provide zeroes for inactive flags - X=0 - while [ $X -lt ${#ZFLAGS} ] - do - echo "#define FLAG_${ZFLAGS:$X:1} 0" - X=$(($X+1)) - done - echo "#define TT this.${NAME}" - echo "#endif" + [ ! -z "$DATA" ] && echo -e "// $i\n\n$DATA\n" done } diff --git a/scripts/mkflags.c b/scripts/mkflags.c new file mode 100644 index 00000000..eca59603 --- /dev/null +++ b/scripts/mkflags.c @@ -0,0 +1,129 @@ +// Take three word input lines on stdin (the three space separated words are +// command name, option string with current config, option string from +// allyesconfig; space separated, the last two are and double quotes) +// and produce flag #defines to stdout. + +// This is intentionally crappy code because we control the inputs. It leaks +// memory like a sieve and segfaults if malloc returns null, but does the job. + +#include +#include +#include +#include + +struct flag { + struct flag *next; + char *command; + struct flag *lopt; +}; + +// Break down a command string into struct flag list. + +struct flag *digest(char *string) +{ + struct flag *list = NULL; + + while (*string) { + // Groups must be at end. + if (*string == '[') break; + + // Longopts + if (*string == '(') { + struct flag *new = calloc(sizeof(struct flag), 1); + + new->command = ++string; + + // Attach longopt to previous short opt, if any. + if (list && list->command) { + new->next = list->lopt; + list->lopt = new; + } else { + struct flag *blank = calloc(sizeof(struct flag), 1); + + blank->next = list; + blank->lopt = new; + list = blank; + } + while (*++string != ')'); // An empty longopt () would break this. + *(string++) = 0; + continue; + } + + if (strchr("?&^-:#|@*; ", *string)) string++; + else if (strchr("=<>", *string)) { + while (isdigit(*++string)) { + if (!list) { + string++; + break; + } + } + } else { + struct flag *new = calloc(sizeof(struct flag), 1); + + new->command = string++; + new->next = list; + list = new; + } + } + + return list; +} + +int main(int argc, char *argv[]) +{ + char command[256], flags[1023], allflags[1024]; + unsigned bit; + + for (;;) { + struct flag *flist, *aflist, *offlist; + unsigned bit = 0; + + if (3 != fscanf(stdin, "%255s \"%1023[^\"]\" \"%1023[^\"]\"\n", + command, flags, allflags)) break; + + printf("// %s %s %s\n", command, flags, allflags); + flist = digest(flags); + offlist = aflist = digest(allflags); + + + printf("#ifdef CLEANUP_%s\n#undef CLEANUP_%s\n#undef FOR_%s\n#undef TT\n", + command, command, command); + + while (offlist) { + struct flag *f = offlist->lopt; + while (f) { + printf("#undef FLAG_%s\n", f->command); + f = f->next; + } + if (offlist->command) printf("#undef FLAG_%c\n", *offlist->command); + offlist = offlist->next; + } + printf("#endif\n\n"); + + printf("#ifdef FOR_%s\n#define TT this.%s\n", command, command); + + while (aflist) { + if (aflist->lopt) { + if (flist && flist->lopt && + !strcmp(flist->lopt->command, aflist->lopt->command)) + { + printf("#define FLAG_%s (1<<%d)\n", flist->lopt->command, bit); + flist->lopt = flist->lopt->next; + } else printf("#define FLAG_%s 0\n", aflist->lopt->command); + aflist->lopt = aflist->lopt->next; + } else { + if (flist && (!aflist->command || *aflist->command == *flist->command)) + { + if (aflist->command) + printf("#define FLAG_%c (1<<%d)\n", *aflist->command, bit); + bit++; + flist = flist->next; + } else printf("#define FLAG_%c 0\n", *aflist->command); + aflist = aflist->next; + } + } + printf("#endif\n\n"); + } + + return fflush(0) && ferror(stdout); +} diff --git a/toys.h b/toys.h index b7acc270..caf96123 100644 --- a/toys.h +++ b/toys.h @@ -64,6 +64,7 @@ #define OLDTOY(name, oldname, opts, flags) #include "generated/newtoys.h" #include "generated/oldtoys.h" +#include "generated/flags.h" #include "generated/globals.h" // These live in main.c -- cgit v1.2.3