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/putshell.c
Line
Count
Source
1
2
/**
3
 * \file putshell.c
4
 *
5
 *  This module will interpret the options set in the tOptions
6
 *  structure and print them to standard out in a fashion that
7
 *  will allow them to be interpreted by the Bourne or Korn shells.
8
 *
9
 * @addtogroup autoopts
10
 * @{
11
 */
12
/*
13
 *  This file is part of AutoOpts, a companion to AutoGen.
14
 *  AutoOpts is free software.
15
 *  AutoOpts is Copyright (C) 1992-2018 by Bruce Korb - all rights reserved
16
 *
17
 *  AutoOpts is available under any one of two licenses.  The license
18
 *  in use must be one of these two and the choice is under the control
19
 *  of the user of the license.
20
 *
21
 *   The GNU Lesser General Public License, version 3 or later
22
 *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
23
 *
24
 *   The Modified Berkeley Software Distribution License
25
 *      See the file "COPYING.mbsd"
26
 *
27
 *  These files have the following sha256 sums:
28
 *
29
 *  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
30
 *  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
31
 *  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
32
 */
33
34
/**
35
 * Count the number of bytes required to represent a string as a
36
 * compilable string.
37
 *
38
 * @param[in] scan    the text to be rewritten as a C program text string.
39
 * @param[in] nl_len  the number of bytes used for each embedded newline.
40
 *
41
 * @returns the count, including the terminating NUL byte.
42
 */
43
static size_t
44
string_size(char const * scan, size_t nl_len)
45
0
{
46
    /*
47
     *  Start by counting the start and end quotes, plus the NUL.
48
     */
49
0
    size_t res_ln = 3;
50
51
0
    for (;;) {
52
0
        char ch = *(scan++);
53
0
        if ((ch >= ' ') && (ch <= '~')) {
54
55
            /*
56
             * a backslash allowance for double quotes and baskslashes
57
             */
58
0
            res_ln += ((ch == '"') || (ch == '\\')) ? 2 : 1;
59
0
        }
60
61
        /*
62
         *  When not a normal character, then count the characters
63
         *  required to represent whatever it is.
64
         */
65
0
        else switch (ch) {
66
0
        case NUL:
67
0
            return res_ln;
68
69
0
        case NL:
70
0
            res_ln += nl_len;
71
0
            break;
72
73
0
        case HT:
74
0
        case BEL:
75
0
        case BS:
76
0
        case FF:
77
0
        case CR:
78
0
        case VT:
79
0
            res_ln += 2;
80
0
            break;
81
82
0
        default:
83
0
            res_ln += 4; /* text len for \xNN */
84
0
        }
85
0
    }
86
0
}
87
88
/*=export_func  optionQuoteString
89
 * private:
90
 *
91
 * what:  Print a string as quoted text suitable for a C compiler.
92
 * arg:   + char const * + text  + a block of text to quote +
93
 * arg:   + char const * + nl    + line splice text         +
94
 *
95
 * ret_type:  char const *
96
 * ret_desc:  the allocated input string as a quoted string
97
 *
98
 * doc:
99
 *  This is for internal use by autogen and autoopts.
100
 *  It takes an input string and produces text the C compiler can process
101
 *  to produce an exact copy of the original string.
102
 *  The caller must deallocate the result.  Standard C strings and
103
 *  K&R strings are distinguished by the "nl" string.
104
=*/
105
char const *
106
optionQuoteString(char const * text, char const * nl)
107
0
{
108
0
    size_t   nl_len = strlen(nl);
109
0
    size_t   out_sz = string_size(text, nl_len);
110
0
    char *   out;
111
0
    char *   res    = out = AGALOC(out_sz, "quot str");
112
113
0
    *(out++) = '"';
114
115
0
    for (;;) {
116
0
        unsigned char ch = (unsigned char)*text;
117
0
        if ((ch >= ' ') && (ch <= '~')) {
118
0
            if ((ch == '"') || (ch == '\\'))
119
                /*
120
                 *  We must escape these characters in the output string
121
                 */
122
0
                *(out++) = '\\';
123
0
            *(out++) = (char)ch;
124
125
0
        } else switch (ch) {
126
0
#       define   add_esc_ch(_ch)  { *(out++) = '\\'; *(out++) = (_ch); }
127
0
        case BEL: add_esc_ch('a'); break;
128
0
        case BS:  add_esc_ch('b'); break;
129
0
        case HT:  add_esc_ch('t'); break;
130
0
        case VT:  add_esc_ch('v'); break;
131
0
        case FF:  add_esc_ch('f'); break;
132
0
        case CR:  add_esc_ch('r'); break;
133
134
0
        case LF:
135
            /*
136
             *  Place contiguous new-lines on a single line.
137
             *  The current character is a NL, check the next one.
138
             */
139
0
            while (*++text == NL)
140
0
                add_esc_ch('n');
141
142
            /*
143
             *  Insert a splice before starting next line
144
             */
145
0
            if (*text != NUL) {
146
0
                memcpy(out, nl, nl_len);
147
0
                out += nl_len;
148
149
0
                continue; /* text is already at the next character */
150
0
            }
151
152
0
            add_esc_ch('n');
153
            /* FALLTHROUGH */
154
155
0
        case NUL:
156
            /*
157
             *  End of string.  Terminate the quoted output.  If necessary,
158
             *  deallocate the text string.  Return the scan resumption point.
159
             */
160
0
            *(out++) = '"';
161
0
            *(out++) = NUL;
162
0
#ifndef NDEBUG
163
0
            if ((size_t)(out - res) > out_sz) {
164
0
                fputs(misguess_len, stderr);
165
0
                option_exits(EXIT_FAILURE);
166
0
            }
167
0
#endif
168
0
            return res;
169
170
0
        default:
171
            /*
172
             *  sprintf is safe here, because we already computed
173
             *  the amount of space we will be using.  Assertion is above.
174
             */
175
0
            out += sprintf(out, MK_STR_OCT_FMT, ch);
176
0
        }
177
178
0
        text++;
179
0
#       undef add_esc_ch
180
0
    }
181
0
}
182
183
/**
184
 *  Print out escaped apostorophes.
185
 *
186
 *  @param[in] str  the apostrophies to print
187
 */
