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

# Time-stamp:      "2012-08-11 08:13:56 bkorb"

##  This file is part of AutoOpts, a companion to AutoGen.
##  AutoOpts is free software.
##  AutoOpts is Copyright (c) 1992-2012 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 md5sums:
##
##  43b91e8ca915626ed3818ffb1b71248b COPYING.gplv3
##  06a1a2e4760c90ea5e1dad8dfaac4d39 COPYING.lgplv3
##  66a5cedaf62c4b2637025f049f9b826f 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

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

#if defined([=(. main-guard)=]) /* TEST MAIN PROCEDURE: */[=

  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 argc argument count
 * @param 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);[=

  ELIF (exist? "main-text") =][=
    IF (not (exist? "option-code")) =]
    {
        int ct = optionProcess(&[=(. pname)=]Options, argc, argv);
        argc -= ct;
        argv += ct;
    }[=
    ELSE            =][=
      (def-file-line "option-code" extract-fmt) =][=
      option-code   =][=
    ENDIF           =][=

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

  ELSE test-main is not optionParseShell and main-text not exist

=]
    (void)optionProcess(&[=(. pname)=]Options, argc, argv);
    [= (. option-emitter-proc) =](&[=(. pname)=]Options);
    res = ferror(stdout);
    if (res != 0)
        fputs("output error writing to stdout\n", stderr);[=
  ENDIF=]
    return res;
}
#endif  /* defined [= (. 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)

=]
/**
 * 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 pz_s source text pointer
 * @returns pointer to the same text buffer, but after finding the
 * first non-white space character.
 */
static char *
trim_input_line(char * pz_s)
{
    char* pz_e = pz_s + strlen(pz_s);
    while ((pz_e > pz_s) && isspace((unsigned int)pz_e[-1]))  pz_e--;
    *pz_e = '\0';
    while ((unsigned int)isspace(*pz_s))  pz_s++;

    switch (*pz_s) {
    case '\0':
    case '[= ?% comment-char "%s" "#" =]':
        return NULL;
    default:
        return pz_s;
    }
}[=

CASE handler-type =][=
=*  name         =][= (set! handler-arg-type "char const* pz_fname")
                      (define handler-proc "validate_fname")       =][=
=*  file         =][=
   (set! handler-arg-type "char const* pz_fname, FILE* entry_fp")
                      (define handler-proc "validate_fname")       =][=
*=* text         =][=
   (set! handler-arg-type
         "char const* pz_fname, char* pz_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

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

IF (exist? "handler-type")                      =]
/**
 * 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 pz_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* pz_fname)
{
    static char const * pz_fs_err = NULL;[=

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

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

    {
        struct stat sb;
        if (stat(pz_fname, &sb) < 0) {[=
  IF (. emit-failing-printf)                    =]
            fprintf(stderr, pz_fs_err, errno, strerror(errno), "stat",
                    pz_fname);[=
  ENDIF                                         =]
            return [= (. file-fail-exit-code)   =];
        }[=

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

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

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

        if (sb.st_size == 0) {[=
      IF (. emit-failing-printf)                =]
            fprintf(stderr, pz_fs_err, EINVAL, strerror(EINVAL),
                    "empty file:", pz_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 =](pz_fname);[=

=*  file          =][= (tpl-file-line extract-fmt) =]
    {
        int res;
        FILE* fp = fopen(pz_fname, "[=
        (shellf "echo '%s' | sed 's/.*-//'"
                (get "handler-type")) =]");
        if (fp == NULL) {
            fprintf(stderr, pz_fs_err, errno, strerror(errno), "fopen",
                    pz_fname);
            return [= (. file-fail-exit-code) =];
        }
        res = [= handler-proc =](pz_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, pz_fname);
        exit([=(. nomem-exit-code)=]);
    }

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

        if (fd < 0) {
            fprintf(stderr, pz_fs_err, errno, strerror(errno), "open",
                    pz_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, pz_fs_err, errno, strerror(errno), "read",
                        pz_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 =](pz_fname, file_text, text_size);
    free(file_text);
    return res;[=
ESAC             =]
}[=

ENDIF handler-type exists  =][=

(tpl-file-line extract-fmt)

=]
/**
 * 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 argc argument count
 * @param argv argument vector
 * @returns program exit code
 */
int
main(int argc, char** argv)
{
    int res = 0;
    {
        int ct = optionProcess(&[=(. pname)=]Options, argc, argv);
        argc -= ct;
        argv += ct;
    }[=
    (if (exist? "main-init") (string-append
    "\n    " (def-file-line "main-init" extract-fmt) "\n" (get "main-init")))

    =][= (tpl-file-line extract-fmt) =]
    /*
     *  Input list from command line
     */
    if (argc > 0) {
        do  {
            res |= [= (. handler-proc) =](*(argv++));
        } while (--argc > 0);
    }

    /*
     *  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 {
        int in_ct   = 0;
        size_t pg_size = getpagesize();
        char* buf   = malloc(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) {
                 res |= [= (. handler-proc) =](pz);
                 in_ct++;
            }
        }

        if (in_ct == 0)
            fputs(_("[=(. prog-name)
                   =] Warning:  no input lines were read\n"), stderr);
        free(buf);
    }[=
    (if (exist? "main-fini") (string-append
    "\n    " (def-file-line "main-fini" extract-fmt) "\n" (get "main-fini")))
    \=]

    return res;
}[=

ENDDEF  for-each-main

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

   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 make-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 (. make-test-main)                         =][=
    (set! extern-proc-list (string-append extern-proc-list
          "optionVersionStderr\n"))             =][=
    INVOKE emit-testing-defines                 =][=
  ENDIF make-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 make-test-main
      (ag-fprintf 0 "\n#endif /* defined(%s) */" main-guard) )

  undef-proc-names                              =][=

ENDDEF decl-callbacks                           =][=

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

DEFINE emit-testing-defines

=]
#if defined([=(tpl-file-line extract-fmt) 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

=].
 *
 * @param pOptions the [=(. prog-name)=] options data structure
 * @param 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, (void *)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([=(. fail-exit-code)=]);
