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/enum.c
Line
Count
Source
1
2
/**
3
 * \file enumeration.c
4
 *
5
 *  Handle options with enumeration names and bit mask bit names
6
 *  for their arguments.
7
 *
8
 * @addtogroup autoopts
9
 * @{
10
 */
11
/*
12
 *  This routine will run run-on options through a pager so the
13
 *  user may examine, print or edit them at their leisure.
14
 *
15
 *  This file is part of AutoOpts, a companion to AutoGen.
16
 *  AutoOpts is free software.
17
 *  AutoOpts is Copyright (C) 1992-2018 by Bruce Korb - all rights reserved
18
 *
19
 *  AutoOpts is available under any one of two licenses.  The license
20
 *  in use must be one of these two and the choice is under the control
21
 *  of the user of the license.
22
 *
23
 *   The GNU Lesser General Public License, version 3 or later
24
 *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
25
 *
26
 *   The Modified Berkeley Software Distribution License
27
 *      See the file "COPYING.mbsd"
28
 *
29
 *  These files have the following sha256 sums:
30
 *
31
 *  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
32
 *  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
33
 *  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
34
 */
35
36
static void
37
enum_err(tOptions * pOpts, tOptDesc * pOD,
38
         char const * const * paz_names, int name_ct)
39
0
{
40
0
    size_t max_len = 0;
41
0
    size_t ttl_len = 0;
42
0
    int    ct_down = name_ct;
43
0
    int    hidden  = 0;
44
45
    /*
46
     *  A real "pOpts" pointer means someone messed up.  Give a real error.
47
     */
48
0
    if (pOpts > OPTPROC_EMIT_LIMIT)
49
0
        fprintf(option_usage_fp, pz_enum_err_fmt, pOpts->pzProgName,
50
0
                pOD->optArg.argString, pOD->pz_Name);
51
52
0
    fprintf(option_usage_fp, zValidKeys, pOD->pz_Name);
53
54
    /*
55
     *  If the first name starts with this funny character, then we have
56
     *  a first value with an unspellable name.  You cannot specify it.
57
     *  So, we don't list it either.
58
     */
59
0
    if (**paz_names == 0x7F) {
60
0
        paz_names++;
61
0
        hidden  = 1;
62
0
        ct_down = --name_ct;
63
0
    }
64
65
    /*
66
     *  Figure out the maximum length of any name, plus the total length
67
     *  of all the names.
68
     */
69
0
    {
70
0
        char const * const * paz = paz_names;
71
72
0
        do  {
73
0
            size_t len = strlen(*(paz++)) + 1;
74
0
            if (len > max_len)
75
0
                max_len = len;
76
0
            ttl_len += len;
77
0
        } while (--ct_down > 0);
78
79
0
        ct_down = name_ct;
80
0
    }
81
82
    /*
83
     *  IF any one entry is about 1/2 line or longer, print one per line
84
     */
85
0
    if (max_len > 35) {
86
0
        do  {
87
0
            fprintf(option_usage_fp, ENUM_ERR_LINE, *(paz_names++));
88
0
        } while (--ct_down > 0);
89
0
    }
90
91
    /*
92
     *  ELSE IF they all fit on one line, then do so.
93
     */
94
0
    else if (ttl_len < 76) {
95
0
        fputc(' ', option_usage_fp);
96
0
        do  {
97
0
            fputc(' ', option_usage_fp);
98
0
            fputs(*(paz_names++), option_usage_fp);
99
0
        } while (--ct_down > 0);
100
0
        fputc(NL, option_usage_fp);
101
0
    }
102
103
    /*
104
     *  Otherwise, columnize the output
105
     */
106
0
    else {
107
0
        unsigned int ent_no = 0;
108
0
        char fmt[16];  /* format for all-but-last entries on a line */
109
110
0
        if (snprintf(fmt, 16, ENUM_ERR_WIDTH, (int)max_len) >= 16)
111
0
            option_exits(EXIT_FAILURE);
112
0
        max_len = 78 / max_len; /* max_len is now max entries on a line */
113
0
        fputs(TWO_SPACES_STR, option_usage_fp);
114
115
        /*
116
         *  Loop through all but the last entry
117
         */
118
0
        ct_down = name_ct;
119
0
        while (--ct_down > 0) {
120
0
            if (++ent_no == max_len) {
121
                /*
122
                 *  Last entry on a line.  Start next line, too.
123
                 */
124
0
                fprintf(option_usage_fp, NLSTR_SPACE_FMT, *(paz_names++));
125
0
                ent_no = 0;
126
0
            }
127
128
0
            else
129
0
                fprintf(option_usage_fp, fmt, *(paz_names++) );
130
0
        }
131
0
        fprintf(option_usage_fp, NLSTR_FMT, *paz_names);
132
0
    }
133
134
0
    if (pOpts > OPTPROC_EMIT_LIMIT) {
135
0
        fprintf(option_usage_fp, zIntRange, hidden, name_ct - 1 + hidden);
136
137
0
        (*(pOpts->pUsageProc))(pOpts, EXIT_FAILURE);
138
        /* NOTREACHED */
139
0
    }
140
141
0
    if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_MEMBERSHIP) {
142
0
        fprintf(option_usage_fp, zLowerBits, name_ct);
143
0
        fputs(zSetMemberSettings, option_usage_fp);
144
0
    } else {
145
0
        fprintf(option_usage_fp, zIntRange, hidden, name_ct - 1 + hidden);
146
0
    }
