diff --git a/bin/is b/bin/is index 8ddd6a5..bd069b3 100755 --- a/bin/is +++ b/bin/is @@ -35,6 +35,11 @@ Conditions: Negation: is not equal VALUE_A VALUE_B + +Optional article: + is not a number VALUE + is an existing PATH + is the file PATH EOF exit fi diff --git a/bin/mo b/bin/mo index 3fd65c2..458472e 100755 --- a/bin/mo +++ b/bin/mo @@ -29,6 +29,10 @@ # Public: Template parser function. Writes templates to stdout. # # $0 - Name of the mo file, used for getting the help message. +# --allow-function-arguments +# - Permit functions in templates to be called with additional +# arguments. This puts template data directly in to the path +# of an eval statement. Use with caution. # --fail-not-set - Fail upon expansion of an unset variable. Default behavior # is to silently ignore and expand into empty string. # --false - Treat "false" as an empty value. You may set the @@ -43,6 +47,12 @@ # # Mo uses the following environment variables: # +# MO_ALLOW_FUNCTION_ARGUMENTS +# - When set to a non-empty value, this allows functions +# referenced in templates to receive additional +# options and arguments. This puts the content from the +# template directly into an eval statement. Use with +# extreme care. # MO_FAIL_ON_UNSET - When set to a non-empty value, expansion of an unset # env variable will be aborted with an error. # MO_FALSE_IS_EMPTY - When set to a non-empty value, the string "false" @@ -74,6 +84,11 @@ mo() ( exit 0 ;; + --allow-function-arguments) + # shellcheck disable=SC2030 + MO_ALLOW_FUNCTION_ARGUMENTS=true + ;; + --fail-not-set) # shellcheck disable=SC2030 MO_FAIL_ON_UNSET=true @@ -115,6 +130,29 @@ mo() ( ) +# Internal: Call a function. +# +# $1 - Function to call +# $2 - Content to pass +# $3 - Additional arguments as a single string +# +# This can be dangerous, especially if you are using tags like +# {{someFunction ; rm -rf / }} +# +# Returns nothing. +moCallFunction() { + local moCommand + + # shellcheck disable=SC2031 + if [[ -n "$MO_ALLOW_FUNCTION_ARGUMENTS" ]]; then + printf -v moCommand "%q %q %s" "$1" "$2" "$3" + eval "$moCommand" + else + "$1" "$2" + fi +} + + # Internal: Scan content until the right end tag is found. Creates an array # with the following members: # @@ -567,7 +605,7 @@ moLoop() { moParse() { # Keep naming variables mo* here to not overwrite needed variables # used in the string replacements - local moBlock moContent moCurrent moIsBeginning moNextIsBeginning moTag + local moArgs moBlock moContent moCurrent moIsBeginning moNextIsBeginning moTag moCurrent=$2 moIsBeginning=$3 @@ -585,6 +623,13 @@ moParse() { # Sets context moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning" moTrimWhitespace moTag "${moTag:1}" + + # Split arguments from the tag name. Arguments are passed to + # functions. + moArgs=$moTag + moTag=${moTag%% *} + moTag=${moTag%%$'\t'*} + moArgs=${moArgs:${#moTag}} moFindEndTag moBlock "$moContent" "$moTag" moFullTagName moTag "$moCurrent" "$moTag" @@ -593,13 +638,13 @@ moParse() { if moIsFunction "$moTag"; then #: Consider piping the output to moGetContent #: so the lambda does not execute in a subshell? - moContent=$($moTag "${moBlock[0]}") + moContent=$(moCallFunction "$moTag" "${moBlock[0]}" "$moArgs") moParse "$moContent" "$moCurrent" false moContent="${moBlock[2]}" elif moIsArray "$moTag"; then eval "moLoop \"\${moBlock[0]}\" \"$moTag\" \"\${!${moTag}[@]}\"" else - moParse "${moBlock[0]}" "$moCurrent" false + moParse "${moBlock[0]}" "$moCurrent" true fi fi @@ -658,11 +703,16 @@ moParse() { moContent="${moTag:1}"'}}'"$moContent" moSplit moContent "$moContent" '}}}' moTrimWhitespace moTag "${moContent[0]}" + moArgs=$moTag + moTag=${moTag%% *} + moTag=${moTag%%$'\t'*} + moArgs=${moArgs:${#moTag}} moFullTagName moTag "$moCurrent" "$moTag" moContent=${moContent[1]} # Now show the value - moShow "$moTag" "$moCurrent" + # Quote moArgs here, do not quote it later. + moShow "$moTag" "$moCurrent" "$moArgs" ;; '&'*) @@ -676,8 +726,14 @@ moParse() { *) # Normal environment variable or function call moStandaloneDenied moContent "${moContent[@]}" + moArgs=$moTag + moTag=${moTag%% *} + moTag=${moTag%%$'\t'*} + moArgs=${moArgs:${#moTag}} moFullTagName moTag "$moCurrent" "$moTag" - moShow "$moTag" "$moCurrent" + + # Quote moArgs here, do not quote it later. + moShow "$moTag" "$moCurrent" "$moArgs" ;; esac @@ -763,6 +819,7 @@ moPartial() { # # $1 - Name of environment variable or function # $2 - Current context +# $3 - Arguments string if $1 is a function # # Returns nothing. moShow() { @@ -770,7 +827,7 @@ moShow() { local moJoined moNameParts if moIsFunction "$1"; then - CONTENT=$($1 "") + CONTENT=$(moCallFunction "$1" "" "$3") moParse "$CONTENT" "$2" false return 0 fi diff --git a/lib/completions b/lib/completions index 538f2a0..372f9b4 160000 --- a/lib/completions +++ b/lib/completions @@ -1 +1 @@ -Subproject commit 538f2a0287bf8d3775e3745e212e27ad29f5003d +Subproject commit 372f9b47e9c528e0516652155308d239abc5b727 diff --git a/lib/gitignore b/lib/gitignore index 9da1b5d..fad7792 160000 --- a/lib/gitignore +++ b/lib/gitignore @@ -1 +1 @@ -Subproject commit 9da1b5d8ce4e009ff627c4fe49a4488b2a3f60d4 +Subproject commit fad779220742a6d54ccfc0c1a0e5b3d820253de6 diff --git a/lib/ohmyzsh b/lib/ohmyzsh index 4cb7307..7cea847 160000 --- a/lib/ohmyzsh +++ b/lib/ohmyzsh @@ -1 +1 @@ -Subproject commit 4cb730773b52a47f2e9ab159a6b1c5185a3a37fb +Subproject commit 7cea8475fbf8e1ba9e665e7740e35182c57bfb6f