]> git.lizzy.rs Git - plan9front.git/blob - sys/src/libjson/json.c
ip/tftpd: use procsetuser() instead of writing #c/user
[plan9front.git] / sys / src / libjson / json.c
1 #include <u.h>
2 #include <libc.h>
3 #include <ctype.h>
4 #include <json.h>
5
6 typedef struct Lex Lex;
7
8 enum {
9         TEOF,
10         TSTRING = Runemax+1,
11         TNUM,
12         TNULL,
13         TFALSE,
14         TTRUE,
15 };
16
17 struct Lex
18 {
19         char *s;
20         ulong slen;
21         int t;
22         double n;
23         char *buf;
24         Rune peeked;
25 };
26
27 static Rune
28 getch(Lex *l)
29 {
30         Rune r;
31
32         if(l->peeked){
33                 r = l->peeked;
34                 l->peeked = 0;
35                 return r;
36         }
37         if(l->s[0] == '\0')
38                 return 0;
39         l->s += chartorune(&r, l->s);
40         return r;
41 }
42
43 static Rune
44 peekch(Lex *l)
45 {
46         if(!l->peeked)
47                 l->peeked = getch(l);
48         return l->peeked;
49 }
50
51 static Rune
52 peeknonspace(Lex *l)
53 {
54         Rune r;
55
56         for(;;){
57                 r = peekch(l);
58                 if(r != 0x20 && r != 0x09 && r != 0x0A && r != 0x0D)
59                         break;
60                 getch(l);
61         }
62         return r;
63 }
64
65 static int
66 fixsurrogate(Rune *rp, Rune r2)
67 {
68         Rune r1;
69
70         r1 = *rp;
71         if(r1 >= 0xD800 && r1 <= 0xDBFF){
72                 if(r2 >= 0xDC00 && r2 <= 0xDFFF){
73                         *rp = 0x10000 + (((r1 - 0xD800)<<10) | (r2 - 0xDC00));
74                         return 0;
75                 }
76                 return 1;
77         } else
78         if(r1 >= 0xDC00 && r1 <= 0xDFFF){
79                 if(r2 >= 0xD800 && r2 <= 0xDBFF){
80                         *rp = 0x10000 + (((r2 - 0xD800)<<10) | (r1 - 0xDC00));
81                         return 0;
82                 }
83                 return 1;
84         }
85         return 0;
86 }
87
88 static int
89 lex(Lex *l)
90 {
91         Rune r, r2;
92         char *t;
93         int i;
94         char c;
95
96         peeknonspace(l);
97         r = getch(l);
98         if(r == 0 || r == '{' || r == '[' || r == ']' || r == '}' || r == ':' || r == ','){
99                 l->t = r;
100                 return 0;
101         }
102         if(r >= 0x80 || isalpha(r)){
103                 t = l->buf;
104                 for(;;){
105                         t += runetochar(t, &r);
106                         if(t >= l->buf + l->slen){
107                                 werrstr("json: literal too long");
108                                 return -1;
109                         }
110                         r = peekch(l);
111                         if(r < 0x80 && !isalpha(r))
112                                 break;
113                         getch(l);
114                 }
115                 *t = 0;
116                 if(strcmp(l->buf, "true") == 0)
117                         l->t = TTRUE;
118                 else if(strcmp(l->buf, "false") == 0)
119                         l->t = TFALSE;
120                 else if(strcmp(l->buf, "null") == 0)
121                         l->t = TNULL;
122                 else{
123                         werrstr("json: invalid literal");
124                         return -1;
125                 }
126                 return 0;
127         }
128         if(isdigit(r) || r == '-'){
129                 l->n = strtod(l->s-1, &l->s);
130                 l->t = TNUM;
131                 return 0;
132         }
133         if(r == '"'){
134                 r2 = 0;
135                 t = l->buf;
136                 for(;;){
137                         r = getch(l);
138                         if(r == '"')
139                                 break;
140                         if(r < ' '){
141                                 werrstr("json: invalid char in string %x", r);
142                                 return -1;
143                         }
144                         if(r == '\\'){
145                                 r = getch(l);
146                                 switch(r){
147                                 case 'n':
148                                         r = '\n';
149                                         break;
150                                 case 'r':
151                                         r = '\r';
152                                         break;
153                                 case 'u':
154                                         r = 0;
155                                         for(i = 0; i < 4; i++){
156                                                 if(!isxdigit(peekch(l)))
157                                                         break;
158
159                                                 c = getch(l);
160                                                 r *= 16;
161                                                 if(c >= '0' && c <= '9')
162                                                         r += c - '0';
163                                                 else if(c >= 'a' && c <= 'f')
164                                                         r += c - 'a' + 10;
165                                                 else if(c >= 'A' && c <= 'F')
166                                                         r += c - 'A' + 10;
167                                         }
168                                         if(fixsurrogate(&r, r2)){
169                                                 r2 = r;
170                                                 continue;
171                                         }
172                                         break;
173                                 case 't':
174                                         r = '\t';
175                                         break;
176                                 case 'f':
177                                         r = '\f';
178                                         break;
179                                 case 'b':
180                                         r = '\b';
181                                         break;
182                                 case '"': case '/': case '\\':
183                                         break;
184                                 default:
185                                         werrstr("json: invalid escape sequence \\%C", r);
186                                         return -1;
187                                 }
188                         }
189                         r2 = 0;
190                         t += runetochar(t, &r);
191                         if(t >= l->buf + l->slen){
192                                 werrstr("json: string too long");
193                                 return -1;
194                         }
195                 }
196                 *t = 0;
197                 l->t = TSTRING;
198                 return 0;
199         }
200         werrstr("json: invalid char %C", peekch(l));
201         return -1;
202 }
203
204 static JSON*
205 jsonobj(Lex *l)
206 {
207         JSON *j;
208         JSONEl *e;
209         JSONEl **ln;
210         int obj;
211
212         if((j = mallocz(sizeof(*j), 1)) == nil)
213                 return nil;
214
215         if(lex(l) < 0){
216 error:
217                 free(j);
218                 return nil;
219         }
220         switch(l->t){
221         case TEOF:
222                 werrstr("json: unexpected eof");
223                 goto error;
224         case TNULL:
225                 j->t = JSONNull;
226                 break;
227         case TTRUE:
228                 j->t = JSONBool;
229                 j->n = 1;
230                 break;
231         case TFALSE:
232                 j->t = JSONBool;
233                 j->n = 0;
234                 break;
235         case TSTRING:
236                 j->t = JSONString;
237                 if((j->s = strdup(l->buf)) == nil)
238                         goto error;
239                 break;
240         case TNUM:
241                 j->t = JSONNumber;
242                 j->n = l->n;
243                 break;
244         case '{':
245         case '[':
246                 obj = l->t == '{';
247                 ln = &j->first;
248                 if(obj){
249                         j->t = JSONObject;
250                         if(lex(l) < 0)
251                                 goto abort;
252                         if(l->t == '}')
253                                 return j;
254                         goto firstobj;
255                 }else{
256                         j->t = JSONArray;
257                         if(peeknonspace(l) == ']'){
258                                 getch(l);
259                                 return j;
260                         }
261                 }
262                 for(;;){
263                         if(obj){
264                                 if(lex(l) < 0)
265                                         goto abort;
266                         firstobj:
267                                 if(l->t != TSTRING){
268                                         werrstr("json: syntax error, not string");
269                                         goto abort;
270                                 }
271                                 if((e = mallocz(sizeof(*e), 1)) == nil)
272                                         goto abort;
273                                 e->name = strdup(l->buf);
274                                 if(e->name == nil || lex(l) < 0){
275                                         free(e);
276                                         goto abort;
277                                 }
278                                 if(l->t != ':'){
279                                         werrstr("json: syntax error, not colon");
280                                         free(e);
281                                         goto abort;
282                                 }
283                         }else{
284                                 if((e = mallocz(sizeof(*e), 1)) == nil)
285                                         goto abort;
286                         }
287                         e->val = jsonobj(l);
288                         if(e->val == nil){
289                                 free(e);
290                                 goto abort;
291                         }
292                         *ln = e;
293                         ln = &e->next;
294                         if(lex(l) < 0)
295                                 goto abort;
296                         if(l->t == (obj ? '}' : ']'))
297                                 break;
298                         if(l->t != ','){
299                                 werrstr("json: syntax error, neither comma nor ending paren");
300                                 goto abort;
301                         }
302                 }
303                 break;
304         abort:
305                 jsonfree(j);
306                 return nil;
307         case ']': case '}': case ',': case ':':
308                 werrstr("json: unexpected %C", l->t);
309                 goto error;
310         default:
311                 werrstr("json: the front fell off");
312                 goto error;
313         }
314         return j;
315 }
316
317 JSON*
318 jsonparse(char *s)
319 {
320         JSON *j;
321         Lex l;
322
323         memset(&l, 0, sizeof(l));
324         l.s = s;
325         l.slen = strlen(s);
326         if((l.buf = mallocz(l.slen+UTFmax+1, 1)) == nil)
327                 return nil;
328
329         j = jsonobj(&l);
330         free(l.buf);
331         return j;
332 }
333
334 void
335 jsonfree(JSON *j)
336 {
337         JSONEl *e, *f;
338
339         if(j == nil)
340                 return;
341         switch(j->t){
342         case JSONString:
343                 if(j->s)
344                         free(j->s);
345                 break;
346         case JSONArray: case JSONObject:
347                 for(e = j->first; e != nil; e = f){
348                         if(e->name)
349                                 free(e->name);
350                         jsonfree(e->val);
351                         f = e->next;
352                         free(e);
353                 }
354         }
355         free(j);
356 }
357
358 JSON *
359 jsonbyname(JSON *j, char *n)
360 {
361         JSONEl *e;
362         
363         if(j->t != JSONObject){
364                 werrstr("not an object");
365                 return nil;
366         }
367         for(e = j->first; e != nil; e = e->next)
368                 if(strcmp(e->name, n) == 0)
369                         return e->val;
370         werrstr("key '%s' not found", n);
371         return nil;
372 }
373
374 char *
375 jsonstr(JSON *j)
376 {
377         if(j == nil)
378                 return nil;
379         if(j->t != JSONString){
380                 werrstr("not a string");
381                 return nil;
382         }
383         return j->s;
384 }