????

Your IP : 18.227.209.41


Current Path : C:/opt/msys64/usr/share/autogen/
Upload File :
Current File : C:/opt/msys64/usr/share/autogen/optmain.tlib

[= AutoGen5 Template -*- Mode: C -*-

##  This file is part of AutoOpts, a companion to AutoGen.
##  AutoOpts is free software.
##  AutoOpts is Copyright (C) 1992-2018 by Bruce Korb - all rights reserved
##
##  AutoOpts is available under any one of two licenses.  The license
##  in use must be one of these two and the choice is under the control
##  of the user of the license.
##
##   The GNU Lesser General Public License, version 3 or later
##      See the files "COPYING.lgplv3" and "COPYING.gplv3"
##
##   The Modified Berkeley Software Distribution License
##      See the file "COPYING.mbsd"
##
##  These files have the following sha256 sums:
##
##  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
##  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
##  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd

=][=

(out-push-new) =]
ck_flag_code() {
    addon_txt=''
    txt1=`[=(. grep-prog)=] -w pOptDesc ${tmp_dir}/flag-code`
    test -z "$txt1" && addon_txt='    (void)pOptDesc;\n'
    txt2=`[=(. grep-prog)=] -w pOptions ${tmp_dir}/flag-code`
    test -z "$txt2" && addon_txt=${addon_txt}'    (void)pOptions;\n'
    cat ${tmp_dir}/flag-code
    test -z "$addon_txt" || printf "\n$addon_txt"
    rm -f ${tmp_dir}/flag-code
}[=
(shell (out-pop #t)) =][=

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

   BUILD TEST MAIN

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=

DEFINE build-test-main          =][=

  IF (emit (tpl-file-line extract-fmt))
     (define end-test-main-guard "")
     guarded-test-main
=]
#if defined([=(set! end-test-main-guard (string-append
                    "\n#endif  /* " main-guard " END-TEST-MAIN-PROCEDURE */"))
              main-guard=]) /* TEST-MAIN-PROCEDURE: */[=

  ENDIF guarded-test-main       =][=

  IF (= (get "test-main") "optionParseShell")

=]

extern tOptions  genshelloptOptions;
extern void      optionParseShell(tOptions*);
extern tOptions* optionParseShellOptions;[=

  ELIF (not (exist? "main-text"))  =][=

    (define option-emitter-proc (get "test-main"))
    (if (<= (string-length option-emitter-proc) 3)
        (set! option-emitter-proc "optionPutShell"))
=]

extern void [= (. option-emitter-proc) =](tOptions*);[=

  ENDIF

=]

/**
 * Generated main procedure.  This will emit text that a Bourne shell can
 * process to handle its command line arguments.
 *
 * @param[in] argc argument count
 * @param[in] argv argument vector
 * @returns program exit code
 */
int
main(int argc, char ** argv)
{
    int res = [=(. succ-exit-code)=];[=

  IF (= (get "test-main") "optionParseShell") =]
    /*
     *  Stash a pointer to the options we are generating.
     *  `genshellUsage()' will use it.
     */
    optionParseShellOptions = &[=(. pname)=]Options;
    (void)optionProcess(&genshelloptOptions, argc, argv);
    optionParseShell(&[=(. pname)=]Options);[=

  ELSE                          =][=
  INVOKE emit-option-code       =][=
  INVOKE emit-main-text         =][=
  ENDIF                         =]
    return res;
}[=(. end-test-main-guard)      =][=

ENDDEF  build-test-main

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

   BUILD FOR-EACH MAIN

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=

DEFINE for-each-main            =][=

  (if (not (==* (get "argument") "[" ))
      (error "command line arguments must be optional for a 'for-each' main"))

  (if (not (exist? "handler-proc"))
      (error "'for-each' mains require a handler proc") )

  (define handler-arg-type "")
  (tpl-file-line extract-fmt)   =][=

INVOKE emit-handler-proc        =][=

IF (exist? "handler-type")      =][=
    INVOKE emit-file-dispatcher    =][=
ENDIF handler-type exists       =][=

(tpl-file-line extract-fmt)

=][=

IF (not (exist? "stdin-input"))  =][=
  INVOKE emit-trim-input         =][=
ENDIF

=]
/**
 * Generated main procedure.  This will call the [=(. handler-proc)=] procedure
 * for every operand on the command line.  If there are no operands, then stdin
 * is read for a list of file names to process.  stdin must not be a terminal.
 * It must be a pipe or a file.
 *
 * @param[in] argc argument count
 * @param[in] argv argument vector
 * @returns program exit code
 */
