Coverage Report

Created: 2023-05-19 06:16

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