Coverage Report

Created: 2025-08-26 06:50

/src/open5gs/lib/core/ogs-getopt.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2019-2020 by Sukchan Lee <acetcom@gmail.com>
3
 *
4
 * The code is stolen from optparse
5
 * https://github.com/skeeto/optparse
6
 *
7
 * This is free and unencumbered software released into the public domain.
8
 *
9
 * Anyone is free to copy, modify, publish, use, compile, sell, or
10
 * distribute this software, either in source code form or as a compiled
11
 * binary, for any purpose, commercial or non-commercial, and by any
12
 * means.
13
 *
14
 * In jurisdictions that recognize copyright laws, the author or authors
15
 * of this software dedicate any and all copyright interest in the
16
 * software to the public domain. We make this dedication for the benefit
17
 * of the public at large and to the detriment of our heirs and
18
 * successors. We intend this dedication to be an overt act of
19
 * relinquishment in perpetuity of all present and future rights to this
20
 * software under copyright law.
21
 *
22
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
25
 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
26
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
27
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28
 * OTHER DEALINGS IN THE SOFTWARE.
29
 *
30
 * For more information, please refer to <http://unlicense.org/>
31
 */
32
33
#include "ogs-getopt.h"
34
35
0
#define OGS_GETOPT_MSG_INVALID "invalid option"
36
0
#define OGS_GETOPT_MSG_MISSING "option requires an argument"
37
0
#define OGS_GETOPT_MSG_TOOMANY "option takes no arguments"
38
39
static int ogs_getopt_error(
40
        ogs_getopt_t *options, const char *msg, const char *data)
41
0
{
42
0
    unsigned p = 0;
43
0
    const char *sep = " -- '";
44
0
    while (*msg)
45
0
        options->errmsg[p++] = *msg++;
46
0
    while (*sep)
47
0
        options->errmsg[p++] = *sep++;
48
0
    while (p < sizeof(options->errmsg) - 2 && *data)
49
0
        options->errmsg[p++] = *data++;
50
0
    options->errmsg[p++] = '\'';
51
0
    options->errmsg[p++] = '\0';
52
0
    return '?';
53
0
}
54
55
void ogs_getopt_init(ogs_getopt_t *options, char **argv)
56
0
{
57
0
    options->argv = argv;
58
0
    options->permute = 1;
59
0
    options->optind = 1;
60
0
    options->subopt = 0;
61
0
    options->optarg = 0;
62
0
    options->errmsg[0] = '\0';
63
0
}
64
65
static int ogs_getopt_is_dashdash(const char *arg)
66
0
{
67
0
    return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0';
68
0
}
69
70
static int ogs_getopt_is_shortopt(const char *arg)
71
0
{
72
0
    return arg != 0 && arg[0] == '-' && arg[1] != '-' && arg[1] != '\0';
73
0
}
74
75
static int
76
ogs_getopt_is_longopt(const char *arg)
77
0
{
78
0
    return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] != '\0';
