From b0f10ec7274144b4e048953b11f2a4d98e055810 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Mon, 16 Feb 2009 13:09:28 +0200 Subject: 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. --- bootstrap.conf | 8 +- gconf/gconf-preproc.c | 5 +- gconf/gconf.h | 6 +- src/.gitignore | 1 + src/Makefile.am | 9 +- src/cmdline.opt | 90 +++++++++ src/config.c | 170 +++++++++-------- src/directive.c | 2 +- src/diskio.c | 2 +- src/getopt.m4 | 517 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/update-2.0.awk | 41 +++- src/wydawca.c | 75 +------- src/wydawca.h | 1 + 13 files changed, 760 insertions(+), 167 deletions(-) create mode 100644 src/cmdline.opt create mode 100644 src/getopt.m4 diff --git a/bootstrap.conf b/bootstrap.conf index 1d1a1fa..6119189 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -37,12 +37,8 @@ XGETTEXT_OPTIONS=$XGETTEXT_OPTIONS'\\\ --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 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 @@ -258,7 +258,7 @@ 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; @@ -270,8 +270,7 @@ pp_list_find (gl_list_t list, struct file_data *dptr) 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; diff --git a/gconf/gconf.h b/gconf/gconf.h index 8c04b6d..f88e87a 100644 --- a/gconf/gconf.h +++ b/gconf/gconf.h @@ -101,8 +101,10 @@ 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); 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 @@ -16,6 +16,7 @@ sbin_PROGRAMS=wydawca wydawca_SOURCES=\ + cmdline.h\ config.c\ directive.c\ diskio.c\ @@ -33,7 +34,13 @@ wydawca_SOURCES=\ 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) 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 . */ + +OPTIONS_BEGIN(gnu, "wydawca", + []) + +OPTION(config-file,c,FILE, + []) +BEGIN + conffile = optarg; +END + +OPTION(cron,,, + []) +ALIAS(syslog) +BEGIN + log_to_stderr = 0; +END + +OPTION(stderr,e,, + []) +BEGIN +END + +OPTION(debug,d,, + []) +BEGIN +END + +OPTION(include-directory,I,[DIR], + []) +BEGIN + gconf_preproc_add_include_dir (optarg); +END + +OPTION(dry-run,n,, + []) +BEGIN + log_to_stderr = 1; + debug_level++; + dry_run_mode = 1; +END + +OPTION(lint,t,, + []) +BEGIN + lint_mode = 1; + log_to_stderr = 1; +END + +OPTION(dump-grammar-trace,,, + []) +BEGIN + gconf_gram_trace (1); +END + +OPTION(dump-lex-trace,,, + []) +BEGIN + gconf_lex_trace (1); +END + +OPTION(config-help,,, + []) +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 @@ -218,16 +218,16 @@ 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; @@ -267,9 +267,9 @@ cb_mailer (enum gconf_callback_command cmd, 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); @@ -571,7 +571,7 @@ cb_sql (enum gconf_callback_command cmd, 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; @@ -614,9 +614,6 @@ cb_syslog_facility (enum gconf_callback_command cmd, gconf_value_t *value, void *cb_data) { - struct sqlconn *pconn = varptr; - char *p; - if (assert_string_arg (locus, cmd, value)) return 1; @@ -641,7 +638,7 @@ cb_define_message (enum gconf_callback_command cmd, 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; @@ -735,6 +732,30 @@ get_backup_version (gconf_locus_t *locus, const char *ctx, } } +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, @@ -743,70 +764,60 @@ cb_archive (enum gconf_callback_command cmd, 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; } @@ -943,7 +954,7 @@ cb_access_method_params (enum gconf_callback_command cmd, 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; @@ -954,7 +965,7 @@ cb_access_method_params (enum gconf_callback_command cmd, 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; @@ -1023,7 +1034,7 @@ cb_access_method (enum gconf_callback_command cmd, 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; @@ -1079,11 +1090,9 @@ static struct gconf_keyword directory_kw[] = { { "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_(" []"), - 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 } }; @@ -1101,7 +1110,7 @@ cb_directory (enum gconf_callback_command cmd, 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; @@ -1124,8 +1133,7 @@ cb_directory (enum gconf_callback_command cmd, 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); @@ -1133,8 +1141,7 @@ cb_directory (enum gconf_callback_command cmd, 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); @@ -1202,10 +1209,9 @@ static struct gconf_keyword wydawca_kw[] = { N_("Define message text"), gconf_type_string, NULL, 0, cb_define_message }, - { "archive", - N_(" []"), - 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 }, diff --git a/src/directive.c b/src/directive.c index eeb82c1..ed28cc5 100644 --- a/src/directive.c +++ b/src/directive.c @@ -363,7 +363,7 @@ process_directives (struct file_triplet *trp, struct directory_pair *dpair) 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)); diff --git a/src/diskio.c b/src/diskio.c index 3cffd10..2148abd 100644 --- a/src/diskio.c +++ b/src/diskio.c @@ -643,7 +643,7 @@ symlink_file (struct file_triplet *trp, struct directory_pair *dpair, { logmsg (LOG_ERR, "file %s exists and is not a symbolic link", - dst, strerror (errno)); + dst); rc = 1; } else if (unlink (dst)) 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 . +divert(-1) +changequote([<,>]) +changecom(/*,*/) + +dnl upcase(ARGS...) +dnl Concatenate and convert ARGS to upper case. +dnl +define([], [], [], [])>]) + +dnl concat(ARGS...) +dnl Concatenate arguments, inserting ", " between each of pair of them. +dnl +define([],[],1,[<$1>],[<$1, concat(shift($@))>])>]) + +dnl flushleft(ARGS...) +dnl Concatenate ARGS and remove any leading whitespace +dnl +define([], + [], [<^[ ]+>])>]) + +dnl chop(ARGS...) +dnl Concatenate ARGS and remove any trailing whitespace +dnl +define([], + [], [<[ ]+$>])>]) + +dnl escape(ARGS...) +dnl Concatenate ARGS and escape any occurrences of double-quotes with +dnl backslashes. +dnl +define([], +[],[<[\"]>],[<\\\&>])>]) + +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([], + [],[<[ ]* ++[ ]*>],[< >])))>]) + +dnl SHORT_OPTS +dnl Accumulator for the 3rd argument of getopt_long +dnl +define([],[<>]) + +dnl GROUP(STRING) +dnl Begin a named group of options +dnl +define([],[])") }, +divert(-1)>]) + +define([<__GATHER_OPTIONS>],[< +define([],ifelse([<$2>],,[]upcase(patsubst($1,-,_)),'$2')) +ifelse([<$2>],,[< +divert(1) + KEY, +divert(-1) +>]) +define([],ifdef([],SELECTOR) case KEY:) +ifelse([<$1>],,,[< +divert(2) + { "$1", ARGTYPE, 0, KEY }, +divert(-1)>]) +dnl +define([],SHORT_OPTS[<>]dnl +ifelse([<$2>],,,$2[<>]ifelse(ARGTYPE,[],,ARGTYPE,[],:,ARGTYPE,[],::))) +dnl +ifelse([<$1>],,,dnl +[],ifelse(LONG_TAG,,[<--$1>],[]))>]) +ifelse([<$2>],,,dnl +[],ifelse(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([