aboutsummaryrefslogtreecommitdiff
path: root/src/options.c
blob: a4dbcb9b1a3b9bf5610c3c7b2fcfdc296a31bcdf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "options.h"
#include "print.h"

/* This header file is automatically generated at build time from the Makefile
 */
#include "program_info.h"

#define OPTIONS_DEFAULT_SECTOR_SIZE 512
#define OPTIONS_DEFAULT_BUFFER_SIZE (4 * 1024 * 1024)

void options_init(options_t *const opts);
int options_parse_unsigned(const char *const arg, uint32_t *const value);

int
options_parse(int argc, char **argv, options_t *const opts)
{
    options_init(opts);

    int ch;
    char *arg_sector_size = NULL;
    char *arg_buffer_size = NULL;

    while ((ch = getopt(argc, argv, ":b:hs:")) != -1) {
        switch (ch) {
        case 'b':
            arg_buffer_size = optarg;
            break;

        case 'h':
            opts->help = true;
            break;

        case 's':
            arg_sector_size = optarg;
            break;

        case ':':
            print_error("missing argument for option '-%c'", optopt);
            return 1;

        default:
            print_error("unknown option '-%c'", optopt);
            return 1;
        }
    }

    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;
    }

    /* 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;
    } else if ((arg_buffer_size != NULL) &&
               options_parse_unsigned(arg_buffer_size, &(opts->buffer_size))) {
        print_error("incorrect buffer size");
        return 1;
    } else if (opts->sector_size == 0) {
        print_error("sector size cannot be 0");
        return 1;
    } else if (opts->buffer_size == 0) {
        print_error("buffer size cannot be 0");
        return 1;
    } else if (opts->sector_size > opts->buffer_size) {
        print_error("sector size cannot larger than buffer size");
        return 1;
    } else if ((opts->buffer_size % opts->sector_size) != 0) {
        print_error("buffer size is not multiple of sector size");
        return 1;
    }

    /* 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;
}

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);
}

void
options_init(options_t *const opts)
{
    opts->help = false;
    opts->sector_size = OPTIONS_DEFAULT_SECTOR_SIZE;
    opts->buffer_size = OPTIONS_DEFAULT_BUFFER_SIZE;

    opts->in_file_path = NULL;
    opts->ref_file_path = NULL;
    opts->out_file_path = NULL;
}

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;
}