Imported Upstream version 0.7.2
[platform/upstream/libsolv.git] / ext / solv_jsonparser.c
1 /*
2  * solv_jsonparser.c
3  *
4  * Simple JSON stream parser
5  *
6  * Copyright (c) 2018, SUSE LLC
7  *
8  * This program is licensed under the BSD license, read LICENSE.BSD
9  * for further information
10  */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14
15 #include "util.h"
16 #include "solv_jsonparser.h"
17
18 struct solv_jsonparser *
19 jsonparser_init(struct solv_jsonparser *jp, FILE *fp)
20 {
21   memset(jp, 0, sizeof(*jp));
22   jp->fp = fp;
23   jp->state = JP_START;
24   jp->line = jp->nextline = 1;
25   jp->nextc = ' ';
26   queue_init(&jp->stateq);
27   return jp;
28 }
29
30 struct solv_jsonparser *
31 jsonparser_free(struct solv_jsonparser *jp)
32 {
33   queue_free(&jp->stateq);
34   return 0;
35 }
36
37 static void
38 savec(struct solv_jsonparser *jp, char c)
39 {
40   if (jp->nspace == jp->aspace)
41     {
42       jp->aspace += 256;
43       jp->space = solv_realloc(jp->space, jp->aspace);
44     }
45   jp->space[jp->nspace++] = c;
46 }
47
48 static void
49 saveutf8(struct solv_jsonparser *jp, int c)
50 {
51   int i;
52   if (c < 0x80)
53     {
54       savec(jp, c);
55       return;
56     }
57   i = c < 0x800 ? 1 : c < 0x10000 ? 2 : 3;
58   savec(jp, (0x1f80 >> i) | (c >> (6 * i)));
59   while (--i >= 0)
60     savec(jp, 0x80 | ((c >> (6 * i)) & 0x3f));
61 }
62
63 static inline int
64 nextc(struct solv_jsonparser *jp)
65 {
66   int c = getc(jp->fp);
67   if (c == '\n')
68     jp->nextline++;
69   return c;
70 }
71
72 static int
73 skipspace(struct solv_jsonparser *jp)
74 {
75   int c = jp->nextc;
76   while (c == ' ' || c == '\t' || c == '\r' || c == '\n')
77     c = nextc(jp);
78   jp->line = jp->nextline;
79   return (jp->nextc = c);
80 }
81
82 static int
83 parseliteral(struct solv_jsonparser *jp)
84 {
85   size_t nspace = jp->nspace;
86   int c;
87   savec(jp, jp->nextc);
88   for (;;)
89     {
90       c = nextc(jp);
91       if (c < 'a' || c > 'z')
92         break;
93       savec(jp, c);
94     }
95   jp->nextc = c;
96   savec(jp, 0);
97   if (!strcmp(jp->space + nspace, "true"))
98     return JP_BOOL;
99   if (!strcmp(jp->space + nspace, "false"))
100     return JP_BOOL;
101   if (!strcmp(jp->space + nspace, "null"))
102     return JP_NULL;
103   return JP_ERROR;
104 }
105
106 static int
107 parsenumber(struct solv_jsonparser *jp)
108 {
109   int c;
110   savec(jp, jp->nextc);
111   for (;;)
112     {
113       c = nextc(jp);
114       if ((c < '0' || c > '9') && c != '+' && c != '-' && c != '.' && c != 'e' && c != 'E')
115         break;
116       savec(jp, c);
117     }
118   jp->nextc = c;
119   savec(jp, 0);
120   return JP_NUMBER;
121 }
122
123 static int
124 parseutf8(struct solv_jsonparser *jp, int surrogate)
125 {
126   int c, i, r = 0;
127   /* parse 4-digit hex */
128   for (i = 0; i < 4; i++)
129     {
130       c = nextc(jp);
131       if (c >= '0' && c <= '9')
132         c -= '0';
133       else if (c >= 'a' && c <= 'f')
134         c -= 'a' - 10;
135       else if (c >= 'A' && c <= 'F')
136         c -= 'A' - 10;
137       else
138         return -1;
139       r = (r << 4) | c;
140     }
141   if (!surrogate && r >= 0xd800 && r < 0xdc00)
142     {
143       /* utf16 surrogate pair encodes 0x10000 - 0x10ffff */
144       int r2;
145       if (nextc(jp) != '\\' || nextc(jp) != 'u' || (r2 = parseutf8(jp, 1)) < 0xdc00 || r2 >= 0xe000)
146         return -1;
147       r = 0x10000 + ((r & 0x3ff) << 10 | (r2 & 0x3ff));
148     }
149   return r;
150 }
151
152 static int
153 parsestring(struct solv_jsonparser *jp)
154 {
155   int c;
156   for (;;)
157     {
158       if ((c = nextc(jp)) < 32)
159         return JP_ERROR;
160       if (c == '"')
161         break;
162       if (c == '\\')
163         {
164           switch (c = nextc(jp))
165             {
166             case '"':
167             case '\\':
168             case '/':
169             case '\n':
170               break;
171             case 'b':
172               c = '\b';
173               break;
174             case 'f':
175               c = '\f';
176               break;
177             case 'n':
178               c = '\n';
179               break;
180             case 'r':
181               c = '\r';
182               break;
183             case 't':
184               c = '\t';
185               break;
186             case 'u':
187               if ((c = parseutf8(jp, 0)) < 0)
188                 return JP_ERROR;
189               saveutf8(jp, c);
190               continue;
191             default:
192               return JP_ERROR;
193             }
194         }
195       savec(jp, c);
196     }
197   jp->nextc = ' ';
198   savec(jp, 0);
199   return JP_STRING;
200 }
201
202 static int
203 parsevalue(struct solv_jsonparser *jp)
204 {
205   int c = skipspace(jp);
206   if (c == '"')
207     return parsestring(jp);
208   if ((c >= '0' && c <= '9') || c == '+' || c == '-' || c == '.')
209     return parsenumber(jp);
210   if ((c >= 'a' && c <= 'z'))
211     return parseliteral(jp);
212   jp->nextc = ' ';
213   if (c == '[')
214     return JP_ARRAY;
215   if (c == '{')
216     return JP_OBJECT;
217   if (c == ']')
218     return JP_ARRAY_END;
219   if (c == '}')
220     return JP_OBJECT_END;
221   return JP_ERROR;
222 }
223
224 int
225 jsonparser_parse(struct solv_jsonparser *jp)
226 {
227   int type;
228   size_t nspace;
229
230   jp->depth = jp->stateq.count;
231   jp->key = jp->value = 0;
232   jp->keylen = jp->valuelen = 0;
233   nspace = jp->nspace = 0;
234
235   if (jp->state == JP_END)
236     return JP_END;
237   if (jp->state == JP_START)
238     jp->state = JP_END;
239   type = parsevalue(jp);
240   if (type <= 0)
241     return JP_ERROR;
242   if (type == JP_OBJECT_END || type == JP_ARRAY_END)
243     {
244       if (jp->state != type - 1)
245         return JP_ERROR;
246       jp->state = queue_pop(&jp->stateq);
247     }
248   else if (jp->state == JP_OBJECT)
249     {
250       nspace = jp->nspace;
251       if (type != JP_STRING)
252         return JP_ERROR;
253       if (skipspace(jp) != ':')
254         return JP_ERROR;
255       jp->nextc = ' ';
256       type = parsevalue(jp);
257       if (type == JP_OBJECT_END || type == JP_ARRAY_END)
258         return JP_ERROR;
259       jp->key = jp->space;
260       jp->keylen = nspace - 1;
261     }
262   if (type == JP_STRING || type == JP_NUMBER || type == JP_BOOL || type == JP_NULL)
263     {
264       jp->value = jp->space + nspace;
265       jp->valuelen = jp->nspace - nspace - 1;
266     }
267   if (type == JP_ARRAY || type == JP_OBJECT)
268     {
269       queue_push(&jp->stateq, jp->state);
270       jp->state = type;
271     }
272   else if (jp->state == JP_OBJECT || jp->state == JP_ARRAY)
273     {
274       int c = skipspace(jp);
275       if (c == ',')
276         jp->nextc = ' ';
277       else if (c != (jp->state == JP_OBJECT ? '}' : ']'))
278         return JP_ERROR;
279     }
280   return type;
281 }
282
283 int
284 jsonparser_skip(struct solv_jsonparser *jp, int type)
285 {
286   if (type == JP_ARRAY || type == JP_OBJECT)
287     {
288       int depth = jp->depth + 1, endtype = type + 1;
289       while (type > 0 && (type != endtype || jp->depth != depth))
290         type = jsonparser_parse(jp);
291     }
292   return type;
293 }
294