aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2009-02-16 13:09:28 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2009-02-16 13:09:28 +0200
commitb0f10ec7274144b4e048953b11f2a4d98e055810 (patch)
tree4bcc06122d183582d0b53fe2b8ba4c87b289b96f
parent169a680208f2de8f92b4a2a4977953d4452e981f (diff)
downloadwydawca-b0f10ec7274144b4e048953b11f2a4d98e055810.tar.gz
wydawca-b0f10ec7274144b4e048953b11f2a4d98e055810.tar.bz2
Improve argument parsing
* src/cmdline.opt, src/getopt.m4: New files. * src/wydawca.c: Switch to new way of command line parsing. * src/wydawca.h (gettext): Define. * bootstrap.conf: Add formatting flags for gconf_warning and gconf_error * gconf-preproc.c (pp_list_find): Minor change. * gconf/gconf.h (gconf_warning,gconf_error): Mark as printflike. * src/.gitignore: Add cmdline.h * src/Makefile.am (wydawca_SOURCES): Add cmdline.h. (.opt.h): New rule. * src/config.c (cb_archive): Archive is now a block statement (unless type "none" is declared). (all functions): Tighten input checking * src/update-2.0.awk: Reflect the above change. * src/directive.c (process_directives): Minor change. * src/diskio.c (symlink_file): Minor change.
-rw-r--r--bootstrap.conf8
-rw-r--r--gconf/gconf-preproc.c5
-rw-r--r--gconf/gconf.h6
-rw-r--r--src/.gitignore1
-rw-r--r--src/Makefile.am9
-rw-r--r--src/cmdline.opt90
-rw-r--r--src/config.c170
-rw-r--r--src/directive.c2
-rw-r--r--src/diskio.c2
-rw-r--r--src/getopt.m4517
-rw-r--r--src/update-2.0.awk41
-rw-r--r--src/wydawca.c75
-rw-r--r--src/wydawca.h1
13 files changed, 760 insertions, 167 deletions
diff --git a/bootstrap.conf b/bootstrap.conf
index 1d1a1fa..6119189 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -34,18 +34,14 @@ gnulib_modules="`grep -h '^[^#]' gnulib.modules gconf/gnulib.modules | sort | un
# Additional xgettext options to use. Use "\\\newline" to break lines.
XGETTEXT_OPTIONS=$XGETTEXT_OPTIONS'\\\
--flag=_:1:pass-c-format\\\
--flag=N_:1:pass-c-format\\\
--flag=error:3:c-format --flag=error_at_line:5:c-format\\\
--flag=asnprintf:3:c-format --flag=vasnprintf:3:c-format\\\
- --flag=argp_error:2:c-format\\\
- --flag=__argp_error:2:c-format\\\
- --flag=argp_failure:4:c-format\\\
- --flag=__argp_failure:4:c-format\\\
- --flag=argp_fmtstream_printf:2:c-format\\\
- --flag=__argp_fmtstream_printf:2:c-format\\\
+ --flag=gconf_warning:3:c-format\\\
+ --flag=gconf_error:3:c-format\\\
'
# Gettext supplies these files, but we don't need them since
# we don't have an intl subdirectory.
excluded_files='
m4/glibc2.m4
diff --git a/gconf/gconf-preproc.c b/gconf/gconf-preproc.c
index af26d1a..eb50474 100644
--- a/gconf/gconf-preproc.c
+++ b/gconf/gconf-preproc.c
@@ -255,26 +255,25 @@ struct file_data
static int
pp_list_find (gl_list_t list, struct file_data *dptr)
{
const void *p;
gl_list_iterator_t itr = gl_list_iterator (list);
- while (gl_list_iterator_next (&itr, &p, NULL))
+ while (!dptr->found && gl_list_iterator_next (&itr, &p, NULL))
{
const char *dir = p;
size_t size = strlen (dir) + 1 + dptr->namelen + 1;
if (size > dptr->buflen)
{
dptr->buflen = size;
dptr->buf = xrealloc (dptr->buf, dptr->buflen);
}
strcpy (dptr->buf, dir);
strcat (dptr->buf, "/");
strcat (dptr->buf, dptr->name);
- if (dptr->found = (access (dptr->buf, F_OK) == 0))
- break;
+ dptr->found = access (dptr->buf, F_OK) == 0;
}
gl_list_iterator_free (&itr);
return dptr->found;
}
gl_list_t
diff --git a/gconf/gconf.h b/gconf/gconf.h
index 8c04b6d..f88e87a 100644
--- a/gconf/gconf.h
+++ b/gconf/gconf.h
@@ -98,14 +98,16 @@ struct gconf_keyword {
};
gconf_value_t *gconf_value_dup(gconf_value_t *input);
extern void gconf_print_diag(gconf_locus_t *, int, int, const char*);
-void gconf_warning(gconf_locus_t *locus, int errcode, const char *fmt, ...);
-void gconf_error(gconf_locus_t *locus, int errcode, const char *fmt, ...);
+void gconf_warning(gconf_locus_t *locus, int errcode, const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+void gconf_error(gconf_locus_t *locus, int errcode, const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
void gconf_set_keywords(struct gconf_keyword *kwd);
void gconf_gram_trace(int n);
void gconf_lex_trace (int n);
int gconf_lex_begin(const char*);
void gconf_lex_end(void);
diff --git a/src/.gitignore b/src/.gitignore
index 2e5d6fc..02f7682 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -1,2 +1,3 @@
.gdbinit
+cmdline.h
wydawca
diff --git a/src/Makefile.am b/src/Makefile.am
index a6611af..10a6b60 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -13,12 +13,13 @@
#
# You should have received a copy of the GNU General Public License
# along with Wydawca. If not, see <http://www.gnu.org/licenses/>.
sbin_PROGRAMS=wydawca
wydawca_SOURCES=\
+ cmdline.h\
config.c\
directive.c\
diskio.c\
exec.c\
gpg.c\
interval.c\
@@ -30,13 +31,19 @@ wydawca_SOURCES=\
verify.c\
wydawca.c\
wydawca.h\
mail.h\
mail.c
-EXTRA_DIST=pp-setup
+BUILT_SOURCES=cmdline.h
+EXTRA_DIST=cmdline.opt getopt.m4 pp-setup
+
+SUFFIXES=.opt .c .h
+
+.opt.h:
+ m4 -s $(srcdir)/getopt.m4 $< | sed '1d' > $@
incdir=$(pkgdatadir)/$(VERSION)/include
inc_DATA = $(PP_SETUP_FILE)
LDADD=../gconf/libgconf.a ../gnu/libgnu.a @SQLLIB@ @GPGMELIB@ @MAILUTILS_LIBS@
INCLUDES = -I$(top_srcdir)/gconf -I$(top_srcdir)/gnu -I../gnu @MAILUTILS_INCLUDES@
diff --git a/src/cmdline.opt b/src/cmdline.opt
new file mode 100644
index 0000000..00bbedc
--- /dev/null
+++ b/src/cmdline.opt
@@ -0,0 +1,90 @@
+/* wydawca - automatic release submission daemon
+ Copyright (C) 2007, 2009 Sergey Poznyakoff
+
+ Wydawca 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.
+
+ Wydawca 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 wydawca. If not, see <http://www.gnu.org/licenses/>. */
+
+OPTIONS_BEGIN(gnu, "wydawca",
+ [<wydawca synchronizes files from a set of upload directories with the corresponding distribution sites>])
+
+OPTION(config-file,c,FILE,
+ [<use FILE instead of the default configuration>])
+BEGIN
+ conffile = optarg;
+END
+
+OPTION(cron,,,
+ [<log to syslog>])
+ALIAS(syslog)
+BEGIN
+ log_to_stderr = 0;
+END
+
+OPTION(stderr,e,,
+ [<log to stderr>])
+BEGIN
+END
+
+OPTION(debug,d,,
+ [<increase debugging level>])
+BEGIN
+END
+
+OPTION(include-directory,I,[DIR],
+ [<add include directory>])
+BEGIN
+ gconf_preproc_add_include_dir (optarg);
+END
+
+OPTION(dry-run,n,,
+ [<do nothing, print almost everything; implies `--debug --stderr',
+ use additional `--debug' options to get even more info>])
+BEGIN
+ log_to_stderr = 1;
+ debug_level++;
+ dry_run_mode = 1;
+END
+
+OPTION(lint,t,,
+ [<parse configuration file and exit>])
+BEGIN
+ lint_mode = 1;
+ log_to_stderr = 1;
+END
+
+OPTION(dump-grammar-trace,,,
+ [<dump configuration grammar traces>])
+BEGIN
+ gconf_gram_trace (1);
+END
+
+OPTION(dump-lex-trace,,,
+ [<dump lexical analyzer traces>])
+BEGIN
+ gconf_lex_trace (1);
+END
+
+OPTION(config-help,,,
+ [<show configuration file summary>])
+BEGIN
+ config_help ();
+ exit (0);
+END
+
+OPTIONS_END
+
+void
+parse_options(int argc, char *argv[])
+{
+ GETOPT(argc, argv)
+}
diff --git a/src/config.c b/src/config.c
index 3545e05..32b54dd 100644
--- a/src/config.c
+++ b/src/config.c
@@ -215,22 +215,22 @@ string_to_notification_target (gconf_locus_t *locus, const char *val,
}
static int
assert_string_arg (gconf_locus_t *locus,
enum gconf_callback_command cmd,
- gconf_value_t *value)
+ const gconf_value_t *value)
{
if (cmd != gconf_callback_set_value)
{
gconf_error (locus, 0, _("Unexpected block statement"));
return 1;
}
- if (value->type != GCONF_TYPE_STRING)
+ if (!value || value->type != GCONF_TYPE_STRING)
{
- gconf_error (locus, 0, _("expected scalar value but found list"));
+ gconf_error (locus, 0, _("expected scalar value as a tag"));
return 1;
}
return 0;
}
gconf_value_t *
@@ -264,15 +264,15 @@ cb_mailer (enum gconf_callback_command cmd,
return 1;
if (cmd != gconf_callback_set_value)
{
gconf_error (locus, 0, _("Unexpected block statement"));
return 1;
}
- if (value->type != GCONF_TYPE_STRING)
+ if (!value || value->type != GCONF_TYPE_STRING)
{
- gconf_error (locus, 0, _("expected scalar value but found list"));
+ gconf_error (locus, 0, _("expected scalar value"));
return 1;
}
rc = mu_mailer_create (&mailer, value->v.string);
if (rc)
gconf_error (locus, 0, _("cannot create mailer `%s': %s"),
value->v.string, mu_strerror (rc));
@@ -568,13 +568,13 @@ cb_sql (enum gconf_callback_command cmd,
{
struct sqlconn *pconn;
void **pdata = cb_data;
switch (cmd) {
case gconf_callback_section_begin:
- if (value->type != GCONF_TYPE_STRING || !value->v.string)
+ if (!value || value->type != GCONF_TYPE_STRING)
{
gconf_error(locus, 0, _("tag must be a string"));
return 0;
}
pconn = xzalloc (sizeof (*pconn));
pconn->ident = strdup (value->v.string);
@@ -611,15 +611,12 @@ static int
cb_syslog_facility (enum gconf_callback_command cmd,
gconf_locus_t *locus,
void *varptr,
gconf_value_t *value,
void *cb_data)
{
- struct sqlconn *pconn = varptr;
- char *p;
-
if (assert_string_arg (locus, cmd, value))
return 1;
if (mu_string_to_syslog_facility (value->v.string, varptr))
gconf_error (locus, 0, _("Unknown syslog facility `%s'"),
value->v.string);
@@ -638,13 +635,13 @@ cb_define_message (enum gconf_callback_command cmd,
if (cmd != gconf_callback_set_value)
{
gconf_error (locus, 0, _("Unexpected block statement"));
return 1;
}
- if (value->type != GCONF_TYPE_ARRAY || value->v.arg.c != 2)
+ if (!value || value->type != GCONF_TYPE_ARRAY || value->v.arg.c != 2)
{
gconf_error (locus, 0, _("expected two arguments"));
return 1;
}
if (value->v.arg.v[0].type != GCONF_TYPE_STRING)
@@ -733,83 +730,97 @@ get_backup_version (gconf_locus_t *locus, const char *ctx,
}
return backup_types[d];
}
}
static int
+cb_backup (enum gconf_callback_command cmd,
+ gconf_locus_t *locus,
+ void *varptr,
+ gconf_value_t *value,
+ void *cb_data)
+{
+ enum backup_type *ptype = varptr;
+
+ if (assert_string_arg (locus, cmd, value))
+ return 1;
+ *ptype = get_backup_version (locus, NULL, value->v.string);
+ return 0;
+}
+
+static struct gconf_keyword archive_kw[] = {
+ { "name", N_("file-or-dir"), N_("Name of archive file or directory"),
+ gconf_type_string, NULL, offsetof(struct archive_descr, name) },
+ { "backup", N_("type"), N_("Define backup type"),
+ gconf_type_string, NULL, offsetof(struct archive_descr, backup_type),
+ cb_backup },
+ { NULL }
+};
+
+static int
cb_archive (enum gconf_callback_command cmd,
gconf_locus_t *locus,
void *varptr,
gconf_value_t *value,
void *cb_data)
{
struct archive_descr *arch = varptr;
- gconf_value_t *argp;
- const char *type;
-
- if (cmd != gconf_callback_set_value)
- {
- gconf_error (locus, 0, _("Unexpected block statement"));
- return 1;
- }
-
- if (value->type != GCONF_TYPE_ARRAY || value->v.arg.c > 3)
- {
- gconf_error (locus, 0, _("expected 1-3 arguments"));
- return 1;
- }
-
- argp = get_arg (locus, value, 0, GCONF_TYPE_STRING);
- if (!argp)
- return 1;
+ void **pdata = cb_data;
- type = argp->v.string;
- if (strcmp (type, "none") == 0)
- {
- arch->type = archive_none;
- if (value->v.arg.c > 1)
- gconf_warning (locus, 0,
- _("rest of line ignored for archive type `none'"));
- }
- else
+ switch (cmd)
{
- argp = get_arg (locus, value, 1, GCONF_TYPE_STRING);
- if (!argp)
- return 1;
- arch->name = safe_file_name (xstrdup (argp->v.string));
- if (!arch->name)
- {
- gconf_error (locus, 0, _("invalid archive name: %s"),
- argp->v.string);
+ case gconf_callback_section_begin:
+ *pdata = arch;
+ /* fallthrough */
+ case gconf_callback_set_value:
+ if (!value)
+ {
+ gconf_error (locus, 0, _("expected tag"));
return 1;
}
-
- if (strcmp (type, "tar") == 0)
+
+ if (value->type != GCONF_TYPE_STRING)
{
- arch->type = archive_tar;
- if (value->v.arg.c > 2)
- gconf_warning (locus, 0,
- _("junk after the archive name ignored"));
+ gconf_error (locus, 0, _("expected scalar value but found list"));
+ return 1;
}
- else if (strcmp (type, "directory") == 0)
+
+ if (strcmp (value->v.string, "none") == 0)
+ arch->type = archive_none;
+ else if (strcmp (value->v.string, "tar") == 0)
+ arch->type = archive_tar;
+ else if (strcmp (value->v.string, "directory") == 0)
+ arch->type = archive_directory;
+ else
{
- arch->type = archive_directory;
- if (value->v.arg.c > 2)
- {
- argp = get_arg (locus, value, 2, GCONF_TYPE_STRING);
- arch->backup_type = get_backup_version (locus, NULL,
- argp->v.string);
- }
- else
- get_backup_version (locus,
- "VERSION_CONTROL environment variable",
- getenv ("VERSION_CONTROL"));
+ gconf_error (locus, 0, _("unknown archive type"));
+ return 1;
}
- else
- gconf_warning (locus, 0, _("unknown archive type"));
+ if (cmd == gconf_callback_section_begin)
+ return 0;
+ break;
+
+ case gconf_callback_section_end:
+ break;
+ }
+
+ if (arch->type == archive_none)
+ return 0;
+
+ if (arch->name == NULL)
+ {
+ gconf_error (locus, 0, _("at least archive name must be set"));
+ return 1;
+ }
+
+ if (arch->type == archive_tar && arch->backup_type != no_backups)
+ {
+ gconf_warning (locus, 0, _("backup type ignored for this archive type"));
+ return 1;
}
+
return 0;
}
static struct gconf_keyword mail_statistics_kw[] = {
{ "message", N_("text"), N_("Message text"),
@@ -940,24 +951,24 @@ cb_access_method_params (enum gconf_callback_command cmd,
if (cmd != gconf_callback_set_value)
{
gconf_error (locus, 0, _("Unexpected block statement"));
return 1;
}
- if (value->type != GCONF_TYPE_LIST)
+ if (!value || value->type != GCONF_TYPE_LIST)
{
gconf_error (locus, 0, _("expected list value"));
return 1;
}
size = gl_list_size (value->v.list);
if (size == 0)
param[0] = param[1] = NULL;
else
{
- gconf_value_t *vp = gl_list_get_at (value->v.list, 0);
+ const gconf_value_t *vp = gl_list_get_at (value->v.list, 0);
if (assert_string_arg (locus, cmd, vp))
return 1;
param[0] = xstrdup (vp->v.string);
if (size > 1)
{
@@ -1020,13 +1031,13 @@ cb_access_method (enum gconf_callback_command cmd,
struct access_method **pmeth, *meth;
void **pdata = cb_data;
enum access_method_id id;
switch (cmd) {
case gconf_callback_section_begin:
- if (value->type != GCONF_TYPE_STRING || !value->v.string)
+ if (!value || value->type != GCONF_TYPE_STRING)
{
gconf_error(locus, 0, _("tag must be a string"));
return 0;
}
if (string_to_access_method_id (locus, value->v.string, &id))
return 1;
@@ -1076,17 +1087,15 @@ static struct gconf_keyword directory_kw[] = {
{ "file-sweep-time", N_("interval"), N_("Define file sweep time"),
gconf_type_string, NULL, offsetof(struct directory_pair, file_sweep_time),
cb_interval },
{ "access-method", N_("ident"), N_("Define access method"),
gconf_type_section, NULL, offsetof(struct directory_pair, access_method),
cb_access_method, NULL, access_method_kw },
- { "archive",
- N_("<type: string> <archive-name: string> [<backup-method: method>]"),
- N_("Set up archivation"),
- gconf_type_string, NULL, offsetof(struct directory_pair, archive),
- cb_archive },
+ { "archive", N_("type: string"), N_("Set up archivation"),
+ gconf_type_section, NULL, offsetof(struct directory_pair, archive),
+ cb_archive, NULL, archive_kw },
{ NULL }
};
static int
cb_directory (enum gconf_callback_command cmd,
gconf_locus_t *locus,
@@ -1098,13 +1107,13 @@ cb_directory (enum gconf_callback_command cmd,
void **pdata = cb_data;
int ec, i;
switch (cmd)
{
case gconf_callback_section_begin:
- if (value->type != GCONF_TYPE_STRING || !value->v.string)
+ if (!value || value->type != GCONF_TYPE_STRING)
{
gconf_error (locus, 0, _("tag must be a string"));
return 1;
}
dpair = xzalloc (sizeof (*dpair));
dpair->url = xstrdup (value->v.string);
@@ -1121,23 +1130,21 @@ cb_directory (enum gconf_callback_command cmd,
gconf_error (locus, 0, _("source is not given"));
else if (!dpair->dest_dir)
gconf_error (locus, 0, _("destination is not given"));
else if (test_dir (dpair->source_dir, &ec))
{
if (ec)
- gconf_error (locus, ec, _("cannot access %s"),
- dpair->source_dir, strerror (ec));
+ gconf_error (locus, ec, _("cannot access %s"), dpair->source_dir);
else
gconf_error (locus, 0, _("%s is not a directory"),
dpair->source_dir);
}
else if (test_dir (dpair->dest_dir, &ec))
{
if (ec)
- gconf_error (locus, ec, _("cannot access %s"),
- dpair->dest_dir, strerror (ec));
+ gconf_error (locus, ec, _("cannot access %s"), dpair->dest_dir);
else
gconf_error (locus, ec, _("%s is not a directory"),
dpair->dest_dir);
}
else if (dpair->access_method[verify_method]->type != method_none
&& dpair->access_method[gpg_key_method]->type != method_none)
@@ -1199,16 +1206,15 @@ static struct gconf_keyword wydawca_kw[] = {
gconf_type_section, NULL, 0, NULL, NULL, syslog_kw },
{ "define-message", N_("ident: string> <text: string"),
N_("Define message text"),
gconf_type_string, NULL, 0, cb_define_message },
- { "archive",
- N_("<type: string> <archive-name: string> [<backup-method: method>]"),
- N_("Set up archivation"),
- gconf_type_string, &default_archive_descr, 0, cb_archive },
+ { "archive", N_("type: string"), N_("Set up archivation"),
+ gconf_type_section, &default_archive_descr, 0,
+ cb_archive, NULL, archive_kw },
{ "mail-statistics", NULL, N_("Send statistics"),
gconf_type_section, NULL, 0, NULL, NULL, mail_statistics_kw },
{ "notify-event", NULL, N_("Configure notification"),
gconf_type_section, NULL, 0, cb_notify_event, NULL, notify_event_kw },
diff --git a/src/directive.c b/src/directive.c
index eeb82c1..ed28cc5 100644
--- a/src/directive.c
+++ b/src/directive.c
@@ -360,13 +360,13 @@ process_directives (struct file_triplet *trp, struct directory_pair *dpair)
case symlink_dir:
{
int argc;
char **argv;
int rc = 0;
- if (rc = argcv_get (val, NULL, NULL, &argc, &argv))
+ if ((rc = argcv_get (val, NULL, NULL, &argc, &argv)))
{
logmsg (LOG_ERR, "cannot parse symlink value `%s': %s",
val, strerror (rc));
return 1;
}
diff --git a/src/diskio.c b/src/diskio.c
index 3cffd10..2148abd 100644
--- a/src/diskio.c
+++ b/src/diskio.c
@@ -640,13 +640,13 @@ symlink_file (struct file_triplet *trp, struct directory_pair *dpair,
if (lstat (dst, &st) == 0)
{
if (!S_ISLNK (st.st_mode))
{
logmsg (LOG_ERR,
"file %s exists and is not a symbolic link",
- dst, strerror (errno));
+ dst);
rc = 1;
}
else if (unlink (dst))
{
logmsg (LOG_ERR,
"Cannot unlink %s: %s",
diff --git a/src/getopt.m4 b/src/getopt.m4
new file mode 100644
index 0000000..d6f5580
--- /dev/null
+++ b/src/getopt.m4
@@ -0,0 +1,517 @@
+dnl This file is part of GNU Rush.
+dnl Copyright (C) 2007, 2008, 2009 Sergey Poznyakoff.
+dnl
+dnl GNU Rush is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation; either version 3, or (at your option)
+dnl any later version.
+dnl
+dnl GNU Rush is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+dnl GNU General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License
+dnl along with GNU Rush. If not, see <http://www.gnu.org/licenses/>.
+divert(-1)
+changequote([<,>])
+changecom(/*,*/)
+
+dnl upcase(ARGS...)
+dnl Concatenate and convert ARGS to upper case.
+dnl
+define([<upcase>], [<translit([<$*>], [<a-z>], [<A-Z>])>])
+
+dnl concat(ARGS...)
+dnl Concatenate arguments, inserting ", " between each of pair of them.
+dnl
+define([<concat>],[<ifelse([<$#>],1,[<$1>],[<$1, concat(shift($@))>])>])
+
+dnl flushleft(ARGS...)
+dnl Concatenate ARGS and remove any leading whitespace
+dnl
+define([<flushleft>],
+ [<patsubst([<concat($*)>], [<^[ ]+>])>])
+
+dnl chop(ARGS...)
+dnl Concatenate ARGS and remove any trailing whitespace
+dnl
+define([<chop>],
+ [<patsubst([<$*>], [<[ ]+$>])>])
+
+dnl escape(ARGS...)
+dnl Concatenate ARGS and escape any occurrences of double-quotes with
+dnl backslashes.
+dnl
+define([<escape>],
+[<patsubst([<concat($*)>],[<[\"]>],[<\\\&>])>])
+
+dnl prep(ARG)
+dnl Prepare ARG for including in C strings: replace newlines with any amount
+dnl of preceding and following whitespace by a single space character, remove
+dnl leading whitespace, and escape double-quotes.
+dnl
+define([<prep>],
+ [<escape(flushleft(patsubst([<$1>],[<[ ]*
++[ ]*>],[< >])))>])
+
+dnl SHORT_OPTS
+dnl Accumulator for the 3rd argument of getopt_long
+dnl
+define([<SHORT_OPTS>],[<>])
+
+dnl GROUP(STRING)
+dnl Begin a named group of options
+dnl
+define([<GROUP>],[<dnl
+divert(3)
+ { NULL, NULL, 0, N_("prep([<$1>])") },
+divert(-1)>])
+
+define([<__GATHER_OPTIONS>],[<
+define([<KEY>],ifelse([<$2>],,[<OPTION_>]upcase(patsubst($1,-,_)),'$2'))
+ifelse([<$2>],,[<
+divert(1)
+ KEY,
+divert(-1)
+>])
+define([<SELECTOR>],ifdef([<SELECTOR>],SELECTOR) case KEY:)
+ifelse([<$1>],,,[<
+divert(2)
+ { "$1", ARGTYPE, 0, KEY },
+divert(-1)>])
+dnl
+define([<SHORT_OPTS>],SHORT_OPTS[<>]dnl
+ifelse([<$2>],,,$2[<>]ifelse(ARGTYPE,[<no_argument>],,ARGTYPE,[<required_argument>],:,ARGTYPE,[<optional_argument>],::)))
+dnl
+ifelse([<$1>],,,dnl
+[<define([<LONG_TAG>],ifelse(LONG_TAG,,[<--$1>],[<LONG_TAG; --$1>]))>])
+ifelse([<$2>],,,dnl
+[<define([<SHORT_TAG>],ifelse(SHORT_TAG,,[<-$2>],[<SHORT_TAG; -$2>]))>])
+>])
+
+dnl OPTION(long-opt, short-opt, [arg], [descr])
+dnl Introduce a command line option. Arguments:
+dnl long-opt Long option.
+dnl short-opt Short option (a single char)
+dnl (At least one of long-opt or short-opt must be present)
+dnl
+dnl Optional arguments:
+dnl arg Option argument.
+dnl descr Option description
+dnl
+dnl If arg is absent, the option does not take any arguments. If arg is
+dnl enclosed in square brackets, the option takes an optional argument.
+dnl Otherwise, the argument is required.
+dnl
+dnl If descr is not given the option will not appear in the --help and
+dnl --usage outputs.
+dnl
+define([<OPTION>],[<
+pushdef([<LONG_TAG>])
+pushdef([<SHORT_TAG>])
+pushdef([<ARGNAME>],[<$3>])
+pushdef([<DOCSTRING>],[<prep([<$4>])>])
+pushdef([<ARGTYPE>],[<ifelse([<$3>],,[<no_argument>],dnl
+patsubst([<$3>],[<\[.*\]>]),,[<optional_argument>],dnl
+[<required_argument>])>])
+__GATHER_OPTIONS($@)
+>])
+
+dnl ALIAS(long-opt, short-opt)
+dnl Declare aliases for the previous OPTION statement.
+dnl long-opt Long option.
+dnl short-opt Short option (a single char)
+dnl (At least one of long-opt or short-opt must be present)
+dnl An OPTION statement may be followed by any number of ALIAS statements.
+dnl
+define([<ALIAS>],[<
+__GATHER_OPTIONS($1,$2)
+>])
+
+dnl BEGIN
+dnl Start an action associated with the declared option. Must follow OPTION
+dnl statement, with optional ALIAS statements in between.
+dnl
+define([<BEGIN>],[<
+ifelse([<DOCSTRING>],,,[<
+divert(3)
+ { "translit(dnl
+ifelse(SHORT_TAG,,LONG_TAG,[<SHORT_TAG[<>]ifelse(LONG_TAG,,,; LONG_TAG)>]),
+ [<;>],[<,>])", ifelse(ARGNAME,,[<NULL, 0>],
+[<ifelse(ARGTYPE,[<optional_argument>],
+[<patsubst(ARGNAME,[<\[\(.*\)\]>],[<N_("\1"), 1>])>],[<N_("ARGNAME"), 0>])>]), N_("DOCSTRING") },
+divert(-1)>])
+popdef([<ARGTYPE>])
+popdef([<ARGNAME>])
+popdef([<DOCSTRING>])
+divert(4)dnl
+popdef([<LONG_TAG>])dnl
+popdef([<SHORT_TAG>])dnl
+ SELECTOR
+ {
+>])
+
+dnl END
+dnl Finish the associated action
+dnl
+define([<END>],[<
+ break;
+ }
+divert(-1)
+undefine([<SELECTOR>])>])
+
+dnl GETOPT(argc, argv, [default])
+dnl Emit option parsing code. Arguments:
+dnl
+dnl argc Name of the 1st argument to getopt_long.
+dnl argv Name of the 2nd argument to getopt_long.
+dnl default Code for the default branch
+dnl
+define([<GETOPT>],[<
+ {
+ int c;
+
+ifelse([<$#>],3,opterr = 0;)
+ while ((c = getopt_long($1, $2, "SHORT_OPTS",
+ long_options, NULL)) != EOF)
+ {
+ switch (c)
+ {
+ default:
+ ifelse([<$#>],3,$3,[<exit(1)>]);
+
+ undivert(4)
+ }
+ }
+ }
+>])
+
+define([<STDFUNC>],[<
+divert(0)
+void print_help(void);
+void print_usage(void);
+divert(5)
+const char *program_version = [<$1>];
+static char doc[] = N_("[<$3>]");
+static char args_doc[] = N_("[<$4>]");
+const char *program_bug_address = "<" PACKAGE_BUGREPORT ">";
+
+#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 || isspace (descr[i]))
+ {
+ if (i > width)
+ break;
+ s = i;
+ if (descr[i] == 0)
+ break;
+ }
+ }
+ printf ("%*.*s\n", s, s, descr);
+ descr += s;
+ if (*descr)
+ {
+ indent (0, lmargin);
+ descr++;
+ }
+ }
+}
+
+void
+print_help(void)
+{
+ unsigned i;
+
+ printf ("%s %s [%s]... %s\n", _("Usage:"), [<$2>], _("[<OPTION>]"),
+ gettext (args_doc));
+ if (doc && doc[0])
+ print_option_descr(gettext (doc), 0, RMARGIN);
+ putchar ('\n');
+
+ for (i = 0; i < sizeof (opthelp) / sizeof (opthelp[0]); i++)
+ {
+ unsigned n;
+ if (opthelp[i].opt)
+ {
+ n = printf (" %s", opthelp[i].opt);
+ if (opthelp[i].arg)
+ {
+ char *cb, *ce, *sep = "";
+ if (opthelp[i].is_optional)
+ {
+ cb = "[";
+ ce = "]";
+ }
+ else
+ cb = ce = "";
+
+ if (strlen (opthelp[i].opt) == 2)
+ {
+ if (!opthelp[i].is_optional)
+ sep = " ";
+ }
+ else
+ sep = "=";
+ n += printf ("%s%s%s%s", cb, sep, gettext (opthelp[i].arg), ce);
+ }
+ if (n >= DESCRCOLUMN)
+ {
+ putchar ('\n');
+ n = 0;
+ }
+ indent (n, DESCRCOLUMN);
+ print_option_descr (gettext (opthelp[i].descr), DESCRCOLUMN, RMARGIN);
+ }
+ else
+ {
+ if (i)
+ putchar ('\n');
+ indent (0, GROUPCOLUMN);
+ print_option_descr (gettext (opthelp[i].descr),
+ GROUPCOLUMN, RMARGIN);
+ putchar ('\n');
+ }
+ }
+
+ putchar ('\n');
+dnl **************************************************************************
+dnl This string cannot be split over several lines, because this would trigger
+dnl a bug in GNU M4 (version 1.4.9 and 1.4.10), which would insert #line
+dnl directives between the lines.
+dnl **************************************************************************
+ print_option_descr (_("Mandatory or optional arguments to long options are also mandatory or optional for any corresponding short options."), 0, RMARGIN);
+ putchar ('\n');
+ printf (_("Report bugs to %s.\n"), program_bug_address);
+}
+
+void
+print_usage(void)
+{
+ unsigned i;
+ int f = 0;
+ unsigned n;
+ char buf[RMARGIN+1];
+
+#define FLUSH dnl
+ do dnl
+ { dnl
+ buf[n] = 0; dnl
+ printf ("%s\n", buf); dnl
+ n = USAGECOLUMN; dnl
+ memset (buf, ' ', n); dnl
+ } dnl
+ while (0)
+#define ADDC(c) dnl
+ do { if (n == RMARGIN) FLUSH; buf[n++] = c; } while (0)
+
+ n = snprintf (buf, sizeof buf, "%s %s ", _("Usage:"), [<$2>]);
+
+ /* Print a list of short options without arguments. */
+ for (i = 0; i < sizeof (opthelp) / sizeof (opthelp[0]); i++)
+ {
+ if (opthelp[i].opt && opthelp[i].descr && opthelp[i].opt[1] != '-'
+ && opthelp[i].arg == NULL)
+ {
+ if (f == 0)
+ {
+ ADDC('[');
+ ADDC('-');
+ f = 1;
+ }
+ ADDC(opthelp[i].opt[1]);
+ }
+ }
+ if (f)
+ ADDC(']');
+
+ /* Print a list of short options with arguments. */
+ for (i = 0; i < sizeof (opthelp) / sizeof (opthelp[0]); i++)
+ {
+ if (opthelp[i].opt && opthelp[i].descr && opthelp[i].opt[1] != '-'
+ && opthelp[i].arg)
+ {
+ size_t len = 5
+ + strlen (opthelp[i].arg)
+ + (opthelp[i].is_optional ? 2 : 1);
+ if (n + len > RMARGIN) FLUSH;
+ buf[n++] = ' ';
+ buf[n++] = '[';
+ buf[n++] = '-';
+ buf[n++] = opthelp[i].opt[1];
+ if (opthelp[i].is_optional)
+ {
+ buf[n++] = '[';
+ strcpy (&buf[n], opthelp[i].arg);
+ n += strlen (opthelp[i].arg);
+ buf[n++] = ']';
+ }
+ else
+ {
+ buf[n++] = ' ';
+ strcpy (&buf[n], opthelp[i].arg);
+ n += strlen (opthelp[i].arg);
+ }
+ buf[n++] = ']';
+ }
+ }