Coverage Report

Created: 2025-08-26 06:38

/src/util-linux/lib/jsonwrt.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * JSON output formatting functions.
3
 *
4
 * No copyright is claimed.  This code is in the public domain; do with
5
 * it what you wish.
6
 *
7
 * Written by Karel Zak <kzak@redhat.com>
8
 */
9
#include <stdio.h>
10
#include <inttypes.h>
11
#include <ctype.h>
12
#include <cctype.h>
13
14
#include "c.h"
15
#include "jsonwrt.h"
16
17
/*
18
 * Requirements enumerated via testing (V8, Firefox, IE11):
19
 *
20
 * var charsToEscape = [];
21
 * for (var i = 0; i < 65535; i += 1) {
22
 *  try {
23
 *    JSON.parse('{"sample": "' + String.fromCodePoint(i) + '"}');
24
 *  } catch (e) {
25
 *    charsToEscape.push(i);
26
 *  }
27
 * }
28
 */
29
static void fputs_quoted_case_json(const char *data, FILE *out, int dir, size_t size)
30
0
{
31
0
  const char *p;
32
33
0
  fputc('"', out);
34
0
  for (p = data; p && *p && (!size || p < data + size); p++) {
35
36
0
    const unsigned int c = (unsigned int) *p;
37
38
    /* From http://www.json.org
39
     *
40
     * The double-quote and backslashes would break out a string or
41
     * init an escape sequence if not escaped.
42
     *
43
     * Note that single-quotes and forward slashes, while they're
44
     * in the JSON spec, don't break double-quoted strings.
45
     */
46
0
    if (c == '"' || c == '\\') {
47
0
      fputc('\\', out);
48
0
      fputc(c, out);
49
0
      continue;
50
0
    }
51
52
    /* All non-control characters OK; do the case swap as required. */
53
0
    if (c >= 0x20) {
54
      /*
55
       * Don't use locale sensitive ctype.h functions for regular
56
       * ASCII chars, because for example with Turkish locale
57
       * (aka LANG=tr_TR.UTF-8) toupper('I') returns 'I'.
58
       */
59
0
      if (c <= 127)
60
0
        fputc(dir ==  1 ? c_toupper(c) :
61
0
              dir == -1 ? c_tolower(c) : *p, out);
62
0
      else
63
0
        fputc(dir ==  1 ? toupper(c) :
64
0
              dir == -1 ? tolower(c) : *p, out);
65
0
      continue;
66
0
    }
67
68
    /* In addition, all chars under ' ' break Node's/V8/Chrome's, and
69
     * Firefox's JSON.parse function
70
     */
71
0
    switch (c) {
72
      /* Handle short-hand cases to reduce output size.  C
73
       * has most of the same stuff here, so if there's an
74
       * "Escape for C" function somewhere in the STL, we
75
       * should probably be using it.
76
       */
77
0
      case '\b':
78
0
        fputs("\\b", out);
79
0
        break;
80
0
      case '\t':
81
0
        fputs("\\t", out);
82
0
        break;
83
0
      case '\n':
84
0
        fputs("\\n", out);
85
0
        break;
86
0
      case '\f':
87
0
        fputs("\\f", out);
88
0
        break;
89
0
      case '\r':
90
0
        fputs("\\r", out);
91
0
        break;
92
0
      default:
93
        /* Other assorted control characters */
94
0
        fprintf(out, "\\u00%02x", c);
95
0
        break;
96
0
    }
97
0
  }
98
0
  fputc('"', out);
99
0
}
100
101
0
#define fputs_quoted_json(_d, _o)       fputs_quoted_case_json(_d, _o, 0, 0)
102
#define fputs_quoted_json_upper(_d, _o) fputs_quoted_case_json(_d, _o, 1, 0)
103
0
#define fputs_quoted_json_lower(_d, _o) fputs_quoted_case_json(_d, _o, -1, 0)
104
105
void ul_jsonwrt_init(struct ul_jsonwrt *fmt, FILE *out, int indent)
106
0
{
107
0
  fmt->out = out;
108
0
  fmt->indent = indent;
109
0
  fmt->after_close = 0;
110
0
}
111
112
int ul_jsonwrt_is_ready(struct ul_jsonwrt *fmt)
113
0
{
114
0
  return fmt->out == NULL ? 0 : 1;
115
0
}
116
117
void ul_jsonwrt_indent(struct ul_jsonwrt *fmt)
118
0
{
119
0
  int i;
120
121
0
  for (i = 0; i < fmt->indent; i++)
122
0
    fputs("   ", fmt->out);
123
0
}
124
125
static void print_name(struct ul_jsonwrt *fmt, const char *name)
126
0
{
127
0
  if (name) {
128
0
    if (fmt->after_close)
129
0
      fputs(",\n", fmt->out);
130
0
    ul_jsonwrt_indent(fmt);
131
0
    fputs_quoted_json_lower(name, fmt->out);
132
0
  } else {
133
0
    if (fmt->after_close)
134
0
      fputs(",", fmt->out);
135
0
    else
136
0
      ul_jsonwrt_indent(fmt);
137
0
  }
138
0
}
139
140
void ul_jsonwrt_open(struct ul_jsonwrt *fmt, const char *name, int type)
141
0
{
142
0
  print_name(fmt, name);
143
144
0
  switch (type) {
145
0
  case UL_JSON_OBJECT:
146
0
    fputs(name ? ": {\n" : "{\n", fmt->out);
147
0
    fmt->indent++;
148
0
    break;
149
0
  case UL_JSON_ARRAY:
150
0
    fputs(name ? ": [\n" : "[\n", fmt->out);
151
0
    fmt->indent++;
152
0
    break;
153
0
  case UL_JSON_VALUE:
154
0
    fputs(name ? ": " : " ", fmt->out);
155
0
    break;
156
0
  }
157
0
  fmt->after_close = 0;
158
0
}
159
160
void ul_jsonwrt_empty(struct ul_jsonwrt *fmt, const char *name, int type)
161
0
{
162
0
  print_name(fmt, name);
163
164
0
  switch (type) {
165
0
  case UL_JSON_OBJECT:
166
0
    fputs(name ? ": {}" : "{}", fmt->out);
167
0
    break;
168
0
  case UL_JSON_ARRAY:
169
0
    fputs(name ? ": []" : "[]", fmt->out);
170
0
    break;
171
0
  case UL_JSON_VALUE:
172
0
    fputs(name ? ": null" : "null", fmt->out);
173
0
    break;
174
0
  }
175
176
0
  fmt->after_close = 1;
177
0
}
178
179
void ul_jsonwrt_close(struct ul_jsonwrt *fmt, int type)
180
0
{
181
0
  assert(fmt->indent > 0);
182
183
0
  switch (type) {
184
0
  case UL_JSON_OBJECT:
185
0
    fmt->indent--;
186
0
    fputc('\n', fmt->out);
187
0
    ul_jsonwrt_indent(fmt);
188
0
    fputs("}", fmt->out);
189
0
    if (fmt->indent == 0)
190
0
      fputs("\n", fmt->out);
191
0
    break;
192
0
  case UL_JSON_ARRAY:
193
0
    fmt->indent--;
194
0
    fputc('\n', fmt->out);
195
0
    ul_jsonwrt_indent(fmt);
196
0
    fputs("]", fmt->out);
197
0
    break;
198
0
  case UL_JSON_VALUE:
199
0
    break;
200
0
  }
201
202
0
  fmt->after_close = 1;
203
0
}
204
205
206
void ul_jsonwrt_flush(struct ul_jsonwrt *fmt)
207
0
{
208
0
  fflush(fmt->out);
209
0
}
210
211
void ul_jsonwrt_value_raw(struct ul_jsonwrt *fmt,
212
      const char *name, const char *data)
