Coverage Report

Created: 2025-02-26 06:21

/src/libplist/src/jsmn.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * jsmn.c
3
 * Simple JSON parser
4
 *
5
 * Copyright (c) 2010 Serge A. Zaitsev
6
 *
7
 * Permission is hereby granted, free of charge, to any person obtaining a copy
8
 * of this software and associated documentation files (the "Software"), to deal
9
 * in the Software without restriction, including without limitation the rights
10
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
 * copies of the Software, and to permit persons to whom the Software is
12
 * furnished to do so, subject to the following conditions:
13
 *
14
 * The above copyright notice and this permission notice shall be included in
15
 * all copies or substantial portions of the Software.
16
 *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
 * THE SOFTWARE.
24
 */
25
26
#include <stdlib.h>
27
28
#include "jsmn.h"
29
30
/**
31
 * Allocates a fresh unused token from the token pull.
32
 */
33
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
34
0
    jsmntok_t *tokens, int num_tokens) {
35
0
  jsmntok_t *tok;
36
0
  if (parser->toknext >= num_tokens) {
37
0
    return NULL;
38
0
  }
39
0
  tok = &tokens[parser->toknext++];
40
0
  tok->start = tok->end = -1;
41
0
  tok->size = 0;
42
#ifdef JSMN_PARENT_LINKS
43
  tok->parent = -1;
44
#endif
45
0
  return tok;
46
0
}
47
48
/**
49
 * Fills token type and boundaries.
50
 */
51
static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
52
0
                            int start, int end) {
53
0
  token->type = type;
54
0
  token->start = start;
55
0
  token->end = end;
56
0
  token->size = 0;
57
0
}
58
59
/**
60
 * Fills next available token with JSON primitive.
61
 */
62
static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js,
63
0
    jsmntok_t *tokens, int num_tokens) {
64
0
  jsmntok_t *token;
65
0
  int start;
66
67
0
  start = parser->pos;
68
69
0
  for (; (parser->end > 0 && parser->pos < parser->end) && js[parser->pos] != '\0'; parser->pos++) {
70
0
    switch (js[parser->pos]) {
71
0
#ifndef JSMN_STRICT
72
      /* In strict mode primitive must be followed by "," or "}" or "]" */
73
0
      case ':':
74
0
#endif
75
0
      case '\t' : case '\r' : case '\n' : case ' ' :
76
0
      case ','  : case ']'  : case '}' :
77
0
        goto found;
78
0
      default:
79
0
        break;
80
0
    }
81
0
    if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
82
0
      parser->pos = start;
83
0
      return JSMN_ERROR_INVAL;
84
0
    }
85
0
  }
86
#ifdef JSMN_STRICT
87
  /* In strict mode primitive must be followed by a comma/object/array */
88
  parser->pos = start;
89
  return JSMN_ERROR_PART;
90
#endif
91
92
0
found:
93
0
  token = jsmn_alloc_token(parser, tokens, num_tokens);
94
0
  if (token == NULL) {
95
0
    parser->pos = start;
96
0
    return JSMN_ERROR_NOMEM;
97
0
  }
98
0
  jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
99
#ifdef JSMN_PARENT_LINKS
100
  token->parent = parser->toksuper;
101
#endif
102
0
  parser->pos--;
103
0
  return JSMN_SUCCESS;
104
0
}
105
106
/**
107
 * Fills next token with JSON string.
108
 */
109
static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js,
110
0
    jsmntok_t *tokens, int num_tokens) {
111
0
  jsmntok_t *token;
112
113
0
  int start = parser->pos;
114
115
0
  parser->pos++;
116
117
  /* Skip starting quote */
118
0
  for (; (parser->end > 0 && parser->pos < parser->end) && js[parser->pos] != '\0'; parser->pos++) {
119
0
    char c = js[parser->pos];
120
121
    /* Quote: end of string */
122
0
    if (c == '\"') {
123
0
      token = jsmn_alloc_token(parser, tokens, num_tokens);
124
0
      if (token == NULL) {
125
0
        parser->pos = start;
126
0
        return JSMN_ERROR_NOMEM;
127
0
      }
128
0
      jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
129
#ifdef JSMN_PARENT_LINKS
130
      token->parent = parser->toksuper;
131
#endif
132
0
      return JSMN_SUCCESS;
133
0
    }
134
135
    /* Backslash: Quoted symbol expected */
136
0
    if (c == '\\') {
137
0
      parser->pos++;
138
0
      if (parser->end > 0 && parser->pos >= parser->end) {
139
0
        parser->pos = start;
140
0
        return JSMN_ERROR_INVAL;
141
0
      }
142
0
      switch (js[parser->pos]) {
143
        /* Allowed escaped symbols */
144
0
        case '\"': case '/' : case '\\' : case 'b' :
145
0
        case 'f' : case 'r' : case 'n'  : case 't' :
146
0
          break;
147
        /* Allows escaped symbol \uXXXX */
148
0
        case 'u':
149
          /* TODO */
150
0
          break;
151
        /* Unexpected symbol */
152
0
        default:
153
0
          parser->pos = start;
154
0
          return JSMN_ERROR_INVAL;
155
0
      }
156
0
    }
157
0
  }
158
0
  parser->pos = start;
159
0
  return JSMN_ERROR_PART;
160
0
}
161
162
/**
163
 * Parse JSON string and fill tokens.
164
 */