188
static char const *
189
print_quoted_apostrophes(char const * str)
190
0
{
191
0
    while (*str == APOSTROPHE) {
192
0
        fputs(QUOT_APOS, stdout);
193
0
        str++;
194
0
    }
195
0
    return str;
196
0
}
197
198
/**
199
 *  Print a single quote (apostrophe quoted) string.
200
 *  Other than somersaults for apostrophes, nothing else needs quoting.
201
 *
202
 *  @param[in] str  the string to print
203
 */
204
static void
205
print_quot_str(char const * str)
206
0
{
207
    /*
208
     *  Handle empty strings to make the rest of the logic simpler.
209
     */
210
0
    if ((str == NULL) || (*str == NUL)) {
211
0
        fputs(EMPTY_ARG, stdout);
212
0
        return;
213
0
    }
214
215
    /*
216
     *  Emit any single quotes/apostrophes at the start of the string and
217
     *  bail if that is all we need to do.
218
     */
219
0
    str = print_quoted_apostrophes(str);
220
0
    if (*str == NUL)
221
0
        return;
222
223
    /*
224
     *  Start the single quote string
225
     */
226
0
    fputc(APOSTROPHE, stdout);
227
0
    for (;;) {
228
0
        char const * pz = strchr(str, APOSTROPHE);
229
0
        if (pz == NULL)
230
0
            break;
231
232
        /*
233
         *  Emit the string up to the single quote (apostrophe) we just found.
234
         */
235
0
        (void)fwrite(str, (size_t)(pz - str), (size_t)1, stdout);
236
237
        /*
238
         * Close the current string, emit the apostrophes and re-open the
239
         * string (IFF there is more text to print).
240
         */
241
0
        fputc(APOSTROPHE, stdout);
242
0
        str = print_quoted_apostrophes(pz);
243
0
        if (*str == NUL)
244
0
            return;
245
246
0
        fputc(APOSTROPHE, stdout);
247
0
    }
248
249
    /*
250
     *  If we broke out of the loop, we must still emit the remaining text
251
     *  and then close the single quote string.
252
     */
253
0
    fputs(str, stdout);
254
0
    fputc(APOSTROPHE, stdout);
255
0
}
256
257
static void
258
print_enumeration(tOptions * pOpts, tOptDesc * pOD)
259
0
{
260
0
    uintptr_t e_val = pOD->optArg.argEnum;
261
0
    printf(OPT_VAL_FMT, pOpts->pzPROGNAME, pOD->pz_NAME);
262
263
    /*
264
     *  Convert value to string, print that and restore numeric value.
265
     */
266
0
    (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD);
267
0
    printf(QUOT_ARG_FMT, pOD->optArg.argString);
268
0
    if (pOD->fOptState & OPTST_ALLOC_ARG)
269
0
        AGFREE(pOD->optArg.argString);
270
0
    pOD->optArg.argEnum = e_val;
271
272
0
    printf(OPT_END_FMT, pOpts->pzPROGNAME, pOD->pz_NAME);
273
0
}
274
275
static void
276
print_membership(tOptions * pOpts, tOptDesc * pOD)
277
0
{
278
0
    char const * svstr = pOD->optArg.argString;
279
0
    char const * pz;
280
0
    uintptr_t val = 1;
281
0
    printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME,
282
0
           (int)(uintptr_t)(pOD->optCookie));
