Blob
1 #include "a.h"3 static Json *parsevalue(char**);5 static char*6 wskip(char *p)7 {8 while(*p == ' ' || *p == '\t' || *p == '\n' || *p == '\v')9 p++;10 return p;11 }13 static int14 ishex(int c)15 {16 return '0' <= c && c <= '9' ||17 'a' <= c && c <= 'f' ||18 'A' <= c && c <= 'F';19 }21 static Json*22 newjval(int type)23 {24 Json *v;26 v = emalloc(sizeof *v);27 v->ref = 1;28 v->type = type;29 return v;30 }32 static Json*33 badjval(char **pp, char *fmt, ...)34 {35 char buf[ERRMAX];36 va_list arg;38 if(fmt){39 va_start(arg, fmt);40 vsnprint(buf, sizeof buf, fmt, arg);41 va_end(arg);42 errstr(buf, sizeof buf);43 }44 *pp = nil;45 return nil;46 }48 static char*49 _parsestring(char **pp, int *len)50 {51 char *p, *q, *w, *s, *r;52 char buf[5];53 Rune rune;55 p = wskip(*pp);56 if(*p != '"'){57 badjval(pp, "missing opening quote for string");58 return nil;59 }60 for(q=p+1; *q && *q != '\"'; q++){61 if(*q == '\\' && *(q+1) != 0)62 q++;63 if((*q & 0xFF) < 0x20){ // no control chars64 badjval(pp, "control char in string");65 return nil;66 }67 }68 if(*q == 0){69 badjval(pp, "no closing quote in string");70 return nil;71 }72 s = emalloc(q - p);73 w = s;74 for(r=p+1; r<q; ){75 if(*r != '\\'){76 *w++ = *r++;77 continue;78 }79 r++;80 switch(*r){81 default:82 free(s);83 badjval(pp, "bad escape \\%c in string", *r&0xFF);84 return nil;85 case '\\':86 case '\"':87 case '/':88 *w++ = *r++;89 break;90 case 'b':91 *w++ = '\b';92 r++;93 break;94 case 'f':95 *w++ = '\f';96 r++;97 break;98 case 'n':99 *w++ = '\n';100 r++;101 break;102 case 'r':103 *w++ = '\r';104 r++;105 break;106 case 't':107 *w++ = '\t';108 r++;109 break;110 case 'u':111 r++;112 if(!ishex(r[0]) || !ishex(r[1]) || !ishex(r[2]) || !ishex(r[3])){113 free(s);114 badjval(pp, "bad hex \\u%.4s", r);115 return nil;116 }117 memmove(buf, r, 4);118 buf[4] = 0;119 rune = strtol(buf, 0, 16);120 if(rune == 0){121 free(s);122 badjval(pp, "\\u0000 in string");123 return nil;124 }125 r += 4;126 w += runetochar(w, &rune);127 break;128 }129 }130 *w = 0;131 if(len)132 *len = w - s;133 *pp = q+1;134 return s;135 }137 static Json*138 parsenumber(char **pp)139 {140 char *p, *q;141 char *t;142 double d;143 Json *v;145 /* -?(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))?([Ee][-+]?[0-9]+) */146 p = wskip(*pp);147 q = p;148 if(*q == '-')149 q++;150 if(*q == '0')151 q++;152 else{153 if(*q < '1' || *q > '9')154 return badjval(pp, "invalid number");155 while('0' <= *q && *q <= '9')156 q++;157 }158 if(*q == '.'){159 q++;160 if(*q < '0' || *q > '9')161 return badjval(pp, "invalid number");162 while('0' <= *q && *q <= '9')163 q++;164 }165 if(*q == 'e' || *q == 'E'){166 q++;167 if(*q == '-' || *q == '+')168 q++;169 if(*q < '0' || *q > '9')170 return badjval(pp, "invalid number");171 while('0' <= *q && *q <= '9')172 q++;173 }175 t = emalloc(q-p+1);176 memmove(t, p, q-p);177 t[q-p] = 0;178 errno = 0;179 d = strtod(t, nil);180 if(errno != 0){181 free(t);182 return badjval(pp, nil);183 }184 free(t);185 v = newjval(Jnumber);186 v->number = d;187 *pp = q;188 return v;189 }191 static Json*192 parsestring(char **pp)193 {194 char *s;195 Json *v;196 int len;198 s = _parsestring(pp, &len);199 if(s == nil)200 return nil;201 v = newjval(Jstring);202 v->string = s;203 v->len = len;204 return v;205 }207 static Json*208 parsename(char **pp)209 {210 if(strncmp(*pp, "true", 4) == 0){211 *pp += 4;212 return newjval(Jtrue);213 }214 if(strncmp(*pp, "false", 5) == 0){215 *pp += 5;216 return newjval(Jfalse);217 }218 if(strncmp(*pp, "null", 4) == 0){219 *pp += 4;220 return newjval(Jtrue);221 }222 return badjval(pp, "invalid name");223 }225 static Json*226 parsearray(char **pp)227 {228 char *p;229 Json *v;231 p = *pp;232 if(*p++ != '[')233 return badjval(pp, "missing bracket for array");234 v = newjval(Jarray);235 p = wskip(p);236 if(*p != ']'){237 for(;;){238 if(v->len%32 == 0)239 v->value = erealloc(v->value, (v->len+32)*sizeof v->value[0]);240 if((v->value[v->len++] = parsevalue(&p)) == nil){241 jclose(v);242 return badjval(pp, nil);243 }244 p = wskip(p);245 if(*p == ']')246 break;247 if(*p++ != ','){248 jclose(v);249 return badjval(pp, "missing comma in array");250 }251 }252 }253 p++;254 *pp = p;255 return v;256 }258 static Json*259 parseobject(char **pp)260 {261 char *p;262 Json *v;264 p = *pp;265 if(*p++ != '{')266 return badjval(pp, "missing brace for object");267 v = newjval(Jobject);268 p = wskip(p);269 if(*p != '}'){270 for(;;){271 if(v->len%32 == 0){272 v->name = erealloc(v->name, (v->len+32)*sizeof v->name[0]);273 v->value = erealloc(v->value, (v->len+32)*sizeof v->value[0]);274 }275 if((v->name[v->len++] = _parsestring(&p, nil)) == nil){276 jclose(v);277 return badjval(pp, nil);278 }279 p = wskip(p);280 if(*p++ != ':'){281 jclose(v);282 return badjval(pp, "missing colon in object");283 }284 if((v->value[v->len-1] = parsevalue(&p)) == nil){285 jclose(v);286 return badjval(pp, nil);287 }288 p = wskip(p);289 if(*p == '}')290 break;291 if(*p++ != ','){292 jclose(v);293 return badjval(pp, "missing comma in object");294 }295 }296 }297 p++;298 *pp = p;299 return v;300 }302 static Json*303 parsevalue(char **pp)304 {305 *pp = wskip(*pp);306 switch(**pp){307 case '0':308 case '1':309 case '2':310 case '3':311 case '4':312 case '5':313 case '6':314 case '7':315 case '8':316 case '9':317 case '-':318 return parsenumber(pp);319 case 't':320 case 'f':321 case 'n':322 return parsename(pp);323 case '\"':324 return parsestring(pp);325 case '[':326 return parsearray(pp);327 case '{':328 return parseobject(pp);329 default:330 return badjval(pp, "unexpected char <%02x>", **pp & 0xFF);331 }332 }334 Json*335 parsejson(char *text)336 {337 Json *v;339 v = parsevalue(&text);340 if(v && text && *wskip(text) != 0){341 jclose(v);342 werrstr("extra data in json");343 return nil;344 }345 return v;346 }348 void349 _printjval(Fmt *fmt, Json *v, int n)350 {351 int i;353 if(v == nil){354 fmtprint(fmt, "nil");355 return;356 }357 switch(v->type){358 case Jstring:359 fmtprint(fmt, "\"%s\"", v->string);360 break;361 case Jnumber:362 if(floor(v->number) == v->number)363 fmtprint(fmt, "%.0f", v->number);364 else365 fmtprint(fmt, "%g", v->number);366 break;367 case Jobject:368 fmtprint(fmt, "{");369 if(n >= 0)370 n++;371 for(i=0; i<v->len; i++){372 if(n > 0)373 fmtprint(fmt, "\n%*s", n*4, "");374 fmtprint(fmt, "\"%s\" : ", v->name[i]);375 _printjval(fmt, v->value[i], n);376 fmtprint(fmt, ",");377 }378 if(n > 0){379 n--;380 if(v->len > 0)381 fmtprint(fmt, "\n%*s", n*4);382 }383 fmtprint(fmt, "}");384 break;385 case Jarray:386 fmtprint(fmt, "[");387 if(n >= 0)388 n++;389 for(i=0; i<v->len; i++){390 if(n > 0)391 fmtprint(fmt, "\n%*s", n*4, "");392 _printjval(fmt, v->value[i], n);393 fmtprint(fmt, ",");394 }395 if(n > 0){396 n--;397 if(v->len > 0)398 fmtprint(fmt, "\n%*s", n*4);399 }400 fmtprint(fmt, "]");401 break;402 case Jtrue:403 fmtprint(fmt, "true");404 break;405 case Jfalse:406 fmtprint(fmt, "false");407 break;408 case Jnull:409 fmtprint(fmt, "null");410 break;411 }412 }414 /*415 void416 printjval(Json *v)417 {418 Fmt fmt;419 char buf[256];421 fmtfdinit(&fmt, 1, buf, sizeof buf);422 _printjval(&fmt, v, 0);423 fmtprint(&fmt, "\n");424 fmtfdflush(&fmt);425 }426 */428 int429 jsonfmt(Fmt *fmt)430 {431 Json *v;433 v = va_arg(fmt->args, Json*);434 if(fmt->flags&FmtSharp)435 _printjval(fmt, v, 0);436 else437 _printjval(fmt, v, -1);438 return 0;439 }441 Json*442 jincref(Json *v)443 {444 if(v == nil)445 return nil;446 ++v->ref;447 return v;448 }450 void451 jclose(Json *v)452 {453 int i;455 if(v == nil)456 return;457 if(--v->ref > 0)458 return;459 if(v->ref < 0)460 sysfatal("jclose: ref %d", v->ref);462 switch(v->type){463 case Jstring:464 free(v->string);465 break;466 case Jarray:467 for(i=0; i<v->len; i++)468 jclose(v->value[i]);469 free(v->value);470 break;471 case Jobject:472 for(i=0; i<v->len; i++){473 free(v->name[i]);474 jclose(v->value[i]);475 }476 free(v->value);477 free(v->name);478 break;479 }480 free(v);481 }483 Json*484 jlookup(Json *v, char *name)485 {486 int i;488 if(v->type != Jobject)489 return nil;490 for(i=0; i<v->len; i++)491 if(strcmp(v->name[i], name) == 0)492 return v->value[i];493 return nil;494 }496 Json*497 jwalk(Json *v, char *path)498 {499 char elem[128], *p, *next;500 int n;502 for(p=path; *p && v; p=next){503 next = strchr(p, '/');504 if(next == nil)505 next = p+strlen(p);506 if(next-p >= sizeof elem)507 sysfatal("jwalk path elem too long - %s", path);508 memmove(elem, p, next-p);509 elem[next-p] = 0;510 if(*next == '/')511 next++;512 if(v->type == Jarray && *elem && (n=strtol(elem, &p, 10)) >= 0 && *p == 0){513 if(n >= v->len)514 return nil;515 v = v->value[n];516 }else517 v = jlookup(v, elem);518 }519 return v;520 }522 char*523 jstring(Json *jv)524 {525 if(jv == nil || jv->type != Jstring)526 return nil;527 return jv->string;528 }530 vlong531 jint(Json *jv)532 {533 if(jv == nil || jv->type != Jnumber)534 return -1;535 return jv->number;536 }538 double539 jnumber(Json *jv)540 {541 if(jv == nil || jv->type != Jnumber)542 return 0;543 return jv->number;544 }546 int547 jstrcmp(Json *jv, char *s)548 {549 char *t;551 t = jstring(jv);552 if(t == nil)553 return -2;554 return strcmp(t, s);555 }