From 875dc2d8636555b53b99ace11c703c13971555af Mon Sep 17 00:00:00 2001 From: Ilya Pogodaev Date: Mon, 10 Jun 2019 09:14:39 +0300 Subject: [PATCH 1/3] Add TCHAR and _TEXT macros. --- optparse.h | 95 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 50 insertions(+), 45 deletions(-) diff --git a/optparse.h b/optparse.h index 3a577a7..6529e7d 100644 --- a/optparse.h +++ b/optparse.h @@ -47,17 +47,22 @@ #ifndef OPTPARSE_H #define OPTPARSE_H +#ifndef _WIN32 +#define TCHAR char +#define _TEXT(x) x +#endif + #ifndef OPTPARSE_API # define OPTPARSE_API #endif struct optparse { - char **argv; + TCHAR **argv; int permute; int optind; int optopt; - char *optarg; - char errmsg[64]; + TCHAR *optarg; + TCHAR errmsg[64]; int subopt; }; @@ -68,7 +73,7 @@ enum optparse_argtype { }; struct optparse_long { - const char *longname; + const TCHAR *longname; int shortname; enum optparse_argtype argtype; }; @@ -77,7 +82,7 @@ struct optparse_long { * Initializes the parser state. */ OPTPARSE_API -void optparse_init(struct optparse *options, char **argv); +void optparse_init(struct optparse *options, TCHAR **argv); /** * Read the next option in the argv array. @@ -89,7 +94,7 @@ void optparse_init(struct optparse *options, char **argv); * colons means the option takes an optional argument. */ OPTPARSE_API -int optparse(struct optparse *options, const char *optstring); +int optparse(struct optparse *options, const TCHAR *optstring); /** * Handles GNU-style long options in addition to getopt() options. @@ -112,65 +117,65 @@ int optparse_long(struct optparse *options, * ignore the value of optind. */ OPTPARSE_API -char *optparse_arg(struct optparse *options); +TCHAR *optparse_arg(struct optparse *options); /* Implementation */ #ifdef OPTPARSE_IMPLEMENTATION -#define OPTPARSE_MSG_INVALID "invalid option" -#define OPTPARSE_MSG_MISSING "option requires an argument" -#define OPTPARSE_MSG_TOOMANY "option takes no arguments" +#define OPTPARSE_MSG_INVALID _TEXT("invalid option") +#define OPTPARSE_MSG_MISSING _TEXT("option requires an argument") +#define OPTPARSE_MSG_TOOMANY _TEXT("option takes no arguments") static int -optparse_error(struct optparse *options, const char *msg, const char *data) +optparse_error(struct optparse *options, const TCHAR *msg, const TCHAR *data) { unsigned p = 0; - const char *sep = " -- '"; + const TCHAR *sep = _TEXT(" -- '"); while (*msg) options->errmsg[p++] = *msg++; while (*sep) options->errmsg[p++] = *sep++; while (p < sizeof(options->errmsg) - 2 && *data) options->errmsg[p++] = *data++; - options->errmsg[p++] = '\''; - options->errmsg[p++] = '\0'; + options->errmsg[p++] = _TEXT('\''); + options->errmsg[p++] = _TEXT('\0'); return '?'; } OPTPARSE_API void -optparse_init(struct optparse *options, char **argv) +optparse_init(struct optparse *options, TCHAR **argv) { options->argv = argv; options->permute = 1; options->optind = 1; options->subopt = 0; options->optarg = 0; - options->errmsg[0] = '\0'; + options->errmsg[0] = _TEXT('\0'); } static int -optparse_is_dashdash(const char *arg) +optparse_is_dashdash(const TCHAR *arg) { - return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0'; + return arg != 0 && arg[0] == _TEXT('-') && arg[1] == _TEXT('-') && arg[2] == _TEXT('\0') ; } static int -optparse_is_shortopt(const char *arg) +optparse_is_shortopt(const TCHAR *arg) { - return arg != 0 && arg[0] == '-' && arg[1] != '-' && arg[1] != '\0'; + return arg != 0 && arg[0] == _TEXT('-') && arg[1] != _TEXT('-') && arg[1] != _TEXT('\0') ; } static int -optparse_is_longopt(const char *arg) +optparse_is_longopt(const TCHAR *arg) { - return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] != '\0'; + return arg != 0 && arg[0] == _TEXT('-') && arg[1] == _TEXT('-') && arg[2] != _TEXT('\0'); } static void optparse_permute(struct optparse *options, int index) { - char *nonoption = options->argv[index]; + TCHAR *nonoption = options->argv[index]; int i; for (i = index; i < options->optind - 1; i++) options->argv[i] = options->argv[i + 1]; @@ -178,7 +183,7 @@ optparse_permute(struct optparse *options, int index) } static int -optparse_argtype(const char *optstring, char c) +optparse_argtype(const TCHAR *optstring, TCHAR c) { int count = OPTPARSE_NONE; if (c == ':') @@ -193,12 +198,12 @@ optparse_argtype(const char *optstring, char c) OPTPARSE_API int -optparse(struct optparse *options, const char *optstring) +optparse(struct optparse *options, const TCHAR *optstring) { int type; - char *next; - char *option = options->argv[options->optind]; - options->errmsg[0] = '\0'; + TCHAR *next; + TCHAR *option = options->argv[options->optind]; + options->errmsg[0] = _TEXT('\0'); options->optopt = 0; options->optarg = 0; if (option == 0) { @@ -223,7 +228,7 @@ optparse(struct optparse *options, const char *optstring) next = options->argv[options->optind + 1]; switch (type) { case -1: { - char str[2] = {0, 0}; + TCHAR str[2] = {0, 0}; str[0] = option[0]; options->optind++; return optparse_error(options, OPTPARSE_MSG_INVALID, str); @@ -245,7 +250,7 @@ optparse(struct optparse *options, const char *optstring) options->optarg = next; options->optind++; } else { - char str[2] = {0, 0}; + TCHAR str[2] = {0, 0}; str[0] = option[0]; options->optarg = 0; return optparse_error(options, OPTPARSE_MSG_MISSING, str); @@ -264,10 +269,10 @@ optparse(struct optparse *options, const char *optstring) } OPTPARSE_API -char * +TCHAR * optparse_arg(struct optparse *options) { - char *option = options->argv[options->optind]; + TCHAR *option = options->argv[options->optind]; options->subopt = 0; if (option != 0) options->optind++; @@ -281,26 +286,26 @@ optparse_longopts_end(const struct optparse_long *longopts, int i) } static void -optparse_from_long(const struct optparse_long *longopts, char *optstring) +optparse_from_long(const struct optparse_long *longopts, TCHAR *optstring) { - char *p = optstring; + TCHAR *p = optstring; int i; for (i = 0; !optparse_longopts_end(longopts, i); i++) { if (longopts[i].shortname) { int a; *p++ = longopts[i].shortname; for (a = 0; a < (int)longopts[i].argtype; a++) - *p++ = ':'; + *p++ = _TEXT(':'); } } - *p = '\0'; + *p = _TEXT('\0'); } /* Unlike strcmp(), handles options containing "=". */ static int -optparse_longopts_match(const char *longname, const char *option) +optparse_longopts_match(const TCHAR *longname, const TCHAR *option) { - const char *a = option, *n = longname; + const TCHAR *a = option, *n = longname; if (longname == 0) return 0; for (; *a && *n && *a != '='; a++, n++) @@ -310,8 +315,8 @@ optparse_longopts_match(const char *longname, const char *option) } /* Return the part after "=", or NULL. */ -static char * -optparse_longopts_arg(char *option) +static TCHAR * +optparse_longopts_arg(TCHAR *option) { for (; *option && *option != '='; option++); if (*option == '=') @@ -326,7 +331,7 @@ optparse_long_fallback(struct optparse *options, int *longindex) { int result; - char optstring[96 * 3 + 1]; /* 96 ASCII printable characters */ + TCHAR optstring[96 * 3 + 1]; /* 96 ASCII printable characters */ optparse_from_long(longopts, optstring); result = optparse(options, optstring); if (longindex != 0) { @@ -348,7 +353,7 @@ optparse_long(struct optparse *options, int *longindex) { int i; - char *option = options->argv[options->optind]; + TCHAR *option = options->argv[options->optind]; if (option == 0) { return -1; } else if (optparse_is_dashdash(option)) { @@ -369,15 +374,15 @@ optparse_long(struct optparse *options, } /* Parse as long option. */ - options->errmsg[0] = '\0'; + options->errmsg[0] = _TEXT('\0'); options->optopt = 0; options->optarg = 0; option += 2; /* skip "--" */ options->optind++; for (i = 0; !optparse_longopts_end(longopts, i); i++) { - const char *name = longopts[i].longname; + const TCHAR *name = longopts[i].longname; if (optparse_longopts_match(name, option)) { - char *arg; + TCHAR *arg; if (longindex) *longindex = i; options->optopt = longopts[i].shortname; From c821b400afdf57d67ad467d7ff96f1e496495004 Mon Sep 17 00:00:00 2001 From: Ilya Pogodaev Date: Mon, 10 Jun 2019 09:17:50 +0300 Subject: [PATCH 2/3] Add optparse_long_only (like getopt_long_only) --- optparse.h | 138 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 108 insertions(+), 30 deletions(-) diff --git a/optparse.h b/optparse.h index 6529e7d..88a414a 100644 --- a/optparse.h +++ b/optparse.h @@ -107,6 +107,11 @@ int optparse_long(struct optparse *options, const struct optparse_long *longopts, int *longindex); +OPTPARSE_API +int optparse_long_only(struct optparse *options, + const struct optparse_long *longopts, + int *longindex); + /** * Used for stepping over non-option arguments. * @return the next non-option argument, or NULL for no more arguments @@ -125,6 +130,7 @@ TCHAR *optparse_arg(struct optparse *options); #define OPTPARSE_MSG_INVALID _TEXT("invalid option") #define OPTPARSE_MSG_MISSING _TEXT("option requires an argument") #define OPTPARSE_MSG_TOOMANY _TEXT("option takes no arguments") +#define OPTPARSE_MSG_AMBIGUITY _TEXT("option is ambiguous") static int optparse_error(struct optparse *options, const TCHAR *msg, const TCHAR *data) @@ -293,7 +299,7 @@ optparse_from_long(const struct optparse_long *longopts, TCHAR *optstring) for (i = 0; !optparse_longopts_end(longopts, i); i++) { if (longopts[i].shortname) { int a; - *p++ = longopts[i].shortname; + *p++ = (TCHAR)longopts[i].shortname; for (a = 0; a < (int)longopts[i].argtype; a++) *p++ = _TEXT(':'); } @@ -314,6 +320,21 @@ optparse_longopts_match(const TCHAR *longname, const TCHAR *option) return *n == '\0' && (*a == '\0' || *a == '='); } +/* --prop == --property */ +/* Returns true if LCP(longname, option) == option */ +static int +optparse_longopts_prefix_match(const TCHAR *longname, const TCHAR *option) +{ + const TCHAR *a = option, *n = longname; + if (longname == 0) + return 0; + for (; *a && *n && *a != '='; a++, n++) + if (*a != *n) + return 0; + // longname can be longer. + return *a == '\0'; +} + /* Return the part after "=", or NULL. */ static TCHAR * optparse_longopts_arg(TCHAR *option) @@ -346,13 +367,80 @@ optparse_long_fallback(struct optparse *options, return result; } +static int +parse_long_option(TCHAR *option, + struct optparse *options, + const struct optparse_long *longopts, + int *longindex, + BOOL is_long_only) +{ + int i; + int cMatchedOptions = 0; + int last_matched_longopt_index = -1; + const TCHAR *name = NULL; + const TCHAR *last_matched_name = NULL; + /* Parse as long option. */ + options->errmsg[0] = _TEXT('\0'); + options->optopt = 0; + options->optarg = 0; + if (is_long_only && !optparse_is_longopt(option)) { + option += 1; /* skip "-" */ + } + else { + option += 2; /* skip "--" */ + } + options->optind++; + + for (i = 0; !optparse_longopts_end(longopts, i); i++) { + name = longopts[i].longname; + if (optparse_longopts_match(name, option)) { + // exact match + last_matched_name = name; + last_matched_longopt_index = i; + cMatchedOptions = 1; + break; + } + else if (optparse_longopts_prefix_match(name, option)) { + // nonexact match + last_matched_name = name; + last_matched_longopt_index = i; + ++cMatchedOptions; + } + } + + if (cMatchedOptions > 1) { + return optparse_error(options, OPTPARSE_MSG_AMBIGUITY, name); + } if (cMatchedOptions) { + i = last_matched_longopt_index; + name = last_matched_name; + TCHAR *arg; + if (longindex) + *longindex = i; + options->optopt = longopts[i].shortname; + arg = optparse_longopts_arg(option); + if (longopts[i].argtype == OPTPARSE_NONE && arg != 0) { + return optparse_error(options, OPTPARSE_MSG_TOOMANY, name); + } if (arg != 0) { + options->optarg = arg; + } + else if (longopts[i].argtype == OPTPARSE_REQUIRED) { + options->optarg = options->argv[options->optind]; + if (options->optarg == 0) + return optparse_error(options, OPTPARSE_MSG_MISSING, name); + else + options->optind++; + } + return options->optopt; + } + return optparse_error(options, OPTPARSE_MSG_INVALID, option); +} + OPTPARSE_API int optparse_long(struct optparse *options, const struct optparse_long *longopts, int *longindex) { - int i; TCHAR *option = options->argv[options->optind]; if (option == 0) { return -1; @@ -373,35 +461,25 @@ optparse_long(struct optparse *options, } } - /* Parse as long option. */ - options->errmsg[0] = _TEXT('\0'); - options->optopt = 0; - options->optarg = 0; - option += 2; /* skip "--" */ - options->optind++; - for (i = 0; !optparse_longopts_end(longopts, i); i++) { - const TCHAR *name = longopts[i].longname; - if (optparse_longopts_match(name, option)) { - TCHAR *arg; - if (longindex) - *longindex = i; - options->optopt = longopts[i].shortname; - arg = optparse_longopts_arg(option); - if (longopts[i].argtype == OPTPARSE_NONE && arg != 0) { - return optparse_error(options, OPTPARSE_MSG_TOOMANY, name); - } if (arg != 0) { - options->optarg = arg; - } else if (longopts[i].argtype == OPTPARSE_REQUIRED) { - options->optarg = options->argv[options->optind]; - if (options->optarg == 0) - return optparse_error(options, OPTPARSE_MSG_MISSING, name); - else - options->optind++; - } - return options->optopt; - } + return parse_long_option(option, options, longopts, longindex, FALSE); +} + +OPTPARSE_API +int +optparse_long_only(struct optparse *options, + const struct optparse_long *longopts, + int *longindex) +{ + TCHAR *option = options->argv[options->optind]; + if (option == 0) { + return -1; } - return optparse_error(options, OPTPARSE_MSG_INVALID, option); + else if (optparse_is_dashdash(option)) { + options->optind++; /* consume "--" */ + return -1; + } + + return parse_long_option(option, options, longopts, longindex, TRUE); } #endif /* OPTPARSE_IMPLEMENTATION */ From 717c2492d240bb1c5117c65417691af83ac7b11c Mon Sep 17 00:00:00 2001 From: Ilya Pogodaev Date: Fri, 14 Jun 2019 11:16:41 +0300 Subject: [PATCH 3/3] Changed include guards to avoid multiple definitions of functions. --- optparse.h | 65 +++++++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/optparse.h b/optparse.h index 88a414a..bf76335 100644 --- a/optparse.h +++ b/optparse.h @@ -44,6 +44,9 @@ * arguments to the end. This can be disabled by setting the `permute` * field to 0 after initialization. */ + +#ifndef OPTPARSE_IMPLEMENTATION + #ifndef OPTPARSE_H #define OPTPARSE_H @@ -107,6 +110,9 @@ int optparse_long(struct optparse *options, const struct optparse_long *longopts, int *longindex); +/** + * Works a lot like GNU's getopt_long_only(). +*/ OPTPARSE_API int optparse_long_only(struct optparse *options, const struct optparse_long *longopts, @@ -124,8 +130,10 @@ int optparse_long_only(struct optparse *options, OPTPARSE_API TCHAR *optparse_arg(struct optparse *options); +#endif /* OPTPARSE_H */ + +#else /* OPTPARSE_IMPLEMENTATION */ /* Implementation */ -#ifdef OPTPARSE_IMPLEMENTATION #define OPTPARSE_MSG_INVALID _TEXT("invalid option") #define OPTPARSE_MSG_MISSING _TEXT("option requires an argument") @@ -308,31 +316,25 @@ optparse_from_long(const struct optparse_long *longopts, TCHAR *optstring) } /* Unlike strcmp(), handles options containing "=". */ +/* is_exact_match is true if LCP(longname, option) == option */ static int -optparse_longopts_match(const TCHAR *longname, const TCHAR *option) -{ - const TCHAR *a = option, *n = longname; - if (longname == 0) - return 0; - for (; *a && *n && *a != '='; a++, n++) - if (*a != *n) - return 0; - return *n == '\0' && (*a == '\0' || *a == '='); -} - -/* --prop == --property */ -/* Returns true if LCP(longname, option) == option */ -static int -optparse_longopts_prefix_match(const TCHAR *longname, const TCHAR *option) +optparse_longopts_match(const TCHAR *longname, const TCHAR *option, int *is_exact_match) { + *is_exact_match = 0; const TCHAR *a = option, *n = longname; if (longname == 0) return 0; for (; *a && *n && *a != '='; a++, n++) if (*a != *n) return 0; - // longname can be longer. - return *a == '\0'; + if (*n == '\0' && (*a == '\0' || *a == '=')) { + *is_exact_match = 1; + return 1; + } if (*a == '\0' || *a == '=') { + // longname can be longer. + return 1; + } + return 0; } /* Return the part after "=", or NULL. */ @@ -372,14 +374,14 @@ parse_long_option(TCHAR *option, struct optparse *options, const struct optparse_long *longopts, int *longindex, - BOOL is_long_only) + int is_long_only) { int i; - int cMatchedOptions = 0; + int matched_options_count = 0; int last_matched_longopt_index = -1; const TCHAR *name = NULL; const TCHAR *last_matched_name = NULL; - /* Parse as long option. */ + options->errmsg[0] = _TEXT('\0'); options->optopt = 0; options->optarg = 0; @@ -392,25 +394,29 @@ parse_long_option(TCHAR *option, options->optind++; for (i = 0; !optparse_longopts_end(longopts, i); i++) { + int is_exact_match = 0; name = longopts[i].longname; - if (optparse_longopts_match(name, option)) { + if (!optparse_longopts_match(name, option, &is_exact_match)) { + continue; + } + if (is_exact_match) { // exact match last_matched_name = name; last_matched_longopt_index = i; - cMatchedOptions = 1; + matched_options_count = 1; break; } - else if (optparse_longopts_prefix_match(name, option)) { + else { // nonexact match last_matched_name = name; last_matched_longopt_index = i; - ++cMatchedOptions; + ++matched_options_count; } } - if (cMatchedOptions > 1) { + if (matched_options_count > 1) { return optparse_error(options, OPTPARSE_MSG_AMBIGUITY, name); - } if (cMatchedOptions) { + } if (matched_options_count) { i = last_matched_longopt_index; name = last_matched_name; TCHAR *arg; @@ -461,7 +467,7 @@ optparse_long(struct optparse *options, } } - return parse_long_option(option, options, longopts, longindex, FALSE); + return parse_long_option(option, options, longopts, longindex, 0); } OPTPARSE_API @@ -479,8 +485,7 @@ optparse_long_only(struct optparse *options, return -1; } - return parse_long_option(option, options, longopts, longindex, TRUE); + return parse_long_option(option, options, longopts, longindex, 1); } #endif /* OPTPARSE_IMPLEMENTATION */ -#endif /* OPTPARSE_H */