strings.subr revision 252995
1252995Sdteskeif [ ! "$_STRINGS_SUBR" ]; then _STRINGS_SUBR=1 2252995Sdteske# 3252995Sdteske# Copyright (c) 2006-2013 Devin Teske 4252995Sdteske# All rights reserved. 5252995Sdteske# 6252995Sdteske# Redistribution and use in source and binary forms, with or without 7252995Sdteske# modification, are permitted provided that the following conditions 8252995Sdteske# are met: 9252995Sdteske# 1. Redistributions of source code must retain the above copyright 10252995Sdteske# notice, this list of conditions and the following disclaimer. 11252995Sdteske# 2. Redistributions in binary form must reproduce the above copyright 12252995Sdteske# notice, this list of conditions and the following disclaimer in the 13252995Sdteske# documentation and/or other materials provided with the distribution. 14252995Sdteske# 15252995Sdteske# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16252995Sdteske# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17252995Sdteske# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18252995Sdteske# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19252995Sdteske# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20252995Sdteske# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21252995Sdteske# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22252995Sdteske# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23252995Sdteske# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24252995Sdteske# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25252995Sdteske# SUCH DAMAGE. 26252995Sdteske# 27252995Sdteske# $FreeBSD: stable/9/usr.sbin/bsdconfig/share/strings.subr 252995 2013-07-07 19:13:34Z dteske $ 28252995Sdteske# 29252995Sdteske############################################################ GLOBALS 30252995Sdteske 31252995Sdteske# 32252995Sdteske# Valid characters that can appear in an sh(1) variable name 33252995Sdteske# 34252995Sdteske# Please note that the character ranges A-Z and a-z should be avoided because 35252995Sdteske# these can include accent characters (which are not valid in a variable name). 36252995Sdteske# For example, A-Z matches any character that sorts after A but before Z, 37252995Sdteske# including A and Z. Although ASCII order would make more sense, that is not 38252995Sdteske# how it works. 39252995Sdteske# 40252995SdteskeVALID_VARNAME_CHARS="0-9ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_" 41252995Sdteske 42252995Sdteske############################################################ FUNCTIONS 43252995Sdteske 44252995Sdteske# f_substr "$string" $start [ $length ] 45252995Sdteske# 46252995Sdteske# Simple wrapper to awk(1)'s `substr' function. 47252995Sdteske# 48252995Sdteskef_substr() 49252995Sdteske{ 50252995Sdteske local string="$1" start="${2:-0}" len="${3:-0}" 51252995Sdteske echo "$string" | awk "{ print substr(\$0, $start, $len) }" 52252995Sdteske} 53252995Sdteske 54252995Sdteske# f_snprintf $var_to_set $size $format ... 55252995Sdteske# 56252995Sdteske# Similar to snprintf(3), write at most $size number of bytes into $var_to_set 57252995Sdteske# using printf(1) syntax (`$format ...'). The value of $var_to_set is NULL 58252995Sdteske# unless at-least one byte is stored from the output. 59252995Sdteske# 60252995Sdteskef_snprintf() 61252995Sdteske{ 62252995Sdteske local __var_to_set="$1" __size="$2" 63252995Sdteske shift 2 # var_to_set/size 64252995Sdteske eval "$__var_to_set"=\$\( printf \"\$@\" \| awk -v max=\"\$__size\" \'' 65252995Sdteske { 66252995Sdteske len = length($0) 67252995Sdteske max -= len 68252995Sdteske print substr($0,0,(max > 0 ? len : max + len)) 69252995Sdteske if ( max < 0 ) exit 70252995Sdteske max-- 71252995Sdteske }'\' \) 72252995Sdteske} 73252995Sdteske 74252995Sdteske# f_vsnprintf $var_to_set $size $format $format_args 75252995Sdteske# 76252995Sdteske# Similar to vsnprintf(3), write at most $size number of bytes into $var_to_set 77252995Sdteske# using printf(1) syntax (`$format $format_args'). The value of $var_to_set is 78252995Sdteske# NULL unless at-least one byte is stored from the output. 79252995Sdteske# 80252995Sdteske# Example 1: 81252995Sdteske# 82252995Sdteske# limit=7 format="%s" 83252995Sdteske# format_args="'abc 123'" # 3-spaces between abc and 123 84252995Sdteske# f_vsnprintf foo $limit "$format" "$format_args" # foo=[abc 1] 85252995Sdteske# 86252995Sdteske# Example 2: 87252995Sdteske# 88252995Sdteske# limit=12 format="%s %s" 89252995Sdteske# format_args=" 'doghouse' 'foxhound' " 90252995Sdteske# # even more spaces added to illustrate escape-method 91252995Sdteske# f_vsnprintf foo $limit "$format" "$format_args" # foo=[doghouse fox] 92252995Sdteske# 93252995Sdteske# Example 3: 94252995Sdteske# 95252995Sdteske# limit=13 format="%s %s" 96252995Sdteske# f_shell_escape arg1 'aaa"aaa' # arg1=[aaa"aaa] (no change) 97252995Sdteske# f_shell_escape arg2 "aaa'aaa" # arg2=[aaa'\''aaa] (escaped s-quote) 98252995Sdteske# format_args="'$arg1' '$arg2'" # use single-quotes to surround args 99252995Sdteske# f_vsnprintf foo $limit "$format" "$format_args" # foo=[aaa"aaa aaa'a] 100252995Sdteske# 101252995Sdteske# In all of the above examples, the call to f_vsnprintf() does not change. Only 102252995Sdteske# the contents of $limit, $format, and $format_args changes in each example. 103252995Sdteske# 104252995Sdteskef_vsnprintf() 105252995Sdteske{ 106252995Sdteske eval f_snprintf \"\$1\" \"\$2\" \"\$3\" $4 107252995Sdteske} 108252995Sdteske 109252995Sdteske# f_longest_line_length 110252995Sdteske# 111252995Sdteske# Simple wrapper to an awk(1) script to print the length of the longest line of 112252995Sdteske# input (read from stdin). Supports the newline escape-sequence `\n' for 113252995Sdteske# splitting a single line into multiple lines. 114252995Sdteske# 115252995Sdteskef_longest_line_length_awk=' 116252995SdteskeBEGIN { longest = 0 } 117252995Sdteske{ 118252995Sdteske if (split($0, lines, /\\n/) > 1) 119252995Sdteske { 120252995Sdteske for (n in lines) 121252995Sdteske { 122252995Sdteske len = length(lines[n]) 123252995Sdteske longest = ( len > longest ? len : longest ) 124252995Sdteske } 125252995Sdteske } 126252995Sdteske else 127252995Sdteske { 128252995Sdteske len = length($0) 129252995Sdteske longest = ( len > longest ? len : longest ) 130252995Sdteske } 131252995Sdteske} 132252995SdteskeEND { print longest } 133252995Sdteske' 134252995Sdteskef_longest_line_length() 135252995Sdteske{ 136252995Sdteske awk "$f_longest_line_length_awk" 137252995Sdteske} 138252995Sdteske 139252995Sdteske# f_number_of_lines 140252995Sdteske# 141252995Sdteske# Simple wrapper to an awk(1) script to print the number of lines read from 142252995Sdteske# stdin. Supports newline escape-sequence `\n' for splitting a single line into 143252995Sdteske# multiple lines. 144252995Sdteske# 145252995Sdteskef_number_of_lines_awk=' 146252995SdteskeBEGIN { num_lines = 0 } 147252995Sdteske{ 148252995Sdteske num_lines += split(" "$0, unused, /\\n/) 149252995Sdteske} 150252995SdteskeEND { print num_lines } 151252995Sdteske' 152252995Sdteskef_number_of_lines() 153252995Sdteske{ 154252995Sdteske awk "$f_number_of_lines_awk" 155252995Sdteske} 156252995Sdteske 157252995Sdteske# f_isinteger $arg 158252995Sdteske# 159252995Sdteske# Returns true if argument is a positive/negative whole integer. 160252995Sdteske# 161252995Sdteskef_isinteger() 162252995Sdteske{ 163252995Sdteske local arg="$1" 164252995Sdteske 165252995Sdteske # Prevent division-by-zero 166252995Sdteske [ "$arg" = "0" ] && return $SUCCESS 167252995Sdteske 168252995Sdteske # Attempt to perform arithmetic divison (an operation which will exit 169252995Sdteske # with error unless arg is a valid positive/negative whole integer). 170252995Sdteske # 171252995Sdteske ( : $((0/$arg)) ) > /dev/null 2>&1 172252995Sdteske} 173252995Sdteske 174252995Sdteske# f_uriencode [$text] 175252995Sdteske# 176252995Sdteske# Encode $text for the purpose of embedding safely into a URL. Non-alphanumeric 177252995Sdteske# characters are converted to `%XX' sequence where XX represents the hexa- 178252995Sdteske# decimal ordinal of the non-alphanumeric character. If $text is missing, data 179252995Sdteske# is instead read from standard input. 180252995Sdteske# 181252995Sdteskef_uriencode_awk=' 182252995SdteskeBEGIN { 183252995Sdteske output = "" 184252995Sdteske for (n = 0; n < 256; n++) pack[sprintf("%c", n)] = sprintf("%%%02x", n) 185252995Sdteske} 186252995Sdteske{ 187252995Sdteske sline = "" 188252995Sdteske slen = length($0) 189252995Sdteske for (n = 1; n <= slen; n++) { 190252995Sdteske char = substr($0, n, 1) 191252995Sdteske if ( char !~ /^[[:alnum:]_]$/ ) char = pack[char] 192252995Sdteske sline = sline char 193252995Sdteske } 194252995Sdteske output = output ( output ? "%0a" : "" ) sline 195252995Sdteske} 196252995SdteskeEND { print output } 197252995Sdteske' 198252995Sdteskef_uriencode() 199252995Sdteske{ 200252995Sdteske if [ $# -gt 0 ]; then 201252995Sdteske echo "$1" | awk "$f_uriencode_awk" 202252995Sdteske else 203252995Sdteske awk "$f_uriencode_awk" 204252995Sdteske fi 205252995Sdteske} 206252995Sdteske 207252995Sdteske# f_uridecode [$text] 208252995Sdteske# 209252995Sdteske# Decode $text from a URI. Encoded characters are converted from their `%XX' 210252995Sdteske# sequence into original unencoded ASCII sequences. If $text is missing, data 211252995Sdteske# is instead read from standard input. 212252995Sdteske# 213252995Sdteskef_uridecode_awk=' 214252995SdteskeBEGIN { for (n = 0; n < 256; n++) chr[n] = sprintf("%c", n) } 215252995Sdteske{ 216252995Sdteske sline = "" 217252995Sdteske slen = length($0) 218252995Sdteske for (n = 1; n <= slen; n++) 219252995Sdteske { 220252995Sdteske seq = substr($0, n, 3) 221252995Sdteske if ( seq ~ /^%[[:xdigit:]][[:xdigit:]]$/ ) { 222252995Sdteske hex = substr(seq, 2, 2) 223252995Sdteske sline = sline chr[sprintf("%u", "0x"hex)] 224252995Sdteske n += 2 225252995Sdteske } else 226252995Sdteske sline = sline substr(seq, 1, 1) 227252995Sdteske } 228252995Sdteske print sline 229252995Sdteske} 230252995Sdteske' 231252995Sdteskef_uridecode() 232252995Sdteske{ 233252995Sdteske if [ $# -gt 0 ]; then 234252995Sdteske echo "$1" | awk "$f_uridecode_awk" 235252995Sdteske else 236252995Sdteske awk "$f_uridecode_awk" 237252995Sdteske fi 238252995Sdteske} 239252995Sdteske 240252995Sdteske# f_replaceall $string $find $replace [$var_to_set] 241252995Sdteske# 242252995Sdteske# Replace all occurrences of $find in $string with $replace. If $var_to_set is 243252995Sdteske# either missing or NULL, the variable name is produced on standard out for 244252995Sdteske# capturing in a sub-shell (which is less recommended due to performance 245252995Sdteske# degradation). 246252995Sdteske# 247252995Sdteskef_replaceall() 248252995Sdteske{ 249252995Sdteske local __left="" __right="$1" 250252995Sdteske local __find="$2" __replace="$3" __var_to_set="$4" 251252995Sdteske while :; do 252252995Sdteske case "$__right" in *$__find*) 253252995Sdteske __left="$__left${__right%%$__find*}$__replace" 254252995Sdteske __right="${__right#*$__find}" 255252995Sdteske continue 256252995Sdteske esac 257252995Sdteske break 258252995Sdteske done 259252995Sdteske __left="$__left${__right#*$__find}" 260252995Sdteske if [ "$__var_to_set" ]; then 261252995Sdteske setvar "$__var_to_set" "$__left" 262252995Sdteske else 263252995Sdteske echo "$__left" 264252995Sdteske fi 265252995Sdteske} 266252995Sdteske 267252995Sdteske# f_str2varname $string [$var_to_set] 268252995Sdteske# 269252995Sdteske# Convert a string into a suitable value to be used as a variable name 270252995Sdteske# by converting unsuitable characters into the underscrore [_]. If $var_to_set 271252995Sdteske# is either missing or NULL, the variable name is produced on standard out for 272252995Sdteske# capturing in a sub-shell (which is less recommended due to performance 273252995Sdteske# degradation). 274252995Sdteske# 275252995Sdteskef_str2varname() 276252995Sdteske{ 277252995Sdteske local __string="$1" __var_to_set="$2" 278252995Sdteske f_replaceall "$__string" "[!$VALID_VARNAME_CHARS]" "_" "$__var_to_set" 279252995Sdteske} 280252995Sdteske 281252995Sdteske# f_shell_escape $string [$var_to_set] 282252995Sdteske# 283252995Sdteske# Escape $string for shell eval statement(s) by replacing all single-quotes 284252995Sdteske# with a special sequence that creates a compound string when interpolated 285252995Sdteske# by eval with surrounding single-quotes. 286252995Sdteske# 287252995Sdteske# For example: 288252995Sdteske# 289252995Sdteske# foo="abc'123" 290252995Sdteske# f_shell_escape "$foo" bar # bar=[abc'\''123] 291252995Sdteske# eval echo \'$bar\' # produces abc'123 292252995Sdteske# 293252995Sdteske# This is helpful when processing an argument list that has to retain its 294252995Sdteske# escaped structure for later evaluations. 295252995Sdteske# 296252995Sdteske# WARNING: Surrounding single-quotes are not added; this is the responsibility 297252995Sdteske# of the code passing the escaped values to eval (which also aids readability). 298252995Sdteske# 299252995Sdteskef_shell_escape() 300252995Sdteske{ 301252995Sdteske local __string="$1" __var_to_set="$2" 302252995Sdteske f_replaceall "$__string" "'" "'\\''" "$__var_to_set" 303252995Sdteske} 304252995Sdteske 305252995Sdteske# f_shell_unescape $string [$var_to_set] 306252995Sdteske# 307252995Sdteske# The antithesis of f_shell_escape(), this function takes an escaped $string 308252995Sdteske# and expands it. 309252995Sdteske# 310252995Sdteske# For example: 311252995Sdteske# 312252995Sdteske# foo="abc'123" 313252995Sdteske# f_shell_escape "$foo" bar # bar=[abc'\''123] 314252995Sdteske# f_shell_unescape "$bar" # produces abc'123 315252995Sdteske# 316252995Sdteskef_shell_unescape() 317252995Sdteske{ 318252995Sdteske local __string="$1" __var_to_set="$2" 319252995Sdteske f_replaceall "$__string" "'\\''" "'" "$__var_to_set" 320252995Sdteske} 321252995Sdteske 322252995Sdteske############################################################ MAIN 323252995Sdteske 324252995Sdteskef_dprintf "%s: Successfully loaded." strings.subr 325252995Sdteske 326252995Sdteskefi # ! $_STRINGS_SUBR 327