xref: /freebsd/usr.sbin/bsdconfig/share/strings.subr (revision 9acbeddc57ddf4561ff182171cad2df29b9cb366)
1ab2043b8SDevin Teskeif [ ! "$_STRINGS_SUBR" ]; then _STRINGS_SUBR=1
2ab2043b8SDevin Teske#
37323adacSDevin Teske# Copyright (c) 2006-2013 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#
37d3a0f918SDevin Teske# Valid characters that can appear in an sh(1) variable name
38d3a0f918SDevin Teske#
39d3a0f918SDevin Teske# Please note that the character ranges A-Z and a-z should be avoided because
40d3a0f918SDevin Teske# these can include accent characters (which are not valid in a variable name).
41d3a0f918SDevin Teske# For example, A-Z matches any character that sorts after A but before Z,
42d3a0f918SDevin Teske# including A and Z. Although ASCII order would make more sense, that is not
43d3a0f918SDevin Teske# how it works.
44d3a0f918SDevin Teske#
45d3a0f918SDevin TeskeVALID_VARNAME_CHARS="0-9ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"
46d3a0f918SDevin Teske
47d3a0f918SDevin Teske############################################################ FUNCTIONS
48ab2043b8SDevin Teske
49ab2043b8SDevin Teske# f_substr "$string" $start [ $length ]
50ab2043b8SDevin Teske#
51ab2043b8SDevin Teske# Simple wrapper to awk(1)'s `substr' function.
52ab2043b8SDevin Teske#
53ab2043b8SDevin Teskef_substr()
54ab2043b8SDevin Teske{
55ab2043b8SDevin Teske	local string="$1" start="${2:-0}" len="${3:-0}"
56ab2043b8SDevin Teske	echo "$string" | awk "{ print substr(\$0, $start, $len) }"
57ab2043b8SDevin Teske}
58ab2043b8SDevin Teske
5960d8a2b5SDevin Teske# f_snprintf $var_to_set $size $format ...
6060d8a2b5SDevin Teske#
6160d8a2b5SDevin Teske# Similar to snprintf(3), write at most $size number of bytes into $var_to_set
6260d8a2b5SDevin Teske# using printf(1) syntax (`$format ...'). The value of $var_to_set is NULL
6360d8a2b5SDevin Teske# unless at-least one byte is stored from the output.
6460d8a2b5SDevin Teske#
6560d8a2b5SDevin Teskef_snprintf()
6660d8a2b5SDevin Teske{
6760d8a2b5SDevin Teske	local __var_to_set="$1" __size="$2"
6860d8a2b5SDevin Teske	shift 2 # var_to_set/size
6960d8a2b5SDevin Teske	eval "$__var_to_set"=\$\( printf \"\$@\" \| awk -v max=\"\$__size\" \''
7060d8a2b5SDevin Teske	{
7160d8a2b5SDevin Teske		len = length($0)
7260d8a2b5SDevin Teske		max -= len
7360d8a2b5SDevin Teske		print substr($0,0,(max > 0 ? len : max + len))
7460d8a2b5SDevin Teske		if ( max < 0 ) exit
7560d8a2b5SDevin Teske		max--
7660d8a2b5SDevin Teske	}'\' \)
7760d8a2b5SDevin Teske}
7860d8a2b5SDevin Teske
79dd5cc066SDevin Teske# f_vsnprintf $var_to_set $size $format $format_args
80dd5cc066SDevin Teske#
81dd5cc066SDevin Teske# Similar to vsnprintf(3), write at most $size number of bytes into $var_to_set
82dd5cc066SDevin Teske# using printf(1) syntax (`$format $format_args'). The value of $var_to_set is
83dd5cc066SDevin Teske# NULL unless at-least one byte is stored from the output.
84dd5cc066SDevin Teske#
85dd5cc066SDevin Teske# Example 1:
86dd5cc066SDevin Teske#
87dd5cc066SDevin Teske# 	limit=7 format="%s"
88dd5cc066SDevin Teske# 	format_args="'abc   123'" # 3-spaces between abc and 123
89dd5cc066SDevin Teske# 	f_vsnprintf foo $limit "$format" "$format_args" # foo=[abc   1]
90dd5cc066SDevin Teske#
91dd5cc066SDevin Teske# Example 2:
92dd5cc066SDevin Teske#
93dd5cc066SDevin Teske# 	limit=12 format="%s %s"
94dd5cc066SDevin Teske# 	format_args="   'doghouse'      'foxhound'   "
95dd5cc066SDevin Teske# 		# even more spaces added to illustrate escape-method
96dd5cc066SDevin Teske# 	f_vsnprintf foo $limit "$format" "$format_args" # foo=[doghouse fox]
97dd5cc066SDevin Teske#
98dd5cc066SDevin Teske# Example 3:
99dd5cc066SDevin Teske#
100dd5cc066SDevin Teske# 	limit=13 format="%s %s"
101dd5cc066SDevin Teske# 	f_shell_escape arg1 'aaa"aaa' # arg1=[aaa"aaa] (no change)
102dd5cc066SDevin Teske# 	f_shell_escape arg2 "aaa'aaa" # arg2=[aaa'\''aaa] (escaped s-quote)
103dd5cc066SDevin Teske# 	format_args="'$arg1' '$arg2'" # use single-quotes to surround args
104dd5cc066SDevin Teske# 	f_vsnprintf foo $limit "$format" "$format_args" # foo=[aaa"aaa aaa'a]
105dd5cc066SDevin Teske#
106dd5cc066SDevin Teske# In all of the above examples, the call to f_vsnprintf() does not change. Only
107dd5cc066SDevin Teske# the contents of $limit, $format, and $format_args changes in each example.
108dd5cc066SDevin Teske#
109dd5cc066SDevin Teskef_vsnprintf()
110dd5cc066SDevin Teske{
111dd5cc066SDevin Teske	eval f_snprintf \"\$1\" \"\$2\" \"\$3\" $4
112dd5cc066SDevin Teske}
113dd5cc066SDevin Teske
114ab2043b8SDevin Teske# f_longest_line_length
115ab2043b8SDevin Teske#
116ab2043b8SDevin Teske# Simple wrapper to an awk(1) script to print the length of the longest line of
117ab2043b8SDevin Teske# input (read from stdin). Supports the newline escape-sequence `\n' for
118ab2043b8SDevin Teske# splitting a single line into multiple lines.
119ab2043b8SDevin Teske#
120ab2043b8SDevin Teskef_longest_line_length_awk='
121ab2043b8SDevin TeskeBEGIN { longest = 0 }
122ab2043b8SDevin Teske{
123ab2043b8SDevin Teske	if (split($0, lines, /\\n/) > 1)
124ab2043b8SDevin Teske	{
125ab2043b8SDevin Teske		for (n in lines)
126ab2043b8SDevin Teske		{
127ab2043b8SDevin Teske			len = length(lines[n])
128ab2043b8SDevin Teske			longest = ( len > longest ? len : longest )
129ab2043b8SDevin Teske		}
130ab2043b8SDevin Teske	}
131ab2043b8SDevin Teske	else
132ab2043b8SDevin Teske	{
133ab2043b8SDevin Teske		len = length($0)
134ab2043b8SDevin Teske		longest = ( len > longest ? len : longest )
135ab2043b8SDevin Teske	}
136ab2043b8SDevin Teske}
137ab2043b8SDevin TeskeEND { print longest }
138ab2043b8SDevin Teske'
139ab2043b8SDevin Teskef_longest_line_length()
140ab2043b8SDevin Teske{
141ab2043b8SDevin Teske	awk "$f_longest_line_length_awk"
142ab2043b8SDevin Teske}
143ab2043b8SDevin Teske
144ab2043b8SDevin Teske# f_number_of_lines
145ab2043b8SDevin Teske#
146ab2043b8SDevin Teske# Simple wrapper to an awk(1) script to print the number of lines read from
147ab2043b8SDevin Teske# stdin. Supports newline escape-sequence `\n' for splitting a single line into
148ab2043b8SDevin Teske# multiple lines.
149ab2043b8SDevin Teske#
150ab2043b8SDevin Teskef_number_of_lines_awk='
151ab2043b8SDevin TeskeBEGIN { num_lines = 0 }
152ab2043b8SDevin Teske{
1537fba6f48SDevin Teske	num_lines += split(" "$0, unused, /\\n/)
154ab2043b8SDevin Teske}
155ab2043b8SDevin TeskeEND { print num_lines }
156ab2043b8SDevin Teske'
157ab2043b8SDevin Teskef_number_of_lines()
158ab2043b8SDevin Teske{
159ab2043b8SDevin Teske	awk "$f_number_of_lines_awk"
160ab2043b8SDevin Teske}
161ab2043b8SDevin Teske
162ab2043b8SDevin Teske# f_isinteger $arg
163ab2043b8SDevin Teske#
164ab2043b8SDevin Teske# Returns true if argument is a positive/negative whole integer.
165ab2043b8SDevin Teske#
166ab2043b8SDevin Teskef_isinteger()
167ab2043b8SDevin Teske{
168ab2043b8SDevin Teske	local arg="$1"
169ab2043b8SDevin Teske
170ab2043b8SDevin Teske	# Prevent division-by-zero
171ab2043b8SDevin Teske	[ "$arg" = "0" ] && return $SUCCESS
172ab2043b8SDevin Teske
173ab2043b8SDevin Teske	# Attempt to perform arithmetic divison (an operation which will exit
174ab2043b8SDevin Teske	# with error unless arg is a valid positive/negative whole integer).
175ab2043b8SDevin Teske	#
176fb7d723eSDevin Teske	( : $((0/$arg)) ) > /dev/null 2>&1
177ab2043b8SDevin Teske}
178ab2043b8SDevin Teske
1797323adacSDevin Teske# f_uriencode [$text]
1807323adacSDevin Teske#
1817323adacSDevin Teske# Encode $text for the purpose of embedding safely into a URL. Non-alphanumeric
1827323adacSDevin Teske# characters are converted to `%XX' sequence where XX represents the hexa-
1837323adacSDevin Teske# decimal ordinal of the non-alphanumeric character. If $text is missing, data
1847323adacSDevin Teske# is instead read from standard input.
1857323adacSDevin Teske#
1867323adacSDevin Teskef_uriencode_awk='
1877323adacSDevin TeskeBEGIN {
1887323adacSDevin Teske	output = ""
1897323adacSDevin Teske	for (n = 0; n < 256; n++) pack[sprintf("%c", n)] = sprintf("%%%02x", n)
1907323adacSDevin Teske}
1917323adacSDevin Teske{
1927323adacSDevin Teske	sline = ""
1937323adacSDevin Teske	slen = length($0)
1947323adacSDevin Teske	for (n = 1; n <= slen; n++) {
1957323adacSDevin Teske		char = substr($0, n, 1)
1967323adacSDevin Teske		if ( char !~ /^[[:alnum:]_]$/ ) char = pack[char]
1977323adacSDevin Teske		sline = sline char
1987323adacSDevin Teske	}
1997323adacSDevin Teske	output = output ( output ? "%0a" : "" ) sline
2007323adacSDevin Teske}
2017323adacSDevin TeskeEND { print output }
2027323adacSDevin Teske'
2037323adacSDevin Teskef_uriencode()
2047323adacSDevin Teske{
2057323adacSDevin Teske	if [ $# -gt 0 ]; then
2067323adacSDevin Teske		echo "$1" | awk "$f_uriencode_awk"
2077323adacSDevin Teske	else
2087323adacSDevin Teske		awk "$f_uriencode_awk"
2097323adacSDevin Teske	fi
2107323adacSDevin Teske}
2117323adacSDevin Teske
2127323adacSDevin Teske# f_uridecode [$text]
2137323adacSDevin Teske#
2147323adacSDevin Teske# Decode $text from a URI. Encoded characters are converted from their `%XX'
2157323adacSDevin Teske# sequence into original unencoded ASCII sequences. If $text is missing, data
2167323adacSDevin Teske# is instead read from standard input.
2177323adacSDevin Teske#
2187323adacSDevin Teskef_uridecode_awk='
2197323adacSDevin TeskeBEGIN { for (n = 0; n < 256; n++) chr[n] = sprintf("%c", n) }
2207323adacSDevin Teske{
2217323adacSDevin Teske	sline = ""
2227323adacSDevin Teske	slen = length($0)
2237323adacSDevin Teske	for (n = 1; n <= slen; n++)
2247323adacSDevin Teske	{
2257323adacSDevin Teske		seq = substr($0, n, 3)
2267323adacSDevin Teske		if ( seq ~ /^%[[:xdigit:]][[:xdigit:]]$/ ) {
2277323adacSDevin Teske			hex = substr(seq, 2, 2)
2287323adacSDevin Teske			sline = sline chr[sprintf("%u", "0x"hex)]
2297323adacSDevin Teske			n += 2
2307323adacSDevin Teske		} else
2317323adacSDevin Teske			sline = sline substr(seq, 1, 1)
2327323adacSDevin Teske	}
2337323adacSDevin Teske	print sline
2347323adacSDevin Teske}
2357323adacSDevin Teske'
2367323adacSDevin Teskef_uridecode()
2377323adacSDevin Teske{
2387323adacSDevin Teske	if [ $# -gt 0 ]; then
2397323adacSDevin Teske		echo "$1" | awk "$f_uridecode_awk"
2407323adacSDevin Teske	else
2417323adacSDevin Teske		awk "$f_uridecode_awk"
2427323adacSDevin Teske	fi
2437323adacSDevin Teske}
2447323adacSDevin Teske
245d3a0f918SDevin Teske# f_replaceall $string $find $replace [$var_to_set]
246d3a0f918SDevin Teske#
247f82ca17bSDevin Teske# Replace all occurrences of $find in $string with $replace. If $var_to_set is
248d3a0f918SDevin Teske# either missing or NULL, the variable name is produced on standard out for
249d3a0f918SDevin Teske# capturing in a sub-shell (which is less recommended due to performance
250d3a0f918SDevin Teske# degradation).
251d3a0f918SDevin Teske#
252d3a0f918SDevin Teskef_replaceall()
253d3a0f918SDevin Teske{
254d3a0f918SDevin Teske	local __left="" __right="$1"
255d3a0f918SDevin Teske	local __find="$2" __replace="$3" __var_to_set="$4"
256d3a0f918SDevin Teske	while :; do
257d3a0f918SDevin Teske		case "$__right" in *$__find*)
258d3a0f918SDevin Teske			__left="$__left${__right%%$__find*}$__replace"
259d3a0f918SDevin Teske			__right="${__right#*$__find}"
260d3a0f918SDevin Teske			continue
261d3a0f918SDevin Teske		esac
262d3a0f918SDevin Teske		break
263d3a0f918SDevin Teske	done
264d3a0f918SDevin Teske	__left="$__left${__right#*$__find}"
265d3a0f918SDevin Teske	if [ "$__var_to_set" ]; then
266d3a0f918SDevin Teske		setvar "$__var_to_set" "$__left"
267d3a0f918SDevin Teske	else
268d3a0f918SDevin Teske		echo "$__left"
269d3a0f918SDevin Teske	fi
270d3a0f918SDevin Teske}
271d3a0f918SDevin Teske
272d3a0f918SDevin Teske# f_str2varname $string [$var_to_set]
273d3a0f918SDevin Teske#
274d3a0f918SDevin Teske# Convert a string into a suitable value to be used as a variable name
275d3a0f918SDevin Teske# by converting unsuitable characters into the underscrore [_]. If $var_to_set
276d3a0f918SDevin Teske# is either missing or NULL, the variable name is produced on standard out for
277d3a0f918SDevin Teske# capturing in a sub-shell (which is less recommended due to performance
278d3a0f918SDevin Teske# degradation).
279d3a0f918SDevin Teske#
280d3a0f918SDevin Teskef_str2varname()
281d3a0f918SDevin Teske{
282d3a0f918SDevin Teske	local __string="$1" __var_to_set="$2"
283d3a0f918SDevin Teske	f_replaceall "$__string" "[!$VALID_VARNAME_CHARS]" "_" "$__var_to_set"
284d3a0f918SDevin Teske}
285d3a0f918SDevin Teske
286d3a0f918SDevin Teske# f_shell_escape $string [$var_to_set]
287d3a0f918SDevin Teske#
288d3a0f918SDevin Teske# Escape $string for shell eval statement(s) by replacing all single-quotes
289d3a0f918SDevin Teske# with a special sequence that creates a compound string when interpolated
290d3a0f918SDevin Teske# by eval with surrounding single-quotes.
291d3a0f918SDevin Teske#
292d3a0f918SDevin Teske# For example:
293d3a0f918SDevin Teske#
294d3a0f918SDevin Teske# 	foo="abc'123"
295d3a0f918SDevin Teske# 	f_shell_escape "$foo" bar # bar=[abc'\''123]
29610908a6fSDevin Teske# 	eval echo \'$bar\' # produces abc'123
297d3a0f918SDevin Teske#
298d3a0f918SDevin Teske# This is helpful when processing an argument list that has to retain its
299d3a0f918SDevin Teske# escaped structure for later evaluations.
300d3a0f918SDevin Teske#
301d3a0f918SDevin Teske# WARNING: Surrounding single-quotes are not added; this is the responsibility
302d3a0f918SDevin Teske# of the code passing the escaped values to eval (which also aids readability).
303d3a0f918SDevin Teske#
304d3a0f918SDevin Teskef_shell_escape()
305d3a0f918SDevin Teske{
306d3a0f918SDevin Teske	local __string="$1" __var_to_set="$2"
307d3a0f918SDevin Teske	f_replaceall "$__string" "'" "'\\''" "$__var_to_set"
308d3a0f918SDevin Teske}
309d3a0f918SDevin Teske
310d3a0f918SDevin Teske# f_shell_unescape $string [$var_to_set]
311d3a0f918SDevin Teske#
312d3a0f918SDevin Teske# The antithesis of f_shell_escape(), this function takes an escaped $string
313d3a0f918SDevin Teske# and expands it.
314d3a0f918SDevin Teske#
315d3a0f918SDevin Teske# For example:
316d3a0f918SDevin Teske#
317d3a0f918SDevin Teske# 	foo="abc'123"
318d3a0f918SDevin Teske# 	f_shell_escape "$foo" bar # bar=[abc'\''123]
319d3a0f918SDevin Teske# 	f_shell_unescape "$bar" # produces abc'123
320d3a0f918SDevin Teske#
321d3a0f918SDevin Teskef_shell_unescape()
322d3a0f918SDevin Teske{
323d3a0f918SDevin Teske	local __string="$1" __var_to_set="$2"
324d3a0f918SDevin Teske	f_replaceall "$__string" "'\\''" "'" "$__var_to_set"
325d3a0f918SDevin Teske}
326d3a0f918SDevin Teske
327a96ea66fSDevin Teske# f_expand_number $string [$var_to_set]
328a96ea66fSDevin Teske#
329a96ea66fSDevin Teske# Unformat $string into a number, optionally to be stored in $var_to_set. This
330a96ea66fSDevin Teske# function follows the SI power of two convention.
331a96ea66fSDevin Teske#
332a96ea66fSDevin Teske# The prefixes are:
333a96ea66fSDevin Teske#
334a96ea66fSDevin Teske# 	Prefix	Description	Multiplier
335a96ea66fSDevin Teske# 	k	kilo		1024
336a96ea66fSDevin Teske# 	M	mega		1048576
337a96ea66fSDevin Teske# 	G	giga		1073741824
338a96ea66fSDevin Teske# 	T	tera		1099511627776
339a96ea66fSDevin Teske# 	P	peta		1125899906842624
340a96ea66fSDevin Teske# 	E	exa		1152921504606846976
341a96ea66fSDevin Teske#
342a96ea66fSDevin Teske# NOTE: Prefixes are case-insensitive.
343a96ea66fSDevin Teske#
344*9acbeddcSDevin Teske# Upon successful completion, success status is returned; otherwise the number
345*9acbeddcSDevin Teske# -1 is produced ($var_to_set set to -1 or if $var_to_set is NULL or missing)
346*9acbeddcSDevin Teske# on standard output. In the case of failure, the error status will be one of:
347a96ea66fSDevin Teske#
348*9acbeddcSDevin Teske# 	Status	Reason
349*9acbeddcSDevin Teske# 	1	Given $string contains no digits
350*9acbeddcSDevin Teske# 	2	An unrecognized prefix was given
351*9acbeddcSDevin Teske# 	3	Result too large to calculate
352a96ea66fSDevin Teske#
353a96ea66fSDevin Teskef_expand_number()
354a96ea66fSDevin Teske{
355a96ea66fSDevin Teske	local __string="$1" __var_to_set="$2"
356*9acbeddcSDevin Teske	local __cp __num __bshift __maxinput
357a96ea66fSDevin Teske
358a96ea66fSDevin Teske	# Remove any leading non-digits
359a96ea66fSDevin Teske	while :; do
360a96ea66fSDevin Teske		__cp="$__string"
361a96ea66fSDevin Teske		__string="${__cp#[!0-9]}"
362a96ea66fSDevin Teske		[ "$__string" = "$__cp" ] && break
363a96ea66fSDevin Teske	done
364a96ea66fSDevin Teske
365*9acbeddcSDevin Teske	# Produce `-1' if string didn't contain any digits
366a96ea66fSDevin Teske	if [ ! "$__string" ]; then
367a96ea66fSDevin Teske		if [ "$__var_to_set" ]; then
368a96ea66fSDevin Teske			setvar "$__var_to_set" -1
369a96ea66fSDevin Teske		else
370a96ea66fSDevin Teske			echo -1
371a96ea66fSDevin Teske		fi
372*9acbeddcSDevin Teske		return 1 # 1 = "Given $string contains no digits"
373a96ea66fSDevin Teske	fi
374a96ea66fSDevin Teske
375a96ea66fSDevin Teske	# Store the numbers
376a96ea66fSDevin Teske	__num="${__string%%[!0-9]*}"
377a96ea66fSDevin Teske
378a96ea66fSDevin Teske	# Shortcut
379a96ea66fSDevin Teske	if [ $__num -eq 0 ]; then
380a96ea66fSDevin Teske		if [ "$__var_to_set" ]; then
381a96ea66fSDevin Teske			setvar "$__var_to_set" 0
382a96ea66fSDevin Teske		else
383a96ea66fSDevin Teske			echo 0
384a96ea66fSDevin Teske		fi
385a96ea66fSDevin Teske		return $SUCCESS
386a96ea66fSDevin Teske	fi
387a96ea66fSDevin Teske
388a96ea66fSDevin Teske	# Remove all the leading numbers from the string to get at the prefix
389a96ea66fSDevin Teske	while :; do
390a96ea66fSDevin Teske		__cp="$__string"
391a96ea66fSDevin Teske		__string="${__cp#[0-9]}"
392a96ea66fSDevin Teske		[ "$__string" = "$__cp" ] && break
393a96ea66fSDevin Teske	done
394a96ea66fSDevin Teske
395*9acbeddcSDevin Teske	#
396*9acbeddcSDevin Teske	# Test for invalid prefix (and determine bitshift length)
397*9acbeddcSDevin Teske	#
398a96ea66fSDevin Teske	case "$__string" in
399*9acbeddcSDevin Teske	""|[[:space:]]*) # Shortcut
400*9acbeddcSDevin Teske		if [ "$__var_to_set" ]; then
401*9acbeddcSDevin Teske			setvar "$__var_to_set" $__num
402*9acbeddcSDevin Teske		else
403*9acbeddcSDevin Teske			echo $__num
404*9acbeddcSDevin Teske		fi
405*9acbeddcSDevin Teske		return $SUCCESS ;;
406*9acbeddcSDevin Teske	[Kk]*) __bshift=10 ;;
407*9acbeddcSDevin Teske	[Mm]*) __bshift=20 ;;
408*9acbeddcSDevin Teske	[Gg]*) __bshift=30 ;;
409*9acbeddcSDevin Teske	[Tt]*) __bshift=40 ;;
410*9acbeddcSDevin Teske	[Pp]*) __bshift=50 ;;
411*9acbeddcSDevin Teske	[Ee]*) __bshift=60 ;;
412a96ea66fSDevin Teske	*)
413a96ea66fSDevin Teske		# Unknown prefix
414a96ea66fSDevin Teske		if [ "$__var_to_set" ]; then
415a96ea66fSDevin Teske			setvar "$__var_to_set" -1
416a96ea66fSDevin Teske		else
417a96ea66fSDevin Teske			echo -1
418a96ea66fSDevin Teske		fi
419*9acbeddcSDevin Teske		return 2 # 2 = "An unrecognized prefix was given"
420a96ea66fSDevin Teske	esac
421a96ea66fSDevin Teske
422*9acbeddcSDevin Teske	# Determine if the wheels fall off
423*9acbeddcSDevin Teske	__maxinput=$(( 0x7fffffffffffffff >> $__bshift ))
424*9acbeddcSDevin Teske	if [ $__num -gt $__maxinput ]; then
425*9acbeddcSDevin Teske		# Input (before expanding) would exceed 64-bit signed int
426a96ea66fSDevin Teske		if [ "$__var_to_set" ]; then
427a96ea66fSDevin Teske			setvar "$__var_to_set" -1
428a96ea66fSDevin Teske		else
429a96ea66fSDevin Teske			echo -1
430a96ea66fSDevin Teske		fi
431*9acbeddcSDevin Teske		return 3 # 3 = "Result too large to calculate"
432a96ea66fSDevin Teske	fi
433a96ea66fSDevin Teske
434*9acbeddcSDevin Teske	# Shift the number out and produce it
435*9acbeddcSDevin Teske	__num=$(( $__num << $__bshift ))
436a96ea66fSDevin Teske	if [ "$__var_to_set" ]; then
437a96ea66fSDevin Teske		setvar "$__var_to_set" $__num
438a96ea66fSDevin Teske	else
439a96ea66fSDevin Teske		echo $__num
440a96ea66fSDevin Teske	fi
441a96ea66fSDevin Teske}
442a96ea66fSDevin Teske
443d3a0f918SDevin Teske############################################################ MAIN
444d3a0f918SDevin Teske
44556961fd7SDevin Teskef_dprintf "%s: Successfully loaded." strings.subr
44656961fd7SDevin Teske
447ab2043b8SDevin Teskefi # ! $_STRINGS_SUBR
448