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 ############################################################ GLOBALS
32 # Valid characters that can appear in an sh(1) variable name
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
40 VALID_VARNAME_CHARS="0-9ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"
42 ############################################################ FUNCTIONS
44 # f_substr "$string" $start [ $length ]
46 # Simple wrapper to awk(1)'s `substr' function.
50 local string="$1" start="${2:-0}" len="${3:-0}"
51 echo "$string" | awk "{ print substr(\$0, $start, $len) }"
54 # f_snprintf $var_to_set $size $format ...
56 # Similar to snprintf(3), write at most $size number of bytes into $var_to_set
57 # using printf(1) syntax (`$format ...'). The value of $var_to_set is NULL
58 # unless at-least one byte is stored from the output.
62 local __var_to_set="$1" __size="$2"
63 shift 2 # var_to_set/size
64 eval "$__var_to_set"=\$\( printf \"\$@\" \| awk -v max=\"\$__size\" \''
68 print substr($0,0,(max > 0 ? len : max + len))
74 # f_vsnprintf $var_to_set $size $format $format_args
76 # Similar to vsnprintf(3), write at most $size number of bytes into $var_to_set
77 # using printf(1) syntax (`$format $format_args'). The value of $var_to_set is
78 # NULL unless at-least one byte is stored from the output.
83 # format_args="'abc 123'" # 3-spaces between abc and 123
84 # f_vsnprintf foo $limit "$format" "$format_args" # foo=[abc 1]
88 # limit=12 format="%s %s"
89 # format_args=" 'doghouse' 'foxhound' "
90 # # even more spaces added to illustrate escape-method
91 # f_vsnprintf foo $limit "$format" "$format_args" # foo=[doghouse fox]
95 # limit=13 format="%s %s"
96 # f_shell_escape arg1 'aaa"aaa' # arg1=[aaa"aaa] (no change)
97 # f_shell_escape arg2 "aaa'aaa" # arg2=[aaa'\''aaa] (escaped s-quote)
98 # format_args="'$arg1' '$arg2'" # use single-quotes to surround args
99 # f_vsnprintf foo $limit "$format" "$format_args" # foo=[aaa"aaa aaa'a]
101 # In all of the above examples, the call to f_vsnprintf() does not change. Only
102 # the contents of $limit, $format, and $format_args changes in each example.
106 eval f_snprintf \"\$1\" \"\$2\" \"\$3\" $4
109 # f_longest_line_length
111 # Simple wrapper to an awk(1) script to print the length of the longest line of
112 # input (read from stdin). Supports the newline escape-sequence `\n' for
113 # splitting a single line into multiple lines.
115 f_longest_line_length_awk='
116 BEGIN { longest = 0 }
118 if (split($0, lines, /\\n/) > 1)
122 len = length(lines[n])
123 longest = ( len > longest ? len : longest )
129 longest = ( len > longest ? len : longest )
132 END { print longest }
134 f_longest_line_length()
136 awk "$f_longest_line_length_awk"
141 # Simple wrapper to an awk(1) script to print the number of lines read from
142 # stdin. Supports newline escape-sequence `\n' for splitting a single line into
145 f_number_of_lines_awk='
146 BEGIN { num_lines = 0 }
148 num_lines += split(" "$0, unused, /\\n/)
150 END { print num_lines }
154 awk "$f_number_of_lines_awk"
159 # Returns true if argument is a positive/negative whole integer.
165 # Prevent division-by-zero
166 [ "$arg" = "0" ] && return $SUCCESS
168 # Attempt to perform arithmetic divison (an operation which will exit
169 # with error unless arg is a valid positive/negative whole integer).
171 ( : $((0/$arg)) ) > /dev/null 2>&1
174 # f_uriencode [$text]
176 # Encode $text for the purpose of embedding safely into a URL. Non-alphanumeric
177 # characters are converted to `%XX' sequence where XX represents the hexa-
178 # decimal ordinal of the non-alphanumeric character. If $text is missing, data
179 # is instead read from standard input.
184 for (n = 0; n < 256; n++) pack[sprintf("%c", n)] = sprintf("%%%02x", n)
189 for (n = 1; n <= slen; n++) {
190 char = substr($0, n, 1)
191 if ( char !~ /^[[:alnum:]_]$/ ) char = pack[char]
194 output = output ( output ? "%0a" : "" ) sline
200 if [ $# -gt 0 ]; then
201 echo "$1" | awk "$f_uriencode_awk"
203 awk "$f_uriencode_awk"
207 # f_uridecode [$text]
209 # Decode $text from a URI. Encoded characters are converted from their `%XX'
210 # sequence into original unencoded ASCII sequences. If $text is missing, data
211 # is instead read from standard input.
214 BEGIN { for (n = 0; n < 256; n++) chr[n] = sprintf("%c", n) }
218 for (n = 1; n <= slen; n++)
220 seq = substr($0, n, 3)
221 if ( seq ~ /^%[[:xdigit:]][[:xdigit:]]$/ ) {
222 hex = substr(seq, 2, 2)
223 sline = sline chr[sprintf("%u", "0x"hex)]
226 sline = sline substr(seq, 1, 1)
233 if [ $# -gt 0 ]; then
234 echo "$1" | awk "$f_uridecode_awk"
236 awk "$f_uridecode_awk"
240 # f_replaceall $string $find $replace [$var_to_set]
242 # Replace all occurrences of $find in $string with $replace. If $var_to_set is
243 # either missing or NULL, the variable name is produced on standard out for
244 # capturing in a sub-shell (which is less recommended due to performance
249 local __left="" __right="$1"
250 local __find="$2" __replace="$3" __var_to_set="$4"
252 case "$__right" in *$__find*)
253 __left="$__left${__right%%$__find*}$__replace"
254 __right="${__right#*$__find}"
259 __left="$__left${__right#*$__find}"
260 if [ "$__var_to_set" ]; then
261 setvar "$__var_to_set" "$__left"
267 # f_str2varname $string [$var_to_set]
269 # Convert a string into a suitable value to be used as a variable name
270 # by converting unsuitable characters into the underscrore [_]. If $var_to_set
271 # is either missing or NULL, the variable name is produced on standard out for
272 # capturing in a sub-shell (which is less recommended due to performance
277 local __string="$1" __var_to_set="$2"
278 f_replaceall "$__string" "[!$VALID_VARNAME_CHARS]" "_" "$__var_to_set"
281 # f_shell_escape $string [$var_to_set]
283 # Escape $string for shell eval statement(s) by replacing all single-quotes
284 # with a special sequence that creates a compound string when interpolated
285 # by eval with surrounding single-quotes.
290 # f_shell_escape "$foo" bar # bar=[abc'\''123]
291 # eval echo \'$bar\' # produces abc'123
293 # This is helpful when processing an argument list that has to retain its
294 # escaped structure for later evaluations.
296 # WARNING: Surrounding single-quotes are not added; this is the responsibility
297 # of the code passing the escaped values to eval (which also aids readability).
301 local __string="$1" __var_to_set="$2"
302 f_replaceall "$__string" "'" "'\\''" "$__var_to_set"
305 # f_shell_unescape $string [$var_to_set]
307 # The antithesis of f_shell_escape(), this function takes an escaped $string
313 # f_shell_escape "$foo" bar # bar=[abc'\''123]
314 # f_shell_unescape "$bar" # produces abc'123
318 local __string="$1" __var_to_set="$2"
319 f_replaceall "$__string" "'\\''" "'" "$__var_to_set"
322 ############################################################ MAIN
324 f_dprintf "%s: Successfully loaded." strings.subr
326 fi # ! $_STRINGS_SUBR