Coverage Report

Created: 2023-05-19 06:16

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