Browse Source

Add internal getopt implamentation. BSD version

Serj Kalichev 5 years ago
parent
commit
1a733d2882
13 changed files with 615 additions and 278 deletions
  1. 2 0
      Makefile.am
  2. 8 2
      bin/clish.c
  3. 0 231
      bin/getopt.c
  4. 0 31
      bin/getopt.h
  5. 8 3
      bin/konf.c
  6. 8 2
      bin/konfd.c
  7. 7 2
      bin/module.am
  8. 7 2
      bin/sigexec.c
  9. 26 3
      configure.ac
  10. 6 2
      konf/query/query.c
  11. 466 0
      libc/getopt.c
  12. 74 0
      libc/getopt.h
  13. 3 0
      libc/module.am

+ 2 - 0
Makefile.am

@@ -26,6 +26,7 @@ EXTRA_DIST = \
 	tinyrl/module.am \
 	konf/module.am \
 	plugins/module.am \
+	libc/module.am \
 	contrib \
 	xml-examples \
 	debian \
@@ -41,3 +42,4 @@ include $(top_srcdir)/konf/module.am
 include $(top_srcdir)/clish/module.am
 include $(top_srcdir)/bin/module.am
 include $(top_srcdir)/plugins/module.am
+include $(top_srcdir)/libc/module.am

+ 8 - 2
bin/clish.c

@@ -15,9 +15,15 @@
 #include <string.h>
 #include <unistd.h>
 #include <syslog.h>
+
+#if WITH_INTERNAL_GETOPT
+#include "libc/getopt.h"
+#else
 #ifdef HAVE_GETOPT_H
 #include <getopt.h>
 #endif
+#endif
+
 #include <signal.h>
 #if HAVE_LOCALE_H
 #include <locale.h>
@@ -77,7 +83,7 @@ int main(int argc, char **argv)
 	sigset_t sigpipe_set;
 
 	static const char *shortopts = "hvs:ledx:w:i:bqu8oO:kt:c:f:z:";
