Skip to content

Commit

Permalink
Merge pull request #60 from matejak/manpages
Browse files Browse the repository at this point in the history
Enable generation of manpages
  • Loading branch information
matejak authored Jan 20, 2019
2 parents 3c80fc3 + 2e2ec04 commit a731a40
Show file tree
Hide file tree
Showing 20 changed files with 432 additions and 107 deletions.
2 changes: 2 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ New features:
* Allow argbash and argbash-init to be run from symbolic links.
* Allow scripts generated by argbash-init with complete separation (`-s -s`) to be run from a symbolic link.
* Double quotes in help messages are escaped (fixes #61).
* Introduced the `ARG_VERSION_AUTO` macro.
* Enabled manpage output consumable by the `rst2man` utility.


2.7.1 (2018-08-15)
Expand Down
49 changes: 24 additions & 25 deletions bin/argbash
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,22 @@
# SC2016: Expressions don't expand in single quotes, use double quotes for that.
# SC2059 Don't use variables in the printf format string.

version=2.7.1


# DEFINE_SCRIPT_DIR()
# ARG_POSITIONAL_SINGLE([input],[The input template file (pass '-' for stdin)])
# ARG_OPTIONAL_SINGLE([output],[o],[Name of the output file (pass '-' for stdout)],[-])
# ARG_OPTIONAL_SINGLE([type],[t],[Output type to generate],[bash-script])
# ARG_OPTIONAL_BOOLEAN([library],[],[Whether the input file if the pure parsing library.])
# ARG_OPTIONAL_SINGLE([strip],[],[Determines what to have in the output.],[none])
# ARG_OPTIONAL_BOOLEAN([library],[],[Whether the input file if the pure parsing library])
# ARG_OPTIONAL_SINGLE([strip],[],[Determines what to have in the output],[none])
# ARG_OPTIONAL_BOOLEAN([check-typos],[],[Whether to check for possible argbash macro typos],[on])
# ARG_OPTIONAL_BOOLEAN([commented],[c],[Commented mode - include explanatory comments with the parsing code],[off])
# ARG_OPTIONAL_REPEATED([search],[I],[Directories to search for the wrapped scripts (directory of the template will be added to the end of the list)],["."])
# ARG_OPTIONAL_SINGLE([debug],[],[(developer option) Tell autom4te to trace a macro])
# ARG_TYPE_GROUP_SET([content],[content],[strip],[none,user-content,all])
# ARG_TYPE_GROUP_SET([type],[type],[type],[bash-script,posix-script,completion,docopt])
# ARG_TYPE_GROUP_SET([type],[type],[type],[bash-script,posix-script,manpage,manpage-defs,completion,docopt])
# ARG_DEFAULTS_POS([])
# ARG_VERSION([echo "argbash v$version"])
# ARG_HELP([Argbash is an argument parser generator for Bash.])
# ARG_VERSION_AUTO([_ARGBASH_VERSION])

# ARGBASH_GO()
# needed because of Argbash --> m4_ignore([
Expand Down Expand Up @@ -54,18 +52,18 @@ content()

type()
{
local _allowed=("bash-script" "posix-script" "completion" "docopt") _seeking="$1"
local _allowed=("bash-script" "posix-script" "manpage" "manpage-defs" "completion" "docopt") _seeking="$1"
for element in "${_allowed[@]}"
do
test "$element" = "$_seeking" && echo "$element" && return 0
done
die "Value '$_seeking' (of argument '$2') doesn't match the list of allowed values: 'bash-script', 'posix-script', 'completion' and 'docopt'" 4
die "Value '$_seeking' (of argument '$2') doesn't match the list of allowed values: 'bash-script', 'posix-script', 'manpage', 'manpage-defs', 'completion' and 'docopt'" 4
}


begins_with_short_option()
{
local first_option all_short_options='otcIvh'
local first_option all_short_options='otcIhv'
first_option="${1:0:1}"
test "$all_short_options" = "${all_short_options/$first_option/}" && return 1 || return 0
}
Expand All @@ -87,20 +85,20 @@ _arg_debug=
print_help()
{
printf '%s\n' "Argbash is an argument parser generator for Bash."
printf 'Usage: %s [-o|--output <arg>] [-t|--type <type>] [--(no-)library] [--strip <content>] [--(no-)check-typos] [-c|--(no-)commented] [-I|--search <arg>] [--debug <arg>] [-v|--version] [-h|--help] <input>\n' "$0"
printf 'Usage: %s [-o|--output <arg>] [-t|--type <type>] [--(no-)library] [--strip <content>] [--(no-)check-typos] [-c|--(no-)commented] [-I|--search <arg>] [--debug <arg>] [-h|--help] [-v|--version] <input>\n' "$0"
printf '\t%s\n' "<input>: The input template file (pass '-' for stdin)"
printf '\t%s\n' "-o, --output: Name of the output file (pass '-' for stdout) (default: '-')"
printf '\t%s\n' "-t, --type: Output type to generate (default: 'bash-script')"
printf '\t%s\n' "--library, --no-library: Whether the input file if the pure parsing library. (off by default)"
printf '\t%s\n' "--strip: Determines what to have in the output. (default: 'none')"
printf '\t%s\n' "--library, --no-library: Whether the input file if the pure parsing library (off by default)"
printf '\t%s\n' "--strip: Determines what to have in the output (default: 'none')"
printf '\t%s\n' "--check-typos, --no-check-typos: Whether to check for possible argbash macro typos (on by default)"
printf '\t%s\n' "-c, --commented, --no-commented: Commented mode - include explanatory comments with the parsing code (off by default)"
printf '\t%s' "-I, --search: Directories to search for the wrapped scripts (directory of the template will be added to the end of the list) (default array elements:"
printf " '%s'" "."
printf ')\n'
printf '\t%s\n' "--debug: (developer option) Tell autom4te to trace a macro (no default)"
printf '\t%s\n' "-v, --version: Prints version"
printf '\t%s\n' "-h, --help: Prints help"
printf '\t%s\n' "-v, --version: Prints version"
}


Expand Down Expand Up @@ -180,14 +178,6 @@ parse_commandline()
--debug=*)
_arg_debug="${_key##--debug=}"
;;
-v|--version)
echo "argbash v$version"
exit 0
;;
-v*)
echo "argbash v$version"
exit 0
;;
-h|--help)
print_help
exit 0
Expand All @@ -196,6 +186,14 @@ parse_commandline()
print_help
exit 0
;;
-v|--version)
printf '%s %s\n\n%s\n' "argbash" "2.7.1" 'Argbash is an argument parser generator for Bash.'
exit 0
;;
-v*)
printf '%s %s\n\n%s\n' "argbash" "2.7.1" 'Argbash is an argument parser generator for Bash.'
exit 0
;;
*)
_last_positional="$1"
_positionals+=("$_last_positional")
Expand Down Expand Up @@ -295,14 +293,15 @@ interpret_error()
# $2: The original intended output file
define_file_metadata()
{
local _defines='' _input_dirname _output_dirname
local _defines='' _intended_destination="$ARGBASH_INTENDED_DESTINATION" _input_dirname _output_dirname
test -n "$_intended_destination" || _intended_destination="$2"

_input_dirname="$(dirname "$1")"
test "$1" != '-' && _defines="${_defines}m4_define([INPUT_BASENAME], [[$(basename "$1")]])"
_defines="${_defines}m4_define([INPUT_ABS_DIRNAME], [[$(cd "$_input_dirname" && pwd)]])"

_output_dirname="$(dirname "$2")"
test "$2" != '-' && _defines="${_defines}m4_define([OUTPUT_BASENAME], [[$(basename "$2")]])"
_output_dirname="$(dirname "$_intended_destination")"
test "$_intended_destination" != '-' && _defines="${_defines}m4_define([OUTPUT_BASENAME], [[$(basename "$_intended_destination" "$SPURIONS_OUTPUT_SUFFIX")]])"
_defines="${_defines}m4_define([OUTPUT_ABS_DIRNAME], [[$(cd "$_output_dirname" && pwd)]])"
printf "%s" "$_defines"
}
Expand Down Expand Up @@ -461,7 +460,7 @@ then
# match against suspicious, then inverse match against correct stuff:
# #<optional whitespace>\(allowed\|another allowed\|...\)<optional whitespace><opening bracket <or> end of line>
# Then, extract all matches (assumed to be alnum chars + '_') from grep and put them in the error msg.
grep_output="$(printf "%s" "$output" | grep '^#\s*\(ARG_\|ARGBASH\)' | grep -v '^#\s*\(ARGBASH_SET_INDENT\|ARG_OPTIONAL_SINGLE\|ARG_VERSION\|ARG_HELP\|ARG_OPTIONAL_INCREMENTAL\|ARG_OPTIONAL_REPEATED\|ARG_VERBOSE\|ARG_OPTIONAL_BOOLEAN\|ARG_OPTIONAL_ACTION\|ARG_POSITIONAL_SINGLE\|ARG_POSITIONAL_INF\|ARG_POSITIONAL_MULTI\|ARG_POSITIONAL_DOUBLEDASH\|ARG_OPTION_STACKING\|ARG_RESTRICT_VALUES\|ARG_DEFAULTS_POS\|ARG_LEFTOVERS\|ARGBASH_WRAP\|INCLUDE_PARSING_CODE\|DEFINE_SCRIPT_DIR\|ARGBASH_SET_DELIM\|ARGBASH_GO\|ARGBASH_PREPARE\|ARG_TYPE_GROUP\|ARG_TYPE_GROUP_SET\|ARG_USE_ENV\|ARG_USE_PROG\)\s*\((\|$\)' | sed -e 's/#\s*\([[:alnum:]_]*\).*/\1 /' | tr -d '\n\r')"
grep_output="$(printf "%s" "$output" | grep '^#\s*\(ARG_\|ARGBASH\)' | grep -v '^#\s*\(ARGBASH_SET_INDENT\|ARG_OPTIONAL_SINGLE\|ARG_VERSION\|ARG_VERSION_AUTO\|ARG_HELP\|ARG_OPTIONAL_INCREMENTAL\|ARG_OPTIONAL_REPEATED\|ARG_VERBOSE\|ARG_OPTIONAL_BOOLEAN\|ARG_OPTIONAL_ACTION\|ARG_POSITIONAL_SINGLE\|ARG_POSITIONAL_INF\|ARG_POSITIONAL_MULTI\|ARG_POSITIONAL_DOUBLEDASH\|ARG_OPTION_STACKING\|ARG_RESTRICT_VALUES\|ARG_DEFAULTS_POS\|ARG_LEFTOVERS\|ARGBASH_WRAP\|INCLUDE_PARSING_CODE\|DEFINE_SCRIPT_DIR\|ARGBASH_SET_DELIM\|ARGBASH_GO\|ARGBASH_PREPARE\|ARG_TYPE_GROUP\|ARG_TYPE_GROUP_SET\|ARG_USE_ENV\|ARG_USE_PROG\)\s*\((\|$\)' | sed -e 's/#\s*\([[:alnum:]_]*\).*/\1 /' | tr -d '\n\r')"
test -n "$grep_output" && die "Your script contains possible misspelled Argbash macros: $grep_output" 1
fi
if test "$outfname" != '-'
Expand Down
5 changes: 5 additions & 0 deletions doc/Makefile.pieces
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ STATICDIR = _static
NUL =