283
0
    pOD->optCookie = VOIDP(~0UL);
284
0
    (*(pOD->pOptProc))(OPTPROC_RETURN_VALNAME, pOD);
285
286
0
    pz = pOD->optArg.argString;
287
0
    while (*pz != NUL) {
288
0
        printf("readonly %s_", pOD->pz_NAME);
289
0
        pz = SPN_PLUS_N_SPACE_CHARS(pz);
290
291
0
        for (;;) {
292
0
            int ch = *(pz++);
293
0
            if (IS_LOWER_CASE_CHAR(ch))   fputc(toupper(ch), stdout);
294
0
            else if (IS_UPPER_CASE_CHAR(ch))   fputc(ch, stdout);
295
0
            else if (IS_PLUS_N_SPACE_CHAR(ch)) goto name_done;
296
0
            else if (ch == NUL)        { pz--; goto name_done; }
297
0
            else fputc('_', stdout);
298
0
        } name_done:;
299
0
        printf(SHOW_VAL_FMT, (unsigned long)val);
300
0
        val <<= 1;
301
0
    }
302
303
0
    AGFREE(pOD->optArg.argString);
304
0
    pOD->optArg.argString = svstr;
305
0
}
306
307
static void
308
print_stacked_arg(tOptions * pOpts, tOptDesc * pOD)
309
0
{
310
0
    tArgList *      pAL = (tArgList *)pOD->optCookie;
311
0
    char const **   ppz = pAL->apzArgs;
312
0
    int             ct  = pAL->useCt;
313
314
0
    printf(zOptCookieCt, pOpts->pzPROGNAME, pOD->pz_NAME, ct);
315
316
0
    while (--ct >= 0) {
317
0
        printf(ARG_BY_NUM_FMT, pOpts->pzPROGNAME, pOD->pz_NAME,
318
0
               pAL->useCt - ct);
319
0
        print_quot_str(*(ppz++));
320
0
        printf(EXPORT_ARG_FMT, pOpts->pzPROGNAME, pOD->pz_NAME,
321
0
               pAL->useCt - ct);
322
0
    }
323
0
}
324
325
/**
326
 * emit the arguments as readily parsed text.
327
 * The program options are set by emitting the shell "set" command.
328
 *
329
 * @param[in] opts  the program options structure
330
 */
