strings.subr revision 251272
1238438Sdteskeif [ ! "$_STRINGS_SUBR" ]; then _STRINGS_SUBR=1
2238438Sdteske#
3247280Sdteske# Copyright (c) 2006-2013 Devin Teske
4238438Sdteske# All Rights Reserved.
5238438Sdteske#
6238438Sdteske# Redistribution and use in source and binary forms, with or without
7238438Sdteske# modification, are permitted provided that the following conditions
8238438Sdteske# are met:
9238438Sdteske# 1. Redistributions of source code must retain the above copyright
10238438Sdteske#    notice, this list of conditions and the following disclaimer.
11238438Sdteske# 2. Redistributions in binary form must reproduce the above copyright
12238438Sdteske#    notice, this list of conditions and the following disclaimer in the
13238438Sdteske#    documentation and/or other materials provided with the distribution.
14238438Sdteske#
15238438Sdteske# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16238438Sdteske# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, THE
17238438Sdteske# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18238438Sdteske# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19238438Sdteske# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20238438Sdteske# DAMAGES (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21238438Sdteske# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22238438Sdteske# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23238438Sdteske# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24238438Sdteske# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25238438Sdteske# SUCH DAMAGE.
26238438Sdteske#
27238438Sdteske# $FreeBSD: head/usr.sbin/bsdconfig/share/strings.subr 251272 2013-06-02 23:08:21Z dteske $
28249751Sdteske#
29249751Sdteske############################################################ GLOBALS
30238438Sdteske
31249751Sdteske#
32249751Sdteske# Valid characters that can appear in an sh(1) variable name
33249751Sdteske#
34249751Sdteske# Please note that the character ranges A-Z and a-z should be avoided because
35249751Sdteske# these can include accent characters (which are not valid in a variable name).
36249751Sdteske# For example, A-Z matches any character that sorts after A but before Z,
37249751Sdteske# including A and Z. Although ASCII order would make more sense, that is not
38249751Sdteske# how it works.
39249751Sdteske#
40249751SdteskeVALID_VARNAME_CHARS="0-9ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"
41249751Sdteske
42249751Sdteske############################################################ FUNCTIONS
43249751Sdteske
44238438Sdteske# f_substr "$string" $start [ $length ]
45238438Sdteske#
46238438Sdteske# Simple wrapper to awk(1)'s `substr' function.
47238438Sdteske#
48238438Sdteskef_substr()
49238438Sdteske{
50238438Sdteske	local string="$1" start="${2:-0}" len="${3:-0}"
51238438Sdteske	echo "$string" | awk "{ print substr(\$0, $start, $len) }"
52238438Sdteske}
53238438Sdteske
54250701Sdteske# f_snprintf $var_to_set $size $format ...
55250701Sdteske#
56250701Sdteske# Similar to snprintf(3), write at most $size number of bytes into $var_to_set
57250701Sdteske# using printf(1) syntax (`$format ...'). The value of $var_to_set is NULL
58250701Sdteske# unless at-least one byte is stored from the output.
59250701Sdteske#
60250701Sdteskef_snprintf()
61250701Sdteske{
62250701Sdteske	local __var_to_set="$1" __size="$2"
63250701Sdteske	shift 2 # var_to_set/size
64250701Sdteske	eval "$__var_to_set"=\$\( printf \"\$@\" \| awk -v max=\"\$__size\" \''
65250701Sdteske	{
66250701Sdteske		len = length($0)
67250701Sdteske		max -= len
68250701Sdteske		print substr($0,0,(max > 0 ? len : max + len))
69250701Sdteske		if ( max < 0 ) exit
70250701Sdteske		max--
71250701Sdteske	}'\' \)
72250701Sdteske}
73250701Sdteske
74238438Sdteske# f_longest_line_length
75238438Sdteske#
76238438Sdteske# Simple wrapper to an awk(1) script to print the length of the longest line of
77238438Sdteske# input (read from stdin). Supports the newline escape-sequence `\n' for
78238438Sdteske# splitting a single line into multiple lines.
79238438Sdteske#
80238438Sdteskef_longest_line_length_awk='
81238438SdteskeBEGIN { longest = 0 }
82238438Sdteske{
83238438Sdteske	if (split($0, lines, /\\n/) > 1)
84238438Sdteske	{
85238438Sdteske		for (n in lines)
86238438Sdteske		{
87238438Sdteske			len = length(lines[n])
88238438Sdteske			longest = ( len > longest ? len : longest )
89238438Sdteske		}
90238438Sdteske	}
91238438Sdteske	else
92238438Sdteske	{
93238438Sdteske		len = length($0)
94238438Sdteske		longest = ( len > longest ? len : longest )
95238438Sdteske	}
96238438Sdteske}
97238438SdteskeEND { print longest }
98238438Sdteske'
99238438Sdteskef_longest_line_length()
100238438Sdteske{
101238438Sdteske	awk "$f_longest_line_length_awk"
102238438Sdteske}
103238438Sdteske
104238438Sdteske# f_number_of_lines
105238438Sdteske#
106238438Sdteske# Simple wrapper to an awk(1) script to print the number of lines read from
107238438Sdteske# stdin. Supports newline escape-sequence `\n' for splitting a single line into
108238438Sdteske# multiple lines.
109238438Sdteske#
110238438Sdteskef_number_of_lines_awk='
111238438SdteskeBEGIN { num_lines = 0 }
112238438Sdteske{
113241700Sdteske	num_lines += split(" "$0, unused, /\\n/)
114238438Sdteske}
115238438SdteskeEND { print num_lines }
116238438Sdteske'
117238438Sdteskef_number_of_lines()
118238438Sdteske{
119238438Sdteske	awk "$f_number_of_lines_awk"
120238438Sdteske}
121238438Sdteske
122238438Sdteske# f_isinteger $arg
123238438Sdteske#
124238438Sdteske# Returns true if argument is a positive/negative whole integer.
125238438Sdteske#
126238438Sdteskef_isinteger()
127238438Sdteske{
128238438Sdteske	local arg="$1"
129238438Sdteske
130238438Sdteske	# Prevent division-by-zero
131238438Sdteske	[ "$arg" = "0" ] && return $SUCCESS
132238438Sdteske
133238438Sdteske	# Attempt to perform arithmetic divison (an operation which will exit
134238438Sdteske	# with error unless arg is a valid positive/negative whole integer).
135238438Sdteske	#
136240783Sdteske	( : $((0/$arg)) ) > /dev/null 2>&1
137238438Sdteske}
138238438Sdteske
139247280Sdteske# f_uriencode [$text]
140247280Sdteske#
141247280Sdteske# Encode $text for the purpose of embedding safely into a URL. Non-alphanumeric
142247280Sdteske# characters are converted to `%XX' sequence where XX represents the hexa-
143247280Sdteske# decimal ordinal of the non-alphanumeric character. If $text is missing, data
144247280Sdteske# is instead read from standard input.
145247280Sdteske#
146247280Sdteskef_uriencode_awk='
147247280SdteskeBEGIN {
148247280Sdteske	output = ""
149247280Sdteske	for (n = 0; n < 256; n++) pack[sprintf("%c", n)] = sprintf("%%%02x", n)
150247280Sdteske}
151247280Sdteske{
152247280Sdteske	sline = ""
153247280Sdteske	slen = length($0)
154247280Sdteske	for (n = 1; n <= slen; n++) {
155247280Sdteske		char = substr($0, n, 1)
156247280Sdteske		if ( char !~ /^[[:alnum:]_]$/ ) char = pack[char]
157247280Sdteske		sline = sline char
158247280Sdteske	}
159247280Sdteske	output = output ( output ? "%0a" : "" ) sline
160247280Sdteske}
161247280SdteskeEND { print output }
162247280Sdteske'
163247280Sdteskef_uriencode()
164247280Sdteske{
165247280Sdteske	if [ $# -gt 0 ]; then
166247280Sdteske		echo "$1" | awk "$f_uriencode_awk"
167247280Sdteske	else
168247280Sdteske		awk "$f_uriencode_awk"
169247280Sdteske	fi
170247280Sdteske}
171247280Sdteske
172247280Sdteske# f_uridecode [$text]
173247280Sdteske#
174247280Sdteske# Decode $text from a URI. Encoded characters are converted from their `%XX'
175247280Sdteske# sequence into original unencoded ASCII sequences. If $text is missing, data
176247280Sdteske# is instead read from standard input.
177247280Sdteske#
178247280Sdteskef_uridecode_awk='
179247280SdteskeBEGIN { for (n = 0; n < 256; n++) chr[n] = sprintf("%c", n) }
180247280Sdteske{
181247280Sdteske	sline = ""
182247280Sdteske	slen = length($0)
183247280Sdteske	for (n = 1; n <= slen; n++)
184247280Sdteske	{
185247280Sdteske		seq = substr($0, n, 3)
186247280Sdteske		if ( seq ~ /^%[[:xdigit:]][[:xdigit:]]$/ ) {
187247280Sdteske			hex = substr(seq, 2, 2)
188247280Sdteske			sline = sline chr[sprintf("%u", "0x"hex)]
189247280Sdteske			n += 2
190247280Sdteske		} else
191247280Sdteske			sline = sline substr(seq, 1, 1)
192247280Sdteske	}
193247280Sdteske	print sline
194247280Sdteske}
195247280Sdteske'
196247280Sdteskef_uridecode()
197247280Sdteske{
198247280Sdteske	if [ $# -gt 0 ]; then
199247280Sdteske		echo "$1" | awk "$f_uridecode_awk"
200247280Sdteske	else
201247280Sdteske		awk "$f_uridecode_awk"
202247280Sdteske	fi
203247280Sdteske}
204247280Sdteske
205249751Sdteske# f_replaceall $string $find $replace [$var_to_set]
206249751Sdteske#
207250702Sdteske# Replace all occurrences of $find in $string with $replace. If $var_to_set is
208249751Sdteske# either missing or NULL, the variable name is produced on standard out for
209249751Sdteske# capturing in a sub-shell (which is less recommended due to performance
210249751Sdteske# degradation).
211249751Sdteske#
212249751Sdteskef_replaceall()
213249751Sdteske{
214249751Sdteske	local __left="" __right="$1"
215249751Sdteske	local __find="$2" __replace="$3" __var_to_set="$4"
216249751Sdteske	while :; do
217249751Sdteske		case "$__right" in *$__find*)
218249751Sdteske			__left="$__left${__right%%$__find*}$__replace"
219249751Sdteske			__right="${__right#*$__find}"
220249751Sdteske			continue
221249751Sdteske		esac
222249751Sdteske		break
223249751Sdteske	done
224249751Sdteske	__left="$__left${__right#*$__find}"
225249751Sdteske	if [ "$__var_to_set" ]; then
226249751Sdteske		setvar "$__var_to_set" "$__left"
227249751Sdteske	else
228249751Sdteske		echo "$__left"
229249751Sdteske	fi
230249751Sdteske}
231249751Sdteske
232249751Sdteske# f_str2varname $string [$var_to_set]
233249751Sdteske#
234249751Sdteske# Convert a string into a suitable value to be used as a variable name
235249751Sdteske# by converting unsuitable characters into the underscrore [_]. If $var_to_set
236249751Sdteske# is either missing or NULL, the variable name is produced on standard out for
237249751Sdteske# capturing in a sub-shell (which is less recommended due to performance
238249751Sdteske# degradation).
239249751Sdteske#
240249751Sdteskef_str2varname()
241249751Sdteske{
242249751Sdteske	local __string="$1" __var_to_set="$2"
243249751Sdteske	f_replaceall "$__string" "[!$VALID_VARNAME_CHARS]" "_" "$__var_to_set"
244249751Sdteske}
245249751Sdteske
246249751Sdteske# f_shell_escape $string [$var_to_set]
247249751Sdteske#
248249751Sdteske# Escape $string for shell eval statement(s) by replacing all single-quotes
249249751Sdteske# with a special sequence that creates a compound string when interpolated
250249751Sdteske# by eval with surrounding single-quotes.
251249751Sdteske#
252249751Sdteske# For example:
253249751Sdteske#
254249751Sdteske# 	foo="abc'123"
255249751Sdteske# 	f_shell_escape "$foo" bar # bar=[abc'\''123]
256251272Sdteske# 	eval echo \'$bar\' # produces abc'123
257249751Sdteske#
258249751Sdteske# This is helpful when processing an argument list that has to retain its
259249751Sdteske# escaped structure for later evaluations.
260249751Sdteske#
261249751Sdteske# WARNING: Surrounding single-quotes are not added; this is the responsibility
262249751Sdteske# of the code passing the escaped values to eval (which also aids readability).
263249751Sdteske#
264249751Sdteskef_shell_escape()
265249751Sdteske{
266249751Sdteske	local __string="$1" __var_to_set="$2"
267249751Sdteske	f_replaceall "$__string" "'" "'\\''" "$__var_to_set"
268249751Sdteske}
269249751Sdteske
270249751Sdteske# f_shell_unescape $string [$var_to_set]
271249751Sdteske#
272249751Sdteske# The antithesis of f_shell_escape(), this function takes an escaped $string
273249751Sdteske# and expands it.
274249751Sdteske#
275249751Sdteske# For example:
276249751Sdteske#
277249751Sdteske# 	foo="abc'123"
278249751Sdteske# 	f_shell_escape "$foo" bar # bar=[abc'\''123]
279249751Sdteske# 	f_shell_unescape "$bar" # produces abc'123
280249751Sdteske#
281249751Sdteskef_shell_unescape()
282249751Sdteske{
283249751Sdteske	local __string="$1" __var_to_set="$2"
284249751Sdteske	f_replaceall "$__string" "'\\''" "'" "$__var_to_set"
285249751Sdteske}
286249751Sdteske
287249751Sdteske############################################################ MAIN
288249751Sdteske
289244675Sdteskef_dprintf "%s: Successfully loaded." strings.subr
290244675Sdteske
291238438Sdteskefi # ! $_STRINGS_SUBR
292