Coverage Report

Created: 2023-05-19 06:16

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