luci-lib-jsonc: Add ltn12-compatible sink factory
[project/luci.git] / libs / luci-lib-jsonc / src / jsonc.c
1 /*
2 Copyright 2015 Jo-Philipp Wich <jow@openwrt.org>
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8         http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 #define _GNU_SOURCE
18
19 #include <math.h>
20 #include <stdbool.h>
21 #include <json-c/json.h>
22
23 #include <lua.h>
24 #include <lualib.h>
25 #include <lauxlib.h>
26
27 #define LUCI_JSONC "luci.jsonc"
28 #define LUCI_JSONC_PARSER "luci.jsonc.parser"
29
30 struct json_state {
31         struct json_object *obj;
32         struct json_tokener *tok;
33         enum json_tokener_error err;
34 };
35
36 static void _json_to_lua(lua_State *L, struct json_object *obj);
37 static struct json_object * _lua_to_json(lua_State *L, int index);
38
39 static int json_new(lua_State *L)
40 {
41         struct json_state *s;
42         struct json_tokener *tok = json_tokener_new();
43
44         if (!tok)
45                 return 0;
46
47         s = lua_newuserdata(L, sizeof(*s));
48
49         if (!s)
50         {
51                 json_tokener_free(tok);
52                 return 0;
53         }
54
55         s->tok = tok;
56         s->obj = NULL;
57         s->err = json_tokener_continue;
58
59         luaL_getmetatable(L, LUCI_JSONC_PARSER);
60         lua_setmetatable(L, -2);
61
62         return 1;
63 }
64
65 static int json_parse(lua_State *L)
66 {
67         size_t len;
68         const char *json = luaL_checklstring(L, 1, &len);
69         struct json_state s = {
70                 .tok = json_tokener_new()
71         };
72
73         if (!s.tok)
74                 return 0;
75
76         s.obj = json_tokener_parse_ex(s.tok, json, len);
77         s.err = json_tokener_get_error(s.tok);
78
79         if (s.obj)
80         {
81                 _json_to_lua(L, s.obj);
82                 json_object_put(s.obj);
83         }
84         else
85         {
86                 lua_pushnil(L);
87         }
88
89         if (s.err == json_tokener_continue)
90                 s.err = json_tokener_error_parse_eof;
91
92         if (s.err)
93                 lua_pushstring(L, json_tokener_error_desc(s.err));
94
95         json_tokener_free(s.tok);
96         return (1 + !!s.err);
97 }
98
99 static int json_stringify(lua_State *L)
100 {
101         struct json_object *obj = _lua_to_json(L, 1);
102         bool pretty = lua_toboolean(L, 2);
103         int flags = 0;
104
105         if (pretty)
106                 flags |= JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED;
107
108         lua_pushstring(L, json_object_to_json_string_ext(obj, flags));
109         json_object_put(obj);
110         return 1;
111 }
112
113
114 static int json_parse_chunk(lua_State *L)
115 {
116         size_t len;
117         struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER);
118         const char *chunk = luaL_checklstring(L, 2, &len);
119
120         s->obj = json_tokener_parse_ex(s->tok, chunk, len);
121     s->err = json_tokener_get_error(s->tok);
122
123         if (!s->err)
124         {
125                 lua_pushboolean(L, true);
126                 return 1;
127         }
128         else if (s->err == json_tokener_continue)
129         {
130                 lua_pushboolean(L, false);
131                 return 1;
132         }
133
134         lua_pushnil(L);
135         lua_pushstring(L, json_tokener_error_desc(s->err));
136         return 2;
137 }
138
139 static void _json_to_lua(lua_State *L, struct json_object *obj)
140 {
141         int n;
142
143         switch (json_object_get_type(obj))
144         {
145         case json_type_object:
146                 lua_newtable(L);
147                 json_object_object_foreach(obj, key, val)
148                 {
149                         _json_to_lua(L, val);
150                         lua_setfield(L, -2, key);
151                 }
152                 break;
153
154         case json_type_array:
155                 lua_newtable(L);
156                 for (n = 0; n < json_object_array_length(obj); n++)
157                 {
158                         _json_to_lua(L, json_object_array_get_idx(obj, n));
159                         lua_rawseti(L, -2, n + 1);
160                 }
161                 break;
162
163         case json_type_boolean:
164                 lua_pushboolean(L, json_object_get_boolean(obj));
165                 break;
166
167         case json_type_int:
168                 lua_pushinteger(L, json_object_get_int(obj));
169                 break;
170
171         case json_type_double:
172                 lua_pushnumber(L, json_object_get_double(obj));
173                 break;
174
175         case json_type_string:
176                 lua_pushstring(L, json_object_get_string(obj));
177                 break;
178
179         case json_type_null:
180                 lua_pushnil(L);
181                 break;
182         }
183 }
184
185 static int json_parse_get(lua_State *L)
186 {
187         struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER);
188
189         if (!s->obj || s->err)
190                 lua_pushnil(L);
191         else
192                 _json_to_lua(L, s->obj);
193
194         return 1;
195 }
196
197 static int _lua_test_array(lua_State *L, int index)
198 {
199         int max = 0;
200         lua_Number idx;
201
202         lua_pushnil(L);
203
204         /* check for non-integer keys */
205         while (lua_next(L, index))
206         {
207                 if (lua_type(L, -2) != LUA_TNUMBER)
208                         goto out;
209
210                 idx = lua_tonumber(L, -2);
211
212                 if (idx != (lua_Number)(lua_Integer)idx)
213                         goto out;
214
215                 if (idx <= 0)
216                         goto out;
217
218                 if (idx > max)
219                         max = idx;
220
221                 lua_pop(L, 1);
222                 continue;
223
224 out:
225                 lua_pop(L, 2);
226                 return -1;
227         }
228
229         /* check for holes */
230         //for (i = 1; i <= max; i++)
231         //{
232         //      lua_rawgeti(L, index, i);
233         //
234         //      if (lua_isnil(L, -1))
235         //      {
236         //              lua_pop(L, 1);
237         //              return 0;
238         //      }
239         //
240         //      lua_pop(L, 1);
241         //}
242
243         return max;
244 }
245
246 static struct json_object * _lua_to_json(lua_State *L, int index)
247 {
248         lua_Number nd, ni;
249         struct json_object *obj;
250         const char *key;
251         int i, max;
252
253         switch (lua_type(L, index))
254         {
255         case LUA_TTABLE:
256                 max = _lua_test_array(L, index);
257
258                 if (max >= 0)
259                 {
260                         obj = json_object_new_array();
261
262                         if (!obj)
263                                 return NULL;
264
265                         for (i = 1; i <= max; i++)
266                         {
267                                 lua_rawgeti(L, index, i);
268
269                                 json_object_array_put_idx(obj, i - 1,
270                                                           _lua_to_json(L, lua_gettop(L)));
271
272                                 lua_pop(L, 1);
273                         }
274
275                         return obj;
276                 }
277
278                 obj = json_object_new_object();
279
280                 if (!obj)
281                         return NULL;
282
283                 lua_pushnil(L);
284
285                 while (lua_next(L, index))
286                 {
287                         lua_pushvalue(L, -2);
288                         key = lua_tostring(L, -1);
289
290                         if (key)
291                                 json_object_object_add(obj, key,
292                                                        _lua_to_json(L, lua_gettop(L) - 1));
293
294                         lua_pop(L, 2);
295                 }
296
297                 return obj;
298
299         case LUA_TNIL:
300                 return NULL;
301
302         case LUA_TBOOLEAN:
303                 return json_object_new_boolean(lua_toboolean(L, index));
304
305         case LUA_TNUMBER:
306                 nd = lua_tonumber(L, index);
307                 ni = lua_tointeger(L, index);
308
309                 if (nd == ni)
310                         return json_object_new_int(nd);
311
312                 return json_object_new_double(nd);
313
314         case LUA_TSTRING:
315                 return json_object_new_string(lua_tostring(L, index));
316         }
317
318         return NULL;
319 }
320
321 static int json_parse_set(lua_State *L)
322 {
323         struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER);
324
325         s->err = 0;
326         s->obj = _lua_to_json(L, 2);
327
328         return 0;
329 }
330
331 static int json_parse_sink_closure(lua_State *L)
332 {
333         bool finished = lua_toboolean(L, lua_upvalueindex(2));
334         if (lua_isnil(L, 1))
335         {
336                 // no more data available
337                 if (finished)
338                 {
339                         // we were finished parsing
340                         lua_pushboolean(L, true);
341                         return 1;
342                 }
343                 else
344                 {
345                         lua_pushnil(L);
346                         lua_pushstring(L, "Incomplete JSON data");
347                         return 2;
348                 }
349         }
350         else
351         {
352                 if (finished)
353                 {
354                         lua_pushnil(L);
355                         lua_pushstring(L, "Unexpected data after complete JSON object");
356                         return 2;
357                 }
358                 else
359                 {
360                         // luci.jsonc.parser.chunk()
361                         lua_pushcfunction(L, json_parse_chunk);
362                         // parser object from closure
363                         lua_pushvalue(L, lua_upvalueindex(1));
364                         // chunk
365                         lua_pushvalue(L, 1);
366                         lua_call(L, 2, 2);
367
368                         if (lua_isnil(L, -2))
369                         {
370                                 // an error occurred, leave (nil, errmsg) on the stack and return it
371                                 return 2;
372                         }
373                         else if (lua_toboolean(L, -2))
374                         {
375                                 // finished reading, set finished=true and return nil to prevent further input
376                                 lua_pop(L, 2);
377                                 lua_pushboolean(L, true);
378                                 lua_replace(L, lua_upvalueindex(2));
379                                 lua_pushnil(L);
380                                 return 1;
381                         }
382                         else
383                         {
384                                 // not finished reading, return true
385                                 lua_pop(L, 2);
386                                 lua_pushboolean(L, true);
387                                 return 1;
388                         }
389                 }
390         }
391 }
392
393 static int json_parse_sink(lua_State *L)
394 {
395         luaL_checkudata(L, 1, LUCI_JSONC_PARSER);
396         lua_pushboolean(L, false);
397         lua_pushcclosure(L, json_parse_sink_closure, 2);
398         return 1;
399 }
400
401 static int json_tostring(lua_State *L)
402 {
403         struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER);
404         bool pretty = lua_toboolean(L, 2);
405         int flags = 0;
406
407         if (pretty)
408                 flags |= JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED;
409
410         lua_pushstring(L, json_object_to_json_string_ext(s->obj, flags));
411         return 1;
412 }
413
414 static int json_gc(lua_State *L)
415 {
416         struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER);
417
418         if (s->obj)
419                 json_object_put(s->obj);
420
421         if (s->tok)
422                 json_tokener_free(s->tok);
423
424         return 0;
425 }
426
427
428 static const luaL_reg jsonc_methods[] = {
429         { "new",                        json_new          },
430         { "parse",                      json_parse        },
431         { "stringify",          json_stringify    },
432
433         { }
434 };
435
436 static const luaL_reg jsonc_parser_methods[] = {
437         { "parse",                      json_parse_chunk  },
438         { "get",                        json_parse_get    },
439         { "set",                        json_parse_set    },
440         { "sink",                       json_parse_sink   },
441         { "stringify",          json_tostring     },
442
443         { "__gc",                       json_gc           },
444         { "__tostring",         json_tostring     },
445
446         { }
447 };
448
449
450 int luaopen_luci_jsonc(lua_State *L)
451 {
452         luaL_register(L, LUCI_JSONC, jsonc_methods);
453
454         luaL_newmetatable(L, LUCI_JSONC_PARSER);
455         luaL_register(L, NULL, jsonc_parser_methods);
456         lua_pushvalue(L, -1);
457         lua_setfield(L, -2, "__index");
458         lua_pop(L, 1);
459
460         return 1;
461 }