331
static void
332
print_reordering(tOptions * opts)
333
0
{
334
0
    unsigned int ix;
335
336
0
    fputs(set_dash, stdout);
337
338
0
    for (ix = opts->curOptIdx;
339
0
         ix < opts->origArgCt;
340
0
         ix++) {
341
0
        fputc(' ', stdout);
342
0
        print_quot_str(opts->origArgVect[ ix ]);
343
0
    }
344
0
    fputs(init_optct, stdout);
345
0
}
346
347
/*=export_func  optionPutShell
348
 * what:  write a portable shell script to parse options
349
 * private:
350
 * arg:   tOptions *, pOpts, the program options descriptor
351
 * doc:   This routine will emit portable shell script text for parsing
352
 *        the options described in the option definitions.
353
=*/
354
void
355
optionPutShell(tOptions * pOpts)
356
0
{
357
0
    int  optIx = 0;
358
359
0
    printf(zOptCtFmt, pOpts->curOptIdx-1);
360
361
0
    do  {
362
0
        tOptDesc * pOD = pOpts->pOptDesc + optIx;
363
364
0
        if ((pOD->fOptState & OPTST_NO_OUTPUT_MASK) != 0)
365
0
            continue;
366
367
        /*
368
         *  Equivalence classes are hard to deal with.  Where the
369
         *  option data wind up kind of squishes around.  For the purposes
370
         *  of emitting shell state, they are not recommended, but we'll
371
         *  do something.  I guess we'll emit the equivalenced-to option
372
         *  at the point in time when the base option is found.
373
         */
374
0
        if (pOD->optEquivIndex != NO_EQUIVALENT)
375
0
            continue; /* equivalence to a different option */
376
377
        /*
378
         *  Equivalenced to a different option.  Process the current option
379
         *  as the equivalenced-to option.  Keep the persistent state bits,
380
         *  but copy over the set-state bits.
381
         */
382
0
        if (pOD->optActualIndex != optIx) {
383
0
            tOptDesc * p  = pOpts->pOptDesc + pOD->optActualIndex;
384
0
            p->optArg     = pOD->optArg;
385
0
            p->fOptState &= OPTST_PERSISTENT_MASK;
386
0
            p->fOptState |= pOD->fOptState & ~OPTST_PERSISTENT_MASK;
387
0
            printf(zEquivMode, pOpts->pzPROGNAME, pOD->pz_NAME, p->pz_NAME);
388
0
            pOD = p;
389
0
        }
390
391
        /*
392
         *  If the argument type is a set membership bitmask, then we always
393
         *  emit the thing.  We do this because it will always have some sort
394
         *  of bitmask value and we need to emit the bit values.
395
         */
396
0
        if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_MEMBERSHIP) {
397
0
            print_membership(pOpts, pOD);
398
0
            continue;
399
0
        }
400
401
        /*
402
         *  IF the option was either specified or it wakes up enabled,
403
         *  then we will emit information.  Otherwise, skip it.
404
         *  The idea is that if someone defines an option to initialize
405
         *  enabled, we should tell our shell script that it is enabled.
406
         */
407
0
        if (UNUSED_OPT(pOD) && DISABLED_OPT(pOD))
408
0
            continue;
409
410
        /*
411
         *  Handle stacked arguments
412
         */
413
0
        if (  (pOD->fOptState & OPTST_STACKED)
414
0
           && (pOD->optCookie != NULL) )  {
415
0
            print_stacked_arg(pOpts, pOD);
416
0
            continue;
417
0
        }
418
419
        /*
420
         *  If the argument has been disabled,
421
         *  Then set its value to the disablement string
422
         */
423
0
        if ((pOD->fOptState & OPTST_DISABLED) != 0) {
424
0
            printf(zOptDisabl, pOpts->pzPROGNAME, pOD->pz_NAME,
425
0
                   (pOD->pz_DisablePfx != NULL)
426
0
                   ? pOD->pz_DisablePfx : "false");
427
0
            continue;
428
0
        }
429
430
        /*
431
         *  If the argument type is numeric, the last arg pointer
432
         *  is really the VALUE of the string that was pointed to.
433
         */
434
0
        if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_NUMERIC) {
435
0
            printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME,
436
0
                   (int)pOD->optArg.argInt);
437
0
            continue;
438
0
        }
439
440
        /*
441
         *  If the argument type is an enumeration, then it is much
442
         *  like a text value, except we call the callback function
443
         *  to emit the value corresponding to the "optArg" number.
444
         */
445
0
        if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_ENUMERATION) {
446
0
            print_enumeration(pOpts, pOD);
447
0
            continue;
448
0
        }
449
450
        /*
451
         *  If the argument type is numeric, the last arg pointer
452
         *  is really the VALUE of the string that was pointed to.
453
         */
454
0
        if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_BOOLEAN) {
455
0
            printf(zFullOptFmt, pOpts->pzPROGNAME, pOD->pz_NAME,
456
0
                   (pOD->optArg.argBool == 0) ? "false" : "true");
457
0
            continue;
458
0
        }
459
460
        /*
461
         *  IF the option has an empty value,
462
         *  THEN we set the argument to the occurrence count.
463
         */
464
0
        if (  (pOD->optArg.argString == NULL)
465
0
           || (pOD->optArg.argString[0] == NUL) ) {
466
467
0
            printf(zOptNumFmt, pOpts->pzPROGNAME, pOD->pz_NAME,
468
0
                   pOD->optOccCt);
469
0
            continue;
470
0
        }
471
472
        /*
473
         *  This option has a text value
474
         */
475
0
        printf(OPT_VAL_FMT, pOpts->pzPROGNAME, pOD->pz_NAME);
476
0
        print_quot_str(pOD->optArg.argString);
477
0
        printf(OPT_END_FMT, pOpts->pzPROGNAME, pOD->pz_NAME);
478
479
0
    } while (++optIx < pOpts->presetOptCt );
480
481
0
    if (  ((pOpts->fOptSet & OPTPROC_REORDER) != 0)
482
0
       && (pOpts->curOptIdx < pOpts->origArgCt))
483
0
        print_reordering(pOpts);
484
485
    fflush(stdout);
486
0
}
487
488
/** @}
489
 *
490
 * Local Variables:
491
 * mode: C
492
 * c-file-style: "stroustrup"
493
 * indent-tabs-mode: nil
494
 * End:
495
 * end of autoopts/putshell.c */