|
@@ -0,0 +1,466 @@
|
|
|
+
|
|
|
+
|
|
|
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
|
|
|
+ * All rights reserved.
|
|
|
+ *
|
|
|
+ * This code is derived from software contributed to The NetBSD Foundation
|
|
|
+ * by Dieter Baron and Thomas Klausner.
|
|
|
+ *
|
|
|
+ * Redistribution and use in source and binary forms, with or without
|
|
|
+ * modification, are permitted provided that the following conditions
|
|
|
+ * are met:
|
|
|
+ * 1. Redistributions of source code must retain the above copyright
|
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
|
+ * 2. Redistributions in binary form must reproduce the above copyright
|
|
|
+ * notice, this list of conditions and the following disclaimer in the
|
|
|
+ * documentation and/or other materials provided with the distribution.
|
|
|
+ *
|
|
|
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
|
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
|
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
|
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
+ * POSSIBILITY OF SUCH DAMAGE.
|
|
|
+ *
|
|
|
+ * $NetBSD: getopt_long.c,v 1.3 2008/04/29 05:46:09 martin Exp $
|
|
|
+ */
|
|
|
+
|
|
|
+#include <assert.h>
|
|
|
+#include <errno.h>
|
|
|
+#include <stdarg.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <string.h>
|
|
|
+#include "getopt.h"
|
|
|
+
|
|
|
+#ifndef _DIAGASSERT
|
|
|
+#define _DIAGASSERT(e)
|
|
|
+#endif
|
|
|
+
|
|
|
+int opterr = 1;
|
|
|
+int optind = 1;
|
|
|
+int optopt = '?';
|
|
|
+int optreset;
|
|
|
+char *optarg;
|
|
|
+
|
|
|
+
|
|
|
+#define IGNORE_FIRST (*options == '-' || *options == '+')
|
|
|
+#define PRINT_ERROR ((opterr) && ((*options != ':') \
|
|
|
+ || (IGNORE_FIRST && options[1] != ':')))
|
|
|
+#define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL)
|
|
|
+#define PERMUTE (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
|
|
|
+
|
|
|
+#define IN_ORDER (!IS_POSIXLY_CORRECT && *options == '-')
|
|
|
+
|
|
|
+
|
|
|
+#define BADCH (int)'?'
|
|
|
+#define BADARG (int)':'
|
|
|
+#define INORDER (int)1
|
|
|
+
|
|
|
+#define EMSG ""
|
|
|
+
|
|
|
+static int getopt_internal(int, char * const *, const char *);
|
|
|
+static int gcd(int, int);
|
|
|
+static void permute_args(int, int, int, char * const *);
|
|
|
+static void xwarnx(const char *, ...);
|
|
|
+
|
|
|
+static char *place = EMSG;
|
|
|
+
|
|
|
+
|
|
|
+static int nonopt_start = -1;
|
|
|
+static int nonopt_end = -1;
|
|
|
+
|
|
|
+
|
|
|
+static const char recargchar[] = "option requires an argument -- %c";
|
|
|
+static const char recargstring[] = "option requires an argument -- %s";
|
|
|
+static const char ambig[] = "ambiguous option -- %.*s";
|
|
|
+static const char noarg[] = "option doesn't take an argument -- %.*s";
|
|
|
+static const char illoptchar[] = "illegal option -- %c";
|
|
|
+static const char illoptstring[] = "illegal option -- %s";
|
|
|
+
|
|
|
+static const char *progname;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+static void xwarnx(const char *fmt, ...) {
|
|
|
+ va_list ap;
|
|
|
+
|
|
|
+ va_start(ap, fmt);
|
|
|
+ if (progname)
|
|
|
+ (void) fprintf(stderr, "%s: ", progname);
|
|
|
+ if (fmt)
|
|
|
+ (void) vfprintf(stderr, fmt, ap);
|
|
|
+ (void) fprintf(stderr, "\n");
|
|
|
+ va_end(ap);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * Compute the greatest common divisor of a and b.
|
|
|
+ */
|
|
|
+static int
|
|
|
+gcd(int a, int b)
|
|
|
+{
|
|
|
+ int c;
|
|
|
+
|
|
|
+ c = a % b;
|
|
|
+ while (c != 0) {
|
|
|
+ a = b;
|
|
|
+ b = c;
|
|
|
+ c = a % b;
|
|
|
+ }
|
|
|
+
|
|
|
+ return b;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * Exchange the block from nonopt_start to nonopt_end with the block
|
|
|
+ * from nonopt_end to opt_end (keeping the same order of arguments
|
|
|
+ * in each block).
|
|
|
+ */
|
|
|
+static void
|
|
|
+permute_args(int nonopt_start, int nonopt_end, int opt_end, char * const *nargv)
|
|
|
+{
|
|
|
+ int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
|
|
|
+ char *swap;
|
|
|
+
|
|
|
+
|
|
|
+ * compute lengths of blocks and number and size of cycles
|
|
|
+ */
|
|
|
+ nnonopts = nonopt_end - nonopt_start;
|
|
|
+ nopts = opt_end - nonopt_end;
|
|
|
+ ncycle = gcd(nnonopts, nopts);
|
|
|
+ cyclelen = (opt_end - nonopt_start) / ncycle;
|
|
|
+
|
|
|
+ for (i = 0; i < ncycle; i++) {
|
|
|
+ cstart = nonopt_end+i;
|
|
|
+ pos = cstart;
|
|
|
+ for (j = 0; j < cyclelen; j++) {
|
|
|
+ if (pos >= nonopt_end)
|
|
|
+ pos -= nnonopts;
|
|
|
+ else
|
|
|
+ pos += nopts;
|
|
|
+ swap = nargv[pos];
|
|
|
+
|
|
|
+ ((char **) nargv)[pos] = nargv[cstart];
|
|
|
+
|
|
|
+ ((char **)nargv)[cstart] = swap;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * getopt_internal --
|
|
|
+ * Parse argc/argv argument vector. Called by user level routines.
|
|
|
+ * Returns -2 if -- is found (can be long option or end of options marker).
|
|
|
+ */
|
|
|
+static int
|
|
|
+getopt_internal(int nargc, char * const *nargv, const char *options)
|
|
|
+{
|
|
|
+ char *oli;
|
|
|
+ int optchar;
|
|
|
+
|
|
|
+ _DIAGASSERT(nargv != NULL);
|
|
|
+ _DIAGASSERT(options != NULL);
|
|
|
+
|
|
|
+ optarg = NULL;
|
|
|
+
|
|
|
+
|
|
|
+ * XXX Some programs (like rsyncd) expect to be able to
|
|
|
+ * XXX re-initialize optind to 0 and have getopt_long(3)
|
|
|
+ * XXX properly function again. Work around this braindamage.
|
|
|
+ */
|
|
|
+ if (optind == 0)
|
|
|
+ optind = 1;
|
|
|
+
|
|
|
+ if (optreset)
|
|
|
+ nonopt_start = nonopt_end = -1;
|
|
|
+start:
|
|
|
+ if (optreset || !*place) {
|
|
|
+ optreset = 0;
|
|
|
+ if (optind >= nargc) {
|
|
|
+ place = EMSG;
|
|
|
+ if (nonopt_end != -1) {
|
|
|
+
|
|
|
+ permute_args(nonopt_start, nonopt_end,
|
|
|
+ optind, nargv);
|
|
|
+ optind -= nonopt_end - nonopt_start;
|
|
|
+ }
|
|
|
+ else if (nonopt_start != -1) {
|
|
|
+
|
|
|
+ * If we skipped non-options, set optind
|
|
|
+ * to the first of them.
|
|
|
+ */
|
|
|
+ optind = nonopt_start;
|
|
|
+ }
|
|
|
+ nonopt_start = nonopt_end = -1;
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ if (*(place = nargv[optind]) != '-') {
|
|
|
+ place = EMSG;
|
|
|
+ if (IN_ORDER) {
|
|
|
+
|
|
|
+ * GNU extension:
|
|
|
+ * return non-option as argument to option 1
|
|
|
+ */
|
|
|
+ optarg = nargv[optind++];
|
|
|
+ return INORDER;
|
|
|
+ }
|
|
|
+ if (!PERMUTE) {
|
|
|
+
|
|
|
+ * if no permutation wanted, stop parsing
|
|
|
+ * at first non-option
|
|
|
+ */
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (nonopt_start == -1)
|
|
|
+ nonopt_start = optind;
|
|
|
+ else if (nonopt_end != -1) {
|
|
|
+ permute_args(nonopt_start, nonopt_end,
|
|
|
+ optind, nargv);
|
|
|
+ nonopt_start = optind -
|
|
|
+ (nonopt_end - nonopt_start);
|
|
|
+ nonopt_end = -1;
|
|
|
+ }
|
|
|
+ optind++;
|
|
|
+
|
|
|
+ goto start;
|
|
|
+ }
|
|
|
+ if (nonopt_start != -1 && nonopt_end == -1)
|
|
|
+ nonopt_end = optind;
|
|
|
+ if (place[1] && *++place == '-') {
|
|
|
+ place++;
|
|
|
+ return -2;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if ((optchar = (int)*place++) == (int)':' ||
|
|
|
+ (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
|
|
|
+
|
|
|
+ if (!*place)
|
|
|
+ ++optind;
|
|
|
+ if (PRINT_ERROR)
|
|
|
+ xwarnx(illoptchar, optchar);
|
|
|
+ optopt = optchar;
|
|
|
+ return BADCH;
|
|
|
+ }
|
|
|
+ if (optchar == 'W' && oli[1] == ';') {
|
|
|
+
|
|
|
+ if (*place)
|
|
|
+ return -2;
|
|
|
+
|
|
|
+ if (++optind >= nargc) {
|
|
|
+ place = EMSG;
|
|
|
+ if (PRINT_ERROR)
|
|
|
+ xwarnx(recargchar, optchar);
|
|
|
+ optopt = optchar;
|
|
|
+
|
|
|
+ return BADARG;
|
|
|
+ } else
|
|
|
+ place = nargv[optind];
|
|
|
+
|
|
|
+ * Handle -W arg the same as --arg (which causes getopt to
|
|
|
+ * stop parsing).
|
|
|
+ */
|
|
|
+ return -2;
|
|
|
+ }
|
|
|
+ if (*++oli != ':') {
|
|
|
+ if (!*place)
|
|
|
+ ++optind;
|
|
|
+ } else {
|
|
|
+ optarg = NULL;
|
|
|
+ if (*place)
|
|
|
+ optarg = place;
|
|
|
+
|
|
|
+ else if (oli[1] != ':') {
|
|
|
+ if (++optind >= nargc) {
|
|
|
+ place = EMSG;
|
|
|
+ if (PRINT_ERROR)
|
|
|
+ xwarnx(recargchar, optchar);
|
|
|
+ optopt = optchar;
|
|
|
+
|
|
|
+ return BADARG;
|
|
|
+ } else
|
|
|
+ optarg = nargv[optind];
|
|
|
+ }
|
|
|
+ place = EMSG;
|
|
|
+ ++optind;
|
|
|
+ }
|
|
|
+
|
|
|
+ return optchar;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * getopt --
|
|
|
+ * Parse argc/argv argument vector.
|
|
|
+ *
|
|
|
+ * [eventually this will replace the real getopt]
|
|
|
+ */
|
|
|
+int
|
|
|
+getopt(int nargc, char * const *nargv, const char *options)
|
|
|
+{
|
|
|
+ int retval;
|
|
|
+
|
|
|
+ progname = nargv[0];
|
|
|
+
|
|
|
+ if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
|
|
|
+ ++optind;
|
|
|
+
|
|
|
+ * We found an option (--), so if we skipped non-options,
|
|
|
+ * we have to permute.
|
|
|
+ */
|
|
|
+ if (nonopt_end != -1) {
|
|
|
+ permute_args(nonopt_start, nonopt_end, optind,
|
|
|
+ nargv);
|
|
|
+ optind -= nonopt_end - nonopt_start;
|
|
|
+ }
|
|
|
+ nonopt_start = nonopt_end = -1;
|
|
|
+ retval = -1;
|
|
|
+ }
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * getopt_long --
|
|
|
+ * Parse argc/argv argument vector.
|
|
|
+ */
|
|
|
+int
|
|
|
+getopt_long(int nargc,
|
|
|
+ char * const *nargv,
|
|
|
+ const char *options,
|
|
|
+ const struct option *long_options,
|
|
|
+ int *idx)
|
|
|
+{
|
|
|
+ int retval;
|
|
|
+
|
|
|
+ _DIAGASSERT(nargv != NULL);
|
|
|
+ _DIAGASSERT(options != NULL);
|
|
|
+ _DIAGASSERT(long_options != NULL);
|
|
|
+
|
|
|
+
|
|
|
+ progname = nargv[0];
|
|
|
+
|
|
|
+ if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
|
|
|
+ char *current_argv, *has_equal;
|
|
|
+ size_t current_argv_len;
|
|
|
+ int i, match;
|
|
|
+
|
|
|
+ current_argv = place;
|
|
|
+ match = -1;
|
|
|
+
|
|
|
+ optind++;
|
|
|
+ place = EMSG;
|
|
|
+
|
|
|
+ if (*current_argv == '\0') {
|
|
|
+
|
|
|
+ * We found an option (--), so if we skipped
|
|
|
+ * non-options, we have to permute.
|
|
|
+ */
|
|
|
+ if (nonopt_end != -1) {
|
|
|
+ permute_args(nonopt_start, nonopt_end,
|
|
|
+ optind, nargv);
|
|
|
+ optind -= nonopt_end - nonopt_start;
|
|
|
+ }
|
|
|
+ nonopt_start = nonopt_end = -1;
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ if ((has_equal = strchr(current_argv, '=')) != NULL) {
|
|
|
+
|
|
|
+ current_argv_len = has_equal - current_argv;
|
|
|
+ has_equal++;
|
|
|
+ } else
|
|
|
+ current_argv_len = strlen(current_argv);
|
|
|
+
|
|
|
+ for (i = 0; long_options[i].name; i++) {
|
|
|
+
|
|
|
+ if (strncmp(current_argv, long_options[i].name,
|
|
|
+ current_argv_len))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (strlen(long_options[i].name) ==
|
|
|
+ (unsigned)current_argv_len) {
|
|
|
+
|
|
|
+ match = i;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (match == -1)
|
|
|
+ match = i;
|
|
|
+ else {
|
|
|
+
|
|
|
+ if (PRINT_ERROR)
|
|
|
+ xwarnx(ambig, (int)current_argv_len,
|
|
|
+ current_argv);
|
|
|
+ optopt = 0;
|
|
|
+ return BADCH;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (match != -1) {
|
|
|
+ if (long_options[match].has_arg == no_argument
|
|
|
+ && has_equal) {
|
|
|
+ if (PRINT_ERROR)
|
|
|
+ xwarnx(noarg, (int)current_argv_len,
|
|
|
+ current_argv);
|
|
|
+
|
|
|
+ * XXX: GNU sets optopt to val regardless of
|
|
|
+ * flag
|
|
|
+ */
|
|
|
+ if (long_options[match].flag == NULL)
|
|
|
+ optopt = long_options[match].val;
|
|
|
+ else
|
|
|
+ optopt = 0;
|
|
|
+
|
|
|
+ return BADARG;
|
|
|
+ }
|
|
|
+ if (long_options[match].has_arg == required_argument ||
|
|
|
+ long_options[match].has_arg == optional_argument) {
|
|
|
+ if (has_equal)
|
|
|
+ optarg = has_equal;
|
|
|
+ else if (long_options[match].has_arg ==
|
|
|
+ required_argument) {
|
|
|
+
|
|
|
+ * optional argument doesn't use
|
|
|
+ * next nargv
|
|
|
+ */
|
|
|
+ optarg = nargv[optind++];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if ((long_options[match].has_arg == required_argument)
|
|
|
+ && (optarg == NULL)) {
|
|
|
+
|
|
|
+ * Missing argument; leading ':'
|
|
|
+ * indicates no error should be generated
|
|
|
+ */
|
|
|
+ if (PRINT_ERROR)
|
|
|
+ xwarnx(recargstring, current_argv);
|
|
|
+
|
|
|
+ * XXX: GNU sets optopt to val regardless
|
|
|
+ * of flag
|
|
|
+ */
|
|
|
+ if (long_options[match].flag == NULL)
|
|
|
+ optopt = long_options[match].val;
|
|
|
+ else
|
|
|
+ optopt = 0;
|
|
|
+
|
|
|
+ --optind;
|
|
|
+ return BADARG;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (PRINT_ERROR)
|
|
|
+ xwarnx(illoptstring, current_argv);
|
|
|
+ optopt = 0;
|
|
|
+ return BADCH;
|
|
|
+ }
|
|
|
+ if (long_options[match].flag) {
|
|
|
+ *long_options[match].flag = long_options[match].val;
|
|
|
+ retval = 0;
|
|
|
+ } else
|
|
|
+ retval = long_options[match].val;
|
|
|
+ if (idx)
|
|
|
+ *idx = match;
|
|
|
+ }
|
|
|
+ return retval;
|
|
|
+}
|