79
0
}
80
81
static void ogs_getopt_permute(ogs_getopt_t *options, int index)
82
0
{
83
0
    char *nonoption = options->argv[index];
84
0
    int i;
85
0
    for (i = index; i < options->optind - 1; i++)
86
0
        options->argv[i] = options->argv[i + 1];
87
0
    options->argv[options->optind - 1] = nonoption;
88
0
}
89
90
static int ogs_getopt_argtype(const char *optstring, char c)
91
0
{
92
0
    int count = OGS_GETOPT_NONE;
93
0
    if (c == ':')
94
0
        return -1;
95
0
    for (; *optstring && c != *optstring; optstring++);
96
0
    if (!*optstring)
97
0
        return -1;
98
0
    if (optstring[1] == ':')
99
0
        count += optstring[2] == ':' ? 2 : 1;
100
0
    return count;
101
0
}
102
103
int ogs_getopt(ogs_getopt_t *options, const char *optstring)
104
0
{
105
0
    int type;
106
0
    char *next;
107
0
    char *option = options->argv[options->optind];
108
0
    options->errmsg[0] = '\0';
109
0
    options->optopt = 0;
110
0
    options->optarg = 0;
111
0
    if (option == 0) {
112
0
        return -1;
113
0
    } else if (ogs_getopt_is_dashdash(option)) {
114
0
        options->optind++; /* consume "--" */
115
0
        return -1;
116
0
    } else if (!ogs_getopt_is_shortopt(option)) {
117
0
        if (options->permute) {
118
0
            int index = options->optind++;
119
0
            int r = ogs_getopt(options, optstring);
120
0
            ogs_getopt_permute(options, index);
121
0
            options->optind--;
122
0
            return r;
123
0
        } else {
124
0
            return -1;
125
0
        }
126
0
    }
127
0
    option += options->subopt + 1;
128
0
    options->optopt = option[0];
129
0
    type = ogs_getopt_argtype(optstring, option[0]);
130
0
    next = options->argv[options->optind + 1];
131
0
    switch (type) {
132
0
    case -1: {
133
0
        char str[2] = {0, 0};
134
0
        str[0] = option[0];
135
0
        options->optind++;
136
0
        return ogs_getopt_error(options, OGS_GETOPT_MSG_INVALID, str);
137
0
    }
138
0
    case OGS_GETOPT_NONE:
139
0
        if (option[1]) {
140
0
            options->subopt++;
141
0
        } else {
142
0
            options->subopt = 0;
143
0
            options->optind++;
144
0
        }
145
0
        return option[0];
146
0
    case OGS_GETOPT_REQUIRED:
147
0
        options->subopt = 0;
148
0
        options->optind++;
149
0
        if (option[1]) {
150
0
            options->optarg = option + 1;
151
0
        } else if (next != 0) {
152
0
            options->optarg = next;
153
0
            options->optind++;
154
0
        } else {
155
0
            char str[2] = {0, 0};
156
0
            str[0] = option[0];
157
0
            options->optarg = 0;
158
0
            return ogs_getopt_error(options, OGS_GETOPT_MSG_MISSING, str);
159
0
        }
160
0
        return option[0];
161
0
    case OGS_GETOPT_OPTIONAL:
162
0
        options->subopt = 0;
163
0
        options->optind++;
164
0
        if (option[1])
165
0
            options->optarg = option + 1;
166
0
        else
167
0
            options->optarg = 0;
168
0
        return option[0];
169
0
    }
170
0
    return 0;
171
0
}
172
173
char *ogs_getopt_arg(ogs_getopt_t *options)
174
0
{
175
0
    char *option = options->argv[options->optind];
176
0
    options->subopt = 0;
177
0
    if (option != 0)
178
0
        options->optind++;
179
0
    return option;
180
0
}
181
182
static int ogs_getopt_longopts_end(const ogs_getopt_long_t *longopts, int i)
183
0
{
184
0
    return !longopts[i].longname && !longopts[i].shortname;
185
0
}
186
187
static void
188
ogs_getopt_from_long(const ogs_getopt_long_t *longopts, char *optstring)
189
0
{
190
0
    char *p = optstring;
191
0
    int i;
192
0
    for (i = 0; !ogs_getopt_longopts_end(longopts, i); i++) {
193
0
        if (longopts[i].shortname) {
194
0
            int a;
195
0
            *p++ = longopts[i].shortname;
196
0
            for (a = 0; a < (int)longopts[i].argtype; a++)
197
0
                *p++ = ':';
198
0
        }
199
0
    }
200
0
    *p = '\0';
201
0
}
202
203
/* Unlike strcmp(), handles options containing "=". */
204
static int ogs_getopt_longopts_match(const char *longname, const char *option)
205
0
{
206
0
    const char *a = option, *n = longname;
207
0
    if (longname == 0)
208
0
        return 0;
209
0
    for (; *a && *n && *a != '='; a++, n++)
210
0
        if (*a != *n)
211
0
            return 0;
212
0
    return *n == '\0' && (*a == '\0' || *a == '=');
213
0
}
214
215
/* Return the part after "=", or NULL. */
216
static char *ogs_getopt_longopts_arg(char *option)
217
0
{
218
0
    for (; *option && *option != '='; option++);
219
0
    if (*option == '=')
220
0
        return option + 1;
221
0
    else
222
0
        return 0;
223
0
}
224
225
static int ogs_getopt_long_fallback(ogs_getopt_t *options,
226
                       const ogs_getopt_long_t *longopts,
227
                       int *longindex)