PIECES = \
$(STATICDIR)/argbash-version.txt \
$(STATICDIR)/index_script-create.txt \
$(STATICDIR)/index_script-help.txt \
$(STATICDIR)/index_script-output.txt \
Expand Down Expand Up @@ -46,7 +47,11 @@ define CMD_OUT_LOCAL
./$< $(FLAGS) >> $@
endef

$(STATICDIR)/argbash-version.txt:
$(AB) --version > "$@"

A_INIT_ARGS = --pos positional-arg --opt option --opt-bool print

$(STATICDIR)/minimal_init-create.txt: $(AB_INIT)
$(eval FLAGS := $(A_INIT_ARGS) $(MINIMAL_INIT_CREATE))
$(CMD_OUT)
Expand Down
3 changes: 3 additions & 0 deletions doc/_static/argbash-version.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
argbash 2.7.1

Argbash is an argument parser generator for Bash.
8 changes: 5 additions & 3 deletions doc/_static/wrapper-output-action.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,24 @@ Contents of '../src' matching '*.m4':
argbash-lib.m4: 0 kiB
argbash.m4: 10 kiB
argument_value_types.m4: 5 kiB
collectors.m4: 18 kiB
collectors.m4: 20 kiB
constants.m4: 0 kiB
default_settings.m4: 0 kiB
docopt.m4: 2 kiB
env_vars.m4: 1 kiB
function_generators.m4: 7 kiB
list.m4: 5 kiB
output-bash-script.m4: 1 kiB
output-completion.m4: 4 kiB
output-docopt.m4: 2 kiB
output-docopt.m4: 0 kiB
output-manpage.m4: 1 kiB
output-posix-script.m4: 3 kiB
output-strip-all.m4: 0 kiB
output-strip-none.m4: 0 kiB
output-strip-user-content.m4: 0 kiB
progs.m4: 2 kiB
stuff.m4: 44 kiB
utilities.m4: 9 kiB
utilities.m4: 10 kiB
value_validators.m4: 5 kiB
Contents of '../resources/examples' matching '*.m4':
minimal.m4: 0 kiB
Expand Down
17 changes: 17 additions & 0 deletions doc/guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,23 @@ Special arguments

