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/makeshell.c
Line
Count
Source
1
2
/**
3
 * \file makeshell.c
4
 *
5
 *  This module will interpret the options set in the tOptions
6
 *  structure and create a Bourne shell script capable of parsing them.
7
 *
8
 * @addtogroup autoopts
9
 * @{
10
 */
11
/*
12
 *  This file is part of AutoOpts, a companion to AutoGen.
13
 *  AutoOpts is free software.
14
 *  AutoOpts is Copyright (C) 1992-2018 by Bruce Korb - all rights reserved
15
 *
16
 *  AutoOpts is available under any one of two licenses.  The license
17
 *  in use must be one of these two and the choice is under the control
18
 *  of the user of the license.
19
 *
20
 *   The GNU Lesser General Public License, version 3 or later
21
 *      See the files "COPYING.lgplv3" and "COPYING.gplv3"
22
 *
23
 *   The Modified Berkeley Software Distribution License
24
 *      See the file "COPYING.mbsd"
25
 *
26
 *  These files have the following sha256 sums:
27
 *
28
 *  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
29
 *  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
30
 *  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
31
 */
32
33
0
 static inline unsigned char to_uchar (char ch) { return ch; }
34
35
0
#define UPPER(_c) (toupper(to_uchar(_c)))
36
0
#define LOWER(_c) (tolower(to_uchar(_c)))
37
38
lo_noreturn static void
39
option_exits(int exit_code)
40
0
{
41
0
    if (print_exit)
42
0
        printf("\nexit %d\n", exit_code);
43
0
    exit(exit_code);
44
0
}
45
46
lo_noreturn static void
47
ao_bug(char const * msg)
48
0
{
49
0
    fprintf(stderr, zao_bug_msg, msg);
50
0
    option_exits(EX_SOFTWARE);
51
0
}
52
53
static void
54
fserr_warn(char const * prog, char const * op, char const * fname)
55
0
{
56
0
    fprintf(stderr, zfserr_fmt, prog, errno, strerror(errno),
57
0
            op, fname);
58
0
}
59
60
lo_noreturn static void
61
fserr_exit(char const * prog, char const * op, char const * fname)
62
0
{
63
0
    fserr_warn(prog, op, fname);
64
0
    option_exits(EXIT_FAILURE);
65
0
}
66
67
/*=export_func  optionParseShell
68
 * private:
69
 *
70
 * what:  Decipher a boolean value
71
 * arg:   + tOptions * + pOpts    + program options descriptor +
72
 *
73
 * doc:
74
 *  Emit a shell script that will parse the command line options.
75
=*/
76
void
77
optionParseShell(tOptions * opts)
78
0
{
79
    /*
80
     *  Check for our SHELL option now.
81
     *  IF the output file contains the "#!" magic marker,
82
     *  it will override anything we do here.
83
     */
84
0
    if (HAVE_GENSHELL_OPT(SHELL))
85
0
        shell_prog = GENSHELL_OPT_ARG(SHELL);
86
87
0
    else if (! ENABLED_GENSHELL_OPT(SHELL))
88
0
        shell_prog = NULL;
89
90
0
    else if ((shell_prog = getenv("SHELL")),
91
0
             shell_prog == NULL)
92
93
0
        shell_prog = POSIX_SHELL;
94
95
    /*
96
     *  Check for a specified output file
97
     */
98
0
    if (HAVE_GENSHELL_OPT(SCRIPT))
99
0
        open_out(GENSHELL_OPT_ARG(SCRIPT), opts->pzProgName);
100
    
101
0
    emit_usage(opts);
102
0
    emit_setup(opts);
103
104
    /*
105
     *  There are four modes of option processing.
106
     */
107
0
    switch (opts->fOptSet & (OPTPROC_LONGOPT|OPTPROC_SHORTOPT)) {
108
0
    case OPTPROC_LONGOPT:
109
0
        fputs(LOOP_STR,         stdout);
110
111
0
        fputs(LONG_OPT_MARK,    stdout);
112
0
        fputs(INIT_LOPT_STR,    stdout);
113
0
        emit_long(opts);
114
0
        printf(LOPT_ARG_FMT,    opts->pzPROGNAME);
115
0
        fputs(END_OPT_SEL_STR,  stdout);
116
117
0
        fputs(NOT_FOUND_STR,    stdout);
118
0
        break;
119
120
0
    case 0:
121
0
        fputs(ONLY_OPTS_LOOP,   stdout);
122
0
        fputs(INIT_LOPT_STR,    stdout);
123
0
        emit_long(opts);
124
0
        printf(LOPT_ARG_FMT,    opts->pzPROGNAME);
125
0
        break;
126
127
0
    case OPTPROC_SHORTOPT:
128
0
        fputs(LOOP_STR,         stdout);
129
130
0
        fputs(FLAG_OPT_MARK,    stdout);
131
0
        fputs(INIT_OPT_STR,     stdout);
132
0
        emit_flag(opts);
133
0
        printf(OPT_ARG_FMT,     opts->pzPROGNAME);
134
0
        fputs(END_OPT_SEL_STR,  stdout);
135
136
0
        fputs(NOT_FOUND_STR,    stdout);
137
0
        break;
138
139
0
    case OPTPROC_LONGOPT|OPTPROC_SHORTOPT:
140
0
        fputs(LOOP_STR,         stdout);
141
142
0
        fputs(LONG_OPT_MARK,    stdout);
143
0
        fputs(INIT_LOPT_STR,    stdout);
144
0
        emit_long(opts);
145
0
        printf(LOPT_ARG_FMT,    opts->pzPROGNAME);
146
0
        fputs(END_OPT_SEL_STR,  stdout);
147
148
0
        fputs(FLAG_OPT_MARK,    stdout);
149
0
        fputs(INIT_OPT_STR,     stdout);
150
0
        emit_flag(opts);
151
0
        printf(OPT_ARG_FMT,     opts->pzPROGNAME);
152
0
        fputs(END_OPT_SEL_STR,  stdout);
153
154
0
        fputs(NOT_FOUND_STR,    stdout);
155
0
        break;
156
0
    }
157
158
0
    emit_wrapup(opts);
159
0
    if ((script_trailer != NULL) && (*script_trailer != NUL))
160
0
        fputs(script_trailer, stdout);
161
0
    else if (ENABLED_GENSHELL_OPT(SHELL))
162
0
        printf(SHOW_PROG_ENV, opts->pzPROGNAME);
163
164
0
#ifdef HAVE_FCHMOD
165
0
    fchmod(STDOUT_FILENO, 0755);
166
0
#endif
167
0
    fclose(stdout);
168
169
0
    if (ferror(stdout))
170
0
        fserr_exit(opts->pzProgName, zwriting, zstdout_name);
171
172
0
    AGFREE(script_text);
173
0
    script_leader    = NULL;
174
0
    script_trailer   = NULL;
175
0
    script_text      = NULL;
176
0
}
177
178
#ifdef HAVE_WORKING_FORK
179
/**
180
 * Print the value of "var" to a file descriptor.
181
 * The "fdin" is the read end of a pipe to a forked process that
182
 * is writing usage text to it.  We read that text in and re-emit
183
 * to standard out, formatting it so that it is assigned to a
184
 * shell variable.
185
 *
186
 * @param[in] prog  The capitalized, c-variable-formatted program name
187
 * @param[in] var   a similarly formatted type name
188
 *                  (LONGUSAGE, USAGE or VERSION)
189
 * @param[in] fdin  the input end of a pipe
190
 */
