mirror of
https://github.com/tests-always-included/mo.git
synced 2025-04-07 01:16:38 +00:00
Implemented function bareword arguments
This commit is contained in:
parent
6610e2a390
commit
f353bc7003
236
mo
236
mo
@ -178,31 +178,39 @@ mo() (
|
||||
#
|
||||
# $1 - Variable for output
|
||||
# $2 - Function to call
|
||||
# $3 - Content to pass
|
||||
# $4 - Additional arguments as a single string
|
||||
# $3 - Current Context
|
||||
# $4 - Content to pass
|
||||
# $5 - Additional arguments as a single string
|
||||
#
|
||||
# This can be dangerous, especially if you are using tags like
|
||||
# {{someFunction ; rm -rf / }}
|
||||
#
|
||||
# Returns nothing.
|
||||
moCallFunction() {
|
||||
local moArgs moContent moFunctionArgs moFunctionResult
|
||||
local moArgs moQuoted moContent MO_FUNCTION_ARGS moFunctionResult
|
||||
|
||||
moTrimWhitespace MO_FUNCTION_ARGS "$5"
|
||||
moSplitArgs MO_FUNCTION_ARGS "$MO_FUNCTION_ARGS"
|
||||
|
||||
moArgs=()
|
||||
moTrimWhitespace moFunctionArgs "$4"
|
||||
for (( m=0; m<${#MO_FUNCTION_ARGS[@]}; m++ )); do
|
||||
MO_FUNCTION_ARGS[$m]="$(moParse "(${MO_FUNCTION_ARGS[$m]})" "$3" "$3" true)"
|
||||
moQuote moQuoted "${MO_FUNCTION_ARGS[$m]}"
|
||||
moArgs+=($moQuoted)
|
||||
done
|
||||
|
||||
# shellcheck disable=SC2031
|
||||
if [[ -n "${MO_ALLOW_FUNCTION_ARGUMENTS-}" ]]; then
|
||||
if [[ ! -n "${MO_ALLOW_FUNCTION_ARGUMENTS-}" ]]; then
|
||||
# Intentionally bad behavior
|
||||
# shellcheck disable=SC2206
|
||||
moArgs=($4)
|
||||
moArgs=()
|
||||
fi
|
||||
|
||||
moContent=$(echo -n "$3" | MO_FUNCTION_ARGS="$moFunctionArgs" eval "$2" "${moArgs[@]}") || {
|
||||
moContent=$(echo -n "$4" | eval "$2" "${moArgs[@]}") || {
|
||||
moFunctionResult=$?
|
||||
# shellcheck disable=SC2031
|
||||
if [[ -n "${MO_FAIL_ON_FUNCTION-}" && "$moFunctionResult" != 0 ]]; then
|
||||
echo "Function '$2' with args (${moArgs[*]+"${moArgs[@]}"}) failed with status code $moFunctionResult"
|
||||
echo "Function '$2' with args (${MO_FUNCTION_ARGS[*]+"${MO_FUNCTION_ARGS[@]}"}) failed with status code $moFunctionResult"
|
||||
exit "$moFunctionResult"
|
||||
fi
|
||||
}
|
||||
@ -316,6 +324,72 @@ moFindString() {
|
||||
}
|
||||
|
||||
|
||||
# Internal: Find the index of the first unescaped backslash
|
||||
#
|
||||
# $1 - Destination variable for the index
|
||||
# $2 - Haystack
|
||||
#
|
||||
# Returns nothing.
|
||||
moFindUnescapedSlash() {
|
||||
local moSlashPos moSlashHaystack moSlashOffset moEscSlashOffset moEscaped
|
||||
|
||||
moSlashHaystack=$2
|
||||
moSlashPos=-2
|
||||
moEscaped=true
|
||||
|
||||
while [[ $moEscaped == true ]] && [[ $moSlashPos -ne ${#2} ]]; do
|
||||
moSlashOffset=$(($moSlashPos + 2))
|
||||
moSlashHaystack="${2:$moSlashOffset}"
|
||||
|
||||
moFindString moSlashPos "$moSlashHaystack" "\\"
|
||||
moSlashPos=$(($moSlashOffset + $moSlashPos))
|
||||
moEscSlashOffset=$(($moSlashPos+1))
|
||||
|
||||
moEscaped=false
|
||||
[[ "${2:$moSlashPos+1:1}" == "\\" ]] && moEscaped=true
|
||||
done
|
||||
|
||||
[[ $moSlashPos -eq ${#2} ]] && moSlashPos=-1
|
||||
local "$1" && moIndirect "$1" "$moSlashPos"
|
||||
}
|
||||
|
||||
|
||||
# Internal: Find the index of an unescaped substring. If not found, sets the
|
||||
# index to -1.
|
||||
#
|
||||
# $1 - Destination variable for the index
|
||||
# $2 - Haystack
|
||||
# $3 - Needle
|
||||
#
|
||||
# Returns nothing.
|
||||
moFindUnescaped() {
|
||||
local moHaystack moPos moOffset moEscPos
|
||||
|
||||
if [[ "$3" == "\\" ]]; then
|
||||
moFindUnescapedSlash "$1" "$2"
|
||||
return 0
|
||||
fi
|
||||
|
||||
moHaystack=$2
|
||||
moPos=-1
|
||||
moEscPos=-2
|
||||
|
||||
while [[ $moPos -ne ${#2} ]] && [[ $moEscPos -ne -1 ]] && [[ $(($moEscPos+1)) -eq $moPos ]]; do
|
||||
moOffset=$(($moPos + 1))
|
||||
moHaystack="${2:$moOffset}"
|
||||
|
||||
moFindString moPos "$moHaystack" "$3"
|
||||
moPos=$(($moOffset + $moPos))
|
||||
|
||||
moFindUnescapedSlash moEscPos "$moHaystack"
|
||||
moEscPos=$(($moOffset + $moEscPos))
|
||||
done
|
||||
|
||||
[[ $moPos -eq ${#2} ]] && moPos=-1
|
||||
local "$1" && moIndirect "$1" "$moPos"
|
||||
}
|
||||
|
||||
|
||||
# Internal: Generate a dotted name based on current context and target name.
|
||||
#
|
||||
# $1 - Target variable to store results
|
||||
@ -432,6 +506,65 @@ moIndentLines() {
|
||||
}
|
||||
|
||||
|
||||
# Internal: Removes escape characters for displaying.
|
||||
#
|
||||
# $1 - Destination variable for the finalized content
|
||||
# $2 - Content to be cleaned
|
||||
#
|
||||
# Returns nothing.
|
||||
moUnescape() {
|
||||
local moUnResult moSlashPos moUnclean
|
||||
|
||||
if [[ "$2" =~ "\\" ]]; then
|
||||
moUnResult=""
|
||||
moUnclean="$2"
|
||||
moSlashPos=-1
|
||||
|
||||
moFindString moSlashPos "$2" "\\"
|
||||
|
||||
while [[ $moSlashPos -gt -1 ]]; do
|
||||
moUnResult+="${moUnclean:0:$moSlashPos}"
|
||||
moUnclean="${moUnclean:$moSlashPos+1}"
|
||||
moFindString moSlashPos "$moUnclean" "\\"
|
||||
done
|
||||
moUnResult+=$moUnclean
|
||||
else
|
||||
moUnResult="$2"
|
||||
fi
|
||||
|
||||
local "$1" && moIndirect "$1" "$moUnResult"
|
||||
}
|
||||
|
||||
|
||||
# Internal: Outputs a string of text with quotes around it, and escapes existing quotes
|
||||
#
|
||||
# $1 - Destination variable
|
||||
# $2 - Content to quote
|
||||
#
|
||||
# Returns nothing.
|
||||
moQuote() {
|
||||
#echo "FOR: $1" >&2
|
||||
#echo "UNQUOTED: >$2<" >&2
|
||||
#echo "QUOTED: >\"${2//\"/\\\"}\"<" >&2
|
||||
local "$1" && moIndirect "$1" "\"${2//\"/\\\"}\""
|
||||
}
|
||||
|
||||
|
||||
# Internal: Removes quotes from a string an unescapes the quotes inside of it
|
||||
#
|
||||
# $1 - Destination variable
|
||||
# $2 - Content to unquote
|
||||
#
|
||||
# Returns nothing.
|
||||
moUnquote() {
|
||||
local moLen moUnquoted
|
||||
moUnquoted="$2"
|
||||
moLen=${#moUnquoted}
|
||||
moUnescape moUnquoted "${moUnquoted:1:$moLen - 2}"
|
||||
local "$1" && moIndirect "$1" "$moUnquoted"
|
||||
}
|
||||
|
||||
|
||||
# Internal: Send a variable up to the parent of the caller of this function.
|
||||
#
|
||||
# $1 - Variable name
|
||||
@ -675,7 +808,7 @@ moLoop() {
|
||||
moParse() {
|
||||
# Keep naming variables mo* here to not overwrite needed variables
|
||||
# used in the string replacements
|
||||
local moArgs moBlock moContent moCurrent moIsBeginning moNextIsBeginning moTag moKey moIsSubExp moOpen moClose
|
||||
local moArgs moBlock moContent moCurrent moIsBeginning moNextIsBeginning moTag moKey moIsSubExp moOpen moClose moParsed
|
||||
|
||||
moCurrent=$2
|
||||
moIsBeginning=$3
|
||||
@ -692,14 +825,12 @@ moParse() {
|
||||
moSplit moContent "$1" "$moOpen" "$moClose"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
while [[ "${#moContent[@]}" -gt 1 ]]; do
|
||||
moTrimWhitespace moTag "${moContent[1]}"
|
||||
moNextIsBeginning=false
|
||||
|
||||
if [[ ! "$moTag" == ">"* ]] && [[ "${moContent[1]}" =~ "(" ]]; then
|
||||
moParsed=$(moParse "${moContent[1]}" "$moCurrent" "$moCurrent" true)
|
||||
if [[ ! "$moTag" == ">"* ]] && [[ "$moTag" =~ "(" ]]; then
|
||||
moParsed=$(moParse "$moTag" "$moCurrent" "$moCurrent" true)
|
||||
moContent[1]="$moParsed"
|
||||
moTrimWhitespace moTag "${moContent[1]}"
|
||||
fi
|
||||
@ -712,11 +843,9 @@ moParse() {
|
||||
|
||||
if [[ $moIsSubExp == true ]]; then
|
||||
moStandaloneDenied moContent "${moContent[@]}"
|
||||
if moTest "$moTag"; then
|
||||
echo -n "true"
|
||||
else
|
||||
echo -n "false"
|
||||
fi
|
||||
echo -n "\""
|
||||
moTest "$moTag" && echo -n "true" || echo -n "false"
|
||||
echo -n "\""
|
||||
else
|
||||
moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning"
|
||||
|
||||
@ -732,7 +861,7 @@ moParse() {
|
||||
if moTest "$moTag"; then
|
||||
# Show / loop / pass through function
|
||||
if moIsFunction "$moTag"; then
|
||||
moCallFunction moContent "$moTag" "${moBlock[0]}" "$moArgs"
|
||||
moCallFunction moContent "$moTag" "$moCurrent" "${moBlock[0]}" "$moArgs"
|
||||
moParse "$moContent" "$moCurrent" false
|
||||
moContent="${moBlock[2]}"
|
||||
elif moIsArray "$moTag"; then
|
||||
@ -759,17 +888,22 @@ moParse() {
|
||||
moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning"
|
||||
;;
|
||||
|
||||
'"'*|"'"*)
|
||||
# Quoted string
|
||||
moUnquote moContent "${moContent[1]}"
|
||||
echo -n "$moContent"
|
||||
moContent=""
|
||||
;;
|
||||
|
||||
'^'*)
|
||||
# Display section if named thing does not exist
|
||||
moTrimWhitespace moTag "${moTag:1}"
|
||||
|
||||
if [[ $moIsSubExp == true ]]; then
|
||||
moStandaloneDenied moContent "${moContent[@]}"
|
||||
if ! moTest "$moTag"; then
|
||||
echo -n "true"
|
||||
else
|
||||
echo -n "false"
|
||||
fi
|
||||
echo -n "\""
|
||||
! moTest "$moTag" && echo -n "true" || echo -n "false"
|
||||
echo -n "\""
|
||||
else
|
||||
moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning"
|
||||
moFindEndTag moBlock "$moContent" "$moTag" "" "$moCurrent"
|
||||
@ -849,7 +983,7 @@ moParse() {
|
||||
moFullTagName moTag "$moCurrent" "$moTag"
|
||||
|
||||
# Quote moArgs here, do not quote it later.
|
||||
moShow "$moTag" "$moCurrent" "$moArgs"
|
||||
moShow "$moTag" "$moCurrent" "$moArgs" $moIsSubExp
|
||||
;;
|
||||
esac
|
||||
|
||||
@ -944,7 +1078,7 @@ moShow() {
|
||||
local moJoined moNameParts moContent
|
||||
|
||||
if moIsFunction "$1"; then
|
||||
moCallFunction moContent "$1" "" "$3"
|
||||
moCallFunction moContent "$1" "$2" "" "$3"
|
||||
moParse "$moContent" "$2" false
|
||||
return 0
|
||||
fi
|
||||
@ -977,6 +1111,7 @@ moShow() {
|
||||
# $2 - String to split
|
||||
# $3 - Starting delimiter
|
||||
# $4 - Ending delimiter (optional)
|
||||
# $5 - Restore delimiter (optional)
|
||||
#
|
||||
# Returns nothing.
|
||||
moSplit() {
|
||||
@ -985,7 +1120,7 @@ moSplit() {
|
||||
moResult=( "$2" )
|
||||
moClosePos=-1
|
||||
|
||||
moFindString moOpenPos "${moResult[0]}" "$3"
|
||||
moFindUnescaped moOpenPos "${moResult[0]}" "$3"
|
||||
if [[ $moOpenPos -gt -1 ]]; then
|
||||
moResult[1]=${moResult[0]:$moOpenPos + ${#3}}
|
||||
moResult[0]=${moResult[0]:0:$moOpenPos}
|
||||
@ -993,13 +1128,13 @@ moSplit() {
|
||||
moNext=$moOpenPos
|
||||
if [[ -n "${4-}" ]]; then
|
||||
# Is there another open delimiter inside this one?
|
||||
moFindString moNext "${moResult[1]}" "$3"
|
||||
moFindString moClosePos "${moResult[1]}" "$4"
|
||||
moFindUnescaped moNext "${moResult[1]}" "$3"
|
||||
moFindUnescaped moClosePos "${moResult[1]}" "$4"
|
||||
|
||||
while [[ $moNext -gt ${#3} ]] && [[ $moClosePos -gt $moNext ]]; do
|
||||
moFurthest=$(($moClosePos + ${#4}))
|
||||
moFindString moNext "${moResult[1]:$moFurthest}" "$3"
|
||||
moFindString moClosePos "${moResult[1]:$moFurthest}" "$4"
|
||||
moFindUnescaped moNext "${moResult[1]:$moFurthest}" "$3"
|
||||
moFindUnescaped moClosePos "${moResult[1]:$moFurthest}" "$4"
|
||||
moNext=$(($moNext + $moFurthest))
|
||||
moClosePos=$(($moFurthest + $moClosePos))
|
||||
done
|
||||
@ -1012,10 +1147,49 @@ moSplit() {
|
||||
fi
|
||||
fi
|
||||
|
||||
[[ "$5" == "true" ]] && moResult[1]="$3${moResult[1]}$4"
|
||||
|
||||
local "$1" && moIndirectArray "$1" "${moResult[@]}"
|
||||
}
|
||||
|
||||
|
||||
# Internal: Parse function arguments
|
||||
#
|
||||
# $1 - Destination
|
||||
# $2 - Arguments as string
|
||||
#
|
||||
# Returns nothing.
|
||||
moSplitArgs() {
|
||||
local moSCurArg moSArgs moSingle moDouble moParen
|
||||
|
||||
moSArgs=()
|
||||
|
||||
moSCurArg=()
|
||||
moSCurArg[2]=$2
|
||||
while : ; do
|
||||
[[ ! -z "${moSCurArg[2]}" ]] || break
|
||||
|
||||
moFindUnescaped moSingle "${moSCurArg[2]}" "'"
|
||||
moFindUnescaped moDouble "${moSCurArg[2]}" "\""
|
||||
[[ $moSingle -gt -1 ]] || [[ $moDouble -gt -1 ]] || {
|
||||
moSArgs+=(${moSCurArg[2]})
|
||||
break
|
||||
}
|
||||
|
||||
if [[ $moSingle < $moDouble ]] && [[ $moSingle -gt -1 ]]; then
|
||||
moSplit moSCurArg "${moSCurArg[2]}" "'" "'" true
|
||||
else
|
||||
moSplit moSCurArg "${moSCurArg[2]}" '"' '"' true
|
||||
fi
|
||||
|
||||
moSArgs+=(${moSCurArg[0]})
|
||||
moSArgs+=("${moSCurArg[1]}")
|
||||
done
|
||||
|
||||
local "$1" && moIndirectArray "$1" "${moSArgs[@]}"
|
||||
}
|
||||
|
||||
|
||||
# Internal: Handle the content for a standalone tag. This means removing
|
||||
# whitespace (not newlines) before a tag and whitespace and a newline after
|
||||
# a tag. That is, assuming, that the line is otherwise empty.
|
||||
|
@ -1,3 +1,5 @@
|
||||
MO_ALLOW_FUNCTION_ARGUMENTS=true
|
||||
|
||||
testArgs() {
|
||||
echo "$MO_FUNCTION_ARGS"
|
||||
echo "$@"
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
No args: [{{testArgs}}] - done
|
||||
One arg: [{{testArgs one}}] - done
|
||||
Multiple arguments: [{{testArgs aa bb cc 'x' " ! {[_.| }}] - done
|
||||
Evil: [{{testArgs bla; cat /etc/issue}}] - done
|
||||
One arg: [{{testArgs "one"}}] - done
|
||||
Multiple arguments: [{{testArgs "aa" "bb" "cc" "'x'" "\" ! {[_.|" }}] - done
|
||||
Evil: [{{testArgs "bla; cat /etc/issue"}}] - done
|
||||
|
@ -1,4 +1,4 @@
|
||||
No args: {{testArgs}} - done
|
||||
One arg: {{testArgs one}} - done
|
||||
One arg: {{testArgs "one"}} - done
|
||||
Getting name in a string: {{testArgs "The name is $name"}} - done
|
||||
Reverse this: {{#pipeTo rev}}abcde{{/pipeTo}}
|
||||
Reverse this: {{#pipeTo "rev"}}abcde{{/pipeTo}}
|
||||
|
@ -1,3 +1,5 @@
|
||||
MO_ALLOW_FUNCTION_ARGUMENTS=true
|
||||
|
||||
x="repo"
|
||||
i=2
|
||||
repo=( "resque" "hub" "rip" )
|
||||
@ -8,14 +10,16 @@ double_quote() {
|
||||
echo "\"${MO_FUNCTION_ARGS[@]}\""
|
||||
}
|
||||
inter_examp() {
|
||||
args=($MO_FUNCTION_ARGS)
|
||||
first_t=${args[0]}
|
||||
second_t=${args[1]}
|
||||
first_v=${args[2]}
|
||||
second_v=${args[3]}
|
||||
#for v in "$@"; do
|
||||
# echo "> $v"
|
||||
#done
|
||||
first_t="$1"
|
||||
second_t="$2"
|
||||
first_v="$3"
|
||||
second_v="$4"
|
||||
|
||||
[[ "$first_t" == "true" ]] && echo -n "$first_v "
|
||||
[[ "$second_t" == "true" ]] && echo -n "$second_v "
|
||||
[[ $first_t == true ]] && echo -n "$first_v "
|
||||
[[ $second_t == true ]] && echo -n "$second_v "
|
||||
}
|
||||
exists="hello"
|
||||
#doesnt="foo"
|
||||
|
@ -5,5 +5,5 @@ Loop:
|
||||
<b>"'hub'" hub</b>
|
||||
<b>"'rip'" rip</b>
|
||||
|
||||
hello world
|
||||
hello world help me "escape !
|
||||
X: repo
|
||||
|
@ -1,9 +1,9 @@
|
||||
Function: {{quote (x)}}
|
||||
Function: {{quote x}}
|
||||
Specific Element: {{repo.(i)}}
|
||||
Loop:
|
||||
{{#repo}}
|
||||
<b>{{double_quote (quote (.))}} {{.}}</b>
|
||||
<b>{{double_quote "(quote .)"}} {{.}}</b>
|
||||
{{/repo}}
|
||||
|
||||
{{inter_examp (#exists) (^doesnt) (exists) world}}
|
||||
{{inter_examp (#exists) (^doesnt) exists "world help me \"escape !" exists "testing"}}
|
||||
X: {{x}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user