frontend: properly handle option processing

this means that the usage goes from:
 lb {-h|--help|-u|--usage|-v|--version}
 lb COMMAND [OPTIONS]

to:
 lb {-h|--help|-u|--usage|-v|--version}
 lb [FRONTEND_OPTIONS] COMMAND [COMMAND_OPTIONS]

though it is probably not worth is to update the description in the
manpages...? hmm...

so for instance this matters for color control with --color|--no-color
(you already had full control via environment vars). previously you could
do `lb COMMAND --no-color` to turn off colour, only to find that output
at the frontend level was still coloured (the option is processed at the
command context level, not the frontend), so you might try to instead use
`lb --no-color COMMAND`, only to find that this was not supported. Well
now it is, and used at the frontend level will fully control colour output
(after the command is processed anyway).

the full set of common options are thus available (except --force) at the
frontend level, and thus for instance all Echo_*() helpers used in the
frontend will work correctly after args are processed.

furthermore usage like `lb --color --help` will actually work. (not that
color is used there, but this previously would have failed with the
frontend treating the `--color` argument as the command; that's the point!)

Gbp-Dch: Short
This commit is contained in:
Lyndon Brown 2020-03-18 01:51:53 +00:00 committed by Raphaël Hertzog
parent 89e965ccae
commit 6b7c8ed4bf
5 changed files with 100 additions and 46 deletions

View File

@ -19,25 +19,14 @@ DESCRIPTION="Utility to build live systems"
HELP=""
USAGE="lb {clean|config|build}"
case "${1}" in
-h|--help)
if [ $(which man) ]; then
man lb
else
Usage
fi
exit 0
;;
# Handle options up to any command
# We replace the arg set with any remaining args on return
Arguments frontend "${@}"
eval set -- "${REMAINING_ARGS}"
""|-u|--usage)
if [ -z "${1}" ]; then
Usage
;;
-v|--version)
echo "${VERSION}"
exit 0
;;
esac
fi
COMMAND="${1}"
shift

View File

