Coverage Report

Created: 2026-02-26 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ntp-dev/sntp/libopts/sort.c
Line
Count
Source
1
2
/*
3
 * \file sort.c
4
 *
5
 *  This module implements argument sorting.
6
 *
7
 * @addtogroup autoopts
8
 * @{
9
 */
10
/*
11
 *  This file is part of AutoOpts, a companion to AutoGen.
12
 *  AutoOpts is free software.
13
 *  AutoOpts is Copyright (C) 1992-2018 by Bruce Korb - all rights reserved
14
 *
15
 *  AutoOpts is available under any one of two licenses.  The license
16
 *  in use must be one of these two and the choice is under the control
17
 *  of the user of the license.
18
 *
19
 *   The GNU Lesser General Public License, version 3 or later
20
 *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
21
 *
22
 *   The Modified Berkeley Software Distribution License
23
 *      See the file "COPYING.mbsd"
24
 *
25
 *  These files have the following sha256 sums:
26
 *
27
 *  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
28
 *  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
29
 *  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
30
 */
31
32
/*
33
 *  "must_arg" and "maybe_arg" are really similar.  The biggest
34
 *  difference is that "may" will consume the next argument only if it
35
 *  does not start with a hyphen and "must" will consume it, hyphen or not.
36
 */
37
static tSuccess
38
must_arg(tOptions * opts, char * arg_txt, tOptState * pOS,
39
         char ** opt_txt, uint32_t * opt_idx)
40
0
{
41
    /*
42
     *  An option argument is required.  Long options can either have
43
     *  a separate command line argument, or an argument attached by
44
     *  the '=' character.  Figure out which.
45
     */
46
0
    switch (pOS->optType) {
47
0
    case TOPT_SHORT:
48
        /*
49
         *  See if an arg string follows the flag character.  If not,
50
         *  the next arg must be the option argument.
51
         */
52
0
        if (*arg_txt != NUL)
53
0
            return SUCCESS;
54
0
        break;
55
56
0
    case TOPT_LONG:
57
        /*
58
         *  See if an arg string has already been assigned (glued on
59
         *  with an `=' character).  If not, the next is the opt arg.
60
         */
61
0
        if (pOS->pzOptArg != NULL)
62
0
            return SUCCESS;
63
0
        break;
64
65
0
    default:
66
0
        return FAILURE;
67
0
    }
68
0
    if (opts->curOptIdx >= opts->origArgCt)
69
0
        return FAILURE;
70
71
0
    opt_txt[ (*opt_idx)++ ] = opts->origArgVect[ (opts->curOptIdx)++ ];
72
0
    return SUCCESS;
73
0
}
74
75
static tSuccess
76
maybe_arg(tOptions * opts, char * arg_txt, tOptState * pOS,
77
          char ** opt_txt, uint32_t * opt_idx)
78
0
{
79
    /*
80
     *  An option argument is optional.
81
     */
82
0
    switch (pOS->optType) {
83
0
    case TOPT_SHORT:
84
        /*
85
         *  IF nothing is glued on after the current flag character,
86
         *  THEN see if there is another argument.  If so and if it
87
         *  does *NOT* start with a hyphen, then it is the option arg.
88
         */
89
0
        if (*arg_txt != NUL)
90
0
            return SUCCESS;
91
0
        break;
92
93
0
    case TOPT_LONG:
94
        /*
95
         *  Look for an argument if we don't already have one (glued on
96
         *  with a `=' character)
97
         */
98
0
        if (pOS->pzOptArg != NULL)
99
0
            return SUCCESS;
100
0
        break;
101
102
0
    default:
103
0
        return FAILURE;
104
0
    }
105
0
    if (opts->curOptIdx >= opts->origArgCt)
106
0
        return PROBLEM;
107
108
0
    arg_txt = opts->origArgVect[ opts->curOptIdx ];
109
0
    if (*arg_txt != '-')
110
0
        opt_txt[ (*opt_idx)++ ] = opts->origArgVect[ (opts->curOptIdx)++ ];
111
0
    return SUCCESS;
112
0
}
113
114
/*
115
 *  Process a string of short options glued together.  If the last one
116
 *  does or may take an argument, the do the argument processing and leave.
117
 */
