You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
299 lines
6.7 KiB
299 lines
6.7 KiB
#include <stdio.h>
|
|
|
|
#include "cmdopt.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif // __cplusplus
|
|
|
|
// Moves `optind' to the end and shifts other arguments.
|
|
static void cmdopt_shift(cmdopt_t *h) {
|
|
int i;
|
|
char *tmp;
|
|
|
|
tmp = h->argv[h->optind];
|
|
for (i = h->optind; i < h->argc - 1; i++) {
|
|
h->argv[i] = h->argv[i + 1];
|
|
}
|
|
h->argv[i] = tmp;
|
|
|
|
h->nextchar = NULL;
|
|
h->optnum--;
|
|
}
|
|
|
|
// Moves to the next argument.
|
|
static void cmdopt_next(cmdopt_t *h) {
|
|
h->optind++;
|
|
h->nextchar = NULL;
|
|
}
|
|
|
|
// Checks if the current argument is an option or not.
|
|
static int cmdopt_check(cmdopt_t *h) {
|
|
int ret = 1;
|
|
const char *arg = h->argv[h->optind];
|
|
|
|
if (*arg++ != '-') {
|
|
return 0;
|
|
}
|
|
|
|
if (*arg == '-') {
|
|
arg++;
|
|
ret++;
|
|
}
|
|
|
|
return ret - (*arg == '\0');
|
|
}
|
|
|
|
// Gets an argument of the current option.
|
|
static void cmdopt_getopt(cmdopt_t *h) {
|
|
// Moves to the next argument if the current argument has no more characters.
|
|
if (*h->nextchar == '\0') {
|
|
cmdopt_next(h);
|
|
h->nextchar = h->argv[h->optind];
|
|
}
|
|
|
|
// Checks whether the current option has an argument or not.
|
|
if (h->optind < h->optnum) {
|
|
h->optarg = h->nextchar;
|
|
cmdopt_next(h);
|
|
} else {
|
|
h->optarg = NULL;
|
|
}
|
|
}
|
|
|
|
// Searches an option.
|
|
static int cmdopt_search(cmdopt_t *h) {
|
|
const char *ptr;
|
|
|
|
// Updates an option character.
|
|
h->optopt = *h->nextchar++;
|
|
|
|
for (ptr = h->optstring; *ptr != '\0'; ptr++) {
|
|
if (*ptr == h->optopt) {
|
|
// Gets an option argument if required.
|
|
if (ptr[1] == ':') {
|
|
cmdopt_getopt(h);
|
|
|
|
// Returns ':' if there is no argument.
|
|
if (h->optarg == NULL && ptr[2] != ':') {
|
|
return ':';
|
|
}
|
|
}
|
|
return h->optopt;
|
|
}
|
|
}
|
|
|
|
if (h->optopt == '-') {
|
|
cmdopt_next(h);
|
|
while (h->optind < h->optnum) {
|
|
cmdopt_shift(h);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// Returns '?' if the option character is undefined.
|
|
return '?';
|
|
}
|
|
|
|
// Compares a long option with an argument and returns the length of the
|
|
// matched prefix.
|
|
static int cmdopt_match_len(const char *opt, const char *arg) {
|
|
int len = 0;
|
|
|
|
// Returns 0 if there is a mismatch.
|
|
while ((*arg != '\0') && (*arg != '=')) {
|
|
if (*arg++ != *opt++) {
|
|
return 0;
|
|
}
|
|
len++;
|
|
}
|
|
|
|
// Returns a negative value in case of a perfect match.
|
|
if ((*arg == '\0') || (*arg == '=')) {
|
|
return -len;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
// Checks long options.
|
|
static int cmdopt_match(cmdopt_t *h) {
|
|
int i, len;
|
|
int max = 0, max_optind = -1;
|
|
|
|
// Returns -1 if there are no long options.
|
|
if (h->longopts == NULL) {
|
|
return max_optind;
|
|
}
|
|
|
|
for (i = 0; h->longopts[i].name != NULL; i++) {
|
|
len = cmdopt_match_len(h->longopts[i].name, h->nextchar);
|
|
if (len < 0) {
|
|
// In case of a perfect match.
|
|
h->nextchar -= len;
|
|
return i;
|
|
} else if (len > max) {
|
|
// In case of a prefix match.
|
|
max = len;
|
|
max_optind = i;
|
|
} else if (len == max) {
|
|
// There are other candidates.
|
|
max_optind = -1;
|
|
}
|
|
}
|
|
|
|
// If there is no perfect match, adopts the longest one.
|
|
h->nextchar += max;
|
|
return max_optind;
|
|
}
|
|
|
|
// Gets an argument of a long option.
|
|
static void cmdopt_getopt_long(cmdopt_t *h) {
|
|
if (*h->nextchar == '=') {
|
|
h->optarg = h->nextchar + 1;
|
|
cmdopt_next(h);
|
|
} else {
|
|
cmdopt_next(h);
|
|
|
|
// Checks whether there are more options or not.
|
|
if (h->optind < h->optnum) {
|
|
h->optarg = h->argv[h->optind];
|
|
cmdopt_next(h);
|
|
} else {
|
|
h->optarg = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Searches long options.
|
|
static int cmdopt_search_long(cmdopt_t *h) {
|
|
const cmdopt_option *option;
|
|
|
|
// Keeps the long option.
|
|
h->optlong = h->argv[h->optind];
|
|
|
|
// Gets the next option.
|
|
h->longindex = cmdopt_match(h);
|
|
if (h->longindex < 0) {
|
|
cmdopt_next(h);
|
|
return '?';
|
|
}
|
|
|
|
// Gets an argument if required.
|
|
option = h->longopts + h->longindex;
|
|
if (option->has_arg) {
|
|
cmdopt_getopt_long(h);
|
|
|
|
// Return ':' if there are no more arguments.
|
|
if (h->optarg == NULL) {
|
|
return ':';
|
|
}
|
|
} else if (*h->nextchar == '=') {
|
|
// Returns '?' for an extra option argument.
|
|
cmdopt_getopt_long(h);
|
|
return '?';
|
|
}
|
|
|
|
// Overwrites a variable if specified in settings.
|
|
if (option->flag != NULL) {
|
|
*option->flag = option->val;
|
|
return 0;
|
|
}
|
|
|
|
return option->val;
|
|
}
|
|
|
|
// Analyze command line option.
|
|
static int cmdopt_main(cmdopt_t *h) {
|
|
int type;
|
|
|
|
// Initializes the internal state.
|
|
h->optopt = 0;
|
|
h->optlong = NULL;
|
|
h->optarg = NULL;
|
|
h->longindex = 0;
|
|
|
|
while (h->optind < h->optnum) {
|
|
if (h->nextchar == NULL) {
|
|
// Checks whether the next argument is an option or not.
|
|
type = cmdopt_check(h);
|
|
if (type == 0) {
|
|
cmdopt_shift(h);
|
|
} else {
|
|
h->nextchar = h->argv[h->optind] + type;
|
|
if (type == 2) {
|
|
return cmdopt_search_long(h);
|
|
}
|
|
}
|
|
} else {
|
|
if (*h->nextchar == '\0') {
|
|
cmdopt_next(h);
|
|
continue;
|
|
}
|
|
// Searches an option string.
|
|
return cmdopt_search(h);
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// cmdopt_init() initializes a cmdopt_t for successive cmdopt_get()s.
|
|
void cmdopt_init(cmdopt_t *h, int argc, char **argv,
|
|
const char *optstring, const cmdopt_option *longopts) {
|
|
static const char empty_optstring[] = "";
|
|
|
|
h->argc = argc;
|
|
h->argv = argv;
|
|
h->optnum = h->argc;
|
|
|
|
h->longopts = longopts;
|
|
h->optstring = (optstring != NULL) ? optstring : empty_optstring;
|
|
|
|
h->optind = 1;
|
|
h->nextchar = NULL;
|
|
h->optarg = NULL;
|
|
h->optopt = 0;
|
|
h->optlong = NULL;
|
|
h->opterr = 1;
|
|
h->longindex = 0;
|
|
}
|
|
|
|
// cmdopt_get() analyzes command line arguments and gets the next option.
|
|
int cmdopt_get(cmdopt_t *h) {
|
|
int value = cmdopt_main(h);
|
|
|
|
// Prints a warning to the standard error stream if enabled.
|
|
if (h->opterr) {
|
|
if (value == ':') {
|
|
// Warning for a lack of an option argument.
|
|
if (h->optlong == NULL) {
|
|
fprintf(stderr, "option requires an argument -- %c\n", h->optopt);
|
|
} else {
|
|
fprintf(stderr, "option `--%s' requires an argument\n",
|
|
h->longopts[h->longindex].name);
|
|
}
|
|
} else if (value == '?') {
|
|
// Warning for an invalid option.
|
|
if (h->optlong == NULL) {
|
|
fprintf(stderr, "invalid option -- %c\n", h->optopt);
|
|
} else {
|
|
fprintf(stderr, "unrecognized option `%s'\n", h->optlong);
|
|
}
|
|
} else if ((value != -1) && (h->opterr == 2)) {
|
|
// Actually this is not for warning, but for debugging.
|
|
if (h->optlong == NULL) {
|
|
fprintf(stderr, "option with `%s' -- %c\n", h->optarg, h->optopt);
|
|
} else {
|
|
fprintf(stderr, "option `--%s' with `%s'\n",
|
|
h->longopts[h->longindex].name, h->optarg);
|
|
}
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
} // extern "C"
|
|
#endif // __cplusplus
|