191
static void
192
emit_var_text(char const * prog, char const * var, int fdin)
193
0
{
194
0
    FILE * fp   = fdopen(fdin, "r" FOPEN_BINARY_FLAG);
195
0
    int    nlct = 0; /* defer newlines and skip trailing ones */
196
197
0
    printf(SET_TEXT_FMT, prog, var);
198
0
    if (fp == NULL)
199
0
        goto skip_text;
200
201
0
    for (;;) {
202
0
        int  ch = fgetc(fp);
203
0
        switch (ch) {
204
205
0
        case NL:
206
0
            nlct++;
207
0
            break;
208
209
0
        case '\'':
210
0
            while (nlct > 0) {
211
0
                fputc(NL, stdout);
212
0
                nlct--;
213
0
            }
214
0
            fputs(apostrophe, stdout);
215
0
            break;
216
217
0
        case EOF:
218
0
            goto done;
219
220
0
        default:
221
0
            while (nlct > 0) {
222
0
                fputc(NL, stdout);
223
0
                nlct--;
224
0
            }
225
0
            fputc(ch, stdout);
226
0
            break;
227
0
        }
228
0
    } done:;
229
230
0
    fclose(fp);
231
232
0
 skip_text:
233
234
0
    fputs(END_SET_TEXT, stdout);
235
0
}
236
#endif
237
238
/**
239
 *  The purpose of this function is to assign "long usage", short usage
240
 *  and version information to a shell variable.  Rather than wind our
241
 *  way through all the logic necessary to emit the text directly, we
242
 *  fork(), have our child process emit the text the normal way and
243
 *  capture the output in the parent process.
244
 *
245
 * @param[in] opts  the program options
246
 * @param[in] which what to print: long usage, usage or version
247
 * @param[in] od    for TT_VERSION, it is the version option
248
 */
