Skip to content

Commit

Permalink
✅ mommy improves her input validation~
Browse files Browse the repository at this point in the history
  • Loading branch information
FWDekker committed Feb 15, 2024
1 parent c4e3925 commit e1947ff
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 101 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
# Changelog
## [unreleased]
### added
* improved input validation
* improved links (back and forth from toc) in readme


## [1.3.0] -- 2024-01-10
### added
* 🪹 mommy now supports newlines in templates using `%%N%%`~ ([#58](https://github.com/FWDekker/mommy/issues/58)) ([#82](https://github.com/FWDekker/mommy/issues/82))
Expand Down
89 changes: 53 additions & 36 deletions src/main/sh/mommy
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,22 @@ MOMMY_FORBIDDEN_WORDS=""
MOMMY_IGNORED_STATUSES="130"


## Input validation
# Writes whitespace-concatenated input arguments to stderr and exits.
die() { echo "$*" >&2; exit 1; }

# Dies if `$OPTARG` is an empty string.
require_arg() { if [ -z "$OPTARG" ]; then die "mommy is missing the argument for option '$OPT'~"; fi }

# Dies if `$OPTARG` is not a non-negative integer.
require_int() {
case "$OPTARG" in
''|*[!0-9]*) die "mommy expected the argument for option '$OPT' to be an integer, but it was '$OPTARG'~" ;;
*) ;;
esac
}


## Lists
# A list is a collection of entries. Entries are separated by a forward slash (`/`) or by a newline. Entries containing
# only whitespace or starting with a `#` are considered comments and are ignored. If a line starts with a `#`, all `/`
Expand Down Expand Up @@ -121,7 +137,7 @@ escape_sed_replacement() {
}


## Functions
## Templates
# Prints `$2`, but with color depending on `$1`. If `$1` equals `lolcat`, stdin is piped to `lolcat`. If `$1` is empty,
# or color is not enabled in the terminal, stdin is printed normally. Otherwise, `$1` is used as the xterm color.
color_echo() {
Expand Down Expand Up @@ -197,64 +213,65 @@ fill_template() {


## Read options
MOMMY_OPT_HELP=""
MOMMY_OPT_VERSION=""
MOMMY_OPT_TARGET="2"
MOMMY_OPT_CONFIG_FILE="${MOMMY_OPT_CONFIG_FILE:-"${XDG_CONFIG_HOME:-"$HOME/.config"}/mommy/config.sh"}"
MOMMY_OPT_EVAL=""
MOMMY_OPT_STATUS=""

while getopts ":hv1c:e:s:-:" OPTION; do
# Cheap workaround for long options without arguments
if [ "$OPTION" = "-" ]; then
OPTION="$OPTARG"
opt_help=""
opt_version=""
opt_target="2"
opt_config="${opt_config:-"${XDG_CONFIG_HOME:-"$HOME/.config"}/mommy/config.sh"}"
opt_eval=""
opt_status=""

while getopts ":hv1c:e:s:-:" OPT; do
# Cheap workaround for long options, cf. https://stackoverflow.com/a/28466267
if [ "$OPT" = "-" ]; then
OPT="${OPTARG%%=*}"
OPTARG="${OPTARG#$OPT}"
OPTARG="${OPTARG#=}"
fi

# shellcheck disable=SC2214 # Handled by workaround
case "$OPTION" in
h|help) MOMMY_OPT_HELP="1" ;;
v|version) MOMMY_OPT_VERSION="1" ;;
1) MOMMY_OPT_TARGET="1" ;;
c) MOMMY_OPT_CONFIG_FILE="$OPTARG" ;;
e) MOMMY_OPT_EVAL="$OPTARG" ;;
s) MOMMY_OPT_STATUS="$OPTARG" ;;
?) echo "mommy does not know option -$OPTARG~" >&2; exit 1 ;;
*) echo "mommy does not know option --$OPTION~" >&2; exit 1 ;;
case "$OPT" in
h|help) opt_help="1" ;;
v|version) opt_version="1" ;;
1) opt_target="1" ;;
c|config) require_arg; opt_config="$OPTARG" ;;
e|eval) require_arg; opt_eval="$OPTARG" ;;
s|status) require_arg; require_int; opt_status="$OPTARG" ;;
:) die "mommy's last option is missing its argument~" ;;
?) die "mommy doesn't know option -$OPTARG~" ;;
*) die "mommy doesn't know option --$OPT~" ;;
esac
done