147
0
}
148
149
/**
150
 * Convert a name or number into a binary number.
151
 * "~0" and "-1" will be converted to the largest value in the enumeration.
152
 *
153
 * @param name       the keyword name (number) to convert
154
 * @param pOpts      the program's option descriptor
155
 * @param pOD        the option descriptor for this option
156
 * @param paz_names  the list of keywords for this option
157
 * @param name_ct    the count of keywords
158
 */
159
static uintptr_t
160
find_name(char const * name, tOptions * pOpts, tOptDesc * pOD,
161
          char const * const *  paz_names, unsigned int name_ct)
162
0
{
163
    /*
164
     *  Return the matching index as a pointer sized integer.
165
     *  The result gets stashed in a char * pointer.
166
     */
167
0
    uintptr_t   res = name_ct;
168
0
    size_t      len = strlen((char *)name);
169
0
    uintptr_t   idx;
170
171
0
    if (IS_DEC_DIGIT_CHAR(*name)) {
172
0
        char * pz = VOIDP(name);
173
0
        unsigned long val = strtoul(pz, &pz, 0);
174
0
        if ((*pz == NUL) && (val < name_ct))
175
0
            return (uintptr_t)val;
176
0
        pz_enum_err_fmt = znum_too_large;
177
0
        option_usage_fp = stderr;
178
0
        enum_err(pOpts, pOD, paz_names, (int)name_ct);
179
0
        return name_ct;
180
0
    }
181
182
0
    if (IS_INVERSION_CHAR(*name) && (name[2] == NUL)) {
183
0
        if (  ((name[0] == '~') && (name[1] == '0'))
184
0
           || ((name[0] == '-') && (name[1] == '1')))
185
0
        return (uintptr_t)(name_ct - 1);
186
0
        goto oops;
187
0
    }
188
189
    /*
190
     *  Look for an exact match, but remember any partial matches.
191
     *  Multiple partial matches means we have an ambiguous match.
192
     */
193
0
    for (idx = 0; idx < name_ct; idx++) {
194
0
        if (strncmp((char *)paz_names[idx], (char *)name, len) == 0) {
195
0
            if (paz_names[idx][len] == NUL)
196
0
                return idx;  /* full match */
197
198
0
            if (res == name_ct)
199
0
                res = idx; /* save partial match */
200
0
            else
201
0
                res = (uintptr_t)~0;  /* may yet find full match */
202
0
        }
203
0
    }
204
205
0
    if (res < name_ct)
206
0
        return res; /* partial match */
207
208
0
 oops:
209
210
0
    pz_enum_err_fmt = (res == name_ct) ? zNoKey : zambiguous_key;
211
0
    option_usage_fp = stderr;
212
0
    enum_err(pOpts, pOD, paz_names, (int)name_ct);
213
0
    return name_ct;
214
0
}
215
216
217
/*=export_func  optionKeywordName
218
 * what:  Convert between enumeration values and strings
219
 * private:
220
 *
221
 * arg:   tOptDesc *,    pOD,       enumeration option description
222
 * arg:   unsigned int,  enum_val,  the enumeration value to map
223
 *
224
 * ret_type:  char const *
225
 * ret_desc:  the enumeration name from const memory
226
 *
227
 * doc:   This converts an enumeration value into the matching string.
228
=*/
229
char const *
230
optionKeywordName(tOptDesc * pOD, unsigned int enum_val)
231
0
{
232
0
    tOptDesc od = { 0 };
233
0
    od.optArg.argEnum = enum_val;
234
235
0
    (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, &od );
236
0
    return od.optArg.argString;
237
0
}
238
239
240
/*=export_func  optionEnumerationVal
241
 * what:  Convert from a string to an enumeration value
242
 * private:
243
 *
244
 * arg:   tOptions *,    pOpts,     the program options descriptor
245
 * arg:   tOptDesc *,    pOD,       enumeration option description
246
 * arg:   char const * const *,  paz_names, list of enumeration names
247
 * arg:   unsigned int,  name_ct,   number of names in list
248
 *
249
 * ret_type:  uintptr_t
250
 * ret_desc:  the enumeration value
251
 *
252
 * doc:   This converts the optArg.argString string from the option description
253
 *        into the index corresponding to an entry in the name list.
254
 *        This will match the generated enumeration value.
255
 *        Full matches are always accepted.  Partial matches are accepted
256
 *        if there is only one partial match.
257
=*/
258
uintptr_t
259
optionEnumerationVal(tOptions * pOpts, tOptDesc * pOD,
260
                     char const * const * paz_names, unsigned int name_ct)
