strings.subr revision 249751
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 249751 2013-04-22 05:52:06Z 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
54238438Sdteske# f_longest_line_length
55238438Sdteske#
56238438Sdteske# Simple wrapper to an awk(1) script to print the length of the longest line of
57238438Sdteske# input (read from stdin). Supports the newline escape-sequence `\n' for
58238438Sdteske# splitting a single line into multiple lines.
59238438Sdteske#
60238438Sdteskef_longest_line_length_awk='
61238438SdteskeBEGIN { longest = 0 }
62238438Sdteske{
63238438Sdteske	if (split($0, lines, /\\n/) > 1)
64238438Sdteske	{
65238438Sdteske		for (n in lines)
66238438Sdteske		{
67238438Sdteske			len = length(lines[n])
68238438Sdteske			longest = ( len > longest ? len : longest )
69238438Sdteske		}
70238438Sdteske	}
71238438Sdteske	else
72238438Sdteske	{
73238438Sdteske		len = length($0)
74238438Sdteske		longest = ( len > longest ? len : longest )
75238438Sdteske	}
76238438Sdteske}
77238438SdteskeEND { print longest }
78238438Sdteske'
79238438Sdteskef_longest_line_length()
80238438Sdteske{
81238438Sdteske	awk "$f_longest_line_length_awk"
82238438Sdteske}
83238438Sdteske
84238438Sdteske# f_number_of_lines
85238438Sdteske#
86238438Sdteske# Simple wrapper to an awk(1) script to print the number of lines read from
87238438Sdteske# stdin. Supports newline escape-sequence `\n' for splitting a single line into
88238438Sdteske# multiple lines.
89238438Sdteske#
90238438Sdteskef_number_of_lines_awk='
91238438SdteskeBEGIN { num_lines = 0 }
92238438Sdteske{
93241700Sdteske	num_lines += split(" "$0, unused, /\\n/)
94238438Sdteske}
95238438SdteskeEND { print num_lines }
96238438Sdteske'
97238438Sdteskef_number_of_lines()
98238438Sdteske{
99238438Sdteske	awk "$f_number_of_lines_awk"
100238438Sdteske}
101238438Sdteske
102238438Sdteske# f_isinteger $arg
103238438Sdteske#
104238438Sdteske# Returns true if argument is a positive/negative whole integer.
105238438Sdteske#
106238438Sdteskef_isinteger()
107238438Sdteske{
108238438Sdteske	local arg="$1"
109238438Sdteske
110238438Sdteske	# Prevent division-by-zero
111238438Sdteske	[ "$arg" = "0" ] && return $SUCCESS
112238438Sdteske
113238438Sdteske	# Attempt to perform arithmetic divison (an operation which will exit
114238438Sdteske	# with error unless arg is a valid positive/negative whole integer).
115238438Sdteske	#
116240783Sdteske	( : $((0/$arg)) ) > /dev/null 2>&1
117238438Sdteske}
118238438Sdteske
119247280Sdteske# f_uriencode [$text]
120247280Sdteske#
121247280Sdteske# Encode $text for the purpose of embedding safely into a URL. Non-alphanumeric
122247280Sdteske# characters are converted to `%XX' sequence where XX represents the hexa-
123247280Sdteske# decimal ordinal of the non-alphanumeric character. If $text is missing, data
124247280Sdteske# is instead read from standard input.
125247280Sdteske#
126247280Sdteskef_uriencode_awk='
127247280SdteskeBEGIN {
128247280Sdteske	output = ""
129247280Sdteske	for (n = 0; n < 256; n++) pack[sprintf("%c", n)] = sprintf("%%%02x", n)
130247280Sdteske}
131247280Sdteske{
132247280Sdteske	sline = ""
133247280Sdteske	slen = length($0)
134247280Sdteske	for (n = 1; n <= slen; n++) {
135247280Sdteske		char = substr($0, n, 1)
136247280Sdteske		if ( char !~ /^[[:alnum:]_]$/ ) char = pack[char]
137247280Sdteske		sline = sline char
138247280Sdteske	}
139247280Sdteske	output = output ( output ? "%0a" : "" ) sline
140247280Sdteske}
141247280SdteskeEND { print output }
142247280Sdteske'
143247280Sdteskef_uriencode()
144247280Sdteske{
145247280Sdteske	if [ $# -gt 0 ]; then
146247280Sdteske		echo "$1" | awk "$f_uriencode_awk"
147247280Sdteske	else
148247280Sdteske		awk "$f_uriencode_awk"
149247280Sdteske	fi
150247280Sdteske}
151247280Sdteske
152247280Sdteske# f_uridecode [$text]
153247280Sdteske#
154247280Sdteske# Decode $text from a URI. Encoded characters are converted from their `%XX'
155247280Sdteske# sequence into original unencoded ASCII sequences. If $text is missing, data
156247280Sdteske# is instead read from standard input.
157247280Sdteske#
158247280Sdteskef_uridecode_awk='
159247280SdteskeBEGIN { for (n = 0; n < 256; n++) chr[n] = sprintf("%c", n) }
160247280Sdteske{
161247280Sdteske	sline = ""
162247280Sdteske	slen = length($0)
163247280Sdteske	for (n = 1; n <= slen; n++)
164247280Sdteske	{
165247280Sdteske		seq = substr($0, n, 3)
166247280Sdteske		if ( seq ~ /^%[[:xdigit:]][[:xdigit:]]$/ ) {
167247280Sdteske			hex = substr(seq, 2, 2)
168247280Sdteske			sline = sline chr[sprintf("%u", "0x"hex)]
169247280Sdteske			n += 2
170247280Sdteske		} else
171247280Sdteske			sline = sline substr(seq, 1, 1)
172247280Sdteske	}
173247280Sdteske	print sline
174247280Sdteske}
175247280Sdteske'
176247280Sdteskef_uridecode()
177247280Sdteske{
178247280Sdteske	if [ $# -gt 0 ]; then
179247280Sdteske		echo "$1" | awk "$f_uridecode_awk"
180247280Sdteske	else
181247280Sdteske		awk "$f_uridecode_awk"
182247280Sdteske	fi
183247280Sdteske}
184247280Sdteske
185249751Sdteske# f_replaceall $string $find $replace [$var_to_set]
186249751Sdteske#
187249751Sdteske# Replace all occurrences of $find in $sting with $replace. If $var_to_set is
188249751Sdteske# either missing or NULL, the variable name is produced on standard out for
189249751Sdteske# capturing in a sub-shell (which is less recommended due to performance
190249751Sdteske# degradation).
191249751Sdteske#
192249751Sdteskef_replaceall()
193249751Sdteske{
194249751Sdteske	local __left="" __right="$1"
195249751Sdteske	local __find="$2" __replace="$3" __var_to_set="$4"
196249751Sdteske	while :; do
197249751Sdteske		case "$__right" in *$__find*)
198249751Sdteske			__left="$__left${__right%%$__find*}$__replace"
199249751Sdteske			__right="${__right#*$__find}"
200249751Sdteske			continue
201249751Sdteske		esac
202249751Sdteske		break
203249751Sdteske	done
204249751Sdteske	__left="$__left${__right#*$__find}"
205249751Sdteske	if [ "$__var_to_set" ]; then
206249751Sdteske		setvar "$__var_to_set" "$__left"
207249751Sdteske	else
208249751Sdteske		echo "$__left"
209249751Sdteske	fi
210249751Sdteske}
211249751Sdteske
212249751Sdteske# f_str2varname $string [$var_to_set]
213249751Sdteske#
214249751Sdteske# Convert a string into a suitable value to be used as a variable name
215249751Sdteske# by converting unsuitable characters into the underscrore [_]. If $var_to_set
216249751Sdteske# is either missing or NULL, the variable name is produced on standard out for
217249751Sdteske# capturing in a sub-shell (which is less recommended due to performance
218249751Sdteske# degradation).
219249751Sdteske#
220249751Sdteskef_str2varname()
221249751Sdteske{
222249751Sdteske	local __string="$1" __var_to_set="$2"
223249751Sdteske	f_replaceall "$__string" "[!$VALID_VARNAME_CHARS]" "_" "$__var_to_set"
224249751Sdteske}
225249751Sdteske
226249751Sdteske# f_shell_escape $string [$var_to_set]
227249751Sdteske#
228249751Sdteske# Escape $string for shell eval statement(s) by replacing all single-quotes
229249751Sdteske# with a special sequence that creates a compound string when interpolated
230249751Sdteske# by eval with surrounding single-quotes.
231249751Sdteske#
232249751Sdteske# For example:
233249751Sdteske#
234249751Sdteske# 	foo="abc'123"
235249751Sdteske# 	f_shell_escape "$foo" bar # bar=[abc'\''123]
236249751Sdteske# 	eval echo \'$foo\' # produces abc'123
237249751Sdteske#
238249751Sdteske# This is helpful when processing an argument list that has to retain its
239249751Sdteske# escaped structure for later evaluations.
240249751Sdteske#
241249751Sdteske# WARNING: Surrounding single-quotes are not added; this is the responsibility
242249751Sdteske# of the code passing the escaped values to eval (which also aids readability).
243249751Sdteske#
244249751Sdteskef_shell_escape()
245249751Sdteske{
246249751Sdteske	local __string="$1" __var_to_set="$2"
247249751Sdteske	f_replaceall "$__string" "'" "'\\''" "$__var_to_set"
248249751Sdteske}
249249751Sdteske
250249751Sdteske# f_shell_unescape $string [$var_to_set]
251249751Sdteske#
252249751Sdteske# The antithesis of f_shell_escape(), this function takes an escaped $string
253249751Sdteske# and expands it.
254249751Sdteske#
255249751Sdteske# For example:
256249751Sdteske#
257249751Sdteske# 	foo="abc'123"
258249751Sdteske# 	f_shell_escape "$foo" bar # bar=[abc'\''123]
259249751Sdteske# 	f_shell_unescape "$bar" # produces abc'123
260249751Sdteske#
261249751Sdteskef_shell_unescape()
262249751Sdteske{
263249751Sdteske	local __string="$1" __var_to_set="$2"
264249751Sdteske	f_replaceall "$__string" "'\\''" "'" "$__var_to_set"
265249751Sdteske}
266249751Sdteske
267249751Sdteske############################################################ MAIN
268249751Sdteske
269244675Sdteskef_dprintf "%s: Successfully loaded." strings.subr
270244675Sdteske
271238438Sdteskefi # ! $_STRINGS_SUBR
272