summaryrefslogtreecommitdiff
path: root/libmailutils
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2016-10-07 21:58:50 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2016-10-07 21:58:50 +0300
commit87ef8884693eb0cdcdafa2365c86709f33e3a375 (patch)
tree3988c862973ebb3b5764af6feefc77aae223133a /libmailutils
parent8b8f29eb6485e5ab09e2215d595fbbf45c27c97b (diff)
downloadmailutils-87ef8884693eb0cdcdafa2365c86709f33e3a375.tar.gz
mailutils-87ef8884693eb0cdcdafa2365c86709f33e3a375.tar.bz2
Add generic library for option parsing.
* configure.ac: Build libmailutils/opt * include/mailutils/Makefile.am: Add opt.h * include/mailutils/opt.h: New file. * include/mailutils/util.h (mu_c_type_t): New datatype. (mu_str_to_c): New proto. * libmailutils/Makefile.am: Add opt to subdirs. * libmailutils/opt/Makefile.am: New file. * libmailutils/opt/help.c: New file. * libmailutils/opt/opt.c: New file. * libmailutils/opt/progname.c: New file. * libmailutils/string/Makefile.am: Add new files. * libmailutils/string/str_to_c.c: New file. * libmailutils/string/to_sn.c: New file. * libmailutils/string/to_un.c: New file. * libmailutils/tests/.gitignore: Update. * libmailutils/tests/Makefile.am: Update. * libmailutils/tests/parseopt.c: New file.
Diffstat (limited to 'libmailutils')
-rw-r--r--libmailutils/Makefile.am3
-rw-r--r--libmailutils/opt/Makefile.am26
-rw-r--r--libmailutils/opt/help.c337
-rw-r--r--libmailutils/opt/opt.c625
-rw-r--r--libmailutils/opt/progname.c43
-rw-r--r--libmailutils/string/Makefile.am5
-rw-r--r--libmailutils/string/str_to_c.c274
-rw-r--r--libmailutils/string/to_sn.c37
-rw-r--r--libmailutils/string/to_un.c34
-rw-r--r--libmailutils/tests/.gitignore1
-rw-r--r--libmailutils/tests/Makefile.am1
-rw-r--r--libmailutils/tests/parseopt.c101
12 files changed, 1485 insertions, 2 deletions
diff --git a/libmailutils/Makefile.am b/libmailutils/Makefile.am
index 9d0c3bfe6..7a5270c4c 100644
--- a/libmailutils/Makefile.am
+++ b/libmailutils/Makefile.am
@@ -18,7 +18,7 @@
SUBDIRS = \
auth base address list sockaddr cidr cfg diag\
- filter mailbox mailer mime msgset server string stream stdstream\
+ filter mailbox mailer mime msgset opt server string stream stdstream\
property url imapio datetime . tests
lib_LTLIBRARIES = libmailutils.la
@@ -42,6 +42,7 @@ libmailutils_la_LIBADD = \
mailer/libmailer.la\
mime/libmime.la\
msgset/libmsgset.la\
+ opt/libopt.la\
property/libproperty.la\
server/libserver.la\
string/libstring.la\
diff --git a/libmailutils/opt/Makefile.am b/libmailutils/opt/Makefile.am
new file mode 100644
index 000000000..d229c4552
--- /dev/null
+++ b/libmailutils/opt/Makefile.am
@@ -0,0 +1,26 @@
+# GNU Mailutils -- a suite of utilities for electronic mail
+# Copyright (C) 2016 Free Software Foundation, Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General
+# Public License along with this library. If not, see
+# <http://www.gnu.org/licenses/>.
+
+noinst_LTLIBRARIES = libopt.la
+
+libopt_la_SOURCES = \
+ opt.c\
+ help.c\
+ progname.c
+
+AM_CPPFLAGS = \
+ @MU_LIB_COMMON_INCLUDES@ -I/libmailutils
diff --git a/libmailutils/opt/help.c b/libmailutils/opt/help.c
new file mode 100644
index 000000000..7ffc43ff9
--- /dev/null
+++ b/libmailutils/opt/help.c
@@ -0,0 +1,337 @@
+/* help.c -- general-purpose command line option parser
+ Copyright (C) 2016 Free Software Foundation, Inc.
+
+ GNU Mailutils 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.
+
+ GNU Mailutils 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 GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <mailutils/alloc.h>
+#include <mailutils/opt.h>
+#include <mailutils/cctype.h>
+#include <mailutils/nls.h>
+
+#define LMARGIN 2
+#define DESCRCOLUMN 30
+#define RMARGIN 79
+#define GROUPCOLUMN 2
+#define USAGECOLUMN 13
+
+static void
+indent (size_t start, size_t col)
+{
+ for (; start < col; start++)
+ putchar (' ');
+}
+
+static void
+print_option_descr (const char *descr, size_t lmargin, size_t rmargin)
+{
+ while (*descr)
+ {
+ size_t s = 0;
+ size_t i;
+ size_t width = rmargin - lmargin;
+
+ for (i = 0; ; i++)
+ {
+ if (descr[i] == 0 || descr[i] == ' ' || descr[i] == '\t')
+ {
+ if (i > width)
+ break;
+ s = i;
+ if (descr[i] == 0)
+ break;
+ }
+ }
+ fwrite (descr, 1, s, stdout);
+ fputc ('\n', stdout);
+ descr += s;
+ if (*descr)
+ {
+ indent (0, lmargin);
+ descr++;
+ }
+ }
+}
+
+
+static size_t
+print_option (struct mu_option **optbuf, size_t optcnt, size_t num,
+ int *argsused)
+{
+ struct mu_option *opt = optbuf[num];
+ size_t next, i;
+ int delim;
+ int w;
+
+ if (MU_OPTION_IS_GROUP_HEADER (opt))
+ {
+ if (num)
+ putchar ('\n');
+ indent (0, GROUPCOLUMN);
+ print_option_descr (gettext (opt->opt_doc), GROUPCOLUMN, RMARGIN);
+ putchar ('\n');
+ return num + 1;
+ }
+
+ /* count aliases */
+ for (next = num + 1;
+ next < optcnt && optbuf[next]->opt_flags & MU_OPTION_ALIAS;
+ next++);
+
+ if (opt->opt_flags & MU_OPTION_HIDDEN)
+ return next;
+
+ w = 0;
+ for (i = num; i < next; i++)
+ {
+ if (MU_OPTION_IS_VALID_SHORT_OPTION (optbuf[i]))
+ {
+ if (w == 0)
+ {
+ indent (0, LMARGIN);
+ w = LMARGIN;
+ }
+ else
+ w += printf (", ");
+ w += printf ("-%c", optbuf[i]->opt_short);
+ delim = ' ';
+ }
+ }
+
+ for (i = num; i < next; i++)
+ {
+ if (MU_OPTION_IS_VALID_LONG_OPTION (optbuf[i]))
+ {
+ if (w == 0)
+ {
+ indent (0, LMARGIN);
+ w = LMARGIN;
+ }
+ else
+ w += printf (", ");
+ w += printf ("--%s", optbuf[i]->opt_long);
+ delim = '=';
+ }
+ }
+
+ if (opt->opt_arg)
+ {
+ *argsused = 1;
+ w += printf ("%c%s", delim, gettext (opt->opt_arg));
+ }
+ if (w >= DESCRCOLUMN)
+ {
+ putchar ('\n');
+ w = 0;
+ }
+ indent (w, DESCRCOLUMN);
+ print_option_descr (gettext (opt->opt_doc), DESCRCOLUMN, RMARGIN);
+
+ return next;
+}
+
+void
+mu_option_describe_options (struct mu_option **optbuf, size_t optcnt)
+{
+ unsigned i;
+ int argsused = 0;
+
+ for (i = 0; i < optcnt; )
+ i = print_option (optbuf, optcnt, i, &argsused);
+ putchar ('\n');
+
+ if (argsused)
+ {
+ print_option_descr (_("Mandatory or optional arguments to long options are also mandatory or optional for any corresponding short options."), 0, RMARGIN);
+ putchar ('\n');
+ }
+}
+
+void
+mu_program_help (struct mu_parseopt *po)
+{
+ printf ("%s", _("Usage:"));
+ if (po->po_prog_name)
+ printf (" %s", po->po_prog_name);
+ printf (" [%s]...", _("OPTION"));
+ if (po->po_prog_args)
+ printf (" %s", gettext (po->po_prog_args));
+ putchar ('\n');
+
+ if (po->po_prog_doc)
+ print_option_descr (gettext (po->po_prog_doc), 0, RMARGIN);
+ putchar ('\n');
+
+ mu_option_describe_options (po->po_optv, po->po_optc);
+
+ if (po->po_help_hook)
+ po->po_help_hook (stdout);
+
+ if (po->po_bug_address)
+ /* 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). */
+ printf (_("Report bugs to %s.\n"), po->po_bug_address);
+
+ if (po->po_package_name && po->po_package_url)
+ printf (_("%s home page: <%s>\n"),
+ po->po_package_name, po->po_package_url);
+}
+
+static struct mu_option **option_tab;
+
+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;
+}
+
+static int
+cmpidx_long (const void *a, const void *b)
+{
+ unsigned const *ai = (unsigned const *)a;
+ unsigned const *bi = (unsigned const *)b;
+ struct mu_option const *ap = option_tab[*ai];
+ struct mu_option const *bp = option_tab[*bi];
+ return strcmp (ap->opt_long, bp->opt_long);
+}
+
+void
+mu_program_usage (struct mu_parseopt *po)
+{
+ unsigned i;
+ unsigned n;
+ char buf[RMARGIN+1];
+ unsigned *idxbuf;
+ unsigned nidx;
+
+ struct mu_option **optbuf = po->po_optv;
+ size_t optcnt = po->po_optc;
+
+#define FLUSH \
+ do \
+ { \
+ buf[n] = 0; \
+ printf ("%s\n", buf); \
+ n = USAGECOLUMN; \
+ memset (buf, ' ', n); \
+ } \
+ while (0)
+#define ADDC(c) \
+ do \
+ { \
+ if (n == RMARGIN) FLUSH; \
+ buf[n++] = c; \
+ } \
+ while (0)
+
+ option_tab = optbuf;
+
+ idxbuf = mu_calloc (optcnt, sizeof (idxbuf[0]));
+
+ n = snprintf (buf, sizeof buf, "%s %s ", _("Usage:"), mu_progname);
+
+ /* Print a list of short options without arguments. */
+ for (i = nidx = 0; i < optcnt; i++)
+ if (MU_OPTION_IS_VALID_SHORT_OPTION (optbuf[i]) && !optbuf[i]->opt_arg)
+ idxbuf[nidx++] = i;
+
+ if (nidx)
+ {
+ qsort (idxbuf, nidx, sizeof (idxbuf[0]), cmpidx_short);
+
+ ADDC ('[');
+ ADDC ('-');
+ for (i = 0; i < nidx; i++)
+ {
+ ADDC (optbuf[idxbuf[i]]->opt_short);
+ }
+ ADDC (']');
+ }
+
+ /* Print a list of short options with arguments. */
+ for (i = nidx = 0; i < optcnt; i++)
+ {
+ if (MU_OPTION_IS_VALID_SHORT_OPTION (optbuf[i]) && optbuf[i]->opt_arg)
+ idxbuf[nidx++] = i;
+ }
+
+ if (nidx)
+ {
+ qsort (idxbuf, nidx, sizeof (idxbuf[0]), cmpidx_short);
+
+ for (i = 0; i < nidx; i++)
+ {
+ struct mu_option *opt = optbuf[idxbuf[i]];
+ const char *arg = gettext (opt->opt_arg);
+ size_t len = 5 + strlen (arg) + 1;
+
+ if (n + len > RMARGIN) FLUSH;
+ buf[n++] = ' ';
+ buf[n++] = '[';
+ buf[n++] = '-';
+ buf[n++] = opt->opt_short;
+ buf[n++] = ' ';
+ strcpy (&buf[n], arg);
+ n += strlen (arg);
+ buf[n++] = ']';
+ }
+ }
+
+ /* Print a list of long options */
+ for (i = nidx = 0; i < optcnt; i++)
+ {
+ if (MU_OPTION_IS_VALID_LONG_OPTION (optbuf[i]))
+ idxbuf[nidx++] = i;
+ }
+
+ if (nidx)
+ {
+ qsort (idxbuf, nidx, sizeof (idxbuf[0]), cmpidx_long);
+
+ for (i = 0; i < nidx; i++)
+ {
+ struct mu_option *opt = optbuf[idxbuf[i]];
+ const char *arg = opt->opt_arg ? gettext (opt->opt_arg) : NULL;
+ size_t len = 3 + strlen (opt->opt_long)
+ + (arg ? 1 + strlen (arg) : 0);
+ if (n + len > RMARGIN) FLUSH;
+ buf[n++] = ' ';
+ buf[n++] = '[';
+ buf[n++] = '-';
+ buf[n++] = '-';
+ strcpy (&buf[n], opt->opt_long);
+ n += strlen (opt->opt_long);
+ if (opt->opt_arg)
+ {
+ buf[n++] = '=';
+ strcpy (&buf[n], arg);
+ n += strlen (arg);
+ }
+ buf[n++] = ']';
+ }
+ }
+
+ FLUSH;
+ free (idxbuf);
+}
+
diff --git a/libmailutils/opt/opt.c b/libmailutils/opt/opt.c
new file mode 100644
index 000000000..bec74cda7
--- /dev/null
+++ b/libmailutils/opt/opt.c
@@ -0,0 +1,625 @@
+/* opt.c -- general-purpose command line option parser
+ Copyright (C) 2016 Free Software Foundation, Inc.
+
+ GNU Mailutils 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.
+
+ GNU Mailutils 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 GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <mailutils/alloc.h>
+#include <mailutils/opt.h>
+#include <mailutils/nls.h>
+#include <mailutils/errno.h>
+
+#define EXIT_SUCCESS 0
+#define EXIT_ERROR 1
+
+/* Compare pointers to two option structs */
+static int
+optcmp (const void *a, const void *b)
+{
+ struct mu_option const *ap = *(struct mu_option const **)a;
+ struct mu_option const *bp = *(struct mu_option const **)b;
+
+ while (ap->opt_flags & MU_OPTION_ALIAS)
+ ap--;
+ while (bp->opt_flags & MU_OPTION_ALIAS)
+ bp--;
+
+ if (MU_OPTION_IS_VALID_SHORT_OPTION (ap)
+ && MU_OPTION_IS_VALID_SHORT_OPTION (bp))
+ return ap->opt_short - bp->opt_short;
+ if (MU_OPTION_IS_VALID_LONG_OPTION (ap)
+ && MU_OPTION_IS_VALID_LONG_OPTION (bp))
+ return strcmp (ap->opt_long, bp->opt_long);
+ if (MU_OPTION_IS_VALID_LONG_OPTION (ap))
+ return 1;
+ return -1;
+}
+
+/* Sort a group of options in OPTBUF, starting at index START (first
+ option slot after a group header (if any). The group spans up to
+ next group header or end of options */
+static size_t
+sort_group (struct mu_option **optbuf, size_t start)
+{
+ size_t i;
+
+ for (i = start; optbuf[i] && !MU_OPTION_IS_GROUP_HEADER (optbuf[i]); i++)
+ ;
+
+ qsort (&optbuf[start], i - start, sizeof (optbuf[0]), optcmp);
+ return i;
+}
+
+/* Print help summary and exit. */
+static void
+fn_help (struct mu_parseopt *po, struct mu_option *opt, char const *unused)
+{
+ mu_program_help (po);
+ exit (EXIT_SUCCESS);
+}
+
+/* Print usage summary and exit. */
+static void
+fn_usage (struct mu_parseopt *po, struct mu_option *opt, char const *unused)
+{
+ mu_program_usage (po);
+ exit (EXIT_SUCCESS);
+}
+
+/* Default options */
+struct mu_option mu_default_options[] = {
+ MU_OPTION_GROUP(""),
+ { "help", 'h', NULL, MU_OPTION_IMMEDIATE, N_("give this help list"),
+ mu_c_string, NULL, fn_help },
+ { "version", 'V', NULL, MU_OPTION_IMMEDIATE, N_("print program version"),
+ mu_c_string, NULL, /* FIXME: fn_version */ },
+ { "usage", 0, NULL, MU_OPTION_IMMEDIATE, N_("give a short usage message"),
+ mu_c_string, NULL, fn_usage
+ },
+ MU_OPTION_END
+};
+
+/* Output error message */
+static void
+parse_error (struct mu_parseopt *po, char const *fmt, ...)
+{
+ va_list ap;
+
+ if (po->po_flags & MU_PARSEOPT_IGNORE_ERRORS)
+ return;
+
+ if (po->po_prog_name)
+ fprintf (stderr, "%s: ", po->po_prog_name);
+ va_start (ap, fmt);
+ vfprintf (stderr, fmt, ap);
+ va_end (ap);
+ fputc ('\n', stderr);
+}
+
+static void
+mu_option_cache_destroy (void *ptr)
+{
+ struct mu_option_cache *cache = ptr;
+ free (cache->cache_arg);
+ free (cache);
+}
+
+static int parseopt_apply (void *item, void *data);
+
+/* If OPT is an immediate option, evaluate it right away. Otherwise,
+ add option OPT with argument ARG to the cache in PO. */
+void
+add_option_cache (struct mu_parseopt *po, struct mu_option *opt,
+ char const *arg)
+{
+ struct mu_option_cache *cache = mu_alloc (sizeof (*cache));
+ cache->cache_opt = opt;
+ cache->cache_arg = arg ? mu_strdup (arg) : NULL;
+
+ if (opt->opt_flags & MU_OPTION_IMMEDIATE)
+ {
+ parseopt_apply (cache, po);
+ mu_option_cache_destroy (cache);
+ }
+ else
+ {
+ mu_list_append (po->po_optlist, cache);
+ }
+}
+
+/* Find first option for which I is an alias */
+struct mu_option *
+option_unalias (struct mu_parseopt *po, int i)
+{
+ while (i > 0 && po->po_optv[i]->opt_flags & MU_OPTION_ALIAS)
+ --i;
+ return po->po_optv[i];
+}
+
+/* Find a descriptor of short option CHR */
+struct mu_option *
+find_short_option (struct mu_parseopt *po, int chr)
+{
+ size_t i;
+
+ for (i = 0; i < po->po_optc; i++)
+ {
+ if (MU_OPTION_IS_VALID_SHORT_OPTION (po->po_optv[i])
+ && po->po_optv[i]->opt_short == chr)
+ return option_unalias (po, i);
+ }
+ parse_error (po, _("unrecognized option '-%c'"), chr);
+ return NULL;
+}
+
+/* Find a descriptor of long option OPTSTR. If it has argument, return
+ it in *ARGPTR. */
+struct mu_option *
+find_long_option (struct mu_parseopt *po, char const *optstr,
+ char **argptr)
+{
+ size_t i;
+ size_t optlen;
+ size_t ind;
+ int found = 0;
+
+ optlen = strcspn (optstr, "=");
+
+ for (i = 0; i < po->po_optc; i++)
+ {
+ if (MU_OPTION_IS_VALID_LONG_OPTION (po->po_optv[i])
+ && optlen <= strlen (po->po_optv[i]->opt_long)
+ && memcmp (po->po_optv[i]->opt_long, optstr, optlen) == 0)
+ {
+ switch (found)
+ {
+ case 0:
+ ind = i;
+ found++;
+ break;
+
+ case 1:
+ if (po->po_flags & MU_PARSEOPT_IGNORE_ERRORS)
+ return NULL;
+ parse_error (po,
+ _("option '--%*.*s' is ambiguous; possibilities:"),
+ optlen, optlen, optstr);
+ fprintf (stderr, "--%s\n", po->po_optv[ind]->opt_long);
+ found++;
+
+ case 2:
+ fprintf (stderr, "--%s\n", po->po_optv[i]->opt_long);
+ }
+ }
+ }
+
+ switch (found)
+ {
+ case 0:
+ parse_error (po, _("unrecognized option '--%s'"), optstr);
+ break;
+
+ case 1:
+ if (optstr[optlen])
+ ++optlen;
+ *argptr = (char *)(optstr + optlen);
+ return option_unalias (po, ind);
+
+ case 2:
+ break;
+ }
+
+ return NULL;
+}
+
+
+/* Consume next option from PO. On success, update PO members as
+ described below and return 0. On end of options, return 1.
+
+ If the consumed option is a short option, then
+
+ po_chr keeps its option character, and
+ po_cur points to the next option character to be processed
+
+ Otherwise, if the consumed option is a long one, then
+
+ po_chr is 0
+ po_cur points to the first character after --
+*/
+static int
+next_opt (struct mu_parseopt *po)
+{
+ if (!*po->po_cur)
+ {
+ if (!(po->po_flags & MU_PARSEOPT_IN_ORDER) && po->po_arg_count)
+ {
+ /* Array to save arguments in */
+ char *save[2];
+ /* Number of arguments processed (at most two) */
+ int n = po->po_ind - (po->po_arg_start + po->po_arg_count);
+
+ if (n > 2)
+ abort ();
+
+ /* Store the processed elements away */
+ save[0] = po->po_argv[po->po_arg_start + po->po_arg_count];
+ if (n == 2)
+ save[1] = po->po_argv[po->po_arg_start + po->po_arg_count + 1];
+
+ /* Shift the array */
+ memmove (po->po_argv + po->po_arg_start + n,
+ po->po_argv + po->po_arg_start,
+ po->po_arg_count * sizeof (po->po_argv[0]));
+
+ /* Place stored elements in the vacating slots */
+ po->po_argv[po->po_arg_start] = save[0];
+ if (n == 2)
+ po->po_argv[po->po_arg_start + 1] = save[1];
+
+ /* Fix up start index */
+ po->po_arg_start += n;
+ }
+
+ if (po->po_ind == po->po_argc)
+ return 1;
+
+ while (1)
+ {
+ po->po_cur = po->po_argv[po->po_ind++];
+ if (!po->po_cur)
+ return 1;
+ if (po->po_cur[0] == '-' && po->po_cur[1])
+ break;
+ if (!(po->po_flags & MU_PARSEOPT_IN_ORDER))
+ {
+ po->po_arg_count++;
+ continue;
+ }
+ else
+ return 1;
+ }
+
+ if (*++po->po_cur == '-')
+ {
+ if (*++po->po_cur == 0)
+ /* End of options */
+ return 1;
+
+ /* It's a long option */
+ po->po_chr = 0;
+ return 0;
+ }
+ }
+
+ po->po_chr = *po->po_cur++;
+
+ return 0;
+}
+
+/* Parse options */
+static int
+parse (struct mu_parseopt *po)
+{
+ int rc;
+
+ rc = mu_list_create (&po->po_optlist);
+ if (rc)
+ return rc;
+ mu_list_set_destroy_item (po->po_optlist, mu_option_cache_destroy);
+
+ po->po_ind = 0;
+ if (!(po->po_flags & MU_PARSEOPT_ARGV0))
+ {
+ po->po_ind++;
+ if (!(po->po_flags & MU_PARSEOPT_PROG_NAME))
+ {
+ char *p = strrchr (po->po_argv[0], '/');
+ if (p)
+ po->po_prog_name = p + 1;
+ else
+ po->po_prog_name = (char*) po->po_argv[0];
+ }
+ }
+ else if (!(po->po_flags & MU_PARSEOPT_PROG_NAME))
+ po->po_prog_name = NULL;
+
+ po->po_arg_start = po->po_ind;
+ po->po_arg_count = 0;
+
+ po->po_cur = "";
+
+ po->po_opterr = -1;
+
+ while (next_opt (po) == 0)
+ {
+ struct mu_option *opt;
+ char *long_opt;
+
+ if (po->po_chr)
+ {
+ opt = find_short_option (po, po->po_chr);
+ long_opt = NULL;
+ }
+ else
+ {
+ long_opt = po->po_cur;
+ opt = find_long_option (po, long_opt, &po->po_cur);
+ }
+
+ if (opt)
+ {
+ char *arg = NULL;
+
+ if (opt->opt_arg)
+ {
+ if (po->po_cur[0])
+ {
+ arg = po->po_cur;
+ po->po_cur = "";
+ }
+ else if (opt->opt_flags & MU_OPTION_ARG_OPTIONAL)
+ /* ignore it */;
+ else if (po->po_ind < po->po_argc)
+ arg = po->po_argv[po->po_ind++];
+ else
+ {
+ if (long_opt)
+ parse_error (po,
+ _("option '--%s' requires an argument"),
+ long_opt);
+ else
+ parse_error (po,
+ _("option '-%c' requires an argument"),
+ po->po_chr);
+ po->po_opterr = po->po_ind;
+ if (po->po_flags & MU_PARSEOPT_NO_ERREXIT)
+ {
+ if (!(po->po_flags & MU_PARSEOPT_IN_ORDER))
+ po->po_arg_count++;
+ continue;
+ }
+ exit (EXIT_ERROR);
+ }
+ }
+ else
+ {
+ if (long_opt
+ && po->po_cur[0]
+ && !(po->po_flags & MU_OPTION_ARG_OPTIONAL))
+ {
+ parse_error (po,
+ _("option '--%s' doesn't allow an argument"),
+ long_opt);
+ po->po_opterr = po->po_ind;
+ if (po->po_flags & MU_PARSEOPT_NO_ERREXIT)
+ {
+ if (!(po->po_flags & MU_PARSEOPT_IN_ORDER))
+ po->po_arg_count++;
+ continue;
+ }
+ exit (EXIT_ERROR);
+ }
+ arg = NULL;
+ }
+
+ add_option_cache (po, opt, arg);
+ }
+ else
+ {
+ po->po_opterr = po->po_ind;
+ if (po->po_flags & MU_PARSEOPT_NO_ERREXIT)
+ {
+ if (!(po->po_flags & MU_PARSEOPT_IN_ORDER))
+ po->po_arg_count++;
+ continue;
+ }
+ exit (EXIT_ERROR);
+ }
+ }
+
+ if (po->po_arg_count)
+ po->po_ind = po->po_arg_start;
+ return 0;
+}
+
+/* Initialize structure mu_parseopt with given options and flags. */
+static int
+parseopt_init (struct mu_parseopt *po, struct mu_option **options,
+ int flags)
+{
+ struct mu_option *opt;
+ size_t i, j;
+
+ memset (po, 0, sizeof *po);
+ po->po_argc = 0;
+ po->po_argv = NULL;
+ po->po_optc = 0;
+ po->po_flags = flags;
+
+ /* Fix up flags */
+ if (flags & MU_PARSEOPT_IGNORE_ERRORS)
+ flags |= MU_PARSEOPT_NO_ERREXIT;
+
+ if (!(flags & MU_PARSEOPT_PROG_DOC))
+ po->po_prog_doc = NULL;
+ if (!(flags & MU_PARSEOPT_PROG_ARGS))
+ po->po_prog_args = NULL;
+ if (!(flags & MU_PARSEOPT_BUG_ADDRESS))
+ po->po_bug_address = NULL;
+ if (!(flags & MU_PARSEOPT_PACKAGE_NAME))
+ po->po_package_name = NULL;
+ if (!(flags & MU_PARSEOPT_PACKAGE_URL))
+ po->po_package_url = NULL;
+ if (!(flags & MU_PARSEOPT_PACKAGE_URL))
+ po->po_data = NULL;
+ if (!(flags & MU_PARSEOPT_HELP_HOOK))
+ po->po_help_hook = NULL;
+
+ /* Count the options */
+ po->po_optc = 0;
+ for (i = 0; options[i]; i++)
+ for (opt = options[i]; !MU_OPTION_IS_END (opt); opt++)
+ ++po->po_optc;
+
+ if (!(flags & MU_PARSEOPT_NO_STDOPT))
+ for (i = 0; !MU_OPTION_IS_END (&mu_default_options[i]); i++)
+ ++po->po_optc;
+
+ /* Allocate the working buffer of option pointers */
+ po->po_optv = mu_calloc (po->po_optc + 1, sizeof (*po->po_optv));
+ if (!po->po_optv)
+ return -1;
+
+ /* Fill in the array */
+ j = 0;
+ for (i = 0; options[i]; i++)
+ for (opt = options[i]; !MU_OPTION_IS_END (opt); opt++, j++)
+ {
+ if (!opt->opt_set)
+ opt->opt_set = mu_option_set_value;
+ po->po_optv[j] = opt;
+ }
+
+ if (!(flags & MU_PARSEOPT_NO_STDOPT))
+ for (i = 0; !MU_OPTION_IS_END (&mu_default_options[i]); i++, j++)
+ po->po_optv[j] = &mu_default_options[i];
+
+ po->po_optv[j] = NULL;
+
+ /* Ensure sane start of options. This is necessary, in particular,
+ because optcmp backs up until it finds an element with cleared
+ MU_OPTION_ALIAS bit. */
+ po->po_optv[0]->opt_flags &= MU_OPTION_ALIAS;
+ if (!(flags & MU_PARSEOPT_NO_SORT))
+ {
+ /* Sort the options */
+ size_t start;
+
+ for (start = 0; start < po->po_optc; )
+ {
+ if (MU_OPTION_IS_GROUP_HEADER (po->po_optv[start]))
+ start = sort_group (po->po_optv, start + 1);
+ else
+ start = sort_group (po->po_optv, start);
+ }
+ }
+ return 0;
+}
+
+/* Parse command line from ARGC/ARGV. Valid options are given in
+ OPTIONS. FLAGS control the parsing. */
+int
+mu_parseopt (struct mu_parseopt *po,
+ int argc, char **argv, struct mu_option **options,
+ int flags)
+{
+ int rc;
+
+ if (flags & MU_PARSEOPT_REUSE)
+ {
+ mu_list_clear (po->po_optlist);
+ po->po_flags = (po->po_flags & MU_PARSEOPT_IMMUTABLE_MASK)
+ | (flags & ~MU_PARSEOPT_IMMUTABLE_MASK);
+ }
+ else
+ {
+ rc = parseopt_init (po, options, flags);
+ if (rc)
+ return rc;
+ }
+ po->po_argc = argc;
+ po->po_argv = argv;
+
+ rc = parse (po);
+
+ if (rc == 0)
+ {
+ if (po->po_opterr >= 0)
+ rc = -1;
+ else
+ {
+ if (po->po_flags & MU_PARSEOPT_IMMEDIATE)
+ rc = mu_parseopt_apply (po);
+ }
+ }
+
+ return rc;
+}
+
+void
+mu_parseopt_free (struct mu_parseopt *popt)
+{
+ free (popt->po_optv);
+ mu_list_destroy (&popt->po_optlist);
+}
+
+static int
+parseopt_apply (void *item, void *data)
+{
+ struct mu_option_cache *cp = item;
+ struct mu_parseopt *popt = data;
+ cp->cache_opt->opt_set (popt, cp->cache_opt, cp->cache_arg);
+ return 0;
+}
+
+int
+mu_parseopt_apply (struct mu_parseopt *popt)
+{
+ return mu_list_foreach (popt->po_optlist, parseopt_apply, popt);
+}
+
+void
+mu_option_set_value (struct mu_parseopt *po, struct mu_option *opt,
+ char const *arg)
+{
+ if (opt->opt_ptr)
+ {
+ char *errmsg;
+ int rc;
+
+ if (arg == NULL)
+ {
+ if (opt->opt_arg == NULL)
+ arg = "1";
+ else
+ {
+ *(void**)opt->opt_ptr = NULL;
+ return;
+ }
+ }
+ rc = mu_str_to_c (arg, opt->opt_type, opt->opt_ptr, &errmsg);
+ if (rc)
+ {
+ char const *errtext;
+ if (errmsg)
+ errtext = errmsg;
+ else
+ errtext = mu_strerror (rc);
+
+ if (opt->opt_long)
+ parse_error (po, "--%s: %s", opt->opt_long, errtext);
+ else
+ parse_error (po, "-%c: %s", opt->opt_short, errtext);
+ free (errmsg);
+
+ if (!(po->po_flags & MU_PARSEOPT_NO_ERREXIT))
+ exit (EXIT_ERROR);
+ }
+ }
+}
diff --git a/libmailutils/opt/progname.c b/libmailutils/opt/progname.c
new file mode 100644
index 000000000..5d9d46b5e
--- /dev/null
+++ b/libmailutils/opt/progname.c
@@ -0,0 +1,43 @@
+/* help.c -- general-purpose command line option parser
+ Copyright (C) 2016 Free Software Foundation, Inc.
+
+ GNU Mailutils 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.
+
+ GNU Mailutils 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 GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <mailutils/alloc.h>
+#include <mailutils/opt.h>
+
+char *mu_progname;
+char *mu_absprogname;
+
+void
+mu_set_progname (char const *arg)
+{
+ char *p;
+
+ free (mu_absprogname);
+ mu_absprogname = mu_strdup (arg);
+
+ p = strrchr (arg, '/');
+ if (p)
+ ++p;
+ else
+ p = (char*) arg;
+ free (mu_progname);
+ mu_progname = mu_strdup (p);
+}
diff --git a/libmailutils/string/Makefile.am b/libmailutils/string/Makefile.am
index a9fbd5645..47e578e62 100644
--- a/libmailutils/string/Makefile.am
+++ b/libmailutils/string/Makefile.am
@@ -25,6 +25,7 @@ libstring_la_SOURCES = \
cstrupper.c\
hexstr.c\
stpcpy.c\
+ str_to_c.c\
strltrim.c\
strskip.c\
stripws.c\
@@ -40,4 +41,6 @@ libstring_la_SOURCES = \
wordsplit.c\
xdecode.c
-AM_CPPFLAGS = @MU_LIB_COMMON_INCLUDES@ -I/libmailutils
+EXTRA_DIST=to_sn.c to_un.c