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 (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 25# SUCH DAMAGE. 26# 27# $FreeBSD$ 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_snprintf $var_to_set $size $format ... 55# 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. 59# 60f_snprintf() 61{ 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\" \'' 65 { 66 len = length($0) 67 max -= len 68 print substr($0,0,(max > 0 ? len : max + len)) 69 if ( max < 0 ) exit 70 max-- 71 }'\' \) 72} 73 74# f_vsnprintf $var_to_set $size $format $format_args 75# 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. 79# 80# Example 1: 81# 82# limit=7 format="%s" 83# format_args="'abc 123'" # 3-spaces between abc and 123 84# f_vsnprintf foo $limit "$format" "$format_args" # foo=[abc 1] 85# 86# Example 2: 87# 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] 92# 93# Example 3: 94# 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] 100# 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. 103# 104f_vsnprintf() 105{ 106 eval f_snprintf \"\$1\" \"\$2\" \"\$3\" $4 107} 108 109# f_longest_line_length 110# 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. 114# 115f_longest_line_length_awk=' 116BEGIN { longest = 0 } 117{ 118 if (split($0, lines, /\\n/) > 1) 119 { 120 for (n in lines) 121 { 122 len = length(lines[n]) 123 longest = ( len > longest ? len : longest ) 124 } 125 } 126 else 127 { 128 len = length($0) 129 longest = ( len > longest ? len : longest ) 130 } 131} 132END { print longest } 133' 134f_longest_line_length() 135{ 136 awk "$f_longest_line_length_awk" 137} 138 139# f_number_of_lines 140# 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 143# multiple lines. 144# 145f_number_of_lines_awk=' 146BEGIN { num_lines = 0 } 147{ 148 num_lines += split(" "$0, unused, /\\n/) 149} 150END { print num_lines } 151' 152f_number_of_lines() 153{ 154 awk "$f_number_of_lines_awk" 155} 156 157# f_isinteger $arg 158# 159# Returns true if argument is a positive/negative whole integer. 160# 161f_isinteger() 162{ 163 local arg="$1" 164 165 # Prevent division-by-zero 166 [ "$arg" = "0" ] && return $SUCCESS 167 168 # Attempt to perform arithmetic divison (an operation which will exit 169 # with error unless arg is a valid positive/negative whole integer). 170 # 171 ( : $((0/$arg)) ) > /dev/null 2>&1 172} 173 174# f_uriencode [$text] 175# 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. 180# 181f_uriencode_awk=' 182BEGIN { 183 output = "" 184 for (n = 0; n < 256; n++) pack[sprintf("%c", n)] = sprintf("%%%02x", n) 185} 186{ 187 sline = "" 188 slen = length($0) 189 for (n = 1; n <= slen; n++) { 190 char = substr($0, n, 1) 191 if ( char !~ /^[[:alnum:]_]$/ ) char = pack[char] 192 sline = sline char 193 } 194 output = output ( output ? "%0a" : "" ) sline 195} 196END { print output } 197' 198f_uriencode() 199{ 200 if [ $# -gt 0 ]; then 201 echo "$1" | awk "$f_uriencode_awk" 202 else 203 awk "$f_uriencode_awk" 204 fi 205} 206 207# f_uridecode [$text] 208# 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. 212# 213f_uridecode_awk=' 214BEGIN { for (n = 0; n < 256; n++) chr[n] = sprintf("%c", n) } 215{ 216 sline = "" 217 slen = length($0) 218 for (n = 1; n <= slen; n++) 219 { 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)] 224 n += 2 225 } else 226 sline = sline substr(seq, 1, 1) 227 } 228 print sline 229} 230' 231f_uridecode() 232{ 233 if [ $# -gt 0 ]; then 234 echo "$1" | awk "$f_uridecode_awk" 235 else 236 awk "$f_uridecode_awk" 237 fi 238} 239 240# f_replaceall $string $find $replace [$var_to_set] 241# 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 245# degradation). 246# 247f_replaceall() 248{ 249 local __left="" __right="$1" 250 local __find="$2" __replace="$3" __var_to_set="$4" 251 while :; do 252 case "$__right" in *$__find*) 253 __left="$__left${__right%%$__find*}$__replace" 254 __right="${__right#*$__find}" 255 continue 256 esac 257 break 258 done 259 __left="$__left${__right#*$__find}" 260 if [ "$__var_to_set" ]; then 261 setvar "$__var_to_set" "$__left" 262 else 263 echo "$__left" 264 fi 265} 266 267# f_str2varname $string [$var_to_set] 268# 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 273# degradation). 274# 275f_str2varname() 276{ 277 local __string="$1" __var_to_set="$2" 278 f_replaceall "$__string" "[!$VALID_VARNAME_CHARS]" "_" "$__var_to_set" 279} 280 281# f_shell_escape $string [$var_to_set] 282# 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. 286# 287# For example: 288# 289# foo="abc'123" 290# f_shell_escape "$foo" bar # bar=[abc'\''123] 291# eval echo \'$bar\' # produces abc'123 292# 293# This is helpful when processing an argument list that has to retain its 294# escaped structure for later evaluations. 295# 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). 298# 299f_shell_escape() 300{ 301 local __string="$1" __var_to_set="$2" 302 f_replaceall "$__string" "'" "'\\''" "$__var_to_set" 303} 304 305# f_shell_unescape $string [$var_to_set] 306# 307# The antithesis of f_shell_escape(), this function takes an escaped $string 308# and expands it. 309# 310# For example: 311# 312# foo="abc'123" 313# f_shell_escape "$foo" bar # bar=[abc'\''123] 314# f_shell_unescape "$bar" # produces abc'123 315# 316f_shell_unescape() 317{ 318 local __string="$1" __var_to_set="$2" 319 f_replaceall "$__string" "'\\''" "'" "$__var_to_set" 320} 321 322############################################################ MAIN 323 324f_dprintf "%s: Successfully loaded." strings.subr 325 326fi # ! $_STRINGS_SUBR 327