basic handing of quoted strings
[heimdal.git] / base / json.c
blob1abcd37b99f91f58dc57e392bab28bbad3c0ef5c
1 /*
2 * Copyright (c) 2010 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
36 #include "baselocl.h"
37 #include <ctype.h>
40 int
41 heim_base2json(heim_object_t obj,
42 void (*out)(const char *, void *), void *ctx);
44 int
45 heim_base2json(heim_object_t obj,
46 void (*out)(const char *, void *), void *ctx)
48 heim_tid_t type = heim_get_tid(obj);
49 __block int fail = 0, needcomma = 0;
51 switch (type) {
52 case HEIM_TID_ARRAY:
53 out("[ ", ctx);
54 heim_array_iterate(obj, ^(heim_object_t sub) {
55 if (needcomma)
56 out(", ", ctx);
57 fail |= heim_base2json(sub, out, ctx);
58 needcomma = 1;
59 });
60 out("]", ctx);
61 break;
63 case HEIM_TID_DICT:
64 out("{ ", ctx);
65 heim_dict_iterate(obj, ^(heim_object_t key, heim_object_t value) {
66 if (needcomma)
67 out(", ", ctx);
68 fail |= heim_base2json(key, out, ctx);
69 out(" = ", ctx);
70 fail |= heim_base2json(value, out, ctx);
71 needcomma = 1;
72 });
73 out("}", ctx);
74 break;
76 case HEIM_TID_STRING:
77 out("\"", ctx);
78 out(heim_string_get_utf8(obj), ctx);
79 out("\"", ctx);
80 break;
82 case HEIM_TID_NUMBER: {
83 char num[16];
84 snprintf(num, sizeof(num), "%d", heim_number_get_int(obj));
85 out(num, ctx);
86 break;
88 case HEIM_TID_NULL:
89 out("null", ctx);
90 break;
91 case HEIM_TID_BOOL:
92 out(heim_bool_val(obj) ? "true" : "false", ctx);
93 break;
94 default:
95 return 1;
97 return fail;
100 struct parse_ctx {
101 unsigned long lineno;
102 const uint8_t *p;
103 const uint8_t *pstart;
104 const uint8_t *pend;
105 heim_error_t error;
109 static heim_object_t
110 parse_value(struct parse_ctx *ctx);
112 static int
113 white_spaces(struct parse_ctx *ctx)
115 while (ctx->p < ctx->pend) {
116 uint8_t c = *ctx->p;
117 if (c == ' ' || c == '\t' || c == '\r') {
119 } else if (c == '\n') {
120 ctx->lineno++;
121 } else
122 return 0;
123 (ctx->p)++;
125 return -1;
128 static int
129 is_number(uint8_t n)
131 return ('0' <= n && n <= '9');
134 static heim_number_t
135 parse_number(struct parse_ctx *ctx)
137 int number = 0, neg = 1;
139 if (ctx->p >= ctx->pend)
140 return NULL;
142 if (*ctx->p == '-') {
143 neg = -1;
144 ctx->p += 1;
147 while (ctx->p < ctx->pend) {
148 if (is_number(*ctx->p)) {
149 number = (number * 10) + (*ctx->p - '0');
150 } else {
151 break;
153 ctx->p += 1;
156 return heim_number_create(number * neg);
159 static heim_string_t
160 parse_string(struct parse_ctx *ctx)
162 const uint8_t *start;
163 int quote = 0;
165 heim_assert(*ctx->p == '"', "string doesnt' start with \"");
166 start = ++ctx->p;
168 while (ctx->p < ctx->pend) {
169 if (*ctx->p == '\n') {
170 ctx->lineno++;
171 } else if (*ctx->p == '\\') {
172 if (ctx->p + 1 == ctx->pend)
173 goto out;
174 ctx->p += 1;
175 quote = 1;
176 } else if (*ctx->p == '"') {
177 heim_object_t o;
179 if (quote) {
180 char *p0, *p;
181 p = p0 = malloc(ctx->p - start);
182 if (p == NULL)
183 goto out;
184 while (start < ctx->p) {
185 if (*start == '\\') {
186 start++;
187 /* XXX validate qouted char */
189 *p++ = *start++;
191 o = heim_string_create_with_bytes(p0, p - p0);
192 free(p0);
193 } else {
194 o = heim_string_create_with_bytes(start, ctx->p - start);
196 ctx->p += 1;
198 return o;
200 ctx->p += 1;
202 out:
203 ctx->error = heim_error_create(EINVAL, "ran out of string");
204 return NULL;
207 static int
208 parse_pair(heim_dict_t dict, struct parse_ctx *ctx)
210 heim_string_t key;
211 heim_object_t value;
213 if (white_spaces(ctx))
214 return -1;
216 if (*ctx->p == '}')
217 return 0;
219 key = parse_string(ctx);
220 if (key == NULL)
221 return -1;
223 if (white_spaces(ctx))
224 return -1;
226 if (*ctx->p != ':') {
227 heim_release(key);
228 return -1;
231 ctx->p += 1;
233 if (white_spaces(ctx)) {
234 heim_release(key);
235 return -1;
238 value = parse_value(ctx);
239 if (value == NULL) {
240 heim_release(key);
241 return -1;
243 heim_dict_set_value(dict, key, value);
244 heim_release(key);
245 heim_release(value);
247 if (white_spaces(ctx))
248 return -1;
250 if (*ctx->p == '}') {
251 ctx->p++;
252 return 0;
253 } else if (*ctx->p == ',') {
254 ctx->p++;
255 return 1;
257 return -1;
260 static heim_dict_t
261 parse_dict(struct parse_ctx *ctx)
263 heim_dict_t dict = heim_dict_create(11);
264 int ret;
266 heim_assert(*ctx->p == '{', "string doesn't start with {");
267 ctx->p += 1;
269 while ((ret = parse_pair(dict, ctx)) > 0)
271 if (ret < 0) {
272 heim_release(dict);
273 return NULL;
275 return dict;
278 static int
279 parse_item(heim_array_t array, struct parse_ctx *ctx)
281 heim_object_t value;
283 if (white_spaces(ctx))
284 return -1;
286 if (*ctx->p == ']')
287 return 0;
289 value = parse_value(ctx);
290 if (value == NULL)
291 return -1;
293 heim_array_append_value(array, value);
294 heim_release(value);
296 if (white_spaces(ctx))
297 return -1;
299 if (*ctx->p == ']') {
300 ctx->p++;
301 return 0;
302 } else if (*ctx->p == ',') {
303 ctx->p++;
304 return 1;
306 return -1;
309 static heim_array_t
310 parse_array(struct parse_ctx *ctx)
312 heim_array_t array = heim_array_create();
313 int ret;
315 heim_assert(*ctx->p == '[', "array doesn't start with [");
316 ctx->p += 1;
318 while ((ret = parse_item(array, ctx)) > 0)
320 if (ret < 0) {
321 heim_release(array);
322 return NULL;
324 return array;
327 static heim_object_t
328 parse_value(struct parse_ctx *ctx)
330 size_t len;
332 if (white_spaces(ctx))
333 return NULL;
335 if (*ctx->p == '"') {
336 return parse_string(ctx);
337 } else if (*ctx->p == '{') {
338 return parse_dict(ctx);
339 } else if (*ctx->p == '[') {
340 return parse_array(ctx);
341 } else if (is_number(*ctx->p) || *ctx->p == '-') {
342 return parse_number(ctx);
345 len = ctx->pend - ctx->p;
347 if (len >= 4 && memcmp(ctx->p, "null", 4) == 0) {
348 ctx->p += 4;
349 return heim_null_create();
350 } else if (len >= 4 && strncasecmp((char *)ctx->p, "true", 4) == 0) {
351 ctx->p += 4;
352 return heim_bool_create(1);
353 } else if (len >= 5 && strncasecmp((char *)ctx->p, "false", 5) == 0) {
354 ctx->p += 5;
355 return heim_bool_create(0);
358 ctx->error = heim_error_create(EINVAL, "unknown char %c at %lu line %lu",
359 (char)*ctx->p,
360 (unsigned long)(ctx->p - ctx->pstart),
361 ctx->lineno);
362 return NULL;
366 heim_object_t
367 heim_json_create(const char *string, heim_error_t *error)
369 return heim_json_create_with_bytes(string, strlen(string), error);
372 heim_object_t
373 heim_json_create_with_bytes(const void *data, size_t length, heim_error_t *error)
375 struct parse_ctx ctx;
376 heim_object_t o;
378 ctx.lineno = 1;
379 ctx.p = data;
380 ctx.pstart = data;
381 ctx.pend = ((uint8_t *)data) + length;
382 ctx.error = NULL;
384 o = parse_value(&ctx);
386 if (o == NULL && error) {
387 *error = ctx.error;
388 } else if (ctx.error) {
389 heim_release(ctx.error);
392 return o;