lib: getopt: rework and extend getopt library
Getopt has been rework in this way that calling it does not require extra state parameter and its execution is thread safe. Global parameters describing the state of the getopt function have been made available to ensure full API compatibility in using this library. However, referencing these global variables directly is not thread safe. In order to get the state of the getopt function for the thread that is currently using it, call: getopt_state_get(); Extended the library with getopt_long and getopt_long_only functions. Moved getopt libary from utils to posix. Signed-off-by: Jakub Rzeszutko <jakub.rzeszutko@nordicsemi.no>
This commit is contained in:
parent
7f1f1229e1
commit
d4559f53fa
22 changed files with 978 additions and 179 deletions
|
@ -17,7 +17,7 @@
|
|||
#include <sys/util.h>
|
||||
|
||||
#if defined CONFIG_SHELL_GETOPT
|
||||
#include <shell/shell_getopt.h>
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -71,7 +71,6 @@ extern "C" {
|
|||
* @{
|
||||
*/
|
||||
|
||||
struct getopt_state;
|
||||
struct shell_static_entry;
|
||||
|
||||
/**
|
||||
|
@ -707,7 +706,7 @@ struct shell_ctx {
|
|||
|
||||
#if defined CONFIG_SHELL_GETOPT
|
||||
/*!< getopt context for a shell backend. */
|
||||
struct getopt_state getopt_state;
|
||||
struct getopt_state getopt;
|
||||
#endif
|
||||
|
||||
uint16_t cmd_buff_len; /*!< Command length.*/
|
||||
|
@ -1028,40 +1027,6 @@ void shell_help(const struct shell *shell);
|
|||
/* @brief Command's help has been printed */
|
||||
#define SHELL_CMD_HELP_PRINTED (1)
|
||||
|
||||
#if defined CONFIG_SHELL_GETOPT
|
||||
/**
|
||||
* @brief Parses the command-line arguments.
|
||||
*
|
||||
* It is based on FreeBSD implementation.
|
||||
*
|
||||
* @param[in] shell Pointer to the shell instance.
|
||||
* @param[in] argc Arguments count.
|
||||
* @param[in] argv Arguments.
|
||||
* @param[in] ostr String containing the legitimate option characters.
|
||||
*
|
||||
* @return If an option was successfully found, function returns
|
||||
* the option character.
|
||||
* @return If options have been detected that is not in @p ostr
|
||||
* function will return '?'.
|
||||
* If function encounters an option with a missing
|
||||
* argument, then the return value depends on the first
|
||||
* character in optstring: if it is ':', then ':' is
|
||||
* returned; otherwise '?' is returned.
|
||||
* @return -1 If all options have been parsed.
|
||||
*/
|
||||
int shell_getopt(const struct shell *shell, int argc, char *const argv[],
|
||||
const char *ostr);
|
||||
|
||||
/**
|
||||
* @brief Returns shell_getopt state.
|
||||
*
|
||||
* @param[in] shell Pointer to the shell instance.
|
||||
*
|
||||
* @return Pointer to struct getopt_state.
|
||||
*/
|
||||
struct getopt_state *shell_getopt_state_get(const struct shell *shell);
|
||||
#endif /* CONFIG_SHELL_GETOPT */
|
||||
|
||||
/** @brief Execute command.
|
||||
*
|
||||
* Pass command line to shell to execute.
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef SHELL_GETOPT_H__
|
||||
#define SHELL_GETOPT_H__
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* Initializing shell getopt module.
|
||||
*
|
||||
* @param[in] shell Pointer to the shell instance.
|
||||
*/
|
||||
void z_shell_getopt_init(struct getopt_state *state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SHELL_GETOPT_H__ */
|
|
@ -23,6 +23,7 @@ zephyr_library_sources_ifdef(CONFIG_PTHREAD_IPC pthread_key.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_POSIX_MQUEUE mqueue.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_POSIX_FS fs.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_EVENTFD eventfd.c)
|
||||
add_subdirectory_ifdef(CONFIG_GETOPT getopt)
|
||||
|
||||
if(NOT (CONFIG_BOARD_NATIVE_POSIX OR CONFIG_BOARD_NATIVE_POSIX_64BIT))
|
||||
zephyr_library_sources(nanosleep.c)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# Copyright (c) 2018 Intel Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
source "lib/posix/getopt/Kconfig"
|
||||
|
||||
config POSIX_MAX_FDS
|
||||
int "Maximum number of open file descriptors"
|
||||
default 16 if POSIX_API
|
||||
|
|
|
@ -11,4 +11,10 @@ zephyr_include_directories_ifdef(
|
|||
zephyr_sources_ifdef(
|
||||
CONFIG_GETOPT
|
||||
getopt.c
|
||||
getopt_common.c
|
||||
)
|
||||
|
||||
zephyr_sources_ifdef(
|
||||
CONFIG_GETOPT_LONG
|
||||
getopt_long.c
|
||||
)
|
27
lib/posix/getopt/Kconfig
Normal file
27
lib/posix/getopt/Kconfig
Normal file
|
@ -0,0 +1,27 @@
|
|||
# Copyright (c) 2021 Nordic Semiconductor
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
|
||||
menuconfig GETOPT
|
||||
bool "Geopt library support"
|
||||
help
|
||||
This option adds support of getopt.
|
||||
Different shell backends are use their own instance of getopt to
|
||||
not interfere with each other.
|
||||
All not shell threads share one global instance of getopt state, hence
|
||||
apart from shell this library is not thread safe. User can add support
|
||||
for other threads by extending function getopt_state_get in
|
||||
getopt_common.c file.
|
||||
This option enables the following function: getopt.
|
||||
|
||||
config GETOPT_LONG
|
||||
bool "Getopt long library support"
|
||||
depends on GETOPT
|
||||
help
|
||||
This option adds support of the getopt long.
|
||||
Different shell backends are using their own instance of getopt to
|
||||
not interfere with each other.
|
||||
All not shell threads share one global instance of getopt state, hence
|
||||
apart from shell this library is not thread safe. User can add support
|
||||
for other threads by extending function getopt_state_get in
|
||||
getopt_common.c file.
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
#include <string.h>
|
||||
#include "getopt.h"
|
||||
#include "getopt_common.h"
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(getopt);
|
||||
|
@ -39,8 +40,12 @@ LOG_MODULE_REGISTER(getopt);
|
|||
#define BADARG ((int)':')
|
||||
#define EMSG ""
|
||||
|
||||
void getopt_init(struct getopt_state *state)
|
||||
void getopt_init(void)
|
||||
{
|
||||
struct getopt_state *state;
|
||||
|
||||
state = getopt_state_get();
|
||||
|
||||
state->opterr = 1;
|
||||
state->optind = 1;
|
||||
state->optopt = 0;
|
||||
|
@ -48,20 +53,30 @@ void getopt_init(struct getopt_state *state)
|
|||
state->optarg = NULL;
|
||||
|
||||
state->place = ""; /* EMSG */
|
||||
|
||||
#if CONFIG_GETOPT_LONG
|
||||
state->nonopt_start = -1; /* first non option argument (for permute) */
|
||||
state->nonopt_end = -1; /* first option after non options (for permute) */
|
||||
#endif
|
||||
|
||||
opterr = 1;
|
||||
optind = 1;
|
||||
optopt = 0;
|
||||
optreset = 0;
|
||||
optarg = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt --
|
||||
* Parse argc/argv argument vector.
|
||||
*/
|
||||
int
|
||||
getopt(state, nargc, nargv, ostr)
|
||||
struct getopt_state *state;
|
||||
int nargc;
|
||||
char *const nargv[];
|
||||
const char *ostr;
|
||||
int getopt(int nargc, char *const nargv[], const char *ostr)
|
||||
{
|
||||
char *oli; /* option letter list index */
|
||||
struct getopt_state *state;
|
||||
char *oli; /* option letter list index */
|
||||
|
||||
/* get getopt state of the current thread */
|
||||
state = getopt_state_get();
|
||||
|
||||
if (state->optreset || *state->place == 0) { /* update scanning pointer */
|
||||
state->optreset = 0;
|
||||
|
@ -69,6 +84,7 @@ getopt(state, nargc, nargv, ostr)
|
|||
if (state->optind >= nargc || *state->place++ != '-') {
|
||||
/* Argument is absent or is not an option */
|
||||
state->place = EMSG;
|
||||
z_getopt_global_state_update(state);
|
||||
return -1;
|
||||
}
|
||||
state->optopt = *state->place++;
|
||||
|
@ -76,6 +92,7 @@ getopt(state, nargc, nargv, ostr)
|
|||
/* "--" => end of options */
|
||||
++state->optind;
|
||||
state->place = EMSG;
|
||||
z_getopt_global_state_update(state);
|
||||
return -1;
|
||||
}
|
||||
if (state->optopt == 0) {
|
||||
|
@ -84,6 +101,7 @@ getopt(state, nargc, nargv, ostr)
|
|||
*/
|
||||
state->place = EMSG;
|
||||
if (strchr(ostr, '-') == NULL) {
|
||||
z_getopt_global_state_update(state);
|
||||
return -1;
|
||||
}
|
||||
state->optopt = '-';
|
||||
|
@ -101,6 +119,7 @@ getopt(state, nargc, nargv, ostr)
|
|||
if (state->opterr && *ostr != ':') {
|
||||
LOG_ERR("illegal option -- %c", state->optopt);
|
||||
}
|
||||
z_getopt_global_state_update(state);
|
||||
return BADCH;
|
||||
}
|
||||
|
||||
|
@ -123,16 +142,19 @@ getopt(state, nargc, nargv, ostr)
|
|||
/* option-argument absent */
|
||||
state->place = EMSG;
|
||||
if (*ostr == ':') {
|
||||
z_getopt_global_state_update(state);
|
||||
return BADARG;
|
||||
}
|
||||
if (state->opterr) {
|
||||
LOG_ERR("option requires an argument -- %c",
|
||||
state->optopt);
|
||||
}
|
||||
z_getopt_global_state_update(state);
|
||||
return BADCH;
|
||||
}
|
||||
state->place = EMSG;
|
||||
++state->optind;
|
||||
}
|
||||
z_getopt_global_state_update(state);
|
||||
return state->optopt; /* return option letter */
|
||||
}
|
138
lib/posix/getopt/getopt.h
Normal file
138
lib/posix/getopt/getopt.h
Normal file
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* 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 */
|
||||
|
||||
#if CONFIG_GETOPT_LONG
|
||||
int nonopt_start;
|
||||
int nonopt_end;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern int opterr; /* if error message should be printed */
|
||||
extern int optind; /* index into parent argv vector */
|
||||
extern int optopt; /* character checked for validity */
|
||||
extern int optreset; /* reset getopt */
|
||||
extern char *optarg; /* argument associated with option */
|
||||
|
||||
#define no_argument 0
|
||||
#define required_argument 1
|
||||
#define optional_argument 2
|
||||
|
||||
struct option {
|
||||
/* name of long option */
|
||||
const char *name;
|
||||
/*
|
||||
* one of no_argument, required_argument, and optional_argument:
|
||||
* whether option takes an argument
|
||||
*/
|
||||
int has_arg;
|
||||
/* if not NULL, set *flag to val when option found */
|
||||
int *flag;
|
||||
/* if flag not NULL, value to set *flag to; else return value */
|
||||
int val;
|
||||
};
|
||||
|
||||
/* Function intializes getopt_state structure for current thread */
|
||||
void getopt_init(void);
|
||||
|
||||
/* Function returns getopt_state structure for the current thread. */
|
||||
struct getopt_state *getopt_state_get(void);
|
||||
|
||||
/**
|
||||
* @brief Parses the command-line arguments.
|
||||
*
|
||||
* It is based on FreeBSD implementation.
|
||||
*
|
||||
* @param[in] argc Arguments count.
|
||||
* @param[in] argv Arguments.
|
||||
* @param[in] options String containing the legitimate option characters.
|
||||
*
|
||||
* @return If an option was successfully found, function returns
|
||||
* the option character.
|
||||
* @return If options have been detected that is not in @p options
|
||||
* function will return '?'.
|
||||
* If function encounters an option with a missing
|
||||
* argument, then the return value depends on the first
|
||||
* character in optstring: if it is ':', then ':' is
|
||||
* returned; otherwise '?' is returned.
|
||||
* @return -1 If all options have been parsed.
|
||||
*/
|
||||
int getopt(int nargc, char *const nargv[], const char *ostr);
|
||||
|
||||
/**
|
||||
* @brief Parses the command-line arguments.
|
||||
*
|
||||
* The getopt_long() function works like @ref getopt() except
|
||||
* it also accepts long options, started with two dashes.
|
||||
*
|
||||
* @note This function is based on FreeBSD implementation but it does not
|
||||
* support environment variable: POSIXLY_CORRECT.
|
||||
*
|
||||
* @param[in] argc Arguments count.
|
||||
* @param[in] argv Arguments.
|
||||
* @param[in] options String containing the legitimate option characters.
|
||||
* @param[in] long_options Pointer to the first element of an array of
|
||||
* @a struct z_option.
|
||||
* @param[in] long_idx If long_idx is not NULL, it points to a variable
|
||||
* which is set to the index of the long option relative
|
||||
* to @p long_options.
|
||||
*
|
||||
* @return If an option was successfully found, function returns
|
||||
* the option character.
|
||||
*/
|
||||
int getopt_long(int nargc, char *const *nargv,
|
||||
const char *options, const struct option *long_options,
|
||||
int *idx);
|
||||
|
||||
/**
|
||||
* @brief Parses the command-line arguments.
|
||||
*
|
||||
* The getopt_long_only() function works like @ref getopt_long(),
|
||||
* but '-' as well as "--" can indicate a long option. If an option that starts
|
||||
* with '-' (not "--") doesn't match a long option, but does match a short
|
||||
* option, it is parsed as a short option instead.
|
||||
*
|
||||
* @note This function is based on FreeBSD implementation but it does not
|
||||
* support environment variable: POSIXLY_CORRECT.
|
||||
*
|
||||
* @param[in] argc Arguments count.
|
||||
* @param[in] argv Arguments.
|
||||
* @param[in] options String containing the legitimate option characters.
|
||||
* @param[in] long_options Pointer to the first element of an array of
|
||||
* @a struct option.
|
||||
* @param[in] long_idx If long_idx is not NULL, it points to a variable
|
||||
* which is set to the index of the long option relative
|
||||
* to @p long_options.
|
||||
*
|
||||
* @return If an option was successfully found, function returns
|
||||
* the option character.
|
||||
*/
|
||||
int getopt_long_only(int nargc, char *const *nargv,
|
||||
const char *options, const struct option *long_options,
|
||||
int *idx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _GETOPT_H__ */
|
64
lib/posix/getopt/getopt_common.c
Normal file
64
lib/posix/getopt/getopt_common.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <zephyr.h>
|
||||
#include <shell/shell.h>
|
||||
#include "getopt.h"
|
||||
|
||||
/* Referring below variables is not thread safe. They reflects getopt state
|
||||
* only when 1 thread is using getopt.
|
||||
* When more threads are using getopt please call getopt_state_get to know
|
||||
* getopt state for the current thread.
|
||||
*/
|
||||
int opterr = 1; /* if error message should be printed */
|
||||
int optind = 1; /* index into parent argv vector */
|
||||
int optopt; /* character checked for validity */
|
||||
int optreset; /* reset getopt */
|
||||
char *optarg; /* argument associated with option */
|
||||
|
||||
/* Common state for all threads that did not have own getopt state. */
|
||||
static struct getopt_state m_getopt_common_state = {
|
||||
.opterr = 1,
|
||||
.optind = 1,
|
||||
.optopt = 0,
|
||||
.optreset = 0,
|
||||
.optarg = NULL,
|
||||
|
||||
.place = "", /* EMSG */
|
||||
|
||||
#if CONFIG_GETOPT_LONG
|
||||
.nonopt_start = -1, /* first non option argument (for permute) */
|
||||
.nonopt_end = -1, /* first option after non options (for permute) */
|
||||
#endif
|
||||
};
|
||||
|
||||
/* This function is not thread safe. All threads using getopt are calling
|
||||
* this function.
|
||||
*/
|
||||
void z_getopt_global_state_update(struct getopt_state *state)
|
||||
{
|
||||
opterr = state->opterr;
|
||||
optind = state->optind;
|
||||
optopt = state->optopt;
|
||||
optreset = state->optreset;
|
||||
optarg = state->optarg;
|
||||
}
|
||||
|
||||
/* It is internal getopt API function, it shall not be called by the user. */
|
||||
struct getopt_state *getopt_state_get(void)
|
||||
{
|
||||
#if CONFIG_SHELL_GETOPT
|
||||
k_tid_t tid;
|
||||
|
||||
tid = k_current_get();
|
||||
STRUCT_SECTION_FOREACH(shell, sh) {
|
||||
if (tid == sh->ctx->tid) {
|
||||
return &sh->ctx->getopt;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* If not a shell thread return a common pointer */
|
||||
return &m_getopt_common_state;
|
||||
}
|
23
lib/posix/getopt/getopt_common.h
Normal file
23
lib/posix/getopt/getopt_common.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef _GETOPT_COMMON_H__
|
||||
#define _GETOPT_COMMON_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This function is not thread safe. All threads using getopt are calling
|
||||
* this function.
|
||||
*/
|
||||
void z_getopt_global_state_update(struct getopt_state *state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _GETOPT_COMMON_H__ */
|
610
lib/posix/getopt/getopt_long.c
Normal file
610
lib/posix/getopt/getopt_long.c
Normal file
|
@ -0,0 +1,610 @@
|
|||
/* $OpenBSD: getopt_long.c,v 1.22 2006/10/04 21:29:04 jmc Exp $ */
|
||||
/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, 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.
|
||||
*
|
||||
* Sponsored in part by the Defense Advanced Research Projects
|
||||
* Agency (DARPA) and Air Force Research Laboratory, Air Force
|
||||
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2000 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by Dieter Baron and Thomas Klausner.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "getopt_common.h"
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_DECLARE(getopt);
|
||||
|
||||
#define GNU_COMPATIBLE /* Be more compatible, configure's use us! */
|
||||
|
||||
#define PRINT_ERROR ((state->opterr) && (*options != ':'))
|
||||
|
||||
#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
|
||||
#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
|
||||
#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
|
||||
|
||||
/* return values */
|
||||
#define BADCH (int)'?'
|
||||
#define BADARG ((*options == ':') ? (int)':' : (int)'?')
|
||||
#define INORDER 1
|
||||
|
||||
#define EMSG ""
|
||||
|
||||
#ifdef GNU_COMPATIBLE
|
||||
#define NO_PREFIX (-1)
|
||||
#define D_PREFIX 0
|
||||
#define DD_PREFIX 1
|
||||
#define W_PREFIX 2
|
||||
#endif
|
||||
|
||||
static int getopt_internal(struct getopt_state *, int, char * const *,
|
||||
const char *, const struct option *, int *, int);
|
||||
static int parse_long_options(struct getopt_state *, char * const *,
|
||||
const char *, const struct option *, int *, int,
|
||||
int);
|
||||
static int gcd(int, int);
|
||||
static void permute_args(int, int, int, char *const *);
|
||||
|
||||
/* Error messages */
|
||||
#define RECARGCHAR "option requires an argument -- %c"
|
||||
#define ILLOPTCHAR "illegal option -- %c" /* From P1003.2 */
|
||||
#ifdef GNU_COMPATIBLE
|
||||
static int dash_prefix = NO_PREFIX;
|
||||
#define GNUOPTCHAR "invalid option -- %c"
|
||||
|
||||
#define RECARGSTRING "option `%s%s' requires an argument"
|
||||
#define AMBIG "option `%s%.*s' is ambiguous"
|
||||
#define NOARG "option `%s%.*s' doesn't allow an argument"
|
||||
#define ILLOPTSTRING "unrecognized option `%s%s'"
|
||||
#else
|
||||
#define RECARGSTRING "option requires an argument -- %s"
|
||||
#define AMBIG "ambiguous option -- %.*s"
|
||||
#define NOARG "option doesn't take an argument -- %.*s"
|
||||
#define ILLOPTSTRING "unknown option -- %s"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Compute the greatest common divisor of a and b.
|
||||
*/
|
||||
static int
|
||||
gcd(int a, int b)
|
||||
{
|
||||
int c;
|
||||
|
||||
c = a % b;
|
||||
while (c != 0) {
|
||||
a = b;
|
||||
b = c;
|
||||
c = a % b;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
/*
|
||||
* Exchange the block from nonopt_start to nonopt_end with the block
|
||||
* from nonopt_end to opt_end (keeping the same order of arguments
|
||||
* in each block).
|
||||
*/
|
||||
static void
|
||||
permute_args(int panonopt_start, int panonopt_end, int opt_end,
|
||||
char * const *nargv)
|
||||
{
|
||||
int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
|
||||
char *swap;
|
||||
|
||||
/*
|
||||
* compute lengths of blocks and number and size of cycles
|
||||
*/
|
||||
nnonopts = panonopt_end - panonopt_start;
|
||||
nopts = opt_end - panonopt_end;
|
||||
ncycle = gcd(nnonopts, nopts);
|
||||
cyclelen = (opt_end - panonopt_start) / ncycle;
|
||||
|
||||
for (i = 0; i < ncycle; i++) {
|
||||
cstart = panonopt_end+i;
|
||||
pos = cstart;
|
||||
for (j = 0; j < cyclelen; j++) {
|
||||
if (pos >= panonopt_end)
|
||||
pos -= nnonopts;
|
||||
else
|
||||
pos += nopts;
|
||||
swap = nargv[pos];
|
||||
/* LINTED const cast */
|
||||
((char **) nargv)[pos] = nargv[cstart];
|
||||
/* LINTED const cast */
|
||||
((char **)nargv)[cstart] = swap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* parse_long_options --
|
||||
* Parse long options in argc/argv argument vector.
|
||||
* Returns -1 if short_too is set and the option does not match long_options.
|
||||
*/
|
||||
static int
|
||||
parse_long_options(struct getopt_state *state, char * const *nargv,
|
||||
const char *options, const struct option *long_options,
|
||||
int *idx, int short_too, int flags)
|
||||
{
|
||||
char *current_argv, *has_equal;
|
||||
#ifdef GNU_COMPATIBLE
|
||||
char *current_dash;
|
||||
#endif
|
||||
size_t current_argv_len;
|
||||
int i, match, exact_match, second_partial_match;
|
||||
|
||||
current_argv = state->place;
|
||||
#ifdef GNU_COMPATIBLE
|
||||
switch (dash_prefix) {
|
||||
case D_PREFIX:
|
||||
current_dash = "-";
|
||||
break;
|
||||
case DD_PREFIX:
|
||||
current_dash = "--";
|
||||
break;
|
||||
case W_PREFIX:
|
||||
current_dash = "-W ";
|
||||
break;
|
||||
default:
|
||||
current_dash = "";
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
match = -1;
|
||||
exact_match = 0;
|
||||
second_partial_match = 0;
|
||||
|
||||
state->optind++;
|
||||
|
||||
has_equal = strchr(current_argv, '=');
|
||||
if (has_equal != NULL) {
|
||||
/* argument found (--option=arg) */
|
||||
current_argv_len = has_equal - current_argv;
|
||||
has_equal++;
|
||||
} else
|
||||
current_argv_len = strlen(current_argv);
|
||||
|
||||
for (i = 0; long_options[i].name; i++) {
|
||||
/* find matching long option */
|
||||
if (strncmp(current_argv, long_options[i].name,
|
||||
current_argv_len))
|
||||
continue;
|
||||
|
||||
if (strlen(long_options[i].name) == current_argv_len) {
|
||||
/* exact match */
|
||||
match = i;
|
||||
exact_match = 1;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* If this is a known short option, don't allow
|
||||
* a partial match of a single character.
|
||||
*/
|
||||
if (short_too && current_argv_len == 1)
|
||||
continue;
|
||||
|
||||
if (match == -1) /* first partial match */
|
||||
match = i;
|
||||
else if ((flags & FLAG_LONGONLY) ||
|
||||
long_options[i].has_arg !=
|
||||
long_options[match].has_arg ||
|
||||
long_options[i].flag != long_options[match].flag ||
|
||||
long_options[i].val != long_options[match].val)
|
||||
second_partial_match = 1;
|
||||
}
|
||||
if (!exact_match && second_partial_match) {
|
||||
/* ambiguous abbreviation */
|
||||
if (PRINT_ERROR)
|
||||
LOG_WRN(AMBIG,
|
||||
#ifdef GNU_COMPATIBLE
|
||||
current_dash,
|
||||
#endif
|
||||
(int)current_argv_len,
|
||||
current_argv);
|
||||
state->optopt = 0;
|
||||
return BADCH;
|
||||
}
|
||||
if (match != -1) { /* option found */
|
||||
if (long_options[match].has_arg == no_argument
|
||||
&& has_equal) {
|
||||
if (PRINT_ERROR)
|
||||
LOG_WRN(NOARG,
|
||||
#ifdef GNU_COMPATIBLE
|
||||
current_dash,
|
||||
#endif
|
||||
(int)current_argv_len,
|
||||
current_argv);
|
||||
/*
|
||||
* XXX: GNU sets optopt to val regardless of flag
|
||||
*/
|
||||
if (long_options[match].flag == NULL)
|
||||
state->optopt = long_options[match].val;
|
||||
else
|
||||
state->optopt = 0;
|
||||
#ifdef GNU_COMPATIBLE
|
||||
return BADCH;
|
||||
#else
|
||||
return BADARG;
|
||||
#endif
|
||||
}
|
||||
if (long_options[match].has_arg == required_argument ||
|
||||
long_options[match].has_arg == optional_argument) {
|
||||
if (has_equal)
|
||||
state->optarg = has_equal;
|
||||
else if (long_options[match].has_arg ==
|
||||
required_argument) {
|
||||
/*
|
||||
* optional argument doesn't use next nargv
|
||||
*/
|
||||
state->optarg = nargv[state->optind++];
|
||||
}
|
||||
}
|
||||
if ((long_options[match].has_arg == required_argument)
|
||||
&& (state->optarg == NULL)) {
|
||||
/*
|
||||
* Missing argument; leading ':' indicates no error
|
||||
* should be generated.
|
||||
*/
|
||||
if (PRINT_ERROR)
|
||||
LOG_WRN(RECARGSTRING,
|
||||
#ifdef GNU_COMPATIBLE
|
||||
current_dash,
|
||||
#endif
|
||||
current_argv);
|
||||
/*
|
||||
* XXX: GNU sets optopt to val regardless of flag
|
||||
*/
|
||||
if (long_options[match].flag == NULL)
|
||||
state->optopt = long_options[match].val;
|
||||
else
|
||||
state->optopt = 0;
|
||||
--state->optind;
|
||||
return BADARG;
|
||||
}
|
||||
} else { /* unknown option */
|
||||
if (short_too) {
|
||||
--state->optind;
|
||||
return -1;
|
||||
}
|
||||
if (PRINT_ERROR)
|
||||
LOG_WRN(ILLOPTSTRING,
|
||||
#ifdef GNU_COMPATIBLE
|
||||
current_dash,
|
||||
#endif
|
||||
current_argv);
|
||||
state->optopt = 0;
|
||||
return BADCH;
|
||||
}
|
||||
if (idx)
|
||||
*idx = match;
|
||||
if (long_options[match].flag) {
|
||||
*long_options[match].flag = long_options[match].val;
|
||||
return 0;
|
||||
} else
|
||||
return long_options[match].val;
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt_internal --
|
||||
* Parse argc/argv argument vector. Called by user level routines.
|
||||
*/
|
||||
static int
|
||||
getopt_internal(struct getopt_state *state, int nargc, char * const *nargv,
|
||||
const char *options, const struct option *long_options,
|
||||
int *idx, int flags)
|
||||
{
|
||||
char *oli; /* option letter list index */
|
||||
int optchar, short_too;
|
||||
|
||||
if (options == NULL)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* Disable GNU extensions if options string begins with a '+'.
|
||||
*/
|
||||
#ifdef GNU_COMPATIBLE
|
||||
if (*options == '-')
|
||||
flags |= FLAG_ALLARGS;
|
||||
else if (*options == '+')
|
||||
flags &= ~FLAG_PERMUTE;
|
||||
#else
|
||||
if (*options == '+')
|
||||
flags &= ~FLAG_PERMUTE;
|
||||
else if (*options == '-')
|
||||
flags |= FLAG_ALLARGS;
|
||||
#endif
|
||||
if (*options == '+' || *options == '-')
|
||||
options++;
|
||||
|
||||
/*
|
||||
* XXX Some GNU programs (like cvs) set optind to 0 instead of
|
||||
* XXX using optreset. Work around this braindamage.
|
||||
*/
|
||||
if (state->optind == 0)
|
||||
state->optind = state->optreset = 1;
|
||||
|
||||
state->optarg = NULL;
|
||||
if (state->optreset)
|
||||
state->nonopt_start = state->nonopt_end = -1;
|
||||
start:
|
||||
if (state->optreset || !*(state->place)) {/* update scanning pointer */
|
||||
state->optreset = 0;
|
||||
if (state->optind >= nargc) { /* end of argument vector */
|
||||
state->place = EMSG;
|
||||
if (state->nonopt_end != -1) {
|
||||
/* do permutation, if we have to */
|
||||
permute_args(state->nonopt_start,
|
||||
state->nonopt_end,
|
||||
state->optind, nargv);
|
||||
state->optind -= state->nonopt_end -
|
||||
state->nonopt_start;
|
||||
} else if (state->nonopt_start != -1) {
|
||||
/*
|
||||
* If we skipped non-options, set optind
|
||||
* to the first of them.
|
||||
*/
|
||||
state->optind = state->nonopt_start;
|
||||
}
|
||||
state->nonopt_start = state->nonopt_end = -1;
|
||||
return -1;
|
||||
}
|
||||
state->place = nargv[state->optind];
|
||||
if (*(state->place) != '-' ||
|
||||
#ifdef GNU_COMPATIBLE
|
||||
state->place[1] == '\0') {
|
||||
#else
|
||||
(state->place[1] == '\0' && strchr(options, '-') == NULL)) {
|
||||
#endif
|
||||
state->place = EMSG; /* found non-option */
|
||||
if (flags & FLAG_ALLARGS) {
|
||||
/*
|
||||
* GNU extension:
|
||||
* return non-option as argument to option 1
|
||||
*/
|
||||
state->optarg = nargv[state->optind++];
|
||||
return INORDER;
|
||||
}
|
||||
if (!(flags & FLAG_PERMUTE)) {
|
||||
/*
|
||||
* If no permutation wanted, stop parsing
|
||||
* at first non-option.
|
||||
*/
|
||||
return -1;
|
||||
}
|
||||
/* do permutation */
|
||||
if (state->nonopt_start == -1) {
|
||||
state->nonopt_start = state->optind;
|
||||
} else if (state->nonopt_end != -1) {
|
||||
permute_args(state->nonopt_start,
|
||||
state->nonopt_end,
|
||||
state->optind,
|
||||
nargv);
|
||||
state->nonopt_start = state->optind -
|
||||
(state->nonopt_end - state->nonopt_start);
|
||||
state->nonopt_end = -1;
|
||||
}
|
||||
state->optind++;
|
||||
/* process next argument */
|
||||
goto start;
|
||||
}
|
||||
if (state->nonopt_start != -1 && state->nonopt_end == -1) {
|
||||
state->nonopt_end = state->optind;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have "-" do nothing, if "--" we are done.
|
||||
*/
|
||||
if (state->place[1] != '\0' && *++(state->place) == '-' &&
|
||||
state->place[1] == '\0') {
|
||||
state->optind++;
|
||||
state->place = EMSG;
|
||||
/*
|
||||
* We found an option (--), so if we skipped
|
||||
* non-options, we have to permute.
|
||||
*/
|
||||
if (state->nonopt_end != -1) {
|
||||
permute_args(state->nonopt_start,
|
||||
state->nonopt_end,
|
||||
state->optind,
|
||||
nargv);
|
||||
state->optind -= state->nonopt_end -
|
||||
state->nonopt_start;
|
||||
}
|
||||
state->nonopt_start = state->nonopt_end = -1;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check long options if:
|
||||
* 1) we were passed some
|
||||
* 2) the arg is not just "-"
|
||||
* 3) either the arg starts with -- we are getopt_long_only()
|
||||
*/
|
||||
if (long_options != NULL && state->place != nargv[state->optind] &&
|
||||
(*(state->place) == '-' || (flags & FLAG_LONGONLY))) {
|
||||
short_too = 0;
|
||||
#ifdef GNU_COMPATIBLE
|
||||
dash_prefix = D_PREFIX;
|
||||
#endif
|
||||
if (*(state->place) == '-') {
|
||||
state->place++; /* --foo long option */
|
||||
#ifdef GNU_COMPATIBLE
|
||||
dash_prefix = DD_PREFIX;
|
||||
#endif
|
||||
} else if (*(state->place) != ':' && strchr(options,
|
||||
*(state->place)) != NULL)
|
||||
short_too = 1; /* could be short option too */
|
||||
|
||||
optchar = parse_long_options(state, nargv, options,
|
||||
long_options, idx, short_too,
|
||||
flags);
|
||||
if (optchar != -1) {
|
||||
state->place = EMSG;
|
||||
return optchar;
|
||||
}
|
||||
}
|
||||
optchar = (int)*(state->place)++;
|
||||
oli = strchr(options, optchar);
|
||||
if (optchar == (int)':' ||
|
||||
(optchar == (int)'-' && *(state->place) != '\0') ||
|
||||
oli == NULL) {
|
||||
/*
|
||||
* If the user specified "-" and '-' isn't listed in
|
||||
* options, return -1 (non-option) as per POSIX.
|
||||
* Otherwise, it is an unknown option character (or ':').
|
||||
*/
|
||||
if (optchar == (int)'-' && *(state->place) == '\0') {
|
||||
return -1;
|
||||
}
|
||||
if (!*(state->place)) {
|
||||
++state->optind;
|
||||
}
|
||||
#ifdef GNU_COMPATIBLE
|
||||
if (PRINT_ERROR) {
|
||||
LOG_WRN(GNUOPTCHAR, optchar);
|
||||
}
|
||||
#else
|
||||
if (PRINT_ERROR) {
|
||||
LOG_WRN(ILLOPTCHAR, optchar);
|
||||
}
|
||||
#endif
|
||||
state->optopt = optchar;
|
||||
return BADCH;
|
||||
}
|
||||
if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
|
||||
/* -W long-option */
|
||||
if (*(state->place)) { /* no space */
|
||||
; /* NOTHING */
|
||||
} else if (++(state->optind) >= nargc) { /* no arg */
|
||||
state->place = EMSG;
|
||||
if (PRINT_ERROR) {
|
||||
LOG_WRN(RECARGCHAR, optchar);
|
||||
}
|
||||
state->optopt = optchar;
|
||||
return BADARG;
|
||||
} else if ((state->optind) < nargc) {
|
||||
state->place = nargv[state->optind];
|
||||
}
|
||||
#ifdef GNU_COMPATIBLE
|
||||
dash_prefix = W_PREFIX;
|
||||
#endif
|
||||
optchar = parse_long_options(state, nargv, options,
|
||||
long_options, idx, 0, flags);
|
||||
state->place = EMSG;
|
||||
return optchar;
|
||||
}
|
||||
if (*++oli != ':') { /* doesn't take argument */
|
||||
if (!*(state->place)) {
|
||||
++state->optind;
|
||||
}
|
||||
} else { /* takes (optional) argument */
|
||||
state->optarg = NULL;
|
||||
if (*(state->place)) { /* no white space */
|
||||
state->optarg = state->place;
|
||||
} else if (oli[1] != ':') { /* arg not optional */
|
||||
if (++state->optind >= nargc) { /* no arg */
|
||||
state->place = EMSG;
|
||||
if (PRINT_ERROR) {
|
||||
LOG_WRN(RECARGCHAR, optchar);
|
||||
}
|
||||
state->optopt = optchar;
|
||||
return BADARG;
|
||||
}
|
||||
state->optarg = nargv[state->optind];
|
||||
}
|
||||
state->place = EMSG;
|
||||
++state->optind;
|
||||
}
|
||||
/* dump back option letter */
|
||||
return optchar;
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt_long --
|
||||
* Parse argc/argv argument vector.
|
||||
*/
|
||||
int
|
||||
getopt_long(int nargc, char *const *nargv,
|
||||
const char *options, const struct option *long_options,
|
||||
int *idx)
|
||||
{
|
||||
struct getopt_state *state;
|
||||
int ret;
|
||||
|
||||
/* Get state of the current thread */
|
||||
state = getopt_state_get();
|
||||
|
||||
ret = getopt_internal(state, nargc, nargv, options, long_options, idx,
|
||||
FLAG_PERMUTE);
|
||||
|
||||
z_getopt_global_state_update(state);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt_long_only --
|
||||
* Parse argc/argv argument vector.
|
||||
*/
|
||||
int
|
||||
getopt_long_only(int nargc, char *const *nargv,
|
||||
const char *options, const struct option *long_options,
|
||||
int *idx)
|
||||
{
|
||||
struct getopt_state *state;
|
||||
int ret;
|
||||
|
||||
/* Get state of the current thread */
|
||||
state = getopt_state_get();
|
||||
|
||||
ret = getopt_internal(state, nargc, nargv, options, long_options, idx,
|
||||
FLAG_PERMUTE|FLAG_LONGONLY);
|
||||
|
||||
z_getopt_global_state_update(state);
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
add_subdirectory_ifdef(CONFIG_FNMATCH fnmatch)
|
||||
add_subdirectory_ifdef(CONFIG_GETOPT getopt)
|
||||
|
|
|
@ -6,6 +6,4 @@ menu "Util libraries"
|
|||
|
||||
source "lib/util/fnmatch/Kconfig"
|
||||
|
||||
source "lib/util/getopt/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
# Copyright (c) 2021 Nordic Semiconductor
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config GETOPT
|
||||
bool "GetOpt Support"
|
||||
help
|
||||
This option enables the getopt library
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* 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__ */
|
|
@ -22,7 +22,10 @@ tests:
|
|||
harness: keyboard
|
||||
extra_args: CONF_FILE="prj_minimal.conf"
|
||||
sample.shell.shell_module.getopt:
|
||||
filter: CONFIG_SERIAL and dt_chosen_enabled("zephyr,shell-uart")
|
||||
integration_platforms:
|
||||
- qemu_x86
|
||||
platform_exclude: native_posix native_posix_64
|
||||
filter: CONFIG_SERIAL and dt_chosen_enabled("zephyr,shell-uart") and not CONFIG_NEWLIB_LIBC
|
||||
tags: shell
|
||||
harness: keyboard
|
||||
min_ram: 40
|
||||
|
|
|
@ -100,7 +100,9 @@ static int cmd_demo_board(const struct shell *sh, size_t argc, char **argv)
|
|||
}
|
||||
|
||||
#if defined CONFIG_SHELL_GETOPT
|
||||
static int cmd_demo_getopt(const struct shell *shell, size_t argc, char **argv)
|
||||
/* Thread save usage */
|
||||
static int cmd_demo_getopt_ts(const struct shell *sh, size_t argc,
|
||||
char **argv)
|
||||
{
|
||||
struct getopt_state *state;
|
||||
char *cvalue = NULL;
|
||||
|
@ -108,8 +110,8 @@ static int cmd_demo_getopt(const struct shell *shell, size_t argc, char **argv)
|
|||
int bflag = 0;
|
||||
int c;
|
||||
|
||||
while ((c = shell_getopt(shell, argc, argv, "abhc:")) != -1) {
|
||||
state = shell_getopt_state_get(shell);
|
||||
while ((c = getopt(argc, argv, "abhc:")) != -1) {
|
||||
state = getopt_state_get();
|
||||
switch (c) {
|
||||
case 'a':
|
||||
aflag = 1;
|
||||
|
@ -125,19 +127,19 @@ static int cmd_demo_getopt(const struct shell *shell, size_t argc, char **argv)
|
|||
* command handler to print help message. It must
|
||||
* be done explicitly.
|
||||
*/
|
||||
shell_help(shell);
|
||||
shell_help(sh);
|
||||
return SHELL_CMD_HELP_PRINTED;
|
||||
case '?':
|
||||
if (state->optopt == 'c') {
|
||||
shell_print(shell,
|
||||
shell_print(sh,
|
||||
"Option -%c requires an argument.",
|
||||
state->optopt);
|
||||
} else if (isprint(state->optopt)) {
|
||||
shell_print(shell,
|
||||
shell_print(sh,
|
||||
"Unknown option `-%c'.",
|
||||
state->optopt);
|
||||
} else {
|
||||
shell_print(shell,
|
||||
shell_print(sh,
|
||||
"Unknown option character `\\x%x'.",
|
||||
state->optopt);
|
||||
}
|
||||
|
@ -147,7 +149,56 @@ static int cmd_demo_getopt(const struct shell *shell, size_t argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
shell_print(shell, "aflag = %d, bflag = %d", aflag, bflag);
|
||||
shell_print(sh, "aflag = %d, bflag = %d", aflag, bflag);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_demo_getopt(const struct shell *sh, size_t argc,
|
||||
char **argv)
|
||||
{
|
||||
char *cvalue = NULL;
|
||||
int aflag = 0;
|
||||
int bflag = 0;
|
||||
int c;
|
||||
|
||||
while ((c = getopt(argc, argv, "abhc:")) != -1) {
|
||||
switch (c) {
|
||||
case 'a':
|
||||
aflag = 1;
|
||||
break;
|
||||
case 'b':
|
||||
bflag = 1;
|
||||
break;
|
||||
case 'c':
|
||||
cvalue = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
/* When getopt is active shell is not parsing
|
||||
* command handler to print help message. It must
|
||||
* be done explicitly.
|
||||
*/
|
||||
shell_help(sh);
|
||||
return SHELL_CMD_HELP_PRINTED;
|
||||
case '?':
|
||||
if (optopt == 'c') {
|
||||
shell_print(sh,
|
||||
"Option -%c requires an argument.",
|
||||
optopt);
|
||||
} else if (isprint(optopt)) {
|
||||
shell_print(sh, "Unknown option `-%c'.",
|
||||
optopt);
|
||||
} else {
|
||||
shell_print(sh,
|
||||
"Unknown option character `\\x%x'.",
|
||||
optopt);
|
||||
}
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
shell_print(sh, "aflag = %d, bflag = %d", aflag, bflag);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -322,8 +373,12 @@ SHELL_STATIC_SUBCMD_SET_CREATE(sub_demo,
|
|||
SHELL_CMD(ping, NULL, "Ping command.", cmd_demo_ping),
|
||||
SHELL_CMD(board, NULL, "Show board name command.", cmd_demo_board),
|
||||
#if defined CONFIG_SHELL_GETOPT
|
||||
SHELL_CMD(getopt, NULL, "Cammand using getopt, looking for: \"abhc:\".",
|
||||
cmd_demo_getopt),
|
||||
SHELL_CMD(getopt_thread_safe, NULL,
|
||||
"Cammand using getopt in thread safe way"
|
||||
" looking for: \"abhc:\".",
|
||||
cmd_demo_getopt_ts),
|
||||
SHELL_CMD(getopt, NULL, "Cammand using getopt in non thread safe way"
|
||||
" looking for: \"abhc:\".\n", cmd_demo_getopt),
|
||||
#endif
|
||||
SHELL_SUBCMD_SET_END /* Array terminated. */
|
||||
);
|
||||
|
|
|
@ -21,11 +21,6 @@ zephyr_sources_ifdef(
|
|||
shell_help.c
|
||||
)
|
||||
|
||||
zephyr_sources_ifdef(
|
||||
CONFIG_SHELL_GETOPT
|
||||
shell_getopt.c
|
||||
)
|
||||
|
||||
zephyr_sources_ifdef(
|
||||
CONFIG_SHELL_CMDS
|
||||
shell_cmds.c
|
||||
|
|
|
@ -138,10 +138,13 @@ config SHELL_VT100_COLORS
|
|||
If enabled VT100 colors are used in shell (e.g. print errors in red).
|
||||
|
||||
config SHELL_GETOPT
|
||||
bool "Enable getopt support"
|
||||
bool "Enable threadsafe getopt support in shell"
|
||||
select GETOPT
|
||||
help
|
||||
Enables getopt support in the shell.
|
||||
This config creates a separate getopt_state for the shell instance.
|
||||
It ensures that using getopt with shell is thread safe.
|
||||
When more threads are using getopt please call getopt_state_get to
|
||||
get getopt state of the shell thread.
|
||||
|
||||
config SHELL_METAKEYS
|
||||
bool "Enable metakeys"
|
||||
|
|
|
@ -547,7 +547,7 @@ static int exec_cmd(const struct shell *shell, size_t argc, const char **argv,
|
|||
|
||||
if (!ret_val) {
|
||||
#if CONFIG_SHELL_GETOPT
|
||||
z_shell_getopt_init(&shell->ctx->getopt_state);
|
||||
getopt_init();
|
||||
#endif
|
||||
|
||||
z_flag_cmd_ctx_set(shell, true);
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <shell/shell.h>
|
||||
#include <shell/shell_getopt.h>
|
||||
|
||||
void z_shell_getopt_init(struct getopt_state *state)
|
||||
{
|
||||
getopt_init(state);
|
||||
}
|
||||
|
||||
int shell_getopt(const struct shell *shell, int argc, char *const argv[],
|
||||
const char *ostr)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_SHELL_GETOPT)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
__ASSERT_NO_MSG(shell);
|
||||
|
||||
return getopt(&shell->ctx->getopt_state, argc, argv, ostr);
|
||||
}
|
||||
|
||||
struct getopt_state *shell_getopt_state_get(const struct shell *shell)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_SHELL_GETOPT)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__ASSERT_NO_MSG(shell);
|
||||
|
||||
return &shell->ctx->getopt_state;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue