From 948a65cf39c8ce31c5adc0f24979e0cb55bc33c3 Mon Sep 17 00:00:00 2001 From: Jan Sucan Date: Sat, 18 Jun 2022 08:50:45 +0200 Subject: Refactor the command line interface Select operation by its name instead of by number of the arguments. --- src/options.c | 222 +++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 165 insertions(+), 57 deletions(-) (limited to 'src/options.c') diff --git a/src/options.c b/src/options.c index 002b36c..e2fcfdb 100644 --- a/src/options.c +++ b/src/options.c @@ -38,22 +38,126 @@ */ #include "program_info.h" +#define OPTIONS_MAX_OPERATION_NAME_LENGTH 8 #define OPTIONS_DEFAULT_SECTOR_SIZE 512 #define OPTIONS_DEFAULT_BUFFER_SIZE (4 * 1024 * 1024) -static void options_init(options_t *const opts); +struct common_options { + bool help; + uint32_t sector_size; + uint32_t buffer_size; +}; + static int options_parse_unsigned(const char *const arg, uint32_t *const value); +static void options_parse_operation(const char *const argv, + options_t *const opts); +static bool options_parse_common(int *const argc, char ***const argv, + struct common_options *const opts); +static bool options_parse_backup(int *const argc, char ***const argv, + options_t *const opts); +static bool options_parse_restore(int *const argc, char ***const argv, + options_t *const opts); -int +bool options_parse(int argc, char **argv, options_t *const opts) { - options_init(opts); + // Skip the executable name + --argc; + ++argv; + + options_parse_operation(argv[0], opts); + + if (options_is_operation(opts, OPERATION_ID_UNKNOWN)) { + return false; + } + + if (options_is_operation(opts, OPERATION_ID_BACKUP)) { + if (!options_parse_backup(&argc, &argv, opts)) { + return false; + } + } else if (options_is_operation(opts, OPERATION_ID_RESTORE)) { + if (!options_parse_restore(&argc, &argv, opts)) { + return false; + } + } + return true; +} + +void +options_usage(int exit_code) +{ + printf("Usage: %s [-s SECTOR_SIZE] [-b BUFFER_SIZE] [INFILE] REFFILE " + "OUTFILE\n", + PROGRAM_NAME_STR); + exit(exit_code); +} + +bool +options_is_operation(const options_t *const opts, operation_id_t operation_id) +{ + return (opts->operation_id == operation_id); +} + +const options_backup_t * +options_get_for_backup(const options_t *const opts) +{ + return &(opts->op.backup); +} + +const options_restore_t * +options_get_for_restore(const options_t *const opts) +{ + return &(opts->op.restore); +} + +static int +options_parse_unsigned(const char *const arg, uint32_t *const value) +{ + char *end; + + errno = 0; + + *value = strtoul(arg, &end, 0); + + return ((*end != '\0') || (errno != 0)) ? -1 : 0; +} + +static void +options_parse_operation(const char *const op_name, options_t *const opts) +{ + if (op_name == NULL) { + opts->operation_id = OPERATION_ID_UNKNOWN; + } else if (strncmp(op_name, "help", OPTIONS_MAX_OPERATION_NAME_LENGTH) == + 0) { + opts->operation_id = OPERATION_ID_HELP; + } else if (strncmp(op_name, "backup", OPTIONS_MAX_OPERATION_NAME_LENGTH) == + 0) { + opts->operation_id = OPERATION_ID_BACKUP; + } else if (strncmp(op_name, "restore", OPTIONS_MAX_OPERATION_NAME_LENGTH) == + 0) { + opts->operation_id = OPERATION_ID_RESTORE; + } else { + opts->operation_id = OPERATION_ID_UNKNOWN; + } +} + +static void +options_init_common(struct common_options *const opts) +{ + opts->help = false; + opts->sector_size = OPTIONS_DEFAULT_SECTOR_SIZE; + opts->buffer_size = OPTIONS_DEFAULT_BUFFER_SIZE; +} +static bool +options_parse_common(int *const argc, char ***const argv, + struct common_options *const opts) +{ int ch; char *arg_sector_size = NULL; char *arg_buffer_size = NULL; - while ((ch = getopt(argc, argv, ":b:hs:")) != -1) { + while ((ch = getopt(*argc, *argv, ":b:hs:")) != -1) { switch (ch) { case 'b': arg_buffer_size = optarg; @@ -69,90 +173,94 @@ options_parse(int argc, char **argv, options_t *const opts) case ':': print_error("missing argument for option '-%c'", optopt); - return 1; - + return false; default: print_error("unknown option '-%c'", optopt); - return 1; + return false; } } - argc -= optind; - argv += optind; - - if (opts->help) { - return 0; - } else if (argc < 2) { - print_error("missing arguments"); - return 1; - } else if (argc > 3) { - print_error("too many arguments"); - return 1; - } + *argc -= optind; + *argv += optind; /* Convert numbers in the arguments */ if ((arg_sector_size != NULL) && options_parse_unsigned(arg_sector_size, &(opts->sector_size))) { print_error("incorrect sector size"); - return 1; + return false; } else if ((arg_buffer_size != NULL) && options_parse_unsigned(arg_buffer_size, &(opts->buffer_size))) { print_error("incorrect buffer size"); - return 1; + return false; } else if (opts->sector_size == 0) { print_error("sector size cannot be 0"); - return 1; + return false; } else if (opts->buffer_size == 0) { print_error("buffer size cannot be 0"); - return 1; + return false; } else if (opts->sector_size > opts->buffer_size) { print_error("sector size cannot larger than buffer size"); - return 1; + return false; } else if ((opts->buffer_size % opts->sector_size) != 0) { print_error("buffer size is not multiple of sector size"); - return 1; + return false; } - /* Pick the file paths */ - int last_path_index = argc - 1; - opts->out_file_path = argv[last_path_index--]; - opts->ref_file_path = argv[last_path_index--]; - if (last_path_index >= 0) { - opts->in_file_path = argv[last_path_index]; - } - - return 0; + return true; } -void -options_usage(int exit_code) +static bool +options_parse_backup(int *const argc, char ***const argv, options_t *const opts) { - printf("Usage: %s [-s SECTOR_SIZE] [-b BUFFER_SIZE] [INFILE] REFFILE " - "OUTFILE\n", - PROGRAM_NAME_STR); - exit(exit_code); -} + struct common_options common_opts; + options_init_common(&common_opts); + if (!options_parse_common(argc, argv, &common_opts)) { + return false; + } -static void -options_init(options_t *const opts) -{ - opts->help = false; - opts->sector_size = OPTIONS_DEFAULT_SECTOR_SIZE; - opts->buffer_size = OPTIONS_DEFAULT_BUFFER_SIZE; + if (common_opts.help) { + opts->operation_id = OPERATION_ID_HELP; + } else if (*argc < 3) { + print_error("missing arguments"); + return false; + } else if (*argc > 3) { + print_error("too many arguments"); + return false; + } else { + opts->op.backup.sector_size = common_opts.sector_size; + opts->op.backup.buffer_size = common_opts.buffer_size; + opts->op.backup.in_file_path = (*argv)[0]; + opts->op.backup.ref_file_path = (*argv)[1]; + opts->op.backup.out_file_path = (*argv)[2]; + } - opts->in_file_path = NULL; - opts->ref_file_path = NULL; - opts->out_file_path = NULL; + return true; } -static int -options_parse_unsigned(const char *const arg, uint32_t *const value) +static bool +options_parse_restore(int *const argc, char ***const argv, + options_t *const opts) { - char *end; - - errno = 0; + struct common_options common_opts; + options_init_common(&common_opts); + if (!options_parse_common(argc, argv, &common_opts)) { + return false; + } - *value = strtoul(arg, &end, 0); + if (common_opts.help) { + opts->operation_id = OPERATION_ID_HELP; + } else if (*argc < 2) { + print_error("missing arguments"); + return false; + } else if (*argc > 2) { + print_error("too many arguments"); + return false; + } else { + opts->op.restore.sector_size = common_opts.sector_size; + opts->op.restore.buffer_size = common_opts.buffer_size; + opts->op.restore.in_file_path = (*argv)[0]; + opts->op.restore.out_file_path = (*argv)[1]; + } - return ((*end != '\0') || (errno != 0)) ? -1 : 0; + return true; } -- cgit v1.2.3