228
0
{
229
0
    int result;
230
0
    char optstring[96 * 3 + 1]; /* 96 ASCII printable characters */
231
0
    ogs_getopt_from_long(longopts, optstring);
232
0
    result = ogs_getopt(options, optstring);
233
0
    if (longindex != 0) {
234
0
        *longindex = -1;
235
0
        if (result != -1) {
236
0
            int i;
237
0
            for (i = 0; !ogs_getopt_longopts_end(longopts, i); i++)
238
0
                if (longopts[i].shortname == options->optopt)
239
0
                    *longindex = i;
240
0
        }
241
0
    }
242
0
    return result;
243
0
}
244
245
int ogs_getopt_long(ogs_getopt_t *options,
246
              const ogs_getopt_long_t *longopts,
247
              int *longindex)
248
0
{
249
0
    int i;
250
0
    char *option = options->argv[options->optind];
251
0
    if (option == 0) {
252
0
        return -1;
253
0
    } else if (ogs_getopt_is_dashdash(option)) {
254
0
        options->optind++; /* consume "--" */
255
0
        return -1;
256
0
    } else if (ogs_getopt_is_shortopt(option)) {
257
0
        return ogs_getopt_long_fallback(options, longopts, longindex);
258
0
    } else if (!ogs_getopt_is_longopt(option)) {
259
0
        if (options->permute) {
260
0
            int index = options->optind++;
261
0
            int r = ogs_getopt_long(options, longopts, longindex);
262
0
            ogs_getopt_permute(options, index);
263
0
            options->optind--;
264
0
            return r;
265
0
        } else {
266
0
            return -1;
267
0
        }
268
0
    }
269
270
    /* Parse as long option. */
271
0
    options->errmsg[0] = '\0';
272
0
    options->optopt = 0;
273
0
    options->optarg = 0;
274
0
    option += 2; /* skip "--" */
275
0
    options->optind++;
276
0
    for (i = 0; !ogs_getopt_longopts_end(longopts, i); i++) {
277
0
        const char *name = longopts[i].longname;
278
0
        if (ogs_getopt_longopts_match(name, option)) {
279
0
            char *arg;
280
0
            if (longindex)
281
0
                *longindex = i;
282
0
            options->optopt = longopts[i].shortname;
283
0
            arg = ogs_getopt_longopts_arg(option);
284
0
            if (longopts[i].argtype == OGS_GETOPT_NONE && arg != 0) {
285
0
                return ogs_getopt_error(options, OGS_GETOPT_MSG_TOOMANY, name);
286
0
            } if (arg != 0) {
287
0
                options->optarg = arg;
288
0
            } else if (longopts[i].argtype == OGS_GETOPT_REQUIRED) {
289
0
                options->optarg = options->argv[options->optind];
290
0
                if (options->optarg == 0)
291
0
                    return ogs_getopt_error(
292
0
                            options, OGS_GETOPT_MSG_MISSING, name);
293
0
                else
294
0
                    options->optind++;
295
0
            }
296
0
            return options->optopt;
297
0
        }
298
0
    }
299
0
    return ogs_getopt_error(options, OGS_GETOPT_MSG_INVALID, option);
300
0
}