249
static void
250
text_to_var(tOptions * opts, teTextTo which, tOptDesc * od)
251
0
{
252
0
#   define _TT_(n) static char const z ## n [] = #n;
253
0
    TEXTTO_TABLE
254
0
#   undef _TT_
255
0
#   define _TT_(n) z ## n ,
256
0
      static char const * ttnames[] = { TEXTTO_TABLE };
257
0
#   undef _TT_
258
259
#if ! defined(HAVE_WORKING_FORK)
260
    printf(SET_NO_TEXT_FMT, opts->pzPROGNAME, ttnames[which]);
261
#else
262
0
    int  fdpair[2];
263
264
0
    fflush(stdout);
265
0
    fflush(stderr);
266
267
0
    if (pipe(fdpair) != 0)
268
0
        fserr_exit(opts->pzProgName, "pipe", zinter_proc_pipe);
269
270
0
    switch (fork()) {
271
0
    case -1:
272
0
        fserr_exit(opts->pzProgName, "fork", opts->pzProgName);
273
        /* NOTREACHED */
274
275
0
    case 0:
276
        /*
277
         * Send both stderr and stdout to the pipe.  No matter which
278
         * descriptor is used, we capture the output on the read end.
279
         */
280
0
        dup2(fdpair[1], STDERR_FILENO);
281
0
        dup2(fdpair[1], STDOUT_FILENO);
282
0
        close(fdpair[0]);
283
284
0
        switch (which) {
285
0
        case TT_LONGUSAGE:
286
0
            (*(opts->pUsageProc))(opts, EXIT_SUCCESS);
287
            /* FALLTHROUGH */ /* NOTREACHED */
288
289
0
        case TT_USAGE:
290
0
            (*(opts->pUsageProc))(opts, EXIT_FAILURE);
291
            /* FALLTHROUGH */ /* NOTREACHED */
292
293
0
        case TT_VERSION:
294
0
            if (od->fOptState & OPTST_ALLOC_ARG) {
295
0
                AGFREE(od->optArg.argString);
296
0
                od->fOptState &= ~OPTST_ALLOC_ARG;
297
0
            }
298
0
            od->optArg.argString = "c";
299
0
            optionPrintVersion(opts, od);
300
            /* FALLTHROUGH */ /* NOTREACHED */
301
302
0
        default:
303
0
            option_exits(EXIT_FAILURE);
304
            /* FALLTHROUGH */ /* NOTREACHED */
305
0
        }
306
        /* FALLTHROUGH */ /* NOTREACHED */
307
308
0
    default:
309
0
        close(fdpair[1]);
310
0
    }
311
312
0
    emit_var_text(opts->pzPROGNAME, ttnames[which], fdpair[0]);
313
0
#endif
314
0
}
315
316
/**
317
 * capture usage text in shell variables.
318
 * 
319
 */
