Skip to content

Commit afcaf9b

Browse files
committed
utils/getopt: Add _r variants of the getopt APIs
Add sys_getopt_r, sys_getopt_long_r and sys_getopt_long_only_r variants of the getopt APIs. This allows applications to explicitly control their getopt state, rather than storing it in global and/or thread local variables. The existing APIs remain available and operate as they did before. Signed-off-by: Keith Packard <[email protected]>
1 parent 8c264d3 commit afcaf9b

File tree

5 files changed

+79
-45
lines changed

5 files changed

+79
-45
lines changed

doc/services/shell/index.rst

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -686,12 +686,12 @@ the arguments string, looking for supported options. Typically, this task
686686
is accomplished by the ``getopt`` family functions.
687687

688688
For this purpose shell supports a variant of the getopt and getopt_long libraries from
689-
the FreeBSD project called sys_getopt and sys_getopt_long. This feature is activated by:
689+
the FreeBSD project called sys_getopt_r and sys_getopt_long_r. This feature is activated by:
690690
:kconfig:option:`CONFIG_GETOPT` set to ``y`` and :kconfig:option:`CONFIG_GETOPT_LONG`
691691
set to ``y``.
692692

693-
This feature can be used in thread safe as well as non thread safe manner.
694-
The former is full compatible with regular getopt usage while the latter
693+
This feature can be used in non thread safe as well as thread safe manner.
694+
The former is fully compatible with regular getopt usage while the latter
695695
a bit differs.
696696

