From 9d4d2587d7ee0d76e1af0bc4e1889176ce209e04 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 16 Feb 2011 00:24:52 +0000 Subject: [PATCH] add jshn (JSON SHell Notation), a small utility and shell library for parsing and generating json data SVN-Revision: 25547 --- package/jshn/Makefile | 32 +++++ package/jshn/example.txt | 19 +++ package/jshn/files/jshn.sh | 109 +++++++++++++++++ package/jshn/src/jshn.c | 245 +++++++++++++++++++++++++++++++++++++ 4 files changed, 405 insertions(+) create mode 100644 package/jshn/Makefile create mode 100644 package/jshn/example.txt create mode 100644 package/jshn/files/jshn.sh create mode 100644 package/jshn/src/jshn.c diff --git a/package/jshn/Makefile b/package/jshn/Makefile new file mode 100644 index 0000000000..d5d1ea0cad --- /dev/null +++ b/package/jshn/Makefile @@ -0,0 +1,32 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=jshn +PKG_VERSION:=0.1 +PKG_RELEASE=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/jshn + SECTION:=utils + CATEGORY:=Utilities + DEPENDS:=+libjson + TITLE:=JSON SHell Notation +endef + +define Package/jshn/description + Library for parsing and generating JSON from shell scripts +endef + +TARGET_CFLAGS += -I$(STAGING_DIR)/usr/include --std=gnu99 + +define Build/Compile + $(TARGET_CC) $(TARGET_CFLAGS) -o $(PKG_BUILD_DIR)/jshn src/jshn.c $(TARGET_LDFLAGS) -ljson +endef + +define Package/jshn/install + $(INSTALL_DIR) $(1)/bin $(1)/lib/functions + $(INSTALL_BIN) $(PKG_BUILD_DIR)/jshn $(1)/bin + $(INSTALL_DATA) ./files/jshn.sh $(1)/lib/functions +endef + +$(eval $(call BuildPackage,jshn)) diff --git a/package/jshn/example.txt b/package/jshn/example.txt new file mode 100644 index 0000000000..d5ab1c4d23 --- /dev/null +++ b/package/jshn/example.txt @@ -0,0 +1,19 @@ +. /lib/functions/jshn.sh + +# generating json data +json_init +json_add_string "msg" "Hello, world!" +json_add_object "test" +json_add_int "testdata" "1" +json_close_object +MSG=`json_dump` +# MSG now contains: { "msg": "Hello, world!", "test": { "testdata": 1 } } + + +# parsing json data +json_load "$MSG" +json_select test +json_get_var var1 testdata +json_select .. +json_get_var var2 msg +echo "msg: $var2 - testdata: $var1" diff --git a/package/jshn/files/jshn.sh b/package/jshn/files/jshn.sh new file mode 100644 index 0000000000..2dddfe8bf6 --- /dev/null +++ b/package/jshn/files/jshn.sh @@ -0,0 +1,109 @@ +[ -z "$N" ] && . /etc/functions.sh + +# functions for parsing and generating json + +json_init() { + [ -n "$JSON_UNSET" ] && eval "unset $JSON_UNSET" + export -- JSON_SEQ=0 JSON_STACK= JSON_CUR="JSON_VAR" JSON_UNSET= +} + +json_add_generic() { + local type="$1" + local var="$2" + local val="$3" + local cur="${4:-$JSON_CUR}" + + export ${NO_EXPORT:+-n} -- "${cur}_$var=$val" + export ${NO_EXPORT:+-n} -- "TYPE_${cur}_$var=$type" + append JSON_UNSET "${cur}_$var TYPE_${cur}_$var" + append "KEYS_${cur}" "$var" +} + +json_add_table() { + JSON_SEQ=$(($JSON_SEQ + 1)) + append JSON_STACK "$JSON_CUR" + local table="JSON_TABLE$JSON_SEQ" + export ${NO_EXPORT:+-n} -- "UP_$table=$JSON_CUR" + JSON_CUR="$table" +} + +json_add_object() { + local cur="$JSON_CUR" + json_add_table + json_add_generic object "$1" "$JSON_CUR" "$cur" +} + +json_close_object() { + local oldstack="$JSON_STACK" + export "KEYS_${JSON_CUR}" + JSON_CUR="${JSON_STACK##* }" + JSON_STACK="${JSON_STACK% *}" + [[ "$oldstack" == "$JSON_STACK" ]] && JSON_STACK= +} + +json_add_array() { + local cur="$JSON_CUR" + json_add_table + json_add_generic array "$1" "$JSON_CUR" "$cur" +} + +json_close_array() { + json_close_object +} + +json_add_string() { + json_add_generic string "$1" "$2" +} + +json_add_int() { + json_add_generic int "$1" "$2" +} + +json_add_boolean() { + json_add_generic boolean "$1" "$2" +} + +# functions read access to json variables + +json_load() { + eval `jshn -r "$1"` +} + +json_dump() { + jshn -w +} + +json_get_type() { + local dest="$1" + local var="$2" + eval "export ${NO_EXPORT:+-n} -- \"$dest=\${TYPE_${JSON_CUR}_$var}\"" +} + +json_get_var() { + local dest="$1" + local var="$2" + eval "export ${NO_EXPORT:+-n} -- \"$dest=\${${JSON_CUR}_$var}\"" +} + +json_select() { + local target="$1" + local type + + [ -z "$1" ] && { + JSON_CUR="JSON_VAR" + return + } + [[ "$1" == ".." ]] && { + eval "JSON_CUR=\"\${UP_$JSON_CUR}\"" + return; + } + json_get_type type "$target" + case "$type" in + object|array) + json_get_var JSON_CUR "$target" + ;; + *) + echo "WARNING: Variable '$target' does not exist or is not an array/object" + ;; + esac +} diff --git a/package/jshn/src/jshn.c b/package/jshn/src/jshn.c new file mode 100644 index 0000000000..a2d711f38e --- /dev/null +++ b/package/jshn/src/jshn.c @@ -0,0 +1,245 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_VARLEN 256 + +static int add_json_element(const char *key, json_object *obj); + +static int add_json_object(json_object *obj) +{ + int ret = 0; + + json_object_object_foreach(obj, key, val) { + ret = add_json_element(key, val); + if (ret) + break; + } + return ret; +} + +static int add_json_array(struct array_list *a) +{ + char seq[12]; + int i, len; + int ret; + + for (i = 0, len = array_list_length(a); i < len; i++) { + sprintf(seq, "%d", i); + ret = add_json_element(seq, array_list_get_idx(a, i)); + if (ret) + return ret; + } + + return 0; +} + +static void add_json_string(const char *str) +{ + char *ptr = (char *) str; + int len; + char *c; + + while ((c = strchr(ptr, '\'')) != NULL) { + len = c - ptr; + if (len > 0) + fwrite(ptr, len, 1, stdout); + ptr = c + 1; + c = "'\\''"; + fwrite(c, strlen(c), 1, stdout); + } + len = strlen(ptr); + if (len > 0) + fwrite(ptr, len, 1, stdout); +} + +static void write_key_string(const char *key) +{ + while (*key) { + putc(isalnum(*key) ? *key : '_', stdout); + key++; + } +} + +static int add_json_element(const char *key, json_object *obj) +{ + char *type; + + if (!obj) + return -1; + + switch (json_object_get_type(obj)) { + case json_type_object: + type = "object"; + break; + case json_type_array: + type = "array"; + break; + case json_type_string: + type = "string"; + break; + case json_type_boolean: + type = "boolean"; + break; + case json_type_int: + type = "int"; + break; + default: + return -1; + } + + fprintf(stdout, "json_add_%s '", type); + write_key_string(key); + + switch (json_object_get_type(obj)) { + case json_type_object: + fprintf(stdout, "';\n"); + add_json_object(obj); + fprintf(stdout, "json_close_object;\n"); + break; + case json_type_array: + fprintf(stdout, "';\n"); + add_json_array(json_object_get_array(obj)); + fprintf(stdout, "json_close_array;\n"); + break; + case json_type_string: + fprintf(stdout, "' '"); + add_json_string(json_object_get_string(obj)); + fprintf(stdout, "';\n"); + break; + case json_type_boolean: + fprintf(stdout, "' %d;\n", json_object_get_boolean(obj)); + break; + case json_type_int: + fprintf(stdout, "' %d;\n", json_object_get_int(obj)); + break; + default: + return -1; + } + + return 0; +} + +static int jshn_parse(const char *str) +{ + json_object *obj; + + obj = json_tokener_parse(str); + if (is_error(obj) || json_object_get_type(obj) != json_type_object) { + fprintf(stderr, "Failed to parse message data\n"); + return 1; + } + fprintf(stdout, "json_init;\n"); + add_json_object(obj); + fflush(stdout); + + return 0; +} + +static char *get_keys(const char *prefix) +{ + char *keys; + + keys = alloca(strlen(prefix) + sizeof("KEYS_") + 1); + sprintf(keys, "KEYS_%s", prefix); + return getenv(keys); +} + +static void get_var(const char *prefix, const char *name, char **var, char **type) +{ + char *tmpname; + + tmpname = alloca(strlen(prefix) + 1 + strlen(name) + 1 + sizeof("TYPE_")); + sprintf(tmpname, "TYPE_%s_%s", prefix, name); + *var = getenv(tmpname + 5); + *type = getenv(tmpname); +} + +static json_object *jshn_add_objects(json_object *obj, const char *prefix, bool array); + +static void jshn_add_object_var(json_object *obj, bool array, const char *prefix, const char *name) +{ + json_object *new; + char *var, *type; + + get_var(prefix, name, &var, &type); + if (!var || !type) + return; + + if (!strcmp(type, "array")) { + new = json_object_new_array(); + jshn_add_objects(new, var, true); + } else if (!strcmp(type, "object")) { + new = json_object_new_object(); + jshn_add_objects(new, var, false); + } else if (!strcmp(type, "string")) { + new = json_object_new_string(var); + } else if (!strcmp(type, "int")) { + new = json_object_new_int(atoi(var)); + } else if (!strcmp(type, "boolean")) { + new = json_object_new_boolean(!!atoi(var)); + } else { + return; + } + + if (array) + json_object_array_add(obj, new); + else + json_object_object_add(obj, name, new); +} + +static json_object *jshn_add_objects(json_object *obj, const char *prefix, bool array) +{ + char *keys, *key, *brk; + + keys = get_keys(prefix); + if (!keys || !obj) + goto out; + + for (key = strtok_r(keys, " ", &brk); key; + key = strtok_r(NULL, " ", &brk)) { + jshn_add_object_var(obj, array, prefix, key); + } + +out: + return obj; +} + +static int jshn_format(void) +{ + json_object *obj; + + obj = json_object_new_object(); + jshn_add_objects(obj, "JSON_VAR", false); + fprintf(stdout, "%s\n", json_object_to_json_string(obj)); + json_object_put(obj); + return 0; +} + +static int usage(const char *progname) +{ + fprintf(stderr, "Usage: %s -r |-w\n", progname); + return 2; +} + +int main(int argc, char **argv) +{ + int ch; + + while ((ch = getopt(argc, argv, "r:w")) != -1) { + switch(ch) { + case 'r': + return jshn_parse(optarg); + case 'w': + return jshn_format(); + default: + return usage(argv[0]); + } + } + return usage(argv[0]); +}