int
main(int argc, char ** argv)
{
    int res     = 0;
    int proc_ct = 0;
    int arg_ix  = optionProcess(&[=(. pname)=]Options, argc, argv);[=

    (if (exist? "main-init") (string-append
    "\n    " (def-file-line "main-init" extract-fmt) "\n" (get "main-init")))

    =][= (tpl-file-line extract-fmt) =]
    /*
     *  IF the input list is from the command line...
     */
    if (arg_ix < argc) {
        for (; arg_ix < argc; arg_ix++) {
            char * arg = argv[arg_ix];[=
    IF (exist? "interleaved")   =]
            if (*arg == '-') {
                RESTART_OPT(arg_ix);
                arg_ix = optionProcess(&[=(. pname)=]Options, argc, argv) - 1;
                continue;
            }[=
    ENDIF interleaved           =]
            res |= [= (. handler-proc) =](arg);
            proc_ct++;
        }[=
    IF (exist? "interleaved")   =]

        if (proc_ct == 0)
            fputs(_("[=(. prog-name)
            =] Warning:  no command operands were processed\n"), stderr);[=
    ENDIF interleaved           =]
    }[=

IF (exist? "stdin-input")       =][=
    INVOKE stdin-as-input       =][=
ELSE                            =][=
    INVOKE files-from-stdin     =][=
ENDIF                           =][=

    (if (exist? "main-fini") (string-append
    "\n    " (def-file-line "main-fini" extract-fmt) "\n" (get "main-fini")))

    =]
    return res;
}[=

ENDDEF  for-each-main

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=

DEFINE emit-file-dispatcher     =][=

(tpl-file-line extract-fmt)     =]
/**
 * validate file name and dispach callout procedure.
 * This procedure is generated by AutoOpts.
 * It will make sure that the input file name refers to a file[=

  CASE handler-type             =][=
=*  name                        =]
 * that exists.[=
=*  file                        =]
 * that exists and has been opened for [=
    CASE
    (define open-mode (shellf "echo '%s' | sed 's/.*-//'"
                (get "handler-type")))
    open-mode                   =][=
    *~~*  '[rwa]\+'             =]reading and writing[=
    *~~*  [wa]                  =]writing[=
    *~~*  r                     =]reading[=
    ESAC                        =].[=

*=*  text                       =]
 * that has been read into memory as text.[=
  ESAC                          =]
 *
 * @param fname the name of the file to process
 * @returns program exit code flag
 */
static [=

(define emit-failing-printf (not (= (get "file-fail-code") "success")))

 pname-down =]_exit_code_t
validate_fname(char const * fname)
{
    static char const * err_str = NULL;[=

  IF (*=* (get "handler-type") "text")          =]
    char*  file_text;
    size_t text_size;
    int    res;[=
  ENDIF                                         =]

    if (err_str == NULL)
        err_str = _("fs error %d (%s) %s-ing %s\n");[=

  IF (== (get "handler-type") "file-r")         =]
    if ((fname[0] == '-') && (fname[1] == '\0'))
        return [= handler-proc =](_("standard input"), stdin);[=
  ENDIF  file-r handler type                    =]
    {
        struct stat sb;
        if (stat(fname, &sb) < 0) {[=
  IF (. emit-failing-printf)                    =]
            fprintf(stderr, err_str, errno, strerror(errno), "stat",
                    fname);[=
  ENDIF                                         =]
            return [= (. file-fail-exit-code)   =];
        }[=

  IF (*=* (get "handler-type") "text")          =]

        if (! S_ISREG(sb.st_mode)) {[=
    IF (. emit-failing-printf)                  =]
            fprintf(stderr, err_str, EINVAL, strerror(EINVAL),
                    _("not regular file:"), fname);[=
    ENDIF                                       =]
            return [= (. file-fail-exit-code)   =];
        }[=

    IF (=* (get "handler-type") "some-text")    =]

        if (sb.st_size == 0) {[=
      IF (. emit-failing-printf)                =]
            fprintf(stderr, err_str, EINVAL, strerror(EINVAL),
                    _("empty file:"), fname);[=
      ENDIF                                     =]
            return [= (. file-fail-exit-code)   =];
        }[=

    ENDIF                                       =]

        text_size = sb.st_size;[=

  ENDIF                                         =]
    }[=

CASE handler-type =][=
=*  name          =][= (tpl-file-line extract-fmt) =]

    return [= handler-proc =](fname);[=

=*  file          =][= (tpl-file-line extract-fmt) =]
    {
        int res;
        FILE* fp = fopen(fname, "[=
        (shellf "echo '%s' | sed 's/.*-//'"
                (get "handler-type")) =]");
        if (fp == NULL) {
            fprintf(stderr, err_str, errno, strerror(errno), "fopen",
                    fname);
            return [= (. file-fail-exit-code) =];
        }
        res = [= handler-proc =](fname, fp);
        fclose(fp);
        return res;
    }[=

*=*  text         =][= (tpl-file-line extract-fmt) =]
    file_text = malloc(text_size + 1);
    if (file_text == NULL) {
        fprintf(stderr, _("cannot allocate %u bytes for %s file text\n"),
                (unsigned int)text_size+1, fname);
        exit([=(. nomem-exit-code)=]);
    }

    {
        char*   pz = file_text;
        size_t  sz = text_size;
        int     fd = open(fname, O_RDONLY);
        int     try_ct = 0;

        if (fd < 0) {
            fprintf(stderr, err_str, errno, strerror(errno), "open", fname);
            free(file_text);
            return [= (. file-fail-exit-code) =];
        }

        while (sz > 0) {
            ssize_t rd_ct = read(fd, pz, sz);
            /*
             *  a read count of zero is theoretically okay, but we've already
             *  checked the file size, so we shoud be reading more.
             *  For us, a count of zero is an error.
             */
            if (rd_ct <= 0) {
                /*
                 * Try retriable errors up to 10 times.  Then bomb out.
                 */
                if (  ((errno == EAGAIN) || (errno == EINTR))
                   && (++try_ct < 10)  )
                    continue;

                fprintf(stderr, err_str, errno, strerror(errno), "read", fname);
                exit([=(. file-fail-exit-code)=]);
            }
            pz += rd_ct;
            sz -= rd_ct;
        }
        close(fd);
    }

    /*
     *  Just in case it is a text file, we have an extra byte to NUL
     *  terminate the thing.
     */
    file_text[ text_size ] = '\0';
    res = [= handler-proc =](fname, file_text, text_size);[=
    IF (not (exist? "handler-frees")) =]
    free(file_text);[=
    ENDIF =]
    return res;[=
ESAC             =]
}[=
ENDDEF emit-file-dispatcher

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=

DEFINE stdin-as-input           =][=
  IF (=* (get "handler-type") "file-")  =]
    else
        /*
         * process standard input as input file
         */
        res = [= handler-proc =](_("standard input"), stdin);[=
  ELSE  =][=
    (error "'stdin-input' specified for non-file handler type") =][=
  ENDIF =][=
ENDDEF stdin-as-input

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=

DEFINE emit-handler-proc        =][=

CASE handler-type =][=
=*  name         =][= (set! handler-arg-type "char const * fname")
                      (define handler-proc "validate_fname")       =][=
=*  file         =][=
   (set! handler-arg-type "char const * fname, FILE * entry_fp")
                      (define handler-proc "validate_fname")       =][=
*=* text         =][=
   (set! handler-arg-type
         "char const * fname, char * file_text, size_t text_size")
                      (define handler-proc "validate_fname")       =][=
!E               =][= (set! handler-arg-type "char const* pz_entry")
                      (define handler-proc (get "handler-proc"))       =][=
*                =][= (error) =][=
ESAC             =]

