Coverage Report

Created: 2025-11-16 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/jansson-2.14/src/dump.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
3
 *
4
 * Jansson is free software; you can redistribute it and/or modify
5
 * it under the terms of the MIT license. See LICENSE for details.
6
 */
7
8
#ifndef _GNU_SOURCE
9
#define _GNU_SOURCE
10
#endif
11
12
#include "jansson_private.h"
13
14
#include <assert.h>
15
#include <stdio.h>
16
#include <stdlib.h>
17
#include <string.h>
18
#ifdef HAVE_UNISTD_H
19
#include <unistd.h>
20
#endif
21
22
#include "jansson.h"
23
#include "strbuffer.h"
24
#include "utf.h"
25
26
0
#define MAX_INTEGER_STR_LENGTH 100
27
0
#define MAX_REAL_STR_LENGTH    100
28
29
0
#define FLAGS_TO_INDENT(f)    ((f)&0x1F)
30
0
#define FLAGS_TO_PRECISION(f) (((f) >> 11) & 0x1F)
31
32
struct buffer {
33
    const size_t size;
34
    size_t used;
35
    char *data;
36
};
37
38
0
static int dump_to_strbuffer(const char *buffer, size_t size, void *data) {
39
0
    return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
40
0
}
41
42
0
static int dump_to_buffer(const char *buffer, size_t size, void *data) {
43
0
    struct buffer *buf = (struct buffer *)data;
44
45
0
    if (buf->used + size <= buf->size)
46
0
        memcpy(&buf->data[buf->used], buffer, size);
47
48
0
    buf->used += size;
49
0
    return 0;
50
0
}
51
52
0
static int dump_to_file(const char *buffer, size_t size, void *data) {
53
0
    FILE *dest = (FILE *)data;
54
0
    if (fwrite(buffer, size, 1, dest) != 1)
55
0
        return -1;
56
0
    return 0;
57
0
}
58
59
0
static int dump_to_fd(const char *buffer, size_t size, void *data) {
60
0
#ifdef HAVE_UNISTD_H
61
0
    int *dest = (int *)data;
62
0
    if (write(*dest, buffer, size) == (ssize_t)size)
63
0
        return 0;
64
0
#endif
65
0
    return -1;
66
0
}
67
68
/* 32 spaces (the maximum indentation size) */
69
static const char whitespace[] = "                                ";
70
71
static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump,
72
0
                       void *data) {
73
0
    if (FLAGS_TO_INDENT(flags) > 0) {
74
0
        unsigned int ws_count = FLAGS_TO_INDENT(flags), n_spaces = depth * ws_count;
75
76
0
        if (dump("\n", 1, data))
77
0
            return -1;
78
79
0
        while (n_spaces > 0) {
80
0
            int cur_n =
81
0
                n_spaces < sizeof whitespace - 1 ? n_spaces : sizeof whitespace - 1;
82
83
0
            if (dump(whitespace, cur_n, data))
84
0
                return -1;
85
86
0
            n_spaces -= cur_n;
87
0
        }
88
0
    } else if (space && !(flags & JSON_COMPACT)) {
89
0
        return dump(" ", 1, data);
90
0
    }
91
0
    return 0;
92
0
}
93
94
static int dump_string(const char *str, size_t len, json_dump_callback_t dump, void *data,
95
0
                       size_t flags) {
96
0
    const char *pos, *end, *lim;
97
0
    int32_t codepoint = 0;
98
99
0
    if (dump("\"", 1, data))
100
0
        return -1;
101
102
0
    end = pos = str;
103
0
    lim = str + len;
104
0
    while (1) {
105
0
        const char *text;
106
0
        char seq[13];
107
0
        int length;
108
109
0
        while (end < lim) {
110
0
            end = utf8_iterate(pos, lim - pos, &codepoint);
111
0
            if (!end)
112
0
                return -1;
113
114
            /* mandatory escape or control char */
115
0
            if (codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
116
0
                break;
117
118
            /* slash */
119
0
            if ((flags & JSON_ESCAPE_SLASH) && codepoint == '/')
120
0
                break;
121
122
            /* non-ASCII */
123
0
            if ((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F)
124
0
                break;
125
126
0
            pos = end;
127
0
        }
128
129
0
        if (pos != str) {
130
0
            if (dump(str, pos - str, data))
131
0
                return -1;
132
0
        }
133
134
0
        if (end == pos)
135
0
            break;
136
137
        /* handle \, /, ", and control codes */
138
0
        length = 2;
139
0
        switch (codepoint) {
140
0
            case '\\':
141
0
                text = "\\\\";
142
0
                break;
143
0
            case '\"':
144
0
                text = "\\\"";
145
0
                break;
146
0
            case '\b':
147
0
                text = "\\b";
148
0
                break;
149
0
            case '\f':
150
0
                text = "\\f";
151
0
                break;
152
0
            case '\n':
153
0
                text = "\\n";
154
0
                break;
155
0
            case '\r':
156
0
                text = "\\r";
157
0
                break;
158
0
            case '\t':
159
0
                text = "\\t";
160
0
                break;
161
0
            case '/':
162
0
                text = "\\/";
163
0
                break;
164
0
            default: {
165
                /* codepoint is in BMP */
166
0
                if (codepoint < 0x10000) {
167
0
                    snprintf(seq, sizeof(seq), "\\u%04X", (unsigned int)codepoint);
168
0
                    length = 6;
169
0
                }
170
171
                /* not in BMP -> construct a UTF-16 surrogate pair */
172
0
                else {
173
0
                    int32_t first, last;
174
175
0
                    codepoint -= 0x10000;
176
0
                    first = 0xD800 | ((codepoint & 0xffc00) >> 10);
177
0
                    last = 0xDC00 | (codepoint & 0x003ff);
178
179
0
                    snprintf(seq, sizeof(seq), "\\u%04X\\u%04X", (unsigned int)first,
180
0
                             (unsigned int)last);
181
0
                    length = 12;
182
0
                }
183
184
0
                text = seq;
185
0
                break;
186
0
            }
187
0
        }
188
189
0
        if (dump(text, length, data))
190
0
            return -1;
191
192
0
        str = pos = end;
193
0
    }
194
195
0
    return dump("\"", 1, data);
196
0
}
197
198
struct key_len {
199
    const char *key;
200
    int len;
201
};
202
203
0
static int compare_keys(const void *key1, const void *key2) {
204
0
    const struct key_len *k1 = key1;
205
0
    const struct key_len *k2 = key2;
206
0
    const size_t min_size = k1->len < k2->len ? k1->len : k2->len;
207
0
    int res = memcmp(k1->key, k2->key, min_size);
208
209
0
    if (res)
210
0
        return res;
211
212
0
    return k1->len - k2->len;
213
0
}
214
215
static int do_dump(const json_t *json, size_t flags, int depth, hashtable_t *parents,
216
0
                   json_dump_callback_t dump, void *data) {
217
0
    int embed = flags & JSON_EMBED;
218
219
0
    flags &= ~JSON_EMBED;
220
221
0
    if (!json)
222
0
        return -1;
223
224
0
    switch (json_typeof(json)) {
225
0
        case JSON_NULL:
226
0
            return dump("null", 4, data);
227
228
0
        case JSON_TRUE:
229
0
            return dump("true", 4, data);
230
231
0
        case JSON_FALSE:
232
0
            return dump("false", 5, data);
233
234
0
        case JSON_INTEGER: {
235
0
            char buffer[MAX_INTEGER_STR_LENGTH];
236
0
            int size;
237
238
0
            size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, "%" JSON_INTEGER_FORMAT,
239
0
                            json_integer_value(json));
240
0
            if (size < 0 || size >= MAX_INTEGER_STR_LENGTH)
241
0
                return -1;
242
243
0
            return dump(buffer, size, data);
244
0
        }
245
246
0
        case JSON_REAL: {
247
0
            char buffer[MAX_REAL_STR_LENGTH];
248
0
            int size;
249
0
            double value = json_real_value(json);
250
251
0
            size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value,
252
0
                                FLAGS_TO_PRECISION(flags));
253
0
            if (size < 0)
254
0
                return -1;
255
256
0
            return dump(buffer, size, data);
257
0
        }
258
259
0
        case JSON_STRING:
260
0
            return dump_string(json_string_value(json), json_string_length(json), dump,
261
0
                               data, flags);
262
263
0
        case JSON_ARRAY: {
264
0
            size_t n;
265
0
            size_t i;
266
            /* Space for "0x", double the sizeof a pointer for the hex and a
267
             * terminator. */
268
0
            char key[2 + (sizeof(json) * 2) + 1];
269
0
            size_t key_len;
270
271
            /* detect circular references */
272
0
            if (jsonp_loop_check(parents, json, key, sizeof(key), &key_len))
273
0
                return -1;
274
275
0
            n = json_array_size(json);
276
277
0
            if (!embed && dump("[", 1, data))
278
0
                return -1;
279
0
            if (n == 0) {
280
0
                hashtable_del(parents, key, key_len);
281
0
                return embed ? 0 : dump("]", 1, data);
282
0
            }
283
0
            if (dump_indent(flags, depth + 1, 0, dump, data))
284
0
                return -1;
285
286
0
            for (i = 0; i < n; ++i) {
287
0
                if (do_dump(json_array_get(json, i), flags, depth + 1, parents, dump,
288
0
                            data))
289
0
                    return -1;
290
291
0
                if (i < n - 1) {
292
0
                    if (dump(",", 1, data) ||
293
0
                        dump_indent(flags, depth + 1, 1, dump, data))
294
0
                        return -1;
295
0
                } else {
296
0
                    if (dump_indent(flags, depth, 0, dump, data))
297
0
                        return -1;
298
0
                }
299
0
            }
300
301
0
            hashtable_del(parents, key, key_len);
302
0
            return embed ? 0 : dump("]", 1, data);
303
0
        }
304
305
0
        case JSON_OBJECT: {
306
0
            void *iter;
307
0
            const char *separator;
308
0
            int separator_length;
309
0
            char loop_key[LOOP_KEY_LEN];
310
0
            size_t loop_key_len;
311
312
0
            if (flags & JSON_COMPACT) {
313
0
                separator = ":";
314
0
                separator_length = 1;
315
0
            } else {
316
0
                separator = ": ";
317
0
                separator_length = 2;
318
0
            }
319
320
            /* detect circular references */
321
0
            if (jsonp_loop_check(parents, json, loop_key, sizeof(loop_key),
322
0
                                 &loop_key_len))
323
0
                return -1;
324
325
0
            iter = json_object_iter((json_t *)json);
326
327
0
            if (!embed && dump("{", 1, data))
328
0
                return -1;
329
0
            if (!iter) {
330
0
                hashtable_del(parents, loop_key, loop_key_len);
331
0
                return embed ? 0 : dump("}", 1, data);
332
0
            }
333
0
            if (dump_indent(flags, depth + 1, 0, dump, data))
334
0
                return -1;
335
336
0
            if (flags & JSON_SORT_KEYS) {
337
0
                struct key_len *keys;
338
0
                size_t size, i;
339
340
0
                size = json_object_size(json);
341
0
                keys = jsonp_malloc(size * sizeof(struct key_len));
342
0
                if (!keys)
343
0
                    return -1;
344
345
0
                i = 0;
346
0
                while (iter) {
347
0
                    struct key_len *keylen = &keys[i];
348
349
0
                    keylen->key = json_object_iter_key(iter);
350
0
                    keylen->len = json_object_iter_key_len(iter);
351
352
0
                    iter = json_object_iter_next((json_t *)json, iter);
353
0
                    i++;
354
0
                }
355
0
                assert(i == size);
356
357
0
                qsort(keys, size, sizeof(struct key_len), compare_keys);
358
359
0
                for (i = 0; i < size; i++) {
360
0
                    const struct key_len *key;
361
0
                    json_t *value;
362
363
0
                    key = &keys[i];
364
0
                    value = json_object_getn(json, key->key, key->len);
365
0
                    assert(value);
366
367
0
                    dump_string(key->key, key->len, dump, data, flags);
368
0
                    if (dump(separator, separator_length, data) ||
369
0
                        do_dump(value, flags, depth + 1, parents, dump, data)) {
370
0
                        jsonp_free(keys);
371
0
                        return -1;
372
0
                    }
373
374
0
                    if (i < size - 1) {
375
0
                        if (dump(",", 1, data) ||
376
0
                            dump_indent(flags, depth + 1, 1, dump, data)) {
377
0
                            jsonp_free(keys);
378
0
                            return -1;
379
0
                        }
380
0
                    } else {
381
0
                        if (dump_indent(flags, depth, 0, dump, data)) {
382
0
                            jsonp_free(keys);
383
0
                            return -1;
384
0
                        }
385
0
                    }
386
0
                }
387
388
0
                jsonp_free(keys);
389
0
            } else {
390
                /* Don't sort keys */
391
392
0
                while (iter) {
393
0
                    void *next = json_object_iter_next((json_t *)json, iter);
394
0
                    const char *key = json_object_iter_key(iter);
395
0
                    const size_t key_len = json_object_iter_key_len(iter);
396
397
0
                    dump_string(key, key_len, dump, data, flags);
398
0
                    if (dump(separator, separator_length, data) ||
399
0
                        do_dump(json_object_iter_value(iter), flags, depth + 1, parents,
400
0
                                dump, data))
401
0
                        return -1;
402
403
0
                    if (next) {
404
0
                        if (dump(",", 1, data) ||
405
0
                            dump_indent(flags, depth + 1, 1, dump, data))
406
0
                            return -1;
407
0
                    } else {
408
0
                        if (dump_indent(flags, depth, 0, dump, data))
409
0
                            return -1;
410
0
                    }
411
412
0
                    iter = next;
413
0
                }
414
0
            }
415
416
0
            hashtable_del(parents, loop_key, loop_key_len);
417
0
            return embed ? 0 : dump("}", 1, data);
418
0
        }
419
420
0
        default:
421
            /* not reached */
422
0
            return -1;
423
0
    }
424
0
}
425
426
0
char *json_dumps(const json_t *json, size_t flags) {
427
0
    strbuffer_t strbuff;
428
0
    char *result;
429
430
0
    if (strbuffer_init(&strbuff))
431
0
        return NULL;
432
433
0
    if (json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags))
