1 if [ ! "$_STRINGS_SUBR" ]; then _STRINGS_SUBR=1
3 # Copyright (c) 2006-2013 Devin Teske
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
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.
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 (INCLUDING, 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
29 ############################################################ INCLUDES
31 BSDCFG_SHARE="/usr/share/bsdconfig"
32 . $BSDCFG_SHARE/common.subr || exit 1
34 ############################################################ GLOBALS
37 # Valid characters that can appear in an sh(1) variable name
39 # Please note that the character ranges A-Z and a-z should be avoided because
40 # these can include accent characters (which are not valid in a variable name).
41 # For example, A-Z matches any character that sorts after A but before Z,
42 # including A and Z. Although ASCII order would make more sense, that is not
45 VALID_VARNAME_CHARS="0-9ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"
47 ############################################################ FUNCTIONS
49 # f_substr "$string" $start [ $length ]
51 # Simple wrapper to awk(1)'s `substr' function.
55 local string="$1" start="${2:-0}" len="${3:-0}"
56 echo "$string" | awk "{ print substr(\$0, $start, $len) }"
59 # f_snprintf $var_to_set $size $format ...
61 # Similar to snprintf(3), write at most $size number of bytes into $var_to_set
62 # using printf(1) syntax (`$format ...'). The value of $var_to_set is NULL
63 # unless at-least one byte is stored from the output.
67 local __var_to_set="$1" __size="$2"
68 shift 2 # var_to_set/size
69 eval "$__var_to_set"=\$\( printf \"\$@\" \| awk -v max=\"\$__size\" \''
73 print substr($0,0,(max > 0 ? len : max + len))
79 # f_vsnprintf $var_to_set $size $format $format_args
81 # Similar to vsnprintf(3), write at most $size number of bytes into $var_to_set
82 # using printf(1) syntax (`$format $format_args'). The value of $var_to_set is
83 # NULL unless at-least one byte is stored from the output.
88 # format_args="'abc 123'" # 3-spaces between abc and 123
89 # f_vsnprintf foo $limit "$format" "$format_args" # foo=[abc 1]
93 # limit=12 format="%s %s"
94 # format_args=" 'doghouse' 'foxhound' "
95 # # even more spaces added to illustrate escape-method
96 # f_vsnprintf foo $limit "$format" "$format_args" # foo=[doghouse fox]
100 # limit=13 format="%s %s"
101 # f_shell_escape arg1 'aaa"aaa' # arg1=[aaa"aaa] (no change)
102 # f_shell_escape arg2 "aaa'aaa" # arg2=[aaa'\''aaa] (escaped s-quote)
103 # format_args="'$arg1' '$arg2'" # use single-quotes to surround args
104 # f_vsnprintf foo $limit "$format" "$format_args" # foo=[aaa"aaa aaa'a]
106 # In all of the above examples, the call to f_vsnprintf() does not change. Only
107 # the contents of $limit, $format, and $format_args changes in each example.
111 eval f_snprintf \"\$1\" \"\$2\" \"\$3\" $4
114 # f_longest_line_length
116 # Simple wrapper to an awk(1) script to print the length of the longest line of
117 # input (read from stdin). Supports the newline escape-sequence `\n' for
118 # splitting a single line into multiple lines.
120 f_longest_line_length_awk='
121 BEGIN { longest = 0 }
123 if (split($0, lines, /\\n/) > 1)
127 len = length(lines[n])
128 longest = ( len > longest ? len : longest )
134 longest = ( len > longest ? len : longest )
137 END { print longest }
139 f_longest_line_length()
141 awk "$f_longest_line_length_awk"
146 # Simple wrapper to an awk(1) script to print the number of lines read from
147 # stdin. Supports newline escape-sequence `\n' for splitting a single line into
150 f_number_of_lines_awk='
151 BEGIN { num_lines = 0 }
153 num_lines += split(" "$0, unused, /\\n/)
155 END { print num_lines }
159 awk "$f_number_of_lines_awk"
164 # Returns true if argument is a positive/negative whole integer.
170 # Prevent division-by-zero
171 [ "$arg" = "0" ] && return $SUCCESS
173 # Attempt to perform arithmetic divison (an operation which will exit
174 # with error unless arg is a valid positive/negative whole integer).
176 ( : $((0/$arg)) ) > /dev/null 2>&1
179 # f_uriencode [$text]
181 # Encode $text for the purpose of embedding safely into a URL. Non-alphanumeric
182 # characters are converted to `%XX' sequence where XX represents the hexa-
183 # decimal ordinal of the non-alphanumeric character. If $text is missing, data
184 # is instead read from standard input.
189 for (n = 0; n < 256; n++) pack[sprintf("%c", n)] = sprintf("%%%02x", n)
194 for (n = 1; n <= slen; n++) {
195 char = substr($0, n, 1)
196 if ( char !~ /^[[:alnum:]_]$/ ) char = pack[char]
199 output = output ( output ? "%0a" : "" ) sline
205 if [ $# -gt 0 ]; then
206 echo "$1" | awk "$f_uriencode_awk"
208 awk "$f_uriencode_awk"
212 # f_uridecode [$text]
214 # Decode $text from a URI. Encoded characters are converted from their `%XX'
215 # sequence into original unencoded ASCII sequences. If $text is missing, data
216 # is instead read from standard input.
219 BEGIN { for (n = 0; n < 256; n++) chr[n] = sprintf("%c", n) }
223 for (n = 1; n <= slen; n++)
225 seq = substr($0, n, 3)
226 if ( seq ~ /^%[[:xdigit:]][[:xdigit:]]$/ ) {
227 hex = substr(seq, 2, 2)
228 sline = sline chr[sprintf("%u", "0x"hex)]
231 sline = sline substr(seq, 1, 1)
238 if [ $# -gt 0 ]; then
239 echo "$1" | awk "$f_uridecode_awk"
241 awk "$f_uridecode_awk"
245 # f_replaceall $string $find $replace [$var_to_set]
247 # Replace all occurrences of $find in $string with $replace. If $var_to_set is
248 # either missing or NULL, the variable name is produced on standard out for
249 # capturing in a sub-shell (which is less recommended due to performance
254 local __left="" __right="$1"
255 local __find="$2" __replace="$3" __var_to_set="$4"
257 case "$__right" in *$__find*)
258 __left="$__left${__right%%$__find*}$__replace"
259 __right="${__right#*$__find}"
264 __left="$__left${__right#*$__find}"
265 if [ "$__var_to_set" ]; then
266 setvar "$__var_to_set" "$__left"
272 # f_str2varname $string [$var_to_set]
274 # Convert a string into a suitable value to be used as a variable name
275 # by converting unsuitable characters into the underscrore [_]. If $var_to_set
276 # is either missing or NULL, the variable name is produced on standard out for
277 # capturing in a sub-shell (which is less recommended due to performance
282 local __string="$1" __var_to_set="$2"
283 f_replaceall "$__string" "[!$VALID_VARNAME_CHARS]" "_" "$__var_to_set"
286 # f_shell_escape $string [$var_to_set]
288 # Escape $string for shell eval statement(s) by replacing all single-quotes
289 # with a special sequence that creates a compound string when interpolated
290 # by eval with surrounding single-quotes.
295 # f_shell_escape "$foo" bar # bar=[abc'\''123]
296 # eval echo \'$bar\' # produces abc'123
298 # This is helpful when processing an argument list that has to retain its
299 # escaped structure for later evaluations.
301 # WARNING: Surrounding single-quotes are not added; this is the responsibility
302 # of the code passing the escaped values to eval (which also aids readability).
306 local __string="$1" __var_to_set="$2"
307 f_replaceall "$__string" "'" "'\\''" "$__var_to_set"
310 # f_shell_unescape $string [$var_to_set]
312 # The antithesis of f_shell_escape(), this function takes an escaped $string
318 # f_shell_escape "$foo" bar # bar=[abc'\''123]
319 # f_shell_unescape "$bar" # produces abc'123
323 local __string="$1" __var_to_set="$2"
324 f_replaceall "$__string" "'\\''" "'" "$__var_to_set"
327 # f_expand_number $string [$var_to_set]
329 # Unformat $string into a number, optionally to be stored in $var_to_set. This
330 # function follows the SI power of two convention.
334 # Prefix Description Multiplier
338 # T tera 1099511627776
339 # P peta 1125899906842624
340 # E exa 1152921504606846976
342 # NOTE: Prefixes are case-insensitive.
344 # Upon successful completion, the value 0 is returned (or stored to
345 # $var_to_set); otherwise -1. Reasons for a -1 return include:
347 # Given $string contains no digits.
348 # An unrecognized prefix was given.
349 # Result too large to calculate.
353 local __string="$1" __var_to_set="$2"
356 # Remove any leading non-digits
359 __string="${__cp#[!0-9]}"
360 [ "$__string" = "$__cp" ] && break
363 # Return `-1' if string didn't contain any digits
364 if [ ! "$__string" ]; then
365 if [ "$__var_to_set" ]; then
366 setvar "$__var_to_set" -1
374 __num="${__string%%[!0-9]*}"
377 if [ $__num -eq 0 ]; then
378 if [ "$__var_to_set" ]; then
379 setvar "$__var_to_set" 0
386 # Remove all the leading numbers from the string to get at the prefix
389 __string="${__cp#[0-9]}"
390 [ "$__string" = "$__cp" ] && break
393 # Test for invalid prefix
395 ""|[KkMmGgTtPpEe]*) : known prefix ;;
398 if [ "$__var_to_set" ]; then
399 setvar "$__var_to_set" -1
406 # Multiply the number out
408 [Kk]) __num=$(( $__num * 1024 )) ;;
409 [Mm]) __num=$(( $__num * 1048576 )) ;;
410 [Gg]) __num=$(( $__num * 1073741824 )) ;;
411 [Tt]) __num=$(( $__num * 1099511627776 )) ;;
412 [Pp]) __num=$(( $__num * 1125899906842624 )) ;;
413 [Ee]) __num=$(( $__num * 1152921504606846976 )) ;;
415 if [ $__num -le 0 ]; then
416 # Arithmetic overflow
417 if [ "$__var_to_set" ]; then
418 setvar "$__var_to_set" -1
426 if [ "$__var_to_set" ]; then
427 setvar "$__var_to_set" $__num
433 ############################################################ MAIN
435 f_dprintf "%s: Successfully loaded." strings.subr
437 fi # ! $_STRINGS_SUBR