320
static void
321
emit_usage(tOptions * opts)
322
0
{
323
0
    char tm_nm_buf[AO_NAME_SIZE];
324
325
    /*
326
     *  First, switch stdout to the output file name.
327
     *  Then, change the program name to the one defined
328
     *  by the definitions (rather than the current
329
     *  executable name).  Down case the upper cased name.
330
     */
331
0
    if (script_leader != NULL)
332
0
        fputs(script_leader, stdout);
333
334
0
    {
335
0
        char const * out_nm;
336
337
0
        {
338
0
            time_t    c_tim = time(NULL);
339
0
            struct tm * ptm = localtime(&c_tim);
340
0
            strftime(tm_nm_buf, AO_NAME_SIZE, TIME_FMT, ptm );
341
0
        }
342
343
0
        if (HAVE_GENSHELL_OPT(SCRIPT))
344
0
             out_nm = GENSHELL_OPT_ARG(SCRIPT);
345
0
        else out_nm = STDOUT;
346
347
0
        if ((script_leader == NULL) && (shell_prog != NULL))
348
0
            printf(SHELL_MAGIC, shell_prog);
349
350
0
        printf(PREAMBLE_FMT, START_MARK, out_nm, tm_nm_buf);
351
0
    }
352
353
0
    printf(END_PRE_FMT, opts->pzPROGNAME);
354
355
    /*
356
     *  Get a copy of the original program name in lower case and
357
     *  fill in an approximation of the program name from it.
358
     */
359
0
    {
360
0
        char *       pzPN = tm_nm_buf;
361
0
        char const * pz   = opts->pzPROGNAME;
362
0
        char **      pp;
363
364
        /* Copy the program name into the time/name buffer */
365
0
        for (;;) {
366
0
            if ((*pzPN++ = (char)tolower(*pz++)) == NUL)
367
0
                break;
368
0
        }
369
370
0
        pp  = VOIDP(&(opts->pzProgPath));
371
0
        *pp = tm_nm_buf;
372
0
        pp  = VOIDP(&(opts->pzProgName));
373
0
        *pp = tm_nm_buf;
374
0
    }
375
376
0
    text_to_var(opts, TT_LONGUSAGE, NULL);
377
0
    text_to_var(opts, TT_USAGE,     NULL);
378
379
0
    {
380
0
        tOptDesc * pOptDesc = opts->pOptDesc;
381
0
        int        optionCt = opts->optCt;
382
383
0
        for (;;) {
384
0
            if (pOptDesc->pOptProc == optionPrintVersion) {
385
0
                text_to_var(opts, TT_VERSION, pOptDesc);
386
0
                break;
387
0
            }
388
389
0
            if (--optionCt <= 0)
390
0
                break;
391
0
            pOptDesc++;
392
0
        }
393
0
    }
394
0
}
395
396
static void
397
emit_wrapup(tOptions * opts)
398
0
{
399
0
    tOptDesc *   od     = opts->pOptDesc;
400
0
    int          opt_ct = opts->presetOptCt;
401
0
    char const * fmt;
402
403
0
    printf(FINISH_LOOP, opts->pzPROGNAME);
404
0
    for (;opt_ct > 0; od++, --opt_ct) {
405
        /*
406
         *  Options that are either usage documentation or are compiled out
407
         *  are not to be processed.
408
         */
409
0
        if (SKIP_OPT(od) || (od->pz_NAME == NULL))
410
0
            continue;
411
412
        /*
413
         *  do not presence check if there is no minimum/must-set
414
         */
415
0
        if ((od->optMinCt == 0) && ((od->fOptState & OPTST_MUST_SET) == 0))
416
0
            continue;
417
418
0
        if (od->optMaxCt > 1)
419
0
             fmt = CHK_MIN_COUNT;
420
0
        else fmt = CHK_ONE_REQUIRED;
421
422
0
        {
423
0
            int min = (od->optMinCt == 0) ? 1 : od->optMinCt;
424
0
            printf(fmt, opts->pzPROGNAME, od->pz_NAME, min);
425
0
        }
426
0
    }
427
0
    fputs(END_MARK, stdout);
428
0
}
429
430
static void
431
emit_setup(tOptions * opts)
432
0
{
433
0
    tOptDesc *   od     = opts->pOptDesc;
434
0
    int          opt_ct = opts->presetOptCt;
435
0
    char const * fmt;
436
0
    char const * def_val;
437
438
0
    for (;opt_ct > 0; od++, --opt_ct) {
439
0
        char int_val_buf[32];
440
441
        /*
442
         *  Options that are either usage documentation or are compiled out
443
         *  are not to be processed.
444
         */
445
0
        if (SKIP_OPT(od) || (od->pz_NAME == NULL))
446
0
            continue;
447
448
0
        if (od->optMaxCt > 1)
449
0
             fmt = MULTI_DEF_FMT;
450
0
        else fmt = SGL_DEF_FMT;
451
452
        /*
453
         *  IF this is an enumeration/bitmask option, then convert the value
454
         *  to a string before printing the default value.
455
         */
456
0
        switch (OPTST_GET_ARGTYPE(od->fOptState)) {
457
0
        case OPARG_TYPE_ENUMERATION:
458
0
            (*(od->pOptProc))(OPTPROC_EMIT_SHELL, od );
459
0
            def_val = od->optArg.argString;
460
0
            break;
461
462
        /*
463
         *  Numeric and membership bit options are just printed as a number.
464
         */
465
0
        case OPARG_TYPE_NUMERIC:
466
0
            snprintf(int_val_buf, sizeof(int_val_buf), "%d",
467
0
                     (int)od->optArg.argInt);
468
0
            def_val = int_val_buf;
469
0
            break;
470
471
0
        case OPARG_TYPE_MEMBERSHIP:
472
0
            snprintf(int_val_buf, sizeof(int_val_buf), "%lu",
473
0
                     (unsigned long)od->optArg.argIntptr);
474
0
            def_val = int_val_buf;
475
0
            break;
476
477
0
        case OPARG_TYPE_BOOLEAN:
478
0
            def_val = (od->optArg.argBool) ? TRUE_STR : FALSE_STR;
479
0
            break;
480
481
0
        default:
482
0
            if (od->optArg.argString == NULL) {
483
0
                if (fmt == SGL_DEF_FMT)
484
0
                    fmt = SGL_NO_DEF_FMT;
485
0
                def_val = NULL;
486
0
            }
487
0
            else
488
0
                def_val = od->optArg.argString;
489
0
        }
490
491
0
        printf(fmt, opts->pzPROGNAME, od->pz_NAME, def_val);
492
0
    }
493
0
}
494
495
static void
496
emit_action(tOptions * opts, tOptDesc * od)
497
0
{
498
0
    if (od->pOptProc == optionPrintVersion)
499
0
        printf(ECHO_N_EXIT, opts->pzPROGNAME, VER_STR);
500
501
0
    else if (od->pOptProc == optionPagedUsage)
502
0
        printf(PAGE_USAGE_TEXT, opts->pzPROGNAME);
503
504
0
    else if (od->pOptProc == optionLoadOpt) {
505
0
        printf(LVL3_CMD, NO_LOAD_WARN);
506
0
        printf(LVL3_CMD, YES_NEED_OPT_ARG);
507
508
0
    } else if (od->pz_NAME == NULL) {
509
510
0
        if (od->pOptProc == NULL) {
511
0
            printf(LVL3_CMD, NO_SAVE_OPTS);
512
0
            printf(LVL3_CMD, OK_NEED_OPT_ARG);
513
0
        } else
514
0
            printf(ECHO_N_EXIT, opts->pzPROGNAME, LONG_USE_STR);
515
516
0
    } else {
517
0
        if (od->optMaxCt == 1)
518
0
            printf(SGL_ARG_FMT, opts->pzPROGNAME, od->pz_NAME);
519
0
        else {
520
0
            if ((unsigned)od->optMaxCt < NOLIMIT)
521
0
                printf(CHK_MAX_COUNT, opts->pzPROGNAME,
522
0
                       od->pz_NAME, od->optMaxCt);
523
524
0
            printf(MULTI_ARG_FMT, opts->pzPROGNAME, od->pz_NAME);
525
0
        }
526
527
        /*
528
         *  Fix up the args.
529
         */
530
0
        if (OPTST_GET_ARGTYPE(od->fOptState) == OPARG_TYPE_NONE) {
531
0
            printf(SET_MULTI_ARG, opts->pzPROGNAME, od->pz_NAME);
532
0
            printf(LVL3_CMD, NO_ARG_NEEDED);
533
534
0
        } else if (od->fOptState & OPTST_ARG_OPTIONAL) {
535
0
            printf(SET_MULTI_ARG,  opts->pzPROGNAME, od->pz_NAME);
536
0
            printf(LVL3_CMD, OK_NEED_OPT_ARG);
537
538
0
        } else {
539
0
            printf(LVL3_CMD, YES_NEED_OPT_ARG);
540
0
        }
541
0
    }
542
0
    fputs(zOptionEndSelect, stdout);
543
0
}
544
545
static void
546
emit_inaction(tOptions * opts, tOptDesc * od)
547
0
{
548
0
    if (od->pOptProc == optionLoadOpt) {
549
0
        printf(LVL3_CMD, NO_SUPPRESS_LOAD);
550
551
0
    } else if (od->optMaxCt == 1)
552
0
        printf(NO_SGL_ARG_FMT, opts->pzPROGNAME,
553
0
               od->pz_NAME, od->pz_DisablePfx);
554
0
    else
555
0
        printf(NO_MULTI_ARG_FMT, opts->pzPROGNAME,
556
0
               od->pz_NAME, od->pz_DisablePfx);
557
558
0
    printf(LVL3_CMD, NO_ARG_NEEDED);
559
0
    fputs(zOptionEndSelect, stdout);
560
0
}
561
562
/**
563
 * recognize flag options.  These go at the end.
564
 * At the end, emit code to handle options we don't recognize.
565
 *
566
 * @param[in] opts  the program options
567
 */
