source: trunk/FACT++/munin/ticktick.sh@ 20115

Last change on this file since 20115 was 16760, checked in by tbretz, 11 years ago
  • Property svn:executable set to *
File size: 10.4 KB
Line 
1#!/usr/bin/env bash
2
3ARGV=$@
4
5__tick_error() {
6 echo "TICKTICK PARSING ERROR: "$1
7}
8
9# This is from https://github.com/dominictarr/JSON.sh
10# See LICENSE for more info. {{{
11__tick_json_tokenize() {
12 local ESCAPE='(\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})'
13 local CHAR='[^[:cntrl:]"\\]'
14 local STRING="\"$CHAR*($ESCAPE$CHAR*)*\""
15 local VARIABLE="\\\$[A-Za-z0-9_]*"
16 local NUMBER='-?(0|[1-9][0-9]*)([.][0-9]*)?([eE][+-]?[0-9]*)?'
17 local KEYWORD='null|false|true'
18 local SPACE='[[:space:]]+'
19 egrep -ao "$STRING|$VARIABLE|$NUMBER|$KEYWORD|$SPACE|." --color=never | egrep -v "^$SPACE$" # eat whitespace
20}
21
22__tick_json_parse_array() {
23 local index=0
24 local ary=''
25
26 read -r Token
27
28 case "$Token" in
29 ']') ;;
30 *)
31 while :
32 do
33 __tick_json_parse_value "$1" "`printf "%012d" $index`"
34
35 (( index++ ))
36 ary+="$Value"
37
38 read -r Token
39 case "$Token" in
40 ']') break ;;
41 ',') ary+=_ ;;
42 *)
43 __tick_error "Array syntax malformed"
44 break ;;
45 esac
46 read -r Token
47 done
48 ;;
49 esac
50}
51
52__tick_json_parse_object() {
53 local key
54 local obj=''
55 read -r Token
56
57 case "$Token" in
58 '}') ;;
59 *)
60 while :
61 do
62 # The key, it should be valid
63 case "$Token" in
64 '"'*'"'|\$[A-Za-z0-9_]*) key=$Token ;;
65 # If we get here then we aren't on a valid key
66 *)
67 __tick_error "Object without a Key"
68 break
69 ;;
70 esac
71
72 # A colon
73 read -r Token
74
75 # The value
76 read -r Token
77 __tick_json_parse_value "$1" "$key"
78 obj+="$key:$Value"
79
80 read -r Token
81 case "$Token" in
82 '}') break ;;
83 ',') obj+=_ ;;
84 esac
85 read -r Token
86 done
87 ;;
88 esac
89}
90
91__tick_json_parse_value() {
92 local jpath="${1:+$1_}$2"
93 local prej=${jpath//\"/}
94
95 [ "$prej" ] && prej="_$prej"
96 [ "$prej" ] && prej=${prej/-/__hyphen__}
97
98 case "$Token" in
99 '{') __tick_json_parse_object "$jpath" ;;
100 '[') __tick_json_parse_array "$jpath" ;;
101
102 *)
103 Value=$Token
104 Path="$Prefix$prej"
105 Path=${Path/#_/}
106 echo __tick_data_${Path// /}=$Value
107 ;;
108 esac
109}
110
111__tick_json_parse() {
112 read -r Token
113 __tick_json_parse_value
114 read -r Token
115}
116# }}} End of code from github
117
118# Since the JSON parser is just json parser, and we have a runtime
119# and assignments built on to this, along with javascript like referencing
120# there is a two-pass system, just because it was easier to code.
121#
122# This one separates out the valid JSON from the runtime library support
123# and little fo' language that this is coded in.
124__tick_fun_tokenize_expression() {
125 CHAR='[0-9]*[A-Za-z_$\\][0-9]*'
126 FUNCTION="(push|pop|shift|items|delete|length)[[:space:]]*\("
127 NUMBER='[0-9]*'
128 STRING="$CHAR*($CHAR*)*"
129 PAREN="[()]"
130 QUOTE="[\"\']"
131 SPACE='[[:space:]]+'
132 egrep -ao "$FUNCTION|$STRING|$QUOTE|$PAREN|$NUMBER|$SPACE|." --color=never |\
133 sed "s/^/S/g;s/$/E/g" # Make sure spaces are respected
134}
135
136__tick_fun_parse_expression() {
137 while read -r token; do
138 token=${token/#S/}
139 token=${token/%E/}
140
141 if [ $done ]; then
142 suffix+="$token"
143 else
144 case "$token" in
145 #
146 # The ( makes sure that you can do key.push = 1, not that you would, but
147 # avoiding having reserved words lowers the barrier to entry. Try doing
148 # say function debugger() {} in javascript and then run it in firefox. That's
149 # a fun one.
150 #
151 # So, it's probably better to be as lenient as possible when dealing with
152 # syntax like this.
153 #
154 'push('|'pop('|'shift('|'items('|'delete('|'length(') function=$token ;;
155 ')')
156 function=${function/%(/}
157
158 #
159 # Since bash only returns integers, we have to have a significant hack in order
160 # to return a string and then do something to the object. Basically, everything
161 # gets slammed inline.
162 #
163 # Q: Why don't you just reserve a global and then have the subfunction assign to it?
164 #
165 # A: Because the assignment has to happen prior to the function running. There's a number
166 # of syntax tricks where you can basically emulate "pointers", but then the coder would
167 # have to know about this "pointer" idea and then deal with their variables a different
168 # way.
169 #
170 # ---------
171 #
172 # Q: Why don't you just do stuff in a sub-shell and then make sure you emit things in
173 # something like a ( ) or a ` ` block?
174 #
175 # A: Because environments get copied into the subshell and then you'd be modifying the
176 # copy, not the original data. After the subshell ended, the original environment
177 # would stay, unmodified.
178 #
179 # ---------
180 #
181 # Q: Why don't you use the file system and do some magic with subthreads or something?
182 #
183 # A: Really? This should have side-effects? In programming there is something called
184 # the principle of least astonishment. In a way, the implementation below somewhat
185 # breaks that principle. However, using a file system or doing something really
186 # funky like that, would violate that principle far more.
187 #
188 # ---------
189 #
190 # But really, I sincerely hate the current solution. If you have a better idea, please
191 # please, open a dialog with me.
192 #
193 case $function in
194 items) echo '${!__tick_data_'"$Prefix"'*}' ;;
195 delete) echo 'unset __tick_data_'${Prefix/%_/} ;;
196 pop) echo '"$( __tick_runtime_last ${!__tick_data_'"$Prefix"'*} )"; __tick_runtime_pop ${!__tick_data_'"$Prefix"'*}' ;;
197 shift) echo '`__tick_runtime_first ${!__tick_data_'"$Prefix"'*}`; __tick_runtime_shift ${!__tick_data_'"$Prefix"'*}' ;;
198 length) echo '`__tick_runtime_length ${!__tick_data_'"$Prefix"'*}`' ;;
199 *) echo "__tick_runtime_$function \"$arguments\" __tick_data_$Prefix "'${!__tick_data_'"$Prefix"'*}'
200 esac
201 unset function
202
203 return
204 ;;
205
206 [0-9]*[A-Za-z]*[0-9]*) [ -n "$function" ] && arguments+="$token" || Prefix+="$token" ;;
207
208 [0-9]*) Prefix+=`printf "%012d" $token` ;;
209 '['|.) Prefix+=_ ;;
210 '"'|"'"|']') ;;
211 =) done=1 ;;
212 # Only respect a space if its in the args.
213 ' ') [ -n "$function" ] && arguments+="$token" ;;
214 *) [ -n "$function" ] && arguments+="$token" || Prefix+="$token" ;;
215 esac
216 fi
217 done
218
219 if [ "$suffix" ]; then
220 echo "$suffix" | __tick_json_tokenize | __tick_json_parse
221 else
222 Prefix=${Prefix/-/__hyphen__}
223 echo '${__tick_data_'$Prefix'}'
224 fi
225}
226
227__tick_fun_parse_tickcount_reset() {
228 # If the tick count is 1 then the backtick we encountered was a
229 # shell code escape. These ticks need to be preserved for the script.
230 if (( ticks == 1 )); then
231 code+='`'
232 fi
233
234 # This resets the backtick counter so that `some shell code` doesn't
235 # trip up the tokenizer
236 ticks=0
237}
238
239# The purpose of this function is to separate out the Bash code from the
240# special "tick tick" code. We do this by hijacking the IFS and reading
241# in a single character at a time
242__tick_fun_parse() {
243 IFS=
244
245 # code oscillates between being bash or tick tick blocks.
246 code=''
247
248 # By using -n, we are given that a newline will be an empty token. We
249 # can certainly test for that.
250 while read -r -n 1 token; do
251 case "$token" in
252 '`')
253
254 # To make sure that we find two sequential backticks, we reset the counter
255 # if it's not a backtick.
256 if (( ++ticks == 2 )); then
257
258 # Whether we are in the stanza or not, is controlled by a different
259 # variable
260 if (( tickFlag == 1 )); then
261 tickFlag=0
262 [ "$code" ] && echo -n "`echo $code | __tick_fun_tokenize_expression | __tick_fun_parse_expression`"
263 else
264 tickFlag=1
265 echo -n "$code"
266 fi
267
268 # If we have gotten this deep, then we are toggling between backtick
269 # and bash. So se should unset the code.
270 unset code
271 fi
272 ;;
273
274 '')
275 __tick_fun_parse_tickcount_reset
276
277 # this is a newline. If we are in ticktick, then we want to consume
278 # them for the parser later on. If we are in bash, then we want to
279 # preserve them. We do this by emitting our buffer and then clearing
280 # it
281 if (( tickFlag == 0 )); then
282 echo "$code"
283 unset code
284 fi
285
286 ;;
287
288 *)
289 __tick_fun_parse_tickcount_reset
290
291 # This is a buffer of the current code, either bash or backtick
292 code+="$token"
293 ;;
294 esac
295 done
296}
297
298__tick_fun_tokenize() {
299 # This makes sure that when we rerun the code that we are
300 # interpreting, we don't try to interpret it again.
301 export __tick_var_tokenized=1
302
303 # Using bash's caller function, which is for debugging, we
304 # can find out the name of the program that called us. We
305 # then cat the calling program and push it through our parser
306 local code=$(cat `caller 1 | cut -d ' ' -f 3` | __tick_fun_parse)
307
308 # Before the execution we search to see if we emitted any parsing errors
309 hasError=`echo "$code" | grep "TICKTICK PARSING ERROR" | wc -l`
310
311 if [ $__tick_var_debug ]; then
312 printf "%s\n" "$code"
313 exit 0
314 fi
315
316 # If there are no errors, then we go ahead
317 if (( hasError == 0 )); then
318 # Take the output and then execute it
319
320 bash -c "$code" -- $ARGV
321 else
322 echo "Parsing Error Detected, see below"
323
324 # printf observes the new lines
325 printf "%s\n" "$code"
326 echo "Parsing stopped here."
327 fi
328
329 exit
330}
331
332## Runtime {
333__tick_runtime_length() { echo $#; }
334__tick_runtime_first() { echo ${!1}; }
335__tick_runtime_last() { eval 'echo $'${!#}; }
336__tick_runtime_pop() { eval unset ${!#}; }
337
338__tick_runtime_shift() {
339 local left=
340 local right=
341
342 for (( i = 1; i <= $# + 1; i++ )) ; do
343 if [ "$left" ]; then
344 eval "$left=\$$right"
345 fi
346 left=$right
347 right=${!i}
348 done
349 eval unset $left
350}
351__tick_runtime_push() {
352 local value="${1/\'/\\\'}"
353 local base=$2
354 local lastarg=${!#}
355
356 let nextval=${lastarg/$base/}+1
357 nextval=`printf "%012d" $nextval`
358
359 eval $base$nextval=\'$value\'
360}
361
362tickParse() {
363 eval `echo "$1" | __tick_json_tokenize | __tick_json_parse | tr '\n' ';'`
364}
365## } End of Runtime
366
367
368[ $__tick_var_tokenized ] || __tick_fun_tokenize
Note: See TracBrowser for help on using the repository browser.