lib: add freebsd getopt library

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.

Signed-off-by: Jakub Rzeszutko <jakub.rzeszutko@nordicsemi.no>
This commit is contained in:
Jakub Rzeszutko 2021-01-15 14:01:12 +01:00 committed by Anas Nashif
commit ddb07f57ec
7 changed files with 261 additions and 0 deletions

View file

@ -1,3 +1,4 @@
# SPDX-License-Identifier: Apache-2.0
add_subdirectory_ifdef(CONFIG_FNMATCH fnmatch)
add_subdirectory_ifdef(CONFIG_GETOPT getopt)

View file

@ -6,4 +6,6 @@ menu "Util libraries"
source "lib/util/fnmatch/Kconfig"
source "lib/util/getopt/Kconfig"
endmenu

View file

@ -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
)

7
lib/util/getopt/Kconfig Normal file
View file

@ -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

58
lib/util/getopt/README Normal file
View file

@ -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]

138
lib/util/getopt/getopt.c Normal file
View file

@ -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 <string.h>
#include "getopt.h"
#include <logging/log.h>
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 */
}

41
lib/util/getopt/getopt.h Normal file
View file

@ -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 <zephyr.h>
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__ */