diff options
Diffstat (limited to 'imap/src/c-client/json.c')
-rw-r--r-- | imap/src/c-client/json.c | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/imap/src/c-client/json.c b/imap/src/c-client/json.c new file mode 100644 index 00000000..f6d05736 --- /dev/null +++ b/imap/src/c-client/json.c @@ -0,0 +1,497 @@ +/* + * Copyright 2018 Eduardo Chappa + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ +#ifdef STANDALONE +#include "headers.h" +#include "mem.h" +#include "readfile.h" +#include "json.h" +#else +#include <ctype.h> +#include <stdio.h> +#include <time.h> +#include "c-client.h" +#include "json.h" +#endif /* STANDALONE */ + +#define NIL 0 + +#define json_ws(X) (((X) == ' ' || (X) == '\t' || (X) == '\n' || (X) == '\r') ? 1 : 0) + +#define json_skipws(X) for(; json_ws(*(X)); (X)++) + +#define json_skipchar(X) { \ + (X)++; \ + json_skipws((X)); \ +} + +#define json_last_char(X) (((X) == ',' || (X) == ']' || (X) == '}') ? 1 : 0) + +JObjType json_number_type(unsigned char *); +unsigned char *json_strchr(unsigned char *, unsigned char); +JSON_S *json_parse_work(unsigned char **); +JSON_S *json_parse_pairs(unsigned char **); + +JSON_X *json_value_parse(unsigned char **); +void json_value_free(JSON_X **); + +/* An array is a JSON object where the name is null */ +JSON_S *json_array_parse_work(unsigned char **); +JSON_S *json_array_parse(unsigned char **); +void json_array_free(JSON_S **); + +#ifdef STANDALONE +int compare_cstring (unsigned char *,unsigned char *); +int compare_uchar (unsigned char,unsigned char); +int compare_ulong (unsigned long,unsigned long); + +int compare_ulong (unsigned long l1,unsigned long l2) +{ + if (l1 < l2) return -1; + if (l1 > l2) return 1; + return 0; +} + +int compare_uchar (unsigned char c1,unsigned char c2) +{ + return compare_ulong (((c1 >= 'a') && (c1 <= 'z')) ? c1 - ('a' - 'A') : c1, + ((c2 >= 'a') && (c2 <= 'z')) ? c2 - ('a' - 'A') : c2); +} + +int compare_cstring (unsigned char *s1,unsigned char *s2) +{ + int i; + if (!s1) return s2 ? -1 : 0; /* empty string cases */ + else if (!s2) return 1; + for (; *s1 && *s2; s1++,s2++) if ((i = (compare_uchar (*s1,*s2))) != 0) return i; + if (*s1) return 1; /* first string is longer */ + return *s2 ? -1 : 0; /* second string longer : strings identical */ +} +#endif /* STANDALONE */ + +/* we are parsing from the text of the json object, so it is + * never possible to have a null character, unless something + * is corrupt, in whose case we return JNumberError. + */ +JObjType json_number_type(unsigned char *s) +{ + unsigned char *w = s, *u; + int PossibleDecimal, PossibleExponential; + + PossibleDecimal = PossibleExponential = 0; + + if(*w == '+' || *w == '-') + w++; + + if (*w == '0'){ + u = w + 1; + if(json_ws(*u)) json_skipws(u); + if(json_last_char(*u)) return JLong; + else if(*(w+1) == '.'){ + for(u = w+2; *u >= '0' && *u <= '9'; u++); + if(json_ws(*u)) json_skipws(u); + if(json_last_char(*u)) return JDecimal; + else return JNumberError; + } + else return JNumberError; + } + + if(*w < '1' || *w > '9') + return JNumberError; + + u = w + 1; + if(json_ws(*u)) json_skipws(u); + if(json_last_char(*u)) return JLong; + else if (*(w+1) == 'e' || *(w+1) == 'E'){ + PossibleExponential++; + w += 2; + } + else if(*(w+1) == '.'){ + PossibleDecimal++; + PossibleExponential++; + w += 2; + } else { + for(; *w >= '0' && *w <= '9'; w++); + if(*w == '.'){ + PossibleDecimal++; + w++; + } + else { + if(json_ws(*w)) json_skipws(w); + if(json_last_char(*w)) return JLong; + else return JNumberError; + } + } + + if(PossibleDecimal){ + for(; *w >= '0' && *w <= '9'; w++); + u = w; + if(json_ws(*u)) json_skipws(u); + if(json_last_char(*u)) return JDecimal; + else if (*w == 'e' || *w == 'E'){ + PossibleExponential++; + w++; + } + else return JNumberError; + } + + if(PossibleExponential){ + if(*w == '+' || *w == '-') + w++; + if(*w == '0'){ + u = w + 1; + if(json_ws(*u)) json_skipws(u); + if(json_last_char(*u)) return JExponential; + else return JNumberError; + } + if(*w < '1' || *w > '9') + return JNumberError; + for(; *w >= '0' && *w <= '9'; w++); + if(json_ws(*w)) json_skipws(w); + if(json_last_char(*w)) return JExponential; + else return JNumberError; + } + return JNumberError; /* never reached */ +} + + +JSON_X * +json_body_value(JSON_S *j, unsigned char *s) +{ + JSON_S *js; + + if(!j || !j->value|| j->value->jtype != JObject) return NIL; + + for(js = (JSON_S *) j->value->value; js ; js = js->next) + if(js->name && !compare_cstring(js->name, s)) + break; + + return js ? js->value : NIL; +} + +unsigned char * +json_strchr(unsigned char *s, unsigned char c) +{ + unsigned char *t, d; + int escaped, extended; + + if(c == '\0'){ + t = s + strlen((char *) s); + return t; + } + + escaped = extended = 0; + for(t = s; (d = *t) != '\0';){ + if(escaped){ + if (d == '\"' || d == '\\' || d == '/' || d == 'b' + || d == 'f' || d == 'n' || d == 'r' || d == 't'){ + escaped = 0; + t++; + continue; + } + else{ + if(d == 'u'){ + escaped = 0; + extended = 1; + t++; + } + else + return NIL; + } + } + else { + if(d == '\\'){ + escaped = 1; + t++; + continue; + } + else if(extended){ + int i; + static char HEX[] = "abcdefABCDEF0123456789"; + + if(strlen((char *) t) < 4) + return NIL; + + for(i = 0; i < 4; i++){ + if(!strchr(HEX, *t)) + return NIL; + else + t++; + } + extended = 0; + } + else if (d == c) + break; + else t++; + } + } + return (*t == '\0') ? NULL : t; +} + +JSON_X * +json_value_parse(unsigned char **s) +{ + JSON_X *jx; + unsigned char *u, *w; + unsigned long *l; + + w = *s; + json_skipws(w); + jx = fs_get(sizeof(JSON_X)); + memset((void **) jx, 0, sizeof(JSON_X)); + jx->jtype = JEnd; + switch(*w){ + case '\"': jx->jtype = JString; break; + case '{' : jx->jtype = JObject; break; + case '[' : jx->jtype = JArray; break; + case 'f' : if(strlen((char *) w) > 5 + && !strncmp((char *) w, "false", 5)){ + u = w+5; + json_skipws(u); + if(json_last_char(*u)) + jx->jtype = JBoolean; + } + break; + case 'n' : if(strlen((char *) w) > 4 + && !strncmp((char *) w, "null", 4)){ + u = w+4; + json_skipws(u); + if(json_last_char(*u)) + jx->jtype = JNull; + } + break; + case 't' : if(strlen((char *) w) > 4 + && !strncmp((char *) w, "true", 4)){ + u = w+4; + json_skipws(u); + if(json_last_char(*u)) + jx->jtype = JBoolean; + } + break; + default: jx->jtype = json_number_type(w); + break; + } + + switch(jx->jtype){ + case JString: u = json_strchr(w+1, '\"'); + if(u != NULL){ + *u = '\0'; + jx->value = (void *) cpystr((char *) w+1); + *u = '\"'; + json_skipchar(u); + w = u; + } + break; + + case JObject: jx->value = (void *) json_parse(&w); + break; + + case JLong : l = fs_get(sizeof(unsigned long)); + *l = strtoul((char *) w, (char **) &w, 10); + jx->value = (void *) l; + json_skipws(w); + break; + + case JDecimal : + case JExponential: l = fs_get(sizeof(double)); + *l = strtod((char *) w, (char **) &w); + jx->value = (void *) l; + json_skipws(w); + break; + + case JBoolean: if(*w == 't'){ + jx->value = (void *) cpystr("true"); + w += 4; + } + else{ + jx->value = (void *) cpystr("false"); + w += 5; + } + json_skipws(w); + break; + + case JNull: jx->value = (void *) cpystr("null"); + w += 4; + json_skipws(w); + break; + + case JArray: jx->value = json_array_parse(&w); + json_skipchar(w); + break; + + default: break; /* let caller handle this */ + } + *s = w; + return jx; +} + +JSON_S * +json_array_parse(unsigned char **s) +{ + JSON_S *j; + unsigned char *w = *s; + + json_skipws(w); + if(*w == '['){ + json_skipchar(w); + j = json_array_parse_work(&w); + } + *s = w; + return j; +} + +JSON_S * +json_array_parse_work(unsigned char **s) +{ + unsigned char *w = *s; + JSON_S *j; + + json_skipws(w); + j = fs_get(sizeof(JSON_S)); + memset((void *) j, 0, sizeof(JSON_S)); + j->value = json_value_parse(&w); + json_skipws(w); + switch(*w){ + case ',' : json_skipchar(w); + j->next = json_array_parse_work(&w); + break; + + case ']' : break; + default : json_free(&j); + } + *s = w; + return j; +} + +void +json_array_free(JSON_S **j) +{ + json_free(j); +} + +JSON_S * +json_parse_pairs(unsigned char **s) +{ + JSON_S *j; + unsigned char *u, *w = *s; + + json_skipws(w); + if(*w++ != '\"') + return NIL; + + u = json_strchr(w, '\"'); + if(!u) + return NIL; + + *u = '\0'; + j = fs_get(sizeof(JSON_S)); + memset((void *) j, 0, sizeof(JSON_S)); + j->name = (unsigned char *) cpystr((char *) w); + + *u = '\"'; + json_skipchar(u); + if(*u != ':') + return j; + json_skipchar(u); + + j->value = json_value_parse(&u); + json_skipws(u); + if (*u == ','){ + json_skipchar(u); + j->next = json_parse_pairs(&u); + } + *s = u; + return j; +} + +void +json_value_free(JSON_X **jxp) +{ + if(!jxp || !*jxp) + return; + + switch((*jxp)->jtype){ + case JString: + case JLong : + case JDecimal: + case JExponential: + case JBoolean: + case JNull: fs_give((void **) &(*jxp)->value); + break; + + case JArray: json_array_free((JSON_S **) &(*jxp)->value); + break; + + case JObject: json_free((JSON_S **) &(*jxp)->value); + break; + + default: printf("Unhandled case in json_value_free"); + exit(1); + } +} + +void +json_free(JSON_S **jp) +{ + + if(jp == NULL || *jp == NULL) return; + + if((*jp)->name) fs_give((void **) &(*jp)->name); + if((*jp)->value) json_value_free(&(*jp)->value); + if((*jp)->next) json_free(&(*jp)->next); + fs_give((void **) jp); +} + +JSON_S * +json_parse(unsigned char **s) +{ + JSON_S *j = NULL; + JSON_X *jx; + unsigned char *w = *s; + + json_skipws(w); + if(*w == '{'){ + json_skipchar(w); + jx = fs_get(sizeof(JSON_X)); + memset((void *) jx, 0, sizeof(JSON_X)); + jx->jtype = JObject; + jx->value = (void *) json_parse_pairs(&w); + j = fs_get(sizeof(JSON_S)); + memset((void *) j, 0, sizeof(JSON_S)); + j->value = jx; + json_skipws(w); + if(*w == '}'){ + json_skipchar(w); + } + else + json_free(&j); + } + *s = w; + return j; +} + +#ifdef STANDALONE +int main (int argc, char *argv[]) +{ + unsigned char *s, *t; + JSON_S *json; + JSON_X *jx; + + t = NULL; + readfile(argv[1], (char **) &t, NULL); + s = t; + json = json_parse(&s); + if(!*s) printf("Success!\n"); +// jx = json_body_value(json, (unsigned char *) "subject"); +// if(jx) printf("subject = %s\n", (char *) jx->value); + if(t) fs_give((void **)&t); + exit(0); +} +#endif /* STANDALONE */ |