568
static void
569
emit_flag(tOptions * opts)
570
0
{
571
0
    tOptDesc * od = opts->pOptDesc;
572
0
    int        opt_ct = opts->optCt;
573
574
0
    fputs(zOptionCase, stdout);
575
576
0
    for (;opt_ct > 0; od++, --opt_ct) {
577
578
0
        if (SKIP_OPT(od) || ! IS_GRAPHIC_CHAR(od->optValue))
579
0
            continue;
580
581
0
        printf(zOptionFlag, od->optValue);
582
0
        emit_action(opts, od);
583
0
    }
584
0
    printf(UNK_OPT_FMT, FLAG_STR, opts->pzPROGNAME);
585
0
}
586
587
/**
588
 *  Emit the match text for a long option.  The passed in \a name may be
589
 *  either the enablement name or the disablement name.
590
 *
591
 * @param[in] name  The current name to check.
592
 * @param[in] cod   current option descriptor
593
 * @param[in] opts  the program options
594
 */
595
static void
596
emit_match_expr(char const * name, tOptDesc * cod, tOptions * opts)
597
0
{
598
0
    char name_bf[32];
599
0
    unsigned int    min_match_ct = 2;
600
0
    unsigned int    max_match_ct = strlen(name) - 1;
601
602
0
    if (max_match_ct >= sizeof(name_bf) - 1)
603
0
        goto leave;
604
    
605
0
    {
606
0
        tOptDesc *  od = opts->pOptDesc;
607
0
        int         ct = opts->optCt;
608
609
0
        for (; ct-- > 0; od++) {
610
0
            unsigned int match_ct = 0;
611
612
            /*
613
             *  Omit the current option, Doc opts and compiled out opts.
614
             */
615
0
            if ((od == cod) || SKIP_OPT(od))
616
0
                continue;
617
618
            /*
619
             *  Check each character of the name case insensitively.
620
             *  They must not be the same.  They cannot be, because it would
621
             *  not compile correctly if they were.
622
             */
623
0
            while (UPPER(od->pz_Name[match_ct]) == UPPER(name[match_ct]))
624
0
                match_ct++;
625
626
0
            if (match_ct > min_match_ct)
627
0
                min_match_ct = match_ct;
628
629
            /*
630
             *  Check the disablement name, too.
631
             */
632
0
            if (od->pz_DisableName == NULL)
633
0
                continue;
634
635
0
            match_ct = 0;
636
0
            while (  toupper(od->pz_DisableName[match_ct])
637
0
                  == toupper(name[match_ct]))
638
0
                match_ct++;
639
0
            if (match_ct > min_match_ct)
640
0
                min_match_ct = match_ct;
641
0
        }
642
0
    }
643
644
    /*
645
     *  Don't bother emitting partial matches if there is only one possible
646
     *  partial match.
647
     */
648
0
    if (min_match_ct < max_match_ct) {
649
0
        char *  pz    = name_bf + min_match_ct;
650
0
        int     nm_ix = min_match_ct;
651
652
0
        memcpy(name_bf, name, min_match_ct);
653
654
0
        for (;;) {
655
0
            *pz = NUL;
656
0
            printf(zOptionPartName, name_bf);
657
0
            *pz++ = name[nm_ix++];
658
0
            if (name[nm_ix] == NUL) {
659
0
                *pz = NUL;
660
0
                break;
661
0
            }
662
0
        }
663
0
    }
664
665
0
leave:
666
0
    printf(zOptionFullName, name);
667
0
}
668
669
/**
670
 *  Emit GNU-standard long option handling code.
671
 *
672
 * @param[in] opts  the program options
673
 */
