From a5ec7dd7404029980fe2dd02b074775e1baaea9d Mon Sep 17 00:00:00 2001 From: Tyler Akins Date: Fri, 3 Nov 2017 16:34:08 -0500 Subject: [PATCH] I think this finally implements arguments to functions This closes #18. --- mo | 47 ++++++++++++++++++++++++++++++++---- tests/function-args.env | 16 ++++++++++++ tests/function-args.expected | 4 +++ tests/function-args.template | 4 +++ 4 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 tests/function-args.env create mode 100644 tests/function-args.expected create mode 100644 tests/function-args.template diff --git a/mo b/mo index dbf61a7..51b3498 100755 --- a/mo +++ b/mo @@ -115,6 +115,24 @@ 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 + + printf -v moCommand "%q %q %s" "$1" "$2" "$3" + eval "$moCommand" +} + + # Internal: Scan content until the right end tag is found. Creates an array # with the following members: # @@ -567,7 +585,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 +603,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,7 +618,7 @@ 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 @@ -658,11 +683,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 +706,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 +799,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 +807,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/tests/function-args.env b/tests/function-args.env new file mode 100644 index 0000000..ac98972 --- /dev/null +++ b/tests/function-args.env @@ -0,0 +1,16 @@ +name=Willy + +pipeTo() { + echo -n "$1" | "$2" +} + +testArgs() { + printf "%d" "$#" + + # Remove content. Note that when zero arguments are passed, this + # line does nothing and $1 will still be the content. + shift + + # Display all arguments + printf " %q" "$@" +} diff --git a/tests/function-args.expected b/tests/function-args.expected new file mode 100644 index 0000000..30eed40 --- /dev/null +++ b/tests/function-args.expected @@ -0,0 +1,4 @@ +No args: 1 '' - done +One arg: 2 one - done +Getting name in a string: 2 The\ name\ is\ Willy - done +Reverse this: edcba diff --git a/tests/function-args.template b/tests/function-args.template new file mode 100644 index 0000000..889dfb8 --- /dev/null +++ b/tests/function-args.template @@ -0,0 +1,4 @@ +No args: {{testArgs}} - done +One arg: {{testArgs one}} - done +Getting name in a string: {{testArgs "The name is $name"}} - done +Reverse this: {{#pipeTo rev}}abcde{{/pipeTo}}