118
static tSuccess
119
short_opt_ck(tOptions * opts, char * arg_txt, tOptState * pOS,
120
             char ** opt_txt, uint32_t * opt_idx)
121
0
{
122
0
    while (*arg_txt != NUL) {
123
0
        if (FAILED(opt_find_short(opts, (uint8_t)*arg_txt, pOS)))
124
0
            return FAILURE;
125
126
        /*
127
         *  See if we can have an arg.
128
         */
129
0
        if (OPTST_GET_ARGTYPE(pOS->pOD->fOptState) == OPARG_TYPE_NONE) {
130
0
            arg_txt++;
131
132
0
        } else if (pOS->pOD->fOptState & OPTST_ARG_OPTIONAL) {
133
            /*
134
             *  Take an argument if it is not attached and it does not
135
             *  start with a hyphen.
136
             */
137
0
            if (arg_txt[1] != NUL)
138
0
                return SUCCESS;
139
140
0
            arg_txt = opts->origArgVect[ opts->curOptIdx ];
141
0
            if (*arg_txt != '-')
142
0
                opt_txt[ (*opt_idx)++ ] =
143
0
                    opts->origArgVect[ (opts->curOptIdx)++ ];
144
0
            return SUCCESS;
145
146
0
        } else {
147
            /*
148
             *  IF we need another argument, be sure it is there and
149
             *  take it.
150
             */
151
0
            if (arg_txt[1] == NUL) {
152
0
                if (opts->curOptIdx >= opts->origArgCt)
153
0
                    return FAILURE;
154
0
                opt_txt[ (*opt_idx)++ ] =
155
0
                    opts->origArgVect[ (opts->curOptIdx)++ ];
156
0
            }
157
0
            return SUCCESS;
158
0
        }
159
0
    }
160
0
    return SUCCESS;
161
0
}
162
163
/*
164
 *  If the program wants sorted options (separated operands and options),
165
 *  then this routine will to the trick.
166
 */
