2015-10-02 14:06:16 +00:00
#!/usr/bin/env bash
2015-01-27 12:05:06 +00:00
#
2015-10-02 14:06:16 +00:00
#/ Mo is a mustache template rendering software written in bash. It inserts
#/ environment variables into templates.
#/
#/ Simply put, mo will change {{VARIABLE}} into the value of that
#/ environment variable. You can use {{#VARIABLE}}content{{/VARIABLE}} to
#/ conditionally display content or iterate over the values of an array.
#/
#/ Learn more about mustache templates at https://mustache.github.io/
2016-07-27 14:56:33 +00:00
#/
#/ Simple usage:
#/
2019-03-14 01:38:00 +00:00
#/ mo [OPTIONS] filenames...
2016-07-27 14:56:33 +00:00
#/
2019-03-14 01:38:00 +00:00
#/ Options:
#/
2023-04-10 13:10:14 +00:00
#/ --allow-function-arguments
#/ Permit functions to be called with additional arguments. Otherwise,
#/ the only way to get access to the arguments is to use the
#/ MO_FUNCTION_ARGS environment variable.
#/ -d, --debug
#/ Enable debug logging to stderr.
2019-03-14 01:38:00 +00:00
#/ -u, --fail-not-set
2023-04-10 13:10:14 +00:00
#/ Fail upon expansion of an unset variable. Will silently ignore by
#/ default. Alternately, set MO_FAIL_ON_UNSET to a non-empty value.
2020-08-05 20:44:04 +00:00
#/ -x, --fail-on-function
2023-04-10 13:10:14 +00:00
#/ Fail when a function returns a non-zero status code instead of
#/ silently ignoring it. Alternately, set MO_FAIL_ON_FUNCTION to a
#/ non-empty value.
#/ -f, --fail-on-file
#/ Fail when a file (from command-line or partial) does not exist.
#/ Alternately, set MO_FAIL_ON_FILE to a non-empty value.
2019-03-14 01:38:00 +00:00
#/ -e, --false
2023-04-10 13:10:14 +00:00
#/ Treat the string "false" as empty for conditionals. Alternately,
#/ set MO_FALSE_IS_EMPTY to a non-empty value.
2019-03-14 01:38:00 +00:00
#/ -h, --help
2020-08-05 20:44:04 +00:00
#/ This message.
2019-03-14 01:38:00 +00:00
#/ -s=FILE, --source=FILE
2020-08-05 20:44:04 +00:00
#/ Load FILE into the environment before processing templates.
2020-10-01 12:39:02 +00:00
#/ Can be used multiple times.
2023-04-10 13:10:14 +00:00
#/ -- Indicate the end of options. All arguments after this will be
#/ treated as filenames only. Use when filenames may start with
#/ hyphens.
#/
#/ 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.
2023-04-10 18:24:06 +00:00
#/ MO_CLOSE_DELIMITER - The string used when closing a tag. Defaults to "}}".
2023-04-10 13:10:14 +00:00
#/ MO_DEBUG - When set to a non-empty value, additional debug information is
#/ written to stderr.
#/ MO_FUNCTION_ARGS - Arguments passed to the function.
#/ MO_FAIL_ON_FILE - If a filename from the command-line is missing or a
#/ partial does not exist, abort with an error.
#/ MO_FAIL_ON_FUNCTION - If a function returns a non-zero status code, abort
#/ with an error.
#/ 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" will
#/ be treated as an empty value for the purposes of conditionals.
2023-04-10 18:24:06 +00:00
#/ MO_OPEN_DELIMITER - The string used when opening a tag. Defaults to "{{".
2023-04-10 13:10:14 +00:00
#/ MO_ORIGINAL_COMMAND - Used to find the `mo` program in order to generate a
#/ help message.
2023-04-11 02:07:45 +00:00
#/ MO_PARSED - Content that has made it through the template engine.
2023-04-10 18:24:06 +00:00
#/ MO_STANDALONE_CONTENT - The content that preceeded the current tag. When a
#/ standalone tag is encountered, this is checked to see if it only
#/ contains whitespace. If this and the whitespace condition after a tag is
#/ met, then this will be reset to $'\n'.
2023-04-11 02:07:45 +00:00
#/ MO_UNPARSED - Template content yet to make it through the parser.
2023-04-10 13:10:14 +00:00
#/
#/ Mo is under a MIT style licence with an additional non-advertising clause.
#/ See LICENSE.md for the full text.
#/
#/ This is open source! Please feel free to contribute.
#/
#/ https://github.com/tests-always-included/mo
2015-01-23 17:43:08 +00:00
2023-04-11 02:07:45 +00:00
# Disable these warnings for the entire file
#
# VAR_NAME was modified in a subshell. That change might be lost.
# shellcheck disable=SC2031
#
# Modification of VAR_NAME is local (to subshell caused by (..) group).
# shellcheck disable=SC2030
2015-01-26 18:51:28 +00:00
2015-10-02 14:46:57 +00:00
# Public: Template parser function. Writes templates to stdout.
#
2020-08-05 20:44:04 +00:00
# $0 - Name of the mo file, used for getting the help message.
# $@ - Filenames to parse.
#
2023-04-10 13:10:14 +00:00
# See the comment above for details.
2016-07-27 14:56:33 +00:00
#
2015-10-02 14:46:57 +00:00
# Returns nothing.
mo() (
2023-04-11 02:07:45 +00:00
local moSource moFiles moDoubleHyphens
2015-10-02 14:46:57 +00:00
2023-04-08 00:35:25 +00:00
# This function executes in a subshell; IFS is reset at the end.
2015-10-02 14:46:57 +00:00
IFS=$' \n\t'
2023-04-08 00:35:25 +00:00
# Enable a strict mode. This is also reset at the end.
set -eEu -o pipefail
moFiles=()
moDoubleHyphens=false
2016-07-21 11:07:02 +00:00
2015-10-02 14:46:57 +00:00
if [[ $# -gt 0 ]]; then
2016-07-21 14:11:11 +00:00
for arg in "$@"; do
2023-04-08 00:35:25 +00:00
if $moDoubleHyphens; then
2020-08-05 20:44:04 +00:00
#: After we encounter two hyphens together, all the rest
#: of the arguments are files.
2023-04-08 00:35:25 +00:00
moFiles=(${moFiles[@]+"${moFiles[@]}"} "$arg")
2016-07-21 14:45:31 +00:00
else
case "$arg" in
2016-07-27 14:56:33 +00:00
-h|--h|--he|--hel|--help|-\?)
2023-04-08 00:35:25 +00:00
mo::usage "$0"
2016-07-21 14:45:31 +00:00
exit 0
;;
2017-11-03 21:45:51 +00:00
--allow-function-arguments)
MO_ALLOW_FUNCTION_ARGUMENTS=true
;;
2019-03-14 01:38:00 +00:00
-u | --fail-not-set)
2017-06-15 18:47:51 +00:00
MO_FAIL_ON_UNSET=true
;;
2020-08-05 20:44:04 +00:00
-x | --fail-on-function)
MO_FAIL_ON_FUNCTION=true
;;
2023-04-10 13:10:14 +00:00
-p | --fail-on-file)
MO_FAIL_ON_FILE=true
;;
2019-03-14 01:38:00 +00:00
-e | --false)
2016-07-27 14:56:33 +00:00
MO_FALSE_IS_EMPTY=true
;;
2019-03-14 01:38:00 +00:00
-s=* | --source=*)
if [[ "$arg" == --source=* ]]; then
2023-04-08 00:35:25 +00:00
moSource="${arg#--source=}"
2019-03-14 01:38:00 +00:00
else
2023-04-08 00:35:25 +00:00
moSource="${arg#-s=}"
2019-03-14 01:38:00 +00:00
fi
2016-07-21 14:45:31 +00:00
2023-04-08 00:35:25 +00:00
if [[ -f "$moSource" ]]; then
2016-10-25 10:56:40 +00:00
# shellcheck disable=SC1090
2023-04-08 00:35:25 +00:00
. "$moSource"
2016-07-21 14:45:31 +00:00
else
2023-04-08 00:35:25 +00:00
echo "No such file: $moSource" >&2
2016-07-21 14:45:31 +00:00
exit 1
fi
;;
2023-04-08 00:35:25 +00:00
-d | --debug)
MO_DEBUG=true
;;
2016-07-21 14:45:31 +00:00
--)
2020-08-05 20:44:04 +00:00
#: Set a flag indicating we've encountered double hyphens
2023-04-08 00:35:25 +00:00
moDoubleHyphens=true
2016-07-21 14:45:31 +00:00
;;
2023-04-10 13:10:14 +00:00
-*)
2023-04-10 16:39:47 +00:00
mo::error "Unknown option: $arg (See --help for options)"
2023-04-10 13:10:14 +00:00
;;
2016-07-21 14:45:31 +00:00
*)
2020-08-05 20:44:04 +00:00
#: Every arg that is not a flag or a option should be a file
2023-04-08 00:35:25 +00:00
moFiles=(${moFiles[@]+"${moFiles[@]}"} "$arg")
2016-07-21 14:45:31 +00:00
;;
esac
fi
2016-07-21 14:11:11 +00:00
done
2015-10-02 14:46:57 +00:00
fi
2023-04-08 00:35:25 +00:00
mo::debug "Debug enabled"
2023-04-10 18:24:06 +00:00
MO_OPEN_DELIMITER="${MO_OPEN_DELIMITER:-"{{"}"
MO_CLOSE_DELIMITER="${MO_CLOSE_DELIMITER:-"}}"}"
# The standalone content is a trick to make the standalone tag detection
# possible. When it's set to content with a newline and if the tag supports
# it, the standalone content check happens. This check ensures only
# whitespace is after the last newline up to the tag, and only whitespace
# is after the tag up to the next newline. If that is the case, remove
# whitespace and the trailing newline. By setting this to $'\n', we're
# saying we are at the beginning of content.
MO_STANDALONE_CONTENT=$'\n'
2023-04-11 02:07:45 +00:00
MO_PARSED=""
mo::content "${moFiles[@]}" || return 1
mo::parse "" "" ""
echo -n "$MO_PARSED$MO_UNPARSED"
2015-10-02 14:46:57 +00:00
)
2023-04-08 00:35:25 +00:00
# Internal: Show a debug message
2017-11-03 21:34:08 +00:00
#
2023-04-08 00:35:25 +00:00
# $1 - The debug message to show
2017-11-03 21:34:08 +00:00
#
# Returns nothing.
2023-04-08 00:35:25 +00:00
mo::debug() {
if [[ -n "${MO_DEBUG:-}" ]]; then
2023-04-10 18:24:06 +00:00
echo "DEBUG ${FUNCNAME[1]:-?} - $1" >&2
2017-11-03 21:45:51 +00:00
fi
2017-11-03 21:34:08 +00:00
}
2023-04-08 00:35:25 +00:00
# Internal: Show an error message and exit
2015-10-02 14:46:57 +00:00
#
2023-04-08 00:35:25 +00:00
# $1 - The error message to show
2015-01-26 18:51:28 +00:00
#
2023-04-08 00:35:25 +00:00
# Returns nothing. Exits the program.
mo::error() {
echo "ERROR: $1" >&2
2023-04-10 13:10:14 +00:00
exit "${2:-1}"
2015-01-27 01:55:06 +00:00
}
2023-04-08 00:35:25 +00:00
# Internal: Displays the usage for mo. Pulls this from the file that
# contained the `mo` function. Can only work when the right filename
# comes is the one argument, and that only happens when `mo` is called
# with `$0` set to this file.
2015-10-02 14:46:57 +00:00
#
2023-04-08 00:35:25 +00:00
# $1 - Filename that has the help message
2015-01-27 01:55:06 +00:00
#
2015-10-02 14:46:57 +00:00
# Returns nothing.
2023-04-08 00:35:25 +00:00
mo::usage() {
while read -r line; do
if [[ "${line:0:2}" == "#/" ]]; then
echo "${line:3}"
fi
2023-04-08 04:10:47 +00:00
done < "$MO_ORIGINAL_COMMAND"
2023-04-08 00:35:25 +00:00
echo ""
echo "MO_VERSION=$MO_VERSION"
2015-01-27 01:55:06 +00:00
}
2023-04-11 02:07:45 +00:00
# Internal: Fetches the content to parse into MO_UNPARSED. Can be a list of
2015-10-02 14:46:57 +00:00
# partials for files or the content from stdin.
#
2023-04-11 02:07:45 +00:00
# $1-@ - File names (optional), read from stdin otherwise
2015-01-27 01:55:06 +00:00
#
2015-10-02 14:46:57 +00:00
# Returns nothing.
2023-04-08 00:35:25 +00:00
mo::content() {
2023-04-11 02:07:45 +00:00
local moFilename
2015-01-27 01:55:06 +00:00
if [[ "${#@}" -gt 0 ]]; then
2023-04-11 02:07:45 +00:00
MO_UNPARSED=""
2015-01-27 01:55:06 +00:00
2020-08-05 20:44:04 +00:00
for moFilename in "$@"; do
2023-04-08 00:35:25 +00:00
mo::debug "Using template to load content from file: $moFilename"
2015-10-02 14:46:57 +00:00
#: This is so relative paths work from inside template files
2023-04-11 02:07:45 +00:00
MO_UNPARSED="$MO_UNPARSED$MO_OPEN_DELIMITER>$moFilename$MO_CLOSE_DELIMITER"
2015-01-27 01:55:06 +00:00
done
else
2023-04-08 00:35:25 +00:00
mo::debug "Will read content from stdin"
2023-04-11 02:07:45 +00:00
mo::contentFile || return 1
2015-01-27 01:55:06 +00:00
fi
}
2023-04-11 02:07:45 +00:00
# Internal: Read a file into MO_UNPARSED.
2015-01-27 01:55:06 +00:00
#
2023-04-11 02:07:45 +00:00
# $1 - Filename to load - if empty, defaults to /dev/stdin
2015-10-02 14:46:57 +00:00
#
# Returns nothing.
2023-04-08 00:35:25 +00:00
mo::contentFile() {
2023-04-11 02:07:45 +00:00
local moFile moResult
2017-06-15 18:47:51 +00:00
2023-04-08 00:35:25 +00:00
# The subshell removes any trailing newlines. We forcibly add
2023-04-11 02:07:45 +00:00
# a dot to the content to preserve all newlines. Reading from
# stdin with a `read` loop does not work as expected, so `cat`
# needs to stay.
moFile=${1:-/dev/stdin}
2023-04-10 13:10:14 +00:00
if [[ -e "$moFile" ]]; then
mo::debug "Loading content: $moFile"
2023-04-11 02:07:45 +00:00
MO_UNPARSED=$(set +Ee; cat -- "$moFile"; moResult=$?; echo -n '.'; exit "$moResult") || return 1
MO_UNPARSED=${MO_UNPARSED%.} # Remove last dot
2023-04-10 13:10:14 +00:00
elif [[ -n "${MO_FAIL_ON_FILE-}" ]]; then
mo::error "No such file: $moFile"
else
mo::debug "File does not exist: $moFile"
2023-04-11 02:07:45 +00:00
MO_UNPARSED=""
2023-04-10 13:10:14 +00:00
fi
2015-01-27 01:55:06 +00:00
}
2015-10-02 14:46:57 +00:00
# Internal: Send a variable up to the parent of the caller of this function.
2015-01-27 01:55:06 +00:00
#
2015-10-02 14:46:57 +00:00
# $1 - Variable name
# $2 - Value
#
# Examples
#
# callFunc () {
2023-04-08 00:35:25 +00:00
# local "$1" && mo::indirect "$1" "the value"
2015-10-02 14:46:57 +00:00
# }
2015-10-02 15:47:53 +00:00
# callFunc dest
# echo "$dest" # writes "the value"
2015-10-02 14:46:57 +00:00
#
# Returns nothing.
2023-04-08 00:35:25 +00:00
mo::indirect() {
2015-01-27 01:55:06 +00:00
unset -v "$1"
printf -v "$1" '%s' "$2"
}
2015-10-02 14:46:57 +00:00
# Internal: Send an array as a variable up to caller of a function
#
2023-04-10 18:24:06 +00:00
# $1 - Variable name
2015-10-07 20:23:50 +00:00
# $2-@ - Array elements
2015-10-02 14:46:57 +00:00
#
# Examples
2015-01-27 01:55:06 +00:00
#
2015-10-02 14:46:57 +00:00
# callFunc () {
# local myArray=(one two three)
2023-04-08 00:35:25 +00:00
# local "$1" && mo::indirectArray "$1" "${myArray[@]}"
2015-10-02 14:46:57 +00:00
# }
2015-10-02 15:47:53 +00:00
# callFunc dest
# echo "${dest[@]}" # writes "one two three"
2015-10-02 14:46:57 +00:00
#
# Returns nothing.
2023-04-08 00:35:25 +00:00
mo::indirectArray() {
2015-01-27 01:55:06 +00:00
unset -v "$1"
2017-02-21 17:00:39 +00:00
# IFS must be set to a string containing space or unset in order for
# the array slicing to work regardless of the current IFS setting on
# bash 3. This is detailed further at
# https://github.com/fidian/gg-core/pull/7
2017-02-23 19:51:12 +00:00
eval "$(printf "IFS= %s=(\"\${@:2}\") IFS=%q" "$1" "$IFS")"
2015-01-27 01:55:06 +00:00
}
2023-04-11 02:07:45 +00:00
# Internal: Trim leading characters from MO_UNPARSED
2016-12-16 00:11:29 +00:00
#
2023-04-08 00:35:25 +00:00
# Returns nothing.
2023-04-11 02:07:45 +00:00
mo::trimUnparsed() {
local moLast moR moN moT
2015-01-27 01:55:06 +00:00
2023-04-08 00:35:25 +00:00
moLast=""
moR=$'\r'
moN=$'\n'
moT=$'\t'
2023-04-11 02:07:45 +00:00
while [[ "$MO_UNPARSED" != "$moLast" ]]; do
moLast=$MO_UNPARSED
MO_UNPARSED=${MO_UNPARSED# }
MO_UNPARSED=${MO_UNPARSED#"$moR"}
MO_UNPARSED=${MO_UNPARSED#"$moN"}
MO_UNPARSED=${MO_UNPARSED#"$moT"}
2023-04-08 00:35:25 +00:00
done
}
# Internal: Remove whitespace and content after whitespace
2015-01-27 01:55:06 +00:00
#
2023-04-08 00:35:25 +00:00
# $1 - Name of the destination variable
# $2 - The string to chomp
2015-01-27 01:55:06 +00:00
#
2023-04-08 00:35:25 +00:00
# Returns nothing.
mo::chomp() {
local moTemp moR moN moT
moR=$'\r'
moN=$'\n'
moT=$'\t'
moTemp=${2%% *}
2023-04-10 13:10:14 +00:00
moTemp=${moTemp%%"$moR"*}
moTemp=${moTemp%%"$moN"*}
moTemp=${moTemp%%"$moT"*}
2023-04-08 00:35:25 +00:00
local "$1" && mo::indirect "$1" "$moTemp"
}
2015-01-27 01:55:06 +00:00
2023-04-11 02:07:45 +00:00
# Internal: Parse MO_UNPARSED, writing content to MO_PARSED. Interpolates
2023-04-08 00:35:25 +00:00
# mustache tags.
#
2023-04-11 02:07:45 +00:00
# $1 - Current name (the variable NAME for what {{.}} means)
# $2 - Current block name
# $3 - Fast mode (skip to end of block) if non-empty
2023-04-08 00:35:25 +00:00
#
# Array has the following elements
# [0] - Parsed content
# [1] - Unparsed content after the closing tag
#
# Returns nothing.
mo::parse() {
2023-04-11 02:07:45 +00:00
local moCurrent moFastMode moRemainder moChunk
2023-04-10 18:24:06 +00:00
2023-04-11 02:07:45 +00:00
moCurrent=$1
moCurrentBlock=$2
moFastMode=$3
2023-04-08 00:35:25 +00:00
moRemainder=""
mo::debug "Starting parse, current: $moCurrent, ending tag: $moCurrentBlock, fast: $moFastMode"
2023-04-11 02:07:45 +00:00
while [[ -n "$MO_UNPARSED" ]]; do
moChunk=${MO_UNPARSED%%"$MO_OPEN_DELIMITER"*}
MO_PARSED="$MO_PARSED$moChunk"
MO_STANDALONE_CONTENT="$MO_STANDALONE_CONTENT$moChunk"
MO_UNPARSED=${MO_UNPARSED:${#moChunk}}
2023-04-08 00:35:25 +00:00
2023-04-11 02:07:45 +00:00
if [[ -n "$MO_UNPARSED" ]]; then
MO_UNPARSED=${MO_UNPARSED:${#MO_OPEN_DELIMITER}}
mo::trimUnparsed
2023-04-08 00:35:25 +00:00
2023-04-11 02:07:45 +00:00
case "$MO_UNPARSED" in
2023-04-08 00:35:25 +00:00
'#'*)
# Loop, if/then, or pass content through function
2023-04-11 02:07:45 +00:00
mo::parseBlock "$moCurrent" false
;;
'^'*)
# Display section if named thing does not exist
mo::parseBlock "$moCurrent" true
2023-04-08 00:35:25 +00:00
;;
'>'*)
# Load partial - get name of file relative to cwd
2023-04-11 02:07:45 +00:00
mo::parsePartial "$moCurrent" "$moFastMode"
2023-04-08 00:35:25 +00:00
;;
'/'*)
# Closing tag
2023-04-11 02:07:45 +00:00
mo::parseCloseTag "$moCurrent" "$moCurrentBlock"
moRemainder=$MO_UNPARSED
MO_UNPARSED=
2023-04-08 00:35:25 +00:00
;;
'!'*)
# Comment - ignore the tag content entirely
2023-04-11 02:07:45 +00:00
mo::parseComment
2023-04-08 00:35:25 +00:00
;;
2015-01-27 14:29:08 +00:00
2023-04-08 00:35:25 +00:00
'='*)
# Change delimiters
# Any two non-whitespace sequences separated by whitespace.
2023-04-11 02:07:45 +00:00
mo::parseDelimiter
2023-04-08 00:35:25 +00:00
;;
'&'*)
2023-04-11 02:07:45 +00:00
# Unescaped - mo doesn't escape/unescape
MO_UNPARSED=${MO_UNPARSED#&}
mo::trimUnparsed
mo::parseValue "$moCurrent" "$moFastMode"
2023-04-08 00:35:25 +00:00
;;
*)
# Normal environment variable, string, subexpression,
# current value, key, or function call
2023-04-11 02:07:45 +00:00
mo::parseValue "$moCurrent" "$moFastMode"
2023-04-08 00:35:25 +00:00
;;
esac
2015-01-27 01:55:06 +00:00
fi
done
2023-04-11 02:07:45 +00:00
MO_UNPARSED="$MO_UNPARSED$moRemainder"
2015-01-27 01:55:06 +00:00
}
2023-04-08 00:35:25 +00:00
# Internal: Handle parsing a block
#
2023-04-11 02:07:45 +00:00
# $1 - Current name (the variable NAME for what {{.}} means)
# $2 - Invert condition ("true" or "false")
2023-04-08 00:35:25 +00:00
#
# Returns nothing
mo::parseBlock() {
2023-04-11 02:07:45 +00:00
local moCurrent moInvertBlock moArgs
moCurrent=$1
moInvertBlock=$2
MO_UNPARSED=${MO_UNPARSED:1}
mo::trimUnparsed
mo::parseValueInner moArgs "$moCurrent"
MO_UNPARSED=${MO_UNPARSED#"$MO_CLOSE_DELIMITER"}
2023-04-08 00:35:25 +00:00
mo::debug "Parsing block: ${moArgs[*]}"
2023-04-11 02:07:45 +00:00
if mo::standaloneCheck; then
mo::standaloneProcess
2023-04-09 14:51:11 +00:00
fi
2023-04-08 00:35:25 +00:00
if [[ "${moArgs[0]}" == "NAME" ]] && mo::isFunction "${moArgs[1]}"; then
2023-04-11 02:07:45 +00:00
mo::parseBlockFunction "$moCurrent" "$moInvertBlock" "${moArgs[@]}"
2023-04-09 14:51:11 +00:00
elif [[ "${moArgs[0]}" == "NAME" ]] && mo::isArray "${moArgs[1]}"; then
2023-04-11 02:07:45 +00:00
mo::parseBlockArray "$moCurrent" "$moInvertBlock" "${moArgs[@]}"
2023-04-09 14:51:11 +00:00
else
2023-04-11 02:07:45 +00:00
mo::parseBlockValue "$moCurrent" "$moInvertBlock" "${moArgs[@]}"
2023-04-09 14:51:11 +00:00
fi
}
2023-04-08 00:35:25 +00:00
2023-04-09 14:51:11 +00:00
# Internal: Handle parsing a block whose first argument is a function
#
2023-04-11 02:07:45 +00:00
# $1 - Current name (the variable NAME for what {{.}} means)
# $2 - Invert condition ("true" or "false")
# $3-@ - The parsed arguments from inside the block tags
2023-04-09 14:51:11 +00:00
#
# Returns nothing
mo::parseBlockFunction() {
2023-04-11 02:07:45 +00:00
local moTarget moCurrent moInvertBlock moArgs moTemp moResult
2023-04-09 14:51:11 +00:00
2023-04-11 02:07:45 +00:00
moCurrent=$1
moInvertBlock=$2
shift 2
2023-04-09 14:51:11 +00:00
moArgs=(${@+"$@"})
mo::debug "Parsing block function: ${moArgs[*]}"
if [[ "$moInvertBlock" == "true" ]]; then
2023-04-11 02:07:45 +00:00
# The function exists and we're inverting the section, so discard
# any additions to the parsed content.
moTemp=$MO_PARSED
mo::parse "$moCurrent" "${moArgs[1]}" "FAST-FUNCTION"
MO_PARSED=$moTemp
2023-04-09 14:51:11 +00:00
else
2023-04-08 00:35:25 +00:00
# Get contents of block after parsing
2023-04-11 02:07:45 +00:00
moTemp=$MO_PARSED
MO_PARSED=""
mo::parse "$moCurrent" "${moArgs[1]}" ""
2023-04-08 00:35:25 +00:00
# Pass contents to function
2023-04-11 02:07:45 +00:00
mo::evaluateFunction moResult "$MO_PARSED" "${moArgs[@]:1}"
MO_PARSED="$moTemp$moResult"
2023-04-09 14:51:11 +00:00
fi
2023-04-08 00:35:25 +00:00
2023-04-11 02:07:45 +00:00
mo::debug "Done parsing block function: ${moArgs[*]}"
2023-04-09 14:51:11 +00:00
}
# Internal: Handle parsing a block whose first argument is an array
#
2023-04-11 02:07:45 +00:00
# $1 - Current name (the variable NAME for what {{.}} means)
# $2 - Invert condition ("true" or "false")
# $2-@ - The parsed arguments from inside the block tags
2023-04-09 14:51:11 +00:00
#
# Returns nothing
mo::parseBlockArray() {
2023-04-11 02:07:45 +00:00
local moCurrent moInvertBlock moArgs moParseResult moResult moArrayName moArrayIndexes moArrayIndex moTemp
2023-04-09 14:51:11 +00:00
2023-04-11 02:07:45 +00:00
moCurrent=$1
moInvertBlock=$2
shift 2
2023-04-09 14:51:11 +00:00
moArgs=(${@+"$@"})
mo::debug "Parsing block array: ${moArgs[*]}"
moArrayName=${moArgs[1]}
eval "moArrayIndexes=(\"\${!${moArrayName}[@]}\")"
if [[ "${#moArrayIndexes[@]}" -lt 1 ]]; then
# No elements
if [[ "$moInvertBlock" == "true" ]]; then
# Show the block
2023-04-11 02:07:45 +00:00
mo::parse "$moArrayName" "$moArrayName" ""
2023-04-08 00:35:25 +00:00
else
2023-04-09 14:51:11 +00:00
# Skip the block processing
2023-04-11 02:07:45 +00:00
moTemp=$MO_PARSED
mo::parse "$moArrayName" "$moArrayName" "FAST-EMPTY"
MO_PARSED=$moTemp
2023-04-08 00:35:25 +00:00
fi
else
2023-04-09 14:51:11 +00:00
if [[ "$moInvertBlock" == "true" ]]; then
# Skip the block processing
2023-04-11 02:07:45 +00:00
moTemp=$MO_PARSED
mo::parse "$moArrayName" "$moArrayName" "FAST-EMPTY"
MO_PARSED=$moTemp
2023-04-09 00:06:04 +00:00
else
2023-04-09 14:51:11 +00:00
# Process for each element in the array
2023-04-11 02:07:45 +00:00
moTemp=$MO_UNPARSED
2023-04-09 14:51:11 +00:00
for moArrayIndex in "${moArrayIndexes[@]}"; do
2023-04-11 02:07:45 +00:00
MO_UNPARSED=$moTemp
2023-04-09 14:51:11 +00:00
mo::debug "Iterate over array using element: $moArrayName.$moArrayIndex"
2023-04-11 02:07:45 +00:00
mo::parse "$moArrayName.$moArrayIndex" "${moArgs[1]}" ""
2023-04-09 14:51:11 +00:00
done
2023-04-08 00:35:25 +00:00
fi
2023-04-09 14:51:11 +00:00
fi
2023-04-08 00:35:25 +00:00
2023-04-09 14:51:11 +00:00
mo::debug "Done parsing block array: ${moArgs[*]}"
}
# Internal: Handle parsing a block whose first argument is a value
#
2023-04-11 02:07:45 +00:00
# $1 - Current name (the variable NAME for what {{.}} means)
# $2 - Invert condition ("true" or "false")
# $3-@ - The parsed arguments from inside the block tags
2023-04-09 14:51:11 +00:00
#
# Returns nothing
mo::parseBlockValue() {
2023-04-11 02:07:45 +00:00
local moCurrent moInvertBlock moArgs moParseResult moResult moTemp
2023-04-08 00:35:25 +00:00
2023-04-11 02:07:45 +00:00
moCurrent=$1
moInvertBlock=$2
shift 2
2023-04-09 14:51:11 +00:00
moArgs=(${@+"$@"})
mo::debug "Parsing block value: ${moArgs[*]}"
# Variable, value, or list of mixed things
mo::evaluateListOfSingles moResult "$moCurrent" "${moArgs[@]}"
if mo::isTruthy "$moResult" "$moInvertBlock"; then
mo::debug "Block is truthy: $moResult"
2023-04-11 02:07:45 +00:00
mo::parse "${moArgs[1]}" "${moArgs[1]}" ""
2023-04-09 14:51:11 +00:00
else
mo::debug "Block is falsy: $moResult"
2023-04-11 02:07:45 +00:00
moTemp=$MO_PARSED
mo::parse "${moArgs[1]}" "${moArgs[1]}" "FAST-FALSY"
MO_PARSED=$moTemp
2023-04-08 00:35:25 +00:00
fi
2023-04-09 14:51:11 +00:00
mo::debug "Done parsing block value: ${moArgs[*]}"
2023-04-08 00:35:25 +00:00
}
# Internal: Handle parsing a partial
2015-01-27 01:55:06 +00:00
#
2023-04-11 02:07:45 +00:00
# $1 - Current name (the variable NAME for what {{.}} means)
# $2 - Fast mode (skip to end of block) if non-empty
2015-01-27 01:55:06 +00:00
#
2023-04-11 02:07:45 +00:00
# Indentation will be applied to the entire partial's contents before parsing.
# This indentation is based on the whitespace that ends the previously parsed
# content.
2023-04-08 00:35:25 +00:00
#
# Returns nothing
mo::parsePartial() {
2023-04-11 02:07:45 +00:00
local moCurrent moFilename moResult moFastMode moIndentation moN moR
moCurrent=$1
moFastMode=$2
MO_UNPARSED=${MO_UNPARSED:1}
mo::trimUnparsed
mo::chomp moFilename "${MO_UNPARSED%%"$MO_CLOSE_DELIMITER"*}"
MO_UNPARSED="${MO_UNPARSED#*"$MO_CLOSE_DELIMITER"}"
2023-04-08 04:10:47 +00:00
moIndentation=""
2023-04-08 00:35:25 +00:00
2023-04-11 02:07:45 +00:00
if mo::standaloneCheck; then
2023-04-08 04:10:47 +00:00
moN=$'\n'
moR=$'\r'
2023-04-11 02:07:45 +00:00
moIndentation="$moN${MO_PARSED//"$moR"/"$moN"}"
2023-04-10 13:10:14 +00:00
moIndentation=${moIndentation##*"$moN"}
2023-04-09 23:53:55 +00:00
mo::debug "Adding indentation to partial: '$moIndentation'"
2023-04-11 02:07:45 +00:00
mo::standaloneProcess
2023-04-08 00:35:25 +00:00
fi
2023-04-11 02:07:45 +00:00
if [[ -z "$moFastMode" ]]; then
2023-04-08 00:35:25 +00:00
mo::debug "Parsing partial: $moFilename"
# Execute in subshell to preserve current cwd and environment
moResult=$(
# It would be nice to remove `dirname` and use a function instead,
# but that is difficult when only given filenames.
cd "$(dirname -- "$moFilename")" || exit 1
echo "$(
2023-04-11 02:07:45 +00:00
if ! mo::contentFile "${moFilename##*/}"; then
2023-04-08 04:10:47 +00:00
exit 1
fi
2023-04-08 00:35:25 +00:00
2023-04-11 02:07:45 +00:00
mo::indentLines "$moIndentation"
2023-04-09 23:53:55 +00:00
2023-04-08 00:35:25 +00:00
# Delimiters are reset when loading a new partial
2023-04-10 18:24:06 +00:00
MO_OPEN_DELIMITER="{{"
MO_CLOSE_DELIMITER="}}"
MO_STANDALONE_CONTENT=""
2023-04-11 02:07:45 +00:00
MO_PARSED=""
mo::parse "$moCurrent" "" ""
2023-04-08 00:35:25 +00:00
# Fix bash handling of subshells and keep trailing whitespace.
2023-04-11 02:07:45 +00:00
echo -n "$MO_PARSED$MO_UNPARSED."
2023-04-08 00:35:25 +00:00
)" || exit 1
) || exit 1
2023-04-11 02:07:45 +00:00
if [[ -z "$moResult" ]]; then
2023-04-08 04:10:47 +00:00
mo::debug "Error detected when trying to read the file"
exit 1
2023-04-08 00:35:25 +00:00
fi
2023-04-08 04:10:47 +00:00
2023-04-11 02:07:45 +00:00
MO_PARSED="$MO_PARSED${moResult%.}"
2023-04-08 00:35:25 +00:00
fi
}
# Internal: Handle closing a tag
2015-10-02 14:46:57 +00:00
#
2023-04-11 02:07:45 +00:00
# $1 - Current name (the variable NAME for what {{.}} means)
# $2 - Current block being processed
2015-10-02 14:46:57 +00:00
#
# Returns nothing.
2023-04-08 00:35:25 +00:00
mo::parseCloseTag() {
2023-04-11 02:07:45 +00:00
local moArgs moCurrent moCurrentBlock
moCurrent=$1
moCurrentBlock=$2
MO_UNPARSED=${MO_UNPARSED:1}
mo::trimUnparsed
mo::parseValueInner moArgs "$moCurrent"
MO_UNPARSED=${MO_UNPARSED#"$MO_CLOSE_DELIMITER"}
mo::debug "Closing tag: ${moArgs[1]}"
if mo::standaloneCheck; then
mo::standaloneProcess
2023-04-08 00:35:25 +00:00
fi
2023-04-11 02:07:45 +00:00
if [[ -n "$moCurrentBlock" ]] && [[ "${moArgs[1]}" != "$moCurrentBlock" ]]; then
mo::error "Unexpected close tag: ${moArgs[1]}, expected $moCurrentBlock"
2023-04-08 00:35:25 +00:00
elif [[ -z "$moCurrentBlock" ]]; then
2023-04-11 02:07:45 +00:00
mo::error "Unexpected close tag: ${moArgs[1]}"
2015-01-27 01:55:06 +00:00
fi
2023-04-08 00:35:25 +00:00
}
# Internal: Handle parsing a comment
#
# Returns nothing
mo::parseComment() {
2023-04-10 18:24:06 +00:00
local moContent moPrevious moContent
2023-04-08 00:35:25 +00:00
2023-04-11 02:07:45 +00:00
MO_UNPARSED=${MO_UNPARSED#*"$MO_CLOSE_DELIMITER"}
2023-04-08 00:35:25 +00:00
mo::debug "Parsing comment"
2015-01-27 01:55:06 +00:00
2023-04-11 02:07:45 +00:00
if mo::standaloneCheck; then
mo::standaloneProcess
2015-01-27 01:55:06 +00:00
fi
2023-04-08 00:35:25 +00:00
}
# Internal: Handle parsing the change of delimiters
#
# Returns nothing
mo::parseDelimiter() {
2023-04-10 18:24:06 +00:00
local moContent moOpen moClose moPrevious
2023-04-08 00:35:25 +00:00
2023-04-11 02:07:45 +00:00
MO_UNPARSED=${MO_UNPARSED:1}
mo::trimUnparsed
mo::chomp moOpen "$MO_UNPARSED"
MO_UNPARSED=${MO_UNPARSED:${#moOpen}}
mo::trimUnparsed
mo::chomp moClose "${MO_UNPARSED%%="$MO_CLOSE_DELIMITER"*}"
MO_UNPARSED=${MO_UNPARSED#*="$MO_CLOSE_DELIMITER"}
2023-04-08 00:35:25 +00:00
mo::debug "Parsing delimiters: $moOpen $moClose"
2023-04-11 02:07:45 +00:00
if mo::standaloneCheck; then
mo::standaloneProcess
2015-01-27 01:55:06 +00:00
fi
2023-04-10 18:24:06 +00:00
MO_OPEN_DELIMITER="$moOpen"
MO_CLOSE_DELIMITER="$moClose"
2015-01-27 01:55:06 +00:00
}
2023-04-08 00:35:25 +00:00
# Internal: Handle parsing value or function call
2015-05-05 15:46:06 +00:00
#
2023-04-11 02:07:45 +00:00
# $1 - Current name (the variable NAME for what {{.}} means)
# $2 - Fast mode (skip to end of block) if non-empty
2023-04-08 00:35:25 +00:00
#
# Returns nothing
mo::parseValue() {
2023-04-11 02:07:45 +00:00
local moUnparsedOriginal moArgs moFastMode
2015-05-05 15:46:06 +00:00
2023-04-11 02:07:45 +00:00
moCurrent=$1
moFastMode=$2
moUnparsedOriginal=$MO_UNPARSED
MO_UNPARSED=${MO_UNPARSED#"$MO_OPEN_DELIMITER"}
mo::trimUnparsed
2015-05-05 15:46:06 +00:00
2023-04-11 02:07:45 +00:00
mo::parseValueInner moArgs "$moCurrent"
2023-04-08 00:35:25 +00:00
2023-04-11 02:07:45 +00:00
if [[ -z "$moFastMode" ]]; then
2023-04-08 00:35:25 +00:00
mo::evaluate moResult "$moCurrent" "${moArgs[@]}"
2023-04-11 02:07:45 +00:00
MO_PARSED="$MO_PARSED$moResult"
2023-04-08 00:35:25 +00:00
fi
2023-04-11 02:07:45 +00:00
if [[ "${MO_UNPARSED:0:${#MO_CLOSE_DELIMITER}}" != "$MO_CLOSE_DELIMITER" ]]; then
mo::error "Did not find closing tag near: ${moUnparsedOriginal:0:20}"
2023-04-08 00:35:25 +00:00
fi
2023-04-11 02:07:45 +00:00
MO_UNPARSED=${MO_UNPARSED:${#MO_CLOSE_DELIMITER}}
2023-04-08 00:35:25 +00:00
}
2023-04-11 02:07:45 +00:00
# Internal: Handle parsing value or function call inside of delimiters.
2023-04-08 00:35:25 +00:00
#
# $1 - Destination variable name, will be set to an array
2023-04-11 02:07:45 +00:00
# $2 - Current name (the variable NAME for what {{.}} means)
2023-04-08 00:35:25 +00:00
#
# The destination value will be an array
2023-04-11 02:07:45 +00:00
# [@] = a list of argument type, argument name/value
2023-04-08 00:35:25 +00:00
#
# Returns nothing
mo::parseValueInner() {
2023-04-11 02:07:45 +00:00
local moCurrent moArgs moArgResult
2023-04-08 00:35:25 +00:00
2023-04-11 02:07:45 +00:00
moCurrent=$2
2023-04-08 00:35:25 +00:00
moArgs=()
2023-04-11 02:07:45 +00:00
while [[ "$MO_UNPARSED" != "$MO_CLOSE_DELIMITER"* ]] && [[ "$MO_UNPARSED" != "}"* ]] && [[ "$MO_UNPARSED" != ")"* ]] && [[ -n "$MO_UNPARSED" ]]; do
mo::getArgument moArgResult
2023-04-08 00:35:25 +00:00
moArgs=(${moArgs[@]+"${moArgs[@]}"} "${moArgResult[0]}" "${moArgResult[1]}")
2015-05-05 15:46:06 +00:00
done
2023-04-08 00:35:25 +00:00
mo::debug "Parsed arguments: ${moArgs[*]}"
2023-04-11 02:07:45 +00:00
local "$1" && mo::indirectArray "$1" ${moArgs[@]+"${moArgs[@]}"}
2015-05-05 15:46:06 +00:00
}
2015-10-02 15:47:53 +00:00
2023-04-11 02:07:45 +00:00
# Internal: Retrieve an argument name from MO_UNPARSED.
2015-10-02 14:46:57 +00:00
#
2023-04-08 00:35:25 +00:00
# $1 - Destination variable name. Will be an array.
2015-01-27 01:55:06 +00:00
#
2023-04-08 00:35:25 +00:00
# The array will have the following elements
# [0] = argument type, "NAME" or "VALUE"
# [1] = argument name or value
#
# Returns nothing
mo::getArgument() {
2023-04-11 02:07:45 +00:00
local moCurrent moArg
2015-01-27 01:55:06 +00:00
2023-04-11 02:07:45 +00:00
moCurrent=$1
2023-04-08 00:35:25 +00:00
2023-04-11 02:07:45 +00:00
case "$MO_UNPARSED" in
2023-04-08 00:35:25 +00:00
'{'*)
2023-04-11 02:07:45 +00:00
mo::getArgumentBrace moArg "$moCurrent"
2023-04-08 00:35:25 +00:00
;;
'('*)
2023-04-11 02:07:45 +00:00
mo::getArgumentParenthesis moArg "$moCurrent"
2023-04-08 00:35:25 +00:00
;;
'"'*)
2023-04-11 02:07:45 +00:00
mo::getArgumentDoubleQuote moArg
2023-04-08 00:35:25 +00:00
;;
"'"*)
2023-04-11 02:07:45 +00:00
mo::getArgumentSingleQuote moArg
2023-04-08 00:35:25 +00:00
;;
*)
2023-04-11 02:07:45 +00:00
mo::getArgumentDefault moArg
2023-04-08 00:35:25 +00:00
esac
2015-10-02 14:46:57 +00:00
2023-04-08 00:35:25 +00:00
mo::debug "Found argument: ${moArg[0]} ${moArg[1]}"
2015-01-27 01:55:06 +00:00
2023-04-11 02:07:45 +00:00
local "$1" && mo::indirectArray "$1" "${moArg[0]}" "${moArg[1]}"
2015-01-27 01:55:06 +00:00
}
2023-04-08 00:35:25 +00:00
# Internal: Get an argument, which is the result of a subexpression as a VALUE
2015-10-02 14:46:57 +00:00
#
2023-04-08 00:35:25 +00:00
# $1 - Destination variable name, an array with two elements
# $3 - Current name (the variable NAME for what {{.}} means)
#
# The array has the following elements.
# [0] = argument type, "NAME" or "VALUE"
# [1] = argument name or value
2015-01-27 01:55:06 +00:00
#
2015-10-02 14:46:57 +00:00
# Returns nothing.
2023-04-08 00:35:25 +00:00
mo::getArgumentBrace() {
2023-04-11 02:07:45 +00:00
local moResult moCurrent moArgs moUnparsedOriginal
2023-04-08 00:35:25 +00:00
2023-04-11 02:07:45 +00:00
moCurrent=$2
moUnparsedOriginal=$MO_UNPARSED
MO_UNPARSED="${MO_UNPARSED:1}"
mo::trimUnparsed
mo::parseValueInner moArgs "$moCurrent"
2023-04-08 00:35:25 +00:00
mo::evaluate moResult "$moCurrent" "${moArgs[@]}"
2023-04-11 02:07:45 +00:00
if [[ "${MO_UNPARSED:0:1}" != "}" ]]; then
mo::escape moResult "${moUnparsedOriginal:0:20}"
mo::error "Unbalanced brace near $moResult"
2023-04-08 00:35:25 +00:00
fi
2015-01-27 01:55:06 +00:00
2023-04-11 02:07:45 +00:00
MO_UNPARSED="${MO_UNPARSED:1}"
mo::trimUnparsed
2015-01-27 01:55:06 +00:00
2023-04-11 02:07:45 +00:00
local "$1" && mo::indirectArray "$1" "VALUE" "${moResult[0]}"
2015-01-27 01:55:06 +00:00
}
2023-04-08 00:35:25 +00:00
# Internal: Get an argument, which is the result of a subexpression as a NAME
2015-10-02 14:46:57 +00:00
#
2023-04-08 00:35:25 +00:00
# $1 - Destination variable name, an array with two elements
2023-04-11 02:07:45 +00:00
# $2 - Current name (the variable NAME for what {{.}} means)
2023-04-08 00:35:25 +00:00
#
# The array has the following elements.
# [0] = argument type, "NAME" or "VALUE"
# [1] = argument name or value
2015-01-23 17:43:08 +00:00
#
2015-10-02 14:46:57 +00:00
# Returns nothing.
2023-04-08 00:35:25 +00:00
mo::getArgumentParenthesis() {
2023-04-11 02:07:45 +00:00
local moResult moContent moCurrent moUnparsedOriginal
2015-01-23 17:43:08 +00:00
2023-04-11 02:07:45 +00:00
moCurrent=$2
moUnparsedOriginal=$MO_UNPARSED
MO_UNPARSED="${MO_UNPARSED:1}"
mo::trimUnparsed
mo::parseValueInner moResult "$moCurrent"
if [[ "${MO_UNPARSED:0:1}" != ")" ]]; then
mo::escape moResult "${moUnparsedOriginal:0:20}"
mo::error "Unbalanced parenthesis near $moResult"
2023-04-08 00:35:25 +00:00
fi
2015-01-23 17:43:08 +00:00
2023-04-11 02:07:45 +00:00
MO_UNPARSED=${MO_UNPARSED:1}
mo::trimUnparsed
2015-01-23 17:43:08 +00:00
2023-04-11 02:07:45 +00:00
local "$1" && mo::indirectArray "$1" "NAME" "${moResult[0]}"
2023-04-08 00:35:25 +00:00
}
2015-01-26 18:51:28 +00:00
2015-01-23 17:43:08 +00:00
2023-04-08 00:35:25 +00:00
# Internal: Get an argument in a double quoted string
#
# $1 - Destination variable name, an array with two elements
#
# The array has the following elements.
# [0] = argument type, "NAME" or "VALUE"
# [1] = argument name or value
#
# Returns nothing.
mo::getArgumentDoubleQuote() {
2023-04-11 02:07:45 +00:00
local moTemp moUnparsedOriginal
2015-01-23 17:43:08 +00:00
2023-04-08 00:35:25 +00:00
moTemp=""
2023-04-11 02:07:45 +00:00
moUnparsedOriginal=$MO_UNPARSED
MO_UNPARSED=${MO_UNPARSED:1}
2015-01-23 17:43:08 +00:00
2023-04-11 02:07:45 +00:00
while [[ "${MO_UNPARSED:0:1}" != '"' ]]; do
case "$MO_UNPARSED" in
2023-04-08 00:35:25 +00:00
\\n)
moTemp="$moTemp"$'\n'
2023-04-11 02:07:45 +00:00
MO_UNPARSED=${MO_UNPARSED:2}
2015-01-26 03:31:21 +00:00
;;
2023-04-08 00:35:25 +00:00
\\r)
moTemp="$moTemp"$'\r'
2023-04-11 02:07:45 +00:00
MO_UNPARSED=${MO_UNPARSED:2}
2015-01-23 17:43:08 +00:00
;;
2023-04-08 00:35:25 +00:00
\\t)
moTemp="$moTemp"$'\t'
2023-04-11 02:07:45 +00:00
MO_UNPARSED=${MO_UNPARSED:2}
2015-01-23 20:26:56 +00:00
;;
2023-04-08 00:35:25 +00:00
\\*)
2023-04-11 02:07:45 +00:00
moTemp="$moTemp${MO_UNPARSED:1:1}"
MO_UNPARSED=${MO_UNPARSED:2}
2023-04-04 02:12:17 +00:00
;;
2015-01-23 17:43:08 +00:00
*)
2023-04-11 02:07:45 +00:00
moTemp="$moTemp${MO_UNPARSED:0:1}"
MO_UNPARSED=${MO_UNPARSED:1}
2015-01-23 17:43:08 +00:00
;;
esac
2023-04-11 02:07:45 +00:00
if [[ -z "$MO_UNPARSED" ]]; then
mo::escape moTemp "${moUnparsedOriginal:0:20}"
mo::error "Found starting double quote but no closing double quote starting near $moTemp"
2023-04-08 00:35:25 +00:00
fi
2015-01-23 17:43:08 +00:00
done
2023-04-08 00:35:25 +00:00
mo::debug "Parsed double quoted value: $moTemp"
2023-04-11 02:07:45 +00:00
MO_UNPARSED=${MO_UNPARSED:1}
mo::trimUnparsed
2023-04-08 00:35:25 +00:00
2023-04-11 02:07:45 +00:00
local "$1" && mo::indirectArray "$1" "VALUE" "$moTemp"
2015-01-23 17:43:08 +00:00
}
2023-04-08 00:35:25 +00:00
# Internal: Get an argument in a single quoted string
2017-06-20 19:19:44 +00:00
#
2023-04-08 00:35:25 +00:00
# $1 - Destination variable name, an array with two elements
2015-01-26 21:09:27 +00:00
#
2023-04-08 00:35:25 +00:00
# The array has the following elements.
# [0] = argument type, "NAME" or "VALUE"
# [1] = argument name or value
2015-10-02 14:46:57 +00:00
#
# Returns nothing.
2023-04-08 00:35:25 +00:00
mo::getArgumentSingleQuote() {
2023-04-11 02:07:45 +00:00
local moTemp moUnparsedOriginal
2015-10-02 15:47:53 +00:00
2023-04-08 00:35:25 +00:00
moTemp=""
2023-04-11 02:07:45 +00:00
moUnparsedOriginal=$MO_UNPARSED
MO_UNPARSED=${MO_UNPARSED:1}
2023-04-08 00:35:25 +00:00
2023-04-11 02:07:45 +00:00
while [[ "${MO_UNPARSED:0:1}" != "'" ]]; do
moTemp="$moTemp${MO_UNPARSED:0:1}"
MO_UNPARSED=${MO_UNPARSED:1}
2015-01-26 21:09:27 +00:00
2023-04-11 02:07:45 +00:00
if [[ -z "$MO_UNPARSED" ]]; then
mo::escape moTemp "${moUnparsedOriginal:0:20}"
mo::error "Found starting single quote but no closing single quote starting near $moTemp"
2023-04-08 00:35:25 +00:00
fi
done
mo::debug "Parsed single quoted value: $moTemp"
2023-04-11 02:07:45 +00:00
MO_UNPARSED=${MO_UNPARSED:1}
mo::trimUnparsed
2023-04-08 00:35:25 +00:00
2023-04-11 02:07:45 +00:00
local "$1" && mo::indirectArray "$1" "VALUE" "$moTemp"
2015-01-26 21:09:27 +00:00
}
2023-04-08 00:35:25 +00:00
# Internal: Get an argument that is a simple variable name
2015-10-02 14:46:57 +00:00
#
2023-04-08 00:35:25 +00:00
# $1 - Destination variable name, an array with two elements
2015-01-27 01:55:06 +00:00
#
2023-04-08 00:35:25 +00:00
# The array has the following elements.
# [0] = argument type, "NAME" or "VALUE"
# [1] = argument name or value
2015-01-26 21:09:27 +00:00
#
2015-10-02 14:46:57 +00:00
# Returns nothing.
2023-04-08 00:35:25 +00:00
mo::getArgumentDefault() {
2023-04-11 02:07:45 +00:00
local moTemp
2023-04-08 00:35:25 +00:00
2023-04-11 02:07:45 +00:00
mo::chomp moTemp "${MO_UNPARSED%%"$MO_CLOSE_DELIMITER"*}"
2023-04-08 00:35:25 +00:00
moTemp=${moTemp%%)*}
moTemp=${moTemp%%\}*}
2023-04-11 02:07:45 +00:00
MO_UNPARSED=${MO_UNPARSED:${#moTemp}}
mo::trimUnparsed
2023-04-08 00:35:25 +00:00
mo::debug "Parsed default argument: $moTemp"
2023-04-11 02:07:45 +00:00
local "$1" && mo::indirectArray "$1" "NAME" "$moTemp"
2023-04-08 00:35:25 +00:00
}
2015-01-27 00:23:28 +00:00
2023-04-08 00:35:25 +00:00
# Internal: Determine if the given name is a defined function.
#
# $1 - Function name to check
#
# Be extremely careful. Even if strict mode is enabled, it is not honored
# in newer versions of Bash. Any errors that crop up here will not be
# caught automatically.
#
# Examples
#
# moo () {
# echo "This is a function"
# }
# if mo::isFunction moo; then
# echo "moo is a defined function"
# fi
#
# Returns 0 if the name is a function, 1 otherwise.
mo::isFunction() {
if declare -F "$1" &> /dev/null; then
2015-01-27 00:23:28 +00:00
return 0
fi
2023-04-08 00:35:25 +00:00
return 1
}
2015-05-05 15:46:06 +00:00
2023-04-08 00:35:25 +00:00
# Internal: Determine if a given environment variable exists and if it is
# an array.
#
# $1 - Name of environment variable
#
# Be extremely careful. Even if strict mode is enabled, it is not honored
# in newer versions of Bash. Any errors that crop up here will not be
# caught automatically.
#
# Examples
#
# var=(abc)
# if moIsArray var; then
# echo "This is an array"
# echo "Make sure you don't accidentally use \$var"
# fi
#
# Returns 0 if the name is not empty, 1 otherwise.
mo::isArray() {
# Namespace this variable so we don't conflict with what we're testing.
local moTestResult
moTestResult=$(declare -p "$1" 2>/dev/null) || return 1
[[ "${moTestResult:0:10}" == "declare -a" ]] && return 0
[[ "${moTestResult:0:10}" == "declare -A" ]] && return 0
return 1
2015-01-27 01:55:06 +00:00
}
2015-01-26 21:09:27 +00:00
2023-04-09 23:53:55 +00:00
# Internal: Determine if an array index exists.
#
# $1 - Variable name to check
# $2 - The index to check
#
# Has to check if the variable is an array and if the index is valid for that
# type of array.
#
# Returns true (0) if everything was ok, 1 if there's any condition that fails.
mo::isArrayIndexValid() {
local moDeclare moTest
moDeclare=$(declare -p "$1")
moTest=""
if [[ "${moDeclare:0:10}" == "declare -a" ]]; then
# Numerically indexed array - must check if the index looks like a
# number because using a string to index a numerically indexed array
# will appear like it worked.
if [[ "$2" == "0" ]] || [[ "$2" =~ ^[1-9][0-9]*$ ]]; then
# Index looks like a number
eval "moTest=\"\${$1[$2]+ok}\""
fi
elif [[ "${moDeclare:0:10}" == "declare -A" ]]; then
# Associative array
eval "moTest=\"\${$1[$2]+ok}\""
fi
if [[ -n "$moTest" ]]; then
return 0;
fi
return 1
}
2023-04-08 00:35:25 +00:00
# Internal: Determine if a variable is assigned, even if it is assigned an empty
# value.
2015-10-02 14:46:57 +00:00
#
2023-04-08 00:35:25 +00:00
# $1 - Variable name to check.
2015-01-27 01:55:06 +00:00
#
2023-04-08 04:10:47 +00:00
# Can not use logic like this in case invalid variable names are passed.
# [[ "${!1-a}" == "${!1-b}" ]]
#
2023-04-08 00:35:25 +00:00
# Returns true (0) if the variable is set, 1 if the variable is unset.
mo::isVarSet() {
2023-04-08 04:10:47 +00:00
if ! declare -p "$1" &> /dev/null; then
return 1
fi
return 0
2023-04-08 00:35:25 +00:00
}
2015-01-26 21:09:27 +00:00
2023-04-08 00:35:25 +00:00
# Internal: Determine if a value is considered truthy.
#
# $1 - The value to test
# $2 - Invert the value, either "true" or "false"
#
# Returns true (0) if truthy, 1 otherwise.
mo::isTruthy() {
local moTruthy
2015-01-26 21:09:27 +00:00
2023-04-08 00:35:25 +00:00
moTruthy=true
2015-01-26 21:09:27 +00:00
2023-04-08 00:35:25 +00:00
if [[ -z "${1-}" ]]; then
moTruthy=false
elif [[ -n "${MO_FALSE_IS_EMPTY-}" ]] && [[ "${1-}" == "false" ]]; then
moTruthy=false
2015-01-26 21:09:27 +00:00
fi
2023-04-08 00:35:25 +00:00
# XOR the results
# moTruthy inverse desiredResult
# true false true
# true true false
# false false false
# false true true
if [[ "$moTruthy" == "$2" ]]; then
2023-04-08 04:10:47 +00:00
mo::debug "Value is falsy, test result: $moTruthy inverse: $2"
2023-04-08 00:35:25 +00:00
return 1
fi
2023-04-08 04:10:47 +00:00
mo::debug "Value is truthy, test result: $moTruthy inverse: $2"
2023-04-08 00:35:25 +00:00
return 0
2015-01-26 21:09:27 +00:00
}
2023-04-08 00:35:25 +00:00
# Internal: Convert an argument list to values
#
# $1 - Destination variable name
# $2 - Current name (the variable NAME for what {{.}} means)
2023-04-10 18:24:06 +00:00
# $3-@ - A list of argument types and argument name/value.
2015-01-26 03:31:21 +00:00
#
2023-04-08 00:35:25 +00:00
# Sample call:
#
# mo::evaluate dest NAME username VALUE abc123
2015-10-02 14:46:57 +00:00
#
# Returns nothing.
2023-04-08 00:35:25 +00:00
mo::evaluate() {
local moResult moTarget moCurrent moFunction moArgs moTemp
2015-01-26 03:31:21 +00:00
2023-04-08 00:35:25 +00:00
moTarget=$1
moCurrent=$2
shift 2
if [[ "$1" == "NAME" ]] && mo::isFunction "$2"; then
# Special case - if the first argument is a function, then the rest are
# passed to the function.
moFunction=$2
mo::evaluateFunction moResult "" "${@:2}"
2015-01-26 03:31:21 +00:00
else
2023-04-08 00:35:25 +00:00
mo::evaluateListOfSingles moResult "$moCurrent" ${@+"$@"}
2015-01-26 03:31:21 +00:00
fi
2023-04-08 00:35:25 +00:00
local "$moTarget" && mo::indirect "$moTarget" "$moResult"
2015-01-26 03:31:21 +00:00
}
2023-04-08 00:35:25 +00:00
# Internal: Convert an argument list to individual values.
#
# $1 - Destination variable name
# $2 - Current name (the variable NAME for what {{.}} means)
2023-04-10 18:24:06 +00:00
# $3-@ - A list of argument types and argument name/value.
2023-04-08 00:35:25 +00:00
#
# This assumes each value is separate from the rest. In contrast, mo::evaluate
# will pass all arguments to a function if the first value is a function.
#
# Sample call:
2015-10-02 14:46:57 +00:00
#
2023-04-08 00:35:25 +00:00
# mo::evaluateListOfSingles dest NAME username VALUE abc123
2015-01-26 03:31:21 +00:00
#
2015-10-02 14:46:57 +00:00
# Returns nothing.
2023-04-08 00:35:25 +00:00
mo::evaluateListOfSingles() {
local moResult moTarget moTemp moCurrent
moTarget=$1
moCurrent=$2
shift 2
moResult=""
while [[ $# -gt 1 ]]; do
mo::evaluateSingle moTemp "$moCurrent" "$1" "$2"
moResult="$moResult$moTemp"
shift 2
done
mo::debug "Evaluated list of singles: $moResult"
local "$moTarget" && mo::indirect "$moTarget" "$moResult"
2015-01-26 03:31:21 +00:00
}
2023-04-08 00:35:25 +00:00
# Internal: Evaluate a single argument
2015-01-23 17:43:08 +00:00
#
2023-04-08 00:35:25 +00:00
# $1 - Name of variable for result
# $2 - Current name (the variable NAME for what {{.}} means)
# $3 - Type of argument, either NAME or VALUE
# $4 - Argument
2015-01-23 17:43:08 +00:00
#
2023-04-08 00:35:25 +00:00
# Returns nothing
mo::evaluateSingle() {
2023-04-09 23:53:55 +00:00
local moResult moCurrent moType moArg
2015-01-23 17:43:08 +00:00
2023-04-08 00:35:25 +00:00
moCurrent=$2
moType=$3
moArg=$4
2023-04-09 23:53:55 +00:00
mo::debug "Evaluating $moType: $moArg ($moCurrent)"
2023-04-08 00:35:25 +00:00
if [[ "$moType" == "VALUE" ]]; then
moResult=$moArg
elif [[ "$moArg" == "." ]]; then
2023-04-09 23:53:55 +00:00
mo::evaluateVariable moResult "$moCurrent" ""
2023-04-08 00:35:25 +00:00
elif [[ "$moArg" == "@key" ]]; then
mo::evaluateKey moResult "$moCurrent"
elif mo::isFunction "$moArg"; then
mo::evaluateFunction moResult "" "$moArg"
2015-01-23 17:43:08 +00:00
else
2023-04-09 23:53:55 +00:00
mo::evaluateVariable moResult "$moArg" "$moCurrent"
2023-04-08 00:35:25 +00:00
fi
local "$1" && mo::indirect "$1" "$moResult"
}
2016-07-27 14:56:33 +00:00
2023-04-08 00:35:25 +00:00
# Internal: Return the value for @key based on current's name
#
# $1 - Name of variable for result
# $2 - Current name (the variable NAME for what {{.}} means)
#
# Returns nothing
mo::evaluateKey() {
local moCurrent moResult
moCurrent=$2
if [[ "$moCurrent" == *.* ]]; then
moResult="${moCurrent#*.}"
else
moResult="${moCurrent}"
2015-01-23 17:43:08 +00:00
fi
2023-04-08 00:35:25 +00:00
local "$1" && mo::indirect "$1" "$moResult"
2015-01-23 17:43:08 +00:00
}
2023-04-08 00:35:25 +00:00
# Internal: Handle a variable name
2017-06-16 13:59:57 +00:00
#
2023-04-08 00:35:25 +00:00
# $1 - Destination variable name
# $2 - Variable name
2023-04-09 23:53:55 +00:00
# $3 - Current value
2017-06-16 13:59:57 +00:00
#
2023-04-08 00:35:25 +00:00
# Returns nothing.
mo::evaluateVariable() {
2023-04-10 13:10:14 +00:00
local moResult moCurrent moArg moNameParts
2023-04-08 00:35:25 +00:00
moArg=$2
2023-04-09 23:53:55 +00:00
moCurrent=$3
2023-04-08 00:35:25 +00:00
moResult=""
2023-04-09 23:53:55 +00:00
mo::findVariableName moNameParts "$moArg" "$moCurrent"
2023-04-10 16:39:47 +00:00
mo::debug "Evaluate variable ($moArg, $moCurrent): ${moNameParts[*]}"
2023-04-08 00:35:25 +00:00
2023-04-09 23:53:55 +00:00
if [[ -z "${moNameParts[1]}" ]]; then
2023-04-08 00:35:25 +00:00
if mo::isArray "$moArg"; then
2023-04-10 13:10:14 +00:00
eval mo::join moResult "," "\${${moArg}[@]}"
2023-04-08 00:35:25 +00:00
else
if mo::isVarSet "$moArg"; then
moResult="${!moArg}"
elif [[ -n "${MO_FAIL_ON_UNSET-}" ]]; then
mo::error "Environment variable not set: $moArg"
fi
fi
else
if mo::isArray "${moNameParts[0]}"; then
2023-04-09 15:20:17 +00:00
eval "set +u;moResult=\"\${${moNameParts[0]}[${moNameParts[1]%%.*}]}\""
2023-04-08 00:35:25 +00:00
else
mo::error "Unable to index a scalar as an array: $moArg"
fi
fi
2023-04-10 13:10:14 +00:00
local "$1" && mo::indirect "$1" "$moResult"
2017-06-16 13:59:57 +00:00
}
2015-01-23 17:43:08 +00:00
2023-04-09 23:53:55 +00:00
# Internal: Find the name of a variable to use
#
# $1 - Destination variable name, receives an array
# $2 - Variable name from the template
# $3 - The name of the "current value", from block parsing
#
# The array contains the following values
# [0] - Variable name
# [1] - Array index, or empty string
#
# Example variables
# a="a"
# b="b"
# c=("c.0" "c.1")
# d=([b]="d.b" [d]="d.d")
#
# Given these inputs, produce these outputs
# a c => a
# a c.0 => a
# b d => d.b
# b d.d => d.b
# a d => d.a
# a d.d => d.a
# c.0 d => c.0
# d.b d => d.b
# Returns nothing.
mo::findVariableName() {
local moVar moCurrent moNameParts moResultBase moResultIndex
moVar=$2
moCurrent=$3
moResultBase=$moVar
moResultIndex=""
if [[ "$moVar" == *.* ]]; then
mo::debug "Find variable name; name has dot: $moVar"
moResultBase=${moVar%%.*}
moResultIndex=${moVar#*.}
elif [[ -n "$moCurrent" ]]; then
moCurrent=${moCurrent%%.*}
mo::debug "Find variable name; look in array: $moCurrent"
if mo::isArrayIndexValid "$moCurrent" "$moVar"; then
moResultBase=$moCurrent
moResultIndex=$moVar
fi
fi
local "$1" && mo::indirectArray "$1" "$moResultBase" "$moResultIndex"
}
2023-04-08 00:35:25 +00:00
# Internal: Join / implode an array
2015-10-02 14:46:57 +00:00
#
2023-04-08 00:35:25 +00:00
# $1 - Variable name to receive the joined content
# $2 - Joiner
2023-04-10 18:24:06 +00:00
# $3-@ - Elements to join
2015-01-23 17:43:08 +00:00
#
2015-10-02 14:46:57 +00:00
# Returns nothing.
2023-04-10 16:39:47 +00:00
mo::join() {
2023-04-08 00:35:25 +00:00
local joiner part result target
2015-01-23 17:43:08 +00:00
2015-10-02 15:47:53 +00:00
target=$1
2023-04-08 00:35:25 +00:00
joiner=$2
result=$3
shift 3
for part in "$@"; do
result="$result$joiner$part"
done
local "$target" && mo::indirect "$target" "$result"
}
# Internal: Call a function.
#
# $1 - Variable for output
# $2 - Content to pass
# $3 - Function to call
2023-04-10 18:24:06 +00:00
# $4-@ - Additional arguments as list of type, value/name
2023-04-08 00:35:25 +00:00
#
# Returns nothing.
mo::evaluateFunction() {
2023-04-10 13:10:14 +00:00
local moArgs moContent moFunctionResult moTarget moFunction moTemp moFunctionCall
2023-04-08 00:35:25 +00:00
moTarget=$1
moContent=$2
moFunction=$3
shift 3
moArgs=()
while [[ $# -gt 1 ]]; do
mo::evaluateSingle moTemp "$moCurrent" "$1" "$2"
moArgs=(${moArgs[@]+"${moArgs[@]}"} "$moTemp")
shift 2
2015-01-23 17:43:08 +00:00
done
2023-04-08 04:10:47 +00:00
mo::escape moFunctionCall "$moFunction"
2023-04-08 00:35:25 +00:00
if [[ -n "${MO_ALLOW_FUNCTION_ARGUMENTS-}" ]]; then
2023-04-08 04:10:47 +00:00
mo::debug "Function arguments are allowed"
for moTemp in "${moArgs[@]}"; do
mo::escape moTemp "$moTemp"
moFunctionCall="$moFunctionCall $moTemp"
done
2023-04-08 00:35:25 +00:00
fi
2023-04-08 04:10:47 +00:00
mo::debug "Calling function: $moFunctionCall"
2023-04-10 13:10:14 +00:00
# Call the function in a subshell for safety. Employ the trick to preserve
# whitespace at the end of the output.
moContent=$(export MO_FUNCTION_ARGS=("${moArgs[@]}"); echo -n "$moContent" | eval "$moFunctionCall ; moFunctionResult=\$? ; echo -n '.' ; exit \"\$moFunctionResult\"") || {
2023-04-08 00:35:25 +00:00
moFunctionResult=$?
if [[ -n "${MO_FAIL_ON_FUNCTION-}" && "$moFunctionResult" != 0 ]]; then
2023-04-10 13:10:14 +00:00
mo::error "Function failed with status code $moFunctionResult: $moFunctionCall" "$moFunctionResult"
2023-04-08 00:35:25 +00:00
fi
}
2023-04-10 13:10:14 +00:00
local "$moTarget" && mo::indirect "$moTarget" "${moContent%.}"
2023-04-08 00:35:25 +00:00
}
# Internal: Check if a tag appears to have only whitespace before it and after
# it on a line. There must be a new line before (see the trick in mo::parse)
# and there must be a newline after or the end of a string
#
# Returns 0 if this is a standalone tag, 1 otherwise.
mo::standaloneCheck() {
local moContent moN moR moT
moN=$'\n'
moR=$'\r'
moT=$'\t'
# Check the content before
2023-04-10 18:24:06 +00:00
moContent=${MO_STANDALONE_CONTENT//"$moR"/"$moN"}
# By default, signal to the next check that this one failed
MO_STANDALONE_CONTENT=""
2023-04-08 00:35:25 +00:00
if [[ "$moContent" != *"$moN"* ]]; then
mo::debug "Not a standalone tag - no newline before"
2023-04-09 00:06:04 +00:00
2023-04-08 00:35:25 +00:00
return 1
fi
2023-04-10 13:10:14 +00:00
moContent=${moContent##*"$moN"}
moContent=${moContent//"$moT"/}
2023-04-08 00:35:25 +00:00
moContent=${moContent// /}
if [[ -n "$moContent" ]]; then
mo::debug "Not a standalone tag - non-whitespace detected before tag"
2023-04-09 00:06:04 +00:00
2023-04-08 00:35:25 +00:00
return 1
fi
# Check the content after
2023-04-11 02:07:45 +00:00
moContent=${MO_UNPARSED//"$moR"/"$moN"}
2023-04-10 13:10:14 +00:00
moContent=${moContent%%"$moN"*}
moContent=${moContent//"$moT"/}
2023-04-08 00:35:25 +00:00
moContent=${moContent// /}
if [[ -n "$moContent" ]]; then
mo::debug "Not a standalone tag - non-whitespace detected after tag"
2023-04-09 00:06:04 +00:00
2023-04-08 00:35:25 +00:00
return 1
fi
2023-04-10 18:24:06 +00:00
# Signal to the next check that this tag removed content
MO_STANDALONE_CONTENT=$'\n'
2023-04-08 00:35:25 +00:00
return 0
2015-01-26 03:31:21 +00:00
}
2023-04-11 02:07:45 +00:00
# Internal: Process content before and after a tag. Remove prior whitespace up to the previous newline. Remove following whitespace up to and including the next newline.
2015-10-02 14:06:16 +00:00
#
# Returns nothing.
2023-04-11 02:07:45 +00:00
mo::standaloneProcess() {
2023-04-08 00:35:25 +00:00
local moContent moLast moT moR moN
moT=$'\t'
moR=$'\r'
moN=$'\n'
moLast=
2023-04-11 02:07:45 +00:00
mo::debug "Standalone tag - processing content before and after tag"
2023-04-08 00:35:25 +00:00
2023-04-11 02:07:45 +00:00
while [[ "$moLast" != "$MO_PARSED" ]]; do
moLast=$MO_PARSED
MO_PARSED=${MO_PARSED% }
MO_PARSED=${MO_PARSED%"$moT"}
2023-04-08 00:35:25 +00:00
done
2023-04-11 02:07:45 +00:00
moLast=
while [[ "$moLast" != "$MO_UNPARSED" ]]; do
moLast=$MO_UNPARSED
MO_UNPARSED=${MO_UNPARSED# }
MO_UNPARSED=${MO_UNPARSED#"$moT"}
done
2023-04-08 00:35:25 +00:00
2023-04-11 02:07:45 +00:00
MO_UNPARSED=${MO_UNPARSED#"$moR"}
MO_UNPARSED=${MO_UNPARSED#"$moN"}
2015-10-02 14:06:16 +00:00
}
2023-04-11 02:07:45 +00:00
# Internal: Apply indentation before any line that has content in MO_UNPARSED.
2023-04-08 04:10:47 +00:00
#
2023-04-11 02:07:45 +00:00
# $1 - The indentation string
2023-04-08 04:10:47 +00:00
#
# Returns nothing.
mo::indentLines() {
local moContent moIndentation moResult moN moR moChunk
2023-04-11 02:07:45 +00:00
moIndentation=$1
if [[ -z "$moIndentation" ]] || [[ -z "$MO_UNPARSED" ]]; then
mo::debug "Not applying indentation, indentation ${#moIndentation} bytes, content ${#MO_UNPARSED} bytes"
return
fi
moContent=$MO_UNPARSED
MO_UNPARSED=
2023-04-08 04:10:47 +00:00
moN=$'\n'
moR=$'\r'
2023-04-11 02:07:45 +00:00
mo::debug "Applying indentation: '${moIndentation}'"
2023-04-08 04:10:47 +00:00
2023-04-11 02:07:45 +00:00
while [[ -n "$moContent" ]]; do
moChunk=${moContent%%"$moN"*}
moChunk=${moChunk%%"$moR"*}
moContent=${moContent:${#moChunk}}
2023-04-08 04:10:47 +00:00
2023-04-11 02:07:45 +00:00
if [[ -n "$moChunk" ]]; then
MO_UNPARSED="$MO_UNPARSED$moIndentation$moChunk"
fi
2023-04-08 04:10:47 +00:00
2023-04-11 02:07:45 +00:00
MO_UNPARSED="$MO_UNPARSED${moContent:0:1}"
moContent=${moContent:1}
done
2023-04-08 04:10:47 +00:00
}
# Internal: Escape a value
#
# $1 - Destination variable name
# $2 - Value to escape
#
# Returns nothing
mo::escape() {
local moResult
moResult=$2
moResult=$(declare -p moResult)
moResult=${moResult#*=}
local "$1" && mo::indirect "$1" "$moResult"
}
2016-07-27 14:56:33 +00:00
# Save the original command's path for usage later
2016-10-25 11:11:33 +00:00
MO_ORIGINAL_COMMAND="$(cd "${BASH_SOURCE[0]%/*}" || exit 1; pwd)/${BASH_SOURCE[0]##*/}"
2023-04-08 00:35:25 +00:00
MO_VERSION="3.0.0"
2016-07-27 14:56:33 +00:00
2015-10-02 14:46:57 +00:00
# If sourced, load all functions.
# If executed, perform the actions as expected.
2016-10-25 10:59:52 +00:00
if [[ "$0" == "${BASH_SOURCE[0]}" ]] || [[ -z "${BASH_SOURCE[0]}" ]]; then
2015-08-21 16:56:52 +00:00
mo "$@"
fi