summaryrefslogtreecommitdiffstats
path: root/lib/substvars.sh
blob: d69c6937bcfc8ff8997be1ae4761038b5c836932 (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
# Functions for setting and substituting variables
#
# Copyright (C) 2012, 2014  Patrick "P. J." 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/>.

[ "x${_SUBSTVARS_SM+set}" = 'xset' ] && return 0
_SUBSTVARS_SM=1

substvars_max_depth=50

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

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

	# Trim leading and trailing newline characters from value.
	value="$(printf '%s\n' "${value}" | sed -n '
		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.
		  p;          # Print the results.
		};
		')"

	# Escape single quotes in value.
	value="$(printf '%s\n' "${value}" | sed "s/'/'\\\\''/g")"

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

	return 0
}

substvars()
{
	local string="${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}"

		# Perform the substitution.
		name="$(printf '%s\n' "${name}" | tr 'A-Z-' 'a-z_')"
		value="$(eval "printf '%s\n' \"\${substvar_${name}}\"")"
		string="${lhs}${value}${rhs}"
		depth=$(($depth + 1))
	done

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

	return 0
}