-#ifdef HAVE_GETOPT_H
+#ifdef HAVE_GETOPT_LONG
 	static const struct option longopts[] = {
 		{"help",	0, NULL, 'h'},
 		{"version",	0, NULL, 'v'},
@@ -122,7 +128,7 @@ int main(int argc, char **argv)
 	/* Parse command line options */
 	while(1) {
 		int opt;
-#ifdef HAVE_GETOPT_H
+#ifdef HAVE_GETOPT_LONG
 		opt = getopt_long(argc, argv, shortopts, longopts, NULL);
 #else
 		opt = getopt(argc, argv, shortopts);

+ 0 - 231
bin/getopt.c

@@ -1,231 +0,0 @@
-/*
-Copyright (c) 2012, Kim Gräsman
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-    * Redistributions of source code must retain the above copyright
-      notice, this list of conditions and the following disclaimer.
-    * 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.
-    * Neither the name of Kim Gräsman nor the
-      names of contributors may be used to endorse or promote products
-      derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 KIM GRÄSMAN 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.
-*/
-
-#include "lub/getopt.h"
-
-#include <stddef.h>
-#include <string.h>
-
-const int no_argument = 0;
-const int required_argument = 1;
-const int optional_argument = 2;
-
-char* optarg;
-int optopt;
-/* The variable optind [...] shall be initialized to 1 by the system. */
-int optind = 1;
-int opterr;
-
-static char* optcursor = NULL;
-
-/* Implemented based on [1] and [2] for optional arguments.
-   optopt is handled FreeBSD-style, per [3].
-   Other GNU and FreeBSD extensions are purely accidental. 
-   
-[1] http://pubs.opengroup.org/onlinepubs/000095399/functions/getopt.html
-[2] http://www.kernel.org/doc/man-pages/online/pages/man3/getopt.3.html
-[3] http://www.freebsd.org/cgi/man.cgi?query=getopt&sektion=3&manpath=FreeBSD+9.0-RELEASE
-*/
-int getopt(int argc, char* const argv[], const char* optstring) {
-  int optchar = -1;
-  const char* optdecl = NULL;
-
-  optarg = NULL;
-  opterr = 0;
-  optopt = 0;
-
-  /* Unspecified, but we need it to avoid overrunning the argv bounds. */
-  if (optind >= argc)
-    goto no_more_optchars;
-
-  /* If, when getopt() is called argv[optind] is a null pointer, getopt() 
-     shall return -1 without changing optind. */
-  if (argv[optind] == NULL)
-    goto no_more_optchars;
-
-  /* If, when getopt() is called *argv[optind]  is not the character '-', 
-     getopt() shall return -1 without changing optind. */
-  if (*argv[optind] != '-')
-    goto no_more_optchars;
-
-  /* If, when getopt() is called argv[optind] points to the string "-", 
-     getopt() shall return -1 without changing optind. */
-  if (strcmp(argv[optind], "-") == 0)
-    goto no_more_optchars;
-
-  /* If, when getopt() is called argv[optind] points to the string "--", 
-     getopt() shall return -1 after incrementing optind. */
-  if (strcmp(argv[optind], "--") == 0) {
-    ++optind;
-    goto no_more_optchars;
-  }
-
-  if (optcursor == NULL || *optcursor == '\0')
-    optcursor = argv[optind] + 1;
-
-  optchar = *optcursor;
-
-  /* FreeBSD: The variable optopt saves the last known option character 
-     returned by getopt(). */
-  optopt = optchar;
-
-  /* The getopt() function shall return the next option character (if one is 
-     found) from argv that matches a character in optstring, if there is 
-     one that matches. */
-  optdecl = strchr(optstring, optchar);
-  if (optdecl) {
-    /* [I]f a character is followed by a colon, the option takes an
-       argument. */
-    if (optdecl[1] == ':') {
-      optarg = ++optcursor;
-      if (*optarg == '\0') {
-        /* GNU extension: Two colons mean an option takes an
-           optional arg; if there is text in the current argv-element 
-           (i.e., in the same word as the option name itself, for example, 
-           "-oarg"), then it is returned in optarg, otherwise optarg is set
-           to zero. */
-        if (optdecl[2] != ':') {
-          /* If the option was the last character in the string pointed to by
-             an element of argv, then optarg shall contain the next element
-             of argv, and optind shall be incremented by 2. If the resulting
-             value of optind is greater than argc, this indicates a missing 
-             option-argument, and getopt() shall return an error indication.
-
-             Otherwise, optarg shall point to the string following the
-             option character in that element of argv, and optind shall be
-             incremented by 1.
-          */
-          if (++optind < argc) {
-            optarg = argv[optind];
-          } else {
-            /* If it detects a missing option-argument, it shall return the 
-               colon character ( ':' ) if the first character of optstring
-               was a colon, or a question-mark character ( '?' ) otherwise.
-            */
-            optarg = NULL;
-            optchar = (optstring[0] == ':') ? ':' : '?';
-          }
-        } else {
-          optarg = NULL;
-        }
-      }
-
-      optcursor = NULL;
-    }
-  } else {
-    /* If getopt() encounters an option character that is not contained in 
-       optstring, it shall return the question-mark ( '?' ) character. */
-    optchar = '?';
-  }
-
-  if (optcursor == NULL || *++optcursor == '\0')
-    ++optind;
-
-  return optchar;
-
-no_more_optchars:
-  optcursor = NULL;
-  return -1;
-}
-
-/* Implementation based on [1].
-
-[1] http://www.kernel.org/doc/man-pages/online/pages/man3/getopt.3.html
-*/
-int getopt_long(int argc, char* const argv[], const char* optstring, 
-  const struct option* longopts, int* longindex) {
-  const struct option* o = longopts;
-  const struct option* match = NULL;
-  int num_matches = 0;
-  size_t argument_name_length = 0;
-  const char* current_argument = NULL;
-  int retval = -1;
-
-  optarg = NULL;
-  optopt = 0;
-
-  if (optind >= argc)
-    return -1;
-
-  if (strlen(argv[optind]) < 3 || strncmp(argv[optind], "--", 2) != 0)
-    return getopt(argc, argv, optstring);
-
-  /* It's an option; starts with -- and is longer than two chars. */
-  current_argument = argv[optind] + 2;
-  argument_name_length = strcspn(current_argument, "=");
-  for (; o->name; ++o) {
-    if (strncmp(o->name, current_argument, argument_name_length) == 0) {
-      match = o;
-      ++num_matches;
-    }
-  }
-
-  if (num_matches == 1) {
-    /* If longindex is not NULL, it points to a variable which is set to the
-       index of the long option relative to longopts. */
-    if (longindex)
-      *longindex = (match - longopts);
-
-    /* If flag is NULL, then getopt_long() shall return val. 
-       Otherwise, getopt_long() returns 0, and flag shall point to a variable
-       which shall be set to val if the option is found, but left unchanged if
-       the option is not found. */
-    if (match->flag)
-      *(match->flag) = match->val;
-
-    retval = match->flag ? 0 : match->val;
-
-    if (match->has_arg != no_argument) {
-      optarg = strchr(argv[optind], '=');
-      if (optarg != NULL)
-        ++optarg;
-
-      if (match->has_arg == required_argument) {
-        /* Only scan the next argv for required arguments. Behavior is not
-           specified, but has been observed with Ubuntu and Mac OSX. */
-        if (optarg == NULL && ++optind < argc) {
-          optarg = argv[optind];
-        }
-
-        if (optarg == NULL)
-          retval = ':';
-      }
-    } else if (strchr(argv[optind], '=')) {
-      /* An argument was provided to a non-argument option. 
-         I haven't seen this specified explicitly, but both GNU and BSD-based
-         implementations show this behavior.
-      */
-      retval = '?';
-    }
-  } else {
-    /* Unknown option or ambiguous match. */
-    retval = '?';
-  }
-
-  ++optind;
-  return retval;
-}

+ 0 - 31
bin/getopt.h

@@ -1,31 +0,0 @@
-#ifndef INCLUDED_GETOPT_PORT_H
-#define INCLUDED_GETOPT_PORT_H
-
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-extern const int no_argument;
-extern const int required_argument;
-extern const int optional_argument;
-
-extern char* optarg;
-extern int optind, opterr, optopt;
-
-struct option {
-  const char* name;
-  int has_arg;
-  int* flag;
-  int val;
-};
-
-int getopt(int argc, char* const argv[], const char* optstring);
-
-int getopt_long(int argc, char* const argv[],
-  const char* optstring, const struct option* longopts, int* longindex);
-
-#if defined(__cplusplus)
-}
-#endif
-
-#endif /* INCLUDED_GETOPT_PORT_H */

+ 8 - 3
bin/konf.c

@@ -12,10 +12,15 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
+#include <signal.h>
+
+#if WITH_INTERNAL_GETOPT
+#include "libc/getopt.h"
+#else
 #ifdef HAVE_GETOPT_H
 #include <getopt.h>
 #endif
-#include <signal.h>
+#endif
 
 #include "konf/net.h"
 #include "konf/query.h"
@@ -48,7 +53,7 @@ int main(int argc, char **argv)
 	sigset_t sigpipe_set;
 
 	static const char *shortopts = "hvs:";
-#ifdef HAVE_GETOPT_H
+#ifdef HAVE_GETOPT_LONG
 	static const struct option longopts[] = {
 		{"help",	0, NULL, 'h'},
 		{"version",	0, NULL, 'v'},
@@ -68,7 +73,7 @@ int main(int argc, char **argv)
 	/* Parse command line options */
 	while(1) {
 		int opt;
-#ifdef HAVE_GETOPT_H
+#ifdef HAVE_GETOPT_LONG
 		opt = getopt_long(argc, argv, shortopts, longopts, NULL);
 #else
 		opt = getopt(argc, argv, shortopts);

+ 8 - 2
bin/konfd.c

@@ -24,9 +24,15 @@
 #include <sys/select.h>
 #include <signal.h>
 #include <syslog.h>
+
+#if WITH_INTERNAL_GETOPT
+#include "libc/getopt.h"
+#else
 #ifdef HAVE_GETOPT_H
 #include <getopt.h>
 #endif
+#endif
+
 #ifdef HAVE_PWD_H
 #include <pwd.h>
 #endif
@@ -631,7 +637,7 @@ void opts_free(struct options *opts)
 static int opts_parse(int argc, char *argv[], struct options *opts)
 {
 	static const char *shortopts = "hvs:S:p:u:g:dr:O:";
-#ifdef HAVE_GETOPT_H
+#ifdef HAVE_GETOPT_LONG
 	static const struct option longopts[] = {
 		{"help",	0, NULL, 'h'},
 		{"version",	0, NULL, 'v'},
@@ -649,7 +655,7 @@ static int opts_parse(int argc, char *argv[], struct options *opts)
 	optind = 1;
 	while(1) {
 		int opt;
-#ifdef HAVE_GETOPT_H
+#ifdef HAVE_GETOPT_LONG
 		opt = getopt_long(argc, argv, shortopts, longopts, NULL);
 #else
 		opt = getopt(argc, argv, shortopts);

+ 7 - 2
bin/module.am

@@ -11,16 +11,21 @@ bin_clish_LDADD = \
 	libkonf.la \
 	libtinyrl.la \
 	liblub.la \
+	$(LIBOBJS) \
 	@CLISH_PLUGIN_BUILTIN_LIBS@
 
 bin_konfd_SOURCES = bin/konfd.c
 bin_konfd_LDADD = \
 	libkonf.la \
-	liblub.la
+	liblub.la \
+	$(LIBOBJS)
 
 bin_konf_SOURCES = bin/konf.c
 bin_konf_LDADD = \
 	libkonf.la \
-	liblub.la
+	liblub.la \
+	$(LIBOBJS)
 
 bin_sigexec_SOURCES = bin/sigexec.c
+bin_sigexec_LDADD = \
+	$(LIBOBJS)

+ 7 - 2
bin/sigexec.c

@@ -15,9 +15,14 @@
 #include <string.h>
 #include <unistd.h>
 #include <signal.h>
+
+#if WITH_INTERNAL_GETOPT
+#include "libc/getopt.h"
+#else
 #ifdef HAVE_GETOPT_H
 #include <getopt.h>
 #endif
+#endif
 
 #ifndef VERSION
 #define VERSION 1.5.6
@@ -33,7 +38,7 @@ int main(int argc, char *argv[])
 	sigset_t sigs;
 
 	static const char *shortopts = "+hv";
-#ifdef HAVE_GETOPT_H
+#ifdef HAVE_GETOPT_LONG
 	static const struct option longopts[] = {
 		{"help",	0, NULL, 'h'},
 		{"version",	0, NULL, 'v'},
@@ -43,7 +48,7 @@ int main(int argc, char *argv[])
 
 	while(1) {
 		int opt;
-#ifdef HAVE_GETOPT_H
+#ifdef HAVE_GETOPT_LONG
 		opt = getopt_long(argc, argv, shortopts, longopts, NULL);
 #else
 		opt = getopt(argc, argv, shortopts);

+ 26 - 3
configure.ac

@@ -29,6 +29,9 @@ AC_CONFIG_HEADERS([config.h])
 AM_INIT_AUTOMAKE(subdir-objects)
 AM_PROG_CC_C_O
 
+# Dir for libc replacements
+AC_CONFIG_LIBOBJ_DIR([libc])
+
 # needed to handle 64-bit architecture
 AC_CHECK_SIZEOF(int)
 AC_CHECK_SIZEOF(long)
@@ -488,10 +491,30 @@ AC_CHECK_HEADERS(regex.h, [],
     AC_MSG_ERROR([regex.h not found: regular expressions are not supported]))
 
 ################################
-# Check for getopt_long()
+# Internal getopt()
 ################################
-AC_CHECK_HEADERS(getopt.h, [],
-    AC_MSG_WARN([getopt.h not found: only short parameters can be used on command line]))
+AC_ARG_WITH(internal-getopt,
+            [AS_HELP_STRING([--with-internal-getopt],
+            [Use internal implementation of getopt [default=no]])],
+            [],
+            [with_internal_getopt=no])
+
+if test x$with_internal_getopt != xno; then
+    AC_DEFINE([WITH_INTERNAL_GETOPT], [1], [Use internal getopt() implementation])
+    AC_LIBOBJ([getopt])
+    AC_MSG_WARN([Use internal implementation of getopt() and getopt_long()])
+else
+    AC_CHECK_HEADERS(getopt.h, [found_getopt_h=yes],
+        AC_MSG_WARN([getopt.h not found: only short parameters can be used on command line]))
+fi
+
+AC_MSG_CHECKING([for getopt_long()])
+if test x$with_internal_getopt = xyes -o x$found_getopt_h = xyes; then
+    AC_DEFINE([HAVE_GETOPT_LONG], [1], [getopt_long() function])
+    AC_MSG_RESULT([yes])
+else
+    AC_MSG_RESULT([no])
+fi
 
 ################################
 # Check for locale.h

+ 6 - 2
konf/query/query.c

@@ -10,9 +10,13 @@
 #include <sys/wait.h>
 #include <errno.h>
 #include <assert.h>
+#if WITH_INTERNAL_GETOPT
+#include "libc/getopt.h"
+#else
 #ifdef HAVE_GETOPT_H
 #include <getopt.h>
 #endif
+#endif
 
 #include "lub/types.h"
 #include "lub/argv.h"
@@ -87,7 +91,7 @@ int konf_query_parse(konf_query_t *this, int argc, char **argv)
 	int pwdc = 0;
 
 	static const char *shortopts = "suoedtp:q:r:l:f:inh:";
-#ifdef HAVE_GETOPT_H
+#ifdef HAVE_GETOPT_LONG
 	static const struct option longopts[] = {
 		{"set",		0, NULL, 's'},
 		{"unset",	0, NULL, 'u'},
@@ -110,7 +114,7 @@ int konf_query_parse(konf_query_t *this, int argc, char **argv)
 	optind = 0; /* It must be 1 for QNX6. This system has no getopt.h */
 	while(1) {
 		int opt;
-#ifdef HAVE_GETOPT_H
+#ifdef HAVE_GETOPT_LONG
 		opt = getopt_long(argc, argv, shortopts, longopts, NULL);
 #else
 		opt = getopt(argc, argv, shortopts);

+ 466 - 0
libc/getopt.c

@@ -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;		/* if error message should be printed */
+int	optind = 1;		/* index into parent argv vector */
+int	optopt = '?';		/* character checked for validity */
+int	optreset;		/* reset getopt */
+char    *optarg;		/* argument associated with option */
+
+
+#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)
+/* XXX: GNU ignores PC if *options == '-' */
+#define IN_ORDER        (!IS_POSIXLY_CORRECT && *options == '-')
+
+/* return values */
+#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; /* option letter processing */
+
+/* XXX: set optreset to 1 rather than these two */
+static int nonopt_start = -1; /* first non option argument (for permute) */
+static int nonopt_end = -1;   /* first option after non options (for permute) */
+
+/* Error messages */
+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;
+
+
+/* Replacement for warnx(3) for systems without it. */
+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];
+			/* LINTED const cast */
+			((char **) nargv)[pos] = nargv[cstart];
+			/* LINTED const cast */
+			((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;				/* option letter list index */
+	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) {		/* update scanning pointer */
+		optreset = 0;
+		if (optind >= nargc) {          /* end of argument vector */
+			place = EMSG;
+			if (nonopt_end != -1) {
+				/* do permutation, if we have to */
+				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]) != '-') {  /* found non-option */
+			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;
+			}
+			/* do permutation */
+			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++;
+			/* process next argument */
+			goto start;
+		}
+		if (nonopt_start != -1 && nonopt_end == -1)
+			nonopt_end = optind;
+		if (place[1] && *++place == '-') {	/* found "--" */
+			place++;
+			return -2;
+		}
+	}
+	if ((optchar = (int)*place++) == (int)':' ||
+	    (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
+		/* option letter unknown or ':' */
+		if (!*place)
+			++optind;
+		if (PRINT_ERROR)
+			xwarnx(illoptchar, optchar);
+		optopt = optchar;
+		return BADCH;
+	}
+	if (optchar == 'W' && oli[1] == ';') {		/* -W long-option */
+		/* XXX: what if no long options provided (called by getopt)? */
+		if (*place)
+			return -2;
+
+		if (++optind >= nargc) {	/* no arg */
+			place = EMSG;
+			if (PRINT_ERROR)
+				xwarnx(recargchar, optchar);
+			optopt = optchar;
+			/* XXX: GNU returns '?' if options[0] != ':' */
+			return BADARG;
+		} else				/* white space */
+			place = nargv[optind];
+		/*
+		 * Handle -W arg the same as --arg (which causes getopt to
+		 * stop parsing).
+		 */
+		return -2;
+	}
+	if (*++oli != ':') {			/* doesn't take argument */
+		if (!*place)
+			++optind;
+	} else {				/* takes (optional) argument */
+		optarg = NULL;
+		if (*place)			/* no white space */
+			optarg = place;
+		/* XXX: disable test for :: if PC? (GNU doesn't) */
+		else if (oli[1] != ':') {	/* arg not optional */
+			if (++optind >= nargc) {	/* no arg */
+				place = EMSG;
+				if (PRINT_ERROR)
+					xwarnx(recargchar, optchar);
+				optopt = optchar;
+				/* XXX: GNU returns '?' if options[0] != ':' */
+				return BADARG;
+			} else
+				optarg = nargv[optind];
+		}
+		place = EMSG;
+		++optind;
+	}
+	/* dump back option letter */
+	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);
+	/* idx may be 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') {		/* found "--" */
+			/*
+			 * 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) {
+			/* argument found (--option=arg) */
+			current_argv_len = has_equal - current_argv;
+			has_equal++;
+		} else
+			current_argv_len = strlen(current_argv);
+
+		for (i = 0; long_options[i].name; i++) {
+			/* find matching long option */
+			if (strncmp(current_argv, long_options[i].name,
+			    current_argv_len))
+				continue;
+
+			if (strlen(long_options[i].name) ==
+			    (unsigned)current_argv_len) {
+				/* exact match */
+				match = i;
+				break;
+			}
+			if (match == -1)		/* partial match */
+				match = i;
+			else {
+				/* ambiguous abbreviation */
+				if (PRINT_ERROR)
+					xwarnx(ambig, (int)current_argv_len,
+					     current_argv);
+				optopt = 0;
+				return BADCH;
+			}
+		}
+		if (match != -1) {			/* option found */
+		        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;
+				/* XXX: GNU returns '?' if options[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;
+				/* XXX: GNU returns '?' if options[0] != ':' */
+				--optind;
+				return BADARG;
+			}
+		} else {			/* unknown option */
+			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;
+}

+ 74 - 0
libc/getopt.h

@@ -0,0 +1,74 @@
+
+/*-
+ * 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.h,v 1.4 2000/07/07 10:43:54 ad Exp $
+ */
+
+#ifndef _GETOPT_H_
+#define _GETOPT_H_
+
+/*
+ * Gnu like getopt_long() and BSD4.4 getsubopt()/optreset extensions
+ */
+#define no_argument        0
+#define required_argument  1
+#define optional_argument  2
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct option {
+	/* name of long option */
+	const char *name;
+	/*
+	 * one of no_argument, required_argument, and optional_argument:
+	 * whether option takes an argument
+	 */
+	int has_arg;
+	/* if not NULL, set *flag to val when option found */
+	int *flag;
+	/* if flag not NULL, value to set *flag to; else return value */
+	int val;
+};
+
+int getopt_long(int, char * const *, const char *,
+    const struct option *, int *);
+
+extern int optreset;
+extern char *optarg;
+extern int opterr;
+extern int optind;
+extern int optopt;
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* !_GETOPT_H_ */

+ 3 - 0
libc/module.am

@@ -0,0 +1,3 @@
+EXTRA_DIST += \
+	libc/getopt.c \
+	libc/getopt.h