261
0
{
262
0
    uintptr_t res = 0UL;
263
264
    /*
265
     *  IF the program option descriptor pointer is invalid,
266
     *  then it is some sort of special request.
267
     */
268
0
    switch ((uintptr_t)pOpts) {
269
0
    case (uintptr_t)OPTPROC_EMIT_USAGE:
270
        /*
271
         *  print the list of enumeration names.
272
         */
273
0
        enum_err(pOpts, pOD, paz_names, (int)name_ct);
274
0
        break;
275
276
0
    case (uintptr_t)OPTPROC_EMIT_SHELL:
277
0
    {
278
0
        unsigned int ix = (unsigned int)pOD->optArg.argEnum;
279
        /*
280
         *  print the name string.
281
         */
282
0
        if (ix >= name_ct)
283
0
            printf(INVALID_FMT, ix);
284
0
        else
285
0
            fputs(paz_names[ ix ], stdout);
286
287
0
        break;
288
0
    }
289
290
0
    case (uintptr_t)OPTPROC_RETURN_VALNAME:
291
0
    {
292
0
        unsigned int ix = (unsigned int)pOD->optArg.argEnum;
293
        /*
294
         *  Replace the enumeration value with the name string.
295
         */
296
0
        if (ix >= name_ct)
297
0
            return (uintptr_t)INVALID_STR;
298
299
0
        pOD->optArg.argString = paz_names[ix];
300
0
        break;
301
0
    }
302
303
0
    default:
304
0
        if ((pOD->fOptState & OPTST_RESET) != 0)
305
0
            break;
306
307
0
        res = find_name(pOD->optArg.argString, pOpts, pOD, paz_names, name_ct);
308
309
0
        if (pOD->fOptState & OPTST_ALLOC_ARG) {
310
0
            AGFREE(pOD->optArg.argString);
311
0
            pOD->fOptState &= ~OPTST_ALLOC_ARG;
312
0
            pOD->optArg.argString = NULL;
313
0
        }
314
0
    }
315
316
0
    return res;
317
0
}
318
319
static void
320
set_memb_shell(tOptions * pOpts, tOptDesc * pOD, char const * const * paz_names,
321
               unsigned int name_ct)
