From d3e3654f75d01376794676885589a911d4b12e41 Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Mon, 1 Mar 2021 23:28:16 -0500 Subject: [PATCH] Use gperf to implement command selection. Instead of iterating through a static a table of commands, let's use gperf and our new string table generator to do this a bit more efficiently. --- Makefile.am | 42 +++++++- common | 2 +- po/en.po | 97 ++++++++++++------ src/.gitignore | 3 + src/cdecl99.c | 241 +++++--------------------------------------- src/cdecl99.h | 31 ++++++ src/commands.c | 155 ++++++++++++++++++++++++++++ src/commands.str | 6 ++ src/execute.gperf | 115 +++++++++++++++++++++ src/gen-cmdlist.awk | 71 +++++++++++++ 10 files changed, 511 insertions(+), 252 deletions(-) create mode 100644 src/cdecl99.h create mode 100644 src/commands.c create mode 100644 src/commands.str create mode 100644 src/execute.gperf create mode 100755 src/gen-cmdlist.awk diff --git a/Makefile.am b/Makefile.am index 95f50cf..ce76be1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -19,7 +19,7 @@ AM_CPPFLAGS = -I$(top_builddir)/src -I$(top_srcdir)/src \ AM_CFLAGS = $(GSL_CFLAGS) MAINTAINERCLEANFILES = src/scan.c src/scan.h src/scan.stamp \ - src/parse.c src/parse.h src/parse.stamp + src/parse.c src/parse.h src/parse.stamp DISTCLEANFILES = @@ -51,12 +51,22 @@ libcdecl_la_LIBADD = libgnu.la $(LTLIBINTL) $(LTLIBTHREAD) $(libcdecl_la_OBJECTS): $(gnulib_headers) bin_PROGRAMS = cdecl99 -cdecl99_SOURCES = src/cdecl99.c src/options.h -cdecl99_LDADD = libcdecl.la libgnu.la \ - $(LTLIBICONV) $(LTLIBINTL) $(LTLIBREADLINE) +cdecl99_SOURCES = src/commands.c src/cdecl99.h +cdecl99_LDADD = $(libmain_a_OBJECTS) $(libexec_a_OBJECTS) libcdecl.la \ + libgnu.la $(LTLIBICONV) $(LTLIBINTL) $(LTLIBREADLINE) $(cdecl99_OBJECTS): $(gnulib_headers) $(cdecl99_OBJECTS): src/options.h +EXTRA_LIBRARIES = libexec.a +libexec_a_SOURCES = src/execute.c src/commands.h +$(libexec_a_OBJECTS): $(gnulib_headers) +$(libexec_a_OBJECTS): src/commands.h src/cmdlist.h + +EXTRA_LIBRARIES += libmain.a +libmain_a_SOURCES = src/cdecl99.c src/options.h +$(libmain_a_OBJECTS): $(gnulib_headers) +$(libmain_a_OBJECTS): src/options.h + check_PROGRAMS = test/crossparse check_LTLIBRARIES = libtest.la libtest_la_LIBADD = $(GSL_LIBS) @@ -192,6 +202,23 @@ $(OPTFILES:.opt=.h): $(DX_BASEDIR)/scripts/gen-options.awk MAINTAINERCLEANFILES += $(OPTFILES:.opt=.h) EXTRA_DIST += $(DX_BASEDIR)/scripts/gen-options.awk $(OPTFILES) +STRFILES = src/commands.str +.str.h: + $(AM_V_GEN) $(AWK) -f $(DX_BASEDIR)/scripts/gen-strtab.awk $< >$@.tmp + $(AM_V_at) mv -f $@.tmp $@ +$(STRFILES:.str=.h): $(DX_BASEDIR)/scripts/gen-strtab.awk +MAINTAINERCLEANFILES += $(STRFILES:.str=.h) +EXTRA_DIST += $(DX_BASEDIR)/scripts/gen-strtab.awk $(STRFILES) + +src/cmdlist.h: src/gen-cmdlist.awk src/execute.c + $(AM_V_GEN) if test -f '$(builddir)/src/execute.c'; \ + then f='$(builddir)/src/execute.c'; \ + else f='$(srcdir)/src/execute.c'; \ + fi; $(AWK) -f $(srcdir)/src/gen-cmdlist.awk "$$f" >$@.tmp + $(AM_V_at) mv -f $@.tmp $@ +DISTCLEANFILES += src/cmdlist.h +EXTRA_DIST += src/gen-cmdlist.awk + # Supporting rules for bison/flex. BISON_V = $(BISON_V_@AM_V@) @@ -250,6 +277,13 @@ endif $(MAKE) $(AM_MAKEFLAGS) $<; \ fi +GPERFFILES = src/execute.gperf +.gperf.c: + $(V_GPERF) $(GPERF) $< >$@.tmp + $(AM_V_at) mv $@.tmp $@ +MAINTAINERCLEANFILES += $(GPERFFILES:.gperf=.c) +EXTRA_DIST += $(GPERFFILES) + atlocal: config.status $(AM_V_GEN) :; { \ printf ': "$${%s=%s}"\n' \ diff --git a/common b/common index 7ba459d..1688bad 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 7ba459d508d5e3c08580e8ec6c3dbc7c1fedd289 +Subproject commit 1688bad1e5dc89cacf33bc426c92a4abf2bc0647 diff --git a/po/en.po b/po/en.po index 700d57a..3f2f384 100644 --- a/po/en.po +++ b/po/en.po @@ -1,13 +1,13 @@ # English translations for cdecl99 package. # This file is put in the public domain. -# Nick Bowler , 2011. +# Nick Bowler , 2021. # msgid "" msgstr "" "Project-Id-Version: cdecl99 0.1\n" "Report-Msgid-Bugs-To: nbowler@draconx.ca\n" -"POT-Creation-Date: 2021-02-26 00:33-0500\n" -"PO-Revision-Date: 2011-09-13 21:54-0400\n" +"POT-Creation-Date: 2021-03-01 23:36-0500\n" +"PO-Revision-Date: 2021-03-01 23:41-0500\n" "Last-Translator: Nick Bowler \n" "Language-Team: English\n" "Language: en\n" @@ -16,39 +16,37 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: lib/getopt.c:278 +#: src/execute.gperf:97 #, c-format -msgid "%s: option '%s%s' is ambiguous\n" +msgid "unknown command %.*s\n" msgstr "" -#: lib/getopt.c:284 -#, c-format -msgid "%s: option '%s%s' is ambiguous; possibilities:" +#: src/execute.gperf:101 +msgid "Try \"help\" for a list of possible commands." +msgstr "Try “help” for a list of possible commands." + +#: src/commands.h:12 +msgid "Print this list of commands" msgstr "" -#: lib/getopt.c:319 -#, c-format -msgid "%s: unrecognized option '%s%s'\n" +#: src/commands.h:13 +msgid "Construct a C declaration" msgstr "" -#: lib/getopt.c:345 -#, c-format -msgid "%s: option '%s%s' doesn't allow an argument\n" +#: src/commands.h:14 +msgid "Simplify a C declaration" msgstr "" -#: lib/getopt.c:360 -#, c-format -msgid "%s: option '%s%s' requires an argument\n" +#: src/commands.h:15 +msgid "Construct a C type name" msgstr "" -#: lib/getopt.c:621 -#, c-format -msgid "%s: invalid option -- '%c'\n" +#: src/commands.h:16 +msgid "Explain a C declaration" msgstr "" -#: lib/getopt.c:636 lib/getopt.c:682 -#, c-format -msgid "%s: option requires an argument -- '%c'\n" +#: src/commands.h:17 +msgid "Quit the program" msgstr "" #: src/cdecl99.c:71 @@ -71,7 +69,7 @@ msgstr "" msgid " -%c, --%s" msgstr "" -#: src/cdecl99.c:123 +#: src/cdecl99.c:142 msgid "" "This is \"cdecl99\": a command-line tool for parsing and constructing\n" "complicated C declarations." @@ -79,27 +77,23 @@ msgstr "" "This is “cdecl99”: a command-line tool for parsing and constructing\n" "complicated C declarations." -#: src/cdecl99.c:127 +#: src/cdecl99.c:146 msgid "Options:" msgstr "" -#: src/cdecl99.c:154 +#: src/cdecl99.c:164 msgid "For more information, see the cdecl99(1) man page." msgstr "" #. TRANSLATORS: Please add *another line* indicating where users should #. report translation bugs. #. -#: src/cdecl99.c:161 +#: src/cdecl99.c:171 #, c-format msgid "Report bugs to <%s>.\n" msgstr "" -#: src/cdecl99.c:186 src/errors.lst:1 -msgid "failed to allocate memory" -msgstr "" - -#: src/cdecl99.c:490 +#: src/cdecl99.c:301 #, c-format msgid "%s: excess command-line arguments:" msgstr "" @@ -153,6 +147,45 @@ msgctxt "help" msgid "Print this message and then exit." msgstr "" +#: lib/getopt.c:278 +#, c-format +msgid "%s: option '%s%s' is ambiguous\n" +msgstr "" + +#: lib/getopt.c:284 +#, c-format +msgid "%s: option '%s%s' is ambiguous; possibilities:" +msgstr "" + +#: lib/getopt.c:319 +#, c-format +msgid "%s: unrecognized option '%s%s'\n" +msgstr "" + +#: lib/getopt.c:345 +#, c-format +msgid "%s: option '%s%s' doesn't allow an argument\n" +msgstr "" + +#: lib/getopt.c:360 +#, c-format +msgid "%s: option '%s%s' requires an argument\n" +msgstr "" + +#: lib/getopt.c:621 +#, c-format +msgid "%s: invalid option -- '%c'\n" +msgstr "" + +#: lib/getopt.c:636 lib/getopt.c:682 +#, c-format +msgid "%s: option requires an argument -- '%c'\n" +msgstr "" + +#: src/commands.c:48 src/errors.lst:1 +msgid "failed to allocate memory" +msgstr "" + #: src/errors.lst:2 msgid "syntax error" msgstr "" diff --git a/src/.gitignore b/src/.gitignore index e8d48de..9428cc9 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -4,5 +4,8 @@ /errtab.h /namespecs.h /options.h +/commands.h /ordspecs.h /validtypes.h +/execute.c +/cmdlist.h diff --git a/src/cdecl99.c b/src/cdecl99.c index 518dbbd..18c6a5d 100644 --- a/src/cdecl99.c +++ b/src/cdecl99.c @@ -35,7 +35,7 @@ #include #include -#define _(x) gettext(x) +#include "cdecl99.h" static const char *progname = "cdecl99"; @@ -114,6 +114,25 @@ out: return w; } +/* + * Print a string, with each line indented by i spaces. The first line + * will be indented by w fewer spaces (to account for the cursor being in + * some other column). + */ +void print_block(const char *s, int i, int w) +{ + for (; *s; w = 0) { + const char *nl = strchr(s, '\n'); + int n = (nl ? nl-s : -1); + + printf("%*s%.*s\n", i-w, "", n, s); + if (!nl) + break; + + s = nl+1; + } +} + static void print_help(void) { const struct option *opt; @@ -138,16 +157,7 @@ static void print_help(void) if (ENABLE_NLS) help.desc = pgettext_expr(opt->name, help.desc); - for (line = help.desc; *line; w = 0) { - const char *nl = strchr(line, '\n'); - int n = (nl ? nl-line : -1); - - printf("%*s%.*s\n", 20-w, "", n, line); - if (!nl) - break; - - line = nl+1; - } + print_block(help.desc, 20, w); } putchar('\n'); @@ -161,205 +171,6 @@ static void print_help(void) printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT); } -/* - * Format a declaration according to the given function and return a pointer - * to the formatted string. The returned pointer remains valid until the - * next call, after which it must not be re-used. - * - * Returns NULL on failure. - */ -static const char * -do_format(size_t func(char *, size_t, struct cdecl *), struct cdecl *decl) -{ - static size_t bufsz; - static char *buf; - - size_t rc; - -retry: - rc = func(buf, bufsz, decl); - if (rc >= bufsz) { - char *tmp; - - tmp = realloc(buf, rc + 1); - if (!tmp) { - fprintf(stderr, "%s\n", _("failed to allocate memory")); - return NULL; - } - - buf = tmp; - bufsz = rc + 1; - goto retry; - } - - return buf; -} - -static int cmd_explain(const char *cmd, const char *arg) -{ - const struct cdecl_error *err; - struct cdecl *decl; - const char *str; - int ret = -1; - - decl = cdecl_parse_decl(arg); - if (!decl) { - err = cdecl_get_error(); - fprintf(stderr, "%s\n", err->str); - goto out; - } - - for (struct cdecl *i = decl; i; i = i->next) { - str = do_format(cdecl_explain, i); - if (!str) - goto out; - - printf("%s\n", str); - } - - ret = 1; -out: - cdecl_free(decl); - return ret; -} - -static int cmd_simplify(const char *cmd, const char *arg) -{ - const struct cdecl_error *err; - struct cdecl *decl; - const char *str; - int ret = -1; - - decl = cdecl_parse_decl(arg); - if (!decl) { - err = cdecl_get_error(); - fprintf(stderr, "%s\n", err->str); - goto out; - } - - for (struct cdecl *i = decl; i; i = i->next) { - struct cdecl_declspec *s = i->specifiers; - - if (i != decl) { - i->specifiers = NULL; - printf(", "); - } - - str = do_format(cdecl_declare, i); - i->specifiers = s; - - if (!str) - goto out; - - printf("%s", str); - } - - putchar('\n'); - - ret = 1; -out: - cdecl_free(decl); - return ret; -} - -static int cmd_declare(const char *cmd, const char *arg) -{ - const struct cdecl_error *err; - struct cdecl *decl; - const char *str; - int ret = -1; - - /* The name of the command is significant here. */ - decl = cdecl_parse_english(cmd); - if (!decl) { - err = cdecl_get_error(); - fprintf(stderr, "%s\n", err->str); - goto out; - } - - /* - * English parses have at most one full declarator, so no loop is - * needed here. - */ - str = do_format(cdecl_declare, decl); - if (!str) - goto out; - - printf("%s\n", str); - ret = 1; -out: - cdecl_free(decl); - return ret; -} - -static int cmd_quit(const char *cmd, const char *arg) -{ - return 0; -} - -static int cmd_help(const char *cmd, const char *arg); - -static const struct command { - char name[16]; - int (*func)(const char *cmd, const char *arg); - const char *blurb; -} commands[] = { - { "explain", cmd_explain, "Explain a C declaration." }, - { "simplify", cmd_simplify, "Simplify a C declaration." }, - { "declare", cmd_declare, "Construct a C declaration." }, - { "type", cmd_declare, "Construct a C type name." }, - { "help", cmd_help, "Print this list of commands." }, - { "quit", cmd_quit, "Quit the program." }, - { "exit", cmd_quit, NULL } -}; -static const size_t ncommands = sizeof commands / sizeof commands[0]; - -static int cmd_help(const char *cmd, const char *arg) -{ - for (size_t i = 0; i < ncommands; i++) { - if (!commands[i].blurb) - continue; - - printf("%s -- %s\n", commands[i].name, commands[i].blurb); - } - - return 1; -} - -/* - * Ensure that the first n characters of str equal the entire (null-terminated) - * string cmd. - */ -static int cmd_cmp(const char *str, const char *cmd, size_t n) -{ - size_t cmdlen = strlen(cmd); - - if (n < cmdlen) - return -1; - if (n > cmdlen) - return 1; - return memcmp(str, cmd, n); -} - -static int run_command(const char *line) -{ - const char *cmd = line + strspn(line, " \t"); - const char *arg = cmd + strcspn(cmd, " \t"); - - if (cmd[0] == '\0') - return 1; - - for (size_t i = 0; i < ncommands; i++) { - if (cmd_cmp(cmd, commands[i].name, arg-cmd) != 0) - continue; - - return commands[i].func(cmd, arg); - } - - fprintf(stderr, "Undefined command: %.*s\n", (int)(arg-cmd), cmd); - return -1; -} - static bool is_blank_line(const char *line) { for (size_t i = 0; line[i]; i++) { @@ -378,7 +189,7 @@ static int repl(void) if (!is_blank_line(line)) cdecl_add_history(line); - if (!run_command(line)) + if (run_command(line, true) > 0) break; } @@ -395,10 +206,10 @@ static int repl_cmdline(int argc, char **argv) if (opt != 'e') continue; - rc = run_command(optarg); + rc = run_command(optarg, false); if (rc < 0) ret = -1; - else if (rc == 0) + else if (rc > 0) break; } @@ -416,10 +227,10 @@ static int repl_noninteractive(void) if (c) *c = '\0'; - rc = run_command(line); + rc = run_command(line, false); if (rc < 0) ret = -1; - else if (rc == 0) + else if (rc > 0) break; } diff --git a/src/cdecl99.h b/src/cdecl99.h new file mode 100644 index 0000000..6eb1b6c --- /dev/null +++ b/src/cdecl99.h @@ -0,0 +1,31 @@ +/* + * Declarations for the "cdecl99" utility sources. + * Copyright © 2021 Nick Bowler + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef CDECL99_H_ +#define CDECL99_H_ + +#include +#define _(x) gettext(x) + +void print_block(const char *s, int i, int w); +int run_command(const char *line, int interactive); +int run_command_simplify(const char *arg); +int run_command_explain(const char *arg); +int run_command_declare(const char *cmdarg); + +#endif diff --git a/src/commands.c b/src/commands.c new file mode 100644 index 0000000..d435f50 --- /dev/null +++ b/src/commands.c @@ -0,0 +1,155 @@ +/* + * Main command implementation routines for cdecl99. + * Copyright © 2011-2012, 2020-2021 Nick Bowler + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "cdecl99.h" +#include "cdecl.h" + +/* + * Format a declaration according to the given function and return a pointer + * to the formatted string. The returned pointer remains valid until the + * next call, after which it must not be re-used. + * + * Returns NULL on failure. + */ +static const char * +do_format(size_t func(char *, size_t, struct cdecl *), struct cdecl *decl) +{ + static size_t bufsz; + static char *buf; + + size_t rc; + +retry: + rc = func(buf, bufsz, decl); + if (rc >= bufsz) { + char *tmp; + + tmp = realloc(buf, rc + 1); + if (!tmp) { + fprintf(stderr, "%s\n", _("failed to allocate memory")); + return NULL; + } + + buf = tmp; + bufsz = rc + 1; + goto retry; + } + + return buf; +} + +int run_command_explain(const char *arg) +{ + const struct cdecl_error *err; + struct cdecl *decl; + const char *str; + int ret = -1; + + decl = cdecl_parse_decl(arg); + if (!decl) { + err = cdecl_get_error(); + fprintf(stderr, "%s\n", err->str); + goto out; + } + + for (struct cdecl *i = decl; i; i = i->next) { + str = do_format(cdecl_explain, i); + if (!str) + goto out; + + printf("%s\n", str); + } + + ret = 0; +out: + cdecl_free(decl); + return ret; +} + +int run_command_simplify(const char *arg) +{ + const struct cdecl_error *err; + struct cdecl *decl; + const char *str; + int ret = -1; + + decl = cdecl_parse_decl(arg); + if (!decl) { + err = cdecl_get_error(); + fprintf(stderr, "%s\n", err->str); + goto out; + } + + for (struct cdecl *i = decl; i; i = i->next) { + struct cdecl_declspec *s = i->specifiers; + + if (i != decl) { + i->specifiers = NULL; + printf(", "); + } + + str = do_format(cdecl_declare, i); + i->specifiers = s; + + if (!str) + goto out; + + printf("%s", str); + } + + putchar('\n'); + + ret = 0; +out: + cdecl_free(decl); + return ret; +} + +int run_command_declare(const char *cmd) +{ + const struct cdecl_error *err; + struct cdecl *decl; + const char *str; + int ret = -1; + + /* The name of the command is significant here. */ + decl = cdecl_parse_english(cmd); + if (!decl) { + err = cdecl_get_error(); + fprintf(stderr, "%s\n", err->str); + goto out; + } + + /* + * English parses have at most one full declarator, so no loop is + * needed here. + */ + str = do_format(cdecl_declare, decl); + if (!str) + goto out; + + printf("%s\n", str); + ret = 0; +out: + cdecl_free(decl); + return ret; +} diff --git a/src/commands.str b/src/commands.str new file mode 100644 index 0000000..f2c6e32 --- /dev/null +++ b/src/commands.str @@ -0,0 +1,6 @@ +&cmd_explain Explain a C declaration +&cmd_simplify Simplify a C declaration +&cmd_declare Construct a C declaration +&cmd_type Construct a C type name +&cmd_help Print this list of commands +&cmd_quit Quit the program diff --git a/src/execute.gperf b/src/execute.gperf new file mode 100644 index 0000000..e8a0665 --- /dev/null +++ b/src/execute.gperf @@ -0,0 +1,115 @@ +%{ +/* + * Copyright © 2021 Nick Bowler + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "cdecl99.h" +#include "commands.h" + +typedef +#if STRTAB_MAX_OFFSET < UINT_LEAST8_MAX +uint_least8_t +#elif STRTAB_MAX_OFFSET < UINT_LEAST16_MAX +uint_least16_t +#else +#error do not know what type to use +#endif +cmd_index_type; + +static const struct command *in_word_set(); +%} + +%struct-type +%compare-strncmp +%readonly-tables +%language=ANSI-C +%global-table +%pic + +struct command { + int_least8_t name; + cmd_index_type cmd; +}; +%% +explain, cmd_explain +simplify, cmd_simplify +declare, cmd_declare +type, cmd_type +help, cmd_help +quit, cmd_quit +exit, cmd_quit +%% +#include "cmdlist.h" + +static int run_cmd_help(void) +{ + static const unsigned char offsets[] = CMD_SEQ; + unsigned i; + + printf("Commands:\n"); + for (i = 0; i < sizeof offsets / sizeof offsets[0]; i++) { + const struct command *c = &wordlist[offsets[i]]; + int w; + + w = printf(" %s", stringpool+c->name); + if (w < 0 || w > 13) { + putchar('\n'); + 0; + } + + print_block(gettext(strtab+c->cmd), 15, w); + } + + return 1; +} + +int run_command(const char *line, int interactive) +{ + const char *cmd = line + strspn(line, " \t"); + const char *arg = cmd + strcspn(cmd, " \t"); + const struct command *c; + + /* empty command */ + if (cmd[0] == '\0') + return 1; + + c = in_word_set(cmd, arg-cmd); + if (!c) { + fprintf(stderr, _("unknown command %.*s\n"), + (int)(arg-cmd), cmd); + if (interactive) { + fprintf(stderr, "%s\n", + _("Try \"help\" for a list of possible commands.")); + } + return -1; + } + + switch (c->cmd) { + case cmd_help: return run_cmd_help(); + case cmd_declare: case cmd_type: return run_command_declare(cmd); + case cmd_simplify: return run_command_simplify(arg); + case cmd_explain: return run_command_explain(arg); + case cmd_quit: return 1; + } + + assert(0); +} diff --git a/src/gen-cmdlist.awk b/src/gen-cmdlist.awk new file mode 100755 index 0000000..ea096eb --- /dev/null +++ b/src/gen-cmdlist.awk @@ -0,0 +1,71 @@ +#!/bin/awk -f +# +# Copyright © 2021 Nick Bowler +# +# Hackjob to try and find all the relevant wordlist items from the gperf +# output, in order to produce (at runtime) a list of those commands, in +# the same order as they are listed in the gperf input file. +# +# The output is a definition of the object-like macro CMD_SEQ, which +# can be used to initialize an array with static storage duration. +# Each element of the array represents offsets into the wordlist table, +# in sequence. +# +# License WTFPL2: Do What The Fuck You Want To Public License, version 2. +# This is free software: you are free to do what the fuck you want to. +# There is NO WARRANTY, to the extent permitted by law. + +END { + print "/*" + if (FILENAME) { + print " * Automatically generated by gen-cmdlist.awk from " FILENAME + } else { + print " * Automatically generated by gen-cmdlist.awk" + } + print " * Do not edit." + print " */" +} + +BEGIN { maxline = 0 } + +$1 == "char" && (id = get_stringpool_id($2)) { + sub(/[^"]*"/, "", $2) + sub(/".*/, "", $2) + pool[id] = $2 +} + +$1 ~ /^#line/ { + line = $2 + if (line > maxline) { + maxline = line + } +} + +(id = get_stringpool_id($0)) && $0 ~ "cmd_" pool[id] { + sub(/^stringpool_str/, "", id) + indices[line] = id +} + +END { + seq = "" + for (i = 1; i <= maxline; i++) { + if (i in indices) { + if (seq) { + seq = seq ", " indices[i] + } else { + seq = indices[i] + } + } + } + + print "#define CMD_SEQ { " seq " }" +} + +function get_stringpool_id(s) +{ + if (sub(/.*stringpool_str/, "stringpool_str", s) && sub(/[,[].*/, "", s)) { + return s + } + + return "" +} -- 2.43.0