[= -*- Mode: texinfo -*- AutoGen5 Template # This file is part of AutoGen. # AutoGen Copyright (C) 1992-2018 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 . =] @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 #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include 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 || { head -n 50000 genshellopt.[ch] 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 =]