322
0
{
323
    /*
324
     *  print the name string.
325
     */
326
0
    unsigned int ix =  0;
327
0
    uintptr_t  bits = (uintptr_t)pOD->optCookie;
328
0
    size_t     len  = 0;
329
330
0
    (void)pOpts;
331
0
    bits &= ((uintptr_t)1 << (uintptr_t)name_ct) - (uintptr_t)1;
332
333
0
    while (bits != 0) {
334
0
        if (bits & 1) {
335
0
            if (len++ > 0) fputs(OR_STR, stdout);
336
0
            fputs(paz_names[ix], stdout);
337
0
        }
338
0
        if (++ix >= name_ct) break;
339
0
        bits >>= 1;
340
0
    }
341
0
}
342
343
static void
344
set_memb_names(tOptions * opts, tOptDesc * od, char const * const * nm_list,
345
               unsigned int nm_ct)
346
0
{
347
0
    char *     pz;
348
0
    uintptr_t  mask = (1UL << (uintptr_t)nm_ct) - 1UL;
349
0
    uintptr_t  bits = (uintptr_t)od->optCookie & mask;
350
0
    unsigned int ix = 0;
351
0
    size_t     len  = 1;
352
353
    /*
354
     *  Replace the enumeration value with the name string.
355
     *  First, determine the needed length, then allocate and fill in.
356
     */
357
0
    while (bits != 0) {
358
0
        if (bits & 1)
359
0
            len += strlen(nm_list[ix]) + PLUS_STR_LEN + 1;
360
0
        if (++ix >= nm_ct) break;
361
0
        bits >>= 1;
362
0
    }
363
364
0
    od->optArg.argString = pz = AGALOC(len, "enum");
365
0
    bits = (uintptr_t)od->optCookie & mask;
366
0
    if (bits == 0) {
367
0
        *pz = NUL;
368
0
        return;
369
0
    }
370
371
0
    for (ix = 0; ; ix++) {
372
0
        size_t nln;
373
0
        int    doit = bits & 1;
374
375
0
        bits >>= 1;
376
0
        if (doit == 0)
377
0
            continue;
378
379
0
        nln = strlen(nm_list[ix]);
380
0
        memcpy(pz, nm_list[ix], nln);
381
0
        pz += nln;
382
0
        if (bits == 0)
383
0
            break;
384
0
        memcpy(pz, PLUS_STR, PLUS_STR_LEN);
385
0
        pz += PLUS_STR_LEN;
386
0
    }
387
0
    *pz = NUL;
388
0
    (void)opts;
389
0
}
390
391
/**
392
 * Check membership start conditions.  An equal character (@samp{=}) says to
393
 * clear the result and not carry over any residual value.  A carat
394
 * (@samp{^}), which may follow the equal character, says to invert the
395
 * result.  The scanning pointer is advanced past these characters and any
396
 * leading white space.  Invalid sequences are indicated by setting the
397
 * scanning pointer to NULL.
398
 *
399
 * @param od      the set membership option description
400
 * @param argp    a pointer to the string scanning pointer
401
 * @param invert  a pointer to the boolean inversion indicator
402
 *
403
 * @returns either zero or the original value for the optCookie.
404
 */