697697
An example non-thread safe usage:
@@ -714,21 +714,17 @@ An example thread safe usage:
714714
.. code-block:: c
715715
716716
char *cvalue = NULL;
717-
struct sys_getopt_state *state;
718-
while ((char c = sys_getopt(argc, argv, "abhc:")) != -1) {
719-
state = sys_getopt_state_get();
717+
struct sys_getopt_state state = SYS_GETOPT_STATE_INITIALIZER;
718+
while ((char c = sys_getopt_r(argc, argv, "abhc:", &state)) != -1) {
720719
switch (c) {
721720
case 'c':
722-
cvalue = state->optarg;
721+
cvalue = state.optarg;
723722
break;
724723
default:
725724
break;
726725
}
727726
}
728727
729-
Thread safe sys_getopt functionality is activated by
730-
:kconfig:option:`CONFIG_SHELL_GETOPT` set to ``y``.
731-
732728
Obscured Input Feature
733729
**********************
734730

include/zephyr/sys/sys_getopt.h

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ extern int sys_getopt_opterr;
3434
extern int sys_getopt_optind;
3535
extern int sys_getopt_optopt;
3636

37+
/* Initialize local sys_getopt_state with this value */
38+
#define SYS_GETOPT_STATE_INITIALIZER {0}
39+
3740
#define sys_getopt_no_argument 0
3841
#define sys_getopt_required_argument 1
3942
#define sys_getopt_optional_argument 2
@@ -52,6 +55,9 @@ struct sys_getopt_option {
5255
int val;
5356
};
5457

58+
/* Function initializes getopt_state structure */
59+
void sys_getopt_init_r(struct sys_getopt_state *state);
60+
5561
/* Function initializes getopt_state structure for current thread */
5662
void sys_getopt_init(void);
5763

@@ -67,16 +73,19 @@ struct sys_getopt_state *sys_getopt_state_get(void);
6773
* @param[in] nargc Arguments count.
6874
* @param[in] nargv Arguments.
6975
* @param[in] ostr String containing the legitimate option characters.
76+
* @param[inout] state Current argument parsing state.
7077
*
7178
* @return If an option was successfully found, function returns
7279
* the option character.
7380
*/
81+
int sys_getopt_r(int nargc, char *const nargv[], const char *ostr, struct sys_getopt_state *state);
82+
7483
int sys_getopt(int nargc, char *const nargv[], const char *ostr);
7584

7685
/**
7786
* @brief Parses the command-line arguments.
7887
*
79-
* The sys_getopt_long() function works like @ref sys_getopt() except
88+
* The sys_getopt_long_r() function works like @ref sys_getopt_r() except
8089
* it also accepts long options, started with two dashes.
8190
*
8291
* @note This function is based on FreeBSD implementation but it does not
@@ -90,17 +99,22 @@ int sys_getopt(int nargc, char *const nargv[], const char *ostr);
9099
* @param[in] idx If idx is not NULL, it points to a variable
91100
* which is set to the index of the long option relative
92101
* to @p long_options.
102+
* @param[inout] state Current argument parsing state.
93103
*
94104
* @return If an option was successfully found, function returns
95105
* the option character.
96106
*/
107+
int sys_getopt_long_r(int nargc, char *const *nargv, const char *options,
108+
const struct sys_getopt_option *long_options, int *idx,
109+
struct sys_getopt_state *state);
110+
97111
int sys_getopt_long(int nargc, char *const *nargv, const char *options,
98112
const struct sys_getopt_option *long_options, int *idx);
99113

100114
/**
101115
* @brief Parses the command-line arguments.
102116
*
103-
* The sys_getopt_long_only() function works like @ref sys_getopt_long(),
117+
* The sys_getopt_long_only_r() function works like @ref sys_getopt_long_r(),
104118
* but '-' as well as "--" can indicate a long option. If an option that starts
105119
* with '-' (not "--") doesn't match a long option, but does match a short
106120
* option, it is parsed as a short option instead.
@@ -116,10 +130,15 @@ int sys_getopt_long(int nargc, char *const *nargv, const char *options,
116130
* @param[in] idx If idx is not NULL, it points to a variable
117131
* which is set to the index of the long option relative
118132
* to @p long_options.
133+
* @param[inout] state Current argument parsing state.
119134
*
120135
* @return If an option was successfully found, function returns
121136
* the option character.
122137
*/
138+
int sys_getopt_long_only_r(int nargc, char *const *nargv, const char *options,
139+
const struct sys_getopt_option *long_options, int *idx,
140+
struct sys_getopt_state *state);
141+
123142
int sys_getopt_long_only(int nargc, char *const *nargv, const char *options,
124143
const struct sys_getopt_option *long_options, int *idx);
125144

lib/utils/getopt/getopt.c

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,8 @@ LOG_MODULE_REGISTER(sys_getopt);
4040
#define BADARG ((int)':')
4141
#define EMSG ""
4242

43-
void sys_getopt_init(void)
43+
void sys_getopt_init_r(struct sys_getopt_state *state)
4444
{
45-
struct sys_getopt_state *state;
46-
47-
state = sys_getopt_state_get();
48-
4945
state->opterr = 1;
5046
state->optind = 1;
5147
state->optopt = 0;
@@ -58,41 +54,44 @@ void sys_getopt_init(void)
5854
state->nonopt_start = -1; /* first non option argument (for permute) */
5955
state->nonopt_end = -1; /* first option after non options (for permute) */
6056
#endif
57+
}
6158

62-
sys_getopt_opterr = 1;
63-
sys_getopt_optind = 1;
64-
sys_getopt_optopt = 0;
65-
sys_getopt_optreset = 0;
66-
sys_getopt_optarg = NULL;
59+
void sys_getopt_init(void)
60+
{
61+
struct sys_getopt_state *state;
62+
63+
state = sys_getopt_state_get();
64+
65+
sys_getopt_init_r(state);
66+
z_getopt_global_state_update(state);
6767
}
6868

6969
/*
7070
* getopt --
7171
* Parse argc/argv argument vector.
7272
*/
73-
int sys_getopt(int nargc, char *const nargv[], const char *ostr)
73+
int sys_getopt_r(int nargc, char *const nargv[], const char *ostr, struct sys_getopt_state *state)
7474
{
75-
struct sys_getopt_state *state;
7675
char *oli; /* option letter list index */
7776

78-
/* get getopt state of the current thread */
79-
state = sys_getopt_state_get();
77+
/* Reset if optind is 0 */
78+
if (state->optind == 0) {
79+
sys_getopt_init_r(state);
80+
}
8081

8182
if (state->optreset || *state->place == 0) { /* update scanning pointer */
8283
state->optreset = 0;
8384
state->place = nargv[state->optind];
8485
if (state->optind >= nargc || *state->place++ != '-') {
8586
/* Argument is absent or is not an option */
8687
state->place = EMSG;
87-
z_getopt_global_state_update(state);
8888
return -1;
8989
}
9090
state->optopt = *state->place++;
9191
if (state->optopt == '-' && *state->place == 0) {
9292
/* "--" => end of options */
9393
++state->optind;
9494
state->place = EMSG;
95-
z_getopt_global_state_update(state);
9695
return -1;
9796
}
9897
if (state->optopt == 0) {
@@ -101,7 +100,6 @@ int sys_getopt(int nargc, char *const nargv[], const char *ostr)
101100
*/
102101
state->place = EMSG;
103102
if (strchr(ostr, '-') == NULL) {
104-
z_getopt_global_state_update(state);
105103
return -1;
106104
}
107105
state->optopt = '-';
@@ -119,7 +117,6 @@ int sys_getopt(int nargc, char *const nargv[], const char *ostr)
119117
if (state->opterr && *ostr != ':') {
120118
LOG_DBG("illegal option -- %c", state->optopt);
121119
}
122-
z_getopt_global_state_update(state);
123120
return BADCH;
124121
}
125122

@@ -142,18 +139,32 @@ int sys_getopt(int nargc, char *const nargv[], const char *ostr)
142139
/* option-argument absent */
143140
state->place = EMSG;
144141
if (*ostr == ':') {
145-
z_getopt_global_state_update(state);
146142
return BADARG;
147143
}
148144
if (state->opterr) {
149145
LOG_DBG("option requires an argument -- %c", state->optopt);
150146
}
151-
z_getopt_global_state_update(state);
152147
return BADCH;
153148
}
154149
state->place = EMSG;
155150
++state->optind;
156151
}
157-
z_getopt_global_state_update(state);
158152
return state->optopt; /* return option letter */
159153
}
154+
155+
/*
156+
* getopt --
157+
* Parse argc/argv argument vector.
158+
*/
159+
int sys_getopt(int nargc, char *const nargv[], const char *ostr)
160+
{
161+
struct sys_getopt_state *state;
162+
int ret;
163+
164+
/* Get state of the current thread */
165+
state = sys_getopt_state_get();
166+
ret = sys_getopt_r(nargc, nargv, ostr, state);
167+
168+
z_getopt_global_state_update(state);
169+
return ret;
170+
}

