[syslinux:master] com32: add getopt_long() to the library

syslinux-bot for H. Peter Anvin hpa at zytor.com
Fri Jul 2 18:00:34 PDT 2010


Commit-ID:  9c6db1a0f333ba89961bbf92e155e9fc597f6d7b
Gitweb:     http://syslinux.zytor.com/commit/9c6db1a0f333ba89961bbf92e155e9fc597f6d7b
Author:     H. Peter Anvin <hpa at zytor.com>
AuthorDate: Fri, 2 Jul 2010 17:57:17 -0700
Committer:  H. Peter Anvin <hpa at zytor.com>
CommitDate: Fri, 2 Jul 2010 17:57:17 -0700

com32: add getopt_long() to the library

Add getopt_long() to the library.  This is code lifted straight out of
klibc.

Signed-off-by: H. Peter Anvin <hpa at zytor.com>


---
 com32/include/getopt.h  |   22 +++++++
 com32/lib/Makefile      |    3 +-
 com32/lib/getopt_long.c |  152 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 176 insertions(+), 1 deletions(-)

diff --git a/com32/include/getopt.h b/com32/include/getopt.h
new file mode 100644
index 0000000..71c41cd
--- /dev/null
+++ b/com32/include/getopt.h
@@ -0,0 +1,22 @@
+#ifndef _GETOPT_H
+#define _GETOPT_H
+
+#include <klibc/extern.h>
+
+struct option {
+	const char *name;
+	int has_arg;
+	int *flag;
+	int val;
+};
+
+enum {
+	no_argument	  = 0,
+	required_argument = 1,
+	optional_argument = 2,
+};
+
+__extern int getopt_long(int, char *const *, const char *,
+			 const struct option *, int *);
+
+#endif /* _GETOPT_H */
diff --git a/com32/lib/Makefile b/com32/lib/Makefile
index 93643ce..e8e89c0 100644
--- a/com32/lib/Makefile
+++ b/com32/lib/Makefile
@@ -11,7 +11,8 @@ LIBOBJS = \
 	abort.o atexit.o atoi.o atol.o atoll.o calloc.o creat.o		\
 	ctypes.o errno.o fgetc.o fgets.o fopen.o fprintf.o fputc.o	\
 	fclose.o putchar.o setjmp.o					\
-	fputs.o fread2.o fread.o free.o fwrite2.o fwrite.o getopt.o	\
+	fputs.o fread2.o fread.o free.o fwrite2.o fwrite.o 		\
+	getopt.o getopt_long.o						\
 	lrand48.o malloc.o stack.o memccpy.o memchr.o memcmp.o		\
 	memcpy.o mempcpy.o memmem.o memmove.o memset.o memswap.o	\
 	exit.o onexit.o	\
diff --git a/com32/lib/getopt_long.c b/com32/lib/getopt_long.c
new file mode 100644
index 0000000..e3d064b
--- /dev/null
+++ b/com32/lib/getopt_long.c
@@ -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(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 '?';
+	}
+}



More information about the Syslinux-commits mailing list