405
static uintptr_t
406
check_membership_start(tOptDesc * od, char const ** argp, bool * invert)
407
0
{
408
0
    uintptr_t    res = (uintptr_t)od->optCookie;
409
0
    char const * arg = SPN_WHITESPACE_CHARS(od->optArg.argString);
410
0
    if ((arg == NULL) || (*arg == NUL))
411
0
        goto member_start_fail;
412
413
0
    *invert = false;
414
415
0
    switch (*arg) {
416
0
    case '=':
417
0
        res = 0UL;
418
0
        arg = SPN_WHITESPACE_CHARS(arg + 1);
419
0
        switch (*arg) {
420
0
        case '=': case ',':
421
0
            goto member_start_fail;
422
0
        case '^':
423
0
            goto inversion;
424
0
        default:
425
0
            break;
426
0
        }
427
0
        break;
428
429
0
    case '^':
430
0
    inversion:
431
0
        *invert = true;
432
0
        arg = SPN_WHITESPACE_CHARS(arg + 1);
433
0
        if (*arg != ',')
434
0
            break;
435
        /* FALLTHROUGH */
436
437
0
    case ',':
438
0
        goto member_start_fail;
439
440
0
    default:
441
0
        break;
442
0
    }
443
444
0
    *argp = arg;
445
0
    return res;
446
447
0
member_start_fail:
448
0
    *argp = NULL;
449
0
    return 0UL;
450
0
}
451
452
/**
453
 * convert a name to a bit.  Look up a name string to get a bit number
454
 * and shift the value "1" left that number of bits.
455
 *
456
 * @param opts      program options descriptor
457
 * @param od        the set membership option description
458
 * @param pz        address of the start of the bit name
459
 * @param nm_list   the list of names for this option
460
 * @param nm_ct     the number of entries in this list
461
 *
462
 * @returns 0UL on error, other an unsigned long with the correct bit set.
463
 */
464
static uintptr_t
465
find_member_bit(tOptions * opts, tOptDesc * od, char const * pz, int len,
466
                char const * const * nm_list, unsigned int nm_ct)
467
0
{
468
0
    char nm_buf[ AO_NAME_SIZE ];
469
470
0
    memcpy(nm_buf, pz, len);
471
0
    nm_buf[len] = NUL;
472
473
0
    {
474
0
        unsigned int shift_ct = (unsigned int)
475
0
            find_name(nm_buf, opts, od, nm_list, nm_ct);
476
0
        if (shift_ct >= nm_ct)
477
0
            return 0UL;
478
479
0
        return 1UL << shift_ct;
480
0
    }
481
0
}
482
483
/*=export_func  optionMemberList
484
 * what:  Get the list of members of a bit mask set
485
 *
486
 * arg:   tOptDesc *,  od,   the set membership option description
487
 *
488
 * ret_type: char *
489
 * ret_desc: the names of the set bits
490
 *
491
 * doc:   This converts the OPT_VALUE_name mask value to a allocated string.
492
 *        It is the caller's responsibility to free the string.
493
=*/
494
char *
495
optionMemberList(tOptDesc * od)
496
0
{
497
0
    uintptr_t    sv = od->optArg.argIntptr;
498
0
    char * res;
499
0
    (*(od->pOptProc))(OPTPROC_RETURN_VALNAME, od);
500
0
    res = VOIDP(od->optArg.argString);
501
0
    od->optArg.argIntptr = sv;
502
0
    return res;
503
0
}
504
505
/*=export_func  optionSetMembers
506
 * what:  Convert between bit flag values and strings
507
 * private:
508
 *
509
 * arg:   tOptions *,     opts,     the program options descriptor
510
 * arg:   tOptDesc *,     od,       the set membership option description
511
 * arg:   char const * const *,
512
 *                       nm_list,  list of enumeration names
513
 * arg:   unsigned int,  nm_ct,    number of names in list
514
 *
515
 * doc:   This converts the optArg.argString string from the option description
516
 *        into the index corresponding to an entry in the name list.
517
 *        This will match the generated enumeration value.
518
 *        Full matches are always accepted.  Partial matches are accepted
519
 *        if there is only one partial match.
520
=*/
521
void
522
optionSetMembers(tOptions * opts, tOptDesc * od,
523
                 char const * const * nm_list, unsigned int nm_ct)
