[PATCH weston 1/2] shared/option-parser: Rework option parsing

Quentin Glidic sardemff7+wayland at sardemff7.net
Mon Jan 27 21:36:09 PST 2014


From: Quentin Glidic <sardemff7+git at sardemff7.net>

Long options with argument support two formats: equal ("--long=arg") and
space ("--long arg")
Short options now support three formats: short ("-sarg"), equal
("-s=arg") and space ("-s value")

Provide a test program

Signed-off-by: Quentin Glidic <sardemff7+git at sardemff7.net>
---
 man/weston.man             |   4 +
 shared/option-parser.c     | 111 ++++++++++++---
 tests/Makefile.am          |   9 +-
 tests/option-parser-test.c | 342 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 447 insertions(+), 19 deletions(-)
 create mode 100644 tests/option-parser-test.c

diff --git a/man/weston.man b/man/weston.man
index f2d1b4c..0f686dc 100644
--- a/man/weston.man
+++ b/man/weston.man
@@ -92,6 +92,10 @@ and
 .\" ***************************************************************
 .SH OPTIONS
 .
+Long options with an argument support two format: equal ("--long=arg") and
+space ("--long arg"). Short options with argument support three format: short
+("-sarg"), equal ("-s=arg") and space ("-s arg").
+.
 .SS Weston core options:
 .TP
 \fB\-\^B\fR\fIbackend.so\fR, \fB\-\-backend\fR=\fIbackend.so\fR
diff --git a/shared/option-parser.c b/shared/option-parser.c
index c00349a..da9c394 100644
--- a/shared/option-parser.c
+++ b/shared/option-parser.c
@@ -30,22 +30,34 @@
 
 #include "config-parser.h"
 
