Skip to content

Commit 592049f

Browse files
committed
posix: Provide POSIX-compatible getopt API
The POSIX implementation of getopt should not expose any non-POSIX interfaces; applications needing Zephyr-compatible APIs should use the sys_getopt APIs. To make these testable, a minor glibc/newlib/picolibc extension was adopted where setting optind to zero causes the getopt state to be reset. Applications purely using the POSIX interface will not be affected by this, but the test cases will fail if the implementation does not support this extension. The only POSIX-compatible way to resolve that would be to split each test case into a separate test program. Signed-off-by: Keith Packard <[email protected]>
1 parent afcaf9b commit 592049f

File tree

4 files changed

+44
-57
lines changed

4 files changed

+44
-57
lines changed

lib/posix/c_lib_ext/getopt/getopt.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,12 @@
1313
extern "C" {
1414
#endif
1515

16-
extern void getopt_init(void);
17-
18-
#define getopt_state sys_getopt_state
1916
#define option sys_getopt_option
2017

2118
#define no_argument 0
2219
#define required_argument 1
2320
#define optional_argument 2
2421

25-
extern struct sys_getopt_state *getopt_state_get(void);
26-
2722
extern int getopt_long(int nargc, char *const *nargv, const char *options,
2823
const struct option *long_options, int *idx);
2924

lib/posix/c_lib_ext/getopt_shim.c

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,37 +11,52 @@
1111
char *optarg;
1212
int opterr, optind, optopt;
1313

14-
void getopt_init(void)
14+
static struct sys_getopt_state getopt_state = SYS_GETOPT_STATE_INITIALIZER;
15+
16+
static void getopt_global_state_get(void)
1517
{
16-
sys_getopt_init();
18+
getopt_state.opterr = opterr;
19+
getopt_state.optind = optind;
20+
getopt_state.optopt = optopt;
21+
getopt_state.optarg = optarg;
1722
}
1823

19-
struct getopt_state *getopt_state_get(void)
24+
static void getopt_global_state_put(void)
2025
{
21-
return sys_getopt_state_get();
26+
opterr = getopt_state.opterr;
27+
optind = getopt_state.optind;
28+
optopt = getopt_state.optopt;
29+
optarg = getopt_state.optarg;
2230
}
2331

2432
int getopt(int argc, char *const argv[], const char *optstring)
2533
{
26-
return sys_getopt(argc, argv, optstring);
27-
}
34+
int ret;
2835

29-
void z_getopt_global_state_update_shim(struct sys_getopt_state *state)
30-
{
31-
opterr = state->opterr;
32-
optind = state->optind;
33-
optopt = state->optopt;
34-
optarg = state->optarg;
36+
getopt_global_state_get();
37+
ret = sys_getopt_r(argc, argv, optstring, &getopt_state);
38+
getopt_global_state_put();
39+
return ret;
3540
}
3641

3742
int getopt_long(int argc, char *const argv[], const char *shortopts,
3843
const struct option *longopts, int *longind)
3944
{
40-
return sys_getopt_long(argc, argv, shortopts, longopts, longind);
45+
int ret;
46+
47+
getopt_global_state_get();
48+
ret = sys_getopt_long_r(argc, argv, shortopts, longopts, longind, &getopt_state);
49+
getopt_global_state_put();
50+
return ret;
4151
}
4252

4353
int getopt_long_only(int argc, char *const argv[], const char *shortopts,
4454
const struct option *longopts, int *longind)
4555
{
46-
return sys_getopt_long_only(argc, argv, shortopts, longopts, longind);
56+
int ret;
57+
58+
getopt_global_state_get();
59+
ret = sys_getopt_long_only_r(argc, argv, shortopts, longopts, longind, &getopt_state);
60+
getopt_global_state_put();
61+
return ret;
4762
}

lib/utils/getopt/getopt_common.c

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,6 @@ static struct sys_getopt_state m_getopt_common_state = {
3838
#endif
3939
};
4040

41-
/* Shim function to update global variables in getopt_shim.c if user wants to
42-
* still use the original non-posix compliant getopt. The shim will be deprecated
43-
* and eventually removed in the future.
44-
*/
45-
extern void z_getopt_global_state_update_shim(struct sys_getopt_state *state);
46-
4741
/* This function is not thread safe. All threads using getopt are calling
4842
* this function.
4943
*/

tests/posix/c_lib_ext/src/getopt.c

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,9 @@ ZTEST(posix_c_lib_ext, test_getopt_basic)
2828
int c;
2929
char **argv;
3030

31+
optind = 0; /* Reset state */
3132
argv = (char **)nargv;
3233

33-
/* Get state of the current thread */
34-
getopt_init();
35-
3634
do {
3735
c = getopt(argc, argv, accepted_opt);
3836
if (cnt >= strlen(expected)) {
@@ -55,7 +53,6 @@ enum getopt_idx {
5553

5654
ZTEST(posix_c_lib_ext, test_getopt)
5755
{
58-
struct getopt_state *state;
5956
static const char *test_opts = "ac:";
6057
static const char *const nargv[] = {
6158
[GETOPT_IDX_CMD_NAME] = "cmd_name",
@@ -67,9 +64,7 @@ ZTEST(posix_c_lib_ext, test_getopt)
6764
char **argv;
6865
int c;
6966

70-
/* Get state of the current thread */
71-
getopt_init();
72-
67+
optind = 0; /* Reset state */
7368
argv = (char **)nargv;
7469

7570
/* Test uknown option */
@@ -80,12 +75,7 @@ ZTEST(posix_c_lib_ext, test_getopt)
8075
zassert_equal(c, 'c', "unexpected opt character");
8176

8277
c = getopt(argc, argv, test_opts);
83-
state = getopt_state_get();
8478

85-
/* Thread safe usge: */
86-
zassert_equal(0, strcmp(argv[GETOPT_IDX_OPTARG], state->optarg),
87-
"unexpected optarg result");
88-
/* Non thread safe usage: */
8979
zassert_equal(0, strcmp(argv[GETOPT_IDX_OPTARG], optarg), "unexpected optarg result");
9080
}
9181

@@ -101,7 +91,6 @@ ZTEST(posix_c_lib_ext, test_getopt_long)
10191
/* Below test is based on example
10292
* https://www.gnu.org/software/libc/manual/html_node/Getopt-Long-Option-Example.html
10393
*/
104-
struct getopt_state *state;
10594
int verbose_flag = 0;
10695
/* getopt_long stores the option index here. */
10796
int option_index = 0;
@@ -156,33 +145,31 @@ ZTEST(posix_c_lib_ext, test_getopt_long)
156145
int argc4 = ARRAY_SIZE(argv4);
157146

158147
/* Test scenario 1 */
159-
/* Get state of the current thread */
160-
getopt_init();
148+
149+
optind = 0; /* Reset state */
161150
argv = (char **)argv1;
162151
c = getopt_long(argc1, argv, accepted_opt, long_options, &option_index);
163152
zassert_equal(verbose_flag, 1, "verbose flag expected");
164153
c = getopt_long(argc1, argv, accepted_opt, long_options, &option_index);
165-
state = getopt_state_get();
166154
zassert_equal('c', c, "unexpected option");
167-
zassert_equal(0, strcmp(state->optarg, argv[GETOPT_LONG_IDX_OPTARG]), "unexpected optarg");
155+
zassert_equal(0, strcmp(optarg, argv[GETOPT_LONG_IDX_OPTARG]), "unexpected optarg");
168156
c = getopt_long(argc1, argv, accepted_opt, long_options, &option_index);
169157
zassert_equal(-1, c, "getopt_long shall return -1");
170158

171159
/* Test scenario 2 */
160+
optind = 0; /* Reset state */
172161
argv = (char **)argv2;
173-
getopt_init();
174162
c = getopt_long(argc2, argv, accepted_opt, long_options, &option_index);
175163
zassert_equal(verbose_flag, 0, "verbose flag expected");
176164
c = getopt_long(argc2, argv, accepted_opt, long_options, &option_index);
177165
zassert_equal('d', c, "unexpected option");
178-
state = getopt_state_get();
179-
zassert_equal(0, strcmp(state->optarg, argv[GETOPT_LONG_IDX_OPTARG]), "unexpected optarg");
166+
zassert_equal(0, strcmp(optarg, argv[GETOPT_LONG_IDX_OPTARG]), "unexpected optarg");
180167
c = getopt_long(argc2, argv, accepted_opt, long_options, &option_index);
181168
zassert_equal(-1, c, "getopt_long shall return -1");
182169

183170
/* Test scenario 3 */
171+
optind = 0; /* Reset state */
184172
argv = (char **)argv3;
185-
getopt_init();
186173
c = getopt_long(argc3, argv, accepted_opt, long_options, &option_index);
187174
zassert_equal(verbose_flag, 0, "verbose flag expected");
188175
c = getopt_long(argc3, argv, accepted_opt, long_options, &option_index);
@@ -191,8 +178,8 @@ ZTEST(posix_c_lib_ext, test_getopt_long)
191178
zassert_equal(-1, c, "getopt_long shall return -1");
192179

193180
/* Test scenario 4 */
181+
optind = 0; /* Reset state */
194182
argv = (char **)argv4;
195-
getopt_init();
196183
c = getopt_long(argc4, argv, accepted_opt, long_options, &option_index);
197184
zassert_equal(verbose_flag, 0, "verbose flag expected");
198185
c = getopt_long(argc4, argv, accepted_opt, long_options, &option_index);
@@ -208,7 +195,6 @@ ZTEST(posix_c_lib_ext, test_getopt_long_only)
208195
/* Below test is based on example
209196
* https://www.gnu.org/software/libc/manual/html_node/Getopt-Long-Option-Example.html
210197
*/
211-
struct getopt_state *state;
212198
int verbose_flag = 0;
213199
/* getopt_long stores the option index here. */
214200
int option_index = 0;
@@ -263,33 +249,30 @@ ZTEST(posix_c_lib_ext, test_getopt_long_only)
263249
int argc4 = ARRAY_SIZE(argv4);
264250

265251
/* Test scenario 1 */
252+
optind = 0; /* Reset state */
266253
argv = (char **)argv1;
267-
getopt_init();
268254
c = getopt_long_only(argc1, argv, accepted_opt, long_options, &option_index);
269255
zassert_equal(verbose_flag, 1, "verbose flag expected");
270256
c = getopt_long_only(argc1, argv, accepted_opt, long_options, &option_index);
271-
state = getopt_state_get();
272257
zassert_equal('c', c, "unexpected option");
273-
zassert_equal(0, strcmp(state->optarg, argv[GETOPT_LONG_IDX_OPTARG]), "unexpected optarg");
258+
zassert_equal(0, strcmp(optarg, argv[GETOPT_LONG_IDX_OPTARG]), "unexpected optarg");
274259
c = getopt_long_only(argc1, argv, accepted_opt, long_options, &option_index);
275260
zassert_equal(-1, c, "getopt_long_only shall return -1");
276261

277262
/* Test scenario 2 */
263+
optind = 0; /* Reset state */
278264
argv = (char **)argv2;
279-
getopt_init();
280-
state = getopt_state_get();
281265
c = getopt_long_only(argc2, argv, accepted_opt, long_options, &option_index);
282266
zassert_equal(verbose_flag, 0, "verbose flag expected");
283267
c = getopt_long_only(argc2, argv, accepted_opt, long_options, &option_index);
284-
state = getopt_state_get();
285268
zassert_equal('d', c, "unexpected option");
286-
zassert_equal(0, strcmp(state->optarg, argv[GETOPT_LONG_IDX_OPTARG]), "unexpected optarg");
269+
zassert_equal(0, strcmp(optarg, argv[GETOPT_LONG_IDX_OPTARG]), "unexpected optarg");
287270
c = getopt_long_only(argc2, argv, accepted_opt, long_options, &option_index);
288271
zassert_equal(-1, c, "getopt_long_only shall return -1");
289272

290273
/* Test scenario 3 */
274+
optind = 0; /* Reset state */
291275
argv = (char **)argv3;
292-
getopt_init();
293276
c = getopt_long_only(argc3, argv, accepted_opt, long_options, &option_index);
294277
zassert_equal(verbose_flag, 0, "verbose flag expected");
295278
c = getopt_long_only(argc3, argv, accepted_opt, long_options, &option_index);
@@ -298,8 +281,8 @@ ZTEST(posix_c_lib_ext, test_getopt_long_only)
298281
zassert_equal(-1, c, "getopt_long_only shall return -1");
299282

300283
/* Test scenario 4 */
284+
optind = 0; /* Reset state */
301285
argv = (char **)argv4;
302-
getopt_init();
303286
c = getopt_long_only(argc4, argv, accepted_opt, long_options, &option_index);
304287
zassert_equal(verbose_flag, 0, "verbose flag expected");
305288
c = getopt_long_only(argc4, argv, accepted_opt, long_options, &option_index);

0 commit comments

Comments
 (0)