1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Omar Kilani <omar@php.net> |
14 | Jakub Zelenka <bukka@php.net> |
15 +----------------------------------------------------------------------+
16 */
17
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include "php.h"
23 #include "php_ini.h"
24 #include "ext/standard/info.h"
25 #include "ext/standard/html.h"
26 #include "zend_smart_str.h"
27 #include "php_json.h"
28 #include "php_json_encoder.h"
29 #include <zend_exceptions.h>
30 #include "zend_enum.h"
31
32 static const char digits[] = "0123456789abcdef";
33
34 static int php_json_escape_string(
35 smart_str *buf, const char *s, size_t len,
36 int options, php_json_encoder *encoder);
37
php_json_determine_array_type(zval * val)38 static int php_json_determine_array_type(zval *val) /* {{{ */
39 {
40 zend_array *myht = Z_ARRVAL_P(val);
41
42 if (myht) {
43 return zend_array_is_list(myht) ? PHP_JSON_OUTPUT_ARRAY : PHP_JSON_OUTPUT_OBJECT;
44 }
45
46 return PHP_JSON_OUTPUT_ARRAY;
47 }
48 /* }}} */
49
50 /* {{{ Pretty printing support functions */
51
php_json_pretty_print_char(smart_str * buf,int options,char c)52 static inline void php_json_pretty_print_char(smart_str *buf, int options, char c) /* {{{ */
53 {
54 if (options & PHP_JSON_PRETTY_PRINT) {
55 smart_str_appendc(buf, c);
56 }
57 }
58 /* }}} */
59
php_json_pretty_print_indent(smart_str * buf,int options,php_json_encoder * encoder)60 static inline void php_json_pretty_print_indent(smart_str *buf, int options, php_json_encoder *encoder) /* {{{ */
61 {
62 int i;
63
64 if (options & PHP_JSON_PRETTY_PRINT) {
65 for (i = 0; i < encoder->depth; ++i) {
66 smart_str_appendl(buf, " ", 4);
67 }
68 }
69 }
70 /* }}} */
71
72 /* }}} */
73
php_json_is_valid_double(double d)74 static inline int php_json_is_valid_double(double d) /* {{{ */
75 {
76 return !zend_isinf(d) && !zend_isnan(d);
77 }
78 /* }}} */
79
php_json_encode_double(smart_str * buf,double d,int options)80 static inline void php_json_encode_double(smart_str *buf, double d, int options) /* {{{ */
81 {
82 size_t len;
83 char num[ZEND_DOUBLE_MAX_LENGTH];
84
85 zend_gcvt(d, (int)PG(serialize_precision), '.', 'e', num);
86 len = strlen(num);
87 if (options & PHP_JSON_PRESERVE_ZERO_FRACTION && strchr(num, '.') == NULL && len < ZEND_DOUBLE_MAX_LENGTH - 2) {
88 num[len++] = '.';
89 num[len++] = '0';
90 num[len] = '\0';
91 }
92 smart_str_appendl(buf, num, len);
93 }
94 /* }}} */
95
96 #define PHP_JSON_HASH_PROTECT_RECURSION(_tmp_ht) \
97 do { \
98 if (_tmp_ht) { \
99 GC_TRY_PROTECT_RECURSION(_tmp_ht); \
100 } \
101 } while (0)
102
103 #define PHP_JSON_HASH_UNPROTECT_RECURSION(_tmp_ht) \
104 do { \
105 if (_tmp_ht) { \
106 GC_TRY_UNPROTECT_RECURSION(_tmp_ht); \
107 } \
108 } while (0)
109
php_json_encode_array(smart_str * buf,zval * val,int options,php_json_encoder * encoder)110 static int php_json_encode_array(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */
111 {
112 int i, r, need_comma = 0;
113 HashTable *myht, *prop_ht;
114
115 if (Z_TYPE_P(val) == IS_ARRAY) {
116 myht = Z_ARRVAL_P(val);
117 prop_ht = NULL;
118 r = (options & PHP_JSON_FORCE_OBJECT) ? PHP_JSON_OUTPUT_OBJECT : php_json_determine_array_type(val);
119 } else if (Z_OBJ_P(val)->properties == NULL
120 && Z_OBJ_HT_P(val)->get_properties_for == NULL
121 && Z_OBJ_HT_P(val)->get_properties == zend_std_get_properties) {
122 /* Optimized version without rebuilding properties HashTable */
123 zend_object *obj = Z_OBJ_P(val);
124 zend_class_entry *ce = obj->ce;
125 zend_property_info *prop_info;
126 zval *prop;
127 int i;
128
129 if (GC_IS_RECURSIVE(obj)) {
130 encoder->error_code = PHP_JSON_ERROR_RECURSION;
131 smart_str_appendl(buf, "null", 4);
132 return FAILURE;
133 }
134
135 PHP_JSON_HASH_PROTECT_RECURSION(obj);
136
137 smart_str_appendc(buf, '{');
138
139 ++encoder->depth;
140
141 for (i = 0; i < ce->default_properties_count; i++) {
142 prop_info = ce->properties_info_table[i];
143 if (!prop_info) {
144 continue;
145 }
146 if (ZSTR_VAL(prop_info->name)[0] == '\0' && ZSTR_LEN(prop_info->name) > 0) {
147 /* Skip protected and private members. */
148 continue;
149 }
150 prop = OBJ_PROP(obj, prop_info->offset);
151 if (Z_TYPE_P(prop) == IS_UNDEF) {
152 continue;
153 }
154
155 if (need_comma) {
156 smart_str_appendc(buf, ',');
157 } else {
158 need_comma = 1;
159 }
160
161 php_json_pretty_print_char(buf, options, '\n');
162 php_json_pretty_print_indent(buf, options, encoder);
163
164 if (php_json_escape_string(buf, ZSTR_VAL(prop_info->name), ZSTR_LEN(prop_info->name),
165 options & ~PHP_JSON_NUMERIC_CHECK, encoder) == FAILURE &&
166 (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) &&
167 buf->s) {
168 ZSTR_LEN(buf->s) -= 4;
169 smart_str_appendl(buf, "\"\"", 2);
170 }
171
172 smart_str_appendc(buf, ':');
173 php_json_pretty_print_char(buf, options, ' ');
174
175 if (php_json_encode_zval(buf, prop, options, encoder) == FAILURE &&
176 !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
177 PHP_JSON_HASH_UNPROTECT_RECURSION(obj);
178 return FAILURE;
179 }
180 }
181
182 PHP_JSON_HASH_UNPROTECT_RECURSION(obj);
183 if (encoder->depth > encoder->max_depth) {
184 encoder->error_code = PHP_JSON_ERROR_DEPTH;
185 if (!(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
186 return FAILURE;
187 }
188 }
189 --encoder->depth;
190
191 if (need_comma) {
192 php_json_pretty_print_char(buf, options, '\n');
193 php_json_pretty_print_indent(buf, options, encoder);
194 }
195 smart_str_appendc(buf, '}');
196 return SUCCESS;
197 } else {
198 prop_ht = myht = zend_get_properties_for(val, ZEND_PROP_PURPOSE_JSON);
199 r = PHP_JSON_OUTPUT_OBJECT;
200 }
201
202 if (myht && GC_IS_RECURSIVE(myht)) {
203 encoder->error_code = PHP_JSON_ERROR_RECURSION;
204 smart_str_appendl(buf, "null", 4);
205 zend_release_properties(prop_ht);
206 return FAILURE;
207 }
208
209 PHP_JSON_HASH_PROTECT_RECURSION(myht);
210
211 if (r == PHP_JSON_OUTPUT_ARRAY) {
212 smart_str_appendc(buf, '[');
213 } else {
214 smart_str_appendc(buf, '{');
215 }
216
217 ++encoder->depth;
218
219 i = myht ? zend_hash_num_elements(myht) : 0;
220
221 if (i > 0) {
222 zend_string *key;
223 zval *data;
224 zend_ulong index;
225
226 ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, data) {
227 if (r == PHP_JSON_OUTPUT_ARRAY) {
228 if (need_comma) {
229 smart_str_appendc(buf, ',');
230 } else {
231 need_comma = 1;
232 }
233
234 php_json_pretty_print_char(buf, options, '\n');
235 php_json_pretty_print_indent(buf, options, encoder);
236 } else if (r == PHP_JSON_OUTPUT_OBJECT) {
237 if (key) {
238 if (ZSTR_VAL(key)[0] == '\0' && ZSTR_LEN(key) > 0 && Z_TYPE_P(val) == IS_OBJECT) {
239 /* Skip protected and private members. */
240 continue;
241 }
242
243 if (need_comma) {
244 smart_str_appendc(buf, ',');
245 } else {
246 need_comma = 1;
247 }
248
249 php_json_pretty_print_char(buf, options, '\n');
250 php_json_pretty_print_indent(buf, options, encoder);
251
252 if (php_json_escape_string(buf, ZSTR_VAL(key), ZSTR_LEN(key),
253 options & ~PHP_JSON_NUMERIC_CHECK, encoder) == FAILURE &&
254 (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) &&
255 buf->s) {
256 ZSTR_LEN(buf->s) -= 4;
257 smart_str_appendl(buf, "\"\"", 2);
258 }
259 } else {
260 if (need_comma) {
261 smart_str_appendc(buf, ',');
262 } else {
263 need_comma = 1;
264 }
265
266 php_json_pretty_print_char(buf, options, '\n');
267 php_json_pretty_print_indent(buf, options, encoder);
268
269 smart_str_appendc(buf, '"');
270 smart_str_append_long(buf, (zend_long) index);
271 smart_str_appendc(buf, '"');
272 }
273
274 smart_str_appendc(buf, ':');
275 php_json_pretty_print_char(buf, options, ' ');
276 }
277
278 if (php_json_encode_zval(buf, data, options, encoder) == FAILURE &&
279 !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
280 PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
281 zend_release_properties(prop_ht);
282 return FAILURE;
283 }
284 } ZEND_HASH_FOREACH_END();
285 }
286
287 PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
288
289 if (encoder->depth > encoder->max_depth) {
290 encoder->error_code = PHP_JSON_ERROR_DEPTH;
291 if (!(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
292 zend_release_properties(prop_ht);
293 return FAILURE;
294 }
295 }
296 --encoder->depth;
297
298 /* Only keep closing bracket on same line for empty arrays/objects */
299 if (need_comma) {
300 php_json_pretty_print_char(buf, options, '\n');
301 php_json_pretty_print_indent(buf, options, encoder);
302 }
303
304 if (r == PHP_JSON_OUTPUT_ARRAY) {
305 smart_str_appendc(buf, ']');
306 } else {
307 smart_str_appendc(buf, '}');
308 }
309
310 zend_release_properties(prop_ht);
311 return SUCCESS;
312 }
313 /* }}} */
314
php_json_escape_string(smart_str * buf,const char * s,size_t len,int options,php_json_encoder * encoder)315 static int php_json_escape_string(
316 smart_str *buf, const char *s, size_t len,
317 int options, php_json_encoder *encoder) /* {{{ */
318 {
319 int status;
320 unsigned int us;
321 size_t pos, checkpoint;
322 char *dst;
323
324 if (len == 0) {
325 smart_str_appendl(buf, "\"\"", 2);
326 return SUCCESS;
327 }
328
329 if (options & PHP_JSON_NUMERIC_CHECK) {
330 double d;
331 int type;
332 zend_long p;
333
334 if ((type = is_numeric_string(s, len, &p, &d, 0)) != 0) {
335 if (type == IS_LONG) {
336 smart_str_append_long(buf, p);
337 return SUCCESS;
338 } else if (type == IS_DOUBLE && php_json_is_valid_double(d)) {
339 php_json_encode_double(buf, d, options);
340 return SUCCESS;
341 }
342 }
343
344 }
345 checkpoint = buf->s ? ZSTR_LEN(buf->s) : 0;
346
347 /* pre-allocate for string length plus 2 quotes */
348 smart_str_alloc(buf, len+2, 0);
349 smart_str_appendc(buf, '"');
350
351 pos = 0;
352
353 do {
354 static const uint32_t charmap[8] = {
355 0xffffffff, 0x500080c4, 0x10000000, 0x00000000,
356 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
357
358 us = (unsigned char)s[pos];
359 if (EXPECTED(!ZEND_BIT_TEST(charmap, us))) {
360 pos++;
361 len--;
362 if (len == 0) {
363 smart_str_appendl(buf, s, pos);
364 break;
365 }
366 } else {
367 if (pos) {
368 smart_str_appendl(buf, s, pos);
369 s += pos;
370 pos = 0;
371 }
372 us = (unsigned char)s[0];
373 if (UNEXPECTED(us >= 0x80)) {
374
375 us = php_next_utf8_char((unsigned char *)s, len, &pos, &status);
376
377 /* check whether UTF8 character is correct */
378 if (UNEXPECTED(status != SUCCESS)) {
379 if (options & PHP_JSON_INVALID_UTF8_IGNORE) {
380 /* ignore invalid UTF8 character */
381 } else if (options & PHP_JSON_INVALID_UTF8_SUBSTITUTE) {
382 /* Use Unicode character 'REPLACEMENT CHARACTER' (U+FFFD) */
383 if (options & PHP_JSON_UNESCAPED_UNICODE) {
384 smart_str_appendl(buf, "\xef\xbf\xbd", 3);
385 } else {
386 smart_str_appendl(buf, "\\ufffd", 6);
387 }
388 } else {
389 ZSTR_LEN(buf->s) = checkpoint;
390 encoder->error_code = PHP_JSON_ERROR_UTF8;
391 if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
392 smart_str_appendl(buf, "null", 4);
393 }
394 return FAILURE;
395 }
396
397 /* Escape U+2028/U+2029 line terminators, UNLESS both
398 JSON_UNESCAPED_UNICODE and
399 JSON_UNESCAPED_LINE_TERMINATORS were provided */
400 } else if ((options & PHP_JSON_UNESCAPED_UNICODE)
401 && ((options & PHP_JSON_UNESCAPED_LINE_TERMINATORS)
402 || us < 0x2028 || us > 0x2029)) {
403 smart_str_appendl(buf, s, pos);
404 } else {
405 /* From http://en.wikipedia.org/wiki/UTF16 */
406 if (us >= 0x10000) {
407 unsigned int next_us;
408
409 us -= 0x10000;
410 next_us = (unsigned short)((us & 0x3ff) | 0xdc00);
411 us = (unsigned short)((us >> 10) | 0xd800);
412 dst = smart_str_extend(buf, 6);
413 dst[0] = '\\';
414 dst[1] = 'u';
415 dst[2] = digits[(us >> 12) & 0xf];
416 dst[3] = digits[(us >> 8) & 0xf];
417 dst[4] = digits[(us >> 4) & 0xf];
418 dst[5] = digits[us & 0xf];
419 us = next_us;
420 }
421 dst = smart_str_extend(buf, 6);
422 dst[0] = '\\';
423 dst[1] = 'u';
424 dst[2] = digits[(us >> 12) & 0xf];
425 dst[3] = digits[(us >> 8) & 0xf];
426 dst[4] = digits[(us >> 4) & 0xf];
427 dst[5] = digits[us & 0xf];
428 }
429 s += pos;
430 len -= pos;
431 pos = 0;
432 } else {
433 s++;
434 switch (us) {
435 case '"':
436 if (options & PHP_JSON_HEX_QUOT) {
437 smart_str_appendl(buf, "\\u0022", 6);
438 } else {
439 smart_str_appendl(buf, "\\\"", 2);
440 }
441 break;
442
443 case '\\':
444 smart_str_appendl(buf, "\\\\", 2);
445 break;
446
447 case '/':
448 if (options & PHP_JSON_UNESCAPED_SLASHES) {
449 smart_str_appendc(buf, '/');
450 } else {
451 smart_str_appendl(buf, "\\/", 2);
452 }
453 break;
454
455 case '\b':
456 smart_str_appendl(buf, "\\b", 2);
457 break;
458
459 case '\f':
460 smart_str_appendl(buf, "\\f", 2);
461 break;
462
463 case '\n':
464 smart_str_appendl(buf, "\\n", 2);
465 break;
466
467 case '\r':
468 smart_str_appendl(buf, "\\r", 2);
469 break;
470
471 case '\t':
472 smart_str_appendl(buf, "\\t", 2);
473 break;
474
475 case '<':
476 if (options & PHP_JSON_HEX_TAG) {
477 smart_str_appendl(buf, "\\u003C", 6);
478 } else {
479 smart_str_appendc(buf, '<');
480 }
481 break;
482
483 case '>':
484 if (options & PHP_JSON_HEX_TAG) {
485 smart_str_appendl(buf, "\\u003E", 6);
486 } else {
487 smart_str_appendc(buf, '>');
488 }
489 break;
490
491 case '&':
492 if (options & PHP_JSON_HEX_AMP) {
493 smart_str_appendl(buf, "\\u0026", 6);
494 } else {
495 smart_str_appendc(buf, '&');
496 }
497 break;
498
499 case '\'':
500 if (options & PHP_JSON_HEX_APOS) {
501 smart_str_appendl(buf, "\\u0027", 6);
502 } else {
503 smart_str_appendc(buf, '\'');
504 }
505 break;
506
507 default:
508 ZEND_ASSERT(us < ' ');
509 dst = smart_str_extend(buf, 6);
510 dst[0] = '\\';
511 dst[1] = 'u';
512 dst[2] = '0';
513 dst[3] = '0';
514 dst[4] = digits[(us >> 4) & 0xf];
515 dst[5] = digits[us & 0xf];
516 break;
517 }
518 len--;
519 }
520 }
521 } while (len);
522
523 smart_str_appendc(buf, '"');
524
525 return SUCCESS;
526 }
527 /* }}} */
528
php_json_encode_serializable_object(smart_str * buf,zval * val,int options,php_json_encoder * encoder)529 static int php_json_encode_serializable_object(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */
530 {
531 zend_class_entry *ce = Z_OBJCE_P(val);
532 HashTable* myht = Z_OBJPROP_P(val);
533 zval retval, fname;
534 int return_code;
535
536 if (myht && GC_IS_RECURSIVE(myht)) {
537 encoder->error_code = PHP_JSON_ERROR_RECURSION;
538 if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
539 smart_str_appendl(buf, "null", 4);
540 }
541 return FAILURE;
542 }
543
544 PHP_JSON_HASH_PROTECT_RECURSION(myht);
545
546 ZVAL_STRING(&fname, "jsonSerialize");
547
548 if (FAILURE == call_user_function(NULL, val, &fname, &retval, 0, NULL) || Z_TYPE(retval) == IS_UNDEF) {
549 if (!EG(exception)) {
550 zend_throw_exception_ex(NULL, 0, "Failed calling %s::jsonSerialize()", ZSTR_VAL(ce->name));
551 }
552 zval_ptr_dtor(&fname);
553
554 if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
555 smart_str_appendl(buf, "null", 4);
556 }
557 PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
558 return FAILURE;
559 }
560
561 if (EG(exception)) {
562 /* Error already raised */
563 zval_ptr_dtor(&retval);
564 zval_ptr_dtor(&fname);
565
566 if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
567 smart_str_appendl(buf, "null", 4);
568 }
569 PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
570 return FAILURE;
571 }
572
573 if ((Z_TYPE(retval) == IS_OBJECT) &&
574 (Z_OBJ(retval) == Z_OBJ_P(val))) {
575 /* Handle the case where jsonSerialize does: return $this; by going straight to encode array */
576 PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
577 return_code = php_json_encode_array(buf, &retval, options, encoder);
578 } else {
579 /* All other types, encode as normal */
580 return_code = php_json_encode_zval(buf, &retval, options, encoder);
581 PHP_JSON_HASH_UNPROTECT_RECURSION(myht);
582 }
583
584 zval_ptr_dtor(&retval);
585 zval_ptr_dtor(&fname);
586
587 return return_code;
588 }
589 /* }}} */
590
php_json_encode_serializable_enum(smart_str * buf,zval * val,int options,php_json_encoder * encoder)591 static int php_json_encode_serializable_enum(smart_str *buf, zval *val, int options, php_json_encoder *encoder)
592 {
593 zend_class_entry *ce = Z_OBJCE_P(val);
594 if (ce->enum_backing_type == IS_UNDEF) {
595 encoder->error_code = PHP_JSON_ERROR_NON_BACKED_ENUM;
596 smart_str_appendc(buf, '0');
597 return FAILURE;
598 }
599
600 zval *value_zv = zend_enum_fetch_case_value(Z_OBJ_P(val));
601 return php_json_encode_zval(buf, value_zv, options, encoder);
602 }
603
php_json_encode_zval(smart_str * buf,zval * val,int options,php_json_encoder * encoder)604 int php_json_encode_zval(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */
605 {
606 again:
607 switch (Z_TYPE_P(val))
608 {
609 case IS_NULL:
610 smart_str_appendl(buf, "null", 4);
611 break;
612
613 case IS_TRUE:
614 smart_str_appendl(buf, "true", 4);
615 break;
616 case IS_FALSE:
617 smart_str_appendl(buf, "false", 5);
618 break;
619
620 case IS_LONG:
621 smart_str_append_long(buf, Z_LVAL_P(val));
622 break;
623
624 case IS_DOUBLE:
625 if (php_json_is_valid_double(Z_DVAL_P(val))) {
626 php_json_encode_double(buf, Z_DVAL_P(val), options);
627 } else {
628 encoder->error_code = PHP_JSON_ERROR_INF_OR_NAN;
629 smart_str_appendc(buf, '0');
630 }
631 break;
632
633 case IS_STRING:
634 return php_json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options, encoder);
635
636 case IS_OBJECT:
637 if (instanceof_function(Z_OBJCE_P(val), php_json_serializable_ce)) {
638 return php_json_encode_serializable_object(buf, val, options, encoder);
639 }
640 if (Z_OBJCE_P(val)->ce_flags & ZEND_ACC_ENUM) {
641 return php_json_encode_serializable_enum(buf, val, options, encoder);
642 }
643 /* fallthrough -- Non-serializable object */
644 ZEND_FALLTHROUGH;
645 case IS_ARRAY: {
646 /* Avoid modifications (and potential freeing) of the array through a reference when a
647 * jsonSerialize() method is invoked. */
648 zval zv;
649 int res;
650 ZVAL_COPY(&zv, val);
651 res = php_json_encode_array(buf, &zv, options, encoder);
652 zval_ptr_dtor_nogc(&zv);
653 return res;
654 }
655
656 case IS_REFERENCE:
657 val = Z_REFVAL_P(val);
658 goto again;
659
660 default:
661 encoder->error_code = PHP_JSON_ERROR_UNSUPPORTED_TYPE;
662 if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) {
663 smart_str_appendl(buf, "null", 4);
664 }
665 return FAILURE;
666 }
667
668 return SUCCESS;
669 }
670 /* }}} */
671