1test -n "$_bsda_test_" && return 0
2readonly _bsda_test_=1
3
4#
5# Print failure and exit.
6#
7# @param 1
8#	The file name
9# @param 2
10#	This should be $LINENO
11# @param 3
12#	The error number
13#
14bsda:test:err() {
15	echo "$1 ERROR: $3" >&2
16	echo "$1:$2: $(/usr/bin/head -n$2 "$1" | /usr/bin/tail -n1)" >&2
17	exit $3
18}
19
20#
21# Check a string against a glob pattern.
22#
23# @param 1
24#	The glob pattern to match
25# @param 2
26#	The string to check
27# @retval 0
28#	The string matches the pattern
29# @retval 1
30#	The string does not match the pattern
31#
32bsda:test:gmatch() {
33	case "$2" in
34	$1)
35		return 0
36	;;
37	esac
38	return 1
39}
40
41#
42# Each line is matched against a set of glob patterns.
43#
44# The return value depends on a relationship criteria that specifies
45# how lines from the string should relate to glob patterns. The relationship
46# is a tuple of two variables: `<lines>:<patterns>`
47#
48# | Relationship | Meaning                                              |
49# |--------------|------------------------------------------------------|
50# | all:*        | All the lines must be matched by a pattern           |
51# | any:*        | At least one line must be matched by a pattern       |
52# |   *:all      | All the patterns must match at least one line        |
53# |   *:any      | Any of the patterns can be matched                   |
54# |   *:once     | All the patterns must be matched by exactly one line |
55#
56# @param 1
57#	The string to match against the patterns
58# @param 2
59#	The relationship between string lines and patterns
60# @param @
61#	The glob patterns to match against
62# @retval 0
63#	The relationship is satisfied
64# @retval 1
65#	The relationship is not satisfied
66# @retval 13
67#	The string line part of the relationship is unknown
68# @retval 23
69#	The pattern part of the relationship is unknown
70#
71bsda:test:xmatch() {
72	local IFS rel str i
73	IFS=$'\n'
74	str="$1"
75	rel="$2"
76	shift 2
77	i=0
78	while [ $i -lt $# ]; do
79		local count_$i
80		i=$((i + 1))
81	done
82	case "$rel" in
83	all:any)
84		for str in $str; do
85			bsda:test:xmatch_any "$str" "$@" || return $?
86		done
87		return 0
88	;;
89	any:any)
90		for str in $str; do
91			bsda:test:xmatch_any "$str" "$@" && return 0
92		done
93		return 1
94	;;
95	all:*)
96		for str in $str; do
97			bsda:test:xmatch_count "$str" "$@" || return $?
98		done
99	;;
100	any:*)
101		for str in $str; do
102			bsda:test:xmatch_count "$str" "$@"
103		done
104	;;
105	*)
106		# Unsupported relation
107		return 13
108	;;
109	esac
110	# Check counts
111	case "$rel" in
112	*:all)
113		i=0
114		while [ $i -lt $# ]; do
115			# Bail out if a pattern was not matched
116			if [ $((count_$i)) -eq 0 ]; then
117				return 1
118			fi
119			i=$((i + 1))
120		done
121		return 0
122	;;
123	*:once)
124		i=0
125		while [ $i -lt $# ]; do
126			# Bail out if a pattern was not matched once
127			if [ $((count_$i)) -ne 1 ]; then
128				return 1
129			fi
130			i=$((i + 1))
131		done
132		return 0
133	;;
134	esac
135	# Unsupported relation
136	return 23
137}
138
139#
140# Helper function to bsda:test:xmatch().
141#
142# Checks the given line against the given patterns.
143#
144# @param 1
145#	The string line to match against patterns
146# @param @
147#	The patterns to match against
148# @retval 0
149#	A pattern match was encountered
150# @retval 1
151#	None of the patterns are a match
152#
153bsda:test:xmatch_any() {
154	local line pattern
155	line="$1"
156	pattern="$2"
157	# Terminate recursion, when running out of patterns to mach
158	if ! shift 2; then
159		return 1
160	fi
161	# Try the current pattern
162	case "$line" in
163	$pattern)
164		return 0
165	;;
166	esac
167	# Try next pattern
168	bsda:test:xmatch_any "$line" "$@"
169}
170
171#
172# Helper function to bsda:test:xmatch().
173#
174# Counts the matches of each pattern.
175#
176# @param [count_0..count_$#)
177#	Store the number of matches for each pattern
178# @param 1
179#	The string line to match against patterns
180# @param @
181#	The patterns to match against
182# @retval 0
183#	At least one pattern match was encountered
184# @retval 1
185#	None of the patterns are a match
186#
187bsda:test:xmatch_count() {
188	local line pattern ret
189	line="$1"
190	pattern="$2"
191	# Terminate recursion, when running out of patterns to mach
192	if ! shift 2; then
193		return 1
194	fi
195	# Recurse to next pattern
196	bsda:test:xmatch_count "$line" "$@"
197	ret=$?
198	# Try the current pattern
199	case "$line" in
200	$pattern)
201		: $((count_$# += 1))
202		return 0
203	;;
204	esac
205	return $ret
206}
207
208#
209# Return the function type of the given function.
210#
211# | Type       | Description                                       |
212# |------------|---------------------------------------------------|
213# | alias      | The given function is a shell alias               |
214# | builtin    | The given function is a shell builtin             |
215# | function   | The given function is a shell function            |
216# | executable | The given function names a file system executable |
217# | none       | The given function cannot be found                |
218# | nil        | The `type` builtin returned an unsupported string |
219#
220# Note that `bash` only supports aliases in interactive mode.
221#
222# @param &1
223#	The variable to receive the function type
224# @param 2
225#	The function to determine the type of
226#
227bsda:test:type() {
228	setvar "$1" "$(
229		case "$(type "$2" 2>&1 )" in
230		"$2 is an alias "*|"$2 is aliased "*)
231			echo alias
232		;;
233		"$2 is a shell builtin")
234			echo builtin
235		;;
236		"$2 is a shell function"|"$2 is a function"*)
237			echo function
238		;;
239		"$2 is /"*)
240			echo executable
241		;;
242		*"$2: not found")
243			echo none
244		;;
245		*)
246			echo nil
247		;;
248		esac
249	)"
250}
251
252#
253# Check if the given function is an alias.
254#
255# @param 1
256#	The name of the function
257# @return
258#	Returns 0 for yes and 1 for no
259#
260bsda:test:isAlias() {
261	local type
262	bsda:test:type type "$1"
263	test "$type" = alias
264}
265
266#
267# Check if the given function is a builtin.
268#
269# @param 1
270#	The name of the function
271# @return
272#	Returns 0 for yes and 1 for no
273#
274bsda:test:isBuiltin() {
275	local type
276	bsda:test:type type "$1"
277	test "$type" = builtin
278}
279
280#
281# Check if the given function is a shell function.
282#
283# @param 1
284#	The name of the function
285# @return
286#	Returns 0 for yes and 1 for no
287#
288bsda:test:isFunction() {
289	local type
290	bsda:test:type type "$1"
291	test "$type" = function
292}
293
294#
295# Check if the given function is a file system executable.
296#
297# @param 1
298#	The name of the function
299# @return
300#	Returns 0 for yes and 1 for no
301#
302bsda:test:isExecutable() {
303	local type
304	bsda:test:type type "$1"
305	test "$type" = executable
306}
307
308#
309# Check if the given function cannot be found.
310#
311# @param 1
312#	The name of the function
313# @return
314#	Returns 0 for yes and 1 for no
315#
316bsda:test:isNone() {
317	local type
318	bsda:test:type type "$1"
319	test "$type" = none
320}
321
322#
323# Check if the given variable exists.
324#
325# Note that this returns true for all defined variables, even empty
326# ones.
327#
328# @param 1
329#	The name of the variable
330# @retval 0
331#	The variable has been defined
332# @retval 1
333#	The variable does not exist
334#
335bsda:test:isSet() {
336	eval "test -n \"\${$1+x}\""
337}
338