diff options
Diffstat (limited to 'usr.sbin/bsdconfig/share/strings.subr')
-rw-r--r-- | usr.sbin/bsdconfig/share/strings.subr | 454 |
1 files changed, 454 insertions, 0 deletions
diff --git a/usr.sbin/bsdconfig/share/strings.subr b/usr.sbin/bsdconfig/share/strings.subr new file mode 100644 index 0000000..487e061 --- /dev/null +++ b/usr.sbin/bsdconfig/share/strings.subr @@ -0,0 +1,454 @@ +if [ ! "$_STRINGS_SUBR" ]; then _STRINGS_SUBR=1 +# +# Copyright (c) 2006-2013 Devin Teske +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ +# +############################################################ INCLUDES + +BSDCFG_SHARE="/usr/share/bsdconfig" +. $BSDCFG_SHARE/common.subr || exit 1 + +############################################################ GLOBALS + +# +# A Literal newline (for use with f_replace_all(), or IFS, or whatever) +# +NL=" +" # END-QUOTE + +# +# Valid characters that can appear in an sh(1) variable name +# +# Please note that the character ranges A-Z and a-z should be avoided because +# these can include accent characters (which are not valid in a variable name). +# For example, A-Z matches any character that sorts after A but before Z, +# including A and Z. Although ASCII order would make more sense, that is not +# how it works. +# +VALID_VARNAME_CHARS="0-9ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_" + +############################################################ FUNCTIONS + +# f_substr "$string" $start [$length] +# +# Simple wrapper to awk(1)'s `substr' function. +# +f_substr() +{ + local string="$1" start="${2:-0}" len="${3:-0}" + echo "$string" | awk "{ print substr(\$0, $start, $len) }" +} + +# f_snprintf $var_to_set $size $format [$arguments ...] +# +# Similar to snprintf(3), write at most $size number of bytes into $var_to_set +# using printf(1) syntax (`$format [$arguments ...]'). The value of $var_to_set +# is NULL unless at-least one byte is stored from the output. +# +f_snprintf() +{ + local __var_to_set="$1" __size="$2" + shift 2 # var_to_set size + eval "$__var_to_set"=\$\( printf -- \"\$@\" \| \ + awk -v max=\"\$__size\" \'' + { + len = length($0) + max -= len + print substr($0,0,(max > 0 ? len : max + len)) + if ( max < 0 ) exit + max-- + }'\' \) +} + +# f_sprintf $var_to_set $format [$arguments ...] +# +# Similar to sprintf(3), write a string into $var_to_set using printf(1) syntax +# (`$format [$arguments ...]'). +# +f_sprintf() +{ + local __var_to_set="$1" + shift 1 # var_to_set + eval "$__var_to_set"=\$\( printf -- \"\$@\" \) +} + +# f_vsnprintf $var_to_set $size $format $format_args +# +# Similar to vsnprintf(3), write at most $size number of bytes into $var_to_set +# using printf(1) syntax (`$format $format_args'). The value of $var_to_set is +# NULL unless at-least one byte is stored from the output. +# +# Example 1: +# +# limit=7 format="%s" +# format_args="'abc 123'" # 3-spaces between abc and 123 +# f_vsnprintf foo $limit "$format" "$format_args" # foo=[abc 1] +# +# Example 2: +# +# limit=12 format="%s %s" +# format_args=" 'doghouse' 'foxhound' " +# # even more spaces added to illustrate escape-method +# f_vsnprintf foo $limit "$format" "$format_args" # foo=[doghouse fox] +# +# Example 3: +# +# limit=13 format="%s %s" +# f_shell_escape arg1 'aaa"aaa' # arg1=[aaa"aaa] (no change) +# f_shell_escape arg2 "aaa'aaa" # arg2=[aaa'\''aaa] (escaped s-quote) +# format_args="'$arg1' '$arg2'" # use single-quotes to surround args +# f_vsnprintf foo $limit "$format" "$format_args" # foo=[aaa"aaa aaa'a] +# +# In all of the above examples, the call to f_vsnprintf() does not change. Only +# the contents of $limit, $format, and $format_args changes in each example. +# +f_vsnprintf() +{ + eval f_snprintf \"\$1\" \"\$2\" \"\$3\" $4 +} + +# f_vsprintf $var_to_set $format $format_args +# +# Similar to vsprintf(3), write a string into $var_to_set using printf(1) +# syntax (`$format $format_args'). +# +f_vsprintf() +{ + eval f_sprintf \"\$1\" \"\$2\" $3 +} + +# f_longest_line_length +# +# Simple wrapper to an awk(1) script to print the length of the longest line of +# input (read from stdin). Supports the newline escape-sequence `\n' for +# splitting a single line into multiple lines. +# +f_longest_line_length_awk=' +BEGIN { longest = 0 } +{ + if (split($0, lines, /\\n/) > 1) + { + for (n in lines) + { + len = length(lines[n]) + longest = ( len > longest ? len : longest ) + } + } + else + { + len = length($0) + longest = ( len > longest ? len : longest ) + } +} +END { print longest } +' +f_longest_line_length() +{ + awk "$f_longest_line_length_awk" +} + +# f_number_of_lines +# +# Simple wrapper to an awk(1) script to print the number of lines read from +# stdin. Supports newline escape-sequence `\n' for splitting a single line into +# multiple lines. +# +f_number_of_lines_awk=' +BEGIN { num_lines = 0 } +{ + num_lines += split(" "$0, unused, /\\n/) +} +END { print num_lines } +' +f_number_of_lines() +{ + awk "$f_number_of_lines_awk" +} + +# f_isinteger $arg +# +# Returns true if argument is a positive/negative whole integer. +# +f_isinteger() +{ + local arg="${1#-}" + [ "${arg:-x}" = "${arg%[!0-9]*}" ] +} + +# f_uriencode [$text] +# +# Encode $text for the purpose of embedding safely into a URL. Non-alphanumeric +# characters are converted to `%XX' sequence where XX represents the hexa- +# decimal ordinal of the non-alphanumeric character. If $text is missing, data +# is instead read from standard input. +# +f_uriencode_awk=' +BEGIN { + output = "" + for (n = 0; n < 256; n++) pack[sprintf("%c", n)] = sprintf("%%%02x", n) +} +{ + sline = "" + slen = length($0) + for (n = 1; n <= slen; n++) { + char = substr($0, n, 1) + if ( char !~ /^[[:alnum:]_]$/ ) char = pack[char] + sline = sline char + } + output = output ( output ? "%0a" : "" ) sline +} +END { print output } +' +f_uriencode() +{ + if [ $# -gt 0 ]; then + echo "$1" | awk "$f_uriencode_awk" + else + awk "$f_uriencode_awk" + fi +} + +# f_uridecode [$text] +# +# Decode $text from a URI. Encoded characters are converted from their `%XX' +# sequence into original unencoded ASCII sequences. If $text is missing, data +# is instead read from standard input. +# +f_uridecode_awk=' +BEGIN { for (n = 0; n < 256; n++) chr[n] = sprintf("%c", n) } +{ + sline = "" + slen = length($0) + for (n = 1; n <= slen; n++) + { + seq = substr($0, n, 3) + if ( seq ~ /^%[[:xdigit:]][[:xdigit:]]$/ ) { + hex = substr(seq, 2, 2) + sline = sline chr[sprintf("%u", "0x"hex)] + n += 2 + } else + sline = sline substr(seq, 1, 1) + } + print sline +} +' +f_uridecode() +{ + if [ $# -gt 0 ]; then + echo "$1" | awk "$f_uridecode_awk" + else + awk "$f_uridecode_awk" + fi +} + +# f_replaceall $string $find $replace [$var_to_set] +# +# Replace all occurrences of $find in $string with $replace. If $var_to_set is +# either missing or NULL, the variable name is produced on standard out for +# capturing in a sub-shell (which is less recommended due to performance +# degradation). +# +# To replace newlines or a sequence containing the newline character, use $NL +# as `\n' is not supported. +# +f_replaceall() +{ + local __left="" __right="$1" + local __find="$2" __replace="$3" __var_to_set="$4" + while :; do + case "$__right" in *$__find*) + __left="$__left${__right%%$__find*}$__replace" + __right="${__right#*$__find}" + continue + esac + break + done + __left="$__left${__right#*$__find}" + if [ "$__var_to_set" ]; then + setvar "$__var_to_set" "$__left" + else + echo "$__left" + fi +} + +# f_str2varname $string [$var_to_set] +# +# Convert a string into a suitable value to be used as a variable name +# by converting unsuitable characters into the underscrore [_]. If $var_to_set +# is either missing or NULL, the variable name is produced on standard out for +# capturing in a sub-shell (which is less recommended due to performance +# degradation). +# +f_str2varname() +{ + local __string="$1" __var_to_set="$2" + f_replaceall "$__string" "[!$VALID_VARNAME_CHARS]" "_" "$__var_to_set" +} + +# f_shell_escape $string [$var_to_set] +# +# Escape $string for shell eval statement(s) by replacing all single-quotes +# with a special sequence that creates a compound string when interpolated +# by eval with surrounding single-quotes. +# +# For example: +# +# foo="abc'123" +# f_shell_escape "$foo" bar # bar=[abc'\''123] +# eval echo \'$bar\' # produces abc'123 +# +# This is helpful when processing an argument list that has to retain its +# escaped structure for later evaluations. +# +# WARNING: Surrounding single-quotes are not added; this is the responsibility +# of the code passing the escaped values to eval (which also aids readability). +# +f_shell_escape() +{ + local __string="$1" __var_to_set="$2" + f_replaceall "$__string" "'" "'\\''" "$__var_to_set" +} + +# f_shell_unescape $string [$var_to_set] +# +# The antithesis of f_shell_escape(), this function takes an escaped $string +# and expands it. +# +# For example: +# +# foo="abc'123" +# f_shell_escape "$foo" bar # bar=[abc'\''123] +# f_shell_unescape "$bar" # produces abc'123 +# +f_shell_unescape() +{ + local __string="$1" __var_to_set="$2" + f_replaceall "$__string" "'\\''" "'" "$__var_to_set" +} + +# f_expand_number $string [$var_to_set] +# +# Unformat $string into a number, optionally to be stored in $var_to_set. This +# function follows the SI power of two convention. +# +# The prefixes are: +# +# Prefix Description Multiplier +# k kilo 1024 +# M mega 1048576 +# G giga 1073741824 +# T tera 1099511627776 +# P peta 1125899906842624 +# E exa 1152921504606846976 +# +# NOTE: Prefixes are case-insensitive. +# +# Upon successful completion, success status is returned; otherwise the number +# -1 is produced ($var_to_set set to -1 or if $var_to_set is NULL or missing) +# on standard output. In the case of failure, the error status will be one of: +# +# Status Reason +# 1 Given $string contains no digits +# 2 An unrecognized prefix was given +# 3 Result too large to calculate +# +f_expand_number() +{ + local __string="$1" __var_to_set="$2" + local __cp __num __bshift __maxinput + + # Remove any leading non-digits + __string="${__string#${__string%%[0-9]*}}" + + # Store the numbers (no trailing suffix) + __num="${__string%%[!0-9]*}" + + # Produce `-1' if string didn't contain any digits + if [ ! "$__num" ]; then + if [ "$__var_to_set" ]; then + setvar "$__var_to_set" -1 + else + echo -1 + fi + return 1 # 1 = "Given $string contains no digits" + fi + + # Remove all the leading numbers from the string to get at the prefix + __string="${__string#"$__num"}" + + # + # Test for invalid prefix (and determine bitshift length) + # + case "$__string" in + ""|[[:space:]]*) # Shortcut + if [ "$__var_to_set" ]; then + setvar "$__var_to_set" $__num + else + echo $__num + fi + return $SUCCESS ;; + [Kk]*) __bshift=10 ;; + [Mm]*) __bshift=20 ;; + [Gg]*) __bshift=30 ;; + [Tt]*) __bshift=40 ;; + [Pp]*) __bshift=50 ;; + [Ee]*) __bshift=60 ;; + *) + # Unknown prefix + if [ "$__var_to_set" ]; then + setvar "$__var_to_set" -1 + else + echo -1 + fi + return 2 # 2 = "An unrecognized prefix was given" + esac + + # Determine if the wheels fall off + __maxinput=$(( 0x7fffffffffffffff >> $__bshift )) + if [ $__num -gt $__maxinput ]; then + # Input (before expanding) would exceed 64-bit signed int + if [ "$__var_to_set" ]; then + setvar "$__var_to_set" -1 + else + echo -1 + fi + return 3 # 3 = "Result too large to calculate" + fi + + # Shift the number out and produce it + __num=$(( $__num << $__bshift )) + if [ "$__var_to_set" ]; then + setvar "$__var_to_set" $__num + else + echo $__num + fi +} + +############################################################ MAIN + +f_dprintf "%s: Successfully loaded." strings.subr + +fi # ! $_STRINGS_SUBR |