Blob Blame History Raw
[= -*- Mode: texinfo -*-

 AutoGen5 Template

#  This file is part of AutoGen.
#  AutoGen Copyright (C) 1992-2015 by Bruce Korb - all rights reserved
#
#  AutoGen is free software: you can redistribute it and/or modify it
#  under the terms of the GNU General Public License as published by the
#  Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  AutoGen is distributed in the hope that it will be useful, but
#  WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#  See the GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License along
#  with this program.  If not, see <http://www.gnu.org/licenses/>.

=]
@page
@node AutoOpts
@chapter Automated Option Processing
@cindex autoopts

AutoOpts [=
(make-tmp-dir)
(out-push-new)
=]
test ${#AGexe} -eq 0 -o ${#top_srcdir} -eq 0 -o ${top_builddir} -eq 0 && \
     die "AGexe, top_srcdir and top_builddir must be set"
ag_cmd="${AGexe} -L${top_srcdir}/autoopts/tpl"
test "X${top_srcdir}" = "X${top_builddir}" || \
     ag_cmd="${ag_cmd} -L${top_builddir}/autoopts/tpl"
readonly ag_cmd

run_ag() {
    echo ${ag_cmd} "$@" >&2
    ${ag_cmd} "$@"
}

eval "`egrep '^AO_[A-Z]*=' ${top_srcdir}/VERSION`" 2> /dev/null
echo ${AO_CURRENT}.${AO_REVISION}
[=

(shell (out-pop #t))

=] is bundled with AutoGen.  It is a tool that virtually eliminates the
hassle of processing options and keeping man pages, info docs and usage text
up to date.  This package allows you to specify several program attributes,
thousands of option types and many option attributes.  From this, it then
produces all the code necessary to parse and handle the command line and
configuration file options, and the documentation that should go with your
program as well.
[=

INVOKE  get-text tag = autoopts  =][=
(out-push-new (string-append tmp-dir "/check.def" ))

=]
AutoGen Definitions options;
prog-name     = check;
prog-title    = "Checkout Automated Options";
long-opts;
gnu-usage;    /* GNU style preferred to default */

main = { main-type = shell-process; };

flag = {
    name      = check-dirs;
    value     = L;        /* flag style option character */
    arg-type  = string;   /* option argument indication  */
    max       = NOLIMIT;  /* occurrence limit (none)     */
    stack-arg;            /* save opt args in a stack    */
    descrip   = "Checkout directory list";
    doc       = 'name of each directory that is to be "checked out".';
};

flag = {
    name      = show_defs;
    descrip   = "Show the definition tree";
    disable   = dont;     /* mark as enable/disable type */
                          /* option.  Disable as `dont-' */
    doc       = 'disable, if you do not want to see the tree.';
};
[= (texi-escape-encode (out-pop #t)) \=]
@end example

@node quick ao build
@subsection Build the example options

This program will produce a program that digests its options and
writes the values as shell script code to stdout.
Run the following short script to produce this program:[= #

Developer note:  the following only works when AutoGen has been installed.
Since this may be being built on a system where it has not been installed,
the code below ensures we are running out tools out of the build directory
=]
@example
[=

(out-push-new (string-append tmp-dir "/mk-check.sh" ))

\=]
base=check
BASE=`echo $base | tr '[a-z-]' '[A-Z_]'`
cflags="-DTEST_${BASE} `autoopts-config cflags`"
ldflags="`autoopts-config ldflags`"
autogen ${base}.def
cc -o ${base} -g ${cflags} ${base}.c ${ldflags}
./${base} --help
[= (texi-escape-encode (out-pop #t)) \=]
@end example

@node quick ao help
@subsection Example option help text

Running the build commands yields:

@example
[= (out-push-new) \=]
base=check
cd ${tmp_dir}
(
  PS4='>ck> '
  BASH_XTRACEFD=2
  exec 2> ${base}-msg.log 1>&2
  set -x

  test -f ${base}.def || die "cannot locate ${base}.def"
  test ! -f ${base} || rm -f ${base}
  echo "include = '#include \"compat/compat.h\"';" >> ${base}.def
  f='@="@="'`echo ${INCLUDES} ${CFLAGS}`' @'
  sed -e "s@^cc @${CC:-cc} -include ${top_builddir}/config.h @" \
      -e '/^cflags="/s'"${f}" \
      -e 's@^autogen @run_ag @' \
            mk-${base}.sh > mk-${base}

  . ./mk-${base}
)
test -x ./${base} || {
  cat mk-${base}
  printf '\n\nFAILURE LOG:\n\n'
  cat ${base}-msg.log
  tar czf /tmp/ck-fail.tgz .
  die cannot create ${base} program
} >&2

./${base} --help | sed 's/\t/        /g'
[=

(texi-escape-encode (shell (out-pop #t)))

=]
@end example
[=

INVOKE  get-text tag = autoopts-main

=]
Here is an example program that uses the following set of definitions:

@example
[=

 (out-push-new (string-append tmp-dir "/default-test.def" ))

=]AutoGen Definitions options;

prog-name  = default-test;
prog-title = 'Default Option Example';
homerc     = '$$/../share/default-test', '$HOME', '.';
environrc;
long-opts;
gnu-usage;
usage-opt;
version    = '1.0';
main = {
  main-type = shell-process;
};
#define DEBUG_FLAG
#define WARN_FLAG
#define WARN_LEVEL
#define VERBOSE_FLAG
#define VERBOSE_ENUM
#define DRY_RUN_FLAG
#define OUTPUT_FLAG
#define INPUT_FLAG
#define DIRECTORY_FLAG
#define INTERACTIVE_FLAG
#include stdoptions.def
[=

 (texi-escape-encode (out-pop #t))

=]@end example

@noindent
Running a few simple commands on that definition file:

@example
autogen default-test.def
copts="-DTEST_DEFAULT_TEST_OPTS `autoopts-config cflags`"
lopts="`autoopts-config ldflags`"
cc -o default-test $@{copts@} default-test.c $@{lopts@}
@end example

@noindent
Yields a program which, when run with @file{--help}, prints out:

@example
[= (out-push-new) \=]
set -x
log_file=${tmp_dir}/ao-doc-log
exec 7>&2 ; exec 2>> ${log_file}
TOPDIR=`cd ${top_builddir} >/dev/null ; pwd`
OPTDIR=${TOPDIR}/autoopts

{
  cd ${tmp_dir}
  chmod 666 *.def
  echo 'config-header = "config.h";' >> default-test.def
  HOME=${tmp_dir} run_ag default-test.def
  test -f default-test.c || die 'NO default-test.c PROGRAM'

  opts="-o default-test -DTEST_DEFAULT_TEST_OPTS ${INCLUDES}"
  ${CC:-cc} ${CFLAGS} ${opts} default-test.c ${LIBS} || \
    rm -f ./default-test

  test -x ./default-test || {
    exec 2>&7
    fail_text=`cat $log_file`$'\n\nprogram:\n'`cat default-test.c`
    die 'NO default-test EXECUTABLE
        '"$fail_text"'
        === END FAIL TEXT'
  }
} >&2

HOME=${tmp_dir} ${tmp_dir}/default-test --help | \
   sed 's,	,        ,g;s,\([@{}]\),@\1,g'

exec 2>&7 7>&-
[=
 (shell (out-pop #t))
=]
@end example
[=

INVOKE  get-text tag = autoopts-api

=]
[=`

f=../autoopts/libopts.texi
test -f $f || {
  f=${top_srcdir}/autoopts/libopts.texi
  test -f $f || die "Cannot locate libopts.texi in $f"
}
cat $f

`=]
[=

INVOKE  get-text tag = "autoopts-data"

=]
@noindent
Doing so with getdefs' option definitions yields this sample-getdefsrc file.
I tend to be wordy in my @code{doc} attributes:

@example
[= (texi-escape-encode (shell "
  cd ${tmp_dir}
  run_ag -Trc-sample.tpl ${top_srcdir}/getdefs/opts.def >/dev/null
  test -f sample-getdefsrc || die did not create sample-getdefsrc
  cat sample-getdefsrc
" )) =]
@end example
[=

INVOKE get-text tag = "ao-data1"

=]
@example[=
(out-push-new (string-append tmp-dir "/hello.c"))

\=]

#include <config.h>
#include <sys/types.h>
#include <stdio.h>
#include <pwd.h>
#include <string.h>
#ifdef   HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <autoopts/options.h>
int main(int argc, char ** argv) {
  char const * greeting = "Hello";
  char const * greeted  = "World";
  tOptionValue const * pOV = configFileLoad("hello.conf");

  if (pOV != NULL) {
    const tOptionValue* pGetV = optionGetValue(pOV, "greeting");

    if (  (pGetV != NULL)
       && (pGetV->valType == OPARG_TYPE_STRING))
      greeting = strdup(pGetV->v.strVal);

    pGetV = optionGetValue(pOV, "personalize");
    if (pGetV != NULL) {
      struct passwd * pwe = getpwuid(getuid());
      if (pwe != NULL)
        greeted = strdup(pwe->pw_gecos);
    }

    optionUnloadNested(pOV); /* deallocate config data */
  }
  printf("%s, %s!\n", greeting, greeted);
  return 0;
}
[= (texi-escape-encode (out-pop #t)) \=]
@end example

@noindent
With that text in a file named ``hello.c'', this short script:

@example
cc -o hello hello.c `autoopts-config cflags ldflags`
./hello
echo 'greeting Buzz off' > hello.conf
./hello
echo personalize > hello.conf
./hello
@end example

@noindent
will produce the following output:

@example
[= (texi-escape-encode (shell "
cd ${tmp_dir}
${CC:-cc} -o hello hello.c ${CFLAGS} ${LIBS} ${LDFLAGS} || \
  die cannot compile hello
./hello
echo 'greeting Buzz off' > hello.conf
./hello
echo personalize > hello.conf
./hello"
)) =]
@end example
[=

INVOKE  get-text tag = "ao-data2"

=]
[=

 (out-push-new)

=](
    exec 4>&2 2> ${tmp_dir}/err-test-log.txt
    BASH_XTRACEFD=2
    PS4='>etl> '
    set -x
    cd ${top_builddir}/autoopts/test || \
        die "cannot cd into ${top_builddir}/autoopts/test"
    VERBOSE=true AUTOGEN_TRACE=every AUTOGEN_TRACE_OUT=">>$PWD/ag-log.txt"
    export VERBOSE AUTOGEN_TRACE AUTOGEN_TRACE_OUT
    ${MAKE:-make} check TESTS=errors.test 1>&2 || :
    if test ! -x errors-testd/errors
    then
      exec 2>&4
      cat ${tmp_dir}/err-test-log.txt >&2
      die "no error usage in $PWD/errors-testd"
    fi
    cat <<-EOF

	Here is the usage output example from AutoOpts error handling
	tests.  The option definition has argument reordering enabled:

	@example
EOF

    ./errors-testd/errors -? | sed 's,	,        ,g;s,\([@{}]\),@\1,g'
    cmd='errors operand1 -s first operand2 -X -- -s operand3'
    cat <<-EOF
	@end example

	Using the invocation,
	@example
	  test-${cmd}
	@end example
	you get the following output for your shell script to evaluate:

	@example
	`./errors-testd/${cmd}`
	@end example
EOF
)[=

(shell (out-pop #t))

=]
@node script-parser
@subsection Parsing with a Portable Script

If you had used @code{test-main = optionParseShell} instead, then you can,
at this point, merely run the program and it will write the parsing
script to standard out.  You may also provide this program with command
line options to specify the shell script file to create or edit, and you
may specify the shell program to use on the first shell script line.
That program's usage text would look something like the following
and the script parser itself would be very verbose:

@example
[= `

log=${tmp_dir}/../genshellopt.log

(
  set -x
  opts="-o genshellopt -DTEST_GETDEFS_OPTS ${INCLUDES}"
  exec 3> ${tmp_dir}/genshellopt.def
  cat ${top_srcdir}/getdefs/opts.def >&3
  echo "test_main = 'optionParseShell';" >&3
  echo 'config-header = "config.h";' >&3
  exec 3>&-

  cd ${tmp_dir}
  HOME='' run_ag -t40 genshellopt.def
  test $? -eq 0 || die "autogen failed to create genshellopt.c - See ${log}"

  ${CC} ${CFLAGS} ${opts} genshellopt.c ${LIBS}
  test $? -eq 0 || die "could not compile genshellopt.c - See ${log}"
) > ${log} 2>&1

test -x ${tmp_dir}/genshellopt || \
  die "NO GENSHELLOPT PROGRAM - See ${log}"

${tmp_dir}/genshellopt --help > ${tmp_dir}/genshellopt.hlp

${tmp_dir}/genshellopt -o ${tmp_dir}/genshellopt.sh || \
  die cannot create ${tmp_dir}/genshellopt.sh

sedcmd='s,\t,        ,g;s,\\([@{}]\\),@\\1,g'
sed "${sedcmd}" ${tmp_dir}/genshellopt.hlp
cat <<- \_EOF_
	@end example

	@noindent
	Resulting in the following script:
	@example
	_EOF_

sed "${sedcmd}" ${tmp_dir}/genshellopt.sh

` =]
@end example
[=

INVOKE  get-text tag = autoinfo

=]