diff --git a/lib/util/CMakeLists.txt b/lib/util/CMakeLists.txt index f4a2b08db17..8ceb6001b9b 100644 --- a/lib/util/CMakeLists.txt +++ b/lib/util/CMakeLists.txt @@ -1,3 +1,4 @@ # SPDX-License-Identifier: Apache-2.0 add_subdirectory_ifdef(CONFIG_FNMATCH fnmatch) +add_subdirectory_ifdef(CONFIG_GETOPT getopt) diff --git a/lib/util/Kconfig b/lib/util/Kconfig index 0907cbba4ae..70bf491cdee 100644 --- a/lib/util/Kconfig +++ b/lib/util/Kconfig @@ -6,4 +6,6 @@ menu "Util libraries" source "lib/util/fnmatch/Kconfig" +source "lib/util/getopt/Kconfig" + endmenu diff --git a/lib/util/getopt/CMakeLists.txt b/lib/util/getopt/CMakeLists.txt new file mode 100644 index 00000000000..9c400610d8d --- /dev/null +++ b/lib/util/getopt/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright (c) 2021 Nordic Semiconductor +# +# SPDX-License-Identifier: Apache-2.0 +# + +zephyr_include_directories_ifdef( + CONFIG_GETOPT + . +) + +zephyr_sources_ifdef( + CONFIG_GETOPT + getopt.c +) diff --git a/lib/util/getopt/Kconfig b/lib/util/getopt/Kconfig new file mode 100644 index 00000000000..0f60b8a6412 --- /dev/null +++ b/lib/util/getopt/Kconfig @@ -0,0 +1,7 @@ +# Copyright (c) 2021 Nordic Semiconductor +# SPDX-License-Identifier: Apache-2.0 + +config GETOPT + bool "GetOpt Support" + help + This option enables the getopt library diff --git a/lib/util/getopt/README b/lib/util/getopt/README new file mode 100644 index 00000000000..e0742b9554e --- /dev/null +++ b/lib/util/getopt/README @@ -0,0 +1,58 @@ +[GetOpt] +##################### + +Origin: + [Lattera FreeBSD] + [https://github.com/lattera/freebsd/blob/master/lib/libc/stdlib/getopt.c] + +Status: + [So far Zephyr samples were using getopt implementation from: argtable3.c.] + +Purpose: + [Shell users would like to parse options passed to the command handler.] + +Description: + [This library is going to be used by the shell module. Some shell users + are not satisfied with subcommands alone and need to use the options for + commands as well. A library is needed that allows the developer to parse + the arguments string, looking for supported options. Typically, this task + is accomplished by the getopt function. + + For this purpose I decided to port the getopt library available + in the FreeBSD project. I had to modify it so that it could be used + by all instances of the shell at the same time. + + Originally this function was using global variables which were defining + its state. In my implementation I put those variables in a structure + and a pointer to that structure is passed as an additional parameter + to getopt function. In proposed implementation each shell backend has its + own getopt function state structure which it uses. + + This module is intended to be used inside the shell command handler + by the abstraction layer "SHELL_GETOPT". For example: + while ((char c = shell_getopt(shell, argc, argv, "abhc:")) != -1) { + /* some code */ + } + ] + +Dependencies: + [This package does not depend on any other component. + + Zephyr project will only need this component if the user configures a shell + module: SHELL_GETOPT.] + +URL: + [https://github.com/lattera/freebsd] + +commit: + [324e4c082c14aebf00f8dbee0fb7ad285393756a] + +Maintained-by: + [External] + +License: + [BSD 3-Clause "New" or "Revised" License] + +License Link: + [BSD 3-Clause used in getopt: https://spdx.org/licenses/BSD-3-Clause.html] + [Project license: https://github.com/lattera/freebsd/blob/master/COPYRIGHT] diff --git a/lib/util/getopt/getopt.c b/lib/util/getopt/getopt.c new file mode 100644 index 00000000000..7ab963a1bfa --- /dev/null +++ b/lib/util/getopt/getopt.c @@ -0,0 +1,138 @@ +/* $NetBSD: getopt.c,v 1.26 2003/08/07 16:43:40 agc Exp $ */ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * 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. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#include +#include "getopt.h" + +#include +LOG_MODULE_REGISTER(getopt); + +#define BADCH ((int)'?') +#define BADARG ((int)':') +#define EMSG "" + +void getopt_init(struct getopt_state *state) +{ + state->opterr = 1; + state->optind = 1; + state->optopt = 0; + state->optreset = 0; + state->optarg = NULL; + + state->place = ""; /* EMSG */ +} + +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int +getopt(state, nargc, nargv, ostr) + struct getopt_state *state; + int nargc; + char *const nargv[]; + const char *ostr; +{ + char *oli; /* option letter list index */ + + if (state->optreset || *state->place == 0) { /* update scanning pointer */ + state->optreset = 0; + state->place = nargv[state->optind]; + if (state->optind >= nargc || *state->place++ != '-') { + /* Argument is absent or is not an option */ + state->place = EMSG; + return -1; + } + state->optopt = *state->place++; + if (state->optopt == '-' && *state->place == 0) { + /* "--" => end of options */ + ++state->optind; + state->place = EMSG; + return -1; + } + if (state->optopt == 0) { + /* Solitary '-', treat as a '-' option + * if the program (eg su) is looking for it. + */ + state->place = EMSG; + if (strchr(ostr, '-') == NULL) { + return -1; + } + state->optopt = '-'; + } + } else { + state->optopt = *state->place++; + } + + /* See if option letter is one the caller wanted... */ + oli = strchr(ostr, state->optopt); + if (state->optopt == ':' || oli == NULL) { + if (*state->place == 0) { + ++state->optind; + } + if (state->opterr && *ostr != ':') { + LOG_ERR("illegal option -- %c", state->optopt); + } + return BADCH; + } + + /* Does this option need an argument? */ + if (oli[1] != ':') { + /* don't need argument */ + state->optarg = NULL; + if (*state->place == 0) { + ++state->optind; + } + } else { + /* Option-argument is either the rest of this argument or the + * entire next argument. + */ + if (*state->place) { + state->optarg = state->place; + } else if (nargc > ++state->optind) { + state->optarg = nargv[state->optind]; + } else { + /* option-argument absent */ + state->place = EMSG; + if (*ostr == ':') { + return BADARG; + } + if (state->opterr) { + LOG_ERR("option requires an argument -- %c", + state->optopt); + } + return BADCH; + } + state->place = EMSG; + ++state->optind; + } + return state->optopt; /* return option letter */ +} diff --git a/lib/util/getopt/getopt.h b/lib/util/getopt/getopt.h new file mode 100644 index 00000000000..c1fb22ab3bd --- /dev/null +++ b/lib/util/getopt/getopt.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _GETOPT_H__ +#define _GETOPT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +struct getopt_state { + int opterr; /* if error message should be printed */ + int optind; /* index into parent argv vector */ + int optopt; /* character checked for validity */ + int optreset; /* reset getopt */ + char *optarg; /* argument associated with option */ + + char *place; /* option letter processing */ +}; + +/* Function intializes getopt_state structure */ +void getopt_init(struct getopt_state *state); + +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int getopt(struct getopt_state *const state, int nargc, + char *const nargv[], const char *ostr); + + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H__ */