From e0179c069958050100a637bb876c96495276ea76 Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Fri, 26 Feb 2021 00:37:17 -0500 Subject: [PATCH] Use the newly-minted option generator script from dxcommon. Instead of maintaining the relation between long options and help text dircetly in C code, let's use this new script to generate C code from a simple description file. This ditches support for translated option names. I'm a bit unsure about the practical use of this functionality as I do not personally use it and locale-specific interpretation of command-line arguments just seems like it would cause more problems than it would solve. --- Makefile.am | 13 ++- common | 2 +- m4/.gitignore | 15 ++++ m4/gnulib-cache.m4 | 2 + po/en.po | 197 ++++++++++++++++----------------------------- src/.gitignore | 16 ++-- src/cdecl99.c | 193 +++++++++++++++----------------------------- src/options.opt | 22 +++++ 8 files changed, 193 insertions(+), 267 deletions(-) create mode 100644 src/options.opt diff --git a/Makefile.am b/Makefile.am index 620b37b..95f50cf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,7 +26,7 @@ DISTCLEANFILES = CLEANFILES = src/validtypes.h src/errtab.h src/namespecs.h src/ordspecs.h \ test/typegen.h $(EXTRA_LTLIBRARIES) -EXTRA_DIST = bootstrap common/scripts/fix-gnulib.pl m4/gnulib-cache.m4 \ +EXTRA_DIST = bootstrap $(DX_BASEDIR)/scripts/fix-gnulib.pl m4/gnulib-cache.m4 \ src/types.lst src/validtypes.sed src/specs.lst src/namespecs.sed \ src/ordspecs.sed src/errors.lst src/strtab.sed test/typegen.sh \ src/parse.y src/parse.stamp src/scan.l src/scan.stamp \ @@ -51,10 +51,11 @@ libcdecl_la_LIBADD = libgnu.la $(LTLIBINTL) $(LTLIBTHREAD) $(libcdecl_la_OBJECTS): $(gnulib_headers) bin_PROGRAMS = cdecl99 -cdecl99_SOURCES = src/cdecl99.c +cdecl99_SOURCES = src/cdecl99.c src/options.h cdecl99_LDADD = libcdecl.la libgnu.la \ $(LTLIBICONV) $(LTLIBINTL) $(LTLIBREADLINE) $(cdecl99_OBJECTS): $(gnulib_headers) +$(cdecl99_OBJECTS): src/options.h check_PROGRAMS = test/crossparse check_LTLIBRARIES = libtest.la @@ -183,6 +184,14 @@ distcheck-hook: echo ' *** programs are required for ChangeLog generation to work.'; \ $(TEST_DISTRIBUTION_OR_ERROR); } >&2 +OPTFILES = src/options.opt +.opt.h: + $(AM_V_GEN) $(AWK) -f $(DX_BASEDIR)/scripts/gen-options.awk $< >$@.tmp + $(AM_V_at) mv -f $@.tmp $@ +$(OPTFILES:.opt=.h): $(DX_BASEDIR)/scripts/gen-options.awk +MAINTAINERCLEANFILES += $(OPTFILES:.opt=.h) +EXTRA_DIST += $(DX_BASEDIR)/scripts/gen-options.awk $(OPTFILES) + # Supporting rules for bison/flex. BISON_V = $(BISON_V_@AM_V@) diff --git a/common b/common index 3a7418b..7ba459d 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 3a7418b75a6a5e2bc2a65264e68112ab2b63228b +Subproject commit 7ba459d508d5e3c08580e8ec6c3dbc7c1fedd289 diff --git a/m4/.gitignore b/m4/.gitignore index 33f5b1e..e43c15a 100644 --- a/m4/.gitignore +++ b/m4/.gitignore @@ -19,6 +19,7 @@ /inline.m4 /intl-thread-locale.m4 /intlmacosx.m4 +/inttypes.m4 /lib-ld.m4 /lib-link.m4 /lib-prefix.m4 @@ -26,11 +27,19 @@ /libunistring-base.m4 /limits-h.m4 /localcharset.m4 +/locale-fr.m4 +/locale-ja.m4 +/locale-zh.m4 +/locale_h.m4 /lock.m4 /ltoptions.m4 /ltsugar.m4 /ltversion.m4 /lt~obsolete.m4 +/mbrtowc.m4 +/mbsinit.m4 +/mbstate_t.m4 +/mbswidth.m4 /multiarch.m4 /nls.m4 /nocrash.m4 @@ -40,7 +49,9 @@ /progtest.m4 /pthread_rwlock_rdlock.m4 /readline.m4 +/setlocale_null.m4 /ssize_t.m4 +/std-gnu11.m4 /stdbool.m4 /stddef_h.m4 /stdint.m4 @@ -49,7 +60,11 @@ /threadlib.m4 /tls.m4 /unistd_h.m4 +/visibility.m4 /warn-on-use.m4 +/wchar_h.m4 /wchar_t.m4 +/wctype_h.m4 +/wcwidth.m4 /wint_t.m4 /zzgnulib.m4 diff --git a/m4/gnulib-cache.m4 b/m4/gnulib-cache.m4 index 5223b7d..8d5104d 100644 --- a/m4/gnulib-cache.m4 +++ b/m4/gnulib-cache.m4 @@ -44,6 +44,7 @@ # gitlog-to-changelog \ # localcharset \ # lock \ +# mbswidth \ # readline \ # striconv \ # tls @@ -56,6 +57,7 @@ gl_MODULES([ gitlog-to-changelog localcharset lock + mbswidth readline striconv tls diff --git a/po/en.po b/po/en.po index 42dc6a0..700d57a 100644 --- a/po/en.po +++ b/po/en.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: cdecl99 0.1\n" "Report-Msgid-Bugs-To: nbowler@draconx.ca\n" -"POT-Creation-Date: 2020-06-29 22:52-0400\n" +"POT-Creation-Date: 2021-02-26 00:33-0500\n" "PO-Revision-Date: 2011-09-13 21:54-0400\n" "Last-Translator: Nick Bowler \n" "Language-Team: English\n" @@ -16,196 +16,141 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: lib/getopt.c:547 lib/getopt.c:576 +#: lib/getopt.c:278 #, c-format -msgid "%s: option '%s' is ambiguous; possibilities:" +msgid "%s: option '%s%s' is ambiguous\n" msgstr "" -#: lib/getopt.c:624 lib/getopt.c:628 +#: lib/getopt.c:284 #, c-format -msgid "%s: option '--%s' doesn't allow an argument\n" +msgid "%s: option '%s%s' is ambiguous; possibilities:" msgstr "" -#: lib/getopt.c:637 lib/getopt.c:642 +#: lib/getopt.c:319 #, c-format -msgid "%s: option '%c%s' doesn't allow an argument\n" +msgid "%s: unrecognized option '%s%s'\n" msgstr "" -#: lib/getopt.c:685 lib/getopt.c:704 +#: lib/getopt.c:345 #, c-format -msgid "%s: option '--%s' requires an argument\n" +msgid "%s: option '%s%s' doesn't allow an argument\n" msgstr "" -#: lib/getopt.c:742 lib/getopt.c:745 +#: lib/getopt.c:360 #, c-format -msgid "%s: unrecognized option '--%s'\n" +msgid "%s: option '%s%s' requires an argument\n" msgstr "" -#: lib/getopt.c:753 lib/getopt.c:756 -#, c-format -msgid "%s: unrecognized option '%c%s'\n" -msgstr "" - -#: lib/getopt.c:805 lib/getopt.c:808 +#: lib/getopt.c:621 #, c-format msgid "%s: invalid option -- '%c'\n" msgstr "" -#: lib/getopt.c:861 lib/getopt.c:878 lib/getopt.c:1088 lib/getopt.c:1106 +#: lib/getopt.c:636 lib/getopt.c:682 #, c-format msgid "%s: option requires an argument -- '%c'\n" msgstr "" -#: lib/getopt.c:934 lib/getopt.c:950 +#: src/cdecl99.c:71 #, c-format -msgid "%s: option '-W %s' is ambiguous\n" +msgid "Usage: %s [options]\n" msgstr "" -#: lib/getopt.c:974 lib/getopt.c:992 +#: src/cdecl99.c:73 #, c-format -msgid "%s: option '-W %s' doesn't allow an argument\n" +msgid "Try %s --help for more information.\n" msgstr "" -#: lib/getopt.c:1013 lib/getopt.c:1031 +#: src/cdecl99.c:88 #, c-format -msgid "%s: option '-W %s' requires an argument\n" -msgstr "" - -#: src/cdecl99.c:43 -msgctxt "longopt" -msgid "quiet" -msgstr "" - -#: src/cdecl99.c:44 -msgctxt "longopt" -msgid "batch" -msgstr "" - -#: src/cdecl99.c:45 -msgctxt "longopt" -msgid "interactive" +msgid " -%c, --%s=%s" msgstr "" -#: src/cdecl99.c:46 -msgctxt "longopt" -msgid "file" +#: src/cdecl99.c:92 +#, c-format +msgid " -%c, --%s" msgstr "" -#: src/cdecl99.c:47 -msgctxt "longopt" -msgid "execute" +#: src/cdecl99.c:123 +msgid "" +"This is \"cdecl99\": a command-line tool for parsing and constructing\n" +"complicated C declarations." msgstr "" +"This is “cdecl99”: a command-line tool for parsing and constructing\n" +"complicated C declarations." -#: src/cdecl99.c:48 -msgctxt "longopt" -msgid "version" +#: src/cdecl99.c:127 +msgid "Options:" msgstr "" -#: src/cdecl99.c:49 -msgctxt "longopt" -msgid "help" +#: src/cdecl99.c:154 +msgid "For more information, see the cdecl99(1) man page." msgstr "" -#. TRANSLATORS: Help messages are indented 20 spaces and thus should -#. not have lines longer than 60 columns. +#. TRANSLATORS: Please add *another line* indicating where users should +#. report translation bugs. #. -#: src/cdecl99.c:67 -msgid "Suppress the welcome message.\n" -msgstr "" - -#: src/cdecl99.c:68 -msgid "Execute commands as normal, but do not print any prompts.\n" -msgstr "" - -#: src/cdecl99.c:69 -msgid "Run in interactive mode. This is the default.\n" -msgstr "" - -#: src/cdecl99.c:70 -msgid "Read commands from FILE instead of standard input.\n" +#: src/cdecl99.c:161 +#, c-format +msgid "Report bugs to <%s>.\n" msgstr "" -#: src/cdecl99.c:71 -msgctxt "longopt|file" -msgid "FILE" +#: src/cdecl99.c:186 src/errors.lst:1 +msgid "failed to allocate memory" msgstr "" -#: src/cdecl99.c:72 -msgid "" -"Execute COMMAND as if it were entered at the prompt.\n" -"This can be specified multiple times.\n" +#: src/cdecl99.c:490 +#, c-format +msgid "%s: excess command-line arguments:" msgstr "" -#: src/cdecl99.c:74 -msgctxt "longopt|execute" +#: src/options.h:52 +msgctxt "execute" msgid "COMMAND" msgstr "" -#: src/cdecl99.c:75 -msgid "Print a version message and then exit.\n" -msgstr "" - -#: src/cdecl99.c:76 -msgid "Print this message.\n" -msgstr "" - -#. TRANSLATORS: ARG is only used for options without help text. -#: src/cdecl99.c:79 -msgctxt "longopt" -msgid "ARG" +#: src/options.h:53 +msgctxt "file" +msgid "FILE" msgstr "" -#: src/cdecl99.c:147 -#, c-format -msgid " -%c" +#: src/options.h:57 +msgctxt "quiet" +msgid "Suppress the welcome message." msgstr "" -#: src/cdecl99.c:150 -#, c-format -msgid " -%c, --%s=%s" +#: src/options.h:59 +msgctxt "batch" +msgid "" +"Run in batch mode. Commands are executed as normal but\n" +"prompts are not printed." msgstr "" -#: src/cdecl99.c:151 -#, c-format -msgid " -%c, --%s" +#: src/options.h:62 +msgctxt "interactive" +msgid "Run in interactive mode. This is the default." msgstr "" -#: src/cdecl99.c:156 -#, c-format -msgid " -%c, --%s=%s, --%s=%s" +#: src/options.h:64 +msgctxt "file" +msgid "Read commands from FILE instead of standard input." msgstr "" -#: src/cdecl99.c:160 -#, c-format -msgid " -%c, --%s, --%s" -msgstr "" - -#: src/cdecl99.c:196 +#: src/options.h:66 +msgctxt "execute" msgid "" -"This is \"cdecl99\": a command-line tool for parsing and constructing\n" -"complicated C declarations.\n" -msgstr "" -"This is “cdecl99”: a command-line tool for parsing and constructing\n" -"complicated C declarations.\n" - -#: src/cdecl99.c:199 -msgid "Options:" -msgstr "" - -#: src/cdecl99.c:208 -msgid "For more information, see the cdecl99(1) man page.\n" +"Execute COMMAND as if it were entered at the prompt.\n" +"This option can be specified multiple times." msgstr "" -#. TRANSLATORS: Please add *another line* indicating where users should -#. report translation bugs. -#. -#: src/cdecl99.c:214 -#, c-format -msgid "Report bugs to <%s>.\n" +#: src/options.h:69 +msgctxt "version" +msgid "Print a version message and then exit." msgstr "" -#: src/errors.lst:1 -msgid "failed to allocate memory" +#: src/options.h:71 +msgctxt "help" +msgid "Print this message and then exit." msgstr "" #: src/errors.lst:2 diff --git a/src/.gitignore b/src/.gitignore index faa646e..e8d48de 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,8 +1,8 @@ -parse.stamp -parse.[ch] -scan.stamp -scan.[ch] -validtypes.h -namespecs.h -ordspecs.h -errtab.h +/*.stamp +/parse.[ch] +/scan.[ch] +/errtab.h +/namespecs.h +/options.h +/ordspecs.h +/validtypes.h diff --git a/src/cdecl99.c b/src/cdecl99.c index 5941009..518dbbd 100644 --- a/src/cdecl99.c +++ b/src/cdecl99.c @@ -33,51 +33,17 @@ #include #include #include +#include #define _(x) gettext(x) -#define N_(x) x -#define PN_(c, x) x static const char *progname = "cdecl99"; -static const char sopts[] = "qbif:e:VH"; -#define RAW_LOPTS \ - { PN_("longopt", "quiet"), 0, NULL, 'q' }, \ - { PN_("longopt", "batch"), 0, NULL, 'b' }, \ - { PN_("longopt", "interactive"), 0, NULL, 'i' }, \ - { PN_("longopt", "file"), 1, NULL, 'f' }, \ - { PN_("longopt", "execute"), 1, NULL, 'e' }, \ - { PN_("longopt", "version"), 0, NULL, 'V' }, \ - { PN_("longopt", "help"), 0, NULL, 'H' } -/* - * With NLS, we need a buffer big enough to store the translated options. - * The translations will be filled in at program startup. - */ -enum { NOPTS = sizeof (struct option[]){RAW_LOPTS} / sizeof (struct option) }; -static struct option lopts[NOPTS + !!ENABLE_NLS * NOPTS + 1] = { RAW_LOPTS }; - -static struct helptext { - int val; - const char *text; - const char *argname; -} helptext[] = { - /* - * TRANSLATORS: Help messages are indented 20 spaces and thus should - * not have lines longer than 60 columns. - */ - { 'q', N_("Suppress the welcome message.\n") }, - { 'b', N_("Execute commands as normal, but do not print any prompts.\n") }, - { 'i', N_("Run in interactive mode. This is the default.\n") }, - { 'f', N_("Read commands from FILE instead of standard input.\n"), - PN_("longopt|file", "FILE") }, - { 'e', N_("Execute COMMAND as if it were entered at the prompt.\n" - "This can be specified multiple times.\n"), - PN_("longopt|execute", "COMMAND") }, - { 'V', N_("Print a version message and then exit.\n") }, - { 'H', N_("Print this message.\n") }, - - /* TRANSLATORS: ARG is only used for options without help text. */ - { 0, NULL, PN_("longopt", "ARG") } +#include "options.h" +static const char sopts[] = SOPT_STRING; +static const struct option lopts[] = { + LOPTS_INITIALIZER, + {0} }; static void print_version(void) @@ -102,114 +68,91 @@ static void print_version(void) static void print_usage(FILE *f) { - fprintf(f, "Usage: %s [options]\n", progname); + fprintf(f, _("Usage: %s [options]\n"), progname); if (f != stdout) fprintf(f, _("Try %s --help for more information.\n"), progname); } -/* - * Print the help description of a particular command-line option, identified - * by its short option name. - */ -static void print_option(int val) +static int +print_optstring(const struct option *opt, const struct lopt_help *help) { - const struct option *tmp[2]; - const struct helptext *help; - const char *argname; - char context[32]; - size_t rc, n = 0; + char optstring[100]; int w; - /* Find the options and help text corresponding to val. */ - for (const struct option *o = lopts; o->val; o++) { - if (o->val == val) { - assert(n < sizeof tmp / sizeof tmp[0]); - tmp[n++] = o; - } - } - - for (help = helptext; help->val; help++) { - if (help->val == val) - break; + if (!ENABLE_NLS) + goto no_translate; + + if (opt->has_arg) { + w = snprintf(optstring, sizeof optstring, + _(" -%c, --%s=%s"), opt->val, opt->name, + pgettext_expr(opt->name, help->arg)); + } else { + w = snprintf(optstring, sizeof optstring, + _(" -%c, --%s"), opt->val, opt->name); } - /* Prepare translations. */ - if (!ENABLE_NLS) { - argname = help->argname; - } else if (n > 0) { - rc = snprintf(context, sizeof context, "longopt%c%s", - help->text ? '|' : '\0', tmp[0]->name); - assert(rc < sizeof context); + if (w < 0) + goto no_translate; - if (help->argname) - argname = pgettext_expr(context, help->argname); - } + w = mbsnwidth(optstring, w, 0); + printf("%s", optstring); + goto out; - switch (n) { - case 0: - w = printf(_(" -%c"), val); - break; - case 1: - w = printf(tmp[0]->has_arg ? _(" -%c, --%s=%s") - : _(" -%c, --%s"), - val, tmp[0]->name, argname); - break; - case 2: - if (tmp[0]->has_arg) { - w = printf(_(" -%c, --%s=%s, --%s=%s"), val, - tmp[0]->name, argname, - tmp[1]->name, argname); - } else { - w = printf(_(" -%c, --%s, --%s"), val, - tmp[0]->name, tmp[1]->name); - } - break; - default: - assert(0); +no_translate: + if (opt->has_arg) { + w = printf(" -%c, --%s=%s", opt->val, opt->name, help->arg); + } else { + w = printf(" -%c, --%s", opt->val, opt->name); } - - if (!help->text) { - putchar('\n'); - return; - } - - if (w > 18) { +out: + if (w < 0 || w > 18) { putchar('\n'); - w = 0; + return 0; } - for (const char *line = gettext(help->text); *line;) { - const char *nl = strchr(line, '\n'); - - if (!nl) { - printf("%*s%s\n", 20-w, "", line); - break; - } - - printf("%*s%.*s\n", 20-w, "", (int)(nl-line), line); - line = nl+1; - } + return w; } static void print_help(void) { + const struct option *opt; + print_usage(stdout); - puts(_( -"This is \"cdecl99\": a command-line tool for parsing and constructing\n" -"complicated C declarations.\n")); + puts(_("This is \"cdecl99\": a command-line tool for parsing and constructing\n" + "complicated C declarations.")); + putchar('\n'); puts(_("Options:")); - for (const char *c = sopts; *c; c++) { - if (*c == ':' || *c == '+' || *c == '-') + for (opt = lopts; opt->name; opt++) { + struct lopt_help help; + const char *line; + int w; + + if (!lopt_get_help(opt, &help)) continue; - print_option(*c); + w = print_optstring(opt, &help); + + 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; + } } putchar('\n'); - puts(_("For more information, see the cdecl99(1) man page.\n")); + puts(_("For more information, see the cdecl99(1) man page.")); + putchar('\n'); /* * TRANSLATORS: Please add *another line* indicating where users should @@ -492,7 +435,7 @@ static int repl_noninteractive(void) return ret; } -/* Initialize gettext and setup translated long options. */ +/* Initialize gettext */ static void init_i18n(void) { if (!ENABLE_NLS) @@ -501,16 +444,6 @@ static void init_i18n(void) setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); - - for (int i = 0, j = NOPTS; i < NOPTS; i++) { - const char *tname = pgettext_expr("longopt", lopts[i].name); - - if (strcmp(tname, lopts[i].name) != 0) { - lopts[j] = lopts[i]; - lopts[j].name = tname; - j++; - } - } } int main(int argc, char **argv) diff --git a/src/options.opt b/src/options.opt new file mode 100644 index 0000000..921feb6 --- /dev/null +++ b/src/options.opt @@ -0,0 +1,22 @@ +-q, --quiet +Suppress the welcome message. + +-b, --batch +Run in batch mode. Commands are executed as normal but +prompts are not printed. + +-i, --interactive +Run in interactive mode. This is the default. + +-f, --file=FILE +Read commands from FILE instead of standard input. + +-e, --execute=COMMAND +Execute COMMAND as if it were entered at the prompt. +This option can be specified multiple times. + +-V, --version +Print a version message and then exit. + +-H, --help +Print this message and then exit. -- 2.43.2