aboutsummaryrefslogtreecommitdiff
path: root/tools/parseopt.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/parseopt.c')
-rw-r--r--tools/parseopt.c713
1 files changed, 713 insertions, 0 deletions
diff --git a/tools/parseopt.c b/tools/parseopt.c
new file mode 100644
index 0000000..98e6aea
--- /dev/null
+++ b/tools/parseopt.c
@@ -0,0 +1,713 @@
+/* This file is part of GDBM, the GNU data base manager.
+ Copyright (C) 2011-2023 Free Software Foundation, Inc.
+
+ GDBM 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, or (at your option)
+ any later version.
+
+ GDBM 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 GDBM. If not, see <http://www.gnu.org/licenses/>. */
+
+# include "autoconf.h"
+# include "gdbm.h"
+# include "gdbmapp.h"
+# include "gdbmdefs.h"
+# include <stdio.h>
+# include <stdarg.h>
+# include <errno.h>
+# include <string.h>
+# include <ctype.h>
+# ifdef HAVE_GETOPT_H
+# include <getopt.h>
+# endif
+
+static int argc;
+static char **argv;
+
+static struct gdbm_option *option_tab;
+static size_t option_count;
+static size_t option_max;
+static char *short_options;
+static size_t short_option_count;
+static size_t short_option_max;
+#ifdef HAVE_GETOPT_LONG
+static struct option *long_options;
+static size_t long_option_count;
+static size_t long_option_max;
+#endif
+
+#define OPT_USAGE -2
+
+struct gdbm_option parseopt_default_options[] = {
+ { 0, NULL, NULL, "" },
+ { 'h', "help", NULL, N_("give this help list") },
+ { 'V', "version", NULL, N_("print program version") },
+ { OPT_USAGE, "usage", NULL, N_("give a short usage message") },
+ { 0 }
+};
+
+#define OPT_END(opt) \
+ ((opt)->opt_short == 0 && (opt)->opt_long == 0 && (opt)->opt_descr == NULL)
+#define IS_OPTION(opt) \
+ ((opt)->opt_short || (opt)->opt_long)
+#define IS_GROUP_HEADER(opt) \
+ (!IS_OPTION(opt) && (opt)->opt_descr)
+#define IS_VALID_SHORT_OPTION(opt) \
+ ((opt)->opt_short > 0 && (opt)->opt_short < 127 && \
+ isalnum ((opt)->opt_short))
+#define IS_VALID_LONG_OPTION(opt) \
+ ((opt)->opt_long != NULL)
+
+
+static int
+optcmp (const void *a, const void *b)
+{
+ struct gdbm_option const *ap = (struct gdbm_option const *)a;
+ struct gdbm_option const *bp = (struct gdbm_option const *)b;
+
+ while (ap->opt_flags & PARSEOPT_ALIAS)
+ ap--;
+ while (bp->opt_flags & PARSEOPT_ALIAS)
+ bp--;
+
+ if (IS_VALID_SHORT_OPTION(ap) && IS_VALID_SHORT_OPTION(bp))
+ return ap->opt_short - bp->opt_short;
+ if (IS_VALID_LONG_OPTION(ap) && IS_VALID_LONG_OPTION(bp))
+ return strcmp (ap->opt_long, bp->opt_long);
+ if (IS_VALID_LONG_OPTION(ap))
+ return 1;
+ return -1;
+}
+
+static void
+sort_options (int start, int count)
+{
+ qsort (option_tab + start, count, sizeof (option_tab[0]), optcmp);
+}
+
+static size_t
+sort_group (size_t start)
+{
+ size_t i;
+
+ for (i = start; i < option_count && !IS_GROUP_HEADER (&option_tab[i]); i++)
+ ;
+ sort_options (start, i - start);
+ return i + 1;
+}
+
+static void
+sort_all_options (void)
+{
+ size_t start;
+
+ /* Ensure sane start of options. This is necessary because optcmp backs up
+ until it finds an element with cleared PARSEOPT_ALIAS flag bit. */
+ option_tab[0].opt_flags &= PARSEOPT_ALIAS;
+ for (start = 0; start < option_count; )
+ {
+ if (IS_GROUP_HEADER (&option_tab[start]))
+ start = sort_group (start + 1);
+ else
+ start = sort_group (start);
+ }
+}
+
+static void
+add_options (struct gdbm_option *options)
+{
+ size_t optcnt = 0;
+ size_t argcnt = 0;
+ size_t count = 0;
+ struct gdbm_option *opt;
+
+ for (opt = options; !OPT_END(opt); opt++)
+ {
+ count++;
+ if (IS_OPTION(opt))
+ {
+ optcnt++;
+ if (opt->opt_arg)
+ argcnt++;
+ }
+ }
+
+ if (option_count + count + 1 > option_max)
+ {
+ option_max = option_count + count + 1;
+ option_tab = erealloc (option_tab,
+ sizeof (option_tab[0]) * option_max);
+ }
+
+#ifdef HAVE_GETOPT_LONG
+ if (long_option_count + optcnt + 1 > long_option_max)
+ {
+ long_option_max = long_option_count + optcnt + 1;
+ long_options = erealloc (long_options,
+ sizeof (long_options[0]) * long_option_max);
+ }
+#endif
+ if (short_option_count + optcnt + argcnt + 1 > short_option_max)
+ {
+ short_option_max = short_option_count + optcnt + argcnt + 1;
+ short_options = erealloc (short_options,
+ sizeof (short_options[0]) * short_option_max);
+ }
+
+ for (opt = options; !OPT_END(opt); opt++)
+ {
+ option_tab[option_count++] = *opt;
+ if (!IS_OPTION (opt))
+ continue;
+ if (IS_VALID_SHORT_OPTION (opt))
+ {
+ short_options[short_option_count++] = opt->opt_short;
+ if (opt->opt_arg)
+ short_options[short_option_count++] = ':';
+ }
+#ifdef HAVE_GETOPT_LONG
+ if (IS_VALID_LONG_OPTION (opt))
+ {
+ long_options[long_option_count].name = opt->opt_long;
+ long_options[long_option_count].has_arg = opt->opt_arg != NULL;
+ long_options[long_option_count].flag = NULL;
+ long_options[long_option_count].val = opt->opt_short;
+ long_option_count++;
+ }
+#endif
+ }
+ short_options[short_option_count] = 0;
+#ifdef HAVE_GETOPT_LONG
+ memset (&long_options[long_option_count], 0,
+ sizeof long_options[long_option_count]);
+#endif
+}
+
+void
+parseopt_free (void)
+{
+ free (option_tab);
+ option_tab = NULL;
+ free (short_options);
+ short_options = NULL;
+ short_option_count = short_option_max = 0;
+#ifdef HAVE_GETOPT_LONG
+ free (long_options);
+ long_options = NULL;
+ long_option_count = long_option_max = 0;
+#endif
+}
+
+int
+parseopt_first (int pc, char **pv, struct gdbm_option *opts)
+{
+ parseopt_free ();
+ add_options (opts);
+ add_options (parseopt_default_options);
+ opterr = 0;
+ argc = pc;
+ argv = pv;
+ return parseopt_next ();
+}
+
+static unsigned short_opt_col = 2;
+static unsigned long_opt_col = 6;
+static unsigned doc_opt_col = 2; /* FIXME: Not used: there are no doc
+ options in this implementation */
+static unsigned header_col = 1;
+static unsigned opt_doc_col = 29;
+static unsigned usage_indent = 12;
+static unsigned rmargin = 79;
+
+static unsigned dup_args = 0;
+static unsigned dup_args_note = 1;
+
+enum usage_var_type
+ {
+ usage_var_column,
+ usage_var_bool
+ };
+
+struct usage_var_def
+{
+ char *name;
+ unsigned *valptr;
+ enum usage_var_type type;
+};
+
+static struct usage_var_def usage_var[] = {
+ { "short-opt-col", &short_opt_col, usage_var_column },
+ { "header-col", &header_col, usage_var_column },
+ { "opt-doc-col", &opt_doc_col, usage_var_column },
+ { "usage-indent", &usage_indent, usage_var_column },
+ { "rmargin", &rmargin, usage_var_column },
+ { "dup-args", &dup_args, usage_var_bool },
+ { "dup-args-note", &dup_args_note, usage_var_bool },
+ { "long-opt-col", &long_opt_col, usage_var_column },
+ { "doc-opt-col", &doc_opt_col, usage_var_column },
+ { NULL }
+};
+
+static void
+set_usage_var (char const *text, char **end)
+{
+ struct usage_var_def *p;
+ int boolval = 1;
+ char const *prog_name = parseopt_program_name ? parseopt_program_name : progname;
+ size_t len = strcspn (text, ",=");
+ char *endp;
+
+ if (len > 3 && memcmp (text, "no-", 3) == 0)
+ {
+ text += 3;
+ len -= 3;
+ boolval = 0;
+ }
+
+ for (p = usage_var; p->name; p++)
+ {
+ if (strlen (p->name) == len && memcmp (p->name, text, len) == 0)
+ break;
+ }
+
+ endp = (char*) text + len;
+ if (p)
+ {
+ if (p->type == usage_var_bool)
+ {
+ if (*endp == '=')
+ {
+ if (prog_name)
+ fprintf (stderr, "%s: ", prog_name);
+ fprintf (stderr,
+ _("error in ARGP_HELP_FMT: improper usage of [no-]%s\n"),
+ p->name);
+ endp = strchr (text + len, ',');
+ }
+ else
+ *p->valptr = boolval;
+ }
+ else if (*endp == '=')
+ {
+ unsigned long val;
+
+ errno = 0;
+ val = strtoul (text + len + 1, &endp, 10);
+ if (errno || (*endp && *endp != ','))
+ {
+ if (prog_name)
+ fprintf (stderr, "%s: ", prog_name);
+ fprintf (stderr,
+ _("error in ARGP_HELP_FMT: bad value for %s"),
+ p->name);
+ if (endp)
+ {
+ fprintf (stderr, _(" (near %s)"), endp);
+ }
+ fputc ('\n', stderr);
+ }
+ else if (val > UINT_MAX)
+ {
+ if (prog_name)
+ fprintf (stderr, "%s: ", prog_name);
+ fprintf (stderr,
+ _("error in ARGP_HELP_FMT: %s value is out of range\n"),
+ p->name);
+ }
+ else
+ *p->valptr = val;
+ }
+ else
+ {
+ if (prog_name)
+ fprintf (stderr, "%s: ", prog_name);
+ fprintf (stderr,
+ _("%s: ARGP_HELP_FMT parameter requires a value\n"),
+ p->name);
+ }
+ }
+ else
+ {
+ if (prog_name)
+ fprintf (stderr, "%s: ", prog_name);
+ fprintf (stderr,
+ _("%s: Unknown ARGP_HELP_FMT parameter\n"),
+ text);
+ }
+ *end = endp;
+}
+
+static void
+init_usage_vars (void)
+{
+ char *fmt, *p;
+
+ fmt = getenv ("ARGP_HELP_FMT");
+ if (!fmt || !*fmt)
+ return;
+
+ while (1)
+ {
+ set_usage_var (fmt, &p);
+ if (*p == 0)
+ break;
+ else if (*p == ',')
+ p++;
+ else
+ {
+ char const *prog_name = parseopt_program_name ? parseopt_program_name : progname;
+ if (prog_name)
+ fprintf (stderr, "%s: ", prog_name);
+ fprintf (stderr, _("ARGP_HELP_FMT: missing delimiter near %s\n"),
+ p);
+ break;
+ }
+ fmt = p;
+ }
+}
+
+char *parseopt_program_name;
+const char *program_bug_address = "<" PACKAGE_BUGREPORT ">";
+void (*parseopt_help_hook) (FILE *stream);
+
+static int argsused;
+
+static int
+print_arg (WORDWRAP_FILE wf, struct gdbm_option *opt, int delim)
+{
+ if (opt->opt_arg)
+ {
+ argsused = 1;
+ return wordwrap_printf (wf, "%c%s", delim,
+ opt->opt_arg[0] ? gettext (opt->opt_arg) : "");
+ }
+ return 0;
+}
+
+size_t
+print_option (WORDWRAP_FILE wf, size_t num)
+{
+ struct gdbm_option *opt = option_tab + num;
+ size_t next, i;
+ int delim;
+ int w;
+
+ if (IS_GROUP_HEADER (opt))
+ {
+ wordwrap_set_left_margin (wf, header_col);
+ wordwrap_set_right_margin (wf, rmargin);
+ if (opt->opt_descr[0])
+ {
+ wordwrap_putc (wf, '\n');
+ wordwrap_puts (wf, gettext (opt->opt_descr));
+ wordwrap_putc (wf, '\n');
+ }
+ wordwrap_putc (wf, '\n');
+ return num + 1;
+ }
+
+ /* count aliases */
+ for (next = num + 1;
+ next < option_count && option_tab[next].opt_flags & PARSEOPT_ALIAS;
+ next++);
+
+ if (opt->opt_flags & PARSEOPT_HIDDEN)
+ return next;
+
+ wordwrap_set_left_margin (wf, short_opt_col);
+ w = 0;
+ for (i = num; i < next; i++)
+ {
+ if (IS_VALID_SHORT_OPTION (&option_tab[i]))
+ {
+ if (w)
+ wordwrap_write (wf, ", ", 2);
+ wordwrap_printf (wf, "-%c", option_tab[i].opt_short);
+ delim = ' ';
+ if (dup_args)
+ print_arg (wf, opt, delim);
+ w = 1;
+ }
+ }
+
+#ifdef HAVE_GETOPT_LONG
+ for (i = num; i < next; i++)
+ {
+ if (IS_VALID_LONG_OPTION (&option_tab[i]))
+ {
+ if (w)
+ wordwrap_write (wf, ", ", 2);
+ wordwrap_set_left_margin (wf, long_opt_col);
+ w = 0;
+ break;
+ }
+ }
+ for (; i < next; i++)
+ {
+ if (IS_VALID_LONG_OPTION (&option_tab[i]))
+ {
+ if (w)
+ wordwrap_write (wf, ", ", 2);
+ wordwrap_printf (wf, "--%s", option_tab[i].opt_long);
+ delim = '=';
+ if (dup_args)
+ print_arg (wf, opt, delim);
+ w = 1;
+ }
+ }
+#endif
+ if (!dup_args)
+ print_arg (wf, opt, delim);
+
+ wordwrap_set_left_margin (wf, opt_doc_col);
+ if (opt->opt_descr[0])
+ wordwrap_puts (wf, gettext (opt->opt_descr));
+
+ return next;
+}
+
+void
+parseopt_print_help (void)
+{
+ unsigned i;
+ WORDWRAP_FILE wf;
+
+ argsused = 0;
+
+ init_usage_vars ();
+
+ wf = wordwrap_fdopen (1);
+
+ wordwrap_printf (wf, "%s %s [%s]... %s\n", _("Usage:"),
+ parseopt_program_name ? parseopt_program_name : progname,
+ _("OPTION"),
+ (parseopt_program_args && parseopt_program_args[0])
+ ? gettext (parseopt_program_args) : "");
+
+ wordwrap_set_right_margin (wf, rmargin);
+ if (parseopt_program_doc && parseopt_program_doc[0])
+ wordwrap_puts (wf, gettext (parseopt_program_doc));
+ wordwrap_para (wf);
+
+ sort_all_options ();
+ for (i = 0; i < option_count; )
+ {
+ i = print_option (wf, i);
+ }
+ wordwrap_para (wf);
+
+#ifdef HAVE_GETOPT_LONG
+ if (argsused && dup_args_note)
+ {
+ wordwrap_set_left_margin (wf, 0);
+ wordwrap_set_right_margin (wf, rmargin);
+ wordwrap_puts (wf, _("Mandatory or optional arguments to long options are also mandatory or optional for any corresponding short options."));
+ wordwrap_para (wf);
+ }
+#endif
+ if (parseopt_help_hook)
+ parseopt_help_hook (stdout);//FIXME
+
+ wordwrap_set_left_margin (wf, 0);
+ wordwrap_set_right_margin (wf, rmargin);
+ /* TRANSLATORS: The placeholder indicates the bug-reporting address
+ for this package. Please add _another line_ saying
+ "Report translation bugs to <...>\n" with the address for translation
+ bugs (typically your translation team's web or email address). */
+ wordwrap_printf (wf, _("Report bugs to %s.\n"), program_bug_address);
+
+#ifdef PACKAGE_URL
+ wordwrap_printf (wf, _("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
+#endif
+}
+
+static int
+cmpidx_short (const void *a, const void *b)
+{
+ unsigned const *ai = (unsigned const *)a;
+ unsigned const *bi = (unsigned const *)b;
+
+ return option_tab[*ai].opt_short - option_tab[*bi].opt_short;
+}
+
+#ifdef HAVE_GETOPT_LONG
+static int
+cmpidx_long (const void *a, const void *b)
+{
+ unsigned const *ai = (unsigned const *)a;
+ unsigned const *bi = (unsigned const *)b;
+ struct gdbm_option const *ap = option_tab + *ai;
+ struct gdbm_option const *bp = option_tab + *bi;
+ return strcmp (ap->opt_long, bp->opt_long);
+}
+#endif
+
+void
+print_usage (void)
+{
+ WORDWRAP_FILE wf;
+ unsigned i;
+ unsigned *idxbuf;
+ unsigned nidx;
+
+ init_usage_vars ();
+
+ idxbuf = ecalloc (option_count, sizeof (idxbuf[0]));
+
+ wf = wordwrap_fdopen (1);
+ wordwrap_set_right_margin (wf, rmargin);
+ wordwrap_printf (wf, "%s %s ", _("Usage:"),
+ parseopt_program_name ? parseopt_program_name : progname);
+ wordwrap_next_left_margin (wf, usage_indent);
+
+ /* Print a list of short options without arguments. */
+ for (i = nidx = 0; i < option_count; i++)
+ if (IS_VALID_SHORT_OPTION (&option_tab[i]) && !option_tab[i].opt_arg)
+ idxbuf[nidx++] = i;
+
+ if (nidx)
+ {
+ qsort (idxbuf, nidx, sizeof (idxbuf[0]), cmpidx_short);
+
+ wordwrap_puts (wf, "[-");
+ for (i = 0; i < nidx; i++)
+ {
+ wordwrap_putc (wf, option_tab[idxbuf[i]].opt_short);
+ }
+ wordwrap_putc (wf, ']');
+ }
+
+ /* Print a list of short options with arguments. */
+ for (i = nidx = 0; i < option_count; i++)
+ {
+ if (IS_VALID_SHORT_OPTION (&option_tab[i]) && option_tab[i].opt_arg)
+ idxbuf[nidx++] = i;
+ }
+
+ if (nidx)
+ {
+ qsort (idxbuf, nidx, sizeof (idxbuf[0]), cmpidx_short);
+
+ for (i = 0; i < nidx; i++)
+ {
+ struct gdbm_option *opt = option_tab + idxbuf[i];
+ const char *arg = gettext (opt->opt_arg);
+
+ wordwrap_word_start (wf);
+ wordwrap_puts (wf, " [-");
+ wordwrap_putc (wf, opt->opt_short);
+ wordwrap_putc (wf, ' ');
+ wordwrap_puts (wf, arg);
+ wordwrap_putc (wf, ']');
+ wordwrap_word_end (wf);
+ }
+ }
+
+#ifdef HAVE_GETOPT_LONG
+ /* Print a list of long options */
+ for (i = nidx = 0; i < option_count; i++)
+ {
+ if (IS_VALID_LONG_OPTION (&option_tab[i]))
+ idxbuf[nidx++] = i;
+ }
+
+ if (nidx)
+ {
+ qsort (idxbuf, nidx, sizeof (idxbuf[0]), cmpidx_long);
+
+ for (i = 0; i < nidx; i++)
+ {
+ struct gdbm_option *opt = option_tab + idxbuf[i];
+ const char *arg = opt->opt_arg ? gettext (opt->opt_arg) : NULL;
+
+ wordwrap_word_start (wf);
+ wordwrap_write (wf, " [--", 4);
+ wordwrap_puts (wf, opt->opt_long);
+ if (opt->opt_arg)
+ {
+ wordwrap_putc (wf, '=');
+ wordwrap_write (wf, arg, strlen (arg));
+ }
+ wordwrap_putc (wf, ']');
+ wordwrap_word_end (wf);
+ }
+ }
+#endif
+ wordwrap_close (wf);
+ free (idxbuf);
+}
+
+const char version_etc_copyright[] =
+ /* Do *not* mark this string for translation. First %s is a copyright
+ symbol suitable for this locale, and second %s are the copyright
+ years. */
+ "Copyright %s %s Free Software Foundation, Inc";
+
+const char license_text[] =
+ "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n"
+ "This is free software: you are free to change and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted by law.";
+
+void
+print_version_only (void)
+{
+ printf ("%s (%s) %s\n",
+ parseopt_program_name ? parseopt_program_name : progname,
+ PACKAGE_NAME,
+ PACKAGE_VERSION);
+ /* TRANSLATORS: Translate "(C)" to the copyright symbol
+ (C-in-a-circle), if this symbol is available in the user's
+ locale. Otherwise, do not translate "(C)"; leave it as-is. */
+ printf (version_etc_copyright, _("(C)"), "2011-2023");
+ putchar ('\n');
+ puts (license_text);
+ putchar ('\n');
+}
+
+
+static int
+handle_option (int c)
+{
+ switch (c)
+ {
+ case 'h':
+ parseopt_print_help ();
+ exit (0);
+
+ case 'V':
+ print_version_only ();
+ exit (0);
+
+ case OPT_USAGE:
+ print_usage ();
+ exit (0);
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+int
+parseopt_next (void)
+{
+ int rc;
+
+ do
+ {
+#ifdef HAVE_GETOPT_LONG
+ rc = getopt_long (argc, argv, short_options, long_options, NULL);
+#else
+ rc = getopt (argc, argv, short_options);
+#endif
+ }
+ while (handle_option (rc));
+
+ return rc;
+}

Return to:

Send suggestions and report system problems to the System administrator.