mirror of
https://github.com/eddyem/eddys_snippets.git
synced 2026-03-20 08:41:02 +03:00
Add simple JSON parser & fix bug wit short option absent in parseargs.c
This commit is contained in:
361
json/json.c
Normal file
361
json/json.c
Normal file
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
* json.c - simple JSON parser
|
||||
*
|
||||
* Copyright 2016 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "usefull_macros.h"
|
||||
#include "json.h"
|
||||
|
||||
/**
|
||||
* don't understand hex and octal numbers
|
||||
* don't understand mixed arrays
|
||||
*/
|
||||
|
||||
static json_object *json_collect(char *data);
|
||||
|
||||
static char *skipspaces(char *data){
|
||||
while(*data){
|
||||
char ch = *data;
|
||||
switch (ch){
|
||||
case '\r': case '\n': case '\t': case ' ':
|
||||
++data;
|
||||
break;
|
||||
default:
|
||||
goto ret;
|
||||
}
|
||||
}
|
||||
ret:
|
||||
return data;
|
||||
}
|
||||
|
||||
static json_object *json_ini(){
|
||||
json_object *ret = MALLOC(json_object, 1);
|
||||
ret->objs = MALLOC(json_pair, JSON_BLKSZ);
|
||||
ret->max_len = JSON_BLKSZ; ret->len = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void json_free_obj(json_object **obj){
|
||||
FREE((*obj)->objs);
|
||||
FREE((*obj)->original_data);
|
||||
FREE(*obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* find end of compound object & set last brace to zero
|
||||
* @return first symbol after object's end
|
||||
*/
|
||||
static char *find_obj_end(char *data){
|
||||
int opening = 0;
|
||||
while(*data && opening != -1){
|
||||
switch(*data++){
|
||||
case '{':
|
||||
++opening;
|
||||
break;
|
||||
case '}':
|
||||
--opening;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
if(opening != -1) return NULL;
|
||||
data[-1] = 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* count objects in array
|
||||
* @return first symbol after array's end
|
||||
*/
|
||||
static char *count_objs(json_pair *pair){
|
||||
int a_closing = 0, o_opening = 0, commas = 0, objects = 0, valstart = 1, values = 0; // counts ']' & '{', ',' & objects
|
||||
char *data = pair->value;
|
||||
if(!data) return NULL;
|
||||
while(*data && a_closing != 1){
|
||||
switch(*data++){
|
||||
case '{':
|
||||
++o_opening; valstart = 0;
|
||||
break;
|
||||
case '}':
|
||||
if(--o_opening == 0) ++objects;
|
||||
break;
|
||||
case '[':
|
||||
--a_closing;
|
||||
break;
|
||||
case ']':
|
||||
++a_closing;
|
||||
break;
|
||||
case ',':
|
||||
if(o_opening == 0){
|
||||
++commas; // count commas separating objects
|
||||
valstart = 1;
|
||||
}
|
||||
break;
|
||||
case '\t': case '\n': case '\r': case ' ':
|
||||
break;
|
||||
default:
|
||||
if(valstart) ++values;
|
||||
valstart = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(a_closing != 1) return NULL;
|
||||
//DBG("find array with %d objects & %d values, commas: %d", objects, values, commas);
|
||||
if((objects && commas < objects-1) || (values && commas < values-1)) return NULL; // delimeter omit
|
||||
if(objects && values) return NULL; // mixed array
|
||||
pair->len = objects + values;
|
||||
data[-1] = 0;
|
||||
pair->type = objects ? json_type_obj_array : json_type_data_array;
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* skip '.', numbers, signs & '[e|E]'
|
||||
* return first non-number
|
||||
*/
|
||||
static char *skipnumbers(char *data){
|
||||
while(*data){
|
||||
char ch = *data;
|
||||
if(ch < '0' || ch > '9'){
|
||||
if(ch != '.' && ch != 'e' && ch != 'E' && ch != '-' && ch !='+')
|
||||
break;
|
||||
}
|
||||
++data;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* read and add object from string
|
||||
*/
|
||||
int json_add_object(json_object *obj, char **data){
|
||||
//FNAME();
|
||||
if(!obj || !data || !*data || !**data) return 0;
|
||||
char *str = skipspaces(*data);
|
||||
json_pair pair;
|
||||
memset(&pair, 0, sizeof(pair));
|
||||
if(*str == '}') return 0; // no objects
|
||||
if(*str != '"') return -1; // err - not an object's name
|
||||
char *eptr = strchr(++str, '"');
|
||||
if(!eptr) return -1;
|
||||
*eptr = 0;
|
||||
pair.name = str;
|
||||
str = eptr + 1;
|
||||
str = skipspaces(str);
|
||||
if(*str != ':') return -1; // no delimeter
|
||||
str = skipspaces(str+1);
|
||||
if(*str == '"'){ // string
|
||||
pair.type = json_type_string;
|
||||
pair.value = ++str;
|
||||
eptr = strchr(str, '"');
|
||||
if(!eptr) return -1;
|
||||
*eptr = 0;
|
||||
str = eptr + 1;
|
||||
}else if(*str == '{'){ // compound object
|
||||
pair.type = json_type_object;
|
||||
pair.value = skipspaces(++str);
|
||||
str = find_obj_end(pair.value);
|
||||
}else if(*str == '['){ // array
|
||||
pair.value = skipspaces(++str);
|
||||
str = count_objs(&pair);
|
||||
}else{ // number ?
|
||||
pair.type = json_type_number;
|
||||
pair.value = str;
|
||||
str = skipnumbers(str);
|
||||
if(pair.value == str) return -1; // not a number
|
||||
}
|
||||
// skip comma & spaces, but leave '}' & add object
|
||||
if(!str) return -1;
|
||||
str = skipspaces(str);
|
||||
//DBG("char: %c", *str);
|
||||
int meetcomma = 0;
|
||||
if(*str == ','){
|
||||
*str++ = 0;
|
||||
meetcomma = 1;
|
||||
str = skipspaces(str);
|
||||
}
|
||||
if(*str == '}') --str;
|
||||
else if(!meetcomma && *str) return -1;
|
||||
*data = str;
|
||||
// add pair
|
||||
if(obj->len == obj->max_len){ // no space left - realloc
|
||||
obj->max_len += JSON_BLKSZ;
|
||||
obj->objs = realloc(obj->objs, sizeof(json_pair)*obj->max_len);
|
||||
if(!obj->objs) return -1;
|
||||
}
|
||||
memcpy(&(obj->objs[obj->len]), &pair, sizeof(json_pair));
|
||||
++obj->len;
|
||||
/*
|
||||
#ifdef EBUG
|
||||
fprintf(stderr, "pair %zd, nm: %s, val: %s, type: %d", obj->len, pair.name, pair.value, pair.type);
|
||||
if(pair.len) fprintf(stderr, "; array length: %zd", pair.len);
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static json_object *json_collect(char *data){
|
||||
//FNAME();
|
||||
json_object *ret = json_ini();
|
||||
ret->original_data = strdup(data);
|
||||
data = skipspaces(ret->original_data);
|
||||
int r;
|
||||
while((r = json_add_object(ret, &data)) > 0);
|
||||
if(r < 0) goto errjson;
|
||||
return ret;
|
||||
errjson:
|
||||
json_free_obj(&ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* get global object
|
||||
*/
|
||||
json_object *json_tokener_parse(char *data){
|
||||
data = skipspaces(data);
|
||||
if(*data != '{') return NULL;
|
||||
data = strdup(data+1);
|
||||
if(!data) return NULL;
|
||||
if(!find_obj_end(data)){
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
json_object *jobj = json_collect(data);
|
||||
free(data);
|
||||
return jobj;
|
||||
}
|
||||
|
||||
/**
|
||||
* return double value of number pair
|
||||
*/
|
||||
double json_pair_get_number(json_pair *pair){
|
||||
if(pair->type != json_type_number) return 0.;
|
||||
char *endptr;
|
||||
return strtod(pair->value, &endptr);
|
||||
}
|
||||
/**
|
||||
* return string value of string pair
|
||||
*/
|
||||
char *json_pair_get_string(json_pair *pair){
|
||||
if(pair->type != json_type_string) return NULL;
|
||||
return pair->value;
|
||||
}
|
||||
/**
|
||||
* create object from compound pair
|
||||
*/
|
||||
json_object *json_pair_get_object(json_pair *pair){
|
||||
if(pair->type != json_type_object) return NULL;
|
||||
return json_collect(pair->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* find pair with name @name
|
||||
*/
|
||||
json_pair *json_object_get_pair(json_object *obj, char *name){
|
||||
//DBG("search pair named %s", name);
|
||||
if(!obj || !name) return NULL;
|
||||
json_pair *pairs = obj->objs;
|
||||
size_t i, L = obj->len;
|
||||
for(i = 0; i < L; ++i){
|
||||
if(strcmp(name, pairs[i].name) == 0){
|
||||
//DBG("Find, val = %s", pairs[i].value);
|
||||
return &pairs[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* return new object with index idx from array
|
||||
*/
|
||||
json_object *json_array_get_object(json_pair *pair, int idx){
|
||||
//DBG("get %d object from array type %d, len %zd", idx, pair->type, pair->len);
|
||||
if(pair->type != json_type_obj_array) return NULL;
|
||||
if(pair->len < 1 || idx > pair->len) return NULL;
|
||||
int opening = 0, curidx = 0;
|
||||
char *data = pair->value, *start = NULL;
|
||||
json_object *obj = NULL;
|
||||
while(*data && curidx <= idx){
|
||||
switch(*data++){
|
||||
case '{':
|
||||
if(++opening == 1 && curidx == idx) start = data;
|
||||
break;
|
||||
case '}':
|
||||
if(--opening == 0){
|
||||
++curidx;
|
||||
if(start){ // found
|
||||
data[-1] = 0;
|
||||
obj = json_collect(start);
|
||||
//DBG("found data with idx %d, val: %s", idx, start);
|
||||
data[-1] = '}';
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
if(!start || opening || !obj->original_data){
|
||||
json_free_obj(&obj);
|
||||
return NULL;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* return allocated memory - must be free'd
|
||||
* @return - string with data
|
||||
*/
|
||||
char *json_array_get_data(json_pair *pair, int idx){
|
||||
if(pair->type != json_type_data_array) return NULL;
|
||||
if(pair->len < 1 || idx > pair->len) return NULL;
|
||||
char *data = pair->value, *eptr, *val = NULL;
|
||||
int curidx = 0;
|
||||
while(*data && curidx <= idx){
|
||||
data = skipspaces(data);
|
||||
char ch = *data;
|
||||
if(!ch) return NULL;
|
||||
if(ch != ','){
|
||||
if(curidx == idx){
|
||||
if(ch == '"'){ // string
|
||||
eptr = strchr(++data, '"');
|
||||
if(!eptr) return NULL;
|
||||
*eptr = 0;
|
||||
val = strdup(data);
|
||||
*eptr = '"';
|
||||
return val;
|
||||
}else{ // number
|
||||
eptr = skipnumbers(data);
|
||||
if(!eptr || eptr == data) return NULL;
|
||||
char oldval = *eptr;
|
||||
*eptr = 0; val = strdup(data);
|
||||
*eptr = oldval;
|
||||
return val;
|
||||
}
|
||||
}else data = strchr(data, ',');
|
||||
}else{
|
||||
do{
|
||||
data = skipspaces(data+1);
|
||||
}while(*data == ',');
|
||||
++curidx;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
Reference in New Issue
Block a user