-
Notifications
You must be signed in to change notification settings - Fork 0
/
compare_argument.c
110 lines (103 loc) · 4.3 KB
/
compare_argument.c
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
#include "compare_argument.h"
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
enum option_comparison_result compare_argument(const char option_name_char, const char *option_name, const char **argv, const char ***out_argv, unsigned *out_args_consumed, bool option_arg_optional, const char **out_option_arg) {
if(!argv) {
fprintf(stderr, "compare_argument: internal error: compare_argument received an argv of NULL (please tell the developer)\n");
return option_comparison_error;
}
const char *arg = *argv;
if(!arg) {
fprintf(stderr, "compare_argument: internal error: compare_argument received an empty argv (please tell the developer)\n");
return false;
}
enum option_comparison_result result = option_comparison_error;
const char **orig_argv = argv;
if(*(arg++) != '-') {
//Not an option (options begin with -).
result = option_comparison_notanopt;
} else {
if(*arg == '\0') {
//Special filename for stdio (-).
result = option_comparison_stdio;
} else if(*arg == '-') {
if(*++arg == '\0') {
//Options terminator (--).
result = option_comparison_endofflags;
} else {
if(!option_name) {
//We're not expecting a long option, so fail the match immediately.
result = option_comparison_nomatch;
} else {
//Long option (e.g. --file).
size_t name_len = strlen(option_name);
if(strncmp(arg, option_name, name_len) == 0) {
//It's a match! Look for a value.
if(!out_option_arg) {
if(*arg == '=') {
//The argument value follows.
//Unfortunately, we weren't expecting an argument to this option. Return an error indicating this fact.
result = option_comparison_optargunexpected;
} else if(*arg != '\0') {
//Not a match! We were fooled by restricting the search to name_len characters.
//This argument's option name is longer than that, and the option name that we're looking for is a prefix of it.
//But they are not equal, so this is not a match.
result = option_comparison_nomatch;
}
} else {
arg += name_len;
if(*arg == '=') {
//The argument value follows.
++arg;
} else if(*arg != '\0') {
//Not a match! We were fooled by restricting the search to name_len characters.
//This argument's option name is longer than that, and the option name that we're looking for is a prefix of it.
//But they are not equal, so this is not a match.
result = option_comparison_nomatch;
} else {
//If there's no arg after this, then that arg will be NULL (per C99), which is what we want to return through out_option_arg in that case.
arg = *++argv;
if(option_arg_optional && arg && (*arg == '-')) {
//The option arg for our option is optional, and the arg that we found for it looks like another option. Return no option arg (NULL).
arg = NULL;
//If option_arg_optional was false, we would accept this arg even though it looks like an option (because we would not even check whether it looks like an option).
}
}
*out_option_arg = arg;
}
result = option_comparison_longopt;
} else {
//No match.
result = option_comparison_nomatch;
}
}
}
} else if(option_name_char && *arg == option_name_char) {
//Short option (e.g. -f).
if(out_option_arg) {
if(*arg != '\0') ++arg;
if(*arg == '\0') {
//Expect two args; e.g. -f filename.
//If there's no arg after this, then that arg will be NULL (per C99), which is what we want to return through out_option_arg in that case.
arg = *++argv;
if(option_arg_optional && arg && (*arg == '-')) {
//The option arg for our option is optional, and the arg that we found for it looks like another option. Return no option arg (NULL).
arg = NULL;
//If option_arg_optional was false, we would accept this arg even though it looks like an option (because we would not even check whether it looks like an option).
}
}
*out_option_arg = arg;
}
result = option_comparison_shortopt;
}
}
if(out_args_consumed || out_argv)
argv = (result > option_comparison_nomatch) ? ++argv : orig_argv;
if(out_argv)
*out_argv = argv;
if(out_args_consumed)
*out_args_consumed = (unsigned int)(ptrdiff_t)(argv - orig_argv);
return result;
}