213
0
{
214
0
  ul_jsonwrt_value_open(fmt, name);
215
0
  if (data && *data)
216
0
    fputs(data, fmt->out);
217
0
  else
218
0
    fputs("null", fmt->out);
219
0
  ul_jsonwrt_value_close(fmt);
220
0
}
221
222
void ul_jsonwrt_value_s(struct ul_jsonwrt *fmt,
223
      const char *name, const char *data)
224
0
{
225
0
  ul_jsonwrt_value_open(fmt, name);
226
0
  if (data && *data)
227
0
    fputs_quoted_json(data, fmt->out);
228
0
  else
229
0
    fputs("null", fmt->out);
230
0
  ul_jsonwrt_value_close(fmt);
231
0
}
232
233
void ul_jsonwrt_value_s_sized(struct ul_jsonwrt *fmt,
234
            const char *name, const char *data, size_t size)
235
0
{
236
0
  ul_jsonwrt_value_open(fmt, name);
237
0
  if (data && *data)
238
0
    fputs_quoted_case_json(data, fmt->out, 0, size);
239
0
  else
240
0
    fputs("null", fmt->out);
241
0
  ul_jsonwrt_value_close(fmt);
242
0
}
243
244
void ul_jsonwrt_value_u64(struct ul_jsonwrt *fmt,
245
      const char *name, uint64_t data)
246
0
{
247
0
  ul_jsonwrt_value_open(fmt, name);
248
0
  fprintf(fmt->out, "%"PRIu64, data);
249
0
  ul_jsonwrt_value_close(fmt);
250
0
}
251
252
void ul_jsonwrt_value_double(struct ul_jsonwrt *fmt,
253
      const char *name, long double data)
254
0
{
255
0
  ul_jsonwrt_value_open(fmt, name);
256
0
  fprintf(fmt->out, "%Lg", data);
257
0
  ul_jsonwrt_value_close(fmt);
258
0
}
259
260
void ul_jsonwrt_value_boolean(struct ul_jsonwrt *fmt,
261
      const char *name, int data)
262
0
{
263
0
  ul_jsonwrt_value_open(fmt, name);
264
0
  fputs(data ? "true" : "false", fmt->out);
265
0
  ul_jsonwrt_value_close(fmt);
266
0
}