From 85244ecdcb86e203b9775e69c0fc66c401afc132 Mon Sep 17 00:00:00 2001
From: Chris Fiege <chris@tinyhost.de>
Date: Tue, 5 Jun 2018 19:28:47 +0200
Subject: [PATCH] Add mo to repo to make it self contained

Signed-off-by: Chris Fiege <chris@tinyhost.de>
---
 .gitignore  |   2 +-
 generate.sh |   2 +-
 mo          | 726 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 728 insertions(+), 2 deletions(-)
 create mode 100755 mo

diff --git a/.gitignore b/.gitignore
index a585b7a..4f5d5e0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,8 +4,8 @@
 anleitung-backbone.html
 anleitung-business.html
 anleitung-starter.html
-auswahl.html
 eintragen.html
+event.html
 faq.html
 impressum.html
 index.html
diff --git a/generate.sh b/generate.sh
index f43b3c6..3f2b85a 100755
--- a/generate.sh
+++ b/generate.sh
@@ -7,7 +7,7 @@ cp .gitignore.template .gitignore
 (echo "# ignoring from *.template generated files"
 for file in $(ls *.template); do
 	new=$(echo $file | sed 's/template/html/')
-	/home/kasalehlia/mo $file > $new
+	./mo $file > $new
 	echo "$new"
 done) >> .gitignore
 