674
static void
675
emit_long(tOptions * opts)
676
0
{
677
0
    tOptDesc * od = opts->pOptDesc;
678
0
    int        ct  = opts->optCt;
679
680
0
    fputs(zOptionCase, stdout);
681
682
    /*
683
     *  do each option, ...
684
     */
685
0
    do  {
686
        /*
687
         *  Documentation & compiled-out options
688
         */
689
0
        if (SKIP_OPT(od))
690
0
            continue;
691
692
0
        emit_match_expr(od->pz_Name, od, opts);
693
0
        emit_action(opts, od);
694
695
        /*
696
         *  Now, do the same thing for the disablement version of the option.
697
         */
698
0
        if (od->pz_DisableName != NULL) {
699
0
            emit_match_expr(od->pz_DisableName, od, opts);
700
0
            emit_inaction(opts, od);
701
0
        }
702
0
    } while (od++, --ct > 0);
703
704
0
    printf(UNK_OPT_FMT, OPTION_STR, opts->pzPROGNAME);
705
0
}
706
707
/**
708
 * Load the previous shell script output file.  We need to preserve any
709
 * hand-edited additions outside of the START_MARK and END_MARKs.
710
 *
711
 * @param[in] fname  the output file name
712
 */
713
static char *
714
load_old_output(char const * fname, char const * pname)
715
0
{
716
    /*
717
     *  IF we cannot stat the file,
718
     *  THEN assume we are creating a new file.
719
     *       Skip the loading of the old data.
720
     */
721
0
    FILE * fp = fopen(fname, "r" FOPEN_BINARY_FLAG);
722
0
    struct stat stbf;
723
0
    char * text;
724
0
    char * scan;
725
726
0
    if (fp == NULL)
727
0
        return NULL;
728
729
    /*
730
     * If we opened it, we should be able to stat it and it needs
731
     * to be a regular file
732
     */
733
0
    if ((fstat(fileno(fp), &stbf) != 0) || (! S_ISREG(stbf.st_mode)))
734
0
        fserr_exit(pname, "fstat", fname);
735
736
0
    scan = text = AGALOC(stbf.st_size + 1, "f data");
737
738
    /*
739
     *  Read in all the data as fast as our OS will let us.
740
     */
741
0
    for (;;) {
742
0
        size_t inct = fread(VOIDP(scan), 1, (size_t)stbf.st_size, fp);
743
0
        if (inct == 0)
744
0
            break;
745
746
0
        stbf.st_size -= (ssize_t)inct;
747
748
0
        if (stbf.st_size == 0)
749
0
            break;
750
751
0
        scan += inct;
752
0
    }
753
754
0
    *scan = NUL;
755
0
    fclose(fp);
756
757
0
    return text;
758
0
}
759
760
/**
761
 * Open the specified output file.  If it already exists, load its
762
 * contents and save the non-generated (hand edited) portions.
763
 * If a "start mark" is found, everything before it is preserved leader.
764
 * If not, the entire thing is a trailer.  Assuming the start is found,
765
 * then everything after the end marker is the trailer.  If the end
766
 * mark is not found, the file is actually corrupt, but we take the
767
 * remainder to be the trailer.
768
 *
769
 * @param[in] fname  the output file name
770
 */
