LCOV - code coverage report
Current view: top level - third_party/heimdal/lib/base - json.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 42 870 4.8 %
Date: 2024-04-21 15:09:00 Functions: 6 29 20.7 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2010 Kungliga Tekniska Högskolan
       3             :  * (Royal Institute of Technology, Stockholm, Sweden).
       4             :  * All rights reserved.
       5             :  *
       6             :  * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
       7             :  *
       8             :  * Redistribution and use in source and binary forms, with or without
       9             :  * modification, are permitted provided that the following conditions
      10             :  * are met:
      11             :  *
      12             :  * 1. Redistributions of source code must retain the above copyright
      13             :  *    notice, this list of conditions and the following disclaimer.
      14             :  *
      15             :  * 2. Redistributions in binary form must reproduce the above copyright
      16             :  *    notice, this list of conditions and the following disclaimer in the
      17             :  *    documentation and/or other materials provided with the distribution.
      18             :  *
      19             :  * 3. Neither the name of the Institute nor the names of its contributors
      20             :  *    may be used to endorse or promote products derived from this software
      21             :  *    without specific prior written permission.
      22             :  *
      23             :  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
      24             :  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      25             :  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      26             :  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
      27             :  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      28             :  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      29             :  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      30             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      31             :  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      32             :  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      33             :  * SUCH DAMAGE.
      34             :  */
      35             : 
      36             : #include "baselocl.h"
      37             : #include <ctype.h>
      38             : #include <base64.h>
      39             : 
      40             : #ifndef WIN32
      41             : #include <langinfo.h>
      42             : #endif
      43             : 
      44             : static heim_base_once_t heim_json_once = HEIM_BASE_ONCE_INIT;
      45             : static heim_string_t heim_tid_data_uuid_key = NULL;
      46             : 
      47             : static void
      48          16 : json_init_once(void *arg)
      49             : {
      50          16 :     heim_tid_data_uuid_key = __heim_string_constant("heimdal-type-data-76d7fca2-d0da-4b20-a126-1a10f8a0eae6");
      51          16 : }
      52             : 
      53             : struct twojson {
      54             :     void *ctx;
      55             :     void (*out)(void *, const char *);
      56             :     size_t indent;
      57             :     heim_json_flags_t flags;
      58             :     int ret;
      59             :     int first;
      60             : };
      61             : 
      62             : struct heim_strbuf {
      63             :     char *str;
      64             :     size_t len;
      65             :     size_t alloced;
      66             :     int enomem;
      67             :     heim_json_flags_t flags;
      68             : };
      69             : 
      70             : static int
      71             : base2json(heim_object_t, struct twojson *, int);
      72             : 
      73             : static void
      74           0 : indent(struct twojson *j)
      75             : {
      76           0 :     size_t i = j->indent;
      77           0 :     if (j->flags & HEIM_JSON_F_ONE_LINE)
      78           0 :         return;
      79           0 :     if (j->flags & HEIM_JSON_F_INDENT2)
      80           0 :         while (i--)
      81           0 :             j->out(j->ctx, "  ");
      82           0 :     else if (j->flags & HEIM_JSON_F_INDENT4)
      83           0 :         while (i--)
      84           0 :             j->out(j->ctx, "    ");
      85           0 :     else if (j->flags & HEIM_JSON_F_INDENT8)
      86           0 :         while (i--)
      87           0 :             j->out(j->ctx, "        ");
      88             :     else
      89           0 :         while (i--)
      90           0 :             j->out(j->ctx, "\t");
      91             : }
      92             : 
      93             : static void
      94           0 : array2json(heim_object_t value, void *ctx, int *stop)
      95             : {
      96           0 :     struct twojson *j = ctx;
      97           0 :     if (j->ret)
      98           0 :         return;
      99           0 :     if (j->first) {
     100           0 :         j->first = 0;
     101             :     } else {
     102           0 :         j->out(j->ctx, NULL); /* eat previous '\n' if possible */
     103           0 :         j->out(j->ctx, ",\n");
     104             :     }
     105           0 :     j->ret = base2json(value, j, 0);
     106             : }
     107             : 
     108             : static void
     109           0 : dict2json(heim_object_t key, heim_object_t value, void *ctx)
     110             : {
     111           0 :     struct twojson *j = ctx;
     112           0 :     if (j->ret)
     113           0 :         return;
     114           0 :     if (j->first) {
     115           0 :         j->first = 0;
     116             :     } else {
     117           0 :         j->out(j->ctx, NULL); /* eat previous '\n' if possible */
     118           0 :         j->out(j->ctx, ",\n");
     119             :     }
     120           0 :     j->ret = base2json(key, j, 0);
     121           0 :     if (j->ret)
     122           0 :         return;
     123           0 :     switch (heim_get_tid(value)) {
     124           0 :     case HEIM_TID_ARRAY:
     125             :     case HEIM_TID_DICT:
     126             :     case HEIM_TID_DATA:
     127           0 :         j->out(j->ctx, ":\n");
     128           0 :         j->indent++;
     129           0 :         j->ret = base2json(value, j, 0);
     130           0 :         if (j->ret)
     131           0 :             return;
     132           0 :         j->indent--;
     133           0 :         break;
     134           0 :     default:
     135           0 :         j->out(j->ctx, ": ");
     136           0 :         j->ret = base2json(value, j, 1);
     137           0 :         break;
     138             :     }
     139             : }
     140             : 
     141             : #ifndef WIN32
     142             : static void
     143          16 : init_is_utf8(void *ptr)
     144             : {
     145          16 :     *(int *)ptr = strcasecmp("utf-8", nl_langinfo(CODESET)) == 0;
     146          16 : }
     147             : #endif
     148             : 
     149             : int
     150         679 : heim_locale_is_utf8(void)
     151             : {
     152             : #ifdef WIN32
     153             :     return 0; /* XXX Implement */
     154             : #else
     155           0 :     static int locale_is_utf8 = -1;
     156           0 :     static heim_base_once_t once = HEIM_BASE_ONCE_INIT;
     157             : 
     158         679 :     heim_base_once_f(&once, &locale_is_utf8, init_is_utf8);
     159         679 :     return locale_is_utf8;
     160             : #endif
     161             : }
     162             : 
     163             : static void
     164           0 : out_escaped_bmp(struct twojson *j, const unsigned char *p, int nbytes)
     165             : {
     166           0 :     unsigned char e[sizeof("\\u0000")];
     167           0 :     unsigned codepoint;
     168             : 
     169           0 :     if (nbytes == 2)
     170           0 :         codepoint = ((p[0] & 0x1f) << 6) | (p[1] & 0x3f);
     171           0 :     else if (nbytes == 3)
     172           0 :         codepoint = ((p[0] & 0x0f) << 12) | ((p[1] & 0x3f) << 6) | (p[2] & 0x3f);
     173             :     else
     174           0 :         abort();
     175           0 :     e[0]  = '\\';
     176           0 :     e[1]  = 'u';
     177           0 :     e[2]  = codepoint >> 12;
     178           0 :     e[2] += (e[2] < 10) ? '0' : ('A' - 10);
     179           0 :     e[3]  = (codepoint >> 8) & 0x0f;
     180           0 :     e[3] += (e[3] < 10) ? '0' : ('A' - 10);
     181           0 :     e[4]  = (codepoint >> 4) & 0x0f;
     182           0 :     e[4] += (e[4] < 10) ? '0' : ('A' - 10);
     183           0 :     e[5]  =  codepoint       & 0x0f;
     184           0 :     e[5] += (e[5] < 10) ? '0' : ('A' - 10);
     185           0 :     e[6]  = '\0';
     186           0 :     j->out(j->ctx, (char *)e);
     187           0 : }
     188             : 
     189             : static int
     190         679 : base2json(heim_object_t obj, struct twojson *j, int skip_indent)
     191             : {
     192           0 :     heim_tid_t type;
     193         679 :     int first = 0;
     194             : 
     195         679 :     if (obj == NULL) {
     196           0 :         if (j->flags & HEIM_JSON_F_CNULL2JSNULL) {
     197           0 :             obj = heim_null_create();
     198           0 :         } else if (j->flags & HEIM_JSON_F_NO_C_NULL) {
     199           0 :             return EINVAL;
     200             :         } else {
     201           0 :             indent(j);
     202           0 :             j->out(j->ctx, "<NULL>\n"); /* This is NOT valid JSON! */
     203           0 :             return 0;
     204             :         }
     205             :     }
     206             : 
     207         679 :     type = heim_get_tid(obj);
     208         679 :     switch (type) {
     209           0 :     case HEIM_TID_ARRAY:
     210           0 :         indent(j);
     211           0 :         j->out(j->ctx, "[\n");
     212           0 :         j->indent++;
     213           0 :         first = j->first;
     214           0 :         j->first = 1;
     215           0 :         heim_array_iterate_f(obj, j, array2json);
     216           0 :         j->indent--;
     217           0 :         if (!j->first)
     218           0 :             j->out(j->ctx, "\n");
     219           0 :         indent(j);
     220           0 :         j->out(j->ctx, "]\n");
     221           0 :         j->first = first;
     222           0 :         break;
     223             : 
     224           0 :     case HEIM_TID_DICT:
     225           0 :         indent(j);
     226           0 :         j->out(j->ctx, "{\n");
     227           0 :         j->indent++;
     228           0 :         first = j->first;
     229           0 :         j->first = 1;
     230           0 :         heim_dict_iterate_f(obj, j, dict2json);
     231           0 :         j->indent--;
     232           0 :         if (!j->first)
     233           0 :             j->out(j->ctx, "\n");
     234           0 :         indent(j);
     235           0 :         j->out(j->ctx, "}\n");
     236           0 :         j->first = first;
     237           0 :         break;
     238             : 
     239           0 :     case HEIM_TID_STRING: {
     240           0 :         const unsigned char *s = (const unsigned char *)heim_string_get_utf8(obj);
     241           0 :         const unsigned char *p;
     242           0 :         unsigned int c, cp, ctop, cbot;
     243           0 :         char e[sizeof("\\u0123\\u3210")];
     244           0 :         int good;
     245           0 :         size_t i;
     246             : 
     247           0 :         if (!skip_indent)
     248           0 :             indent(j);
     249           0 :         j->out(j->ctx, "\"");
     250           0 :         for (p = s; (c = *p); p++) {
     251           0 :             switch (c) {
     252             :             /* ASCII control characters w/ C-like escapes */
     253           0 :             case '\b': j->out(j->ctx, "\\b");  continue;
     254           0 :             case '\f': j->out(j->ctx, "\\f");  continue;
     255           0 :             case '\n': j->out(j->ctx, "\\n");  continue;
     256           0 :             case '\r': j->out(j->ctx, "\\r");  continue;
     257           0 :             case '\t': j->out(j->ctx, "\\t");  continue;
     258             :             /* Other must-escape non-control ASCII characters */
     259           0 :             case '"':  j->out(j->ctx, "\\\""); continue;
     260           0 :             case '\\': j->out(j->ctx, "\\\\"); continue;
     261           0 :             default: break;
     262             :             }
     263             : 
     264             :             /*
     265             :              * JSON string encoding is... complex.
     266             :              *
     267             :              * Invalid UTF-8 w/  HEIM_JSON_F_STRICT_STRINGS set -> return 1
     268             :              *
     269             :              * Invalid UTF-8 w/o HEIM_JSON_F_STRICT_STRINGS set -> pass
     270             :              * through, a sort of Heimdal WTF-8, but not _the_ WTF-8.
     271             :              */
     272           0 :             if (c < 0x20) {
     273             :                 /* ASCII control character w/o C-like escape */
     274           0 :                 e[0] = '\\';
     275           0 :                 e[1] = 'u';
     276           0 :                 e[2] = '0';
     277           0 :                 e[3] = '0';
     278           0 :                 e[4] = "0123456789ABCDEF"[c>>4];
     279           0 :                 e[5] = "0123456789ABCDEF"[c & 0x0f];
     280           0 :                 e[6] = '\0';
     281           0 :                 j->out(j->ctx, e);
     282           0 :                 continue;
     283             :             }
     284           0 :             if (c < 0x80) {
     285             :                 /* ASCII */
     286           0 :                 e[0] = c;
     287           0 :                 e[1] = '\0';
     288           0 :                 j->out(j->ctx, e);
     289           0 :                 continue;
     290             :             }
     291           0 :             if ((c & 0xc0) == 0x80) {
     292             :                 /* UTF-8 bare non-leading byte */
     293           0 :                 if (!(j->flags & HEIM_JSON_F_STRICT_STRINGS)) {
     294           0 :                     e[0] = c;
     295           0 :                     e[1] = '\0';
     296           0 :                     j->out(j->ctx, e);
     297           0 :                     continue;
     298             :                 }
     299           0 :                 return 1;
     300             :             }
     301           0 :             if ((c & 0xe0) == 0xc0) {
     302             :                 /* UTF-8 leading byte of two-byte sequence */
     303           0 :                 good = 1;
     304           0 :                 for (i = 1; i < 2 && good && p[i]; i++) {
     305           0 :                     if ((p[i] & 0xc0) != 0x80)
     306           0 :                         good = 0;
     307             :                 }
     308           0 :                 if (i != 2)
     309           0 :                     good = 0;
     310           0 :                 if (!good && !(j->flags & HEIM_JSON_F_STRICT_STRINGS)) {
     311           0 :                     e[0] = c;
     312           0 :                     e[1] = '\0';
     313           0 :                     j->out(j->ctx, e);
     314           0 :                     continue;
     315           0 :                 } else if (!good) {
     316           0 :                     return 1;
     317             :                 }
     318           0 :                 if (j->flags & HEIM_JSON_F_ESCAPE_NON_ASCII) {
     319           0 :                     out_escaped_bmp(j, p, 2);
     320           0 :                     p += 1;
     321           0 :                     continue;
     322             :                 }
     323           0 :                 e[0] = c;
     324           0 :                 e[1] = p[1];
     325           0 :                 e[2] = '\0';
     326           0 :                 j->out(j->ctx, e);
     327           0 :                 p += 1;
     328           0 :                 continue;
     329             :             }
     330           0 :             if ((c & 0xf0) == 0xe0) {
     331             :                 /* UTF-8 leading byte of three-byte sequence */
     332           0 :                 good = 1;
     333           0 :                 for (i = 1; i < 3 && good && p[i]; i++) {
     334           0 :                     if ((p[i] & 0xc0) != 0x80)
     335           0 :                         good = 0;
     336             :                 }
     337           0 :                 if (i != 3)
     338           0 :                     good = 0;
     339           0 :                 if (!good && !(j->flags & HEIM_JSON_F_STRICT_STRINGS)) {
     340           0 :                     e[0] = c;
     341           0 :                     e[1] = '\0';
     342           0 :                     j->out(j->ctx, e);
     343           0 :                     continue;
     344           0 :                 } else if (!good) {
     345           0 :                     return 1;
     346             :                 }
     347           0 :                 if (j->flags & HEIM_JSON_F_ESCAPE_NON_ASCII) {
     348           0 :                     out_escaped_bmp(j, p, 3);
     349           0 :                     p += 2;
     350           0 :                     continue;
     351             :                 }
     352           0 :                 e[0] = c;
     353           0 :                 e[1] = p[1];
     354           0 :                 e[2] = p[2];
     355           0 :                 e[3] = '\0';
     356           0 :                 j->out(j->ctx, e);
     357           0 :                 p += 2;
     358           0 :                 continue;
     359             :             }
     360             : 
     361           0 :             if (c > 0xf7) {
     362             :                 /* Invalid UTF-8 leading byte */
     363           0 :                 if (!(j->flags & HEIM_JSON_F_STRICT_STRINGS)) {
     364           0 :                     e[0] = c;
     365           0 :                     e[1] = '\0';
     366           0 :                     j->out(j->ctx, e);
     367           0 :                     continue;
     368             :                 }
     369           0 :                 return 1;
     370             :             }
     371             : 
     372             :             /*
     373             :              * A codepoint > U+FFFF, needs encoding a la UTF-16 surrogate
     374             :              * pair because JSON takes after JS which uses UTF-16.  Ugly.
     375             :              */
     376           0 :             cp = c & 0x7;
     377           0 :             good = 1;
     378           0 :             for (i = 1; i < 4 && good && p[i]; i++) {
     379           0 :                 if ((p[i] & 0xc0) == 0x80)
     380           0 :                     cp = (cp << 6) | (p[i] & 0x3f);
     381             :                 else
     382           0 :                     good = 0;
     383             :             }
     384           0 :             if (i != 4)
     385           0 :                 good = 0;
     386           0 :             if (!good && !(j->flags & HEIM_JSON_F_STRICT_STRINGS)) {
     387           0 :                 e[0] = c;
     388           0 :                 e[1] = '\0';
     389           0 :                 j->out(j->ctx, e);
     390           0 :                 continue;
     391           0 :             } else if (!good) {
     392           0 :                 return 1;
     393             :             }
     394           0 :             p += 3;
     395             : 
     396           0 :             cp -= 0x10000;
     397           0 :             ctop = 0xD800 + (cp >>   10);
     398           0 :             cbot = 0xDC00 + (cp & 0x3ff);
     399             : 
     400           0 :             e[0 ] = '\\';
     401           0 :             e[1 ] = 'u';
     402           0 :             e[2 ] = "0123456789ABCDEF"[(ctop         ) >> 12];
     403           0 :             e[3 ] = "0123456789ABCDEF"[(ctop & 0x0f00) >>  8];
     404           0 :             e[4 ] = "0123456789ABCDEF"[(ctop & 0x00f0) >>  4];
     405           0 :             e[5 ] = "0123456789ABCDEF"[(ctop & 0x000f)      ];
     406           0 :             e[6 ] = '\\';
     407           0 :             e[7 ] = 'u';
     408           0 :             e[8 ] = "0123456789ABCDEF"[(cbot         ) >> 12];
     409           0 :             e[9 ] = "0123456789ABCDEF"[(cbot & 0x0f00) >>  8];
     410           0 :             e[10] = "0123456789ABCDEF"[(cbot & 0x00f0) >>  4];
     411           0 :             e[11] = "0123456789ABCDEF"[(cbot & 0x000f)      ];
     412           0 :             e[12] = '\0';
     413           0 :             j->out(j->ctx, e);
     414           0 :             continue;
     415             :         }
     416           0 :         j->out(j->ctx, "\"");
     417           0 :         break;
     418             :     }
     419             : 
     420           0 :     case HEIM_TID_DATA: {
     421           0 :         heim_dict_t d;
     422           0 :         heim_string_t v;
     423           0 :         const heim_octet_string *data;
     424           0 :         char *b64 = NULL;
     425           0 :         int ret;
     426             : 
     427           0 :         if (j->flags & HEIM_JSON_F_NO_DATA)
     428           0 :             return EINVAL; /* JSON doesn't do binary */
     429             : 
     430           0 :         data = heim_data_get_data(obj);
     431           0 :         ret = rk_base64_encode(data->data, data->length, &b64);
     432           0 :         if (ret < 0 || b64 == NULL)
     433           0 :             return ENOMEM;
     434             : 
     435           0 :         if (j->flags & HEIM_JSON_F_NO_DATA_DICT) {
     436           0 :             indent(j);
     437           0 :             j->out(j->ctx, "\"");
     438           0 :             j->out(j->ctx, b64); /* base64-encode; hope there's no aliasing */
     439           0 :             j->out(j->ctx, "\"");
     440           0 :             free(b64);
     441             :         } else {
     442             :             /*
     443             :              * JSON has no way to represent binary data, therefore the
     444             :              * following is a Heimdal-specific convention.
     445             :              *
     446             :              * We encode binary data as a dict with a single very magic
     447             :              * key with a base64-encoded value.  The magic key includes
     448             :              * a uuid, so we're not likely to alias accidentally.
     449             :              */
     450           0 :             d = heim_dict_create(2);
     451           0 :             if (d == NULL) {
     452           0 :                 free(b64);
     453           0 :                 return ENOMEM;
     454             :             }
     455           0 :             v = heim_string_ref_create(b64, free);
     456           0 :             if (v == NULL) {
     457           0 :                 free(b64);
     458           0 :                 heim_release(d);
     459           0 :                 return ENOMEM;
     460             :             }
     461           0 :             ret = heim_dict_set_value(d, heim_tid_data_uuid_key, v);
     462           0 :             heim_release(v);
     463           0 :             if (ret) {
     464           0 :                 heim_release(d);
     465           0 :                 return ENOMEM;
     466             :             }
     467           0 :             ret = base2json(d, j, 0);
     468           0 :             heim_release(d);
     469           0 :             if (ret)
     470           0 :                 return ret;
     471             :         }
     472           0 :         break;
     473             :     }
     474             : 
     475           0 :     case HEIM_TID_NUMBER: {
     476           0 :         char num[32];
     477           0 :         if (!skip_indent)
     478           0 :             indent(j);
     479           0 :         snprintf(num, sizeof (num), "%d", heim_number_get_int(obj));
     480           0 :         j->out(j->ctx, num);
     481           0 :         break;
     482             :     }
     483           0 :     case HEIM_TID_NULL:
     484           0 :         if (!skip_indent)
     485           0 :             indent(j);
     486           0 :         j->out(j->ctx, "null");
     487           0 :         break;
     488           0 :     case HEIM_TID_BOOL:
     489           0 :         if (!skip_indent)
     490           0 :             indent(j);
     491           0 :         j->out(j->ctx, heim_bool_val(obj) ? "true" : "false");
     492           0 :         break;
     493         679 :     default:
     494         679 :         return 1;
     495             :     }
     496           0 :     return 0;
     497             : }
     498             : 
     499             : static int
     500         679 : heim_base2json(heim_object_t obj, void *ctx, heim_json_flags_t flags,
     501             :                void (*out)(void *, const char *))
     502             : {
     503           0 :     struct twojson j;
     504             : 
     505         679 :     heim_base_once_f(&heim_json_once, NULL, json_init_once);
     506             : 
     507         679 :     j.indent = 0;
     508         679 :     j.ctx = ctx;
     509         679 :     j.out = out;
     510         679 :     j.flags = flags;
     511         679 :     j.ret = 0;
     512         679 :     j.first = 1;
     513             : 
     514        1358 :     if (!(flags & HEIM_JSON_F_NO_ESCAPE_NON_ASCII) &&
     515         679 :         !heim_locale_is_utf8())
     516         679 :         j.flags |= HEIM_JSON_F_ESCAPE_NON_ASCII;
     517             : 
     518         679 :     return base2json(obj, &j, 0);
     519             : }
     520             : 
     521             : 
     522             : /*
     523             :  *
     524             :  */
     525             : 
     526             : struct parse_ctx {
     527             :     unsigned long lineno;
     528             :     const uint8_t *p;
     529             :     const uint8_t *pstart;
     530             :     const uint8_t *pend;
     531             :     heim_error_t error;
     532             :     size_t depth;
     533             :     heim_json_flags_t flags;
     534             : };
     535             : 
     536             : 
     537             : static heim_object_t
     538             : parse_value(struct parse_ctx *ctx);
     539             : 
     540             : /*
     541             :  * This function eats whitespace, but, critically, it also succeeds
     542             :  * only if there's anything left to parse.
     543             :  */
     544             : static int
     545           0 : white_spaces(struct parse_ctx *ctx)
     546             : {
     547           0 :     while (ctx->p < ctx->pend) {
     548           0 :         uint8_t c = *ctx->p;
     549           0 :         if (c == ' ' || c == '\t' || c == '\r') {
     550             : 
     551           0 :         } else if (c == '\n') {
     552           0 :             ctx->lineno++;
     553             :         } else
     554           0 :             return 0;
     555           0 :         (ctx->p)++;
     556             :     }
     557           0 :     return -1;
     558             : }
     559             : 
     560             : static int
     561           0 : is_number(uint8_t n)
     562             : {
     563           0 :     return ('0' <= n && n <= '9');
     564             : }
     565             : 
     566             : static heim_number_t
     567           0 : parse_number(struct parse_ctx *ctx)
     568             : {
     569           0 :     int number = 0, neg = 1;
     570             : 
     571           0 :     if (ctx->p >= ctx->pend)
     572           0 :         return NULL;
     573             : 
     574           0 :     if (*ctx->p == '-') {
     575           0 :         if (ctx->p + 1 >= ctx->pend)
     576           0 :             return NULL;
     577           0 :         neg = -1;
     578           0 :         ctx->p += 1;
     579             :     }
     580             : 
     581           0 :     while (ctx->p < ctx->pend) {
     582           0 :         if (is_number(*ctx->p)) {
     583           0 :             number = (number * 10) + (*ctx->p - '0');
     584             :         } else {
     585           0 :             break;
     586             :         }
     587           0 :         ctx->p += 1;
     588             :     }
     589             : 
     590           0 :     return heim_number_create(number * neg);
     591             : }
     592             : 
     593             : /*
     594             :  * Read 4 hex digits from ctx->p.
     595             :  *
     596             :  * If we don't have enough, rewind ctx->p and return -1 .
     597             :  */
     598             : static int
     599           0 : unescape_unicode(struct parse_ctx *ctx)
     600             : {
     601           0 :     int c = 0;
     602           0 :     int i;
     603             : 
     604           0 :     for (i = 0; i < 4 && ctx->p < ctx->pend; i++, ctx->p++) {
     605           0 :         if (*ctx->p >= '0' && *ctx->p <= '9') {
     606           0 :             c = (c << 4) + (*ctx->p - '0');
     607           0 :         } else if (*ctx->p >= 'A' && *ctx->p <= 'F') {
     608           0 :             c = (c << 4) + (10 + *ctx->p - 'A');
     609           0 :         } else if (*ctx->p >= 'a' && *ctx->p <= 'f') {
     610           0 :             c = (c << 4) + (10 + *ctx->p - 'a');
     611             :         } else {
     612           0 :             ctx->p -= i;
     613           0 :             return -1;
     614             :         }
     615             :     }
     616           0 :     return c;
     617             : }
     618             : 
     619             : static int
     620           0 : encode_utf8(struct parse_ctx *ctx, char **pp, char *pend, int c)
     621             : {
     622           0 :     char *p = *pp;
     623             : 
     624           0 :     if (c < 0x80) {
     625             :         /* ASCII */
     626           0 :         if (p >= pend) return 0;
     627           0 :         *(p++) = c;
     628           0 :         *pp = p;
     629           0 :         return 1;
     630             :     }
     631           0 :     if (c < 0x800) {
     632             :         /* 2 code unit UTF-8 sequence */
     633           0 :         if (p >= pend) return 0;
     634           0 :         *(p++) = 0xc0 | ((c >>  6)       );
     635           0 :         if (p == pend) return 0;
     636           0 :         *(p++) = 0x80 | ((c      ) & 0x3f);
     637           0 :         *pp = p;
     638           0 :         return 1;
     639             :     }
     640           0 :     if (c < 0x10000) {
     641             :         /* 3 code unit UTF-8 sequence */
     642           0 :         if (p >= pend) return 0;
     643           0 :         *(p++) = 0xe0 | ((c >> 12)       );
     644           0 :         if (p == pend) return 0;
     645           0 :         *(p++) = 0x80 | ((c >>  6) & 0x3f);
     646           0 :         if (p == pend) return 0;
     647           0 :         *(p++) = 0x80 | ((c)       & 0x3f);
     648           0 :         *pp = p;
     649           0 :         return 1;
     650             :     }
     651           0 :     if (c < 0x110000) {
     652             :         /* 4 code unit UTF-8 sequence */
     653           0 :         if (p >= pend) return 0;
     654           0 :         *(p++) = 0xf0 | ((c >> 18)       );
     655           0 :         if (p == pend) return 0;
     656           0 :         *(p++) = 0x80 | ((c >> 12) & 0x3f);
     657           0 :         if (p == pend) return 0;
     658           0 :         *(p++) = 0x80 | ((c >>  6) & 0x3f);
     659           0 :         if (p == pend) return 0;
     660           0 :         *(p++) = 0x80 | ((c)       & 0x3f);
     661           0 :         *pp = p;
     662           0 :         return 1;
     663             :     }
     664           0 :     return 0;
     665             : }
     666             : 
     667             : static heim_string_t
     668           0 : parse_string_error(struct parse_ctx *ctx,
     669             :                    char *freeme,
     670             :                    const char *msg)
     671             : {
     672           0 :     free(freeme);
     673           0 :     ctx->error = heim_error_create(EINVAL, "%s at %lu", msg, ctx->lineno);
     674           0 :     return NULL;
     675             : }
     676             : 
     677             : static heim_string_t
     678           0 : parse_string(struct parse_ctx *ctx)
     679             : {
     680           0 :     const uint8_t *start;
     681           0 :     heim_object_t o;
     682           0 :     size_t alloc_len = 0;
     683           0 :     size_t need = 0;
     684           0 :     char *p0, *p, *pend;
     685           0 :     int strict = ctx->flags & HEIM_JSON_F_STRICT_STRINGS;
     686           0 :     int binary = 0;
     687             : 
     688           0 :     if (*ctx->p != '"')
     689           0 :         return parse_string_error(ctx, NULL,
     690             :                                   "Expected a JSON string but found "
     691             :                                   "something else");
     692           0 :     start = ++(ctx->p);
     693             : 
     694             :     /* Estimate how many bytes we need to allocate */
     695           0 :     p0 = p = pend = NULL;
     696           0 :     for (need = 1; ctx->p < ctx->pend; ctx->p++) {
     697           0 :         need++;
     698           0 :         if (*ctx->p == '\\')
     699           0 :             ctx->p++;
     700           0 :         else if (*ctx->p == '"')
     701           0 :             break;
     702             :     }
     703           0 :     if (ctx->p == ctx->pend)
     704           0 :         return parse_string_error(ctx, NULL, "Unterminated JSON string");
     705             : 
     706           0 :     ctx->p = start;
     707           0 :     while (ctx->p < ctx->pend) {
     708           0 :         const unsigned char *p_save;
     709           0 :         int32_t ctop, cbot;
     710             : 
     711           0 :         if (*ctx->p == '"') {
     712           0 :             ctx->p++;
     713           0 :             break;
     714             :         }
     715             : 
     716             :         /* Allocate or resize our output buffer if need be */
     717           0 :         if (need || p == pend) {
     718           0 :             char *tmp;
     719             : 
     720             :             /*
     721             :              * Work out how far p is into p0 to re-esablish p after
     722             :              * the realloc()
     723             :              */
     724           0 :             size_t p0_to_p_len = (p - p0);
     725             : 
     726           0 :             tmp = realloc(p0, alloc_len + need + 5 /* slop? */);
     727             : 
     728           0 :             if (tmp == NULL) {
     729           0 :                 ctx->error = heim_error_create_enomem();
     730           0 :                 free(p0);
     731           0 :                 return NULL;
     732             :             }
     733           0 :             alloc_len += need + 5;
     734             : 
     735             :             /*
     736             :              * We have two pointers, p and p0, we want to keep them
     737             :              * pointing into the same memory after the realloc()
     738             :              */
     739           0 :             p = tmp + p0_to_p_len;
     740           0 :             p0 = tmp;
     741           0 :             pend = p0 + alloc_len;
     742             : 
     743           0 :             need = 0;
     744             :         }
     745             : 
     746           0 :         if (*ctx->p != '\\') {
     747           0 :             unsigned char c = *ctx->p;
     748             : 
     749             :             /*
     750             :              * Not backslashed -> consume now.
     751             :              *
     752             :              * NOTE: All cases in this block must continue or return w/ error.
     753             :              */
     754             : 
     755             :             /* Check for unescaped ASCII control characters */
     756           0 :             if (c == '\n') {
     757           0 :                 if (strict)
     758           0 :                     return parse_string_error(ctx, p0,
     759             :                                               "Unescaped newline in JSON string");
     760             :                 /* Count the newline but don't add it to the decoding */
     761           0 :                 ctx->lineno++;
     762           0 :             } else if (strict && *ctx->p <= 0x1f) {
     763           0 :                 return parse_string_error(ctx, p0, "Unescaped ASCII control character");
     764           0 :             } else if (c == 0) {
     765           0 :                 binary = 1;
     766             :             }
     767           0 :             if (!strict || c < 0x80) {
     768             :                 /* ASCII, or not strict -> no need to validate */
     769           0 :                 *(p++) = c;
     770           0 :                 ctx->p++;
     771           0 :                 continue;
     772             :             }
     773             : 
     774             :             /*
     775             :              * Being strict for parsing means we want to detect malformed UTF-8
     776             :              * sequences.
     777             :              *
     778             :              * If not strict then we just go on below and add to `p' whatever
     779             :              * bytes we find in `ctx->p' as we find them.
     780             :              *
     781             :              * For each two-byte sequence we need one more byte in `p[]'.  For
     782             :              * each three-byte sequence we need two more bytes in `p[]'.
     783             :              *
     784             :              * Setting `need' and looping will cause `p0' to be grown.
     785             :              *
     786             :              * NOTE: All cases in this block must continue or return w/ error.
     787             :              */
     788           0 :             if ((c & 0xe0) == 0xc0) {
     789             :                 /* Two-byte UTF-8 encoding */
     790           0 :                 if (pend - p < 2) {
     791           0 :                     need = 2;
     792           0 :                     continue; /* realloc p0 */
     793             :                 }
     794             : 
     795           0 :                 *(p++) = c;
     796           0 :                 ctx->p++;
     797           0 :                 if (ctx->p == ctx->pend)
     798           0 :                     return parse_string_error(ctx, p0, "Truncated UTF-8");
     799           0 :                 c = *(ctx->p++);
     800           0 :                 if ((c & 0xc0) != 0x80)
     801           0 :                     return parse_string_error(ctx, p0, "Truncated UTF-8");
     802           0 :                 *(p++) = c;
     803           0 :                 continue;
     804             :             }
     805           0 :             if ((c & 0xf0) == 0xe0) {
     806             :                 /* Three-byte UTF-8 encoding */
     807           0 :                 if (pend - p < 3) {
     808           0 :                     need = 3;
     809           0 :                     continue; /* realloc p0 */
     810             :                 }
     811             : 
     812           0 :                 *(p++) = c;
     813           0 :                 ctx->p++;
     814           0 :                 if (ctx->p == ctx->pend)
     815           0 :                     return parse_string_error(ctx, p0, "Truncated UTF-8");
     816           0 :                 c = *(ctx->p++);
     817           0 :                 if ((c & 0xc0) != 0x80)
     818           0 :                     return parse_string_error(ctx, p0, "Truncated UTF-8");
     819           0 :                 *(p++) = c;
     820           0 :                 c = *(ctx->p++);
     821           0 :                 if ((c & 0xc0) != 0x80)
     822           0 :                     return parse_string_error(ctx, p0, "Truncated UTF-8");
     823           0 :                 *(p++) = c;
     824           0 :                 continue;
     825             :             }
     826           0 :             if ((c & 0xf8) == 0xf0)
     827           0 :                 return parse_string_error(ctx, p0, "UTF-8 sequence not "
     828             :                                           "encoded as escaped UTF-16");
     829           0 :             if ((c & 0xc0) == 0x80)
     830           0 :                 return parse_string_error(ctx, p0,
     831             :                                           "Invalid UTF-8 "
     832             :                                           "(bare continuation code unit)");
     833             : 
     834           0 :             return parse_string_error(ctx, p0, "Not UTF-8");
     835             :         }
     836             : 
     837             :         /* Backslash-quoted character */
     838           0 :         ctx->p++;
     839           0 :         if (ctx->p == ctx->pend) {
     840           0 :             ctx->error =
     841           0 :                 heim_error_create(EINVAL,
     842             :                                   "Unterminated JSON string at line %lu",
     843             :                                   ctx->lineno);
     844           0 :             free(p0);
     845           0 :             return NULL;
     846             :         }
     847           0 :         switch (*ctx->p) {
     848             :         /* Simple escapes */
     849           0 :         case  'b': *(p++) = '\b'; ctx->p++; continue;
     850           0 :         case  'f': *(p++) = '\f'; ctx->p++; continue;
     851           0 :         case  'n': *(p++) = '\n'; ctx->p++; continue;
     852           0 :         case  'r': *(p++) = '\r'; ctx->p++; continue;
     853           0 :         case  't': *(p++) = '\t'; ctx->p++; continue;
     854           0 :         case  '"': *(p++) = '"';  ctx->p++; continue;
     855           0 :         case '\\': *(p++) = '\\'; ctx->p++; continue;
     856             :         /* Escaped Unicode handled below */
     857           0 :         case  'u':
     858             :             /*
     859             :              * Worst case for !strict we need 11 bytes for a truncated non-BMP
     860             :              * codepoint escape.  Call it 12.
     861             :              */
     862           0 :             if (strict)
     863           0 :                 need = 4;
     864             :             else
     865           0 :                 need = 12;
     866           0 :             if (pend - p < need) {
     867             :                 /* Go back to the backslash, realloc, try again */
     868           0 :                 ctx->p--;
     869           0 :                 continue;
     870             :             }
     871             : 
     872           0 :             need = 0;
     873           0 :             ctx->p++;
     874           0 :             break;
     875           0 :         default:
     876           0 :             if (!strict) {
     877           0 :                 *(p++) = *ctx->p;
     878           0 :                 ctx->p++;
     879           0 :                 continue;
     880             :             }
     881           0 :             ctx->error =
     882           0 :                 heim_error_create(EINVAL,
     883             :                                   "Invalid backslash escape at line %lu",
     884             :                                   ctx->lineno);
     885           0 :             free(p0);
     886           0 :             return NULL;
     887             :         }
     888             : 
     889             :         /* Unicode code point */
     890           0 :         if (pend - p < 12) {
     891           0 :             need = 12;
     892           0 :             ctx->p -= 2; /* for "\\u" */
     893           0 :             continue; /* This will cause p0 to be realloc'ed */
     894             :         }
     895           0 :         p_save = ctx->p;
     896           0 :         cbot = -3;
     897           0 :         ctop = unescape_unicode(ctx);
     898           0 :         if (ctop == -1 && strict)
     899           0 :             return parse_string_error(ctx, p0, "Invalid escaped Unicode");
     900           0 :         if (ctop == -1) {
     901             :             /*
     902             :              * Not strict; tolerate bad input.
     903             :              *
     904             :              * Output "\\u" and then loop to treat what we expected to be four
     905             :              * digits as if they were not part of an escaped Unicode codepoint.
     906             :              */
     907           0 :             ctx->p = p_save;
     908           0 :             if (p < pend)
     909           0 :                 *(p++) = '\\';
     910           0 :             if (p < pend)
     911           0 :                 *(p++) = 'u';
     912           0 :             continue;
     913             :         }
     914           0 :         if (ctop == 0) {
     915           0 :             *(p++) = '\0';
     916           0 :             binary = 1;
     917           0 :             continue;
     918             :         }
     919           0 :         if (ctop < 0xd800) {
     920           0 :             if (!encode_utf8(ctx, &p, pend, ctop))
     921           0 :                 return parse_string_error(ctx, p0,
     922             :                                           "Internal JSON string parse error");
     923           0 :             continue;
     924             :         }
     925             : 
     926             :         /*
     927             :          * We parsed the top escaped codepoint of a surrogate pair encoding
     928             :          * of a non-BMP Unicode codepoint.  What follows must be another
     929             :          * escaped codepoint.
     930             :          */
     931           0 :         if (ctx->p < ctx->pend && ctx->p[0] == '\\')
     932           0 :             ctx->p++;
     933             :         else
     934           0 :             ctop = -1;
     935           0 :         if (ctop > -1 && ctx->p < ctx->pend && ctx->p[0] == 'u')
     936           0 :             ctx->p++;
     937             :         else
     938           0 :             ctop = -1;
     939           0 :         if (ctop > -1) {
     940             :             /* Parse the hex digits of the bottom half of the surrogate pair */
     941           0 :             cbot = unescape_unicode(ctx);
     942           0 :             if (cbot == -1 || cbot < 0xdc00)
     943           0 :                 ctop = -1;
     944             :         }
     945           0 :         if (ctop == -1) {
     946           0 :             if (strict)
     947           0 :                 return parse_string_error(ctx, p0,
     948             :                                           "Invalid surrogate pair");
     949             : 
     950             :             /*
     951             :              * Output "\\u", rewind, output the digits of `ctop'.
     952             :              *
     953             :              * When we get to what should have been the bottom half of the
     954             :              * pair we'll necessarily fail to parse it as a normal escaped
     955             :              * Unicode codepoint, and once again, rewind and output its digits.
     956             :              */
     957           0 :             if (p < pend)
     958           0 :                 *(p++) = '\\';
     959           0 :             if (p < pend)
     960           0 :                 *(p++) = 'u';
     961           0 :             ctx->p = p_save;
     962           0 :             continue;
     963             :         }
     964             : 
     965             :         /* Finally decode the surrogate pair then encode as UTF-8 */
     966           0 :         ctop -= 0xd800;
     967           0 :         cbot -= 0xdc00;
     968           0 :         if (!encode_utf8(ctx, &p, pend, 0x10000 + ((ctop << 10) | (cbot & 0x3ff))))
     969           0 :             return parse_string_error(ctx, p0,
     970             :                                       "Internal JSON string parse error");
     971             :     }
     972             : 
     973           0 :     if (p0 == NULL)
     974           0 :         return heim_string_create("");
     975             : 
     976             :     /* NUL-terminate for rk_base64_decode() and plain paranoia */
     977           0 :     if (p0 != NULL && p == pend) {
     978             :         /*
     979             :          * Work out how far p is into p0 to re-establish p after
     980             :          * the realloc()
     981             :          */
     982           0 :         size_t p0_to_pend_len = (pend - p0);
     983           0 :         char *tmp = realloc(p0, 1 + p0_to_pend_len);
     984             : 
     985           0 :         if (tmp == NULL) {
     986           0 :             ctx->error = heim_error_create_enomem();
     987           0 :             free(p0);
     988           0 :             return NULL;
     989             :         }
     990             :         /*
     991             :          * We have three pointers, p, pend (which are the same)
     992             :          * and p0, we want to keep them pointing into the same
     993             :          * memory after the realloc()
     994             :          */
     995           0 :         p = tmp + p0_to_pend_len;
     996             : 
     997           0 :         pend = p + 1;
     998           0 :         p0 = tmp;
     999             :     }
    1000           0 :     *(p++) = '\0';
    1001             : 
    1002             :     /* If there's embedded NULs, it's not a C string */
    1003           0 :     if (binary) {
    1004           0 :         o = heim_data_ref_create(p0, (p - 1) - p0, free);
    1005           0 :         return o;
    1006             :     }
    1007             : 
    1008             :     /* Sadly this will copy `p0' */
    1009           0 :     o = heim_string_create_with_bytes(p0, p - p0);
    1010           0 :     free(p0);
    1011           0 :     return o;
    1012             : }
    1013             : 
    1014             : static int
    1015           0 : parse_pair(heim_dict_t dict, struct parse_ctx *ctx)
    1016             : {
    1017           0 :     heim_string_t key;
    1018           0 :     heim_object_t value;
    1019             : 
    1020           0 :     if (white_spaces(ctx))
    1021           0 :         return -1;
    1022             : 
    1023           0 :     if (*ctx->p == '}') {
    1024           0 :         ctx->p++;
    1025           0 :         return 0;
    1026             :     }
    1027             : 
    1028           0 :     if (ctx->flags & HEIM_JSON_F_STRICT_DICT)
    1029             :         /* JSON allows only string keys */
    1030           0 :         key = parse_string(ctx);
    1031             :     else
    1032             :         /* heim_dict_t allows any heim_object_t as key */
    1033           0 :         key = parse_value(ctx);
    1034           0 :     if (key == NULL)
    1035             :         /* Even heim_dict_t does not allow C NULLs as keys though! */
    1036           0 :         return -1;
    1037             : 
    1038           0 :     if (white_spaces(ctx)) {
    1039           0 :         heim_release(key);
    1040           0 :         return -1;
    1041             :     }
    1042             : 
    1043           0 :     if (*ctx->p != ':') {
    1044           0 :         heim_release(key);
    1045           0 :         return -1;
    1046             :     }
    1047             : 
    1048           0 :     ctx->p += 1; /* safe because we call white_spaces() next */
    1049             : 
    1050           0 :     if (white_spaces(ctx)) {
    1051           0 :         heim_release(key);
    1052           0 :         return -1;
    1053             :     }
    1054             : 
    1055           0 :     value = parse_value(ctx);
    1056           0 :     if (value == NULL &&
    1057           0 :         (ctx->error != NULL || (ctx->flags & HEIM_JSON_F_NO_C_NULL))) {
    1058           0 :         if (ctx->error == NULL)
    1059           0 :             ctx->error = heim_error_create(EINVAL, "Invalid JSON encoding");
    1060           0 :         heim_release(key);
    1061           0 :         return -1;
    1062             :     }
    1063           0 :     heim_dict_set_value(dict, key, value);
    1064           0 :     heim_release(key);
    1065           0 :     heim_release(value);
    1066             : 
    1067           0 :     if (white_spaces(ctx))
    1068           0 :         return -1;
    1069             : 
    1070           0 :     if (*ctx->p == '}') {
    1071             :         /*
    1072             :          * Return 1 but don't consume the '}' so we can count the one
    1073             :          * pair in a one-pair dict
    1074             :          */
    1075           0 :         return 1;
    1076           0 :     } else if (*ctx->p == ',') {
    1077           0 :         ctx->p++;
    1078           0 :         return 1;
    1079             :     }
    1080           0 :     return -1;
    1081             : }
    1082             : 
    1083             : static heim_dict_t
    1084           0 : parse_dict(struct parse_ctx *ctx)
    1085             : {
    1086           0 :     heim_dict_t dict;
    1087           0 :     size_t count = 0;
    1088           0 :     int ret;
    1089             : 
    1090           0 :     heim_assert(*ctx->p == '{', "string doesn't start with {");
    1091             : 
    1092           0 :     dict = heim_dict_create(11);
    1093           0 :     if (dict == NULL) {
    1094           0 :         ctx->error = heim_error_create_enomem();
    1095           0 :         return NULL;
    1096             :     }
    1097             : 
    1098           0 :     ctx->p += 1; /* safe because parse_pair() calls white_spaces() first */
    1099             : 
    1100           0 :     while ((ret = parse_pair(dict, ctx)) > 0)
    1101           0 :         count++;
    1102           0 :     if (ret < 0) {
    1103           0 :         heim_release(dict);
    1104           0 :         return NULL;
    1105             :     }
    1106           0 :     if (count == 1 && !(ctx->flags & HEIM_JSON_F_NO_DATA_DICT)) {
    1107           0 :         heim_object_t v = heim_dict_copy_value(dict, heim_tid_data_uuid_key);
    1108             : 
    1109             :         /*
    1110             :          * Binary data encoded as a dict with a single magic key with
    1111             :          * base64-encoded value?  Decode as heim_data_t.
    1112             :          */
    1113           0 :         if (v != NULL && heim_get_tid(v) == HEIM_TID_STRING) {
    1114           0 :             void *buf;
    1115           0 :             size_t len;
    1116             : 
    1117           0 :             buf = malloc(strlen(heim_string_get_utf8(v)));
    1118           0 :             if (buf == NULL) {
    1119           0 :                 heim_release(dict);
    1120           0 :                 heim_release(v);
    1121           0 :                 ctx->error = heim_error_create_enomem();
    1122           0 :                 return NULL;
    1123             :             }
    1124           0 :             len = rk_base64_decode(heim_string_get_utf8(v), buf);
    1125           0 :             heim_release(v);
    1126           0 :             if (len == -1) {
    1127           0 :                 free(buf);
    1128           0 :                 return dict; /* assume aliasing accident */
    1129             :             }
    1130           0 :             heim_release(dict);
    1131           0 :             return (heim_dict_t)heim_data_ref_create(buf, len, free);
    1132             :         }
    1133             :     }
    1134           0 :     return dict;
    1135             : }
    1136             : 
    1137             : static int
    1138           0 : parse_item(heim_array_t array, struct parse_ctx *ctx)
    1139             : {
    1140           0 :     heim_object_t value;
    1141             : 
    1142           0 :     if (white_spaces(ctx))
    1143           0 :         return -1;
    1144             : 
    1145           0 :     if (*ctx->p == ']') {
    1146           0 :         ctx->p++; /* safe because parse_value() calls white_spaces() first */
    1147           0 :         return 0;
    1148             :     }
    1149             : 
    1150           0 :     value = parse_value(ctx);
    1151           0 :     if (value == NULL &&
    1152           0 :         (ctx->error || (ctx->flags & HEIM_JSON_F_NO_C_NULL)))
    1153           0 :         return -1;
    1154             : 
    1155           0 :     heim_array_append_value(array, value);
    1156           0 :     heim_release(value);
    1157             : 
    1158           0 :     if (white_spaces(ctx))
    1159           0 :         return -1;
    1160             : 
    1161           0 :     if (*ctx->p == ']') {
    1162           0 :         ctx->p++;
    1163           0 :         return 0;
    1164           0 :     } else if (*ctx->p == ',') {
    1165           0 :         ctx->p++;
    1166           0 :         return 1;
    1167             :     }
    1168           0 :     return -1;
    1169             : }
    1170             : 
    1171             : static heim_array_t
    1172           0 : parse_array(struct parse_ctx *ctx)
    1173             : {
    1174           0 :     heim_array_t array = heim_array_create();
    1175           0 :     int ret;
    1176             : 
    1177           0 :     heim_assert(*ctx->p == '[', "array doesn't start with [");
    1178           0 :     ctx->p += 1;
    1179             : 
    1180           0 :     while ((ret = parse_item(array, ctx)) > 0)
    1181             :         ;
    1182           0 :     if (ret < 0) {
    1183           0 :         heim_release(array);
    1184           0 :         return NULL;
    1185             :     }
    1186           0 :     return array;
    1187             : }
    1188             : 
    1189             : static heim_object_t
    1190           0 : parse_value(struct parse_ctx *ctx)
    1191             : {
    1192           0 :     size_t len;
    1193           0 :     heim_object_t o;
    1194             : 
    1195           0 :     if (white_spaces(ctx))
    1196           0 :         return NULL;
    1197             : 
    1198           0 :     if (*ctx->p == '"') {
    1199           0 :         return parse_string(ctx);
    1200           0 :     } else if (*ctx->p == '{') {
    1201           0 :         if (ctx->depth-- == 1) {
    1202           0 :             ctx->error = heim_error_create(EINVAL, "JSON object too deep");
    1203           0 :             return NULL;
    1204             :         }
    1205           0 :         o = parse_dict(ctx);
    1206           0 :         ctx->depth++;
    1207           0 :         return o;
    1208           0 :     } else if (*ctx->p == '[') {
    1209           0 :         if (ctx->depth-- == 1) {
    1210           0 :             ctx->error = heim_error_create(EINVAL, "JSON object too deep");
    1211           0 :             return NULL;
    1212             :         }
    1213           0 :         o = parse_array(ctx);
    1214           0 :         ctx->depth++;
    1215           0 :         return o;
    1216           0 :     } else if (is_number(*ctx->p) || *ctx->p == '-') {
    1217           0 :         return parse_number(ctx);
    1218             :     }
    1219             : 
    1220           0 :     len = ctx->pend - ctx->p;
    1221             : 
    1222           0 :     if ((ctx->flags & HEIM_JSON_F_NO_C_NULL) == 0 &&
    1223           0 :         len >= 6 && memcmp(ctx->p, "<NULL>", 6) == 0) {
    1224           0 :         ctx->p += 6;
    1225           0 :         return heim_null_create();
    1226           0 :     } else if (len >= 4 && memcmp(ctx->p, "null", 4) == 0) {
    1227           0 :         ctx->p += 4;
    1228           0 :         return heim_null_create();
    1229           0 :     } else if (len >= 4 && strncasecmp((char *)ctx->p, "true", 4) == 0) {
    1230           0 :         ctx->p += 4;
    1231           0 :         return heim_bool_create(1);
    1232           0 :     } else if (len >= 5 && strncasecmp((char *)ctx->p, "false", 5) == 0) {
    1233           0 :         ctx->p += 5;
    1234           0 :         return heim_bool_create(0);
    1235             :     }
    1236             : 
    1237           0 :     ctx->error = heim_error_create(EINVAL, "unknown char %c at %lu line %lu",
    1238           0 :                                    (char)*ctx->p, 
    1239           0 :                                    (unsigned long)(ctx->p - ctx->pstart),
    1240             :                                    ctx->lineno);
    1241           0 :     return NULL;
    1242             : }
    1243             : 
    1244             : 
    1245             : heim_object_t
    1246           0 : heim_json_create(const char *string, size_t max_depth, heim_json_flags_t flags,
    1247             :                  heim_error_t *error)
    1248             : {
    1249           0 :     return heim_json_create_with_bytes(string, strlen(string), max_depth, flags,
    1250             :                                        error);
    1251             : }
    1252             : 
    1253             : heim_object_t
    1254           0 : heim_json_create_with_bytes(const void *data, size_t length, size_t max_depth,
    1255             :                             heim_json_flags_t flags, heim_error_t *error)
    1256             : {
    1257           0 :     struct parse_ctx ctx;
    1258           0 :     heim_object_t o;
    1259             : 
    1260           0 :     heim_base_once_f(&heim_json_once, NULL, json_init_once);
    1261             : 
    1262           0 :     ctx.lineno = 1;
    1263           0 :     ctx.p = data;
    1264           0 :     ctx.pstart = data;
    1265           0 :     ctx.pend = ((uint8_t *)data) + length;
    1266           0 :     ctx.error = NULL;
    1267           0 :     ctx.flags = flags;
    1268           0 :     ctx.depth = max_depth;
    1269             : 
    1270           0 :     o = parse_value(&ctx);
    1271             : 
    1272           0 :     if (o == NULL && error) {
    1273           0 :         *error = ctx.error;
    1274           0 :     } else if (ctx.error) {
    1275           0 :         heim_release(ctx.error);
    1276             :     }
    1277             : 
    1278           0 :     return o;
    1279             : }
    1280             : 
    1281             : 
    1282             : static void
    1283           0 : show_printf(void *ctx, const char *str)
    1284             : {
    1285           0 :     if (str == NULL)
    1286           0 :         return;
    1287           0 :     fprintf(ctx, "%s", str);
    1288             : }
    1289             : 
    1290             : /**
    1291             :  * Dump a heimbase object to stderr (useful from the debugger!)
    1292             :  *
    1293             :  * @param obj object to dump using JSON or JSON-like format
    1294             :  *
    1295             :  * @addtogroup heimbase
    1296             :  */
    1297             : void
    1298           0 : heim_show(heim_object_t obj)
    1299             : {
    1300           0 :     heim_base2json(obj, stderr, HEIM_JSON_F_NO_DATA_DICT, show_printf);
    1301           0 : }
    1302             : 
    1303             : static void
    1304           0 : strbuf_add(void *ctx, const char *str)
    1305             : {
    1306           0 :     struct heim_strbuf *strbuf = ctx;
    1307           0 :     size_t len;
    1308             : 
    1309           0 :     if (strbuf->enomem)
    1310           0 :         return;
    1311             : 
    1312           0 :     if (str == NULL) {
    1313             :         /*
    1314             :          * Eat the last '\n'; this is used when formatting dict pairs
    1315             :          * and array items so that the ',' separating them is never
    1316             :          * preceded by a '\n'.
    1317             :          */
    1318           0 :         if (strbuf->len > 0 && strbuf->str[strbuf->len - 1] == '\n')
    1319           0 :             strbuf->len--;
    1320           0 :         return;
    1321             :     }
    1322             : 
    1323           0 :     len = strlen(str);
    1324           0 :     if ((len + 1) > (strbuf->alloced - strbuf->len)) {
    1325           0 :         size_t new_len = strbuf->alloced + (strbuf->alloced >> 2) + len + 1;
    1326           0 :         char *s;
    1327             : 
    1328           0 :         s = realloc(strbuf->str, new_len);
    1329           0 :         if (s == NULL) {
    1330           0 :             strbuf->enomem = 1;
    1331           0 :             return;
    1332             :         }
    1333           0 :         strbuf->str = s;
    1334           0 :         strbuf->alloced = new_len;
    1335             :     }
    1336             :     /* +1 so we copy the NUL */
    1337           0 :     (void) memcpy(strbuf->str + strbuf->len, str, len + 1);
    1338           0 :     strbuf->len += len;
    1339           0 :     if (strbuf->str[strbuf->len - 1] == '\n' && 
    1340           0 :         strbuf->flags & HEIM_JSON_F_ONE_LINE)
    1341           0 :         strbuf->len--;
    1342             : }
    1343             : 
    1344             : #define STRBUF_INIT_SZ 64
    1345             : 
    1346             : heim_string_t
    1347         679 : heim_json_copy_serialize(heim_object_t obj, heim_json_flags_t flags, heim_error_t *error)
    1348             : {
    1349           0 :     heim_string_t str;
    1350           0 :     struct heim_strbuf strbuf;
    1351           0 :     int ret;
    1352             : 
    1353         679 :     if (error)
    1354           0 :         *error = NULL;
    1355             : 
    1356         679 :     memset(&strbuf, 0, sizeof (strbuf));
    1357         679 :     strbuf.str = malloc(STRBUF_INIT_SZ);
    1358         679 :     if (strbuf.str == NULL) {
    1359           0 :         if (error)
    1360           0 :             *error = heim_error_create_enomem();
    1361           0 :         return NULL;
    1362             :     }
    1363         679 :     strbuf.len = 0;
    1364         679 :     strbuf.alloced = STRBUF_INIT_SZ;
    1365         679 :     strbuf.str[0] = '\0';
    1366         679 :     strbuf.flags = flags;
    1367             : 
    1368         679 :     ret = heim_base2json(obj, &strbuf, flags, strbuf_add);
    1369         679 :     if (ret || strbuf.enomem) {
    1370         679 :         if (error) {
    1371           0 :             if (strbuf.enomem || ret == ENOMEM)
    1372           0 :                 *error = heim_error_create_enomem();
    1373             :             else
    1374           0 :                 *error = heim_error_create(1, "Impossible to JSON-encode "
    1375             :                                            "object");
    1376             :         }
    1377         679 :         free(strbuf.str);
    1378         679 :         return NULL;
    1379             :     }
    1380           0 :     if (flags & HEIM_JSON_F_ONE_LINE) {
    1381           0 :         strbuf.flags &= ~HEIM_JSON_F_ONE_LINE;
    1382           0 :         strbuf_add(&strbuf, "\n");
    1383             :     }
    1384           0 :     str = heim_string_ref_create(strbuf.str, free);
    1385           0 :     if (str == NULL) {
    1386           0 :         if (error)
    1387           0 :             *error = heim_error_create_enomem();
    1388           0 :         free(strbuf.str);
    1389             :     }
    1390           0 :     return str;
    1391             : }
    1392             : 
    1393             : struct heim_eq_f_ctx {
    1394             :     heim_dict_t other;
    1395             :     int ret;
    1396             : };
    1397             : 
    1398             : static void
    1399           0 : heim_eq_dict_iter_f(heim_object_t key, heim_object_t val, void *d)
    1400             : {
    1401           0 :     struct heim_eq_f_ctx *ctx = d;
    1402           0 :     heim_object_t other_val;
    1403             : 
    1404           0 :     if (!ctx->ret)
    1405           0 :         return;
    1406             : 
    1407             :     /*
    1408             :      * This doesn't work if the key is an array or a dict, which, anyways,
    1409             :      * isn't allowed in JSON, though we allow it.
    1410             :      */
    1411           0 :     other_val = heim_dict_get_value(ctx->other, key);
    1412           0 :     ctx->ret = heim_json_eq(val, other_val);
    1413             : }
    1414             : 
    1415             : int
    1416           0 : heim_json_eq(heim_object_t a, heim_object_t b)
    1417             : {
    1418           0 :     heim_tid_t atid, btid;
    1419             : 
    1420           0 :     if (a == b)
    1421           0 :         return 1;
    1422           0 :     if (a == NULL || b == NULL)
    1423           0 :         return 0;
    1424           0 :     atid = heim_get_tid(a);
    1425           0 :     btid = heim_get_tid(b);
    1426           0 :     if (atid != btid)
    1427           0 :         return 0;
    1428           0 :     switch (atid) {
    1429           0 :     case HEIM_TID_ARRAY: {
    1430           0 :         size_t len = heim_array_get_length(b);
    1431           0 :         size_t i;
    1432             : 
    1433           0 :         if (heim_array_get_length(a) != len)
    1434           0 :             return 0;
    1435           0 :         for (i = 0; i < len; i++) {
    1436           0 :             if (!heim_json_eq(heim_array_get_value(a, i),
    1437             :                               heim_array_get_value(b, i)))
    1438           0 :                 return 0;
    1439             :         }
    1440           0 :         return 1;
    1441             :     }
    1442           0 :     case HEIM_TID_DICT: {
    1443           0 :         struct heim_eq_f_ctx ctx;
    1444             : 
    1445           0 :         ctx.other = b;
    1446           0 :         ctx.ret = 1;
    1447           0 :         heim_dict_iterate_f(a, &ctx, heim_eq_dict_iter_f);
    1448             : 
    1449           0 :         if (ctx.ret) {
    1450           0 :             ctx.other = a;
    1451           0 :             heim_dict_iterate_f(b, &ctx, heim_eq_dict_iter_f);
    1452             :         }
    1453           0 :         return ctx.ret;
    1454             :     }
    1455           0 :     case HEIM_TID_STRING:
    1456           0 :         return strcmp(heim_string_get_utf8(a), heim_string_get_utf8(b)) == 0;
    1457           0 :     case HEIM_TID_DATA: {
    1458           0 :         return heim_data_get_length(a) == heim_data_get_length(b) &&
    1459           0 :                memcmp(heim_data_get_ptr(a), heim_data_get_ptr(b),
    1460             :                       heim_data_get_length(a)) == 0;
    1461             :     }
    1462           0 :     case HEIM_TID_NUMBER:
    1463           0 :         return heim_number_get_long(a) == heim_number_get_long(b);
    1464           0 :     case HEIM_TID_NULL:
    1465             :     case HEIM_TID_BOOL:
    1466           0 :         return heim_bool_val(a) == heim_bool_val(b);
    1467           0 :     default:
    1468           0 :         break;
    1469             :     }
    1470           0 :     return 0;
    1471             : }

Generated by: LCOV version 1.14