1252995Sdteskeif [ ! "$_STRINGS_SUBR" ]; then _STRINGS_SUBR=1
2252995Sdteske#
3252995Sdteske# Copyright (c) 2006-2013 Devin Teske
4252995Sdteske# All rights reserved.
5252995Sdteske#
6252995Sdteske# Redistribution and use in source and binary forms, with or without
7252995Sdteske# modification, are permitted provided that the following conditions
8252995Sdteske# are met:
9252995Sdteske# 1. Redistributions of source code must retain the above copyright
10252995Sdteske#    notice, this list of conditions and the following disclaimer.
11252995Sdteske# 2. Redistributions in binary form must reproduce the above copyright
12252995Sdteske#    notice, this list of conditions and the following disclaimer in the
13252995Sdteske#    documentation and/or other materials provided with the distribution.
14252995Sdteske#
15252995Sdteske# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16252995Sdteske# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17252995Sdteske# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18252995Sdteske# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19252995Sdteske# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20252995Sdteske# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21252995Sdteske# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22252995Sdteske# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23252995Sdteske# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24252995Sdteske# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25252995Sdteske# SUCH DAMAGE.
26252995Sdteske#
27252995Sdteske# $FreeBSD$
28252995Sdteske#
29263791Sdteske############################################################ INCLUDES
30263791Sdteske
31263791SdteskeBSDCFG_SHARE="/usr/share/bsdconfig"
32263791Sdteske. $BSDCFG_SHARE/common.subr || exit 1
33263791Sdteske
34252995Sdteske############################################################ GLOBALS
35252995Sdteske
36252995Sdteske#
37263791Sdteske# A Literal newline (for use with f_replace_all(), or IFS, or whatever)
38263791Sdteske#
39263791SdteskeNL="
40263791Sdteske" # END-QUOTE
41263791Sdteske
42263791Sdteske#
43252995Sdteske# Valid characters that can appear in an sh(1) variable name
44252995Sdteske#
45252995Sdteske# Please note that the character ranges A-Z and a-z should be avoided because
46252995Sdteske# these can include accent characters (which are not valid in a variable name).
47252995Sdteske# For example, A-Z matches any character that sorts after A but before Z,
48252995Sdteske# including A and Z. Although ASCII order would make more sense, that is not
49252995Sdteske# how it works.
50252995Sdteske#
51252995SdteskeVALID_VARNAME_CHARS="0-9ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"
52252995Sdteske
53252995Sdteske############################################################ FUNCTIONS
54252995Sdteske
55263791Sdteske# f_substr "$string" $start [$length]
56252995Sdteske#
57252995Sdteske# Simple wrapper to awk(1)'s `substr' function.
58252995Sdteske#
59252995Sdteskef_substr()
60252995Sdteske{
61252995Sdteske	local string="$1" start="${2:-0}" len="${3:-0}"
62252995Sdteske	echo "$string" | awk "{ print substr(\$0, $start, $len) }"
63252995Sdteske}
64252995Sdteske
65263791Sdteske# f_snprintf $var_to_set $size $format [$arguments ...]
66252995Sdteske#
67252995Sdteske# Similar to snprintf(3), write at most $size number of bytes into $var_to_set
68263791Sdteske# using printf(1) syntax (`$format [$arguments ...]'). The value of $var_to_set
69263791Sdteske# is NULL unless at-least one byte is stored from the output.
70252995Sdteske#
71252995Sdteskef_snprintf()
72252995Sdteske{
73252995Sdteske	local __var_to_set="$1" __size="$2"
74263791Sdteske	shift 2 # var_to_set size
75263791Sdteske	eval "$__var_to_set"=\$\( printf -- \"\$@\" \| \
76263791Sdteske		awk -v max=\"\$__size\" \''
77252995Sdteske	{
78252995Sdteske		len = length($0)
79252995Sdteske		max -= len
80252995Sdteske		print substr($0,0,(max > 0 ? len : max + len))
81252995Sdteske		if ( max < 0 ) exit
82252995Sdteske		max--
83252995Sdteske	}'\' \)
84252995Sdteske}
85252995Sdteske
86263791Sdteske# f_sprintf $var_to_set $format [$arguments ...]
87263791Sdteske#
88263791Sdteske# Similar to sprintf(3), write a string into $var_to_set using printf(1) syntax
89263791Sdteske# (`$format [$arguments ...]').
90263791Sdteske#
91263791Sdteskef_sprintf()
92263791Sdteske{
93263791Sdteske	local __var_to_set="$1"
94263791Sdteske	shift 1 # var_to_set
95263791Sdteske	eval "$__var_to_set"=\$\( printf -- \"\$@\" \)
96263791Sdteske}
97263791Sdteske
98252995Sdteske# f_vsnprintf $var_to_set $size $format $format_args
99252995Sdteske#
100252995Sdteske# Similar to vsnprintf(3), write at most $size number of bytes into $var_to_set
101252995Sdteske# using printf(1) syntax (`$format $format_args'). The value of $var_to_set is
102252995Sdteske# NULL unless at-least one byte is stored from the output.
103252995Sdteske#
104252995Sdteske# Example 1:
105252995Sdteske#
106252995Sdteske# 	limit=7 format="%s"
107252995Sdteske# 	format_args="'abc   123'" # 3-spaces between abc and 123
108252995Sdteske# 	f_vsnprintf foo $limit "$format" "$format_args" # foo=[abc   1]
109252995Sdteske#
110252995Sdteske# Example 2:
111252995Sdteske#
112252995Sdteske# 	limit=12 format="%s %s"
113252995Sdteske# 	format_args="   'doghouse'      'foxhound'   "
114252995Sdteske# 		# even more spaces added to illustrate escape-method
115252995Sdteske# 	f_vsnprintf foo $limit "$format" "$format_args" # foo=[doghouse fox]
116252995Sdteske#
117252995Sdteske# Example 3:
118252995Sdteske#
119252995Sdteske# 	limit=13 format="%s %s"
120252995Sdteske# 	f_shell_escape arg1 'aaa"aaa' # arg1=[aaa"aaa] (no change)
121252995Sdteske# 	f_shell_escape arg2 "aaa'aaa" # arg2=[aaa'\''aaa] (escaped s-quote)
122252995Sdteske# 	format_args="'$arg1' '$arg2'" # use single-quotes to surround args
123252995Sdteske# 	f_vsnprintf foo $limit "$format" "$format_args" # foo=[aaa"aaa aaa'a]
124252995Sdteske#
125252995Sdteske# In all of the above examples, the call to f_vsnprintf() does not change. Only
126252995Sdteske# the contents of $limit, $format, and $format_args changes in each example.
127252995Sdteske#
128252995Sdteskef_vsnprintf()
129252995Sdteske{
130252995Sdteske	eval f_snprintf \"\$1\" \"\$2\" \"\$3\" $4
131252995Sdteske}
132252995Sdteske
133263791Sdteske# f_vsprintf $var_to_set $format $format_args
134263791Sdteske#
135263791Sdteske# Similar to vsprintf(3), write a string into $var_to_set using printf(1)
136263791Sdteske# syntax (`$format $format_args').
137263791Sdteske#
138263791Sdteskef_vsprintf()
139263791Sdteske{
140263791Sdteske	eval f_sprintf \"\$1\" \"\$2\" $3
141263791Sdteske}
142263791Sdteske
143252995Sdteske# f_longest_line_length
144252995Sdteske#
145252995Sdteske# Simple wrapper to an awk(1) script to print the length of the longest line of
146252995Sdteske# input (read from stdin). Supports the newline escape-sequence `\n' for
147252995Sdteske# splitting a single line into multiple lines.
148252995Sdteske#
149252995Sdteskef_longest_line_length_awk='
150252995SdteskeBEGIN { longest = 0 }
151252995Sdteske{
152252995Sdteske	if (split($0, lines, /\\n/) > 1)
153252995Sdteske	{
154252995Sdteske		for (n in lines)
155252995Sdteske		{
156252995Sdteske			len = length(lines[n])
157252995Sdteske			longest = ( len > longest ? len : longest )
158252995Sdteske		}
159252995Sdteske	}
160252995Sdteske	else
161252995Sdteske	{
162252995Sdteske		len = length($0)
163252995Sdteske		longest = ( len > longest ? len : longest )
164252995Sdteske	}
165252995Sdteske}
166252995SdteskeEND { print longest }
167252995Sdteske'
168252995Sdteskef_longest_line_length()
169252995Sdteske{
170252995Sdteske	awk "$f_longest_line_length_awk"
171252995Sdteske}
172252995Sdteske
173252995Sdteske# f_number_of_lines
174252995Sdteske#
175252995Sdteske# Simple wrapper to an awk(1) script to print the number of lines read from
176252995Sdteske# stdin. Supports newline escape-sequence `\n' for splitting a single line into
177252995Sdteske# multiple lines.
178252995Sdteske#
179252995Sdteskef_number_of_lines_awk='
180252995SdteskeBEGIN { num_lines = 0 }
181252995Sdteske{
182252995Sdteske	num_lines += split(" "$0, unused, /\\n/)
183252995Sdteske}
184252995SdteskeEND { print num_lines }
185252995Sdteske'
186252995Sdteskef_number_of_lines()
187252995Sdteske{
188252995Sdteske	awk "$f_number_of_lines_awk"
189252995Sdteske}
190252995Sdteske
191252995Sdteske# f_isinteger $arg
192252995Sdteske#
193252995Sdteske# Returns true if argument is a positive/negative whole integer.
194252995Sdteske#
195252995Sdteskef_isinteger()
196252995Sdteske{
197263791Sdteske	local arg="${1#-}"
198263791Sdteske	[ "${arg:-x}" = "${arg%[!0-9]*}" ]
199252995Sdteske}
200252995Sdteske
201252995Sdteske# f_uriencode [$text]
202252995Sdteske#
203252995Sdteske# Encode $text for the purpose of embedding safely into a URL. Non-alphanumeric
204252995Sdteske# characters are converted to `%XX' sequence where XX represents the hexa-
205252995Sdteske# decimal ordinal of the non-alphanumeric character. If $text is missing, data
206252995Sdteske# is instead read from standard input.
207252995Sdteske#
208252995Sdteskef_uriencode_awk='
209252995SdteskeBEGIN {
210252995Sdteske	output = ""
211252995Sdteske	for (n = 0; n < 256; n++) pack[sprintf("%c", n)] = sprintf("%%%02x", n)
212252995Sdteske}
213252995Sdteske{
214252995Sdteske	sline = ""
215252995Sdteske	slen = length($0)
216252995Sdteske	for (n = 1; n <= slen; n++) {
217252995Sdteske		char = substr($0, n, 1)
218252995Sdteske		if ( char !~ /^[[:alnum:]_]$/ ) char = pack[char]
219252995Sdteske		sline = sline char
220252995Sdteske	}
221252995Sdteske	output = output ( output ? "%0a" : "" ) sline
222252995Sdteske}
223252995SdteskeEND { print output }
224252995Sdteske'
225252995Sdteskef_uriencode()
226252995Sdteske{
227252995Sdteske	if [ $# -gt 0 ]; then
228252995Sdteske		echo "$1" | awk "$f_uriencode_awk"
229252995Sdteske	else
230252995Sdteske		awk "$f_uriencode_awk"
231252995Sdteske	fi
232252995Sdteske}
233252995Sdteske
234252995Sdteske# f_uridecode [$text]
235252995Sdteske#
236252995Sdteske# Decode $text from a URI. Encoded characters are converted from their `%XX'
237252995Sdteske# sequence into original unencoded ASCII sequences. If $text is missing, data
238252995Sdteske# is instead read from standard input.
239252995Sdteske#
240252995Sdteskef_uridecode_awk='
241252995SdteskeBEGIN { for (n = 0; n < 256; n++) chr[n] = sprintf("%c", n) }
242252995Sdteske{
243252995Sdteske	sline = ""
244252995Sdteske	slen = length($0)
245252995Sdteske	for (n = 1; n <= slen; n++)
246252995Sdteske	{
247252995Sdteske		seq = substr($0, n, 3)
248252995Sdteske		if ( seq ~ /^%[[:xdigit:]][[:xdigit:]]$/ ) {
249252995Sdteske			hex = substr(seq, 2, 2)
250252995Sdteske			sline = sline chr[sprintf("%u", "0x"hex)]
251252995Sdteske			n += 2
252252995Sdteske		} else
253252995Sdteske			sline = sline substr(seq, 1, 1)
254252995Sdteske	}
255252995Sdteske	print sline
256252995Sdteske}
257252995Sdteske'
258252995Sdteskef_uridecode()
259252995Sdteske{
260252995Sdteske	if [ $# -gt 0 ]; then
261252995Sdteske		echo "$1" | awk "$f_uridecode_awk"
262252995Sdteske	else
263252995Sdteske		awk "$f_uridecode_awk"
264252995Sdteske	fi
265252995Sdteske}
266252995Sdteske
267252995Sdteske# f_replaceall $string $find $replace [$var_to_set]
268252995Sdteske#
269252995Sdteske# Replace all occurrences of $find in $string with $replace. If $var_to_set is
270252995Sdteske# either missing or NULL, the variable name is produced on standard out for
271252995Sdteske# capturing in a sub-shell (which is less recommended due to performance
272252995Sdteske# degradation).
273252995Sdteske#
274263791Sdteske# To replace newlines or a sequence containing the newline character, use $NL
275263791Sdteske# as `\n' is not supported.
276263791Sdteske#
277252995Sdteskef_replaceall()
278252995Sdteske{
279252995Sdteske	local __left="" __right="$1"
280252995Sdteske	local __find="$2" __replace="$3" __var_to_set="$4"
281252995Sdteske	while :; do
282252995Sdteske		case "$__right" in *$__find*)
283252995Sdteske			__left="$__left${__right%%$__find*}$__replace"
284252995Sdteske			__right="${__right#*$__find}"
285252995Sdteske			continue
286252995Sdteske		esac
287252995Sdteske		break
288252995Sdteske	done
289252995Sdteske	__left="$__left${__right#*$__find}"
290252995Sdteske	if [ "$__var_to_set" ]; then
291252995Sdteske		setvar "$__var_to_set" "$__left"
292252995Sdteske	else
293252995Sdteske		echo "$__left"
294252995Sdteske	fi
295252995Sdteske}
296252995Sdteske
297252995Sdteske# f_str2varname $string [$var_to_set]
298252995Sdteske#
299252995Sdteske# Convert a string into a suitable value to be used as a variable name
300252995Sdteske# by converting unsuitable characters into the underscrore [_]. If $var_to_set
301252995Sdteske# is either missing or NULL, the variable name is produced on standard out for
302252995Sdteske# capturing in a sub-shell (which is less recommended due to performance
303252995Sdteske# degradation).
304252995Sdteske#
305252995Sdteskef_str2varname()
306252995Sdteske{
307252995Sdteske	local __string="$1" __var_to_set="$2"
308252995Sdteske	f_replaceall "$__string" "[!$VALID_VARNAME_CHARS]" "_" "$__var_to_set"
309252995Sdteske}
310252995Sdteske
311252995Sdteske# f_shell_escape $string [$var_to_set]
312252995Sdteske#
313252995Sdteske# Escape $string for shell eval statement(s) by replacing all single-quotes
314252995Sdteske# with a special sequence that creates a compound string when interpolated
315252995Sdteske# by eval with surrounding single-quotes.
316252995Sdteske#
317252995Sdteske# For example:
318252995Sdteske#
319252995Sdteske# 	foo="abc'123"
320252995Sdteske# 	f_shell_escape "$foo" bar # bar=[abc'\''123]
321252995Sdteske# 	eval echo \'$bar\' # produces abc'123
322252995Sdteske#
323252995Sdteske# This is helpful when processing an argument list that has to retain its
324252995Sdteske# escaped structure for later evaluations.
325252995Sdteske#
326252995Sdteske# WARNING: Surrounding single-quotes are not added; this is the responsibility
327252995Sdteske# of the code passing the escaped values to eval (which also aids readability).
328252995Sdteske#
329252995Sdteskef_shell_escape()
330252995Sdteske{
331252995Sdteske	local __string="$1" __var_to_set="$2"
332252995Sdteske	f_replaceall "$__string" "'" "'\\''" "$__var_to_set"
333252995Sdteske}
334252995Sdteske
335252995Sdteske# f_shell_unescape $string [$var_to_set]
336252995Sdteske#
337252995Sdteske# The antithesis of f_shell_escape(), this function takes an escaped $string
338252995Sdteske# and expands it.
339252995Sdteske#
340252995Sdteske# For example:
341252995Sdteske#
342252995Sdteske# 	foo="abc'123"
343252995Sdteske# 	f_shell_escape "$foo" bar # bar=[abc'\''123]
344252995Sdteske# 	f_shell_unescape "$bar" # produces abc'123
345252995Sdteske#
346252995Sdteskef_shell_unescape()
347252995Sdteske{
348252995Sdteske	local __string="$1" __var_to_set="$2"
349252995Sdteske	f_replaceall "$__string" "'\\''" "'" "$__var_to_set"
350252995Sdteske}
351252995Sdteske
352263791Sdteske# f_expand_number $string [$var_to_set]
353263791Sdteske#
354263791Sdteske# Unformat $string into a number, optionally to be stored in $var_to_set. This
355263791Sdteske# function follows the SI power of two convention.
356263791Sdteske#
357263791Sdteske# The prefixes are:
358263791Sdteske#
359263791Sdteske# 	Prefix	Description	Multiplier
360263791Sdteske# 	k	kilo		1024
361263791Sdteske# 	M	mega		1048576
362263791Sdteske# 	G	giga		1073741824
363263791Sdteske# 	T	tera		1099511627776
364263791Sdteske# 	P	peta		1125899906842624
365263791Sdteske# 	E	exa		1152921504606846976
366263791Sdteske#
367263791Sdteske# NOTE: Prefixes are case-insensitive.
368263791Sdteske#
369263791Sdteske# Upon successful completion, success status is returned; otherwise the number
370263791Sdteske# -1 is produced ($var_to_set set to -1 or if $var_to_set is NULL or missing)
371263791Sdteske# on standard output. In the case of failure, the error status will be one of:
372263791Sdteske#
373263791Sdteske# 	Status	Reason
374263791Sdteske# 	1	Given $string contains no digits
375263791Sdteske# 	2	An unrecognized prefix was given
376263791Sdteske# 	3	Result too large to calculate
377263791Sdteske#
378263791Sdteskef_expand_number()
379263791Sdteske{
380263791Sdteske	local __string="$1" __var_to_set="$2"
381263791Sdteske	local __cp __num __bshift __maxinput
382263791Sdteske
383263791Sdteske	# Remove any leading non-digits
384263791Sdteske	__string="${__string#${__string%%[0-9]*}}"
385263791Sdteske
386263791Sdteske	# Store the numbers (no trailing suffix)
387263791Sdteske	__num="${__string%%[!0-9]*}"
388263791Sdteske
389263791Sdteske	# Produce `-1' if string didn't contain any digits
390263791Sdteske	if [ ! "$__num" ]; then
391263791Sdteske		if [ "$__var_to_set" ]; then
392263791Sdteske			setvar "$__var_to_set" -1
393263791Sdteske		else
394263791Sdteske			echo -1
395263791Sdteske		fi
396263791Sdteske		return 1 # 1 = "Given $string contains no digits"
397263791Sdteske	fi
398263791Sdteske
399263791Sdteske	# Remove all the leading numbers from the string to get at the prefix
400263791Sdteske	__string="${__string#"$__num"}"
401263791Sdteske
402263791Sdteske	#
403263791Sdteske	# Test for invalid prefix (and determine bitshift length)
404263791Sdteske	#
405263791Sdteske	case "$__string" in
406263791Sdteske	""|[[:space:]]*) # Shortcut
407263791Sdteske		if [ "$__var_to_set" ]; then
408263791Sdteske			setvar "$__var_to_set" $__num
409263791Sdteske		else
410263791Sdteske			echo $__num
411263791Sdteske		fi
412263791Sdteske		return $SUCCESS ;;
413263791Sdteske	[Kk]*) __bshift=10 ;;
414263791Sdteske	[Mm]*) __bshift=20 ;;
415263791Sdteske	[Gg]*) __bshift=30 ;;
416263791Sdteske	[Tt]*) __bshift=40 ;;
417263791Sdteske	[Pp]*) __bshift=50 ;;
418263791Sdteske	[Ee]*) __bshift=60 ;;
419263791Sdteske	*)
420263791Sdteske		# Unknown prefix
421263791Sdteske		if [ "$__var_to_set" ]; then
422263791Sdteske			setvar "$__var_to_set" -1
423263791Sdteske		else
424263791Sdteske			echo -1
425263791Sdteske		fi
426263791Sdteske		return 2 # 2 = "An unrecognized prefix was given"
427263791Sdteske	esac
428263791Sdteske
429263791Sdteske	# Determine if the wheels fall off
430263791Sdteske	__maxinput=$(( 0x7fffffffffffffff >> $__bshift ))
431263791Sdteske	if [ $__num -gt $__maxinput ]; then
432263791Sdteske		# Input (before expanding) would exceed 64-bit signed int
433263791Sdteske		if [ "$__var_to_set" ]; then
434263791Sdteske			setvar "$__var_to_set" -1
435263791Sdteske		else
436263791Sdteske			echo -1
437263791Sdteske		fi
438263791Sdteske		return 3 # 3 = "Result too large to calculate"
439263791Sdteske	fi
440263791Sdteske
441263791Sdteske	# Shift the number out and produce it
442263791Sdteske	__num=$(( $__num << $__bshift ))
443263791Sdteske	if [ "$__var_to_set" ]; then
444263791Sdteske		setvar "$__var_to_set" $__num
445263791Sdteske	else
446263791Sdteske		echo $__num
447263791Sdteske	fi
448263791Sdteske}
449263791Sdteske
450252995Sdteske############################################################ MAIN
451252995Sdteske
452252995Sdteskef_dprintf "%s: Successfully loaded." strings.subr
453252995Sdteske
454252995Sdteskefi # ! $_STRINGS_SUBR
455