ARG_VERSION([code to execute when specified])

* Enhanced version argument (a special case of an action argument):
::

ARG_VERSION_AUTO([version number or macro containing it])

The macro will take it's first argument, expands it, and treats it as a version number.
This allows you to use a quoted macro containing the version number as the first argument.
Then, it attempts to detect the basename of the generated script and outputs a version message out of those two.

If the ``ARG_HELP([MSG], ...)`` macro has been used before, it also outputs the ``MSG`` below the program name --- version pair.

For example, for argbashm, it yields

.. literalinclude:: _static/argbash-version.txt
:language: text


* Verbose argument (a special case of a repeated argument):
::

Expand Down
24 changes: 24 additions & 0 deletions doc/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@ POSIX script posix-script none
POSIX script parsing section posix-script user-content
Bash completion completion all
docopt help message docopt all
manpage template manpage all
manpage template definitions manpage-defs all
============================ ======================= ==========================


Expand Down Expand Up @@ -296,6 +298,7 @@ Typically, you generate bash completion ``my-script.sh`` from the generated scri
and you move the created completion file ``my-script.sh`` to ``/etc/bash_completion.d/`` directory.


.. _docopt_output:

Docopt help message
Expand All @@ -315,6 +318,27 @@ Typically, you generate docopt output to the standard output from the generated
$ argbash my-script --type docopt --strip all
Manpage output
++++++++++++++

