strings.subr revision 249751
1if [ ! "$_STRINGS_SUBR" ]; then _STRINGS_SUBR=1
2#
3# Copyright (c) 2006-2013 Devin Teske
4# All Rights Reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10#    notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12#    notice, this list of conditions and the following disclaimer in the
13#    documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, THE
17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20# DAMAGES (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25# SUCH DAMAGE.
26#
27# $FreeBSD: head/usr.sbin/bsdconfig/share/strings.subr 249751 2013-04-22 05:52:06Z dteske $
28#
29############################################################ GLOBALS
30
31#
32# Valid characters that can appear in an sh(1) variable name
33#
34# Please note that the character ranges A-Z and a-z should be avoided because
35# these can include accent characters (which are not valid in a variable name).
36# For example, A-Z matches any character that sorts after A but before Z,
37# including A and Z. Although ASCII order would make more sense, that is not
38# how it works.
39#
40VALID_VARNAME_CHARS="0-9ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"
41
42############################################################ FUNCTIONS
43
44# f_substr "$string" $start [ $length ]
45#
46# Simple wrapper to awk(1)'s `substr' function.
47#
48f_substr()
49{
50	local string="$1" start="${2:-0}" len="${3:-0}"
51	echo "$string" | awk "{ print substr(\$0, $start, $len) }"
52}
53
54# f_longest_line_length
55#
56# Simple wrapper to an awk(1) script to print the length of the longest line of
57# input (read from stdin). Supports the newline escape-sequence `\n' for
58# splitting a single line into multiple lines.
59#
60f_longest_line_length_awk='
61BEGIN { longest = 0 }
62{
63	if (split($0, lines, /\\n/) > 1)
64	{
65		for (n in lines)
66		{
67			len = length(lines[n])
68			longest = ( len > longest ? len : longest )
69		}
70	}
71	else
72	{
73		len = length($0)
74		longest = ( len > longest ? len : longest )
75	}
76}
77END { print longest }
78'
79f_longest_line_length()
80{
81	awk "$f_longest_line_length_awk"
82}
83
84# f_number_of_lines
85#
86# Simple wrapper to an awk(1) script to print the number of lines read from
87# stdin. Supports newline escape-sequence `\n' for splitting a single line into
88# multiple lines.
89#
90f_number_of_lines_awk='
91BEGIN { num_lines = 0 }
92{
93	num_lines += split(" "$0, unused, /\\n/)
94}
95END { print num_lines }
96'
97f_number_of_lines()
98{
99	awk "$f_number_of_lines_awk"
100}
101
102# f_isinteger $arg
103#
104# Returns true if argument is a positive/negative whole integer.
105#
106f_isinteger()
107{
108	local arg="$1"
109
110	# Prevent division-by-zero
111	[ "$arg" = "0" ] && return $SUCCESS
112
113	# Attempt to perform arithmetic divison (an operation which will exit
114	# with error unless arg is a valid positive/negative whole integer).
115	#
116	( : $((0/$arg)) ) > /dev/null 2>&1
117}
118
119# f_uriencode [$text]
120#
121# Encode $text for the purpose of embedding safely into a URL. Non-alphanumeric
122# characters are converted to `%XX' sequence where XX represents the hexa-
123# decimal ordinal of the non-alphanumeric character. If $text is missing, data
124# is instead read from standard input.
125#
126f_uriencode_awk='
127BEGIN {
128	output = ""
129	for (n = 0; n < 256; n++) pack[sprintf("%c", n)] = sprintf("%%%02x", n)
130}
131{
132	sline = ""
133	slen = length($0)
134	for (n = 1; n <= slen; n++) {
135		char = substr($0, n, 1)
136		if ( char !~ /^[[:alnum:]_]$/ ) char = pack[char]
137		sline = sline char
138	}
139	output = output ( output ? "%0a" : "" ) sline
140}
141END { print output }
142'
143f_uriencode()
144{
145	if [ $# -gt 0 ]; then
146		echo "$1" | awk "$f_uriencode_awk"
147	else
148		awk "$f_uriencode_awk"
149	fi
150}
151
152# f_uridecode [$text]
153#
154# Decode $text from a URI. Encoded characters are converted from their `%XX'
155# sequence into original unencoded ASCII sequences. If $text is missing, data
156# is instead read from standard input.
157#
158f_uridecode_awk='
159BEGIN { for (n = 0; n < 256; n++) chr[n] = sprintf("%c", n) }
160{
161	sline = ""
162	slen = length($0)
163	for (n = 1; n <= slen; n++)
164	{
165		seq = substr($0, n, 3)
166		if ( seq ~ /^%[[:xdigit:]][[:xdigit:]]$/ ) {
167			hex = substr(seq, 2, 2)
168			sline = sline chr[sprintf("%u", "0x"hex)]
169			n += 2
170		} else
171			sline = sline substr(seq, 1, 1)
172	}
173	print sline
174}
175'
176f_uridecode()
177{
178	if [ $# -gt 0 ]; then
179		echo "$1" | awk "$f_uridecode_awk"
180	else
181		awk "$f_uridecode_awk"
182	fi
183}
184
185# f_replaceall $string $find $replace [$var_to_set]
186#
187# Replace all occurrences of $find in $sting with $replace. If $var_to_set is
188# either missing or NULL, the variable name is produced on standard out for
189# capturing in a sub-shell (which is less recommended due to performance
190# degradation).
191#
192f_replaceall()
193{
194	local __left="" __right="$1"
195	local __find="$2" __replace="$3" __var_to_set="$4"
196	while :; do
197		case "$__right" in *$__find*)
198			__left="$__left${__right%%$__find*}$__replace"
199			__right="${__right#*$__find}"
200			continue
201		esac
202		break
203	done
204	__left="$__left${__right#*$__find}"
205	if [ "$__var_to_set" ]; then
206		setvar "$__var_to_set" "$__left"
207	else
208		echo "$__left"
209	fi
210}
211
212# f_str2varname $string [$var_to_set]
213#
214# Convert a string into a suitable value to be used as a variable name
215# by converting unsuitable characters into the underscrore [_]. If $var_to_set
216# is either missing or NULL, the variable name is produced on standard out for
217# capturing in a sub-shell (which is less recommended due to performance
218# degradation).
219#
220f_str2varname()
221{
222	local __string="$1" __var_to_set="$2"
223	f_replaceall "$__string" "[!$VALID_VARNAME_CHARS]" "_" "$__var_to_set"
224}
225
226# f_shell_escape $string [$var_to_set]
227#
228# Escape $string for shell eval statement(s) by replacing all single-quotes
229# with a special sequence that creates a compound string when interpolated
230# by eval with surrounding single-quotes.
231#
232# For example:
233#
234# 	foo="abc'123"
235# 	f_shell_escape "$foo" bar # bar=[abc'\''123]
236# 	eval echo \'$foo\' # produces abc'123
237#
238# This is helpful when processing an argument list that has to retain its
239# escaped structure for later evaluations.
240#
241# WARNING: Surrounding single-quotes are not added; this is the responsibility
242# of the code passing the escaped values to eval (which also aids readability).
243#
244f_shell_escape()
245{
246	local __string="$1" __var_to_set="$2"
247	f_replaceall "$__string" "'" "'\\''" "$__var_to_set"
248}
249
250# f_shell_unescape $string [$var_to_set]
251#
252# The antithesis of f_shell_escape(), this function takes an escaped $string
253# and expands it.
254#
255# For example:
256#
257# 	foo="abc'123"
258# 	f_shell_escape "$foo" bar # bar=[abc'\''123]
259# 	f_shell_unescape "$bar" # produces abc'123
260#
261f_shell_unescape()
262{
263	local __string="$1" __var_to_set="$2"
264	f_replaceall "$__string" "'\\''" "'" "$__var_to_set"
265}
266
267############################################################ MAIN
268
269f_dprintf "%s: Successfully loaded." strings.subr
270
271fi # ! $_STRINGS_SUBR
272