Add support for AIX
[fio.git] / lib / getopt_long.c
diff --git a/lib/getopt_long.c b/lib/getopt_long.c
new file mode 100644 (file)
index 0000000..3d80627
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * getopt.c
+ *
+ * getopt_long(), or at least a common subset thereof:
+ *
+ * - Option reordering is not supported
+ * - -W foo is not supported
+ * - First optstring character "-" not supported.
+ */
+
+#include <stdint.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+
+char *optarg;
+int optind, opterr, optopt;
+static struct getopt_private_state {
+       const char *optptr;
+       const char *last_optstring;
+       char *const *last_argv;
+} pvt;
+
+static inline const char *option_matches(const char *arg_str,
+                                        const char *opt_name)
+{
+       while (*arg_str != '\0' && *arg_str != '=') {
+               if (*arg_str++ != *opt_name++)
+                       return NULL;
+       }
+
+       if (*opt_name)
+               return NULL;
+
+       return arg_str;
+}
+
+int getopt_long_only(int argc, char *const *argv, const char *optstring,
+               const struct option *longopts, int *longindex)
+{
+       const char *carg;
+       const char *osptr;
+       int opt;
+
+       /* getopt() relies on a number of different global state
+          variables, which can make this really confusing if there is
+          more than one use of getopt() in the same program.  This
+          attempts to detect that situation by detecting if the
+          "optstring" or "argv" argument have changed since last time
+          we were called; if so, reinitialize the query state. */
+
+       if (optstring != pvt.last_optstring || argv != pvt.last_argv ||
+           optind < 1 || optind > argc) {
+               /* optind doesn't match the current query */
+               pvt.last_optstring = optstring;
+               pvt.last_argv = argv;
+               optind = 1;
+               pvt.optptr = NULL;
+       }
+
+       carg = argv[optind];
+
+       /* First, eliminate all non-option cases */
+
+       if (!carg || carg[0] != '-' || !carg[1])
+               return -1;
+
+       if (carg[1] == '-') {
+               const struct option *lo;
+               const char *opt_end = NULL;
+
+               optind++;
+
+               /* Either it's a long option, or it's -- */
+               if (!carg[2]) {
+                       /* It's -- */
+                       return -1;
+               }
+
+               for (lo = longopts; lo->name; lo++) {
+                       if ((opt_end = option_matches(carg+2, lo->name)))
+                           break;
+               }
+               if (!opt_end)
+                       return '?';
+
+               if (longindex)
+                       *longindex = lo-longopts;
+
+               if (*opt_end == '=') {
+                       if (lo->has_arg)
+                               optarg = (char *)opt_end+1;
+                       else
+                               return '?';
+               } else if (lo->has_arg == 1) {
+                       if (!(optarg = argv[optind]))
+                               return '?';
+                       optind++;
+               }
+
+               if (lo->flag) {
+                       *lo->flag = lo->val;
+                       return 0;
+               } else {
+                       return lo->val;
+               }
+       }
+
+       if ((uintptr_t) (pvt.optptr - carg) > (uintptr_t) strlen(carg)) {
+               /* Someone frobbed optind, change to new opt. */
+               pvt.optptr = carg + 1;
+       }
+
+       opt = *pvt.optptr++;
+
+       if (opt != ':' && (osptr = strchr(optstring, opt))) {
+               if (osptr[1] == ':') {
+                       if (*pvt.optptr) {
+                               /* Argument-taking option with attached
+                                  argument */
+                               optarg = (char *)pvt.optptr;
+                               optind++;
+                       } else {
+                               /* Argument-taking option with non-attached
+                                  argument */
+                               if (argv[optind + 1]) {
+                                       optarg = (char *)argv[optind+1];
+                                       optind += 2;
+                               } else {
+                                       /* Missing argument */
+                                       optind++;
+                                       return (optstring[0] == ':')
+                                               ? ':' : '?';
+                               }
+                       }
+                       return opt;
+               } else {
+                       /* Non-argument-taking option */
+                       /* pvt.optptr will remember the exact position to
+                          resume at */
+                       if (!*pvt.optptr)
+                               optind++;
+                       return opt;
+               }
+       } else {
+               /* Unknown option */
+               optopt = opt;
+               if (!*pvt.optptr)
+                       optind++;
+               return '?';
+       }
+}