Argbash can generate source for the manual page for your script.
There are two files in the process --- the template, and definitions.
Those two files are in the `reStructuredText <http://docutils.sourceforge.net/rst.html>`_ format, and the template is supposed to be processed by the `rst2man <http://docutils.sourceforge.net/sandbox/manpage-writer/rst2man.txt>`_ utility.

The manpage template is supposed to be generated as script's metadata change, definitions are required to be maintained manually, as they are supposed to contain content that is not present in the script.
You can regenerate the template using the ``manpage`` output, while you are probably going to use the ``manpage-defs`` once to get you kickstarted and then continue to maintain it manually.

So given a argbash-powered script or m4 file, your manpage workflow will typically look like this:

.. code-block:: shell-session
$ argbash my-script --type manpage-defs --strip all -o my-script-defs.rst
$ argbash my-script --type manpage --strip all -o my-script.rst
$ vim my-script-defs.rst # Edit the definitions file
$ rst2man my-script.rst > my-script.1
$ man ./my-script.1
.. _api_change:

API changes support
Expand Down
23 changes: 19 additions & 4 deletions resources/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ M4_SRC = \
$(NUL)

M4_SRC += \
../src/output-bash-script.m4 \
../src/output-completion.m4 \
../src/output-docopt.m4 \
../src/output-bash-script.m4 \
../src/output-manpage.m4 \
../src/output-manpage-defs.m4 \
../src/output-posix-script.m4 \
$(NUL)

Expand All @@ -28,6 +30,7 @@ M4_SRC += \
../src/collectors.m4 \
../src/constants.m4 \
../src/default_settings.m4 \
../src/docopt.m4 \
../src/env_vars.m4 \
../src/function_generators.m4 \
../src/list.m4 \
Expand Down Expand Up @@ -56,9 +59,10 @@ EXAMPLES = \
$(NUL)

ROOT_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))
MANPAGE = argbash.1.gz

$(GENPARSE): ../src/argbash.m4 $(M4_SRC)
bash $(GENPARSE) $< -o argbash.temp && mv argbash.temp $@
ARGBASH_INTENDED_DESTINATION="$@" bash $(GENPARSE) $< -o argbash.temp && mv argbash.temp $@

$(A_INIT): ../src/argbash-init.m4 $(GENPARSE)
$(GENPARSE) $< -o $@
Expand Down Expand Up @@ -143,7 +147,15 @@ define-version:
$(eval VERSION_MAJOR := $(shell cut -f 1 -d . ../src/version))
$(eval VERSION_SUFFIX := -$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH))

install: $(GENPARSE) $(A_INIT) $(ARGBASH_TO) $(COMPLETION)
argbash.rst: ../src/argbash.m4 $(ARGBASH_EXEC)
$(ARGBASH_EXEC) --type manpage --strip all -o $@ $<

man: $(MANPAGE)

$(MANPAGE): argbash.rst argbash-defs.rst
rst2man $< | gzip > $@