167
static void
168
optionSort(tOptions * opts)
169
0
{
170
0
    char **  opt_txt;
171
0
    char **  ppzOpds;
172
0
    uint32_t optsIdx = 0;
173
0
    uint32_t opdsIdx = 0;
174
175
0
    tOptState os = OPTSTATE_INITIALIZER(DEFINED);
176
177
    /*
178
     *  Disable for POSIX conformance, or if there are no operands.
179
     */
180
0
    if (  (getenv("POSIXLY_CORRECT") != NULL)
181
0
       || NAMED_OPTS(opts))
182
0
        return;
183
184
    /*
185
     *  Make sure we can allocate two full-sized arg vectors.
186
     */
187
0
    opt_txt = malloc(opts->origArgCt * sizeof(char *));
188
0
    if (opt_txt == NULL)
189
0
        goto exit_no_mem;
190
191
0
    ppzOpds = malloc(opts->origArgCt * sizeof(char *));
192
0
    if (ppzOpds == NULL) {
193
0
        free(opt_txt);
194
0
        goto exit_no_mem;
195
0
    }
196
197
0
    opts->curOptIdx = 1;
198
0
    opts->pzCurOpt  = NULL;
199
200
    /*
201
     *  Now, process all the options from our current position onward.
202
     *  (This allows interspersed options and arguments for the few
203
     *  non-standard programs that require it.)
204
     */
205
0
    for (;;) {
206
0
        char * arg_txt;
207
0
        tSuccess res;
208
209
        /*
210
         *  If we're out of arguments, we're done.  Join the option and
211
         *  operand lists into the original argument vector.
212
         */
213
0
        if (opts->curOptIdx >= opts->origArgCt) {
214
0
            errno = 0;
215
0
            goto joinLists;
216
0
        }
217
218
0
        arg_txt = opts->origArgVect[ opts->curOptIdx ];
219
0
        if (*arg_txt != '-') {
220
0
            ppzOpds[ opdsIdx++ ] = opts->origArgVect[ (opts->curOptIdx)++ ];
221
0
            continue;
222
0
        }
223
224
0
        switch (arg_txt[1]) {
225
0
        case NUL:
226
            /*
227
             *  A single hyphen is an operand.
228
             */
229
0
            ppzOpds[ opdsIdx++ ] = opts->origArgVect[ (opts->curOptIdx)++ ];
230
0
            continue;
231
232
0
        case '-':
233
            /*
234
             *  Two consecutive hypens.  Put them on the options list and then
235
             *  _always_ force the remainder of the arguments to be operands.
236
             */
237
0
            if (arg_txt[2] == NUL) {
238
0
                opt_txt[ optsIdx++ ] =
239
0
                    opts->origArgVect[ (opts->curOptIdx)++ ];
240
0
                goto restOperands;
241
0
            }
242
0
            res = opt_find_long(opts, arg_txt+2, &os);
243
0
            break;
244
245
0
        default:
246
            /*
247
             *  If short options are not allowed, then do long
248
             *  option processing.  Otherwise the character must be a
249
             *  short (i.e. single character) option.
250
             */
251
0
            if ((opts->fOptSet & OPTPROC_SHORTOPT) == 0) {
252
0
                res = opt_find_long(opts, arg_txt+1, &os);
253
0
            } else {
254
0
                res = opt_find_short(opts, (uint8_t)arg_txt[1], &os);
255
0
            }
256
0
            break;
257
0
        }
258
0
        if (FAILED(res)) {
259
0
            errno = EINVAL;
260
0
            goto freeTemps;
261
0
        }
262
263
        /*
264
         *  We've found an option.  Add the argument to the option list.
265
         *  Next, we have to see if we need to pull another argument to be
266
         *  used as the option argument.
267
         */
268
0
        opt_txt[ optsIdx++ ] = opts->origArgVect[ (opts->curOptIdx)++ ];
269
270
0
        if (OPTST_GET_ARGTYPE(os.pOD->fOptState) == OPARG_TYPE_NONE) {
271
            /*
272
             *  No option argument.  If we have a short option here,
273
             *  then scan for short options until we get to the end
274
             *  of the argument string.
275
             */
276
0
            if (  (os.optType == TOPT_SHORT)
277
0
               && FAILED(short_opt_ck(opts, arg_txt+2, &os, opt_txt,
278
0
                                      &optsIdx)) )  {
279
0
                errno = EINVAL;
280
0
                goto freeTemps;
281
0
            }
282
283
0
        } else if (os.pOD->fOptState & OPTST_ARG_OPTIONAL) {
284
0
            switch (maybe_arg(opts, arg_txt+2, &os, opt_txt, &optsIdx)) {
285
0
            case FAILURE: errno = EIO; goto freeTemps;
286
0
            case PROBLEM: errno = 0;   goto joinLists;
287
0
            }
288
289
0
        } else {
290
0
            switch (must_arg(opts, arg_txt+2, &os, opt_txt, &optsIdx)) {
291
0
            case PROBLEM:
292
0
            case FAILURE: errno = EIO; goto freeTemps;
293
0
            }
294
0
        }
295
0
    } /* for (;;) */
296
297
0
 restOperands:
298
0
    while (opts->curOptIdx < opts->origArgCt)
299
0
        ppzOpds[ opdsIdx++ ] = opts->origArgVect[ (opts->curOptIdx)++ ];
300
301
0
 joinLists:
302
0
    if (optsIdx > 0)
303
0
        memcpy(opts->origArgVect + 1, opt_txt,
304
0
               (size_t)optsIdx * sizeof(char *));
305
0
    if (opdsIdx > 0)
306
0
        memcpy(opts->origArgVect + 1 + optsIdx, ppzOpds,
307
0
               (size_t)opdsIdx * sizeof(char *));
308
309
0
 freeTemps:
310
0
    free(opt_txt);
311
0
    free(ppzOpds);
312
0
    return;
313
314
0
 exit_no_mem:
315
0
    errno = ENOMEM;
316
0
    return;
317
0
}
318
319
/** @}
320
 *
321
 * Local Variables:
322
 * mode: C
323
 * c-file-style: "stroustrup"
324
 * indent-tabs-mode: nil
325
 * End:
326
 * end of autoopts/sort.c */