json-check.c - randomcrap - random crap programs of varying quality
 (HTM) git clone git://git.codemadness.org/randomcrap
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       json-check.c (5513B)
       ---
            1 /* JSON checker (RFC8259), but does not check numbers or UTF-8/16/32. */
            2 
            3 #include <ctype.h>
            4 #include <errno.h>
            5 #include <stdint.h>
            6 #include <stdio.h>
            7 #include <stdlib.h>
            8 #include <string.h>
            9 
           10 #ifdef __OpenBSD__
           11 #include <unistd.h>
           12 #else
           13 #define pledge(a,b) 0
           14 #endif
           15 
           16 enum JSONType {
           17         TYPE_ARRAY     = 'a',
           18         TYPE_OBJECT    = 'o',
           19         TYPE_OTHER     = 'p',
           20 };
           21 
           22 #define JSON_MAX_NODE_DEPTH 64
           23 
           24 static size_t offset = 0, linenr = 1;
           25 
           26 int
           27 getnext(void)
           28 {
           29         int c;
           30 
           31         if ((c = getchar()) == '\n') {
           32                 linenr++;
           33                 offset = 0;
           34         } else {
           35                 offset++;
           36         }
           37         return c;
           38 }
           39 
           40 #define GETNEXT getnext
           41 
           42 static int
           43 hexdigit(int c)
           44 {
           45         if (c >= '0' && c <= '9')
           46                 return c - '0';
           47         else if (c >= 'a' && c <= 'f')
           48                 return 10 + (c - 'a');
           49         else if (c >= 'A' && c <= 'F')
           50                 return 10 + (c - 'A');
           51         return 0;
           52 }
           53 
           54 #define EXPECT_VALUE         "{[\"-0123456789tfn"
           55 #define EXPECT_STRING        "\""
           56 #define EXPECT_END           "}],"
           57 #define EXPECT_OBJECT_STRING EXPECT_STRING "}"
           58 #define EXPECT_OBJECT_KEY    ":"
           59 #define EXPECT_ARRAY_VALUE   EXPECT_VALUE "]"
           60 
           61 #define JSON_INVALID(s)         do { fprintf(stderr, "%zu:%zu: %s\n", linenr, offset, s); return 1; } while (0);
           62 #define JSON_INVALID_EXPECTED() do { fprintf(stderr, "%zu:%zu: expected one of \"%s\"\n", linenr, offset, expect); return 1; } while (0);
           63 
           64 int
           65 parsejson(void)
           66 {
           67         int nodes[JSON_MAX_NODE_DEPTH] = { 0 };
           68         size_t depth = 0;
           69         long cp, hi, lo;
           70         int c, i, escape, iskey = 0;
           71         const char *expect = EXPECT_VALUE;
           72 
           73         while (1) {
           74                 c = GETNEXT();
           75 handlechr:
           76                 if (c == EOF)
           77                         break;
           78 
           79                 /* skip JSON white-space, (NOTE: no \v, \f, \b etc) */
           80                 if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
           81                         continue;
           82 
           83                 if (!c || !strchr(expect, c))
           84                         JSON_INVALID_EXPECTED();
           85 
           86                 switch (c) {
           87                 case ':':
           88                         iskey = 0;
           89                         expect = EXPECT_VALUE;
           90                         break;
           91                 case '"':
           92                         nodes[depth] = TYPE_OTHER;
           93                         escape = 0;
           94                         while (1) {
           95                                 c = GETNEXT();
           96 chr:
           97                                 /* EOF or control char: 0x7f is not defined as a control char in RFC8259 */
           98                                 if (c < 0x20)
           99                                         JSON_INVALID("control-character in string not allowed");
          100 
          101                                 if (escape) {
          102 escchr:
          103                                         escape = 0;
          104                                         switch (c) {
          105                                         case '"': /* FALLTHROUGH */
          106                                         case '\\':
          107                                         case '/': break;
          108                                         case 'b': c = '\b'; break;
          109                                         case 'f': c = '\f'; break;
          110                                         case 'n': c = '\n'; break;
          111                                         case 'r': c = '\r'; break;
          112                                         case 't': c = '\t'; break;
          113                                         case 'u': /* hex hex hex hex */
          114                                                 for (i = 12, cp = 0; i >= 0; i -= 4) {
          115                                                         if ((c = GETNEXT()) == EOF || !isxdigit(c))
          116                                                                 JSON_INVALID("invalid codepoint"); /* invalid codepoint */
          117                                                         cp |= (hexdigit(c) << i);
          118                                                 }
          119                                                 /* RFC8259 - 7. Strings - surrogates.
          120                                                  * 0xd800 - 0xdb7f - high surrogates */
          121                                                 if (cp >= 0xd800 && cp <= 0xdb7f) {
          122                                                         if ((c = GETNEXT()) != '\\')
          123                                                                 goto chr;
          124                                                         if ((c = GETNEXT()) != 'u')
          125                                                                 goto escchr;
          126                                                         for (hi = cp, i = 12, lo = 0; i >= 0; i -= 4) {
          127                                                                 if ((c = GETNEXT()) == EOF || !isxdigit(c))
          128                                                                         JSON_INVALID("invalid codepoint"); /* invalid codepoint */
          129                                                                 lo |= (hexdigit(c) << i);
          130                                                         }
          131                                                         /* 0xdc00 - 0xdfff - low surrogates */
          132                                                         if (lo >= 0xdc00 && lo <= 0xdfff)
          133                                                                 cp = (hi << 10) + lo - 56613888; /* - offset */
          134                                                         else
          135                                                                 continue;
          136                                                 }
          137                                                 continue;
          138                                         default:
          139                                                 JSON_INVALID("invalid escape character"); /* invalid escape char */
          140                                         }
          141                                 } else if (c == '\\') {
          142                                         escape = 1;
          143                                 } else if (c == '"') {
          144                                         break;
          145                                 }
          146                         }
          147                         if (iskey)
          148                                 expect = EXPECT_OBJECT_KEY;
          149                         else
          150                                 expect = EXPECT_END;
          151                         break;
          152                 case '[':
          153                 case '{':
          154                         if (depth + 1 >= JSON_MAX_NODE_DEPTH)
          155                                 JSON_INVALID("array/object too deep"); /* too deep */
          156 
          157                         if (c == '[') {
          158                                 nodes[depth] = TYPE_ARRAY;
          159                                 expect = EXPECT_ARRAY_VALUE;
          160                         } else if (c == '{') {
          161                                 iskey = 1;
          162                                 nodes[depth] = TYPE_OBJECT;
          163                                 expect = EXPECT_OBJECT_STRING;
          164                         }
          165                         depth++;
          166                         break;
          167                 case ']':
          168                 case '}':
          169                         if (!depth ||
          170                            (c == ']' && nodes[depth - 1] != TYPE_ARRAY) ||
          171                            (c == '}' && nodes[depth - 1] != TYPE_OBJECT))
          172                                 JSON_INVALID("unbalanced array/object"); /* unbalanced nodes */
          173 
          174                         depth--;
          175                         expect = EXPECT_END;
          176                         break;
          177                 case ',':
          178                         if (!depth)
          179                                 JSON_INVALID("unbalanced array/object"); /* unbalanced nodes */
          180 
          181                         if (nodes[depth - 1] == TYPE_OBJECT) {
          182                                 iskey = 1;
          183                                 expect = EXPECT_STRING;
          184                         } else {
          185                                 expect = EXPECT_VALUE;
          186                         }
          187                         break;
          188                 case 't': /* true */
          189                         if (GETNEXT() != 'r' || GETNEXT() != 'u' || GETNEXT() != 'e')
          190                                 JSON_INVALID("expected true type, but got something else");
          191                         nodes[depth] = TYPE_OTHER;
          192                         expect = EXPECT_END;
          193                         break;
          194                 case 'f': /* false */
          195                         if (GETNEXT() != 'a' || GETNEXT() != 'l' || GETNEXT() != 's' ||
          196                             GETNEXT() != 'e')
          197                                 JSON_INVALID("expected false type, but got something else");
          198                         nodes[depth] = TYPE_OTHER;
          199                         expect = EXPECT_END;
          200                         break;
          201                 case 'n': /* null */
          202                         if (GETNEXT() != 'u' || GETNEXT() != 'l' || GETNEXT() != 'l')
          203                                 JSON_INVALID("expected null type, but got something else");
          204                         nodes[depth] = TYPE_OTHER;
          205                         expect = EXPECT_END;
          206                         break;
          207                 default: /* number */
          208                         nodes[depth] = TYPE_OTHER;
          209                         expect = EXPECT_END;
          210                         while (1) {
          211                                 c = GETNEXT();
          212                                 if (c == EOF ||
          213                                     !c || !strchr("0123456789eE+-.", c))
          214                                         goto handlechr; /* do not read next char, handle this */
          215                         }
          216                 }
          217         }
          218         if (depth)
          219                 JSON_INVALID("unbalanced array/object"); /* unbalanced nodes */
          220 
          221         return 0;
          222 }
          223 
          224 int
          225 main(void)
          226 {
          227         if (pledge("stdio", NULL) == -1) {
          228                 fprintf(stderr, "pledge stdio: %s\n", strerror(errno));
          229                 return 1;
          230         }
          231         return parsejson();
          232 }