]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.sbin/bsdconfig/share/strings.subr
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / usr.sbin / bsdconfig / share / strings.subr
1 if [ ! "$_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 #
40 VALID_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 #
48 f_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 #
60 f_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 #
104 f_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 #
115 f_longest_line_length_awk='
116 BEGIN { 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 }
132 END { print longest }
133 '
134 f_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 #
145 f_number_of_lines_awk='
146 BEGIN { num_lines = 0 }
147 {
148         num_lines += split(" "$0, unused, /\\n/)
149 }
150 END { print num_lines }
151 '
152 f_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 #
161 f_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 #
181 f_uriencode_awk='
182 BEGIN {
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 }
196 END { print output }
197 '
198 f_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 #
213 f_uridecode_awk='
214 BEGIN { 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 '
231 f_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 #
247 f_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 #
275 f_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 #
299 f_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 #
316 f_shell_unescape()
317 {
318         local __string="$1" __var_to_set="$2"
319         f_replaceall "$__string" "'\\''" "'" "$__var_to_set"
320 }
321
322 ############################################################ MAIN
323
324 f_dprintf "%s: Successfully loaded." strings.subr
325
326 fi # ! $_STRINGS_SUBR