From d01195784352b29fe13d0676a2f50d60371d007f Mon Sep 17 00:00:00 2001 From: Mathieu Lirzin Date: Tue, 25 Apr 2017 16:32:49 +0200 Subject: mcron: Handle command line arguments in C with argp 'argp' is a convenient and maintainable way to parse command line arguments. Guile doesn't offer an equivalent of this, so the command line handling has been moved to C. * src/mcron.c (parse_args, parse_opt): New functions. (inner_main): Call 'parse_args'. * src/mcron/scripts/mcron.scm (show-help, %options): Delete. (main): Remove command line handling. --- src/mcron.c | 82 +++++++++++++++++++++++++++++++++++++++++- src/mcron/scripts/mcron.scm | 87 ++++++++++++++------------------------------- 2 files changed, 108 insertions(+), 61 deletions(-) (limited to 'src') diff --git a/src/mcron.c b/src/mcron.c index 43afc72..5c63c4b 100644 --- a/src/mcron.c +++ b/src/mcron.c @@ -18,10 +18,13 @@ along with GNU Mcron. If not, see . */ #include "utils.h" +#include #include /* Forward declarations. */ static void inner_main (void *closure, int argc, char *argv[]); +static SCM parse_args (int argc, char *argv[]); +static error_t parse_opt (int key, char *arg, struct argp_state *state); int main (int argc, char *argv[]) @@ -44,6 +47,83 @@ main (int argc, char *argv[]) static void inner_main (void *closure, int argc, char *argv[]) { + SCM config = parse_args (argc, argv); scm_set_current_module (scm_c_resolve_module ("mcron scripts mcron")); - scm_call_0 (scm_variable_ref (scm_c_lookup ("main"))); + scm_call_1 (scm_variable_ref (scm_c_lookup ("main")), config); +} + +/* Handle command line arguments. */ +static SCM +parse_args (int argc, char *argv[]) +{ + static struct argp_option options[] = { + {"schedule", 's', "N", 0, + "Display the next N jobs that will be run"}, + {"daemon", 'd', 0, 0, + "Run as a daemon process"}, + {"stdin", 'i', "FORMAT", 0, + "Format of data passed as standard input or file arguments (default guile)"}, + {0, 0, 0, 0, 0} + }; + + static struct argp argp = { + .options = options, + .parser = parse_opt, + .args_doc = "[FILE...]", + .doc = "Run an mcron process according to the specifications in the " + "FILE... (`-' for standard input), or use all the files in " + "~/.config/cron (or the deprecated ~/.cron) with .guile or " + ".vixie extensions." + }; + + SCM config = SCM_EOL; + argp_program_version = PACKAGE_STRING; + argp_program_bug_address = PACKAGE_BUGREPORT; + argp_parse (&argp, argc, argv, 0, NULL, &config); + + return config; +} + +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + SCM *config = state->input; + + switch (key) + { + case 's': + *config = scm_assq_set_x (*config, scm_from_utf8_symbol ("schedule"), + scm_from_int (atoi (arg))); + break; + case 'd': + *config = scm_assq_set_x (*config, scm_from_utf8_symbol ("daemon"), + SCM_BOOL_T); + break; + case 'i': + if (strncmp (arg, "vixie", 6) == 0) + *config = scm_assq_set_x (*config, scm_from_utf8_symbol ("vixie"), + SCM_BOOL_T); + break; + case ARGP_KEY_NO_ARGS: + *config = scm_assq_set_x (*config, scm_from_utf8_symbol ("files"), + SCM_EOL); + break; + case ARGP_KEY_ARGS: + { + SCM lst = SCM_EOL; + int filec = state->argc - state->next; + char **filev = state->argv + state->next; + + for (int i = filec - 1; i >= 0; i--) + lst = scm_cons (scm_from_locale_string (filev[i]), lst); + + *config = scm_assq_set_x (*config, scm_from_utf8_symbol ("files"), + lst); + break; + } + case ARGP_KEY_ARG: + default: + return ARGP_ERR_UNKNOWN; + } + return 0; } diff --git a/src/mcron/scripts/mcron.scm b/src/mcron/scripts/mcron.scm index b6c7729..588734b 100644 --- a/src/mcron/scripts/mcron.scm +++ b/src/mcron/scripts/mcron.scm @@ -25,34 +25,6 @@ #:use-module (mcron vixie-specification) #:export (main)) -(define (show-help) - (display "Usage: mcron [OPTIONS] [FILES] -Run an mcron process according to the specifications in the FILES (`-' for -standard input), or use all the files in ~/.config/cron (or the -deprecated ~/.cron) with .guile or .vixie extensions. - - -v, --version Display version - -h, --help Display this help message - -sN, --schedule[=]N Display the next N jobs that will be run by mcron - -d, --daemon Immediately detach the program from the terminal - and run as a daemon process - -i, --stdin=(guile|vixie) Format of data passed as standard input or - file arguments (default guile)") - (newline) - (show-package-information)) - -(define %options - `((schedule (single-char #\s) (value #t) - (predicate ,(λ (str) (string->number str)))) - (daemon (single-char #\d) (value #f)) - (noetc (single-char #\n) (value #f)) - (stdin (single-char #\i) (value #t) - (predicate ,(λ (val) - (or (string=? val "guile") - (string=? val "vixie"))))) - (version (single-char #\v) (value #f)) - (help (single-char #\h) (value #f)))) - (define process-user-file (let ((guile-regexp (make-regexp "\\.gui(le)?$")) (vixie-regexp (make-regexp "\\.vix(ie)?$"))) @@ -102,35 +74,30 @@ $XDG_CONFIG_HOME is not defined uses ~/.config/cron instead)." ;;; Entry point. ;;; -(define* (main #:optional (args (command-line))) - (let ((opts (parse-args args %options))) - (when config-debug - (debug-enable 'backtrace)) - (cond ((option-ref opts 'help #f) - (show-help) - (exit 0)) - ((option-ref opts 'version #f) - (show-version "mcron") - (exit 0)) - (else - (%process-files (option-ref opts '() '()) - (option-ref opts 'stdin "guile")) - (cond ((option-ref opts 'schedule #f) ;display jobs schedule - => (λ (count) - (display (get-schedule (max 1 (string->number count)))) - (exit 0))) - ((option-ref opts 'daemon #f) ;run mcron as a daemon - (case (primitive-fork) - ((0) (setsid)) - (else (exit 0))))) - ;; Forever execute the 'run-job-loop', and when it drops out (can - ;; only be because a message has come in on the socket) we process - ;; the socket request before restarting the loop again. - (catch-mcron-error - (let ((fdes-list '())) - (while #t - (run-job-loop fdes-list) - ;; we can also drop out of run-job-loop because of a SIGCHLD, - ;; so must test FDES-LIST. - (unless (null? fdes-list) - (process-update-request fdes-list))))))))) +(define* (main #:optional (opts '())) + (when config-debug + (debug-enable 'backtrace)) + + (%process-files (or (assq-ref opts 'files) '()) + (if (assq-ref opts 'vixie) "vixie" "guile")) + + (cond ((assq-ref opts 'schedule) ;display jobs schedule + => (λ (count) + (display (get-schedule (max 1 count))) + (exit 0))) + ((assq-ref opts 'daemon) ;run mcron as a daemon + (case (primitive-fork) + ((0) (setsid)) + (else (exit 0))))) + + ;; Forever execute the 'run-job-loop', and when it drops out (can + ;; only be because a message has come in on the socket) we process + ;; the socket request before restarting the loop again. + (catch-mcron-error + (let ((fdes-list '())) + (while #t + (run-job-loop fdes-list) + ;; we can also drop out of run-job-loop because of a SIGCHLD, + ;; so must test FDES-LIST. + (unless (null? fdes-list) + (process-update-request fdes-list)))))) -- cgit v1.2.3