lib/utils/getopt/getopt_common.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,6 @@ void z_getopt_global_state_update(struct sys_getopt_state *state)
5454
sys_getopt_optopt = state->optopt;
5555
sys_getopt_optreset = state->optreset;
5656
sys_getopt_optarg = state->optarg;
57-
58-
if (!IS_ENABLED(CONFIG_NATIVE_LIBC) && IS_ENABLED(CONFIG_POSIX_C_LIB_EXT)) {
59-
z_getopt_global_state_update_shim(state);
60-
}
6157
}
6258

6359
/* It is internal getopt API function, it shall not be called by the user. */

lib/utils/getopt/getopt_long.c

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,11 @@ static int getopt_internal(struct sys_getopt_state *state, int nargc, char *cons
348348
return -1;
349349
}
350350

351+
/* Reset if optind is 0 */
352+
if (state->optind == 0) {
353+
sys_getopt_init_r(state);
354+
}
355+
351356
/*
352357
* Disable GNU extensions if options string begins with a '+'.
353358
*/
@@ -368,14 +373,6 @@ static int getopt_internal(struct sys_getopt_state *state, int nargc, char *cons
368373
options++;
369374
}
370375

371-
/*
372-
* XXX Some GNU programs (like cvs) set optind to 0 instead of
373-
* XXX using optreset. Work around this braindamage.
374-
*/
375-
if (state->optind == 0) {
376-
state->optind = state->optreset = 1;
377-
}
378-
379376
state->optarg = NULL;
380377
if (state->optreset) {
381378
state->nonopt_start = state->nonopt_end = -1;
@@ -568,6 +565,13 @@ static int getopt_internal(struct sys_getopt_state *state, int nargc, char *cons
568565
* getopt_long --
569566
* Parse argc/argv argument vector.
570567
*/
568+
int sys_getopt_long_r(int nargc, char *const *nargv, const char *options,
569+
const struct sys_getopt_option *long_options, int *idx,
570+
struct sys_getopt_state *state)
571+
{
572+
return getopt_internal(state, nargc, nargv, options, long_options, idx, FLAG_PERMUTE);
573+
}
574+
571575
int sys_getopt_long(int nargc, char *const *nargv, const char *options,
572576
const struct sys_getopt_option *long_options, int *idx)
573577
{
@@ -588,6 +592,14 @@ int sys_getopt_long(int nargc, char *const *nargv, const char *options,
588592
* getopt_long_only --
589593
* Parse argc/argv argument vector.
590594
*/
595+
int sys_getopt_long_only_r(int nargc, char *const *nargv, const char *options,
596+
const struct sys_getopt_option *long_options, int *idx,
597+
struct sys_getopt_state *state)
598+
{
599+
return getopt_internal(state, nargc, nargv, options, long_options, idx,
600+
FLAG_PERMUTE | FLAG_LONGONLY);
601+
}
602+
591603
int sys_getopt_long_only(int nargc, char *const *nargv, const char *options,
592604
const struct sys_getopt_option *long_options, int *idx)
593605
{

0 commit comments

Comments
 (0)