blob: b57cac74af9b584daebcbf470a7d6c4bf5fdeb02 [file] [log] [blame]
Anthony Liguori4a5fcab2009-11-11 10:39:23 -06001/*
2 * JSON Parser
3 *
4 * Copyright IBM, Corp. 2009
5 *
6 * Authors:
7 * Anthony Liguori <aliguori@us.ibm.com>
8 *
9 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
10 * See the COPYING.LIB file in the top-level directory.
11 *
12 */
13
Amos Kongc96c84a2010-03-24 23:12:05 +080014#include <stdarg.h>
Anthony Liguori4a5fcab2009-11-11 10:39:23 -060015
16#include "qemu-common.h"
Paolo Bonzini7b1b5d12012-12-17 18:19:43 +010017#include "qapi/qmp/qstring.h"
18#include "qapi/qmp/qint.h"
19#include "qapi/qmp/qdict.h"
20#include "qapi/qmp/qlist.h"
21#include "qapi/qmp/qfloat.h"
22#include "qapi/qmp/qbool.h"
23#include "qapi/qmp/json-parser.h"
24#include "qapi/qmp/json-lexer.h"
Anthony Liguori4a5fcab2009-11-11 10:39:23 -060025
26typedef struct JSONParserContext
27{
Anthony Liguorief749d02011-06-01 12:14:50 -050028 Error *err;
Michael Roth65c0f1e2012-08-15 13:45:43 -050029 struct {
30 QObject **buf;
31 size_t pos;
32 size_t count;
33 } tokens;
Anthony Liguori4a5fcab2009-11-11 10:39:23 -060034} JSONParserContext;
35
36#define BUG_ON(cond) assert(!(cond))
37
38/**
39 * TODO
40 *
41 * 0) make errors meaningful again
42 * 1) add geometry information to tokens
43 * 3) should we return a parsed size?
44 * 4) deal with premature EOI
45 */
46
Michael Roth65c0f1e2012-08-15 13:45:43 -050047static QObject *parse_value(JSONParserContext *ctxt, va_list *ap);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -060048
49/**
50 * Token manipulators
51 *
52 * tokens are dictionaries that contain a type, a string value, and geometry information
53 * about a token identified by the lexer. These are routines that make working with
54 * these objects a bit easier.
55 */
56static const char *token_get_value(QObject *obj)
57{
58 return qdict_get_str(qobject_to_qdict(obj), "token");
59}
60
61static JSONTokenType token_get_type(QObject *obj)
62{
63 return qdict_get_int(qobject_to_qdict(obj), "type");
64}
65
Anthony Liguori4a5fcab2009-11-11 10:39:23 -060066/**
67 * Error handler
68 */
Stefan Weil8b7968f2010-09-23 21:28:05 +020069static void GCC_FMT_ATTR(3, 4) parse_error(JSONParserContext *ctxt,
70 QObject *token, const char *msg, ...)
Anthony Liguori4a5fcab2009-11-11 10:39:23 -060071{
Amos Kongc96c84a2010-03-24 23:12:05 +080072 va_list ap;
Anthony Liguorief749d02011-06-01 12:14:50 -050073 char message[1024];
Amos Kongc96c84a2010-03-24 23:12:05 +080074 va_start(ap, msg);
Anthony Liguorief749d02011-06-01 12:14:50 -050075 vsnprintf(message, sizeof(message), msg, ap);
Amos Kongc96c84a2010-03-24 23:12:05 +080076 va_end(ap);
Anthony Liguorief749d02011-06-01 12:14:50 -050077 if (ctxt->err) {
78 error_free(ctxt->err);
79 ctxt->err = NULL;
80 }
Cole Robinsonf231b882014-03-21 19:42:26 -040081 error_setg(&ctxt->err, "JSON parse error, %s", message);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -060082}
83
84/**
85 * String helpers
86 *
87 * These helpers are used to unescape strings.
88 */
89static void wchar_to_utf8(uint16_t wchar, char *buffer, size_t buffer_length)
90{
91 if (wchar <= 0x007F) {
92 BUG_ON(buffer_length < 2);
93
94 buffer[0] = wchar & 0x7F;
95 buffer[1] = 0;
96 } else if (wchar <= 0x07FF) {
97 BUG_ON(buffer_length < 3);
98
99 buffer[0] = 0xC0 | ((wchar >> 6) & 0x1F);
100 buffer[1] = 0x80 | (wchar & 0x3F);
101 buffer[2] = 0;
102 } else {
103 BUG_ON(buffer_length < 4);
104
105 buffer[0] = 0xE0 | ((wchar >> 12) & 0x0F);
106 buffer[1] = 0x80 | ((wchar >> 6) & 0x3F);
107 buffer[2] = 0x80 | (wchar & 0x3F);
108 buffer[3] = 0;
109 }
110}
111
112static int hex2decimal(char ch)
113{
114 if (ch >= '0' && ch <= '9') {
115 return (ch - '0');
116 } else if (ch >= 'a' && ch <= 'f') {
117 return 10 + (ch - 'a');
118 } else if (ch >= 'A' && ch <= 'F') {
119 return 10 + (ch - 'A');
120 }
121
122 return -1;
123}
124
125/**
126 * parse_string(): Parse a json string and return a QObject
127 *
128 * string
129 * ""
130 * " chars "
131 * chars
132 * char
133 * char chars
134 * char
135 * any-Unicode-character-
136 * except-"-or-\-or-
137 * control-character
138 * \"
139 * \\
140 * \/
141 * \b
142 * \f
143 * \n
144 * \r
145 * \t
146 * \u four-hex-digits
147 */
148static QString *qstring_from_escaped_str(JSONParserContext *ctxt, QObject *token)
149{
150 const char *ptr = token_get_value(token);
151 QString *str;
152 int double_quote = 1;
153
154 if (*ptr == '"') {
155 double_quote = 1;
156 } else {
157 double_quote = 0;
158 }
159 ptr++;
160
161 str = qstring_new();
162 while (*ptr &&
163 ((double_quote && *ptr != '"') || (!double_quote && *ptr != '\''))) {
164 if (*ptr == '\\') {
165 ptr++;
166
167 switch (*ptr) {
168 case '"':
169 qstring_append(str, "\"");
170 ptr++;
171 break;
172 case '\'':
173 qstring_append(str, "'");
174 ptr++;
175 break;
176 case '\\':
177 qstring_append(str, "\\");
178 ptr++;
179 break;
180 case '/':
181 qstring_append(str, "/");
182 ptr++;
183 break;
184 case 'b':
185 qstring_append(str, "\b");
186 ptr++;
187 break;
Luiz Capitulinobd032692010-05-19 17:06:15 -0300188 case 'f':
189 qstring_append(str, "\f");
190 ptr++;
191 break;
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600192 case 'n':
193 qstring_append(str, "\n");
194 ptr++;
195 break;
196 case 'r':
197 qstring_append(str, "\r");
198 ptr++;
199 break;
200 case 't':
201 qstring_append(str, "\t");
202 ptr++;
203 break;
204 case 'u': {
205 uint16_t unicode_char = 0;
206 char utf8_char[4];
207 int i = 0;
208
209 ptr++;
210
211 for (i = 0; i < 4; i++) {
212 if (qemu_isxdigit(*ptr)) {
213 unicode_char |= hex2decimal(*ptr) << ((3 - i) * 4);
214 } else {
215 parse_error(ctxt, token,
216 "invalid hex escape sequence in string");
217 goto out;
218 }
219 ptr++;
220 }
221
222 wchar_to_utf8(unicode_char, utf8_char, sizeof(utf8_char));
223 qstring_append(str, utf8_char);
224 } break;
225 default:
226 parse_error(ctxt, token, "invalid escape sequence in string");
227 goto out;
228 }
229 } else {
230 char dummy[2];
231
232 dummy[0] = *ptr++;
233 dummy[1] = 0;
234
235 qstring_append(str, dummy);
236 }
237 }
238
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600239 return str;
240
241out:
242 QDECREF(str);
243 return NULL;
244}
245
Michael Roth65c0f1e2012-08-15 13:45:43 -0500246static QObject *parser_context_pop_token(JSONParserContext *ctxt)
247{
248 QObject *token;
249 g_assert(ctxt->tokens.pos < ctxt->tokens.count);
250 token = ctxt->tokens.buf[ctxt->tokens.pos];
251 ctxt->tokens.pos++;
252 return token;
253}
254
255/* Note: parser_context_{peek|pop}_token do not increment the
256 * token object's refcount. In both cases the references will continue
257 * to be tracked and cleaned up in parser_context_free(), so do not
258 * attempt to free the token object.
259 */
260static QObject *parser_context_peek_token(JSONParserContext *ctxt)
261{
262 QObject *token;
263 g_assert(ctxt->tokens.pos < ctxt->tokens.count);
264 token = ctxt->tokens.buf[ctxt->tokens.pos];
265 return token;
266}
267
268static JSONParserContext parser_context_save(JSONParserContext *ctxt)
269{
270 JSONParserContext saved_ctxt = {0};
271 saved_ctxt.tokens.pos = ctxt->tokens.pos;
272 saved_ctxt.tokens.count = ctxt->tokens.count;
273 saved_ctxt.tokens.buf = ctxt->tokens.buf;
274 return saved_ctxt;
275}
276
277static void parser_context_restore(JSONParserContext *ctxt,
278 JSONParserContext saved_ctxt)
279{
280 ctxt->tokens.pos = saved_ctxt.tokens.pos;
281 ctxt->tokens.count = saved_ctxt.tokens.count;
282 ctxt->tokens.buf = saved_ctxt.tokens.buf;
283}
284
285static void tokens_append_from_iter(QObject *obj, void *opaque)
286{
287 JSONParserContext *ctxt = opaque;
288 g_assert(ctxt->tokens.pos < ctxt->tokens.count);
289 ctxt->tokens.buf[ctxt->tokens.pos++] = obj;
290 qobject_incref(obj);
291}
292
293static JSONParserContext *parser_context_new(QList *tokens)
294{
295 JSONParserContext *ctxt;
296 size_t count;
297
298 if (!tokens) {
299 return NULL;
300 }
301
302 count = qlist_size(tokens);
303 if (count == 0) {
304 return NULL;
305 }
306
307 ctxt = g_malloc0(sizeof(JSONParserContext));
308 ctxt->tokens.pos = 0;
309 ctxt->tokens.count = count;
310 ctxt->tokens.buf = g_malloc(count * sizeof(QObject *));
311 qlist_iter(tokens, tokens_append_from_iter, ctxt);
312 ctxt->tokens.pos = 0;
313
314 return ctxt;
315}
316
317/* to support error propagation, ctxt->err must be freed separately */
318static void parser_context_free(JSONParserContext *ctxt)
319{
320 int i;
321 if (ctxt) {
322 for (i = 0; i < ctxt->tokens.count; i++) {
323 qobject_decref(ctxt->tokens.buf[i]);
324 }
325 g_free(ctxt->tokens.buf);
326 g_free(ctxt);
327 }
328}
329
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600330/**
331 * Parsing rules
332 */
Michael Roth65c0f1e2012-08-15 13:45:43 -0500333static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap)
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600334{
Anthony Liguori11e8a462011-06-01 12:14:55 -0500335 QObject *key = NULL, *token = NULL, *value, *peek;
Michael Roth65c0f1e2012-08-15 13:45:43 -0500336 JSONParserContext saved_ctxt = parser_context_save(ctxt);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600337
Michael Roth65c0f1e2012-08-15 13:45:43 -0500338 peek = parser_context_peek_token(ctxt);
Anthony Liguori11e8a462011-06-01 12:14:55 -0500339 if (peek == NULL) {
340 parse_error(ctxt, NULL, "premature EOI");
341 goto out;
342 }
343
Michael Roth65c0f1e2012-08-15 13:45:43 -0500344 key = parse_value(ctxt, ap);
Kevin Wolfd758d902010-02-24 16:17:58 +0100345 if (!key || qobject_type(key) != QTYPE_QSTRING) {
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600346 parse_error(ctxt, peek, "key is not a string in object");
347 goto out;
348 }
349
Michael Roth65c0f1e2012-08-15 13:45:43 -0500350 token = parser_context_pop_token(ctxt);
Anthony Liguori11e8a462011-06-01 12:14:55 -0500351 if (token == NULL) {
352 parse_error(ctxt, NULL, "premature EOI");
353 goto out;
354 }
355
Markus Armbrusterc5461662015-11-25 22:23:26 +0100356 if (token_get_type(token) != JSON_COLON) {
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600357 parse_error(ctxt, token, "missing : in object pair");
358 goto out;
359 }
360
Michael Roth65c0f1e2012-08-15 13:45:43 -0500361 value = parse_value(ctxt, ap);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600362 if (value == NULL) {
363 parse_error(ctxt, token, "Missing value in dict");
364 goto out;
365 }
366
367 qdict_put_obj(dict, qstring_get_str(qobject_to_qstring(key)), value);
368
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600369 qobject_decref(key);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600370
371 return 0;
372
373out:
Michael Roth65c0f1e2012-08-15 13:45:43 -0500374 parser_context_restore(ctxt, saved_ctxt);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600375 qobject_decref(key);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600376
377 return -1;
378}
379
Michael Roth65c0f1e2012-08-15 13:45:43 -0500380static QObject *parse_object(JSONParserContext *ctxt, va_list *ap)
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600381{
382 QDict *dict = NULL;
383 QObject *token, *peek;
Michael Roth65c0f1e2012-08-15 13:45:43 -0500384 JSONParserContext saved_ctxt = parser_context_save(ctxt);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600385
Michael Roth65c0f1e2012-08-15 13:45:43 -0500386 token = parser_context_pop_token(ctxt);
Anthony Liguori11e8a462011-06-01 12:14:55 -0500387 if (token == NULL) {
388 goto out;
389 }
390
Markus Armbrusterc5461662015-11-25 22:23:26 +0100391 if (token_get_type(token) != JSON_LCURLY) {
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600392 goto out;
393 }
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600394
395 dict = qdict_new();
396
Michael Roth65c0f1e2012-08-15 13:45:43 -0500397 peek = parser_context_peek_token(ctxt);
Anthony Liguori11e8a462011-06-01 12:14:55 -0500398 if (peek == NULL) {
399 parse_error(ctxt, NULL, "premature EOI");
400 goto out;
401 }
402
Markus Armbrusterc5461662015-11-25 22:23:26 +0100403 if (token_get_type(peek) != JSON_RCURLY) {
Michael Roth65c0f1e2012-08-15 13:45:43 -0500404 if (parse_pair(ctxt, dict, ap) == -1) {
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600405 goto out;
406 }
407
Michael Roth65c0f1e2012-08-15 13:45:43 -0500408 token = parser_context_pop_token(ctxt);
Anthony Liguori11e8a462011-06-01 12:14:55 -0500409 if (token == NULL) {
410 parse_error(ctxt, NULL, "premature EOI");
411 goto out;
412 }
413
Markus Armbrusterc5461662015-11-25 22:23:26 +0100414 while (token_get_type(token) != JSON_RCURLY) {
415 if (token_get_type(token) != JSON_COMMA) {
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600416 parse_error(ctxt, token, "expected separator in dict");
417 goto out;
418 }
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600419
Michael Roth65c0f1e2012-08-15 13:45:43 -0500420 if (parse_pair(ctxt, dict, ap) == -1) {
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600421 goto out;
422 }
423
Michael Roth65c0f1e2012-08-15 13:45:43 -0500424 token = parser_context_pop_token(ctxt);
Anthony Liguori11e8a462011-06-01 12:14:55 -0500425 if (token == NULL) {
426 parse_error(ctxt, NULL, "premature EOI");
427 goto out;
428 }
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600429 }
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600430 } else {
Gongleia491af42014-06-10 17:20:24 +0800431 (void)parser_context_pop_token(ctxt);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600432 }
433
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600434 return QOBJECT(dict);
435
436out:
Michael Roth65c0f1e2012-08-15 13:45:43 -0500437 parser_context_restore(ctxt, saved_ctxt);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600438 QDECREF(dict);
439 return NULL;
440}
441
Michael Roth65c0f1e2012-08-15 13:45:43 -0500442static QObject *parse_array(JSONParserContext *ctxt, va_list *ap)
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600443{
444 QList *list = NULL;
445 QObject *token, *peek;
Michael Roth65c0f1e2012-08-15 13:45:43 -0500446 JSONParserContext saved_ctxt = parser_context_save(ctxt);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600447
Michael Roth65c0f1e2012-08-15 13:45:43 -0500448 token = parser_context_pop_token(ctxt);
Anthony Liguori11e8a462011-06-01 12:14:55 -0500449 if (token == NULL) {
450 goto out;
451 }
452
Markus Armbrusterc5461662015-11-25 22:23:26 +0100453 if (token_get_type(token) != JSON_LSQUARE) {
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600454 goto out;
455 }
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600456
457 list = qlist_new();
458
Michael Roth65c0f1e2012-08-15 13:45:43 -0500459 peek = parser_context_peek_token(ctxt);
Anthony Liguori11e8a462011-06-01 12:14:55 -0500460 if (peek == NULL) {
461 parse_error(ctxt, NULL, "premature EOI");
462 goto out;
463 }
464
Markus Armbrusterc5461662015-11-25 22:23:26 +0100465 if (token_get_type(peek) != JSON_RSQUARE) {
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600466 QObject *obj;
467
Michael Roth65c0f1e2012-08-15 13:45:43 -0500468 obj = parse_value(ctxt, ap);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600469 if (obj == NULL) {
470 parse_error(ctxt, token, "expecting value");
471 goto out;
472 }
473
474 qlist_append_obj(list, obj);
475
Michael Roth65c0f1e2012-08-15 13:45:43 -0500476 token = parser_context_pop_token(ctxt);
Anthony Liguori11e8a462011-06-01 12:14:55 -0500477 if (token == NULL) {
478 parse_error(ctxt, NULL, "premature EOI");
479 goto out;
480 }
481
Markus Armbrusterc5461662015-11-25 22:23:26 +0100482 while (token_get_type(token) != JSON_RSQUARE) {
483 if (token_get_type(token) != JSON_COMMA) {
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600484 parse_error(ctxt, token, "expected separator in list");
485 goto out;
486 }
487
Michael Roth65c0f1e2012-08-15 13:45:43 -0500488 obj = parse_value(ctxt, ap);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600489 if (obj == NULL) {
490 parse_error(ctxt, token, "expecting value");
491 goto out;
492 }
493
494 qlist_append_obj(list, obj);
495
Michael Roth65c0f1e2012-08-15 13:45:43 -0500496 token = parser_context_pop_token(ctxt);
Anthony Liguori11e8a462011-06-01 12:14:55 -0500497 if (token == NULL) {
498 parse_error(ctxt, NULL, "premature EOI");
499 goto out;
500 }
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600501 }
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600502 } else {
Gongleia491af42014-06-10 17:20:24 +0800503 (void)parser_context_pop_token(ctxt);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600504 }
505
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600506 return QOBJECT(list);
507
508out:
Michael Roth65c0f1e2012-08-15 13:45:43 -0500509 parser_context_restore(ctxt, saved_ctxt);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600510 QDECREF(list);
511 return NULL;
512}
513
Michael Roth65c0f1e2012-08-15 13:45:43 -0500514static QObject *parse_keyword(JSONParserContext *ctxt)
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600515{
516 QObject *token, *ret;
Michael Roth65c0f1e2012-08-15 13:45:43 -0500517 JSONParserContext saved_ctxt = parser_context_save(ctxt);
Markus Armbruster50e2a462015-11-25 22:23:27 +0100518 const char *val;
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600519
Michael Roth65c0f1e2012-08-15 13:45:43 -0500520 token = parser_context_pop_token(ctxt);
Anthony Liguori11e8a462011-06-01 12:14:55 -0500521 if (token == NULL) {
522 goto out;
523 }
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600524
525 if (token_get_type(token) != JSON_KEYWORD) {
526 goto out;
527 }
528
Markus Armbruster50e2a462015-11-25 22:23:27 +0100529 val = token_get_value(token);
530
531 if (!strcmp(val, "true")) {
Eric Blakefc48ffc2015-05-15 16:24:59 -0600532 ret = QOBJECT(qbool_from_bool(true));
Markus Armbruster50e2a462015-11-25 22:23:27 +0100533 } else if (!strcmp(val, "false")) {
Eric Blakefc48ffc2015-05-15 16:24:59 -0600534 ret = QOBJECT(qbool_from_bool(false));
Markus Armbruster50e2a462015-11-25 22:23:27 +0100535 } else if (!strcmp(val, "null")) {
Eric Blakee549e712015-04-29 15:35:06 -0600536 ret = qnull();
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600537 } else {
Markus Armbruster50e2a462015-11-25 22:23:27 +0100538 parse_error(ctxt, token, "invalid keyword '%s'", val);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600539 goto out;
540 }
541
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600542 return ret;
543
544out:
Michael Roth65c0f1e2012-08-15 13:45:43 -0500545 parser_context_restore(ctxt, saved_ctxt);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600546
547 return NULL;
548}
549
Michael Roth65c0f1e2012-08-15 13:45:43 -0500550static QObject *parse_escape(JSONParserContext *ctxt, va_list *ap)
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600551{
552 QObject *token = NULL, *obj;
Michael Roth65c0f1e2012-08-15 13:45:43 -0500553 JSONParserContext saved_ctxt = parser_context_save(ctxt);
Markus Armbruster6b9606f2015-11-25 22:23:28 +0100554 const char *val;
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600555
556 if (ap == NULL) {
557 goto out;
558 }
559
Michael Roth65c0f1e2012-08-15 13:45:43 -0500560 token = parser_context_pop_token(ctxt);
Anthony Liguori11e8a462011-06-01 12:14:55 -0500561 if (token == NULL) {
562 goto out;
563 }
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600564
Markus Armbruster6b9606f2015-11-25 22:23:28 +0100565 if (token_get_type(token) != JSON_ESCAPE) {
566 goto out;
567 }
568
569 val = token_get_value(token);
570
571 if (!strcmp(val, "%p")) {
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600572 obj = va_arg(*ap, QObject *);
Markus Armbruster6b9606f2015-11-25 22:23:28 +0100573 } else if (!strcmp(val, "%i")) {
Eric Blakefc48ffc2015-05-15 16:24:59 -0600574 obj = QOBJECT(qbool_from_bool(va_arg(*ap, int)));
Markus Armbruster6b9606f2015-11-25 22:23:28 +0100575 } else if (!strcmp(val, "%d")) {
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600576 obj = QOBJECT(qint_from_int(va_arg(*ap, int)));
Markus Armbruster6b9606f2015-11-25 22:23:28 +0100577 } else if (!strcmp(val, "%ld")) {
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600578 obj = QOBJECT(qint_from_int(va_arg(*ap, long)));
Markus Armbruster6b9606f2015-11-25 22:23:28 +0100579 } else if (!strcmp(val, "%lld") ||
580 !strcmp(val, "%I64d")) {
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600581 obj = QOBJECT(qint_from_int(va_arg(*ap, long long)));
Markus Armbruster6b9606f2015-11-25 22:23:28 +0100582 } else if (!strcmp(val, "%s")) {
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600583 obj = QOBJECT(qstring_from_str(va_arg(*ap, const char *)));
Markus Armbruster6b9606f2015-11-25 22:23:28 +0100584 } else if (!strcmp(val, "%f")) {
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600585 obj = QOBJECT(qfloat_from_double(va_arg(*ap, double)));
586 } else {
587 goto out;
588 }
589
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600590 return obj;
591
592out:
Michael Roth65c0f1e2012-08-15 13:45:43 -0500593 parser_context_restore(ctxt, saved_ctxt);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600594
595 return NULL;
596}
597
Michael Roth65c0f1e2012-08-15 13:45:43 -0500598static QObject *parse_literal(JSONParserContext *ctxt)
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600599{
600 QObject *token, *obj;
Michael Roth65c0f1e2012-08-15 13:45:43 -0500601 JSONParserContext saved_ctxt = parser_context_save(ctxt);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600602
Michael Roth65c0f1e2012-08-15 13:45:43 -0500603 token = parser_context_pop_token(ctxt);
Anthony Liguori11e8a462011-06-01 12:14:55 -0500604 if (token == NULL) {
605 goto out;
606 }
607
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600608 switch (token_get_type(token)) {
609 case JSON_STRING:
610 obj = QOBJECT(qstring_from_escaped_str(ctxt, token));
611 break;
Michael Roth3d5b3ec2013-05-10 17:46:05 -0500612 case JSON_INTEGER: {
613 /* A possibility exists that this is a whole-valued float where the
614 * fractional part was left out due to being 0 (.0). It's not a big
615 * deal to treat these as ints in the parser, so long as users of the
616 * resulting QObject know to expect a QInt in place of a QFloat in
617 * cases like these.
618 *
619 * However, in some cases these values will overflow/underflow a
620 * QInt/int64 container, thus we should assume these are to be handled
621 * as QFloats/doubles rather than silently changing their values.
622 *
623 * strtoll() indicates these instances by setting errno to ERANGE
624 */
625 int64_t value;
626
627 errno = 0; /* strtoll doesn't set errno on success */
628 value = strtoll(token_get_value(token), NULL, 10);
629 if (errno != ERANGE) {
630 obj = QOBJECT(qint_from_int(value));
631 break;
632 }
633 /* fall through to JSON_FLOAT */
634 }
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600635 case JSON_FLOAT:
636 /* FIXME dependent on locale */
637 obj = QOBJECT(qfloat_from_double(strtod(token_get_value(token), NULL)));
638 break;
639 default:
640 goto out;
641 }
642
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600643 return obj;
644
645out:
Michael Roth65c0f1e2012-08-15 13:45:43 -0500646 parser_context_restore(ctxt, saved_ctxt);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600647
648 return NULL;
649}
650
Michael Roth65c0f1e2012-08-15 13:45:43 -0500651static QObject *parse_value(JSONParserContext *ctxt, va_list *ap)
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600652{
653 QObject *obj;
654
Michael Roth65c0f1e2012-08-15 13:45:43 -0500655 obj = parse_object(ctxt, ap);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600656 if (obj == NULL) {
Michael Roth65c0f1e2012-08-15 13:45:43 -0500657 obj = parse_array(ctxt, ap);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600658 }
659 if (obj == NULL) {
Michael Roth65c0f1e2012-08-15 13:45:43 -0500660 obj = parse_escape(ctxt, ap);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600661 }
662 if (obj == NULL) {
Michael Roth65c0f1e2012-08-15 13:45:43 -0500663 obj = parse_keyword(ctxt);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600664 }
665 if (obj == NULL) {
Michael Roth65c0f1e2012-08-15 13:45:43 -0500666 obj = parse_literal(ctxt);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600667 }
668
669 return obj;
670}
671
672QObject *json_parser_parse(QList *tokens, va_list *ap)
673{
Anthony Liguorief749d02011-06-01 12:14:50 -0500674 return json_parser_parse_err(tokens, ap, NULL);
675}
676
677QObject *json_parser_parse_err(QList *tokens, va_list *ap, Error **errp)
678{
Michael Roth65c0f1e2012-08-15 13:45:43 -0500679 JSONParserContext *ctxt = parser_context_new(tokens);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600680 QObject *result;
681
Michael Roth65c0f1e2012-08-15 13:45:43 -0500682 if (!ctxt) {
Michael Rothc1990eb2011-06-01 12:15:00 -0500683 return NULL;
684 }
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600685
Michael Roth65c0f1e2012-08-15 13:45:43 -0500686 result = parse_value(ctxt, ap);
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600687
Michael Roth65c0f1e2012-08-15 13:45:43 -0500688 error_propagate(errp, ctxt->err);
689
690 parser_context_free(ctxt);
Anthony Liguorief749d02011-06-01 12:14:50 -0500691
Anthony Liguori4a5fcab2009-11-11 10:39:23 -0600692 return result;
693}