-static void
-handle_option(const struct weston_option *option, char *value)
+enum state {
+	OK,
+	EATEN,
+	ERROR
+};
+
+static enum state
+handle_option(const struct weston_option *option, const char *value)
 {
 	switch (option->type) {
 	case WESTON_OPTION_INTEGER:
+		if (value == NULL || value[0] == '\0')
+			return ERROR;
 		* (int32_t *) option->data = strtol(value, NULL, 0);
-		return;
+		return EATEN;
 	case WESTON_OPTION_UNSIGNED_INTEGER:
+		if (value == NULL || value[0] == '\0')
+			return ERROR;
 		* (uint32_t *) option->data = strtoul(value, NULL, 0);
-		return;
+		return EATEN;
 	case WESTON_OPTION_STRING:
+		if (value == NULL)
+			return ERROR;
 		* (char **) option->data = strdup(value);
-		return;
+		return EATEN;
 	case WESTON_OPTION_BOOLEAN:
 		* (int32_t *) option->data = 1;
-		return;
+		return OK;
 	default:
 		assert(0);
 	}
@@ -56,26 +68,89 @@ parse_options(const struct weston_option *options,
 	      int count, int *argc, char *argv[])
 {
 	int i, j, k, len = 0;
+	const char *arg, *value;
 
 	for (i = 1, j = 1; i < *argc; i++) {
-		for (k = 0; k < count; k++) {
-			if (options[k].name)
+		arg = argv[i];
+		value = argv[i+1];
+
+		if (arg[0] != '-')
+		{
+			/* Not an option, just skip it */
+			argv[j++] = argv[i];
+			continue;
+		}
+		++arg; /* Skip the first dash */
+
+		/* We have an option, check for which one */
+		if (arg[0] == '-') {
+			/* Long option */
+			++arg; /* Skip the second dash */
+			for (k = 0; k < count; k++) {
+				if (!options[k].name)
+					/* No long variant for this option */
+					continue;
+
 				len = strlen(options[k].name);
-			if (options[k].name &&
-			    argv[i][0] == '-' &&
-			    argv[i][1] == '-' &&
-			    strncmp(options[k].name, &argv[i][2], len) == 0 &&
-			    (argv[i][len + 2] == '=' || argv[i][len + 2] == '\0')) {
-				handle_option(&options[k], &argv[i][len + 3]);
+				if (strncmp(options[k].name, arg, len) != 0)
+					/* Not matching */
+					continue;
+
+				switch (arg[len]) {
+				case '=': value = arg + (len+1);
+				case '\0': break;
+				default: continue; /* Not fully matching */
+				}
+
+				switch (handle_option(&options[k], value)) {
+				case OK: break;
+				case EATEN:
+					if (arg[len] == '\0')
+						++i;
+					break;
+				case ERROR: return -1;
+				}
+
 				break;
-			} else if (options[k].short_name &&
-				   argv[i][0] == '-' &&
-				   options[k].short_name == argv[i][1]) {
-				handle_option(&options[k], &argv[i][2]);
+			}
+		} else {
+			/* Short option */
+			for (k = 0; k < count; k++) {
+				if (!options[k].short_name)
+					/* no short variant for this option */
+					continue;
+
+				if (options[k].short_name != arg[0])
+					/* Not matching */
+					continue;
+
+				if (arg[1] != '\0')
+					value = arg+1;
+
+				switch (arg[1]) {
+				case '\0': break;
+				case '=': value = arg + 2; break;
+				default: value = arg + 1;
+				}
+				//fprintf(stderr, "arg = %s, value = '%s'\n", arg, value);
+
+				switch (handle_option(&options[k], value)) {
+				case OK:
+					if (arg[1] != '\0')
+						/* Do not support merged short options */
+						return -1;
+					break;
+				case EATEN:
+					if (arg[1] == '\0')
+						++i;
+					break;
+				case ERROR: return -1;
+				}
 				break;
 			}
 		}
 		if (k == count)
+			/* None matched */
 			argv[j++] = argv[i];
 	}
 	argv[j] = NULL;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 8b85146..00e9e23 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -2,7 +2,8 @@ TESTS = $(shared_tests) $(module_tests) $(weston_tests)
 
 shared_tests = \
 	config-parser.test		\
-	vertex-clip.test
+	vertex-clip.test		\
+	option-parser.test
 
 module_tests =				\
 	surface-test.la			\
@@ -55,6 +56,12 @@ AM_CPPFLAGS =					\
 	-DUNIT_TEST				\
 	$(COMPOSITOR_CFLAGS)
 
+option_parser_test_SOURCES =		\
+	option-parser-test.c		\
+	$(weston_test_runner_src)
+option_parser_test_LDADD =		\
+	../shared/libshared.la
+
 surface_global_test_la_SOURCES = surface-global-test.c
 surface_global_test_la_LDFLAGS = -module -avoid-version -rpath $(libdir)
 surface_test_la_SOURCES = surface-test.c
diff --git a/tests/option-parser-test.c b/tests/option-parser-test.c
new file mode 100644
index 0000000..0329122
--- /dev/null
+++ b/tests/option-parser-test.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright © 2013 Quentin "Sardem FF7" Glidic
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include "weston-test-runner.h"
+#include "config-parser.h"
+
+#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
+
+static char *str = NULL;
+static int32_t i = 0;
+static uint32_t u = 0;
+static uint8_t b = 0;
+
+const struct weston_option test_options[] = {
+	{ WESTON_OPTION_STRING,           "string", 's', &str },
+	{ WESTON_OPTION_INTEGER,          "int",    'i', &i },
+	{ WESTON_OPTION_UNSIGNED_INTEGER, "uint",   'u', &u },
+	{ WESTON_OPTION_BOOLEAN,          "bool",   'b', &b },
+};
+
+static void reset(void) {
+	str = NULL;
+	i = -1;
+	u = 0;
+	b = 0;
+}
+
+TEST(option_parser_test_empty)
+{
+	reset();
+	char *argv[] = {
+		"test",
+		NULL
+	};
+	int argc = ARRAY_LENGTH(argv) - 1;
+
+	int r = parse_options(test_options, ARRAY_LENGTH(test_options), &argc, argv);
+	assert(r == 1);
+	assert(argc == r);
+}
+
+TEST(option_parser_test_extra)
+{
+	reset();
+	char *argv[] = {
+		"test",
+		"extra",
+		NULL
+	};
+	int argc = ARRAY_LENGTH(argv) - 1;
+
+	int r = parse_options(test_options, ARRAY_LENGTH(test_options), &argc, argv);
+	assert(r == 2);
+	assert(argc == r);
+}
+
+TEST(option_parser_test_long_equal)
+{
+	reset();
+	char *argv[] = {
+		"test",
+		"--string=s",
+		"--int=-10",
+		"--uint=10",
+		"--bool",
+		NULL
+	};
+	int argc = ARRAY_LENGTH(argv) - 1;
+
+	int r = parse_options(test_options, ARRAY_LENGTH(test_options), &argc, argv);
+	assert(r == 1);
+	assert(argc == r);
+	assert(strcmp(str, "s") == 0);
+	assert(i == -10);
+	assert(u == 10);
+	assert(b);
+}
+
+TEST(option_parser_test_long_equal_extra)
+{
+	reset();
+	char *argv[] = {
+		"test",
+		"--string=s",
+		"--int=-10",
+		"--uint=10",
+		"--bool",
+		"extra",
+		NULL
+	};
+	int argc = ARRAY_LENGTH(argv) - 1;
+
+	int r = parse_options(test_options, ARRAY_LENGTH(test_options), &argc, argv);
+	assert(r == 2);
+	assert(argc == r);
+	assert(strcmp(str, "s") == 0);
+	assert(i == -10);
+	assert(u == 10);
+	assert(b);
+}
+
+TEST(option_parser_test_long_space)
+{
+	reset();
+	char *argv[] = {
+		"test",
+		"--string", "s",
+		"--int", "-10",
+		"--uint", "10",
+		"--bool",
+		NULL
+	};
+	int argc = ARRAY_LENGTH(argv) - 1;
+
+	int r = parse_options(test_options, ARRAY_LENGTH(test_options), &argc, argv);
+	assert(r == 1);
+	assert(argc == r);
+	assert(strcmp(str, "s") == 0);
+	assert(i == -10);
+	assert(u == 10);
+	assert(b);
+}
+
+TEST(option_parser_test_long_space_extra)
+{
+	reset();
+	char *argv[] = {
+		"test",
+		"--string", "s",
+		"--int", "-10",
+		"--uint", "10",
+		"--bool",
+		"extra",
+		NULL
+	};
+	int argc = ARRAY_LENGTH(argv) - 1;
+
+	int r = parse_options(test_options, ARRAY_LENGTH(test_options), &argc, argv);
+	assert(r == 2);
+	assert(argc == r);
+	assert(strcmp(str, "s") == 0);
+	assert(i == -10);
+	assert(u == 10);
+	assert(b);
+}
+
+TEST(option_parser_test_short)
+{
+	reset();
+	char *argv[] = {
+		"test",
+		"-ss",
+		"-i-10",
+		"-u10",
+		"-b",
+		NULL
+	};
+	int argc = ARRAY_LENGTH(argv) - 1;
+
+	int r = parse_options(test_options, ARRAY_LENGTH(test_options), &argc, argv);
+	assert(r == 1);
+	assert(argc == r);
+	assert(strcmp(str, "s") == 0);
+	assert(i == -10);
+	assert(u == 10);
+	assert(b);
+}
+
+TEST(option_parser_test_short_equal)
+{
+	reset();
+	char *argv[] = {
+		"test",
+		"-s=s",
+		"-i=-10",
+		"-u=10",
+		"-b",
+		NULL
+	};
+	int argc = ARRAY_LENGTH(argv) - 1;
+
+	int r = parse_options(test_options, ARRAY_LENGTH(test_options), &argc, argv);
+	assert(r == 1);
+	assert(argc == r);
+	assert(strcmp(str, "s") == 0);
+	assert(i == -10);
+	assert(u == 10);
+	assert(b);
+}
+
+TEST(option_parser_test_short_space)
+{
+	reset();
+	char *argv[] = {
+		"test",
+		"-s", "s",
+		"-i", "-10",
+		"-u", "10",
+		"-b",
+		NULL
+	};
+	int argc = ARRAY_LENGTH(argv) - 1;
+
+	int r = parse_options(test_options, ARRAY_LENGTH(test_options), &argc, argv);
+	assert(r == 1);
+	assert(argc == r);
+	assert(strcmp(str, "s") == 0);
+	assert(i == -10);
+	assert(u == 10);
+	assert(b);
+}
+
+TEST(option_parser_test_long_equal_missing_right)
+{
+	reset();
+	char *argv[] = {
+		"test",
+		"--string=",
+		NULL
+	};
+	int argc = ARRAY_LENGTH(argv) - 1;
+
+	int r = parse_options(test_options, ARRAY_LENGTH(test_options), &argc, argv);
+	assert(r == 1);
+	assert(argc == r);
+	assert(strcmp(str, "") == 0);
+	assert(i == -1);
+	assert(u == 0);
+	assert(!b);
+}
+
+TEST(option_parser_test_long_equal_missing_wrong)
+{
+	reset();
+	char *argv[] = {
+		"test",
+		"--int=",
+		NULL
+	};
+	int argc = ARRAY_LENGTH(argv) - 1;
+
+	int r = parse_options(test_options, ARRAY_LENGTH(test_options), &argc, argv);
+	assert(r == -1);
+}
+
+TEST(option_parser_test_long_space_missing)
+{
+	reset();
+	char *argv[] = {
+		"test",
+		"--string",
+		NULL
+	};
+	int argc = ARRAY_LENGTH(argv) - 1;
+
+	int r = parse_options(test_options, ARRAY_LENGTH(test_options), &argc, argv);
+	assert(r == -1);
+}
+
+TEST(option_parser_test_short_space_missing)
+{
+	reset();
+	char *argv[] = {
+		"test",
+		"-s",
+		NULL
+	};
+	int argc = ARRAY_LENGTH(argv) - 1;
+
+	int r = parse_options(test_options, ARRAY_LENGTH(test_options), &argc, argv);
+	assert(r == -1);
+}
+
+TEST(option_parser_test_short_equal_missing_right)
+{
+	reset();
+	char *argv[] = {
+		"test",
+		"-s=",
+		NULL
+	};
+	int argc = ARRAY_LENGTH(argv) - 1;
+
+	int r = parse_options(test_options, ARRAY_LENGTH(test_options), &argc, argv);
+	assert(r == 1);
+	assert(argc == r);
+	assert(strcmp(str, "") == 0);
+	assert(i == -1);
+	assert(u == 0);
+	assert(!b);
+}
+
+TEST(option_parser_test_short_equal_missing_wrong)
+{
+	reset();
+	char *argv[] = {
+		"test",
+		"-i=",
+		NULL
+	};
+	int argc = ARRAY_LENGTH(argv) - 1;
+
+	int r = parse_options(test_options, ARRAY_LENGTH(test_options), &argc, argv);
+	assert(r == -1);
+}
+
+TEST(option_parser_test_short_concat)
+{
+	reset();
+	char *argv[] = {
+		"test",
+		"-bcd",
+		NULL
+	};
+	int argc = ARRAY_LENGTH(argv) - 1;
+
+	int r = parse_options(test_options, ARRAY_LENGTH(test_options), &argc, argv);
+	assert(r == -1);
+}
-- 
1.8.5.3



More information about the wayland-devel mailing list