#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