xref: /freebsd/usr.sbin/bsdconfig/share/strings.subr (revision 0f9fec9d302f0b6163be715a1264757ff9363a60)
1ab2043b8SDevin Teskeif [ ! "$_STRINGS_SUBR" ]; then _STRINGS_SUBR=1
2ab2043b8SDevin Teske#
30cb8bea7SDevin Teske# Copyright (c) 2006-2016 Devin Teske
4f8ea072aSDevin Teske# All rights reserved.
5ab2043b8SDevin Teske#
6ab2043b8SDevin Teske# Redistribution and use in source and binary forms, with or without
7ab2043b8SDevin Teske# modification, are permitted provided that the following conditions
8ab2043b8SDevin Teske# are met:
9ab2043b8SDevin Teske# 1. Redistributions of source code must retain the above copyright
10ab2043b8SDevin Teske#    notice, this list of conditions and the following disclaimer.
11ab2043b8SDevin Teske# 2. Redistributions in binary form must reproduce the above copyright
12ab2043b8SDevin Teske#    notice, this list of conditions and the following disclaimer in the
13ab2043b8SDevin Teske#    documentation and/or other materials provided with the distribution.
14ab2043b8SDevin Teske#
15ab2043b8SDevin Teske# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
168e37a7c8SDevin Teske# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17ab2043b8SDevin Teske# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18ab2043b8SDevin Teske# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19ab2043b8SDevin Teske# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
208e37a7c8SDevin Teske# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21ab2043b8SDevin Teske# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22ab2043b8SDevin Teske# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23ab2043b8SDevin Teske# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24ab2043b8SDevin Teske# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25ab2043b8SDevin Teske# SUCH DAMAGE.
26ab2043b8SDevin Teske#
27ab2043b8SDevin Teske# $FreeBSD$
28d3a0f918SDevin Teske#
29fcaed0c1SDevin Teske############################################################ INCLUDES
30fcaed0c1SDevin Teske
31fcaed0c1SDevin TeskeBSDCFG_SHARE="/usr/share/bsdconfig"
32fcaed0c1SDevin Teske. $BSDCFG_SHARE/common.subr || exit 1
33fcaed0c1SDevin Teske
34d3a0f918SDevin Teske############################################################ GLOBALS
35d3a0f918SDevin Teske
36d3a0f918SDevin Teske#
3705a0a04aSDevin Teske# A Literal newline (for use with f_replace_all(), or IFS, or whatever)
3805a0a04aSDevin Teske#
3905a0a04aSDevin TeskeNL="
4005a0a04aSDevin Teske" # END-QUOTE
4105a0a04aSDevin Teske
4205a0a04aSDevin Teske#
43d3a0f918SDevin Teske# Valid characters that can appear in an sh(1) variable name
44d3a0f918SDevin Teske#
45d3a0f918SDevin Teske# Please note that the character ranges A-Z and a-z should be avoided because
46d3a0f918SDevin Teske# these can include accent characters (which are not valid in a variable name).
47d3a0f918SDevin Teske# For example, A-Z matches any character that sorts after A but before Z,
48d3a0f918SDevin Teske# including A and Z. Although ASCII order would make more sense, that is not
49d3a0f918SDevin Teske# how it works.
50d3a0f918SDevin Teske#
51d3a0f918SDevin TeskeVALID_VARNAME_CHARS="0-9ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"
52d3a0f918SDevin Teske
53d3a0f918SDevin Teske############################################################ FUNCTIONS
54ab2043b8SDevin Teske
5592db3842SDevin Teske# f_isinteger $arg
5692db3842SDevin Teske#
5792db3842SDevin Teske# Returns true if argument is a positive/negative whole integer.
5892db3842SDevin Teske#
5992db3842SDevin Teskef_isinteger()
6092db3842SDevin Teske{
6192db3842SDevin Teske	local arg="${1#-}"
6292db3842SDevin Teske	[ "${arg:-x}" = "${arg%[!0-9]*}" ]
6392db3842SDevin Teske}
6492db3842SDevin Teske
650cb8bea7SDevin Teske# f_substr [-v $var_to_set] $string $start [$length]
66ab2043b8SDevin Teske#
670cb8bea7SDevin Teske# Similar to awk(1)'s substr(), return length substring of string that begins
680cb8bea7SDevin Teske# at start position counted from 1.
69ab2043b8SDevin Teske#
70ab2043b8SDevin Teskef_substr()
71ab2043b8SDevin Teske{
720cb8bea7SDevin Teske	local OPTIND=1 OPTARG __flag __var_to_set=
730cb8bea7SDevin Teske	while getopts v: __flag; do
740cb8bea7SDevin Teske		case "$__flag" in
750cb8bea7SDevin Teske		v) __var_to_set="$OPTARG" ;;
760cb8bea7SDevin Teske		esac
770cb8bea7SDevin Teske	done
780cb8bea7SDevin Teske	shift $(( $OPTIND - 1 ))
79ab2043b8SDevin Teske
800cb8bea7SDevin Teske	local __tmp="$1" __start="${2:-1}" __size="$3"
810cb8bea7SDevin Teske	local __tbuf __tbuf_len __trim __trimq
820f913ed5SDevin Teske
830cb8bea7SDevin Teske	if [ ! "$__tmp" ]; then
840cb8bea7SDevin Teske		[ "$__var_to_set" ] && setvar "$__var_to_set" ""
850f913ed5SDevin Teske		return ${SUCCESS:-0}
860cb8bea7SDevin Teske	fi
870cb8bea7SDevin Teske	[ "$__start" -ge 1 ] 2> /dev/null || __start=1
880cb8bea7SDevin Teske	if ! [ "${__size:-1}" -ge 1 ] 2> /dev/null; then
890cb8bea7SDevin Teske		[ "$__var_to_set" ] && setvar "$__var_to_set" ""
900f913ed5SDevin Teske		return ${FAILURE:-1}
910f913ed5SDevin Teske	fi
920f913ed5SDevin Teske
930cb8bea7SDevin Teske	__trim=$(( $__start - 1 ))
940cb8bea7SDevin Teske	while [ $__trim -gt 0 ]; do
950cb8bea7SDevin Teske		__tbuf="?"
960cb8bea7SDevin Teske		__tbuf_len=1
970cb8bea7SDevin Teske		while [ $__tbuf_len -lt $(( $__trim / $__tbuf_len )) ]; do
980cb8bea7SDevin Teske			__tbuf="$__tbuf?"
990cb8bea7SDevin Teske			__tbuf_len=$(( $__tbuf_len + 1 ))
1000cb8bea7SDevin Teske		done
1010cb8bea7SDevin Teske		__trimq=$(( $__trim / $__tbuf_len ))
1020cb8bea7SDevin Teske		__trim=$(( $__trim - $__tbuf_len * $__trimq ))
1030cb8bea7SDevin Teske		while [ $__trimq -gt 0 ]; do
1040cb8bea7SDevin Teske			__tmp="${__tmp#$__tbuf}"
1050cb8bea7SDevin Teske			__trimq=$(( $__trimq - 1 ))
1060cb8bea7SDevin Teske		done
1070cb8bea7SDevin Teske	done
1080f913ed5SDevin Teske
1090cb8bea7SDevin Teske	local __tmp_size=${#__tmp}
1100f913ed5SDevin Teske	local __mask __mask_len
1110cb8bea7SDevin Teske	__trim=$(( $__tmp_size - ${__size:-$__tmp_size} ))
1120f913ed5SDevin Teske	while [ $__trim -gt 0 ]; do
1130f913ed5SDevin Teske		__tbuf="?"
1140f913ed5SDevin Teske		__tbuf_len=1
1150f913ed5SDevin Teske		if [ $__trim -le $__size ]; then
1160f913ed5SDevin Teske			while [ $__tbuf_len -lt $(( $__trim / $__tbuf_len )) ]
1170f913ed5SDevin Teske			do
1180f913ed5SDevin Teske				__tbuf="$__tbuf?"
1190f913ed5SDevin Teske				__tbuf_len=$(( $__tbuf_len + 1 ))
1200f913ed5SDevin Teske			done
1210f913ed5SDevin Teske			__trimq=$(( $__trim / $__tbuf_len ))
1220f913ed5SDevin Teske			__trim=$(( $__trim - $__tbuf_len * $__trimq ))
1230f913ed5SDevin Teske			while [ $__trimq -gt 0 ]; do
1240cb8bea7SDevin Teske				__tmp="${__tmp%$__tbuf}"
1250f913ed5SDevin Teske				__trimq=$(( $__trimq - 1 ))
1260f913ed5SDevin Teske			done
1270f913ed5SDevin Teske		else
1280cb8bea7SDevin Teske			__mask="$__tmp"
1290f913ed5SDevin Teske			while [ $__tbuf_len -lt $(( $__size / $__tbuf_len )) ]
1300f913ed5SDevin Teske			do
1310f913ed5SDevin Teske				__tbuf="$__tbuf?"
1320f913ed5SDevin Teske				__tbuf_len=$(( $__tbuf_len + 1 ))
1330f913ed5SDevin Teske			done
1340f913ed5SDevin Teske			__trimq=$(( $__size / $__tbuf_len ))
1350f913ed5SDevin Teske			if [ $(( $__trimq * $__tbuf_len )) -ne $__size ]; then
1360f913ed5SDevin Teske				__tbuf="$__tbuf?"
1370f913ed5SDevin Teske				__tbuf_len=$(( $__tbuf_len + 1 ))
1380f913ed5SDevin Teske			fi
1390f913ed5SDevin Teske			__mask_len=$(( $__tmp_size - $__tbuf_len * $__trimq ))
1400f913ed5SDevin Teske			__trim=$(( $__tmp_size - $__mask_len - $__size ))
1410f913ed5SDevin Teske			while [ $__trimq -gt 0 ]; do
1420f913ed5SDevin Teske				__mask="${__mask#$__tbuf}"
1430f913ed5SDevin Teske				__trimq=$(( $__trimq - 1 ))
1440f913ed5SDevin Teske			done
1450cb8bea7SDevin Teske			__tmp="${__tmp%"$__mask"}"
1460f913ed5SDevin Teske		fi
1470f913ed5SDevin Teske	done
1480cb8bea7SDevin Teske
1490cb8bea7SDevin Teske	setvar "$__var_to_set" "$__tmp"
1500cb8bea7SDevin Teske}
1510cb8bea7SDevin Teske
1520efb8b7aSDevin Teske# f_sprintf $var_to_set $format [$arguments ...]
1530efb8b7aSDevin Teske#
1540efb8b7aSDevin Teske# Similar to sprintf(3), write a string into $var_to_set using printf(1) syntax
1550efb8b7aSDevin Teske# (`$format [$arguments ...]').
1560efb8b7aSDevin Teske#
1570efb8b7aSDevin Teskef_sprintf()
1580efb8b7aSDevin Teske{
1590efb8b7aSDevin Teske	local __var_to_set="$1"
1600efb8b7aSDevin Teske	shift 1 # var_to_set
161fcb16c10SDevin Teske
162fcb16c10SDevin Teske	case "$BASH_VERSION" in
163fcb16c10SDevin Teske	3.1*|4.*)
164fcb16c10SDevin Teske		local __tmp
165fcb16c10SDevin Teske		printf -v __tmp "$@"
166fcb16c10SDevin Teske		eval "$__var_to_set"=\"\${__tmp%\$NL}\"
167fcb16c10SDevin Teske		;;
168fcb16c10SDevin Teske	*) eval "$__var_to_set"=\$\( printf -- \"\$@\" \)
169fcb16c10SDevin Teske	esac
1700efb8b7aSDevin Teske}
1710efb8b7aSDevin Teske
172e4f08d49SDevin Teske# f_vsprintf $var_to_set $format $format_args
173e4f08d49SDevin Teske#
174e4f08d49SDevin Teske# Similar to vsprintf(3), write a string into $var_to_set using printf(1)
175e4f08d49SDevin Teske# syntax (`$format $format_args').
176e4f08d49SDevin Teske#
177e4f08d49SDevin Teskef_vsprintf()
178e4f08d49SDevin Teske{
179e4f08d49SDevin Teske	eval f_sprintf \"\$1\" \"\$2\" $3
180e4f08d49SDevin Teske}
181e4f08d49SDevin Teske
1823c8cd13bSDevin Teske# f_snprintf $var_to_set $size $format [$arguments ...]
1833c8cd13bSDevin Teske#
1843c8cd13bSDevin Teske# Similar to snprintf(3), write at most $size number of bytes into $var_to_set
1853c8cd13bSDevin Teske# using printf(1) syntax (`$format [$arguments ...]').
1863c8cd13bSDevin Teske#
1873c8cd13bSDevin Teskef_snprintf()
1883c8cd13bSDevin Teske{
1893c8cd13bSDevin Teske	local __var_to_set="$1" __size="$2"
1903c8cd13bSDevin Teske	shift 2 # var_to_set size
1913c8cd13bSDevin Teske
1923c8cd13bSDevin Teske	local __f_snprintf_tmp
1933c8cd13bSDevin Teske	f_sprintf __f_snprintf_tmp "$@"
1943c8cd13bSDevin Teske	f_substr "$__var_to_set" "$__f_snprintf_tmp" 1 "$__size"
1953c8cd13bSDevin Teske}
1963c8cd13bSDevin Teske
197dd5cc066SDevin Teske# f_vsnprintf $var_to_set $size $format $format_args
198dd5cc066SDevin Teske#
199dd5cc066SDevin Teske# Similar to vsnprintf(3), write at most $size number of bytes into $var_to_set
200dd5cc066SDevin Teske# using printf(1) syntax (`$format $format_args'). The value of $var_to_set is
201dd5cc066SDevin Teske# NULL unless at-least one byte is stored from the output.
202dd5cc066SDevin Teske#
203dd5cc066SDevin Teske# Example 1:
204dd5cc066SDevin Teske#
205dd5cc066SDevin Teske# 	limit=7 format="%s"
206dd5cc066SDevin Teske# 	format_args="'abc   123'" # 3-spaces between abc and 123
207dd5cc066SDevin Teske# 	f_vsnprintf foo $limit "$format" "$format_args" # foo=[abc   1]
208dd5cc066SDevin Teske#
209dd5cc066SDevin Teske# Example 2:
210dd5cc066SDevin Teske#
211dd5cc066SDevin Teske# 	limit=12 format="%s %s"
21233291485SDevin Teske# 	format_args="   'doghouse'      'fox'   "
213dd5cc066SDevin Teske# 		# even more spaces added to illustrate escape-method
214dd5cc066SDevin Teske# 	f_vsnprintf foo $limit "$format" "$format_args" # foo=[doghouse fox]
215dd5cc066SDevin Teske#
216dd5cc066SDevin Teske# Example 3:
217dd5cc066SDevin Teske#
218dd5cc066SDevin Teske# 	limit=13 format="%s %s"
219dd5cc066SDevin Teske# 	f_shell_escape arg1 'aaa"aaa' # arg1=[aaa"aaa] (no change)
220dd5cc066SDevin Teske# 	f_shell_escape arg2 "aaa'aaa" # arg2=[aaa'\''aaa] (escaped s-quote)
221dd5cc066SDevin Teske# 	format_args="'$arg1' '$arg2'" # use single-quotes to surround args
222dd5cc066SDevin Teske# 	f_vsnprintf foo $limit "$format" "$format_args" # foo=[aaa"aaa aaa'a]
223dd5cc066SDevin Teske#
224dd5cc066SDevin Teske# In all of the above examples, the call to f_vsnprintf() does not change. Only
225dd5cc066SDevin Teske# the contents of $limit, $format, and $format_args changes in each example.
226dd5cc066SDevin Teske#
227dd5cc066SDevin Teskef_vsnprintf()
228dd5cc066SDevin Teske{
229dd5cc066SDevin Teske	eval f_snprintf \"\$1\" \"\$2\" \"\$3\" $4
230dd5cc066SDevin Teske}
231dd5cc066SDevin Teske
232d3a0f918SDevin Teske# f_replaceall $string $find $replace [$var_to_set]
233d3a0f918SDevin Teske#
234f82ca17bSDevin Teske# Replace all occurrences of $find in $string with $replace. If $var_to_set is
235d3a0f918SDevin Teske# either missing or NULL, the variable name is produced on standard out for
236d3a0f918SDevin Teske# capturing in a sub-shell (which is less recommended due to performance
237d3a0f918SDevin Teske# degradation).
238d3a0f918SDevin Teske#
23933db33a7SDevin Teske# To replace newlines or a sequence containing the newline character, use $NL
24033db33a7SDevin Teske# as `\n' is not supported.
24133db33a7SDevin Teske#
242d3a0f918SDevin Teskef_replaceall()
243d3a0f918SDevin Teske{
244d3a0f918SDevin Teske	local __left="" __right="$1"
245d3a0f918SDevin Teske	local __find="$2" __replace="$3" __var_to_set="$4"
246d3a0f918SDevin Teske	while :; do
247d3a0f918SDevin Teske		case "$__right" in *$__find*)
248d3a0f918SDevin Teske			__left="$__left${__right%%$__find*}$__replace"
249d3a0f918SDevin Teske			__right="${__right#*$__find}"
250d3a0f918SDevin Teske			continue
251d3a0f918SDevin Teske		esac
252d3a0f918SDevin Teske		break
253d3a0f918SDevin Teske	done
254d3a0f918SDevin Teske	__left="$__left${__right#*$__find}"
255d3a0f918SDevin Teske	if [ "$__var_to_set" ]; then
256d3a0f918SDevin Teske		setvar "$__var_to_set" "$__left"
257d3a0f918SDevin Teske	else
258d3a0f918SDevin Teske		echo "$__left"
259d3a0f918SDevin Teske	fi
260d3a0f918SDevin Teske}
261d3a0f918SDevin Teske
262d3a0f918SDevin Teske# f_str2varname $string [$var_to_set]
263d3a0f918SDevin Teske#
264d3a0f918SDevin Teske# Convert a string into a suitable value to be used as a variable name
265d3a0f918SDevin Teske# by converting unsuitable characters into the underscrore [_]. If $var_to_set
266d3a0f918SDevin Teske# is either missing or NULL, the variable name is produced on standard out for
267d3a0f918SDevin Teske# capturing in a sub-shell (which is less recommended due to performance
268d3a0f918SDevin Teske# degradation).
269d3a0f918SDevin Teske#
270d3a0f918SDevin Teskef_str2varname()
271d3a0f918SDevin Teske{
272d3a0f918SDevin Teske	local __string="$1" __var_to_set="$2"
273d3a0f918SDevin Teske	f_replaceall "$__string" "[!$VALID_VARNAME_CHARS]" "_" "$__var_to_set"
274d3a0f918SDevin Teske}
275d3a0f918SDevin Teske
276d3a0f918SDevin Teske# f_shell_escape $string [$var_to_set]
277d3a0f918SDevin Teske#
278d3a0f918SDevin Teske# Escape $string for shell eval statement(s) by replacing all single-quotes
279d3a0f918SDevin Teske# with a special sequence that creates a compound string when interpolated
280d3a0f918SDevin Teske# by eval with surrounding single-quotes.
281d3a0f918SDevin Teske#
282d3a0f918SDevin Teske# For example:
283d3a0f918SDevin Teske#
284d3a0f918SDevin Teske# 	foo="abc'123"
285d3a0f918SDevin Teske# 	f_shell_escape "$foo" bar # bar=[abc'\''123]
28610908a6fSDevin Teske# 	eval echo \'$bar\' # produces abc'123
287d3a0f918SDevin Teske#
288d3a0f918SDevin Teske# This is helpful when processing an argument list that has to retain its
289d3a0f918SDevin Teske# escaped structure for later evaluations.
290d3a0f918SDevin Teske#
291d3a0f918SDevin Teske# WARNING: Surrounding single-quotes are not added; this is the responsibility
292d3a0f918SDevin Teske# of the code passing the escaped values to eval (which also aids readability).
293d3a0f918SDevin Teske#
294d3a0f918SDevin Teskef_shell_escape()
295d3a0f918SDevin Teske{
296d3a0f918SDevin Teske	local __string="$1" __var_to_set="$2"
297d3a0f918SDevin Teske	f_replaceall "$__string" "'" "'\\''" "$__var_to_set"
298d3a0f918SDevin Teske}
299d3a0f918SDevin Teske
300d3a0f918SDevin Teske# f_shell_unescape $string [$var_to_set]
301d3a0f918SDevin Teske#
302d3a0f918SDevin Teske# The antithesis of f_shell_escape(), this function takes an escaped $string
303d3a0f918SDevin Teske# and expands it.
304d3a0f918SDevin Teske#
305d3a0f918SDevin Teske# For example:
306d3a0f918SDevin Teske#
307d3a0f918SDevin Teske# 	foo="abc'123"
308d3a0f918SDevin Teske# 	f_shell_escape "$foo" bar # bar=[abc'\''123]
309d3a0f918SDevin Teske# 	f_shell_unescape "$bar" # produces abc'123
310d3a0f918SDevin Teske#
311d3a0f918SDevin Teskef_shell_unescape()
312d3a0f918SDevin Teske{
313d3a0f918SDevin Teske	local __string="$1" __var_to_set="$2"
314d3a0f918SDevin Teske	f_replaceall "$__string" "'\\''" "'" "$__var_to_set"
315d3a0f918SDevin Teske}
316d3a0f918SDevin Teske
317a96ea66fSDevin Teske# f_expand_number $string [$var_to_set]
318a96ea66fSDevin Teske#
319a96ea66fSDevin Teske# Unformat $string into a number, optionally to be stored in $var_to_set. This
320a96ea66fSDevin Teske# function follows the SI power of two convention.
321a96ea66fSDevin Teske#
322a96ea66fSDevin Teske# The prefixes are:
323a96ea66fSDevin Teske#
324a96ea66fSDevin Teske# 	Prefix	Description	Multiplier
325a96ea66fSDevin Teske# 	k	kilo		1024
326a96ea66fSDevin Teske# 	M	mega		1048576
327a96ea66fSDevin Teske# 	G	giga		1073741824
328a96ea66fSDevin Teske# 	T	tera		1099511627776
329a96ea66fSDevin Teske# 	P	peta		1125899906842624
330a96ea66fSDevin Teske# 	E	exa		1152921504606846976
331a96ea66fSDevin Teske#
332a96ea66fSDevin Teske# NOTE: Prefixes are case-insensitive.
333a96ea66fSDevin Teske#
3349acbeddcSDevin Teske# Upon successful completion, success status is returned; otherwise the number
3359acbeddcSDevin Teske# -1 is produced ($var_to_set set to -1 or if $var_to_set is NULL or missing)
3369acbeddcSDevin Teske# on standard output. In the case of failure, the error status will be one of:
337a96ea66fSDevin Teske#
3389acbeddcSDevin Teske# 	Status	Reason
3399acbeddcSDevin Teske# 	1	Given $string contains no digits
3409acbeddcSDevin Teske# 	2	An unrecognized prefix was given
3419acbeddcSDevin Teske# 	3	Result too large to calculate
342a96ea66fSDevin Teske#
343a96ea66fSDevin Teskef_expand_number()
344a96ea66fSDevin Teske{
345a96ea66fSDevin Teske	local __string="$1" __var_to_set="$2"
34605a0a04aSDevin Teske	local __cp __num __bshift __maxinput
347a96ea66fSDevin Teske
34805a0a04aSDevin Teske	# Remove any leading non-digits
349ae978c36SDevin Teske	__string="${__string#${__string%%[0-9]*}}"
35005a0a04aSDevin Teske
35105a0a04aSDevin Teske	# Store the numbers (no trailing suffix)
35205a0a04aSDevin Teske	__num="${__string%%[!0-9]*}"
353a96ea66fSDevin Teske
3549acbeddcSDevin Teske	# Produce `-1' if string didn't contain any digits
35505a0a04aSDevin Teske	if [ ! "$__num" ]; then
356a96ea66fSDevin Teske		if [ "$__var_to_set" ]; then
357a96ea66fSDevin Teske			setvar "$__var_to_set" -1
358a96ea66fSDevin Teske		else
359a96ea66fSDevin Teske			echo -1
360a96ea66fSDevin Teske		fi
3619acbeddcSDevin Teske		return 1 # 1 = "Given $string contains no digits"
362a96ea66fSDevin Teske	fi
363a96ea66fSDevin Teske
364a96ea66fSDevin Teske	# Remove all the leading numbers from the string to get at the prefix
36505a0a04aSDevin Teske	__string="${__string#"$__num"}"
366a96ea66fSDevin Teske
3679acbeddcSDevin Teske	#
3689acbeddcSDevin Teske	# Test for invalid prefix (and determine bitshift length)
3699acbeddcSDevin Teske	#
370a96ea66fSDevin Teske	case "$__string" in
3719acbeddcSDevin Teske	""|[[:space:]]*) # Shortcut
3729acbeddcSDevin Teske		if [ "$__var_to_set" ]; then
37305a0a04aSDevin Teske			setvar "$__var_to_set" $__num
3749acbeddcSDevin Teske		else
37505a0a04aSDevin Teske			echo $__num
3769acbeddcSDevin Teske		fi
3779acbeddcSDevin Teske		return $SUCCESS ;;
3789acbeddcSDevin Teske	[Kk]*) __bshift=10 ;;
3799acbeddcSDevin Teske	[Mm]*) __bshift=20 ;;
3809acbeddcSDevin Teske	[Gg]*) __bshift=30 ;;
3819acbeddcSDevin Teske	[Tt]*) __bshift=40 ;;
3829acbeddcSDevin Teske	[Pp]*) __bshift=50 ;;
3839acbeddcSDevin Teske	[Ee]*) __bshift=60 ;;
384a96ea66fSDevin Teske	*)
385a96ea66fSDevin Teske		# Unknown prefix
386a96ea66fSDevin Teske		if [ "$__var_to_set" ]; then
387a96ea66fSDevin Teske			setvar "$__var_to_set" -1
388a96ea66fSDevin Teske		else
389a96ea66fSDevin Teske			echo -1
390a96ea66fSDevin Teske		fi
3919acbeddcSDevin Teske		return 2 # 2 = "An unrecognized prefix was given"
392a96ea66fSDevin Teske	esac
393a96ea66fSDevin Teske
3949acbeddcSDevin Teske	# Determine if the wheels fall off
3959acbeddcSDevin Teske	__maxinput=$(( 0x7fffffffffffffff >> $__bshift ))
39605a0a04aSDevin Teske	if [ $__num -gt $__maxinput ]; then
3979acbeddcSDevin Teske		# Input (before expanding) would exceed 64-bit signed int
398a96ea66fSDevin Teske		if [ "$__var_to_set" ]; then
399a96ea66fSDevin Teske			setvar "$__var_to_set" -1
400a96ea66fSDevin Teske		else
401a96ea66fSDevin Teske			echo -1
402a96ea66fSDevin Teske		fi
4039acbeddcSDevin Teske		return 3 # 3 = "Result too large to calculate"
404a96ea66fSDevin Teske	fi
405a96ea66fSDevin Teske
4069acbeddcSDevin Teske	# Shift the number out and produce it
40705a0a04aSDevin Teske	__num=$(( $__num << $__bshift ))
408a96ea66fSDevin Teske	if [ "$__var_to_set" ]; then
40905a0a04aSDevin Teske		setvar "$__var_to_set" $__num
410a96ea66fSDevin Teske	else
41105a0a04aSDevin Teske		echo $__num
412a96ea66fSDevin Teske	fi
413a96ea66fSDevin Teske}
414a96ea66fSDevin Teske
415*0f9fec9dSDevin Teske# f_longest_line_length
416*0f9fec9dSDevin Teske#
417*0f9fec9dSDevin Teske# Simple wrapper to an awk(1) script to print the length of the longest line of
418*0f9fec9dSDevin Teske# input (read from stdin). Supports the newline escape-sequence `\n' for
419*0f9fec9dSDevin Teske# splitting a single line into multiple lines.
420*0f9fec9dSDevin Teske#
421*0f9fec9dSDevin Teskef_longest_line_length_awk='
422*0f9fec9dSDevin TeskeBEGIN { longest = 0 }
423*0f9fec9dSDevin Teske{
424*0f9fec9dSDevin Teske	if (split($0, lines, /\\n/) > 1)
425*0f9fec9dSDevin Teske	{
426*0f9fec9dSDevin Teske		for (n in lines)
427*0f9fec9dSDevin Teske		{
428*0f9fec9dSDevin Teske			len = length(lines[n])
429*0f9fec9dSDevin Teske			longest = ( len > longest ? len : longest )
430*0f9fec9dSDevin Teske		}
431*0f9fec9dSDevin Teske	}
432*0f9fec9dSDevin Teske	else
433*0f9fec9dSDevin Teske	{
434*0f9fec9dSDevin Teske		len = length($0)
435*0f9fec9dSDevin Teske		longest = ( len > longest ? len : longest )
436*0f9fec9dSDevin Teske	}
437*0f9fec9dSDevin Teske}
438*0f9fec9dSDevin TeskeEND { print longest }
439*0f9fec9dSDevin Teske'
440*0f9fec9dSDevin Teskef_longest_line_length()
441*0f9fec9dSDevin Teske{
442*0f9fec9dSDevin Teske	awk "$f_longest_line_length_awk"
443*0f9fec9dSDevin Teske}
444*0f9fec9dSDevin Teske
445*0f9fec9dSDevin Teske# f_number_of_lines
446*0f9fec9dSDevin Teske#
447*0f9fec9dSDevin Teske# Simple wrapper to an awk(1) script to print the number of lines read from
448*0f9fec9dSDevin Teske# stdin. Supports newline escape-sequence `\n' for splitting a single line into
449*0f9fec9dSDevin Teske# multiple lines.
450*0f9fec9dSDevin Teske#
451*0f9fec9dSDevin Teskef_number_of_lines_awk='
452*0f9fec9dSDevin TeskeBEGIN { num_lines = 0 }
453*0f9fec9dSDevin Teske{
454*0f9fec9dSDevin Teske	num_lines += split(" "$0, unused, /\\n/)
455*0f9fec9dSDevin Teske}
456*0f9fec9dSDevin TeskeEND { print num_lines }
457*0f9fec9dSDevin Teske'
458*0f9fec9dSDevin Teskef_number_of_lines()
459*0f9fec9dSDevin Teske{
460*0f9fec9dSDevin Teske	awk "$f_number_of_lines_awk"
461*0f9fec9dSDevin Teske}
462*0f9fec9dSDevin Teske
463*0f9fec9dSDevin Teske# f_uriencode [$text]
464*0f9fec9dSDevin Teske#
465*0f9fec9dSDevin Teske# Encode $text for the purpose of embedding safely into a URL. Non-alphanumeric
466*0f9fec9dSDevin Teske# characters are converted to `%XX' sequence where XX represents the hexa-
467*0f9fec9dSDevin Teske# decimal ordinal of the non-alphanumeric character. If $text is missing, data
468*0f9fec9dSDevin Teske# is instead read from standard input.
469*0f9fec9dSDevin Teske#
470*0f9fec9dSDevin Teskef_uriencode_awk='
471*0f9fec9dSDevin TeskeBEGIN {
472*0f9fec9dSDevin Teske	output = ""
473*0f9fec9dSDevin Teske	for (n = 0; n < 256; n++) pack[sprintf("%c", n)] = sprintf("%%%02x", n)
474*0f9fec9dSDevin Teske}
475*0f9fec9dSDevin Teske{
476*0f9fec9dSDevin Teske	sline = ""
477*0f9fec9dSDevin Teske	slen = length($0)
478*0f9fec9dSDevin Teske	for (n = 1; n <= slen; n++) {
479*0f9fec9dSDevin Teske		char = substr($0, n, 1)
480*0f9fec9dSDevin Teske		if ( char !~ /^[[:alnum:]_]$/ ) char = pack[char]
481*0f9fec9dSDevin Teske		sline = sline char
482*0f9fec9dSDevin Teske	}
483*0f9fec9dSDevin Teske	output = output ( output ? "%0a" : "" ) sline
484*0f9fec9dSDevin Teske}
485*0f9fec9dSDevin TeskeEND { print output }
486*0f9fec9dSDevin Teske'
487*0f9fec9dSDevin Teskef_uriencode()
488*0f9fec9dSDevin Teske{
489*0f9fec9dSDevin Teske	if [ $# -gt 0 ]; then
490*0f9fec9dSDevin Teske		echo "$1" | awk "$f_uriencode_awk"
491*0f9fec9dSDevin Teske	else
492*0f9fec9dSDevin Teske		awk "$f_uriencode_awk"
493*0f9fec9dSDevin Teske	fi
494*0f9fec9dSDevin Teske}
495*0f9fec9dSDevin Teske
496*0f9fec9dSDevin Teske# f_uridecode [$text]
497*0f9fec9dSDevin Teske#
498*0f9fec9dSDevin Teske# Decode $text from a URI. Encoded characters are converted from their `%XX'
499*0f9fec9dSDevin Teske# sequence into original unencoded ASCII sequences. If $text is missing, data
500*0f9fec9dSDevin Teske# is instead read from standard input.
501*0f9fec9dSDevin Teske#
502*0f9fec9dSDevin Teskef_uridecode_awk='
503*0f9fec9dSDevin TeskeBEGIN { for (n = 0; n < 256; n++) chr[n] = sprintf("%c", n) }
504*0f9fec9dSDevin Teske{
505*0f9fec9dSDevin Teske	sline = ""
506*0f9fec9dSDevin Teske	slen = length($0)
507*0f9fec9dSDevin Teske	for (n = 1; n <= slen; n++)
508*0f9fec9dSDevin Teske	{
509*0f9fec9dSDevin Teske		seq = substr($0, n, 3)
510*0f9fec9dSDevin Teske		if ( seq ~ /^%[[:xdigit:]][[:xdigit:]]$/ ) {
511*0f9fec9dSDevin Teske			hex = substr(seq, 2, 2)
512*0f9fec9dSDevin Teske			sline = sline chr[sprintf("%u", "0x"hex)]
513*0f9fec9dSDevin Teske			n += 2
514*0f9fec9dSDevin Teske		} else
515*0f9fec9dSDevin Teske			sline = sline substr(seq, 1, 1)
516*0f9fec9dSDevin Teske	}
517*0f9fec9dSDevin Teske	print sline
518*0f9fec9dSDevin Teske}
519*0f9fec9dSDevin Teske'
520*0f9fec9dSDevin Teskef_uridecode()
521*0f9fec9dSDevin Teske{
522*0f9fec9dSDevin Teske	if [ $# -gt 0 ]; then
523*0f9fec9dSDevin Teske		echo "$1" | awk "$f_uridecode_awk"
524*0f9fec9dSDevin Teske	else
525*0f9fec9dSDevin Teske		awk "$f_uridecode_awk"
526*0f9fec9dSDevin Teske	fi
527*0f9fec9dSDevin Teske}
528*0f9fec9dSDevin Teske
529d3a0f918SDevin Teske############################################################ MAIN
530d3a0f918SDevin Teske
53156961fd7SDevin Teskef_dprintf "%s: Successfully loaded." strings.subr
53256961fd7SDevin Teske
533ab2043b8SDevin Teskefi # ! $_STRINGS_SUBR
534