Coverage Report

Created: 2023-05-19 06:16

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