@ -11,23 +11,84 @@
Arguments ()
{
# This function is used for handling arguments both at the frontend (`lb`)
# level and at the command level, since both accept almost the same basic
# argument set, with little difference in response to them.
#
# We enlist the help of getopt here which takes care of some of the
# intricacies of parsing for us. Note that getopt does not itself
# understand the concept of "command" arguments, and the behaviour of it
# shuffling non-options (those arguments that are not options or option
# values) to the end of the argument list would present a difficulty, if it
# were not for the fact that you can control this behaviour with use of the
# `POSIXLY_CORRECT` environment variable; setting this variable causes
# getopt to stop parsing arguments once it encounters the first non-option,
# treating all remaining arguments as being non-options. Note also that
# getopt always outputs a `--` separator argument between option (including
# option value) arguments and non-option arguments.
#
# At the frontend we need getopt to only parse options up to the point of
# a command. A command as far as getopt is concerned is simply a
# "non-option" argument. Using the above mentioned `POSIXLY_CORRECT`
# environment variable when parsing for the frontend, we can thus have
# getopt process options up to the first non-option, if given, which should
# be our command. We can then pass back any remaining arguments including
# the command argument, for a second command-stage handling. If no command
# is given, this is trivial to handle. If an invalid option is used before
# a command, this is caught by getopt.
#
# When a command is run, it is passed all remaining arguments, with most
# scripts then passing them to this function, with argument parsing then
# occurring in command-context, which just so happens to use almost the same
# set of arguments for most scripts (the config command is a notable
# exception).
#
# It is true that many of the common options have no effect in the frontend
# currently, but some do, such as colour control, and others could do in
# future or during development.
#
# Note, do not worry about options unavailable in frontend mode being
# handled in the case statement, they will never reach there if used for the
# frontend (i.e. before a command), they will result in an invalid option
# error!
local LONGOPTS="breakpoints,color,debug,help,no-color,quiet,usage,verbose,version"
local SHORTOPTS="huv"
local IS_FRONTEND="false"
if [ "${1}" = "frontend" ]; then
shift
IS_FRONTEND="true"
else
LONGOPTS="${LONGOPTS},force"
fi
local GETOPT_ARGS="--name=${PROGRAM} --shell sh --longoptions $LONGOPTS --options $SHORTOPTS"
local ARGUMENTS
local ERR=0
ARGUMENTS="$(getopt --longoptions breakpoints,color,debug,force,help,no-color,quiet,usage,verbose,version --name=${PROGRAM} --options huv --shell sh -- "${@}")" || ERR=$?
if [ "${IS_FRONTEND}" = "true" ]; then
ARGUMENTS="$(export POSIXLY_CORRECT=1; getopt $GETOPT_ARGS -- "${@}")" || ERR=$?
else
ARGUMENTS="$(getopt $GETOPT_ARGS -- "${@}")" || ERR=$?
fi
if [ $ERR -eq 1 ]; then
Echo_error "invalid arguments"
Echo_error "Invalid argument(s)"
exit 1
elif [ $ERR -ne 0 ]; then
Echo_error "getopt failure"
exit 1
fi
# Replace arguments with result of getopt processing (e.g. with non-options shuffled to end)
# Note that this only affects this function's parameter set, not the calling function's or
# calling script's argument set.
eval set -- "${ARGUMENTS}"
while true
do
case "${1}" in
local ARG
for ARG in "$@"; do
case "${ARG}" in
--breakpoints)
_BREAKPOINTS="true"
shift
@ -58,8 +119,16 @@ Arguments ()
;;
-h|--help)
Man
shift
if [ $(which man) ]; then
if [ "${IS_FRONTEND}" = "true" ]; then
man ${PROGRAM}
else
man ${PROGRAM} $(basename ${0})
fi
exit 0
elif [ "${IS_FRONTEND}" = "true" ]; then
Usage
fi
;;
--quiet)
@ -88,9 +157,17 @@ Arguments ()
;;
*)
Echo_error "internal error %s" "${0}"
if [ "${IS_FRONTEND}" = "true" ]; then
# We have handled all frontend options up to what we assume to be a command
break
fi
Echo_error "Internal error, unhandled option: %s" "${ARG}"
exit 1
;;
esac
done
# Return remaining args
# Much more simple than trying to deal with command substitution.
REMAINING_ARGS="$@"
}

View File

@ -25,7 +25,14 @@ Auto_build_config ()
Init_config_data ()
{
# Here we ignore the consumption of option arguments, we would have to return REMAINING_ARGS
# for use in a `set -- $REMAINING_ARGS` (or `eval set -- "$REMAINING_ARGS"`) if we wanted to
# remove them from the set of args for calling scripts to make use of, in which case we might
# as well just move use of the function out of here. Note that currently scripts taking args
# like `$_PASS` do so as the first arg to the script, thus then just ignore any following
# option args (and capture them before arg processing takes place).
Arguments "${@}"
unset REMAINING_ARGS
Read_conffiles $(Common_config_files)
Prepare_config

View File

@ -1,19 +0,0 @@
#!/bin/sh
## live-build(7) - System Build Scripts
## Copyright (C) 2016-2020 The Debian Live team
## Copyright (C) 2006-2015 Daniel Baumann <mail@daniel-baumann.ch>
##
## This program comes with ABSOLUTELY NO WARRANTY; for details see COPYING.
## This is free software, and you are welcome to redistribute it
## under certain conditions; see COPYING for details.
Man ()
{
if [ $(which man) ]
then
man ${PROGRAM} $(basename ${0})
exit 0
fi
}

View File

@ -179,7 +179,7 @@ Local_arguments ()
ARGUMENTS="$(getopt --longoptions ${LONG_OPTIONS} --name="${PROGRAM}" --options a:d:m:k:b:s:c:huv --shell sh -- "${@}")" || ERR=$?
if [ $ERR -eq 1 ]; then
Echo_error "invalid arguments"
Echo_error "Invalid argument(s)"
exit 1
elif [ $ERR -ne 0 ]; then
Echo_error "getopt failure"