install: $(GENPARSE) $(A_INIT) $(ARGBASH_TO) $(COMPLETION) $(MANPAGE)
@echo Installing to prefix "'/$(PREFIX)' of root $(ROOT)"
mkdir -p "$(ROOT)/$(PREFIX)/bin"
touch "$(ROOT)/$(PREFIX)/bin/argbash$(VERSION_SUFFIX)"
Expand All @@ -155,6 +167,8 @@ install: $(GENPARSE) $(A_INIT) $(ARGBASH_TO) $(COMPLETION)
cp -p $(A_INIT) "$(ROOT)/$(PREFIX)/bin/argbash-init$(VERSION_SUFFIX)" && chmod a+x "$(ROOT)/$(PREFIX)"/bin/argbash-init$(VERSION_SUFFIX)
chmod a+x "$(ROOT)/$(PREFIX)/bin/argbash$(VERSION_SUFFIX)"
test "$(INSTALL_COMPLETION)" = "no" || (mkdir -p "$(ROOT)/$(BASH_COMPLETION_DIRECTORY)" && mv "$(COMPLETION)" "$(ROOT)/$(BASH_COMPLETION_DIRECTORY)/")
mkdir -p "$(ROOT)/$(PREFIX)/share/man/man1/"
cp -p $(MANPAGE) "$(ROOT)/$(PREFIX)/share/man/man1/"

altpreclean: define-version
$(RM) "$(ROOT)/$(PREFIX)/bin/argbash-$(VERSION_MAJOR).$(VERSION_MINOR)"
Expand All @@ -173,6 +187,7 @@ uninstall:
$(RM) "$(ROOT)/$(PREFIX)"/bin/argbash-*
rmdir "$(ROOT)/$(PREFIXED_LIBDIR)/argbash$(VERSION_SUFFIX)"
$(RM) "$(ROOT)/$(BASH_COMPLETION_DIRECTORY)/$(COMPLETION)"
$(RM) "$(ROOT)/$(PREFIX)/share/man/man1/$(MANPAGE)"

altuninstall: define-version uninstall

Expand All @@ -196,4 +211,4 @@ tag:

# Update using
# grep '^[-a-z]*:' Makefile | cut -f 1 -d ':' | sort | tr '\n' ' '
.PHONY: altinstall altpreclean bootstrap check check-shellcheck define-version develop examples install release tag uninstall unittests version
.PHONY: altinstall altpreclean bootstrap check check-shellcheck define-version develop examples install man release tag uninstall unittests version
34 changes: 34 additions & 0 deletions resources/argbash-defs.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

.. |AUTHOR| replace:: Matěj Týč

.. |MAN_SECTION| replace:: 1

.. |DESCRIPTION| replace::
Argbash is a code generator that typically generates a bash argument-parsing library tailor-made for your script.
It lets you to describe arguments your script should take and then, you can generate the ``bash`` parsing code.
It stays in your script by default, but you can have it generated to a separate file and let ``bash`` to include it in your script for you.
``Argbash`` is very simple to use and the generated code is relatively nice to read.
Moreover, argument definitions stay embedded in the script, so when you need to update the parsing logic, you just re-run the ``argbash`` script on the already generated script.

.. |OPTION_OUTPUT| replace:: \

.. |OPTION_TYPE| replace:: Check out the documentation to learn about all argbash capabilities that are supported.

.. |OPTION_LIBRARY| replace:: This option is deprecated and it is the same as ``--strip user-content``.

.. |OPTION_STRIP| replace::
You can either strip ``none``, which is useful for scripts.
If you strip ``user-content``, you keep the Argbash header.
If you strip ``all``, you will have only the generated content in the result.

.. |OPTION_CHECK_TYPOS| replace:: \

.. |OPTION_COMMENTED| replace:: \

.. |OPTION_SEARCH| replace:: \

.. |OPTION_DEBUG| replace:: \

.. |OPTION_HELP| replace:: \

.. |OPTION_VERSION| replace:: \
5 changes: 5 additions & 0 deletions src/argbash-lib.m4
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ m4_include([collectors.m4])
m4_include([stuff.m4])
m4_include([default_settings.m4])
INCLUDE_ACCORDING_TO_OUTPUT_TYPE(_OUTPUT_TYPE)

dnl These macros that are being undefined are not needed and they present a security threat when exposed during Argbash run
m4_undefine([m4_esyscmd])
m4_undefine([m4_esyscmd_s])
m4_undefine([m4_syscmd])
Loading

0 comments on commit a731a40

Please sign in to comment.