524
0
{
525
    /*
526
     *  IF the program option descriptor pointer is invalid,
527
     *  then it is some sort of special request.
528
     */
529
0
    switch ((uintptr_t)opts) {
530
0
    case (uintptr_t)OPTPROC_EMIT_USAGE:
531
0
        enum_err(OPTPROC_EMIT_USAGE, od, nm_list, nm_ct);
532
0
        return;
533
534
0
    case (uintptr_t)OPTPROC_EMIT_SHELL:
535
0
        set_memb_shell(opts, od, nm_list, nm_ct);
536
0
        return;
537
538
0
    case (uintptr_t)OPTPROC_RETURN_VALNAME:
539
0
        set_memb_names(opts, od, nm_list, nm_ct);
540
0
        return;
541
542
0
    default:
543
0
        break;
544
0
    }
545
546
0
    if ((od->fOptState & OPTST_RESET) != 0)
547
0
        return;
548
549
0
    {
550
0
        char const * arg;
551
0
        bool         invert;
552
0
        uintptr_t    res = check_membership_start(od, &arg, &invert);
553
0
        if (arg == NULL)
554
0
            goto fail_return;
555
556
0
        while (*arg != NUL) {
557
0
            bool inv_val = false;
558
0
            int  len;
559
560
0
            switch (*arg) {
561
0
            case ',':
562
0
                arg = SPN_WHITESPACE_CHARS(arg+1);
563
0
                if ((*arg == ',') || (*arg == '|'))
564
0
                    goto fail_return;
565
0
                continue;
566
567
0
            case '-':
568
0
            case '!':
569
0
                inv_val = true;
570
                /* FALLTHROUGH */
571
572
0
            case '+':
573
0
            case '|':
574
0
                arg = SPN_WHITESPACE_CHARS(arg+1);
575
0
            }
576
577
0
            len = (int)(BRK_SET_SEPARATOR_CHARS(arg) - arg);
578
0
            if (len == 0)
579
0
                break;
580
581
0
            if ((len == 3) && (strncmp(arg, zAll, 3) == 0)) {
582
0
                if (inv_val)
583
0
                     res = 0;
584
0
                else res = ~0UL;
585
0
            }
586
0
            else if ((len == 4) && (strncmp(arg, zNone, 4) == 0)) {
587
0
                if (! inv_val)
588
0
                    res = 0;
589
0
            }
590
0
            else do {
591
0
                char *    pz;
592
0
                uintptr_t bit = strtoul(arg, &pz, 0);
593
594
0
                if (pz != arg + len) {
595
0
                    bit = find_member_bit(opts, od, pz, len, nm_list, nm_ct);
596
0
                    if (bit == 0UL)
597
0
                        goto fail_return;
598
0
                }
599
0
                if (inv_val)
600
0
                     res &= ~bit;
601
0
                else res |= bit;
602
0
            } while (false);
603
604
0
            arg = SPN_WHITESPACE_CHARS(arg + len);
605
0
        }
606
607
0
        if (invert)
608
0
            res ^= ~0UL;
609
610
0
        if (nm_ct < (8 * sizeof(uintptr_t)))
611
0
            res &= (1UL << nm_ct) - 1UL;
612
613
0
        od->optCookie = VOIDP(res);
614
0
    }
615
0
    return;
616
617
0
fail_return:
618
0
    od->optCookie = VOIDP(0);
619
0
}
620
621
/** @}
622
 *
623
 * Local Variables:
624
 * mode: C
625
 * c-file-style: "stroustrup"
626
 * indent-tabs-mode: nil
627
 * End:
628
 * end of autoopts/enum.c */