diff --git a/getopt/cmdlnopts/parceargs.c b/getopt/cmdlnopts/parceargs.c deleted file mode 100644 index 09914d4..0000000 --- a/getopt/cmdlnopts/parceargs.c +++ /dev/null @@ -1,460 +0,0 @@ -/* - * parceargs.c - parcing command line arguments & print help - * - * Copyright 2013 Edward V. Emelianoff - * - * 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 // printf -#include // getopt_long -#include // calloc, exit, strtoll -#include // assert -#include // strdup, strchr, strlen -#include // strcasecmp -#include // INT_MAX & so on -#include // gettext -#include // isalpha -#include "parceargs.h" -#include "usefull_macros.h" - -char *helpstring = "%s\n"; - -/** - * Change standard help header - * MAY consist ONE "%s" for progname - * @param str (i) - new format - */ -void change_helpstring(char *s){ - int pcount = 0, scount = 0; - char *str = s; - // check `helpstring` and set it to default in case of error - for(; pcount < 2; str += 2){ - if(!(str = strchr(str, '%'))) break; - if(str[1] != '%') pcount++; // increment '%' counter if it isn't "%%" - else{ - str += 2; // pass next '%' - continue; - } - if(str[1] == 's') scount++; // increment "%s" counter - }; - if(pcount > 1 || pcount != scount){ // amount of pcount and/or scount wrong - /// "Неправильный формат строки помощи" - ERRX(_("Wrong helpstring!")); - } - helpstring = s; -} - -/** - * Carefull atoll/atoi - * @param num (o) - returning value (or NULL if you wish only check number) - allocated by user - * @param str (i) - string with number must not be NULL - * @param t (i) - T_INT for integer or T_LLONG for long long (if argtype would be wided, may add more) - * @return TRUE if conversion sone without errors, FALSE otherwise - */ -static bool myatoll(void *num, char *str, argtype t){ - long long tmp, *llptr; - int *iptr; - char *endptr; - assert(str); - assert(num); - tmp = strtoll(str, &endptr, 0); - if(endptr == str || *str == '\0' || *endptr != '\0') - return FALSE; - switch(t){ - case arg_longlong: - llptr = (long long*) num; - *llptr = tmp; - break; - case arg_int: - default: - if(tmp < INT_MIN || tmp > INT_MAX){ - /// "Целое вне допустимого диапазона" - WARNX(_("Integer out of range")); - return FALSE; - } - iptr = (int*)num; - *iptr = (int)tmp; - } - return TRUE; -} - -// the same as myatoll but for double -// There's no NAN & INF checking here (what if they would be needed?) -static bool myatod(void *num, const char *str, argtype t){ - double tmp, *dptr; - float *fptr; - char *endptr; - assert(str); - tmp = strtod(str, &endptr); - if(endptr == str || *str == '\0' || *endptr != '\0') - return FALSE; - switch(t){ - case arg_double: - dptr = (double *) num; - *dptr = tmp; - break; - case arg_float: - default: - fptr = (float *) num; - *fptr = (float)tmp; - break; - } - return TRUE; -} - -/** - * Get index of current option in array options - * @param opt (i) - returning val of getopt_long - * @param options (i) - array of options - * @return index in array - */ -static int get_optind(int opt, myoption *options){ - int oind; - myoption *opts = options; - assert(opts); - for(oind = 0; opts->name && opts->val != opt; oind++, opts++); - if(!opts->name || opts->val != opt) // no such parameter - showhelp(-1, options); - return oind; -} - -/** - * reallocate new value in array of multiple repeating arguments - * @arg paptr - address of pointer to array (**void) - * @arg type - its type (for realloc) - * @return pointer to new (next) value - */ -void *get_aptr(void *paptr, argtype type){ - int i = 1; - void **aptr = *((void***)paptr); - if(aptr){ // there's something in array - void **p = aptr; - while(*p++) ++i; - } - size_t sz = 0; - switch(type){ - default: - case arg_none: - /// "Не могу использовать несколько параметров без аргументов!" - ERRX("Can't use multiple args with arg_none!"); - break; - case arg_int: - sz = sizeof(int); - break; - case arg_longlong: - sz = sizeof(long long); - break; - case arg_double: - sz = sizeof(double); - break; - case arg_float: - sz = sizeof(float); - break; - case arg_string: - sz = 0; - break; - /* case arg_function: - sz = sizeof(argfn *); - break;*/ - } - aptr = realloc(aptr, (i + 1) * sizeof(void*)); - *((void***)paptr) = aptr; - aptr[i] = NULL; - if(sz){ - aptr[i - 1] = malloc(sz); - }else - aptr[i - 1] = &aptr[i - 1]; - return aptr[i - 1]; -} - - -/** - * Parce command line arguments - * ! If arg is string, then value will be strdup'ed! - * - * @param argc (io) - address of argc of main(), return value of argc stay after `getopt` - * @param argv (io) - address of argv of main(), return pointer to argv stay after `getopt` - * BE CAREFUL! if you wanna use full argc & argv, save their original values before - * calling this function - * @param options (i) - array of `myoption` for arguments parcing - * - * @exit: in case of error this function show help & make `exit(-1)` - */ -void parceargs(int *argc, char ***argv, myoption *options){ - char *short_options, *soptr; - struct option *long_options, *loptr; - size_t optsize, i; - myoption *opts = options; - // check whether there is at least one options - assert(opts); - assert(opts[0].name); - // first we count how much values are in opts - for(optsize = 0; opts->name; optsize++, opts++); - // now we can allocate memory - short_options = calloc(optsize * 3 + 1, 1); // multiply by three for '::' in case of args in opts - long_options = calloc(optsize + 1, sizeof(struct option)); - opts = options; loptr = long_options; soptr = short_options; - // fill short/long parameters and make a simple checking - for(i = 0; i < optsize; i++, loptr++, opts++){ - // check - assert(opts->name); // check name - if(opts->has_arg){ - assert(opts->type != arg_none); // check error with arg type - assert(opts->argptr); // check pointer - } - if(opts->type != arg_none) // if there is a flag without arg, check its pointer - assert(opts->argptr); - // fill long_options - // don't do memcmp: what if there would be different alignment? - loptr->name = opts->name; - loptr->has_arg = (opts->has_arg < MULT_PAR) ? opts->has_arg : 1; - loptr->flag = opts->flag; - loptr->val = opts->val; - // fill short options if they are: - if(!opts->flag){ - *soptr++ = opts->val; - if(loptr->has_arg) // add ':' if option has required argument - *soptr++ = ':'; - if(loptr->has_arg == 2) // add '::' if option has optional argument - *soptr++ = ':'; - } - } - // now we have both long_options & short_options and can parse `getopt_long` - while(1){ - int opt; - int oindex = 0, optind = 0; // oindex - number of option in argv, optind - number in options[] - if((opt = getopt_long(*argc, *argv, short_options, long_options, &oindex)) == -1) break; - if(opt == '?'){ - opt = optopt; - optind = get_optind(opt, options); - if(options[optind].has_arg == NEED_ARG || options[optind].has_arg == MULT_PAR) - showhelp(optind, options); // need argument - } - else{ - if(opt == 0 || oindex > 0) optind = oindex; - else optind = get_optind(opt, options); - } - opts = &options[optind]; - if(opt == 0 && opts->has_arg == NO_ARGS) continue; // only long option changing integer flag - // now check option - if(opts->has_arg == NEED_ARG || opts->has_arg == MULT_PAR) - if(!optarg) showhelp(optind, options); // need argument - void *aptr; - if(opts->has_arg == MULT_PAR){ - aptr = get_aptr(opts->argptr, opts->type); - }else - aptr = opts->argptr; - bool result = TRUE; - // even if there is no argument, but argptr != NULL, think that optarg = "1" - if(!optarg) optarg = "1"; - switch(opts->type){ - default: - case arg_none: - if(opts->argptr) *((int*)aptr) += 1; // increment value - break; - case arg_int: - result = myatoll(aptr, optarg, arg_int); - break; - case arg_longlong: - result = myatoll(aptr, optarg, arg_longlong); - break; - case arg_double: - result = myatod(aptr, optarg, arg_double); - break; - case arg_float: - result = myatod(aptr, optarg, arg_float); - break; - case arg_string: - result = (*((void**)aptr) = (void*)strdup(optarg)); - break; - case arg_function: - result = ((argfn)aptr)(optarg); - break; - } - if(!result){ - showhelp(optind, options); - } - } - *argc -= optind; - *argv += optind; -} - -/** - * compare function for qsort - * first - sort by short options; second - sort arguments without sort opts (by long options) - */ -static int argsort(const void *a1, const void *a2){ - const myoption *o1 = (myoption*)a1, *o2 = (myoption*)a2; - const char *l1 = o1->name, *l2 = o2->name; - int s1 = o1->val, s2 = o2->val; - int *f1 = o1->flag, *f2 = o2->flag; - // check if both options has short arg - if(f1 == NULL && f2 == NULL){ // both have short arg - return (s1 - s2); - }else if(f1 != NULL && f2 != NULL){ // both don't have short arg - sort by long - return strcmp(l1, l2); - }else{ // only one have short arg -- return it - if(f2) return -1; // a1 have short - it is 'lesser' - else return 1; - } -} - -/** - * Show help information based on myoption->help values - * @param oindex (i) - if non-negative, show only help by myoption[oindex].help - * @param options (i) - array of `myoption` - * - * @exit: run `exit(-1)` !!! - */ -void showhelp(int oindex, myoption *options){ - int max_opt_len = 0; // max len of options substring - for right indentation - const int bufsz = 255; - char buf[bufsz+1]; - myoption *opts = options; - assert(opts); - DBG("hre"); - assert(opts[0].name); // check whether there is at least one options - if(oindex > -1){ // print only one message - opts = &options[oindex]; - printf(" "); - if(!opts->flag && isalpha(opts->val)) printf("-%c, ", opts->val); - printf("--%s", opts->name); - if(opts->has_arg == 1) printf("=arg"); - else if(opts->has_arg == 2) printf("[=arg]"); - printf(" %s\n", _(opts->help)); - exit(-1); - } - // header, by default is just "progname\n" - printf("\n"); - if(strstr(helpstring, "%s")) // print progname - printf(helpstring, __progname); - else // only text - printf("%s", helpstring); - printf("\n"); - // count max_opt_len - do{ - int L = strlen(opts->name); - if(max_opt_len < L) max_opt_len = L; - }while((++opts)->name); - max_opt_len += 14; // format: '-S , --long[=arg]' - get addition 13 symbols - opts = options; - // count amount of options - int N; for(N = 0; opts->name; ++N, ++opts); - if(N == 0) exit(-2); - // Now print all help (sorted) - opts = options; - qsort(opts, N, sizeof(myoption), argsort); - do{ - int p = sprintf(buf, " "); // a little indent - if(!opts->flag && isalpha(opts->val)) // .val is short argument - p += snprintf(buf+p, bufsz-p, "-%c, ", opts->val); - p += snprintf(buf+p, bufsz-p, "--%s", opts->name); - if(opts->has_arg == 1) // required argument - p += snprintf(buf+p, bufsz-p, "=arg"); - else if(opts->has_arg == 2) // optional argument - p += snprintf(buf+p, bufsz-p, "[=arg]"); - assert(p < max_opt_len); // there would be magic if p >= max_opt_len - printf("%-*s%s\n", max_opt_len+1, buf, _(opts->help)); // write options & at least 2 spaces after - ++opts; - }while(--N); - printf("\n\n"); - exit(-1); -} - -/** - * get suboptions from parameter string - * @param str - parameter string - * @param opt - pointer to suboptions structure - * @return TRUE if all OK - */ -bool get_suboption(char *str, mysuboption *opt){ - int findsubopt(char *par, mysuboption *so){ - int idx = 0; - while(so[idx].name){ - if(strcasecmp(par, so[idx].name) == 0) return idx; - ++idx; - } - return -1; // badarg - } - bool opt_setarg(mysuboption *so, int idx, char *val){ - mysuboption *soptr = &so[idx]; - bool result = FALSE; - void *aptr = soptr->argptr; - switch(soptr->type){ - default: - case arg_none: - if(soptr->argptr) *((int*)aptr) += 1; // increment value - result = TRUE; - break; - case arg_int: - result = myatoll(aptr, val, arg_int); - break; - case arg_longlong: - result = myatoll(aptr, val, arg_longlong); - break; - case arg_double: - result = myatod(aptr, val, arg_double); - break; - case arg_float: - result = myatod(aptr, val, arg_float); - break; - case arg_string: - result = (*((void**)aptr) = (void*)strdup(val)); - break; - case arg_function: - result = ((argfn)aptr)(val); - break; - } - return result; - } - char *tok, *val; - bool ret = FALSE; - char *tmpbuf; - tok = strtok_r(str, ":,", &tmpbuf); - do{ - val = strchr(tok, '='); - int noarg = 0; - if(val == NULL){ // no args - val = "1"; - noarg = 1; - }else{ - *val++ = '\0'; - if(!*val || *val == ':' || *val == ','){ // no argument - delimeter after = - val = "1"; noarg = 1; - } - } - int idx = findsubopt(tok, opt); - if(idx < 0){ - /// "Неправильный параметр: %s" - WARNX(_("Wrong parameter: %s"), tok); - goto returning; - } - if(noarg && opt[idx].has_arg == NEED_ARG){ - /// "%s: необходим аргумент!" - WARNX(_("%s: argument needed!"), tok); - goto returning; - } - if(!opt_setarg(opt, idx, val)){ - /// "Неправильный аргумент \"%s\" параметра \"%s\"" - WARNX(_("Wrong argument \"%s\" of parameter \"%s\""), val, tok); - goto returning; - } - }while((tok = strtok_r(NULL, ":,", &tmpbuf))); - ret = TRUE; -returning: - return ret; -} diff --git a/getopt/cmdlnopts/parceargs.h b/getopt/cmdlnopts/parceargs.h deleted file mode 100644 index f069ed6..0000000 --- a/getopt/cmdlnopts/parceargs.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * parceargs.h - headers for parcing command line arguments - * - * Copyright 2013 Edward V. Emelianoff - * - * 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. - */ -#pragma once -#ifndef __PARCEARGS_H__ -#define __PARCEARGS_H__ - -#include // bool -#include - -#ifndef TRUE - #define TRUE true -#endif - -#ifndef FALSE - #define FALSE false -#endif - -// macro for argptr -#define APTR(x) ((void*)x) - -// if argptr is a function: -typedef bool(*argfn)(void *arg); - -/* - * type of getopt's argument - * WARNING! - * My function change value of flags by pointer, so if you want to use another type - * make a latter conversion, example: - * char charg; - * int iarg; - * myoption opts[] = { - * {"value", 1, NULL, 'v', arg_int, &iarg, "char val"}, ..., end_option}; - * ..(parce args).. - * charg = (char) iarg; - */ -typedef enum { - arg_none = 0, // no arg - arg_int, // integer - arg_longlong, // long long - arg_double, // double - arg_float, // float - arg_string, // char * - arg_function // parce_args will run function `bool (*fn)(char *optarg, int N)` -} argtype; - -/* - * Structure for getopt_long & help - * BE CAREFUL: .argptr is pointer to data or pointer to function, - * conversion depends on .type - * - * ATTENTION: string `help` prints through macro PRNT(), bu default it is gettext, - * but you can redefine it before `#include "parceargs.h"` - * - * if arg is string, then value wil be strdup'ed like that: - * char *str; - * myoption opts[] = {{"string", 1, NULL, 's', arg_string, &str, "string val"}, ..., end_option}; - * *(opts[1].str) = strdup(optarg); - * in other cases argptr should be address of some variable (or pointer to allocated memory) - * - * NON-NULL argptr should be written inside macro APTR(argptr) or directly: (void*)argptr - * - * !!!LAST VALUE OF ARRAY SHOULD BE `end_option` or ZEROS !!! - * - */ -typedef enum{ - NO_ARGS = 0, // first three are the same as in getopt_long - NEED_ARG = 1, - OPT_ARG = 2, - MULT_PAR -} hasarg; - -typedef struct{ - // these are from struct option: - const char *name; // long option's name - hasarg has_arg; // 0 - no args, 1 - nesessary arg, 2 - optionally arg, 4 - need arg & key can repeat (args are stored in null-terminated array) - int *flag; // NULL to return val, pointer to int - to set its value of val (function returns 0) - int val; // short opt name (if flag == NULL) or flag's value - // and these are mine: - argtype type; // type of argument - void *argptr; // pointer to variable to assign optarg value or function `bool (*fn)(char *optarg, int N)` - const char *help; // help string which would be shown in function `showhelp` or NULL -} myoption; - -/* - * Suboptions structure, almost the same like myoption - * used in parce_subopts() - */ -typedef struct{ - const char *name; - hasarg has_arg; - argtype type; - void *argptr; -} mysuboption; - -// last string of array (all zeros) -#define end_option {0,0,0,0,0,0,0} -#define end_suboption {0,0,0,0} - -extern const char *__progname; - -void showhelp(int oindex, myoption *options); -void parceargs(int *argc, char ***argv, myoption *options); -void change_helpstring(char *s); -bool get_suboption(char *str, mysuboption *opt); - -#endif // __PARCEARGS_H__ diff --git a/json/json.c b/json/json.c new file mode 100644 index 0000000..6eb966e --- /dev/null +++ b/json/json.c @@ -0,0 +1,361 @@ +/* + * json.c - simple JSON parser + * + * Copyright 2016 Edward V. Emelianov + * + * 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; +} diff --git a/json/json.h b/json/json.h new file mode 100644 index 0000000..8886b67 --- /dev/null +++ b/json/json.h @@ -0,0 +1,64 @@ +/* + * json.h + * + * Copyright 2016 Edward V. Emelianov + * + * 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. + */ + +#pragma once +#ifndef __JSON_H__ +#define __JSON_H__ + +enum json_type{ + json_type_object, // compound object + json_type_obj_array, // array of objects + json_type_data_array, // array of data + json_type_number, // number + json_type_string // string +}; + +// JSON pair (name - value): +typedef struct{ + char *name; + char *value; + enum json_type type; + size_t len; // amount of objects in array +}json_pair; + + +typedef struct{ + size_t len; // amount of pairs + size_t max_len; // max amount of pairs + char *original_data; // contains string with data + json_pair *objs;// objects themself +}json_object; + +#define JSON_BLKSZ (128) + +// JSON object + + +json_object *json_tokener_parse(char *data); +void json_free_obj(json_object **obj); +char *json_pair_get_string(json_pair *pair); +double json_pair_get_number(json_pair *pair); +json_object *json_pair_get_object(json_pair *pair); +json_pair *json_object_get_pair(json_object *obj, char *name); +json_object *json_array_get_object(json_pair *pair, int idx); +char *json_array_get_data(json_pair *pair, int idx); + +#endif // __JSON_H__