oslib/getopt_long: allow (unique) short match
authorJens Axboe <axboe@fb.com>
Fri, 22 Apr 2016 22:14:24 +0000 (18:14 -0400)
committerJens Axboe <axboe@fb.com>
Fri, 22 Apr 2016 22:14:24 +0000 (18:14 -0400)
The GNU version allows short matches, if they are unique. Our version
does not, since it's weird, but let's at least make them have the
same behavior. Otherwise users will have different behavior for
command line parsing for edge cases, on different platforms.

Signed-off-by: Jens Axboe <axboe@fb.com>
oslib/getopt_long.c

index 11d879ad2bfe70f4ab76d06bcc8aad20d36c4c16..8ec77413b7a7a6fe507dbb3b3fae90a761fb00eb 100644 (file)
@@ -26,14 +26,14 @@ static struct getopt_private_state {
 } pvt;
 
 static inline const char *option_matches(const char *arg_str,
 } pvt;
 
 static inline const char *option_matches(const char *arg_str,
-                                        const char *opt_name)
+                                        const char *opt_name, int smatch)
 {
        while (*arg_str != '\0' && *arg_str != '=') {
                if (*arg_str++ != *opt_name++)
                        return NULL;
        }
 
 {
        while (*arg_str != '\0' && *arg_str != '=') {
                if (*arg_str++ != *opt_name++)
                        return NULL;
        }
 
-       if (*opt_name)
+       if (*opt_name && !smatch)
                return NULL;
 
        return arg_str;
                return NULL;
 
        return arg_str;
@@ -84,11 +84,37 @@ int getopt_long_only(int argc, char *const *argv, const char *optstring,
                }
 
                for (lo = longopts; lo->name; lo++) {
                }
 
                for (lo = longopts; lo->name; lo++) {
-                       if ((opt_end = option_matches(carg+2, lo->name)))
+                       opt_end = option_matches(carg+2, lo->name, 0);
+                       if (opt_end)
                            break;
                }
                            break;
                }
-               if (!opt_end)
-                       return '?';
+               /*
+                * The GNU getopt_long_only() apparently allows a short match,
+                * if it's unique and if we don't have a full match. Let's
+                * do the same here, search and see if there is one (and only
+                * one) short match.
+                */
+               if (!opt_end) {
+                       const struct option *lo_match = NULL;
+
+                       for (lo = longopts; lo->name; lo++) {
+                               const char *ret;
+
+                               ret = option_matches(carg+2, lo->name, 1);
+                               if (!ret)
+                                       continue;
+                               if (!opt_end) {
+                                       opt_end = ret;
+                                       lo_match = lo;
+                               } else {
+                                       opt_end = NULL;
+                                       break;
+                               }
+                       }
+                       if (!opt_end)
+                               return '?';
+                       lo = lo_match;
+               }
 
                if (longindex)
                        *longindex = lo-longopts;
 
                if (longindex)
                        *longindex = lo-longopts;