771
static void
772
open_out(char const * fname, char const * pname)
773
0
{
774
775
0
    do  {
776
0
        char * txt = script_text = load_old_output(fname, pname);
777
0
        char * scn;
778
779
0
        if (txt == NULL)
780
0
            break;
781
782
0
        scn = strstr(txt, START_MARK);
783
0
        if (scn == NULL) {
784
0
            script_trailer = txt;
785
0
            break;
786
0
        }
787
788
0
        *(scn++) = NUL;
789
0
        scn = strstr(scn, END_MARK);
790
0
        if (scn == NULL) {
791
            /*
792
             * The file is corrupt.  Set the trailer to be everything
793
             * after the start mark. The user will need to fix it up.
794
             */
795
0
            script_trailer = txt + strlen(txt) + START_MARK_LEN + 1;
796
0
            break;
797
0
        }
798
799
        /*
800
         *  Check to see if the data contains our marker.
801
         *  If it does, then we will skip over it
802
         */
803
0
        script_trailer = scn + END_MARK_LEN;
804
0
        script_leader  = txt;
805
0
    } while (false);
806
807
0
    if (freopen(fname, "w" FOPEN_BINARY_FLAG, stdout) != stdout)
808
0
        fserr_exit(pname, "freopen", fname);
809
0
}
810
811
/*=export_func genshelloptUsage
812
 * private:
813
 * what: The usage function for the genshellopt generated program
814
 *
815
 * arg:  + tOptions * + opts    + program options descriptor +
816
 * arg:  + int        + exit_cd + usage text type to produce +
817
 *
818
 * doc:
819
 *  This function is used to create the usage strings for the option
820
 *  processing shell script code.  Two child processes are spawned
821
 *  each emitting the usage text in either the short (error exit)
822
 *  style or the long style.  The generated program will capture this
823
 *  and create shell script variables containing the two types of text.
824
=*/
825
void
826
genshelloptUsage(tOptions * opts, int exit_cd)
827
0
{
828
#if ! defined(HAVE_WORKING_FORK)
829
    optionUsage(opts, exit_cd);
830
#else
831
    /*
832
     *  IF not EXIT_SUCCESS,
833
     *  THEN emit the short form of usage.
834
     */
835
0
    if (exit_cd != EXIT_SUCCESS)
836
0
        optionUsage(opts, exit_cd);
837
0
    fflush(stderr);
838
0
    fflush(stdout);
839
0
    if (ferror(stdout) || ferror(stderr))
840
0
        option_exits(EXIT_FAILURE);
841
842
0
    option_usage_fp = stdout;
843
844
    /*
845
     *  First, print our usage
846
     */
847
0
    switch (fork()) {
848
0
    case -1:
849
0
        optionUsage(opts, EXIT_FAILURE);
850
        /* FALLTHROUGH */ /* NOTREACHED */
851
852
0
    case 0:
853
0
        pagerState = PAGER_STATE_CHILD;
854
0
        optionUsage(opts, EXIT_SUCCESS);
855
        /* FALLTHROUGH */ /* NOTREACHED */
856
857
0
    default:
858
0
    {
859
0
        int  sts;
860
0
        wait(&sts);
861
0
    }
862
0
    }
863
864
    /*
865
     *  Generate the pzProgName, since optionProcess() normally
866
     *  gets it from the command line
867
     */
868
0
    {
869
0
        char *  pz;
870
0
        char ** pp = VOIDP(&(optionParseShellOptions->pzProgName));
871
0
        AGDUPSTR(pz, optionParseShellOptions->pzPROGNAME, "prog name");
872
0
        *pp = pz;
873
0
        while (*pz != NUL) {
874
0
            *pz = (char)LOWER(*pz);
875
0
            pz++;
876
0
        }
877
0
    }
878
879
    /*
880
     *  Separate the makeshell usage from the client usage
881
     */
882
0
    fprintf(option_usage_fp, zGenshell, optionParseShellOptions->pzProgName);
883
0
    fflush(option_usage_fp);
884
885
    /*
886
     *  Now, print the client usage.
887
     */
888
0
    switch (fork()) {
889
0
    case 0:
890
0
        pagerState = PAGER_STATE_CHILD;
891
        /*FALLTHROUGH*/
892
0
    case -1:
893
0
        optionUsage(optionParseShellOptions, EXIT_FAILURE);
894
        /* FALLTHROUGH */ /* NOTREACHED */
895
896
0
    default:
897
0
    {
898
0
        int  sts;
899
0
        wait(&sts);
900
0
    }
901
0
    }
902
903
0
    fflush(stdout);
904
0
    if (ferror(stdout))
905
0
        fserr_exit(opts->pzProgName, zwriting, zstdout_name);
906
907
    option_exits(EXIT_SUCCESS);
908
0
#endif
909
0
}
910
911
/** @}
912
 *
913
 * Local Variables:
914
 * mode: C
915
 * c-file-style: "stroustrup"
916
 * indent-tabs-mode: nil
917
 * End:
918
 * end of autoopts/makeshell.c */