shift "$((OPTIND - 1))"


## Load configuration
# shellcheck disable=SC1090 # User-defined target
[ -r "$MOMMY_OPT_CONFIG_FILE" ] && . "$MOMMY_OPT_CONFIG_FILE"
# shellcheck source=/dev/null # User-defined target
[ -f "$opt_config" ] && [ -r "$opt_config" ] && . "$opt_config"


## Output
if [ -n "$MOMMY_OPT_HELP" ]; then
if [ -n "$opt_help" ]; then
man mommy

status="$?"
if [ "$status" -ne 0 ]; then
printf "%s" \
"oops! " \
"mommy couldn't find the help page. " \
"but you can visit https://github.com/FWDekker/mommy/blob/%%VERSION_NUMBER%%/README.md for more " \
"information~" \
"$n" >&2
die "oops!" \
"mommy couldn't find the help page." \
"but you can visit https://github.com/FWDekker/mommy/blob/%%VERSION_NUMBER%%/README.md for more" \
"information~"
fi
exit "$status"
elif [ -n "$MOMMY_OPT_VERSION" ]; then
elif [ -n "$opt_version" ]; then
echo "mommy, v%%VERSION_NUMBER%%, %%VERSION_DATE%%"
exit 0
else
# Run command
if [ -n "$MOMMY_OPT_EVAL" ]; then
(eval "$MOMMY_OPT_EVAL")
if [ -n "$opt_eval" ]; then
(eval "$opt_eval")
command_exit_code="$?"
elif [ -n "$MOMMY_OPT_STATUS" ]; then
command_exit_code="$MOMMY_OPT_STATUS"
elif [ -n "$opt_status" ]; then
command_exit_code="$opt_status"
else
("$@")
command_exit_code="$?"
Expand All @@ -281,7 +298,7 @@ else
"$MOMMY_SUFFIX" "$MOMMY_CAPITALIZE")"

# Output template
case "$MOMMY_OPT_TARGET" in
case "$opt_target" in
1) color_echo "$color" "$response" >&1 ;;
2) color_echo "$color" "$response" >&2 ;;
esac
Expand Down
4 changes: 3 additions & 1 deletion src/test/helper/spec_helper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ export MOMMY_EXEC
export MOMMY_TMP_DIR


## Constants
## Constants and helpers
export n="
"

strip_opt() { echo "$1" | sed "s/[-= ]//g"; }


## Hooks
spec_helper_configure() {
Expand Down
27 changes: 9 additions & 18 deletions src/test/sh/integration_spec.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,40 +38,31 @@ Describe "integration of mommy with other programs"
man_before_each() {
unset MANPATH # Required on Windows
if [ "$MOMMY_SYSTEM" != "1" ]; then
export MANPATH="$(readlink -f "$(pwd)/../../main/man/")"
MANPATH="$(readlink -f "$(pwd)/../../main/man/")"
export MANPATH
fi
}
BeforeEach "man_before_each"


It "outputs help information using -h"
When run "$MOMMY_EXEC" -h
The word 1 of output should equal "mommy(1)"
The status should be success
End

It "outputs help information using --help"
When run "$MOMMY_EXEC" --help
The word 1 of output should equal "mommy(1)"
The status should be success
End
Parameters:value "-h" "--help"

It "outputs help information even when -h is not the first option"
When run "$MOMMY_EXEC" -s 432 -h
It "outputs help information using $1"
When run "$MOMMY_EXEC" "$1"
The word 1 of output should equal "mommy(1)"
The status should be success
End

It "outputs help information even when --help is not the first option"
When run "$MOMMY_EXEC" -s 221 --help
It "outputs help information even when $1 is not the first option"
When run "$MOMMY_EXEC" -s 432 "$1"
The word 1 of output should equal "mommy(1)"
The status should be success
End

It "outputs a link to github if the manual page could not be found when using -h"
It "outputs a link to github if the manual page could not be found when using $1"
export MANPATH="/invalid-path"

When run "$MOMMY_EXEC" -h
When run "$MOMMY_EXEC" "$1"
The output should equal ""
The error should include "github.com"
The status should be failure
Expand Down
Loading

0 comments on commit e1947ff

Please sign in to comment.