diff --git a/mo b/mo
new file mode 100755
index 0000000..07da321
--- /dev/null
+++ b/mo
@@ -0,0 +1,726 @@
+#!/bin/bash
+#
+# Mo is a mustache template rendering software written in bash.  It inserts
+# environment variables into templates.
+#
+# Learn more about mustache templates at https://mustache.github.io/
+#
+# 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
+
+
+# Scan content until the right end tag is found.  Returns an array with the
+# following members:
+#     [0] = Content before end tag
+#     [1] = End tag (complete tag)
+#     [2] = Content after end tag
+#
+# Everything using this function uses the "standalone tags" logic.
+#
+# Parameters:
+#     $1: Where to store the array
+#     $2: Content
+#     $3: Name of end tag
+#     $4: If -z, do standalone tag processing before finishing
+mustache-find-end-tag() {
+    local CONTENT SCANNED
+
+    # Find open tags
+    SCANNED=""
+    mustache-split CONTENT "$2" '{{' '}}'
+
+    while [[ "${#CONTENT[@]}" -gt 1 ]]; do
+        mustache-trim-whitespace TAG "${CONTENT[1]}"
+
+        # Restore CONTENT[1] before we start using it
+        CONTENT[1]='{{'"${CONTENT[1]}"'}}'
+
+        case $TAG in
+            '#'* | '^'*)
+                # Start another block
+                SCANNED="${SCANNED}${CONTENT[0]}${CONTENT[1]}"
+                mustache-trim-whitespace TAG "${TAG:1}"
+                mustache-find-end-tag CONTENT "${CONTENT[2]}" "$TAG" "loop"
+                SCANNED="${SCANNED}${CONTENT[0]}${CONTENT[1]}"
+                CONTENT=${CONTENT[2]}
+                ;;
+
+            '/'*)
+                # End a block - could be ours
+                mustache-trim-whitespace TAG "${TAG:1}"
+                SCANNED="$SCANNED${CONTENT[0]}"
+
+                if [[ "$TAG" == "$3" ]]; then
+                    # Found our end tag
+                    if [[ -z "$4" ]] && mustache-is-standalone STANDALONE_BYTES "$SCANNED" "${CONTENT[2]}" true; then
+                        # This is also a standalone tag - clean up whitespace
+                        # and move those whitespace bytes to the "tag" element
+                        STANDALONE_BYTES=( $STANDALONE_BYTES )
+                        CONTENT[1]="${SCANNED:${STANDALONE_BYTES[0]}}${CONTENT[1]}${CONTENT[2]:0:${STANDALONE_BYTES[1]}}"
+                        SCANNED="${SCANNED:0:${STANDALONE_BYTES[0]}}"
+                        CONTENT[2]="${CONTENT[2]:${STANDALONE_BYTES[1]}}"
+                    fi
+
+                    local "$1" && mustache-indirect-array "$1" "$SCANNED" "${CONTENT[1]}" "${CONTENT[2]}"
+                    return 0
+                fi
+
+                SCANNED="$SCANNED${CONTENT[1]}"
+                CONTENT=${CONTENT[2]}
+                ;;
+
+            *)
+                # Ignore all other tags
+                SCANNED="${SCANNED}${CONTENT[0]}${CONTENT[1]}"
+                CONTENT=${CONTENT[2]}
+                ;;
+        esac
+
+        mustache-split CONTENT "$CONTENT" '{{' '}}'
+    done
+
+    # Did not find our closing tag
+    SCANNED="$SCANNED${CONTENT[0]}"
+    local "$1" && mustache-indirect-array "$1" "${SCANNED}" "" ""
+}
+
+
+# Find the first index of a substring
+#
+# Parameters:
+#     $1: Destination variable
+#     $2: Haystack
+#     $3: Needle
+mustache-find-string() {
+    local POS STRING
+
+    STRING=${2%%$3*}
+    [[ "$STRING" == "$2" ]] && POS=-1 || POS=${#STRING}
+    local "$1" && mustache-indirect "$1" $POS
+}
+
+
+# Return a dotted name based on current context and target name
+#
+# Parameters:
+#     $1: Target variable to store results
+#     $2: Context name
+#     $3: Desired variable name
+mustache-full-tag-name() {
+    if [[ -z "$2" ]]; then
+        local "$1" && mustache-indirect "$1" "$3"
+    else
+        local "$1" && mustache-indirect "$1" "${2}.${3}"
+    fi
+}
+
+
+# Return the content to parse.  Can be a list of partials for files or
+# the content from stdin.
+#
+# Parameters:
+#     $1: Variable name to assign this content back as
+#     $2-*: File names (optional)
+mustache-get-content() {
+    local CONTENT FILENAME TARGET
+
+    TARGET=$1
+    shift
+    if [[ "${#@}" -gt 0 ]]; then
+        CONTENT=""
+
+        for FILENAME in "$@"; do
+            # This is so relative paths work from inside template files
+            CONTENT="$CONTENT"'{{>'"$FILENAME"'}}'
+        done
+    else
+        mustache-load-file CONTENT /dev/stdin
+    fi
+
+    local "$TARGET" && mustache-indirect "$TARGET" "$CONTENT"
+}
+
+
+# Indent a string, placing the indent at the beginning of every
+# line that has any content.
+#
+# Parameters:
+#     $1: Name of destination variable to get an array of lines
+#     $2: The indent string
+#     $3: The string to reindent
+mustache-indent-lines() {
+    local CONTENT FRAGMENT LEN POS_N POS_R RESULT TRIMMED
+
+    RESULT=""
+    LEN=$((${#3} - 1))
+    CONTENT="${3:0:$LEN}" # Remove newline and dot from workaround - in mustache-partial
+
+    if [ -z "$2" ]; then
+        local "$1" && mustache-indirect "$1" "$CONTENT"
+        return 0
+    fi
+
+    mustache-find-string POS_N "$CONTENT" $'\n'
+    mustache-find-string POS_R "$CONTENT" $'\r'
+
+    while [[ "$POS_N" -gt -1 ]] || [[ "$POS_R" -gt -1 ]]; do
+        if [[ "$POS_N" -gt -1 ]]; then
+            FRAGMENT="${CONTENT:0:$POS_N + 1}"
+            CONTENT=${CONTENT:$POS_N + 1}
+        else
+            FRAGMENT="${CONTENT:0:$POS_R + 1}"
+            CONTENT=${CONTENT:$POS_R + 1}
+        fi
+
+        mustache-trim-chars TRIMMED "$FRAGMENT" false true " " $'\t' $'\n' $'\r'
+
+        if [ ! -z "$TRIMMED" ]; then
+            FRAGMENT="$2$FRAGMENT"
+        fi
+
+        RESULT="$RESULT$FRAGMENT"
+        mustache-find-string POS_N "$CONTENT" $'\n'
+        mustache-find-string POS_R "$CONTENT" $'\r'
+    done
+
+    mustache-trim-chars TRIMMED "$CONTENT" false true " " $'\t'
+
+    if [ ! -z "$TRIMMED" ]; then
+        CONTENT="$2$CONTENT"
+    fi
+
+    RESULT="$RESULT$CONTENT"
+
+    local "$1" && mustache-indirect "$1" "$RESULT"
+}
+
+
+# Send a variable up to caller of a function
+#
+# Parameters:
+#     $1: Variable name
+#     $2: Value
+mustache-indirect() {
+    unset -v "$1"
+    printf -v "$1" '%s' "$2"
+}
+
+
+# Send an array up to caller of a function
+#
+# Parameters:
+#     $1: Variable name
+#     $2-*: Array elements
+mustache-indirect-array() {
+    unset -v "$1"
+    eval $1=\(\"\${@:2}\"\)
+}
+
+
+# Determine if a given environment variable exists and if it is an array.
+#
+# Parameters:
+#     $1: Name of environment variable
+#
+# Return code:
+#     0 if the name is not empty, 1 otherwise
+mustache-is-array() {
+    local MUSTACHE_TEST
+
+    MUSTACHE_TEST=$(declare -p "$1" 2>/dev/null) || return 1
+    [[ "${MUSTACHE_TEST:0:10}" == "declare -a" ]] && return 0
+    [[ "${MUSTACHE_TEST:0:10}" == "declare -A" ]] && return 0
+
+    return 1
+}
+
+
+# Return 0 if the passed name is a function.
+#
+# Parameters:
+#     $1: Name to check if it's a function
+#
+# Return code:
+#     0 if the name is a function, 1 otherwise
+mustache-is-function() {
+    local FUNCTIONS NAME
+
+    FUNCTIONS=$(declare -F)
+    FUNCTIONS=( ${FUNCTIONS//declare -f /} )
+
+    for NAME in ${FUNCTIONS[@]}; do
+        if [[ "$NAME" == "$1" ]]; then
+            return 0
+        fi
+    done
+
+    return 1
+}
+
+
+# Determine if the tag is a standalone tag based on whitespace before and
+# after the tag.
+#
+# Passes back a string containing two numbers in the format "BEFORE AFTER"
+# like "27 10".  It indicates the number of bytes remaining in the "before"
+# string (27) and the number of bytes to trim in the "after" string (10).
+# Useful for string manipulation:
+#
+#     mustache-is-standalone RESULT "$before" "$after" false || return 0
+#     RESULT_ARRAY=( $RESULT )
+#     echo "${before:0:${RESULT_ARRAY[0]}}...${after:${RESULT_ARRAY[1]}}"
+#
+# Parameters:
+#     $1: Variable to pass data back
+#     $2: Content before the tag
+#     $3: Content after the tag
+#     $4: true/false: is this the beginning of the content?
+mustache-is-standalone() {
+    local AFTER_TRIMMED BEFORE_TRIMMED CHAR
+
+    mustache-trim-chars BEFORE_TRIMMED "$2" false true " " $'\t'
+    mustache-trim-chars AFTER_TRIMMED "$3" true false " " $'\t'
+    CHAR=$((${#BEFORE_TRIMMED} - 1))
+    CHAR=${BEFORE_TRIMMED:$CHAR}
+
+    if [[ "$CHAR" != $'\n' ]] && [[ "$CHAR" != $'\r' ]]; then
+        if [[ ! -z "$CHAR" ]] || ! $4; then
+            return 1;
+        fi
+    fi
+
+    CHAR=${AFTER_TRIMMED:0:1}
+
+    if [[ "$CHAR" != $'\n' ]] && [[ "$CHAR" != $'\r' ]] && [[ ! -z "$CHAR" ]]; then
+        return 2;
+    fi
+
+    if [[ "$CHAR" == $'\r' ]] && [[ "${AFTER_TRIMMED:1:1}" == $'\n' ]]; then
+        CHAR="$CHAR"$'\n'
+    fi
+
+    local "$1" && mustache-indirect "$1" "$((${#BEFORE_TRIMMED})) $((${#3} + ${#CHAR} - ${#AFTER_TRIMMED}))"
+}
+
+
+# Join / implode an array
+#
+# Parameters:
+#     $1: Variable name to receive the joined content
+#     $2: Joiner
+#     $3-$*: Elements to join
+mustache-join() {
+    local JOINER PART RESULT TARGET
+
+    TARGET=$1
+    JOINER=$2
+    RESULT=$3
+    shift 3
+
+    for PART in "$@"; do
+        RESULT="$RESULT$JOINER$PART"
+    done
+
+    local "$TARGET" && mustache-indirect "$TARGET" "$RESULT"
+}
+
+# Read a file
+#
+# Parameters:
+#     $1: Variable name to receive the file's content
+#     $2: Filename to load
+mustache-load-file() {
+    local CONTENT LEN
+
+    # The subshell removes any trailing newlines.  We forcibly add
+    # a dot to the content to preserve all newlines.
+    # TODO: remove cat and replace with read loop?
+    CONTENT=$(cat $2; echo '.')
+    LEN=$((${#CONTENT} - 1))
+    CONTENT=${CONTENT:0:$LEN}  # Remove last dot
+
+    local "$1" && mustache-indirect "$1" "$CONTENT"
+}
+
+
+# Process a chunk of content some number of times.
+#
+# Parameters:
+#     $1: Content to parse and reparse and reparse
+#     $2: Tag prefix (context name)
+#     $3-*: Names to insert into the parsed content
+mustache-loop() {
+    local CONTENT CONTEXT CONTEXT_BASE IGNORE
+
+    CONTENT=$1
+    CONTEXT_BASE=$2
+    shift 2
+
+    while [[ "${#@}" -gt 0 ]]; do
+        mustache-full-tag-name CONTEXT "$CONTEXT_BASE" "$1"
+        mustache-parse "$CONTENT" "$CONTEXT" false
+        shift
+    done
+}
+
+
+# Parse a block of text
+#
+# Parameters:
+#     $1: Block of text to change
+#     $2: Current name (the variable NAME for what {{.}} means)
+#     $3: true when no content before this, false otherwise
+mustache-parse() {
+    # Keep naming variables MUSTACHE_* here to not overwrite needed variables
+    # used in the string replacements
+    local MUSTACHE_BLOCK MUSTACHE_CONTENT MUSTACHE_CURRENT MUSTACHE_IS_BEGINNING MUSTACHE_TAG
+
+    MUSTACHE_CURRENT=$2
+    MUSTACHE_IS_BEGINNING=$3
+
+    # Find open tags
+    mustache-split MUSTACHE_CONTENT "$1" '{{' '}}'
+
+    while [[ "${#MUSTACHE_CONTENT[@]}" -gt 1 ]]; do
+        mustache-trim-whitespace MUSTACHE_TAG "${MUSTACHE_CONTENT[1]}"
+
+        case $MUSTACHE_TAG in
+            '#'*)
+                # Loop, if/then, or pass content through function
+                # Sets context
+                mustache-standalone-allowed MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING
+                mustache-trim-whitespace MUSTACHE_TAG "${MUSTACHE_TAG:1}"
+                mustache-find-end-tag MUSTACHE_BLOCK "$MUSTACHE_CONTENT" "$MUSTACHE_TAG"
+                mustache-full-tag-name MUSTACHE_TAG "$MUSTACHE_CURRENT" "$MUSTACHE_TAG"
+
+                if mustache-test "$MUSTACHE_TAG"; then
+                    # Show / loop / pass through function
+                    if mustache-is-function "$MUSTACHE_TAG"; then
+                        # TODO: Consider piping the output to
+                        # mustache-get-content so the lambda does not
+                        # execute in a subshell?
+                        MUSTACHE_CONTENT=$($MUSTACHE_TAG "${MUSTACHE_BLOCK[0]}")
+                        mustache-parse "$MUSTACHE_CONTENT" "$MUSTACHE_CURRENT" false
+                        MUSTACHE_CONTENT="${MUSTACHE_BLOCK[2]}"
+                    elif mustache-is-array "$MUSTACHE_TAG"; then
+                        eval 'mustache-loop "${MUSTACHE_BLOCK[0]}" "$MUSTACHE_TAG" "${!'"$MUSTACHE_TAG"'[@]}"'
+                    else
+                        mustache-parse "${MUSTACHE_BLOCK[0]}" "$MUSTACHE_CURRENT" false
+                    fi
+                fi
+
+                MUSTACHE_CONTENT="${MUSTACHE_BLOCK[2]}"
+                ;;
+
+            '>'*)
+                # Load partial - get name of file relative to cwd
+                mustache-partial MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING "$MUSTACHE_CURRENT"
+                ;;
+
+            '/'*)
+                # Closing tag - If hit in this loop, we simply ignore
+                # Matching tags are found in mustache-find-end-tag
+                mustache-standalone-allowed MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING
+                ;;
+
+            '^'*)
+                # Display section if named thing does not exist
+                mustache-standalone-allowed MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING
+                mustache-trim-whitespace MUSTACHE_TAG "${MUSTACHE_TAG:1}"
+                mustache-find-end-tag MUSTACHE_BLOCK "$MUSTACHE_CONTENT" "$MUSTACHE_TAG"
+                mustache-full-tag-name MUSTACHE_TAG "$MUSTACHE_CURRENT" "$MUSTACHE_TAG"
+
+                if ! mustache-test "$MUSTACHE_TAG"; then
+                    mustache-parse "${MUSTACHE_BLOCK[0]}" "$MUSTACHE_CURRENT" false "$MUSTACHE_CURRENT"
+                fi
+
+                MUSTACHE_CONTENT="${MUSTACHE_BLOCK[2]}"
+                ;;
+
+            '!'*)
+                # Comment - ignore the tag content entirely
+                # Trim spaces/tabs before the comment
+                mustache-standalone-allowed MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING
+                ;;
+
+            .)
+                # Current content (environment variable or function)
+                mustache-standalone-denied MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}"
+                mustache-show "$MUSTACHE_CURRENT" "$MUSTACHE_CURRENT"
+                ;;
+
+            '=')
+                # Change delimiters
+                # Any two non-whitespace sequences separated by whitespace.
+                # TODO
+                mustache-standalone-allowed MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING
+                ;;
+
+            '{'*)
+                # Unescaped - split on }}} not }}
+                mustache-standalone-denied MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}"
+                MUSTACHE_CONTENT="${MUSTACHE_TAG:1}"'}}'"$MUSTACHE_CONTENT"
+                mustache-split MUSTACHE_CONTENT "$MUSTACHE_CONTENT" '}}}'
+                mustache-trim-whitespace MUSTACHE_TAG "${MUSTACHE_CONTENT[0]}"
+                mustache-full-tag-name MUSTACHE_TAG "$MUSTACHE_CURRENT" "$MUSTACHE_TAG"
+                MUSTACHE_CONTENT=${MUSTACHE_CONTENT[1]}
+
+                # Now show the value
+                mustache-show "$MUSTACHE_TAG" "$MUSTACHE_CURRENT"
+                ;;
+
+            '&'*)
+                # Unescaped
+                mustache-standalone-denied MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}"
+                mustache-trim-whitespace MUSTACHE_TAG "${MUSTACHE_TAG:1}"
+                mustache-full-tag-name MUSTACHE_TAG "$MUSTACHE_CURRENT" "$MUSTACHE_TAG"
+                mustache-show "$MUSTACHE_TAG" "$MUSTACHE_CURRENT"
+                ;;
+
+            *)
+                # Normal environment variable or function call
+                mustache-standalone-denied MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}"
+                mustache-full-tag-name MUSTACHE_TAG "$MUSTACHE_CURRENT" "$MUSTACHE_TAG"
+                mustache-show "$MUSTACHE_TAG" "$MUSTACHE_CURRENT"
+                ;;
+        esac
+
+        MUSTACHE_IS_BEGINNING=false
+        mustache-split MUSTACHE_CONTENT "$MUSTACHE_CONTENT" '{{' '}}'
+    done
+
+    echo -n "${MUSTACHE_CONTENT[0]}"
+}
+
+
+# Process a partial
+#
+# Indentation should be applied to the entire partial
+#
+# Prefix all variables
+#
+# Parameters:
+#     $1: Name of destination "content" variable.
+#     $2: Content before the tag that was not yet written
+#     $3: Tag content
+#     $4: Content after the tag
+#     $5: true/false: is this the beginning of the content?
+#     $6: Current context name
+mustache-partial() {
+    local MUSTACHE_CONTENT MUSTACHE_FILENAME MUSTACHE_INDENT MUSTACHE_LINE MUSTACHE_PARTIAL MUSTACHE_STANDALONE
+
+    if mustache-is-standalone MUSTACHE_STANDALONE "$2" "$4" $5; then
+        MUSTACHE_STANDALONE=( $MUSTACHE_STANDALONE )
+        echo -n "${2:0:${MUSTACHE_STANDALONE[0]}}"
+        MUSTACHE_INDENT=${2:${MUSTACHE_STANDALONE[0]}}
+        MUSTACHE_CONTENT=${4:${MUSTACHE_STANDALONE[1]}}
+    else
+        MUSTACHE_INDENT=""
+        echo -n "$2"
+        MUSTACHE_CONTENT=$4
+    fi
+
+    mustache-trim-whitespace MUSTACHE_FILENAME "${3:1}"
+
+    # Execute in subshell to preserve current cwd and environment
+    (
+        # TODO:  Remove dirname and use a function instead
+        cd "$(dirname "$MUSTACHE_FILENAME")"
+        mustache-indent-lines MUSTACHE_PARTIAL "$MUSTACHE_INDENT" "$(
+            mustache-load-file MUSTACHE_PARTIAL "${MUSTACHE_FILENAME##*/}"
+
+            # Fix bash handling of subshells
+            # The extra dot is removed in mustache-indent-lines
+            echo -n "${MUSTACHE_PARTIAL}."
+        )"
+        mustache-parse "$MUSTACHE_PARTIAL" "$6" true
+    )
+
+    local "$1" && mustache-indirect "$1" "$MUSTACHE_CONTENT"
+}
+
+
+# Show an environment variable or the output of a function.
+#
+# Limit/prefix any variables used
+#
+# Parameters:
+#     $1: Name of environment variable or function
+#     $2: Current context
+mustache-show() {
+    local JOINED MUSTACHE_NAME_PARTS
+
+    if mustache-is-function "$1"; then
+        CONTENT=$($1 "")
+        mustache-parse "$CONTENT" "$2" false
+        return 0
+    fi
+
+    mustache-split MUSTACHE_NAME_PARTS "$1" "."
+
+    if [[ -z "${MUSTACHE_NAME_PARTS[1]}" ]]; then
+        if mustache-is-array "$1"; then
+            eval mustache-join JOINED "," "\${$1[@]}"
+            echo -n "$JOINED"
+        else
+            echo -n "${!1}"
+        fi
+    else
+        # Further subindexes are disallowed
+        eval 'echo -n "${'"${MUSTACHE_NAME_PARTS[0]}"'['"${MUSTACHE_NAME_PARTS[1]%%.*}"']}"'
+    fi
+}
+
+
+# Split a larger string into an array
+#
+# Parameters:
+#     $1: Destination variable
+#     $2: String to split
+#     $3: Starting delimiter
+#     $4: Ending delimiter (optional)
+mustache-split() {
+    local POS RESULT
+
+    RESULT=( "$2" )
+    mustache-find-string POS "${RESULT[0]}" "$3"
+
+    if [[ "$POS" -ne -1 ]]; then
+        # The first delimiter was found
+        RESULT[1]=${RESULT[0]:$POS + ${#3}}
+        RESULT[0]=${RESULT[0]:0:$POS}
+
+        if [[ ! -z "$4" ]]; then
+            mustache-find-string POS "${RESULT[1]}" "$4"
+
+            if [[ "$POS" -ne -1 ]]; then
+                # The second delimiter was found
+                RESULT[2]="${RESULT[1]:$POS + ${#4}}"
+                RESULT[1]="${RESULT[1]:0:$POS}"
+            fi
+        fi
+    fi
+
+    local "$1" && mustache-indirect-array "$1" "${RESULT[@]}"
+}
+
+
+# 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.
+#
+# Parameters:
+#     $1: Name of destination "content" variable.
+#     $2: Content before the tag that was not yet written
+#     $3: Tag content (not used)
+#     $4: Content after the tag
+#     $5: true/false: is this the beginning of the content?
+mustache-standalone-allowed() {
+    local STANDALONE_BYTES
+
+    if mustache-is-standalone STANDALONE_BYTES "$2" "$4" $5; then
+        STANDALONE_BYTES=( $STANDALONE_BYTES )
+        echo -n "${2:0:${STANDALONE_BYTES[0]}}"
+        local "$1" && mustache-indirect "$1" "${4:${STANDALONE_BYTES[1]}}"
+    else
+        echo -n "$2"
+        local "$1" && mustache-indirect "$1" "$4"
+    fi
+}
+
+
+# Handle the content for a tag that is never "standalone".  No adjustments
+# are made for newlines and whitespace.
+#
+# Parameters:
+#     $1: Name of destination "content" variable.
+#     $2: Content before the tag that was not yet written
+#     $3: Tag content (not used)
+#     $4: Content after the tag
+mustache-standalone-denied() {
+    echo -n "$2"
+    local "$1" && mustache-indirect "$1" "$4"
+}
+
+
+# Returns 0 (success) if the named thing is a function or if it is a non-empty
+# environment variable.
+#
+# Do not use unprefixed variables here if possible as this needs to check
+# if any name exists in the environment
+#
+# Parameters:
+#     $1: Name of environment variable or function
+#     $2: Current value (our context)
+#
+# Return code:
+#     0 if the name is not empty, 1 otherwise
+mustache-test() {
+    # Test for functions
+    mustache-is-function "$1" && return 0
+
+    if mustache-is-array "$1"; then
+        # Arrays must have at least 1 element
+        eval '[[ "${#'"$1"'[@]}" -gt 0 ]]' && return 0
+    else
+        # Environment variables must not be empty
+        [[ ! -z "${!1}" ]] && return 0
+    fi
+
+    return 1
+}
+
+
+# Trim the leading whitespace only
+#
+# Parameters:
+#     $1: Name of destination variable
+#     $2: The string
+#     $3: true/false - trim front?
+#     $4: true/false - trim end?
+#     $5-*: Characters to trim
+mustache-trim-chars() {
+    local BACK CURRENT FRONT LAST TARGET VAR
+
+    TARGET=$1
+    CURRENT=$2
+    FRONT=$3
+    BACK=$4
+    LAST=""
+    shift # Remove target
+    shift # Remove string
+    shift # Remove trim front flag
+    shift # Remove trim end flag
+
+    while [[ "$CURRENT" != "$LAST" ]]; do
+        LAST=$CURRENT
+
+        for VAR in "$@"; do
+            $FRONT && CURRENT="${CURRENT/#$VAR}"
+            $BACK && CURRENT="${CURRENT/%$VAR}"
+        done
+    done
+
+    local "$TARGET" && mustache-indirect "$TARGET" "$CURRENT"
+}
+
+
+# Trim leading and trailing whitespace from a string
+#
+# Parameters:
+#     $1: Name of variable to store trimmed string
+#     $2: The string
+mustache-trim-whitespace() {
+    local RESULT
+
+    mustache-trim-chars RESULT "$2" true true $'\r' $'\n' $'\t' " "
+    local "$1" && mustache-indirect "$1" "$RESULT"
+}
+
+
+mustache-get-content MUSTACHE_CONTENT "$@"
+mustache-parse "$MUSTACHE_CONTENT" "" true
-- 
GitLab