434
0
        result = NULL;
435
0
    else
436
0
        result = jsonp_strdup(strbuffer_value(&strbuff));
437
438
0
    strbuffer_close(&strbuff);
439
0
    return result;
440
0
}
441
442
0
size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags) {
443
0
    struct buffer buf = {size, 0, buffer};
444
445
0
    if (json_dump_callback(json, dump_to_buffer, (void *)&buf, flags))
446
0
        return 0;
447
448
0
    return buf.used;
449
0
}
450
451
0
int json_dumpf(const json_t *json, FILE *output, size_t flags) {
452
0
    return json_dump_callback(json, dump_to_file, (void *)output, flags);
453
0
}
454
455
0
int json_dumpfd(const json_t *json, int output, size_t flags) {
456
0
    return json_dump_callback(json, dump_to_fd, (void *)&output, flags);
457
0
}
458
459
0
int json_dump_file(const json_t *json, const char *path, size_t flags) {
460
0
    int result;
461
462
0
    FILE *output = fopen(path, "w");
463
0
    if (!output)
464
0
        return -1;
465
466
0
    result = json_dumpf(json, output, flags);
467
468
0
    if (fclose(output) != 0)
469
0
        return -1;
470
471
0
    return result;
472
0
}
473
474
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data,
475
0
                       size_t flags) {
476
0
    int res;
477
0
    hashtable_t parents_set;
478
479
0
    if (!(flags & JSON_ENCODE_ANY)) {
480
0
        if (!json_is_array(json) && !json_is_object(json))
481
0
            return -1;
482
0
    }
483
484
0
    if (hashtable_init(&parents_set))
485
0
        return -1;
486
0
    res = do_dump(json, flags, 0, &parents_set, callback, data);
487
0
    hashtable_close(&parents_set);
488
489
0
    return res;
490
0
}