jshn: refactor _jshn_append to require fewer evals
[project/libubox.git] / sh / jshn.sh
1 # functions for parsing and generating json
2
3 _json_get_var() {
4         # dest=$1
5         # var=$2
6         eval "$1=\"\$${JSON_PREFIX}$2\""
7 }
8
9 _json_set_var() {
10         # var=$1
11         local ___val="$2"
12         eval "${JSON_PREFIX}$1=\"\$___val\""
13 }
14
15 __jshn_raw_append() {
16         # var=$1
17         local value="$2"
18         local sep="${3:- }"
19
20         eval "export -- \"$1=\${$1:+\${$1}\${value:+\$sep}}\$value\""
21 }
22
23 _jshn_append() {
24         # var=$1
25         local _a_value="$2"
26         local _a_sep="${3:- }"
27         eval "${JSON_PREFIX}$1=\"\${${JSON_PREFIX}$1:+\${${JSON_PREFIX}$1}\${_a_value:+\$_a_sep}}\$_a_value\""
28 }
29
30 _json_export() {
31         # var=$1
32         # value=$2
33         export -- "${JSON_PREFIX}$1=$2"
34 }
35
36 _json_add_key() {
37         # var=$1
38         # value=$2
39         _jshn_append "KEYS_$1" "$2"
40 }
41
42 _get_var() {
43         # var=$1
44         # value=$2
45         eval "$1=\"\$$2\""
46 }
47
48 _set_var() {
49         # var=$1
50         local __val="$2"
51         eval "$1=\"\$__val\""
52 }
53
54 _json_inc() {
55         # var=$1
56         # dest=$2
57         local _seq
58
59         _json_get_var _seq "$1"
60         _seq="$((${_seq:-0} + 1))"
61         _json_set_var "$1" "$_seq"
62         [ -n "$2" ] && _set_var "$2" "$_seq"
63 }
64
65 _json_add_generic() {
66         # type=$1
67         local var="$2"
68         # value=$3
69         local cur="$4"
70
71         [ -n "$cur" ] || _json_get_var cur JSON_CUR
72
73         if [ "${cur%%[0-9]*}" = "JSON_ARRAY" ]; then
74                 _json_inc "SEQ_$cur" var
75         else
76                 local name="${var//[^a-zA-Z0-9_]/_}"
77                 [[ "$name" == "$var" ]] || _json_export "NAME_${cur}_${name}" "$var"
78                 var="$name"
79         fi
80
81         local cur_var=
82         _json_export "${cur}_$var" "$3"
83         _json_export "TYPE_${cur}_$var" "$1"
84         _jshn_append "JSON_UNSET" "${cur}_$var"
85         _json_add_key "$cur" "$var"
86 }
87
88 _json_add_table() {
89         # name=$1
90         # type=$2
91         # itype=$3
92         local cur new_cur seq
93
94         _json_get_var cur JSON_CUR
95         _json_inc JSON_SEQ seq
96
97         local table="JSON_$3$seq"
98         _json_set_var "UP_$table" "$cur"
99         _json_export "KEYS_$table" ""
100         [ "$itype" = "ARRAY" ] && _json_set_var "SEQ_$table" ""
101         _json_set_var JSON_CUR "$table"
102         _jshn_append "JSON_UNSET" "$table"
103
104         _json_get_var new_cur JSON_CUR
105         _json_add_generic "$2" "$1" "$new_cur" "$cur"
106 }
107
108 _json_close_table() {
109         local _s_cur _s_new
110
111         _json_get_var _s_cur JSON_CUR
112         _json_get_var _s_new "UP_$_s_cur"
113         _json_set_var JSON_CUR "$_s_new"
114 }
115
116 json_set_namespace() {
117         local _new="$1"
118         local _old="$2"
119
120         [ -n "$_old" ] && _set_var "$_old" "$JSON_PREFIX"
121         JSON_PREFIX="$_new"
122 }
123
124 json_cleanup() {
125         local unset
126
127         _json_get_var unset JSON_UNSET
128         for tmp in $unset JSON_VAR; do
129                 unset \
130                         ${JSON_PREFIX}UP_$tmp \
131                         ${JSON_PREFIX}KEYS_$tmp \
132                         ${JSON_PREFIX}SEQ_$tmp \
133                         ${JSON_PREFIX}TYPE_$tmp \
134                         ${JSON_PREFIX}NAME_$tmp \
135                         ${JSON_PREFIX}$tmp
136         done
137
138         unset \
139                 ${JSON_PREFIX}JSON_SEQ \
140                 ${JSON_PREFIX}JSON_CUR \
141                 ${JSON_PREFIX}JSON_UNSET
142 }
143
144 json_init() {
145         json_cleanup
146         export -- \
147                 ${JSON_PREFIX}JSON_SEQ=0 \
148                 ${JSON_PREFIX}JSON_CUR="JSON_VAR" \
149                 ${JSON_PREFIX}KEYS_JSON_VAR= \
150                 ${JSON_PREFIX}TYPE_JSON_VAR=
151 }
152
153 json_add_object() {
154         _json_add_table "$1" object TABLE
155 }
156
157 json_close_object() {
158         _json_close_table
159 }
160
161 json_add_array() {
162         _json_add_table "$1" array ARRAY 
163 }
164
165 json_close_array() {
166         _json_close_table
167 }
168
169 json_add_string() {
170         _json_add_generic string "$1" "$2"
171 }
172
173 json_add_int() {
174         _json_add_generic int "$1" "$2"
175 }
176
177 json_add_boolean() {
178         _json_add_generic boolean "$1" "$2"
179 }
180
181 json_add_double() {
182         _json_add_generic double "$1" "$2"
183 }
184
185 # functions read access to json variables
186
187 json_load() {
188         eval `jshn -r "$1"`
189 }
190
191 json_dump() {
192         jshn "$@" ${JSON_PREFIX:+-p "$JSON_PREFIX"} -w 
193 }
194
195 json_get_type() {
196         local __dest="$1"
197         local __cur
198
199         _json_get_var __cur JSON_CUR
200         local __var="${JSON_PREFIX}TYPE_${__cur}_${2//[^a-zA-Z0-9_]/_}"
201         eval "export -- \"$__dest=\${$__var}\"; [ -n \"\${$__var+x}\" ]"
202 }
203
204 json_get_keys() {
205         local __dest="$1"
206         local _tbl_cur
207
208         if [ -n "$2" ]; then
209                 json_get_var _tbl_cur "$2"
210         else
211                 _json_get_var _tbl_cur JSON_CUR
212         fi
213         local __var="${JSON_PREFIX}KEYS_${_tbl_cur}"
214         eval "export -- \"$__dest=\${$__var}\"; [ -n \"\${$__var+x}\" ]"
215 }
216
217 json_get_values() {
218         local _v_dest="$1"
219         local _v_keys _v_val _select=
220
221         unset "$_v_dest"
222         [ -n "$2" ] && {
223                 json_select "$2"
224                 _select=1
225         }
226
227         json_get_keys _v_keys
228         set -- $_v_keys
229         while [ "$#" -gt 0 ]; do
230                 json_get_var _v_val "$1"
231                 __jshn_raw_append "$_v_dest" "$_v_val"
232                 shift
233         done
234         [ -n "$_select" ] && json_select ..
235
236         return 0
237 }
238
239 json_get_var() {
240         local __dest="$1"
241         local __cur
242
243         _json_get_var __cur JSON_CUR
244         local __var="${JSON_PREFIX}${__cur}_${2//[^a-zA-Z0-9_]/_}"
245         eval "export -- \"$__dest=\${$__var}\"; [ -n \"\${$__var+x}\" ]"
246 }
247
248 json_get_vars() {
249         while [ "$#" -gt 0 ]; do
250                 local _var="$1"; shift
251                 json_get_var "$_var" "$_var"
252         done
253 }
254
255 json_select() {
256         local target="$1"
257         local type
258         local cur
259
260         [ -z "$1" ] && {
261                 _json_set_var JSON_CUR "JSON_VAR"
262                 return 0
263         }
264         [[ "$1" == ".." ]] && {
265                 _json_get_var cur JSON_CUR
266                 _json_get_var cur "UP_$cur"
267                 _json_set_var JSON_CUR "$cur"
268                 return 0
269         }
270         json_get_type type "$target"
271         case "$type" in
272                 object|array)
273                         json_get_var cur "$target"
274                         _json_set_var JSON_CUR "$cur"
275                 ;;
276                 *)
277                         echo "WARNING: Variable '$target' does not exist or is not an array/object"
278                         return 1
279                 ;;
280         esac
281 }
282
283 json_is_a() {
284         local type
285
286         json_get_type type "$1"
287         [ "$type" = "$2" ]
288 }