[=

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 azNames[[= (. 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, azNames, [=
           (. 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, azNames, [=(. tmp-ct)=]);[=

  ELSE

=]

    pOptDesc->optArg.argEnum =
        optionEnumerationVal(pOptions, pOptDesc, azNames, [=(. 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 azNames[%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, azNames, [= (. 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 make-test-main
    (begin
      (set! endif-test-main
            (sprintf "\n#endif /* defined(%s) */" main-guard))
      (sprintf "\n\n#if ! defined(%s)" main-guard)
)   )                               =][=

INVOKE callback-proc-header         =][=

IF (out-push-new (string-append tmp-dir "/flag-code"))
   (exist? "flag-code")             =][=
   (def-file-line "flag-code" "    /* extracted from %s, line %d */\n")
   =][=
  (join "\n" (stack "flag-code"))   =][=

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 pOptions the AutoOpts option description structure
 * @param pOptDesc the descriptor for the "help" (usage) option.
 * @noreturn
 */
static void
doUsageOpt(tOptions * pOptions, tOptDesc * pOptDesc)
{[=
  IF (exist? "resettable") =]
    if ((pOptDesc->fOptState & OPTST_RESET) != 0)
        return;
[=ENDIF=][=
  IF (exist? "usage-opt") =]
    int ex_code = (pOptDesc->optIndex == [= (. INDEX-pfx) =]HELP)
        ? [=(. succ-exit-code)=] : AO_EXIT_REQ_USAGE;
    [= (string-append usage-proc "(&" pname "Options, ex_code);") =]
    /* NOTREACHED */[=

  ELSE   =]
    [= (string-append usage-proc "(&" pname "Options, " succ-exit-code ");") =]
    /* NOTREACHED */
    (void)pOptDesc;[=
  ENDIF  =]
    (void)pOptions;
}[=

INVOKE define-option-callbacks  =][=

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

ELIF (. make-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
 */
void
[=(. lc-prefix)=]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, " fail-exit-code ");") =]
    /* NOTREACHED */
}

/**
 * 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
 */
void
[=(. lc-prefix)=]usage_message(char const * fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    [=(. lc-prefix)=]vusage_message(fmt, ap);
    /* NOTREACHED */
    va_end(ap);
}
[=

ENDIF have usage-message    =][=

IF (exist? "die-code")

=]
/**
 * Print a fatal error message and die
 *
 * @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
 */
void
[=(. lc-prefix)=]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);
    /* NOTREACHED */
}

/**
 * Print a fatal error message and die
 *
 * @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
 */
void
[=(. lc-prefix)=]die(int exit_code, char const * fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    vdie(exit_code, fmt, ap);
    /* NOTREACHED */
    va_end(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
 */
void
[=(. lc-prefix)=]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);
    /* NOTREACHED */
}
[=

ENDIF die-code              =][=

ENDDEF emit-option-callbacks

=]
