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$ 28252995Sdteske# 29263791Sdteske############################################################ INCLUDES 30263791Sdteske 31263791SdteskeBSDCFG_SHARE="/usr/share/bsdconfig" 32263791Sdteske. $BSDCFG_SHARE/common.subr || exit 1 33263791Sdteske 34252995Sdteske############################################################ GLOBALS 35252995Sdteske 36252995Sdteske# 37263791Sdteske# A Literal newline (for use with f_replace_all(), or IFS, or whatever) 38263791Sdteske# 39263791SdteskeNL=" 40263791Sdteske" # END-QUOTE 41263791Sdteske 42263791Sdteske# 43252995Sdteske# Valid characters that can appear in an sh(1) variable name 44252995Sdteske# 45252995Sdteske# Please note that the character ranges A-Z and a-z should be avoided because 46252995Sdteske# these can include accent characters (which are not valid in a variable name). 47252995Sdteske# For example, A-Z matches any character that sorts after A but before Z, 48252995Sdteske# including A and Z. Although ASCII order would make more sense, that is not 49252995Sdteske# how it works. 50252995Sdteske# 51252995SdteskeVALID_VARNAME_CHARS="0-9ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_" 52252995Sdteske 53252995Sdteske############################################################ FUNCTIONS 54252995Sdteske 55263791Sdteske# f_substr "$string" $start [$length] 56252995Sdteske# 57252995Sdteske# Simple wrapper to awk(1)'s `substr' function. 58252995Sdteske# 59252995Sdteskef_substr() 60252995Sdteske{ 61252995Sdteske local string="$1" start="${2:-0}" len="${3:-0}" 62252995Sdteske echo "$string" | awk "{ print substr(\$0, $start, $len) }" 63252995Sdteske} 64252995Sdteske 65263791Sdteske# f_snprintf $var_to_set $size $format [$arguments ...] 66252995Sdteske# 67252995Sdteske# Similar to snprintf(3), write at most $size number of bytes into $var_to_set 68263791Sdteske# using printf(1) syntax (`$format [$arguments ...]'). The value of $var_to_set 69263791Sdteske# is NULL unless at-least one byte is stored from the output. 70252995Sdteske# 71252995Sdteskef_snprintf() 72252995Sdteske{ 73252995Sdteske local __var_to_set="$1" __size="$2" 74263791Sdteske shift 2 # var_to_set size 75263791Sdteske eval "$__var_to_set"=\$\( printf -- \"\$@\" \| \ 76263791Sdteske awk -v max=\"\$__size\" \'' 77252995Sdteske { 78252995Sdteske len = length($0) 79252995Sdteske max -= len 80252995Sdteske print substr($0,0,(max > 0 ? len : max + len)) 81252995Sdteske if ( max < 0 ) exit 82252995Sdteske max-- 83252995Sdteske }'\' \) 84252995Sdteske} 85252995Sdteske 86263791Sdteske# f_sprintf $var_to_set $format [$arguments ...] 87263791Sdteske# 88263791Sdteske# Similar to sprintf(3), write a string into $var_to_set using printf(1) syntax 89263791Sdteske# (`$format [$arguments ...]'). 90263791Sdteske# 91263791Sdteskef_sprintf() 92263791Sdteske{ 93263791Sdteske local __var_to_set="$1" 94263791Sdteske shift 1 # var_to_set 95263791Sdteske eval "$__var_to_set"=\$\( printf -- \"\$@\" \) 96263791Sdteske} 97263791Sdteske 98252995Sdteske# f_vsnprintf $var_to_set $size $format $format_args 99252995Sdteske# 100252995Sdteske# Similar to vsnprintf(3), write at most $size number of bytes into $var_to_set 101252995Sdteske# using printf(1) syntax (`$format $format_args'). The value of $var_to_set is 102252995Sdteske# NULL unless at-least one byte is stored from the output. 103252995Sdteske# 104252995Sdteske# Example 1: 105252995Sdteske# 106252995Sdteske# limit=7 format="%s" 107252995Sdteske# format_args="'abc 123'" # 3-spaces between abc and 123 108252995Sdteske# f_vsnprintf foo $limit "$format" "$format_args" # foo=[abc 1] 109252995Sdteske# 110252995Sdteske# Example 2: 111252995Sdteske# 112252995Sdteske# limit=12 format="%s %s" 113252995Sdteske# format_args=" 'doghouse' 'foxhound' " 114252995Sdteske# # even more spaces added to illustrate escape-method 115252995Sdteske# f_vsnprintf foo $limit "$format" "$format_args" # foo=[doghouse fox] 116252995Sdteske# 117252995Sdteske# Example 3: 118252995Sdteske# 119252995Sdteske# limit=13 format="%s %s" 120252995Sdteske# f_shell_escape arg1 'aaa"aaa' # arg1=[aaa"aaa] (no change) 121252995Sdteske# f_shell_escape arg2 "aaa'aaa" # arg2=[aaa'\''aaa] (escaped s-quote) 122252995Sdteske# format_args="'$arg1' '$arg2'" # use single-quotes to surround args 123252995Sdteske# f_vsnprintf foo $limit "$format" "$format_args" # foo=[aaa"aaa aaa'a] 124252995Sdteske# 125252995Sdteske# In all of the above examples, the call to f_vsnprintf() does not change. Only 126252995Sdteske# the contents of $limit, $format, and $format_args changes in each example. 127252995Sdteske# 128252995Sdteskef_vsnprintf() 129252995Sdteske{ 130252995Sdteske eval f_snprintf \"\$1\" \"\$2\" \"\$3\" $4 131252995Sdteske} 132252995Sdteske 133263791Sdteske# f_vsprintf $var_to_set $format $format_args 134263791Sdteske# 135263791Sdteske# Similar to vsprintf(3), write a string into $var_to_set using printf(1) 136263791Sdteske# syntax (`$format $format_args'). 137263791Sdteske# 138263791Sdteskef_vsprintf() 139263791Sdteske{ 140263791Sdteske eval f_sprintf \"\$1\" \"\$2\" $3 141263791Sdteske} 142263791Sdteske 143252995Sdteske# f_longest_line_length 144252995Sdteske# 145252995Sdteske# Simple wrapper to an awk(1) script to print the length of the longest line of 146252995Sdteske# input (read from stdin). Supports the newline escape-sequence `\n' for 147252995Sdteske# splitting a single line into multiple lines. 148252995Sdteske# 149252995Sdteskef_longest_line_length_awk=' 150252995SdteskeBEGIN { longest = 0 } 151252995Sdteske{ 152252995Sdteske if (split($0, lines, /\\n/) > 1) 153252995Sdteske { 154252995Sdteske for (n in lines) 155252995Sdteske { 156252995Sdteske len = length(lines[n]) 157252995Sdteske longest = ( len > longest ? len : longest ) 158252995Sdteske } 159252995Sdteske } 160252995Sdteske else 161252995Sdteske { 162252995Sdteske len = length($0) 163252995Sdteske longest = ( len > longest ? len : longest ) 164252995Sdteske } 165252995Sdteske} 166252995SdteskeEND { print longest } 167252995Sdteske' 168252995Sdteskef_longest_line_length() 169252995Sdteske{ 170252995Sdteske awk "$f_longest_line_length_awk" 171252995Sdteske} 172252995Sdteske 173252995Sdteske# f_number_of_lines 174252995Sdteske# 175252995Sdteske# Simple wrapper to an awk(1) script to print the number of lines read from 176252995Sdteske# stdin. Supports newline escape-sequence `\n' for splitting a single line into 177252995Sdteske# multiple lines. 178252995Sdteske# 179252995Sdteskef_number_of_lines_awk=' 180252995SdteskeBEGIN { num_lines = 0 } 181252995Sdteske{ 182252995Sdteske num_lines += split(" "$0, unused, /\\n/) 183252995Sdteske} 184252995SdteskeEND { print num_lines } 185252995Sdteske' 186252995Sdteskef_number_of_lines() 187252995Sdteske{ 188252995Sdteske awk "$f_number_of_lines_awk" 189252995Sdteske} 190252995Sdteske 191252995Sdteske# f_isinteger $arg 192252995Sdteske# 193252995Sdteske# Returns true if argument is a positive/negative whole integer. 194252995Sdteske# 195252995Sdteskef_isinteger() 196252995Sdteske{ 197263791Sdteske local arg="${1#-}" 198263791Sdteske [ "${arg:-x}" = "${arg%[!0-9]*}" ] 199252995Sdteske} 200252995Sdteske 201252995Sdteske# f_uriencode [$text] 202252995Sdteske# 203252995Sdteske# Encode $text for the purpose of embedding safely into a URL. Non-alphanumeric 204252995Sdteske# characters are converted to `%XX' sequence where XX represents the hexa- 205252995Sdteske# decimal ordinal of the non-alphanumeric character. If $text is missing, data 206252995Sdteske# is instead read from standard input. 207252995Sdteske# 208252995Sdteskef_uriencode_awk=' 209252995SdteskeBEGIN { 210252995Sdteske output = "" 211252995Sdteske for (n = 0; n < 256; n++) pack[sprintf("%c", n)] = sprintf("%%%02x", n) 212252995Sdteske} 213252995Sdteske{ 214252995Sdteske sline = "" 215252995Sdteske slen = length($0) 216252995Sdteske for (n = 1; n <= slen; n++) { 217252995Sdteske char = substr($0, n, 1) 218252995Sdteske if ( char !~ /^[[:alnum:]_]$/ ) char = pack[char] 219252995Sdteske sline = sline char 220252995Sdteske } 221252995Sdteske output = output ( output ? "%0a" : "" ) sline 222252995Sdteske} 223252995SdteskeEND { print output } 224252995Sdteske' 225252995Sdteskef_uriencode() 226252995Sdteske{ 227252995Sdteske if [ $# -gt 0 ]; then 228252995Sdteske echo "$1" | awk "$f_uriencode_awk" 229252995Sdteske else 230252995Sdteske awk "$f_uriencode_awk" 231252995Sdteske fi 232252995Sdteske} 233252995Sdteske 234252995Sdteske# f_uridecode [$text] 235252995Sdteske# 236252995Sdteske# Decode $text from a URI. Encoded characters are converted from their `%XX' 237252995Sdteske# sequence into original unencoded ASCII sequences. If $text is missing, data 238252995Sdteske# is instead read from standard input. 239252995Sdteske# 240252995Sdteskef_uridecode_awk=' 241252995SdteskeBEGIN { for (n = 0; n < 256; n++) chr[n] = sprintf("%c", n) } 242252995Sdteske{ 243252995Sdteske sline = "" 244252995Sdteske slen = length($0) 245252995Sdteske for (n = 1; n <= slen; n++) 246252995Sdteske { 247252995Sdteske seq = substr($0, n, 3) 248252995Sdteske if ( seq ~ /^%[[:xdigit:]][[:xdigit:]]$/ ) { 249252995Sdteske hex = substr(seq, 2, 2) 250252995Sdteske sline = sline chr[sprintf("%u", "0x"hex)] 251252995Sdteske n += 2 252252995Sdteske } else 253252995Sdteske sline = sline substr(seq, 1, 1) 254252995Sdteske } 255252995Sdteske print sline 256252995Sdteske} 257252995Sdteske' 258252995Sdteskef_uridecode() 259252995Sdteske{ 260252995Sdteske if [ $# -gt 0 ]; then 261252995Sdteske echo "$1" | awk "$f_uridecode_awk" 262252995Sdteske else 263252995Sdteske awk "$f_uridecode_awk" 264252995Sdteske fi 265252995Sdteske} 266252995Sdteske 267252995Sdteske# f_replaceall $string $find $replace [$var_to_set] 268252995Sdteske# 269252995Sdteske# Replace all occurrences of $find in $string with $replace. If $var_to_set is 270252995Sdteske# either missing or NULL, the variable name is produced on standard out for 271252995Sdteske# capturing in a sub-shell (which is less recommended due to performance 272252995Sdteske# degradation). 273252995Sdteske# 274263791Sdteske# To replace newlines or a sequence containing the newline character, use $NL 275263791Sdteske# as `\n' is not supported. 276263791Sdteske# 277252995Sdteskef_replaceall() 278252995Sdteske{ 279252995Sdteske local __left="" __right="$1" 280252995Sdteske local __find="$2" __replace="$3" __var_to_set="$4" 281252995Sdteske while :; do 282252995Sdteske case "$__right" in *$__find*) 283252995Sdteske __left="$__left${__right%%$__find*}$__replace" 284252995Sdteske __right="${__right#*$__find}" 285252995Sdteske continue 286252995Sdteske esac 287252995Sdteske break 288252995Sdteske done 289252995Sdteske __left="$__left${__right#*$__find}" 290252995Sdteske if [ "$__var_to_set" ]; then 291252995Sdteske setvar "$__var_to_set" "$__left" 292252995Sdteske else 293252995Sdteske echo "$__left" 294252995Sdteske fi 295252995Sdteske} 296252995Sdteske 297252995Sdteske# f_str2varname $string [$var_to_set] 298252995Sdteske# 299252995Sdteske# Convert a string into a suitable value to be used as a variable name 300252995Sdteske# by converting unsuitable characters into the underscrore [_]. If $var_to_set 301252995Sdteske# is either missing or NULL, the variable name is produced on standard out for 302252995Sdteske# capturing in a sub-shell (which is less recommended due to performance 303252995Sdteske# degradation). 304252995Sdteske# 305252995Sdteskef_str2varname() 306252995Sdteske{ 307252995Sdteske local __string="$1" __var_to_set="$2" 308252995Sdteske f_replaceall "$__string" "[!$VALID_VARNAME_CHARS]" "_" "$__var_to_set" 309252995Sdteske} 310252995Sdteske 311252995Sdteske# f_shell_escape $string [$var_to_set] 312252995Sdteske# 313252995Sdteske# Escape $string for shell eval statement(s) by replacing all single-quotes 314252995Sdteske# with a special sequence that creates a compound string when interpolated 315252995Sdteske# by eval with surrounding single-quotes. 316252995Sdteske# 317252995Sdteske# For example: 318252995Sdteske# 319252995Sdteske# foo="abc'123" 320252995Sdteske# f_shell_escape "$foo" bar # bar=[abc'\''123] 321252995Sdteske# eval echo \'$bar\' # produces abc'123 322252995Sdteske# 323252995Sdteske# This is helpful when processing an argument list that has to retain its 324252995Sdteske# escaped structure for later evaluations. 325252995Sdteske# 326252995Sdteske# WARNING: Surrounding single-quotes are not added; this is the responsibility 327252995Sdteske# of the code passing the escaped values to eval (which also aids readability). 328252995Sdteske# 329252995Sdteskef_shell_escape() 330252995Sdteske{ 331252995Sdteske local __string="$1" __var_to_set="$2" 332252995Sdteske f_replaceall "$__string" "'" "'\\''" "$__var_to_set" 333252995Sdteske} 334252995Sdteske 335252995Sdteske# f_shell_unescape $string [$var_to_set] 336252995Sdteske# 337252995Sdteske# The antithesis of f_shell_escape(), this function takes an escaped $string 338252995Sdteske# and expands it. 339252995Sdteske# 340252995Sdteske# For example: 341252995Sdteske# 342252995Sdteske# foo="abc'123" 343252995Sdteske# f_shell_escape "$foo" bar # bar=[abc'\''123] 344252995Sdteske# f_shell_unescape "$bar" # produces abc'123 345252995Sdteske# 346252995Sdteskef_shell_unescape() 347252995Sdteske{ 348252995Sdteske local __string="$1" __var_to_set="$2" 349252995Sdteske f_replaceall "$__string" "'\\''" "'" "$__var_to_set" 350252995Sdteske} 351252995Sdteske 352263791Sdteske# f_expand_number $string [$var_to_set] 353263791Sdteske# 354263791Sdteske# Unformat $string into a number, optionally to be stored in $var_to_set. This 355263791Sdteske# function follows the SI power of two convention. 356263791Sdteske# 357263791Sdteske# The prefixes are: 358263791Sdteske# 359263791Sdteske# Prefix Description Multiplier 360263791Sdteske# k kilo 1024 361263791Sdteske# M mega 1048576 362263791Sdteske# G giga 1073741824 363263791Sdteske# T tera 1099511627776 364263791Sdteske# P peta 1125899906842624 365263791Sdteske# E exa 1152921504606846976 366263791Sdteske# 367263791Sdteske# NOTE: Prefixes are case-insensitive. 368263791Sdteske# 369263791Sdteske# Upon successful completion, success status is returned; otherwise the number 370263791Sdteske# -1 is produced ($var_to_set set to -1 or if $var_to_set is NULL or missing) 371263791Sdteske# on standard output. In the case of failure, the error status will be one of: 372263791Sdteske# 373263791Sdteske# Status Reason 374263791Sdteske# 1 Given $string contains no digits 375263791Sdteske# 2 An unrecognized prefix was given 376263791Sdteske# 3 Result too large to calculate 377263791Sdteske# 378263791Sdteskef_expand_number() 379263791Sdteske{ 380263791Sdteske local __string="$1" __var_to_set="$2" 381263791Sdteske local __cp __num __bshift __maxinput 382263791Sdteske 383263791Sdteske # Remove any leading non-digits 384263791Sdteske __string="${__string#${__string%%[0-9]*}}" 385263791Sdteske 386263791Sdteske # Store the numbers (no trailing suffix) 387263791Sdteske __num="${__string%%[!0-9]*}" 388263791Sdteske 389263791Sdteske # Produce `-1' if string didn't contain any digits 390263791Sdteske if [ ! "$__num" ]; then 391263791Sdteske if [ "$__var_to_set" ]; then 392263791Sdteske setvar "$__var_to_set" -1 393263791Sdteske else 394263791Sdteske echo -1 395263791Sdteske fi 396263791Sdteske return 1 # 1 = "Given $string contains no digits" 397263791Sdteske fi 398263791Sdteske 399263791Sdteske # Remove all the leading numbers from the string to get at the prefix 400263791Sdteske __string="${__string#"$__num"}" 401263791Sdteske 402263791Sdteske # 403263791Sdteske # Test for invalid prefix (and determine bitshift length) 404263791Sdteske # 405263791Sdteske case "$__string" in 406263791Sdteske ""|[[:space:]]*) # Shortcut 407263791Sdteske if [ "$__var_to_set" ]; then 408263791Sdteske setvar "$__var_to_set" $__num 409263791Sdteske else 410263791Sdteske echo $__num 411263791Sdteske fi 412263791Sdteske return $SUCCESS ;; 413263791Sdteske [Kk]*) __bshift=10 ;; 414263791Sdteske [Mm]*) __bshift=20 ;; 415263791Sdteske [Gg]*) __bshift=30 ;; 416263791Sdteske [Tt]*) __bshift=40 ;; 417263791Sdteske [Pp]*) __bshift=50 ;; 418263791Sdteske [Ee]*) __bshift=60 ;; 419263791Sdteske *) 420263791Sdteske # Unknown prefix 421263791Sdteske if [ "$__var_to_set" ]; then 422263791Sdteske setvar "$__var_to_set" -1 423263791Sdteske else 424263791Sdteske echo -1 425263791Sdteske fi 426263791Sdteske return 2 # 2 = "An unrecognized prefix was given" 427263791Sdteske esac 428263791Sdteske 429263791Sdteske # Determine if the wheels fall off 430263791Sdteske __maxinput=$(( 0x7fffffffffffffff >> $__bshift )) 431263791Sdteske if [ $__num -gt $__maxinput ]; then 432263791Sdteske # Input (before expanding) would exceed 64-bit signed int 433263791Sdteske if [ "$__var_to_set" ]; then 434263791Sdteske setvar "$__var_to_set" -1 435263791Sdteske else 436263791Sdteske echo -1 437263791Sdteske fi 438263791Sdteske return 3 # 3 = "Result too large to calculate" 439263791Sdteske fi 440263791Sdteske 441263791Sdteske # Shift the number out and produce it 442263791Sdteske __num=$(( $__num << $__bshift )) 443263791Sdteske if [ "$__var_to_set" ]; then 444263791Sdteske setvar "$__var_to_set" $__num 445263791Sdteske else 446263791Sdteske echo $__num 447263791Sdteske fi 448263791Sdteske} 449263791Sdteske 450252995Sdteske############################################################ MAIN 451252995Sdteske 452252995Sdteskef_dprintf "%s: Successfully loaded." strings.subr 453252995Sdteske 454252995Sdteskefi # ! $_STRINGS_SUBR 455