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.
This commit is contained in:
		
					parent
					
						
							
								c01106387f
							
						
					
				
			
			
				commit
				
					
						d011957843
					
				
			
		
					 2 changed files with 108 additions and 61 deletions
				
			
		
							
								
								
									
										82
									
								
								src/mcron.c
									
										
									
									
									
								
							
							
						
						
									
										82
									
								
								src/mcron.c
									
										
									
									
									
								
							|  | @ -18,10 +18,13 @@ | |||
|    along with GNU Mcron.  If not, see <http://www.gnu.org/licenses/>.  */
 | ||||
| 
 | ||||
| #include "utils.h" | ||||
| #include <argp.h> | ||||
| #include <libguile.h> | ||||
| 
 | ||||
| /* 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; | ||||
| } | ||||
|  |  | |||
|  | @ -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,27 +74,22 @@ $XDG_CONFIG_HOME is not defined uses ~/.config/cron instead)." | |||
| ;;; Entry point. | ||||
| ;;; | ||||
| 
 | ||||
| (define* (main #:optional (args (command-line))) | ||||
|   (let ((opts (parse-args args %options))) | ||||
| (define* (main #:optional (opts '())) | ||||
|   (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 | ||||
| 
 | ||||
|   (%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 (string->number count)))) | ||||
|               (display (get-schedule (max 1 count))) | ||||
|               (exit 0))) | ||||
|                  ((option-ref opts 'daemon #f) ;run mcron as a daemon | ||||
|         ((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. | ||||
|  | @ -133,4 +100,4 @@ $XDG_CONFIG_HOME is not defined uses ~/.config/cron instead)." | |||
|        ;; 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))))))))) | ||||
|          (process-update-request fdes-list)))))) | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Mathieu Lirzin
				Mathieu Lirzin