URI:
       tjson.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tjson.c (8526B)
       ---
            1 #include "a.h"
            2 
            3 static Json *parsevalue(char**);
            4 
            5 static char*
            6 wskip(char *p)
            7 {
            8         while(*p == ' ' || *p == '\t' || *p == '\n' || *p == '\v')
            9                 p++;
           10         return p;
           11 }
           12 
           13 static int
           14 ishex(int c)
           15 {
           16         return '0' <= c && c <= '9' ||
           17                 'a' <= c && c <= 'f' ||
           18                 'A' <= c && c <= 'F';
           19 }
           20 
           21 static Json*
           22 newjval(int type)
           23 {
           24         Json *v;
           25 
           26         v = emalloc(sizeof *v);
           27         v->ref = 1;
           28         v->type = type;
           29         return v;
           30 }
           31 
           32 static Json*
           33 badjval(char **pp, char *fmt, ...)
           34 {
           35         char buf[ERRMAX];
           36         va_list arg;
           37 
           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 }
           47 
           48 static char*
           49 _parsestring(char **pp, int *len)
           50 {
           51         char *p, *q, *w, *s, *r;
           52         char buf[5];
           53         Rune rune;
           54 
           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 chars
           64                         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 }
          136 
          137 static Json*
          138 parsenumber(char **pp)
          139 {
          140         char *p, *q;
          141         char *t;
          142         double d;
          143         Json *v;
          144 
          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         }
          174 
          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 }
          190 
          191 static Json*
          192 parsestring(char **pp)
          193 {
          194         char *s;
          195         Json *v;
          196         int len;
          197 
          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 }
          206 
          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 }
          224 
          225 static Json*
          226 parsearray(char **pp)
          227 {
          228         char *p;
          229         Json *v;
          230 
          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 }
          257 
          258 static Json*
          259 parseobject(char **pp)
          260 {
          261         char *p;
          262         Json *v;
          263 
          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 }
          301 
          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 }
          333 
          334 Json*
          335 parsejson(char *text)
          336 {
          337         Json *v;
          338 
          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 }
          347 
          348 void
          349 _printjval(Fmt *fmt, Json *v, int n)
          350 {
          351         int i;
          352 
          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                 else
          365                         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 }
          413 
          414 /*
          415 void
          416 printjval(Json *v)
          417 {
          418         Fmt fmt;
          419         char buf[256];
          420 
          421         fmtfdinit(&fmt, 1, buf, sizeof buf);
          422         _printjval(&fmt, v, 0);
          423         fmtprint(&fmt, "\n");
          424         fmtfdflush(&fmt);
          425 }
          426 */
          427 
          428 int
          429 jsonfmt(Fmt *fmt)
          430 {
          431         Json *v;
          432 
          433         v = va_arg(fmt->args, Json*);
          434         if(fmt->flags&FmtSharp)
          435                 _printjval(fmt, v, 0);
          436         else
          437                 _printjval(fmt, v, -1);
          438         return 0;
          439 }
          440 
          441 Json*
          442 jincref(Json *v)
          443 {
          444         if(v == nil)
          445                 return nil;
          446         ++v->ref;
          447         return v;
          448 }
          449 
          450 void
          451 jclose(Json *v)
          452 {
          453         int i;
          454 
          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);
          461 
          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 }
          482 
          483 Json*
          484 jlookup(Json *v, char *name)
          485 {
          486         int i;
          487 
          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 }
          495 
          496 Json*
          497 jwalk(Json *v, char *path)
          498 {
          499         char elem[128], *p, *next;
          500         int n;
          501 
          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                 }else
          517                         v = jlookup(v, elem);
          518         }
          519         return v;
          520 }
          521 
          522 char*
          523 jstring(Json *jv)
          524 {
          525         if(jv == nil || jv->type != Jstring)
          526                 return nil;
          527         return jv->string;
          528 }
          529 
          530 vlong
          531 jint(Json *jv)
          532 {
          533         if(jv == nil || jv->type != Jnumber)
          534                 return -1;
          535         return jv->number;
          536 }
          537 
          538 double
          539 jnumber(Json *jv)
          540 {
          541         if(jv == nil || jv->type != Jnumber)
          542                 return 0;
          543         return jv->number;
          544 }
          545 
          546 int
          547 jstrcmp(Json *jv, char *s)
          548 {
          549         char *t;
          550 
          551         t = jstring(jv);
          552         if(t == nil)
          553                 return -2;
          554         return strcmp(t, s);
          555 }