diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-02-16 13:09:28 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-02-16 13:09:28 +0200 |
commit | b0f10ec7274144b4e048953b11f2a4d98e055810 (patch) | |
tree | 4bcc06122d183582d0b53fe2b8ba4c87b289b96f | |
parent | 169a680208f2de8f92b4a2a4977953d4452e981f (diff) | |
download | wydawca-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.conf | 8 | ||||
-rw-r--r-- | gconf/gconf-preproc.c | 5 | ||||
-rw-r--r-- | gconf/gconf.h | 6 | ||||
-rw-r--r-- | src/.gitignore | 1 | ||||
-rw-r--r-- | src/Makefile.am | 9 | ||||
-rw-r--r-- | src/cmdline.opt | 90 | ||||
-rw-r--r-- | src/config.c | 170 | ||||
-rw-r--r-- | src/directive.c | 2 | ||||
-rw-r--r-- | src/diskio.c | 2 | ||||
-rw-r--r-- | src/getopt.m4 | 517 | ||||
-rw-r--r-- | src/update-2.0.awk | 41 | ||||
-rw-r--r-- | src/wydawca.c | 75 | ||||
-rw-r--r-- | src/wydawca.h | 1 |
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++] = ']'; + } + } |