| #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 |