summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS1
-rw-r--r--doc/api/getopt.rst8
-rw-r--r--doc/api/index.rst1
-rw-r--r--include/getopt.h130
-rw-r--r--lib/Kconfig5
-rw-r--r--lib/Makefile1
-rw-r--r--lib/getopt.c125
7 files changed, 271 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 1505e74559..874cf2c0e5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -758,6 +758,7 @@ T: git https://gitlab.denx.de/u-boot/u-boot.git
F: common/log*
F: cmd/log.c
F: doc/develop/logging.rst
+F: lib/getopt.c
F: test/log/
F: test/py/tests/test_log.py
diff --git a/doc/api/getopt.rst b/doc/api/getopt.rst
new file mode 100644
index 0000000000..773f79aeb6
--- /dev/null
+++ b/doc/api/getopt.rst
@@ -0,0 +1,8 @@
+.. SPDX-License-Identifier: GPL-2.0+
+.. Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+
+Option Parsing
+==============
+
+.. kernel-doc:: include/getopt.h
+ :internal:
diff --git a/doc/api/index.rst b/doc/api/index.rst
index 787b6778e5..ae4a1b6c63 100644
--- a/doc/api/index.rst
+++ b/doc/api/index.rst
@@ -8,6 +8,7 @@ U-Boot API documentation
dfu
efi
+ getopt
linker_lists
pinctrl
rng
diff --git a/include/getopt.h b/include/getopt.h
new file mode 100644
index 0000000000..6f5811e64b
--- /dev/null
+++ b/include/getopt.h
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * getopt.h - a simple getopt(3) implementation.
+ *
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ * Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ */
+
+#ifndef __GETOPT_H
+#define __GETOPT_H
+
+/**
+ * struct getopt_state - Saved state across getopt() calls
+ */
+struct getopt_state {
+ /**
+ * @index: Index of the next unparsed argument of @argv. If getopt() has
+ * parsed all of @argv, then @index will equal @argc.
+ */
+ int index;
+ /* private: */
+ /** @arg_index: Index within the current argument */
+ int arg_index;
+ union {
+ /* public: */
+ /**
+ * @opt: Option being parsed when an error occurs. @opt is only
+ * valid when getopt() returns ``?`` or ``:``.
+ */
+ int opt;
+ /**
+ * @arg: The argument to an option, NULL if there is none. @arg
+ * is only valid when getopt() returns an option character.
+ */
+ char *arg;
+ /* private: */
+ };
+};
+
+/**
+ * getopt_init_state() - Initialize a &struct getopt_state
+ * @gs: The state to initialize
+ *
+ * This must be called before using @gs with getopt().
+ */
+void getopt_init_state(struct getopt_state *gs);
+
+int __getopt(struct getopt_state *gs, int argc, char *const argv[],
+ const char *optstring, bool silent);
+
+/**
+ * getopt() - Parse short command-line options
+ * @gs: Internal state and out-of-band return arguments. This must be
+ * initialized with getopt_init_context() beforehand.
+ * @argc: Number of arguments, not including the %NULL terminator
+ * @argv: Argument list, terminated by %NULL
+ * @optstring: Option specification, as described below
+ *
+ * getopt() parses short options. Short options are single characters. They may
+ * be followed by a required argument or an optional argument. Arguments to
+ * options may occur in the same argument as an option (like ``-larg``), or
+ * in the following argument (like ``-l arg``). An argument containing
+ * options begins with a ``-``. If an option expects no arguments, then it may
+ * be immediately followed by another option (like ``ls -alR``).
+ *
+ * @optstring is a list of accepted options. If an option is followed by ``:``
+ * in @optstring, then it expects a mandatory argument. If an option is followed
+ * by ``::`` in @optstring, it expects an optional argument. @gs.arg points
+ * to the argument, if one is parsed.
+ *
+ * getopt() stops parsing options when it encounters the first non-option
+ * argument, when it encounters the argument ``--``, or when it runs out of
+ * arguments. For example, in ``ls -l foo -R``, option parsing will stop when
+ * getopt() encounters ``foo``, if ``l`` does not expect an argument. However,
+ * the whole list of arguments would be parsed if ``l`` expects an argument.
+ *
+ * An example invocation of getopt() might look like::
+ *
+ * char *argv[] = { "program", "-cbx", "-a", "foo", "bar", 0 };
+ * int opt, argc = ARRAY_SIZE(argv) - 1;
+ * struct getopt_state gs;
+ *
+ * getopt_init_state(&gs);
+ * while ((opt = getopt(&gs, argc, argv, "a::b:c")) != -1)
+ * printf("opt = %c, index = %d, arg = \"%s\"\n", opt, gs.index, gs.arg);
+ * printf("%d argument(s) left\n", argc - gs.index);
+ *
+ * and would produce an output of::
+ *
+ * opt = c, index = 1, arg = "<NULL>"
+ * opt = b, index = 2, arg = "x"
+ * opt = a, index = 4, arg = "foo"
+ * 1 argument(s) left
+ *
+ * For further information, refer to the getopt(3) man page.
+ *
+ * Return:
+ * * An option character if an option is found. @gs.arg is set to the
+ * argument if there is one, otherwise it is set to ``NULL``.
+ * * ``-1`` if there are no more options, if a non-option argument is
+ * encountered, or if an ``--`` argument is encountered.
+ * * ``'?'`` if we encounter an option not in @optstring. @gs.opt is set to
+ * the unknown option.
+ * * ``':'`` if an argument is required, but no argument follows the
+ * option. @gs.opt is set to the option missing its argument.
+ *
+ * @gs.index is always set to the index of the next unparsed argument in @argv.
+ */
+static inline int getopt(struct getopt_state *gs, int argc,
+ char *const argv[], const char *optstring)
+{
+ return __getopt(gs, argc, argv, optstring, false);
+}
+
+/**
+ * getopt_silent() - Parse short command-line options silently
+ * @gs: State
+ * @argc: Argument count
+ * @argv: Argument list
+ * @optstring: Option specification
+ *
+ * Same as getopt(), except no error messages are printed.
+ */
+static inline int getopt_silent(struct getopt_state *gs, int argc,
+ char *const argv[], const char *optstring)
+{
+ return __getopt(gs, argc, argv, optstring, true);
+}
+
+#endif /* __GETOPT_H */
diff --git a/lib/Kconfig b/lib/Kconfig
index 37aae73a26..79651eaad1 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -550,6 +550,11 @@ config SPL_HEXDUMP
This enables functions for printing dumps of binary data in
SPL.
+config GETOPT
+ bool "Enable getopt"
+ help
+ This enables functions for parsing command-line options.
+
config OF_LIBFDT
bool "Enable the FDT library"
default y if OF_CONTROL
diff --git a/lib/Makefile b/lib/Makefile
index 0cd7bea282..7c7fb9aae7 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -106,6 +106,7 @@ obj-y += string.o
obj-y += tables_csum.o
obj-y += time.o
obj-y += hexdump.o
+obj-$(CONFIG_GETOPT) += getopt.o
obj-$(CONFIG_TRACE) += trace.o
obj-$(CONFIG_LIB_UUID) += uuid.o
obj-$(CONFIG_LIB_RAND) += rand.o
diff --git a/lib/getopt.c b/lib/getopt.c
new file mode 100644
index 0000000000..8b4515dc19
--- /dev/null
+++ b/lib/getopt.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * getopt.c - a simple getopt(3) implementation. See getopt.h for explanation.
+ *
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ * Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+ */
+
+#define LOG_CATEGORY LOGC_CORE
+
+#include <common.h>
+#include <getopt.h>
+#include <log.h>
+
+void getopt_init_state(struct getopt_state *gs)
+{
+ gs->index = 1;
+ gs->arg_index = 1;
+}
+
+int __getopt(struct getopt_state *gs, int argc, char *const argv[],
+ const char *optstring, bool silent)
+{
+ char curopt; /* current option character */
+ const char *curoptp; /* pointer to the current option in optstring */
+
+ while (1) {
+ log_debug("arg_index: %d index: %d\n", gs->arg_index,
+ gs->index);
+
+ /* `--` indicates the end of options */
+ if (gs->arg_index == 1 && argv[gs->index] &&
+ !strcmp(argv[gs->index], "--")) {
+ gs->index++;
+ return -1;
+ }
+
+ /* Out of arguments */
+ if (gs->index >= argc)
+ return -1;
+
+ /* Can't parse non-options */
+ if (*argv[gs->index] != '-')
+ return -1;
+
+ /* We have found an option */
+ curopt = argv[gs->index][gs->arg_index];
+ if (curopt)
+ break;
+ /*
+ * no more options in current argv[] element; try the next one
+ */
+ gs->index++;
+ gs->arg_index = 1;
+ }
+
+ /* look up current option in optstring */
+ curoptp = strchr(optstring, curopt);
+
+ if (!curoptp) {
+ if (!silent)
+ printf("%s: invalid option -- %c\n", argv[0], curopt);
+ gs->opt = curopt;
+ gs->arg_index++;
+ return '?';
+ }
+
+ if (*(curoptp + 1) != ':') {
+ /* option with no argument. Just return it */
+ gs->arg = NULL;
+ gs->arg_index++;
+ return curopt;
+ }
+
+ if (*(curoptp + 1) && *(curoptp + 2) == ':') {
+ /* optional argument */
+ if (argv[gs->index][gs->arg_index + 1]) {
+ /* optional argument with directly following arg */
+ gs->arg = argv[gs->index++] + gs->arg_index + 1;
+ gs->arg_index = 1;
+ return curopt;
+ }
+ if (gs->index + 1 == argc) {
+ /* We are at the last argv[] element */
+ gs->arg = NULL;
+ gs->index++;
+ return curopt;
+ }
+ if (*argv[gs->index + 1] != '-') {
+ /*
+ * optional argument with arg in next argv[] element
+ */
+ gs->index++;
+ gs->arg = argv[gs->index++];
+ gs->arg_index = 1;
+ return curopt;
+ }
+
+ /* no optional argument found */
+ gs->arg = NULL;
+ gs->arg_index = 1;
+ gs->index++;
+ return curopt;
+ }
+
+ if (argv[gs->index][gs->arg_index + 1]) {
+ /* required argument with directly following arg */
+ gs->arg = argv[gs->index++] + gs->arg_index + 1;
+ gs->arg_index = 1;
+ return curopt;
+ }
+
+ gs->index++;
+ gs->arg_index = 1;
+
+ if (gs->index >= argc || argv[gs->index][0] == '-') {
+ if (!silent)
+ printf("option requires an argument -- %c\n", curopt);
+ gs->opt = curopt;
+ return ':';
+ }
+
+ gs->arg = argv[gs->index++];
+ return curopt;
+}