165
jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, unsigned int length, jsmntok_t *tokens,
166
0
    unsigned int num_tokens) {
167
0
  jsmnerr_t r;
168
0
  int i;
169
0
  jsmntok_t *token;
170
171
0
  parser->end = length;
172
173
0
  for (; (parser->end > 0 && parser->pos < parser->end) && js[parser->pos] != '\0'; parser->pos++) {
174
0
    char c;
175
0
    jsmntype_t type;
176
177
0
    c = js[parser->pos];
178
0
    switch (c) {
179
0
      case '{': case '[':
180
0
        token = jsmn_alloc_token(parser, tokens, num_tokens);
181
0
        if (token == NULL)
182
0
          return JSMN_ERROR_NOMEM;
183
0
        if (parser->toksuper != -1) {
184
0
          tokens[parser->toksuper].size++;
185
#ifdef JSMN_PARENT_LINKS
186
          token->parent = parser->toksuper;
187
#endif
188
0
        }
189
0
        token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
190
0
        token->start = parser->pos;
191
0
        parser->toksuper = parser->toknext - 1;
192
0
        break;
193
0
      case '}': case ']':
194
0
        type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
195
#ifdef JSMN_PARENT_LINKS
196
        if (parser->toknext < 1) {
197
          return JSMN_ERROR_INVAL;
198
        }
199
        token = &tokens[parser->toknext - 1];
200
        for (;;) {
201
          if (token->start != -1 && token->end == -1) {
202
            if (token->type != type) {
203
              return JSMN_ERROR_INVAL;
204
            }
205
            token->end = parser->pos + 1;
206
            parser->toksuper = token->parent;
207
            break;
208
          }
209
          if (token->parent == -1) {
210
            break;
211
          }
212
          token = &tokens[token->parent];
213
        }
214
#else
215
0
        for (i = parser->toknext - 1; i >= 0; i--) {
216
0
          token = &tokens[i];
217
0
          if (token->start != -1 && token->end == -1) {
218
0
            if (token->type != type) {
219
0
              return JSMN_ERROR_INVAL;
220
0
            }
221
0
            parser->toksuper = -1;
222
0
            token->end = parser->pos + 1;
223
0
            break;
224
0
          }
225
0
        }
226
        /* Error if unmatched closing bracket */
227
0
        if (i == -1) return JSMN_ERROR_INVAL;
228
0
        for (; i >= 0; i--) {
229
0
          token = &tokens[i];
230
0
          if (token->start != -1 && token->end == -1) {
231
0
            parser->toksuper = i;
232
0
            break;
233
0
          }
234
0
        }
235
0
#endif
236
0
        break;
237
0
      case '\"':
238
0
        r = jsmn_parse_string(parser, js, tokens, num_tokens);
239
0
        if (r < 0) return r;
240
0
        if (parser->toksuper != -1)
241
0
          tokens[parser->toksuper].size++;
242
0
        break;
243
0
      case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ':
244
0
        break;
245
#ifdef JSMN_STRICT
246
      /* In strict mode primitives are: numbers and booleans */
247
      case '-': case '0': case '1' : case '2': case '3' : case '4':
248
      case '5': case '6': case '7' : case '8': case '9':
249
      case 't': case 'f': case 'n' :
250
#else
251
      /* In non-strict mode every unquoted value is a primitive */
252
0
      default:
253
0
#endif
254
0
        r = jsmn_parse_primitive(parser, js, tokens, num_tokens);
255
0
        if (r < 0) return r;
256
0
        if (parser->toksuper != -1)
257
0
          tokens[parser->toksuper].size++;
258
0
        break;
259
260
#ifdef JSMN_STRICT
261
      /* Unexpected char in strict mode */
262
      default:
263
        return JSMN_ERROR_INVAL;
264
#endif
265
266
0
    }
267
0
  }
268
269
0
  for (i = parser->toknext - 1; i >= 0; i--) {
270
    /* Unmatched opened object or array */
271
0
    if (tokens[i].start != -1 && tokens[i].end == -1) {
272
0
      return JSMN_ERROR_PART;
273
0
    }
274
0
  }
275
276
0
  return JSMN_SUCCESS;
277
0
}
278
279
/**
280
 * Creates a new parser based over a given  buffer with an array of tokens
281
 * available.
282
 */
283
0
void jsmn_init(jsmn_parser *parser) {
284
0
  parser->pos = 0;
285
0
  parser->end = 0;
286
0
  parser->toknext = 0;
287
0
  parser->toksuper = -1;
288
0
}
289