[=

IF (set! tmp-text (string-append (get "handler-proc") "-code"))
   (exist? tmp-text)

\=]
static int
[= handler-proc =]([=(. handler-arg-type)=])
{
    int res = 0;[=
   (string-append
      (def-file-line tmp-text extract-fmt)
      (get tmp-text) ) =]
    return res;
}[=

ELSE

\=]
extern int [= handler-proc =]([=(. handler-arg-type)=]);[=

ENDIF

=][=

ENDDEF emit-handler-proc

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=

DEFINE emit-trim-input          =]
/**
 * strip (destructively) the leading and trailing white space.
 * Trailing white space is trimmed with a NUL byte.
 * The returned address is that of the first character after the
 * leading white space.  Characters are not moved.
 *
 * @param[in,out] pz_s source text pointer
 * @returns pointer to the same text buffer, but after skipping over the
 * leading white space characters.
 */
static char *
trim_input_line(char * src_str)
{
    while ((unsigned int)isspace(*src_str))
        src_str++;

    {
        char * end = src_str + strlen(src_str);
        while ((end > src_str) && isspace((unsigned int)end[-1]))
            end--;
        *end = '\0';
    }

    switch (*src_str) {
    case '\0':[=
    (define comment-char (substring (get "comment-char" "#") 0 1))
    (if (> (string-length comment-char) 0) (begin
        (if (or (== comment-char "\\") (== comment-char "'"))
            (set! comment-char (string-append "\\" comment-char)) )
        (string-append "\n    case '" comment-char "':")
    )   ) =]
        return NULL;
    default:
        return src_str;
    }
}
[=
ENDDEF emit-trim-input

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=

DEFINE files-from-stdin         =]
    /*
     *  Input list from tty input
     */
    else if (isatty(STDIN_FILENO)) {
        fputs(_("[=(. prog-name)=] ERROR: input list is a tty\n"), stderr);
        [= (. UP-prefix) =]USAGE([=(. file-fail-exit-code)=]);
        /* NOTREACHED */
    }

    /*
     *  Input list from a pipe or file or some such
     */
    else {
        long pg_size = sysconf(_SC_PAGESIZE);
        char * buf   = malloc((size_t)pg_size);
        if (buf == NULL) {
            fputs(_("[=(. prog-name)
                   =] ERROR: no memory for input list\n"), stderr);
            return [=(. nomem-exit-code)=];
        }

        for (;;) {
            char * pz = fgets(buf, (ssize_t)pg_size, stdin);
            if (pz == NULL)
                break;

            pz = trim_input_line(pz);
            if (pz == NULL)
                continue;[=
    IF (= (get "handler-type") "file-r")          =]
            if ((pz[0] == '-') && (pz[1] == '\0'))
                continue; /* disallowed when reading operands from stdin */[=
    ENDIF  file-r handler type                    =][=
    IF (exist? "interleaved")   =]
            if (*pz == '-') {
                optionLoadLine(&[=(. pname)=]Options, pz);
                continue;
            }[=
    ENDIF interleaved           =]
            res |= [= (. handler-proc) =](pz);
            proc_ct++;
        }

        if (proc_ct == 0)
            fputs(_("[=(. prog-name)
                   =] Warning:  no input lines were read\n"), stderr);
        free(buf);
    }
[=

ENDDEF files-from-stdin

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

   BUILD MAIN

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=

DEFINE build-main               =][= FOR main[] =][=

 CASE main-type                 =][=
  == shell-process              =][=
     INVOKE build-test-main  test-main = "optionPutShell"   =][=

  == shell-parser               =][=
     INVOKE build-test-main  test-main = "optionParseShell" =][=

  == main                       =][=
     INVOKE build-test-main     =][=

  == include                    =]
[=   INCLUDE tpl                =][=

  == invoke                     =][=
     INVOKE (get "func")        =][=

  == for-each                   =][=
     INVOKE for-each-main       =][=

  *                             =][=
     (error (sprintf "unknown/invalid main-type: '%s'" (get "main-type"))) =][=

  ESAC =][= ENDFOR first-main   =][=

ENDDEF build-main

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

   DECLARE OPTION CALLBACK PROCEDURES

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=

DEFINE decl-callbacks

   This is the test for whether or not to emit callback handling code:

=]
/**
 *  Declare option callback procedures
 */[=
  (define undef-proc-names "")
  (define decl-type "")
  (define extern-proc-list (string-append
    (if (exist? "version-proc")
        (get "version-proc")
        "optionPrintVersion") "\n"
    "optionBooleanVal\n"
    "optionNestedVal\n"
    "optionNumericVal\n"
    "optionResetOpt\n"
    "optionStackArg\n"
    "optionTimeDate\n"
    "optionTimeVal\n"
    "optionUnstackArg\n"
    "optionVendorOption\n"

  ) )

  (define extern-test-list "")

  (define emit-decl-list (lambda(txt-var is-extern)
    (if (> (string-length txt-var) 1) (begin

      (emit (if is-extern "\nextern tOptProc\n" "\nstatic tOptProc\n"))
      (set! txt-var (shellf "
        (%s -v '^%s$' | sed '/^$/d' | sort -u | \
        sed 's@$@,@;$s@,$@;@' ) <<_EOProcs_\n%s_EOProcs_"
            egrep-prog
            (if is-extern "NULL" "(NULL|optionStackArg|optionUnstackArg)")
            txt-var ))

      (emit (shellf (if (< (string-length txt-var) 72)
                  "f='%s' ; echo \"   \" $f"
                  "${CLexe} --spread=1 -I4 <<_EOProcs_\n%s\n_EOProcs_" )
            txt-var ))
    ))
  ))

  (define static-proc-list "doUsageOpt\n")
  (define static-test-list static-proc-list)
  (define ifdef-fmt (string-append
    "\n#if%1$sdef %2$s"
    "\n  %3$s tOptProc %4$s;"
    "\n#else /* not %2$s */"
    "\n# define %4$s NULL"
    "\n#endif /* def/not %2$s */"))

  (define make-proc-decl #t)

  (define set-ifdef (lambda(n-or-def ifdef-cb ifdef-name) (begin
     (set! decl-type (if (hash-ref is-ext-cb-proc flg-name) "extern" "static"))
     (set! make-proc-decl #f)
     (ag-fprintf 0 ifdef-fmt n-or-def ifdef-name decl-type ifdef-cb )
  )))

  (define set-cb-decl (lambda() (begin

     (set! make-proc-decl #t)
     (set! tmp-val (hash-ref cb-proc-name flg-name))

     (if (exist? "ifdef")
         (set-ifdef ""  tmp-val (get "ifdef"))

         (if (exist? "ifndef")
             (set-ifdef "n" tmp-val (get "ifndef"))

             (if (hash-ref is-ext-cb-proc flg-name)
                 (set! extern-proc-list (string-append
                       extern-proc-list tmp-val "\n" ))

                 (set! static-proc-list (string-append
                       static-proc-list tmp-val "\n" ))
     )  )  )

     (if guarded-test-main (begin
         (set! tmp-val (hash-ref test-proc-name flg-name))
         (if (hash-ref is-ext-cb-proc flg-name)
             (set! extern-test-list (string-append extern-test-list
                   tmp-val "\n" ))

             (if make-proc-decl
                 (set! static-test-list
                       (string-append static-test-list tmp-val "\n") ) ) )
     )   )

  )))
  =][=

  FOR flag =][=

    ;;  Fill in four strings with names of callout procedures:
    ;;  extern-test-list - external callouts done IFF test main is built
    ;;  static-test-list - static callouts done IFF test main is built
    ;;  extern-proc-list - external callouts for normal compilation
    ;;  static-proc-list - static callouts for normal compilation
    ;;
    ;;  Anything under the control of "if[n]def" has the declaration or
    ;;  #define to NULL emitted immediately.
    ;;
    (set! flg-name (get "name"))

    (if (and (hash-ref have-cb-procs flg-name)
             (not (hash-ref is-lib-cb-proc flg-name)) )
              (set-cb-decl)
    )           =][=

  ENDFOR flag   =][=

  IF (. guarded-test-main)                      =][=
    INVOKE emit-testing-defines                 =][=
  ENDIF guarded-test-main                       =][=

  (if (not (exist? "no-libopts"))
      (set! extern-proc-list (string-append extern-proc-list
            "optionPagedUsage\n")) )

  (emit-decl-list extern-proc-list #t)
  (emit-decl-list static-proc-list #f)
  (set! static-proc-list "")                    =][=

  FOR flag                                      =][=

    (set! flg-name (get "name"))
    (if (not (= (hash-ref cb-proc-name  flg-name)
               (hash-ref test-proc-name flg-name)))
        (set! static-proc-list (string-append static-proc-list
              "#define " (get-up-name "name") "_OPT_PROC "
                     (hash-ref cb-proc-name flg-name) "\n"))  )
    =][=
  ENDFOR  flag                                  =][=

  IF (> (string-length static-proc-list) 0) =]

/**
 *  #define map the "normal" callout procs
 */
[= (. static-proc-list)                         =][=

  ENDIF  have some #define mappings

=][=

  (if guarded-test-main
      (emit "\n#endif /* " main-guard " */") )

  undef-proc-names                              =][=

ENDDEF decl-callbacks

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=

DEFINE emit-testing-defines

=][=
  (set! extern-proc-list (string-append extern-proc-list
        "optionVersionStderr\n"))
  (tpl-file-line extract-fmt)                   =]
#if defined([=(. main-guard)=])
/*
 *  Under test, omit argument processing, or call optionStackArg,
 *  if multiple copies are allowed.
 */[=

    (emit-decl-list extern-test-list #t)
    (emit-decl-list static-test-list #f)
    (set! static-test-list "")                  =][=

    FOR flag                                    =][=

      (set! flg-name (get "name"))
      (if (not (= (hash-ref cb-proc-name  flg-name)
                 (hash-ref test-proc-name flg-name)))
          (set! static-test-list (string-append static-test-list
                "#define " (get-up-name "name") "_OPT_PROC "
                       (hash-ref test-proc-name flg-name) "\n"))  )
      =][=
    ENDFOR  flag                                =][=

    IF (> (string-length static-test-list) 0)   =]

/*
 *  #define map the "normal" callout procs to the test ones...
 */
[= (. static-test-list)                         =][=

    ENDIF  have some #define mappings

=]

#else /* NOT defined [=(. main-guard)=] */
/*
 *  When not under test, there are different procs to use
 */[=

ENDDEF emit-testing-defines

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

   DEFINE OPTION CALLBACKS

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=

DEFINE callback-proc-header     =]

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
 * Code to handle the [=name=] option[= # */ =][=

  IF  (exist? "ifdef")

=], when [= ifdef =] is #define-d[=
    (define ifdef-text (string-append "\n#ifdef " (get "ifdef")))
    (set! endif-test-main (string-append
	   (sprintf "\n#endif /* defined %s */" (get "ifdef"))
	   endif-test-main
    )) =][=

  ELIF (exist? "ifndef")

=], when [= ifndef =] is *not* #define-d[=
    (define ifdef-text (string-append "\n#ifndef " (get "ifndef")))
    (set! endif-test-main (string-append
	   (sprintf "\n#endif /* ! defined %s */" (get "ifndef"))
	   endif-test-main
    )) =][=

  ELSE  unconditional code:

=][= (define ifdef-text "") =][=

  ENDIF ifdef / ifndef

=].
[= (prefix " * " (get "doc")) =]
 * @param[in] pOptions the [=(. prog-name)=] options data structure
 * @param[in,out] pOptDesc the option descriptor for this option.
 */[= (. ifdef-text) =]
static void
doOpt[= (set! endif-test-main (string-append "\n}" endif-test-main))
        cap-name =](tOptions* pOptions, tOptDesc* pOptDesc)
{
[=

ENDDEF   callback-proc-header

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=

DEFINE reset-clause =][=

  (if (exist? "flag-code[0]")
      (emit (string-append "\n" (get "flag-code[0]"))))

  (if (exist? "resettable") (emit (string-append
      "\n    if ((pOptDesc->fOptState & OPTST_RESET) != 0)"
      "\n        return;" )) )

  =][=

ENDDEF reset-clause

// # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=

DEFINE range-option-code

=][=

(define option-arg-type (get "arg-type"))
(define range-count     (count "arg-range"))

\=]
    static struct {long rmin, rmax;} const rng[[=
      (. range-count)   =]] = {
[=(out-push-new)        =][=
  FOR arg-range ",\n"   =]{ [=

    CASE arg-range      =][=
      *==    "->"       =][=
             (string-substitute (get "arg-range") "->" "") =], LONG_MAX[=

      ==*    "->"       =]LONG_MIN, [=
             (string-substitute (get "arg-range") "->" "") =][=

      *==*   "->"       =][=
             (string-substitute (get "arg-range") "->" ", ") =][=

      ~~ -{0,1}[0-9]+   =][=arg-range=], LONG_MIN[=

      *  =][= (error (string-append "Invalid range spec:  ``"
              (get "arg-range") "''" ))  =][=

    ESAC arg-range      =] }[=
  ENDFOR                =][=

  (shellf "${CLexe} -I8 --spread=2 <<_EOF_\n%s\n_EOF_"
          (out-pop #t)) =] };
    int  ix;

    if (pOptions <= OPTPROC_EMIT_LIMIT)
        goto emit_ranges;[=

  INVOKE reset-clause =][=

  CASE (define leave-ok-code "") (define ok-return-code "")
    (if (exist? "flag-code[1]")
        (begin
           (set! leave-ok-code "goto return_okay")
           (set! ok-return-code (string-append
                 "\n    return;\n\nreturn_okay:\n"
                 (get "flag-code[1]") ))
        )
        (begin
           (set! leave-ok-code  "return")
           (set! ok-return-code "")
    )   )

    option-arg-type     =][=

  =*  num               =]
    optionNumericVal(pOptions, pOptDesc);[=

  =   time-date         =][=
    (error (string-append "time/date option " low-name
           " may not be range limited.")) )
    =][=

  =*  time              =]
    optionTimeVal(pOptions, pOptDesc);
    if (pOptDesc->optArg.argInt == (long)BAD_TIME)
        return;[=

  *                     =][=
    (error (string-append option-arg-type " option " low-name
           " may not be range limited.")) )
    =][=

  ESAC                  =]

    for (ix = 0; ix < [=(. range-count)=]; ix++) {
        if (pOptDesc->optArg.argInt < rng[ix].rmin)
            continue;  /* ranges need not be ordered. */
        if (pOptDesc->optArg.argInt == rng[ix].rmin)
            [= (. leave-ok-code) =];
        if (rng[ix].rmax == LONG_MIN)
            continue;
        if (pOptDesc->optArg.argInt <= rng[ix].rmax)
            [= (. leave-ok-code) =];
    }

    option_usage_fp = stderr;

 emit_ranges:
optionShowRange(pOptions, pOptDesc, VOIDP(rng), [= (. range-count) =]);[=

(. ok-return-code)  =][=

ENDDEF   range-option-code

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=

DEFINE alias-option-code       \=]
    int res = optionAlias(pOptions, pOptDesc, [=
        (string-append INDEX-pfx (get-up-name "aliases")) =]);
    if ((res != 0) && ((pOptions->fOptSet & OPTPROC_ERRSTOP) != 0))
        [= (. UP-prefix) =]USAGE([=(. usage-err-name)=]);
[=

ENDDEF   alias-option-code

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=

DEFINE   keyword-code           =][=
   (emit (tpl-file-line extract-fmt))

   (set! tmp-ct (count "keyword"))
   (if (not (exist? "arg-default"))
       (begin
         (set! tmp-ct (+ 1 tmp-ct))
         (emit "    static char const zDef[2] = { 0x7F, 0 };\n")
   )   )                        \=]
    static char const * const names[[= (. tmp-ct) =]] = {[=
        (emit (if (not (exist? "arg-default")) " zDef,\n" "\n"))
        (out-push-new) =][=
  FOR keyword =][=
    (string-table-add-ref opt-strs (get "keyword")) =]
[=  ENDFOR =][=
  (shell (string-append
  "${CLexe} -S, -I8 --spread=1 <<-\\_EOF_\n" (out-pop #t) "_EOF_\nset +x" ))
=] };

    if (pOptions <= OPTPROC_EMIT_LIMIT) {
        (void) optionEnumerationVal(pOptions, pOptDesc, names, [=
           (. tmp-ct)=]);
        return; /* protect AutoOpts client code from internal callbacks */
    }[=

  INVOKE reset-clause =][=

  IF (exist? "arg-optional")

=]

    if (pOptDesc->optArg.argString == NULL)
        pOptDesc->optArg.argEnum = [=
             (string-append UP-name "_"    (if (> (len "arg-optional") 0)
                (get-up-name "arg-optional") (if (exist? "arg-default")
                (get-up-name "arg-default")
                "UNDEFINED"  ))) =];
    else
        pOptDesc->optArg.argEnum =
            optionEnumerationVal(pOptions, pOptDesc, names, [=(. tmp-ct)=]);[=

  ELSE

=]

    pOptDesc->optArg.argEnum =
        optionEnumerationVal(pOptions, pOptDesc, names, [=(. tmp-ct)=]);[=

  ENDIF                         =][=

  (if (exist? "flag-code[1]")
      (emit (string-append "\n" (get "flag-code[1]"))))

  =][=

ENDDEF   keyword-code

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=

DEFINE   set-membership-code

=][=

(if (not (exist? "keyword"))
    (error "set membership requires keywords"))
(set! tmp-ct (count "keyword"))
(emit (tpl-file-line extract-fmt))
(ag-fprintf 0 "    static char const * const names[%d] = {\n" tmp-ct)

(shell (string-append

  "${CLexe} -I8 --spread=2 --sep=',' -f'\"%s\"' <<_EOF_\n"
  (join "\n" (stack "keyword"))
  "\n_EOF_\n" )) =]
    };[=

  INVOKE reset-clause =]
    /*
     * This function handles special invalid values for "pOptions"
     */
    optionSetMembers(pOptions, pOptDesc, names, [= (. tmp-ct) =]);[=

  (if (exist? "flag-code[1]")
      (emit (string-append "\n" (get "flag-code[1]"))))

  =][=

ENDDEF   set-membership-code

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=

DEFINE   file-name-code

\=]
    static teOptFileType const  type =
        [= (set! tmp-val (get "open-file")) =][=
   CASE file-exists                 =][=
   == ""                            =]FTYPE_MODE_MAY_EXIST[=
   =* "no"                          =]FTYPE_MODE_MUST_NOT_EXIST[=
   *                                =]FTYPE_MODE_MUST_EXIST[=
   ESAC =] + [= CASE open-file      =][=

   == ""                            =]FTYPE_MODE_NO_OPEN[=
   =* "desc"                        =]FTYPE_MODE_OPEN_FD[=
   *                                =]FTYPE_MODE_FOPEN_FP[=

   ESAC =];
    static tuFileMode           mode;
[= IF (or (=* tmp-val "desc") (== tmp-val "")) \=]
[= IF (not (exist? "file-mode"))   \=]
#ifndef O_CLOEXEC
#  define O_CLOEXEC 0
#endif
[= (define file-mode "O_CLOEXEC")   =][=
   ELSE                             =][=
   (define file-mode (get "file-mode")) \=]
[= ENDIF \=]
    mode.file_flags = [= (. file-mode) =];
[= ELSE \=]
#ifndef FOPEN_BINARY_FLAG
#  define FOPEN_BINARY_FLAG
#endif
    mode.file_mode = [= (c-string (get "file-mode")) =] FOPEN_BINARY_FLAG;
[= ENDIF =][=

  INVOKE reset-clause =]
    /*
     * This function handles special invalid values for "pOptions"
     */
    optionFileCheck(pOptions, pOptDesc, type, mode);[=

  (if (exist? "flag-code[1]")
      (emit (string-append "\n" (get "flag-code[1]"))))

  =][=

ENDDEF   file-name-code

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=

DEFINE   requested-code             =][=

IF (not (or (exist? "extract-code") (exist? "flag-code")))
   =][=  RETURN                     =][=
ENDIF                               =][=

(if guarded-test-main
    (begin
      (set! endif-test-main
            (sprintf "\n#endif /* defined(%s) */" main-guard))
      (emit "\n\n#if ! defined(" main-guard ")")
)   )                               =][=

INVOKE callback-proc-header         =][=

IF (out-push-new (string-append tmp-dir "/flag-code"))
   (exist? "flag-code")             =][=

   IF (exist? "flag-code[0]")      \=]
    /*
     * Be sure the flag-code[0] handles special values for the options pointer
     * viz. (poptions <= OPTPROC_EMIT_LIMIT) *and also* the special flag bit
     * ((poptdesc->fOptState & OPTST_RESET) != 0) telling the option to
     * reset its state.
     */[=
      (def-file-line "flag-code[0]" "\n    /* extracted from %s, line %d */\n")
      =][=  flag-code[0]            =][=
    ENDIF                           =][=

    CASE arg-type                   =][=
    =*   bool                       =]
    optionBooleanVal(pOptions, pOptDesc);[=
    =*   num                        =]
    optionNumericVal(pOptions, pOptDesc);[=
    =    time-date                  =]
    optionTimeDate(pOptions, pOptDesc);[=
    =*   time                       =]
    optionTimeVal(pOptions, pOptDesc);[=
    ~*   hier|nest                  =]
    optionNestedVal(pOptions, pOptDesc);[=
    ESAC                            =][=

    IF (exist? "flag-code[1]")      =]
[=     flag-code[1]                 =][=
    ENDIF                           =][=

ELSE                                =][=

   (extract (string-append (base-name) ".c.save") (string-append
            "/*  %s =-= " cap-name " Opt Code =-= %s */"))
                                    =][=
ENDIF                               =][=
(out-pop)
(shell "ck_flag_code")              =][=

ENDDEF   requested-code

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=

DEFINE define-option-callbacks      =][=

  FOR flag                          =][=
    (define flag-index (for-index))
    (set-flag-names)
    (define endif-test-main "")     =][=

# # # # # # # # # # # # # # # # # # =][=

    IF (exist? "arg-range")         =][=

      INVOKE callback-proc-header   =][=
      INVOKE range-option-code      =][=

# # # # # # # # # # # # # # # # # # =][=

    ELIF (exist? "aliases")         =][=

      INVOKE callback-proc-header   =][=
      INVOKE alias-option-code      =][=

# # # # # # # # # # # # # # # # # # =][=

    ELSE =][= CASE arg-type         =][=

      =* key                        =][=
      INVOKE callback-proc-header   =][=
      INVOKE keyword-code           =][=

# # # # # # # # # # # # # # # # # # =][=

      =* set                        =][=

      INVOKE callback-proc-header   =][=
      INVOKE set-membership-code    =][=

# # # # # # # # # # # # # # # # # # =][=

      =* fil                        =][=
      INVOKE callback-proc-header   =][=
      INVOKE file-name-code         =][=

# # # # # # # # # # # # # # # # # # =][=

      *                             =][=
      INVOKE requested-code         =][=

      ESAC                          =][=
    ENDIF                           =][=

    (. endif-test-main)             =][=

  ENDFOR flag                       =][=

ENDDEF define-option-callbacks

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=

DEFINE emit-option-callbacks        =]
/*
 *  Create the static procedure(s) declared above.
 */
/**
 * The callout function that invokes the [= (. usage-proc)
   =] function.
 *
 * @param[in] opts the AutoOpts option description structure
 * @param[in] od   the descriptor for the "help" (usage) option.
 * @noreturn
 */
static void
doUsageOpt(tOptions * opts, tOptDesc * od)
{
    int ex_code;[=

  IF (define od-used #f)
     (exist? "resettable") =]
    if ((od->fOptState & OPTST_RESET) != 0)
        return;[= (set! od-used #t) =][=

  ENDIF                         =][=

  IF (exist? "usage-opt") =]
    ex_code = (od->optIndex == [= (set! od-used #t) INDEX-pfx =]HELP)
        ? [=(. succ-exit-code)=] : AO_EXIT_REQ_USAGE;[=

  ELSE                          =]
    ex_code = [=(. succ-exit-code)=];[=
  ENDIF                         =][=
  (string-append "\n    " usage-proc "(&" pname "Options, ex_code);") =]
    /* NOTREACHED */
    exit([=(. fail-exit-code)=]);
    (void)opts;[=
    (if od-used "" "\n    (void)od;") =]
}[=

INVOKE define-option-callbacks  =][=

IF (exist? "main")              =][=
  INVOKE build-main             =][=

ELIF (. guarded-test-main)      =][=
  INVOKE build-test-main        =][=

ENDIF

=][=
(tpl-file-line extract-fmt)
=][=

IF (exist? "usage-message")     =]
/**
 * Print a usage message with a format and va_list argument.
 * The [= (. usage-proc) =] function is then invoked to print
 * the error usage text (somewhat abbreviated) and then exit.
 *
 * @param[in] fmt the message format string
 * @param[in] ap  the var-arg list.
 * @noreturn
 */
[=(. no-return-str)=]vusage_message(char const * fmt, va_list ap)
{
    char const * er_leader = _("[= prog-name =] usage error:\n");
    fputs(er_leader, stderr);
    vfprintf(stderr, fmt, ap);
    [= (string-append usage-proc "(&" pname "Options, " usage-err-name ")") =];
    /* NOTREACHED, but C11 compilers cannot tell. */
    abort();
}

/**
 * Print a usage message with a format and a variable argument list.
 * [=(. lc-prefix)=]vusage_message() is called to do the work.
 *
 * @param[in] fmt the message format string
 * @param[in] ... the argument list for the message
 * @noreturn
 */
[=(. no-return-str)=]usage_message(char const * fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    [=(. lc-prefix)=]vusage_message(fmt, ap);
}
[=

ENDIF have usage-message    =][=

IF (exist? "die-code")

=]
/**
 * Print a fatal error message and die, \a va_list style.
 *
 * @param[in] exit_code  the value to call exit(3) with
 * @param[in] fmt        the death rattle message
 * @param[in] ap         the argument list for the message
 * @noreturn
 */
[=(. no-return-str)=]vdie(int exit_code, char const * fmt, va_list ap)
{
    char const * die_leader = _("[= prog-name =] fatal error:\n");[=
  (set! tmp-text (get "die-code"))
  (if (> (string-length tmp-text) 1)
      (string-append "\n\n" tmp-text "\n"))
 =]
    fputs(die_leader, stderr);
    vfprintf(stderr, fmt, ap);
    fflush(stderr);
    exit(exit_code);
}

/**
 * Print a fatal error message and die, var-arg style.
 *
 * @param[in] exit_code  the value to call exit(3) with
 * @param[in] fmt        the death rattle message
 * @param[in] ...        the list of arguments for the message
 * @noreturn
 */
[=(. no-return-str)=]die(int exit_code, char const * fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    vdie(exit_code, fmt, ap);
}

/**
 * Print a file system error fatal error message and die.
 *
 * @param[in] exit_code  the value to call exit(3) with.
 * @param[in] op         the operation that failed.
 * @param[in] fname      the file name the operation was on.
 * @noreturn
 */
[=(. no-return-str)=]fserr(int exit_code, char const * op, char const * fname)
{
    char const * fserr_fmt = _("fserr %d (%s) performing '%s' on %s\n");
    die(exit_code, fserr_fmt, errno, strerror(errno), op, fname);
}
[=
ENDIF die-code              =][=

IF (exist? "warn-code")

=]
/**
 * Print a warning message, \a va_list style.
 *
 * @param[in] fmt        the "something awry" message
 * @param[in] ap         the argument list for the message
 */
void
[=(. lc-prefix)=]vwarning_msg(char const * fmt, va_list ap)
{
    char const * warn_leader = _("[= prog-name =] WARNING:\n");[=
  (set! tmp-text (get "warn-code"))
  (if (> (string-length tmp-text) 1)
      (string-append "\n\n" tmp-text "\n"))
 =]
    fputs(warn_leader, stderr);
    vfprintf(stderr, fmt, ap);
    fflush(stderr);
}

/**
 * Print a warning message, var-arg style.
 *
 * @param[in] fmt        the "something awry" message
 * @param[in] ...        the list of arguments for the message
 */
void
[=(. lc-prefix)=]warning_msg(char const * fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    vwarning_msg(fmt, ap);
    va_end(ap);
}
[=

ENDIF have warn-code    =][=

ENDDEF emit-option-callbacks

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=

DEFINE emit-option-code        =][=

  IF (exist? "option-code") =][=
      (def-file-line "option-code" extract-fmt) =][=
      option-code   =][=

  ELSE            =][=

    IF (and (exist? "no-libopts") (not (exist? "autoopts-usage-tlib"))) =]
    (void)process_[=(. pname)=]_opts(argc, argv);[=
    ELIF (exist? "main-text") =]
    {
        int ct = optionProcess(&[=(. pname)=]Options, argc, argv);
        argc -= ct;
        argv += ct;
    }[=
    ELSE =]
    (void)optionProcess(&[=(. pname)=]Options, argc, argv);[=
    ENDIF =][=

  ENDIF           =][=

ENDDEF emit-option-code

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # =][=

DEFINE emit-main-text        =][=

  IF (exist? "main-text") =][=

    (def-file-line "main-text" extract-fmt) =][=
    main-text         =][=

  ELSE            =][=

    IF (and (exist? "no-libopts") (not (exist? "autoopts-usage-tlib"))) =]
    /* When using AutoOpts with getopt(3C) or getopt_long(3GNU)
       the main-text attribute of main _must_ be defined. */
#error autoopts_with_getopt_must_define_main_text_attribute[=
    ELSE test-main is not optionParseShell and main-text not exist
         Above, we figure out which emitter procedure is to be used.
         The default is optionPutShell.
=]
    [= (. option-emitter-proc) =](&[=(. pname)=]Options);
    res = ferror(stdout);
    if (res != 0)
        fputs("output error writing to stdout\n", stderr);[=
    ENDIF   =][=

  ENDIF           =][=

ENDDEF emit-main-text        =]