summaryrefslogtreecommitdiffstats
path: root/src/substvars.sh
blob: bc0b052da812955f532c631f7e41a8b7d262527c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# Functions for setting and substituting variables
#
# Copyright (C) 2012, 2014, 2019  Patrick McDermott
#
# This file is part of the ProteanOS Development Kit.
#
# The ProteanOS Development Kit is free software: you can redistribute
# it and/or modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# The ProteanOS Development Kit is distributed in the hope that it
# will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with the ProteanOS Development Kit.  If not, see
# <http://www.gnu.org/licenses/>.

SUBSTVARS_MAX_DEPTH=50
SUBSTVAR_TRIM_SED='
	H;             # Store each input line in the hold space.
	${             # At the last line of input:
	  g;           # restore the hold space into the pattern space,
	  s/^[\n]*//;  # remove leading newline characters,
	  s/[\n]*$//;  # remove trailing newline characters, and
	  p;           # print the results.
	};
'

set_substvar()
{
	local name="${1}"
	local value="${2}"
	shift 2

	# Validate variable name and convert to lower case.
	case "${name}" in *[!A-Za-z0-9-]* | '')
		warn "$(get_msg 'substvar_invalid')" "${name}"
		return 1
		;;
	esac
	name="$(printf '%s' "${name}" | tr 'A-Z-' 'a-z_')"

	# Trim leading and trailing whitespace from value.
	value="$(printf '%s' "${value}" | sed -n "${SUBSTVAR_TRIM_SED}")"

	eval "substvar_${name}=\"\${value}\""

	return 0
}

substvars()
{
	local string="${1}"
	shift 1
	local depth=
	local lhs=
	local name=
	local rhs=
	local old_rhs=
	local value=

	# Logic inspired by that of dpkg's Dpkg::Substvars::substvars()
	# subroutine.

	depth=0

	while :; do
		lhs="${string%%\$\{*}"
		if [ ${#lhs} -eq ${#string} ]; then
			# No "${" was found.
			break
		fi
		string="${string#*\$\{}"
		name="${string%%\}*}"
		rhs="${string#*\}}"

		if [ ${#rhs} -lt ${#old_rhs} ]; then
			# Reset the nesting counter if we've advanced the right
			# side of the matched space.
			depth=0
		fi
		if [ ${depth} -ge ${SUBSTVARS_MAX_DEPTH} ]; then
			# Warn of possible recursion.
			warn "$(get_msg 'substvar_deep_nesting')"
			return 1
		fi
		old_rhs="${rhs}"

		# Validate the variable name.
		case "${name}" in *[!A-Za-z0-9-]* | '')
			warn "$(get_msg 'substvar_invalid')" "${name}"
			# Remove the variable expansion altogether.  We can't
			# just leave the variable unexpanded, because the
			# presence of the "${" characters would cause this
			# parser to loop infinitely.
			string="${lhs}${rhs}"
			continue
		esac

		# Perform the substitution.
		name_tr="$(printf '%s' "${name}" | tr 'A-Z-' 'a-z_')"
		if eval "[ x\"\${substvar_${name_tr}:+set}\" = x'set' ]"
		then
			name_tr="substvar_${name_tr}"
		else
			warn "$(get_msg 'substvar_unknown')" "${name}"
			string="${lhs}${rhs}"
			continue
		fi
		value="$(eval "printf '%s' \"\${${name_tr}}\"")"
		string="${lhs}${value}${rhs}"
		depth=$((${depth} + 1))
	done

	printf '%s\n' "${string}"

	return 0
}