// Output string void debug_string(BSP_STRING *str) { if (!str) { fprintf(stderr, "\n\033[1;36m=== [NOTHING TO OUTPUT] ===\033[0m\n"); return; } fprintf(stderr, "\n\033[1;36m=== [Debug string] <%s> <%s> ===\033[0m", (str->is_const) ? "Const" : "Normal", (COMPRESS_TYPE_NONE == str->compress_type) ? "Uncompressed" : "Compressed"); debug_hex(STR_STR(str), STR_LEN(str)); return; }
static void _dump_object(BSP_OBJECT *obj, int layer) { if (!obj) { return; } int i; BSP_VALUE *val; reset_object(obj); switch (obj->type) { case OBJECT_TYPE_SINGLE : // Single fprintf(stderr, "\033[1;37mObject type : [SINGLE]\033[0m\n"); val = object_get_single(obj); _dump_value(val, layer); break; case OBJECT_TYPE_ARRAY : // Array fprintf(stderr, "\033[1;37mObject type : [ARRAY]\033[0m\n"); size_t idx = 0; for (idx = 0; idx < object_size(obj); idx ++) { for (i = 0; i <= layer; i ++) { fprintf(stderr, "\t"); } fprintf(stderr, "\033[1;35m%lld\033[0m\t=> ", (long long int) idx); val = object_get_array(obj, idx); _dump_value(val, layer); } fprintf(stderr, "\n"); break; case OBJECT_TYPE_HASH : // Dict fprintf(stderr, "\033[1;37mObject type : [HASH]\033[0m\n"); val = curr_item(obj); BSP_STRING *key; while (val) { key = curr_hash_key(obj); for (i = 0; i <= layer; i ++) { fprintf(stderr, "\t"); } if (key) { fprintf(stderr, "\033[1;33m"); write(STDERR_FILENO, STR_STR(key), STR_LEN(key)); fprintf(stderr, "\033[0m"); } else { fprintf(stderr, "### NO KEY ###"); } fprintf(stderr, "\t=> "); _dump_value(val, layer); next_item(obj); val = curr_item(obj); } fprintf(stderr, "\n"); break; case OBJECT_TYPE_UNDETERMINED : default : // Null fprintf(stderr, "\033[1;36mObject type : [UNKNOWN]\033[0m\n"); break; } return; }
static void _dump_value(BSP_VALUE *val, int layer) { if (!val) { fprintf(stderr, "\033[1;35m### NO VALUE ###\033[0m\n"); return; } switch (val->type) { case BSP_VAL_NULL : fprintf(stderr, "\033[1;31m(NULL)\033[0m\n"); break; case BSP_VAL_INT : fprintf(stderr, "\033[1;33m(INTEGER)\033[0m => %lld\n", (long long int) get_vint(val->lval, NULL)); break; case BSP_VAL_INT29 : fprintf(stderr, "\033[1;33m(INTEGER29)\033[0m => %d\n", (int) get_vint29(val->lval, NULL)); break; case BSP_VAL_FLOAT : fprintf(stderr, "\033[1;34m(FLOAT)\033[0m => %f\n", get_float(val->lval)); break; case BSP_VAL_DOUBLE : fprintf(stderr, "\033[1;34m(DOUBLE)\033[0m => %g\n", get_double(val->lval)); break; case BSP_VAL_BOOLEAN_TRUE : fprintf(stderr, "\033[1;35m(BOOLEAN_TRUE)\033[0m\n"); break; case BSP_VAL_BOOLEAN_FALSE : fprintf(stderr, "\033[1;35m(BOOLEAN_FALSE)\033[0m\n"); break; case BSP_VAL_STRING : fprintf(stderr, "\033[1;32m(STRING)\033[0m => "); BSP_STRING *str = (BSP_STRING *) val->rval; if (str && STR_STR(str)) { write(STDERR_FILENO, STR_STR(str), STR_LEN(str)); } else { fprintf(stderr, "\033[1;31m### NULL_STRING ###\033[0m"); } fprintf(stderr, "\n"); break; case BSP_VAL_POINTER : fprintf(stderr, "\033[1;36m(POINTER)\033[0m => %p\n", (void *) val->rval); break; case BSP_VAL_OBJECT : fprintf(stderr, "\033[1;36m(OBJECT)\033[0m => "); BSP_OBJECT *sub_obj = (BSP_OBJECT *) val->rval; _dump_object(sub_obj, layer + 1); break; case BSP_VAL_UNKNOWN : fprintf(stderr, "\033[1;31m(UNKNOWN)\033[0m\n"); break; default : break; } return; }
// Parser int parse_conf(BSP_OBJECT *conf) { if (!conf) { return BSP_RTN_INVALID; } BSP_VALUE *node = NULL, *sub; BSP_STRING *str = NULL; BSPD_CONFIG *c = get_global_config(); // Global node = bsp_object_value_hash_original(conf, "global"); if (!node) { // No global segment? return BSP_RTN_ERR_GENERAL; } BSP_OBJECT *global = V_GET_OBJECT(node); if (global && BSP_OBJECT_HASH == global->type) { if (!c->verbose) { sub = bsp_object_value_hash_original(global, "trace_level"); if (sub) { str = V_GET_STRING(sub); if (str) { if (0 == strncasecmp(STR_STR(str), "none", 4)) { c->opt.trace_level = I_NONE; } else if (0 == strncasecmp(STR_STR(str), "emerg", 5)) { c->opt.trace_level = I_EMERG; } else if (0 == strncasecmp(STR_STR(str), "alert", 5)) { c->opt.trace_level = I_EMERG | I_ALERT; } else if (0 == strncasecmp(STR_STR(str), "crit", 4)) { c->opt.trace_level = I_EMERG | I_ALERT | I_CRIT; } else if (0 == strncasecmp(STR_STR(str), "err", 3)) { c->opt.trace_level = I_EMERG | I_ALERT | I_CRIT | I_ERR; } else if (0 == strncasecmp(STR_STR(str), "warn", 4)) { c->opt.trace_level = I_EMERG | I_ALERT | I_CRIT | I_ERR | I_WARN; } else if (0 == strncasecmp(STR_STR(str), "notice", 6)) { c->opt.trace_level = I_EMERG | I_ALERT | I_CRIT | I_ERR | I_WARN | I_NOTICE; } else if (0 == strncasecmp(STR_STR(str), "info", 4)) { c->opt.trace_level = I_EMERG | I_ALERT | I_CRIT | I_ERR | I_WARN | I_NOTICE | I_INFO; } else if (0 == strncasecmp(STR_STR(str), "debug", 5)) { c->opt.trace_level = I_EMERG | I_ALERT | I_CRIT | I_ERR | I_WARN | I_NOTICE | I_INFO | I_DEBUG; } else if (0 == strncasecmp(STR_STR(str), "all", 3)) { c->opt.trace_level = I_ALL; } } } } else { c->opt.trace_level = I_ALL; } sub = bsp_object_value_hash_original(global, "daemonize"); if (sub) { c->opt.daemonize = V_GET_BOOLEAN(sub); } sub = bsp_object_value_hash_original(global, "boss_threads"); if (sub) { c->opt.boss_threads = (int) V_GET_INT(sub); } sub = bsp_object_value_hash_original(global, "acceptor_threads"); if (sub) { c->opt.acceptor_threads = (int) V_GET_INT(sub); } sub = bsp_object_value_hash_original(global, "io_threads"); if (sub) { c->opt.io_threads = (int) V_GET_INT(sub); } sub = bsp_object_value_hash_original(global, "worker_threads"); if (sub) { c->opt.worker_threads = (int) V_GET_INT(sub); } sub = bsp_object_value_hash_original(global, "script"); if (sub) { str = V_GET_STRING(sub); if (str) { c->script = bsp_strndup(STR_STR(str), STR_LEN(str)); } } sub = bsp_object_value_hash_original(global, "lua_hook_load"); if (sub) { str = V_GET_STRING(sub); if (str) { c->lua_hook_load = bsp_strndup(STR_STR(str), STR_LEN(str)); } } } else { // Type error return BSP_RTN_ERR_GENERAL; } // Servers node = bsp_object_value_hash_original(conf, "servers"); if (!node) { // No server } else { BSP_OBJECT *servers = V_GET_OBJECT(node); BSP_OBJECT *server_obj = NULL; BSP_VALUE *server_val = NULL; struct bspd_server_t *server = NULL; if (servers && BSP_OBJECT_ARRAY == servers->type) { bsp_object_reset(servers); server_val = bsp_object_curr(servers, NULL); while (server_val) { server_obj = V_GET_OBJECT(server_val); if (server_obj && BSP_OBJECT_HASH == server_obj->type) { server = _parse_conf_server(server_obj); if (server) { server->next = c->servers; c->servers = server; } } bsp_object_next(servers); server_val = bsp_object_curr(servers, NULL); } } } return BSP_RTN_SUCCESS; }
static struct bspd_server_t * _parse_conf_server(BSP_OBJECT *desc) { if (!desc || BSP_OBJECT_HASH != desc->type) { return NULL; } struct bspd_server_t *ret = bsp_calloc(1, sizeof(struct bspd_server_t)); if (!ret) { return NULL; } ret->inet_type = BSP_INET_ANY; ret->sock_type = BSP_SOCK_ANY; ret->addr = NULL; ret->port = 0; ret->prop.type = BSPD_SERVER_NORMAL; ret->prop.data_type = BSPD_DATA_RAW; BSP_STRING *str = NULL; BSP_VALUE *node = NULL; node = bsp_object_value_hash_original(desc, "name"); if (node) { str = V_GET_STRING(node); if (str) { ret->name = bsp_strndup(STR_STR(str), STR_LEN(str)); } } // Inet node = bsp_object_value_hash_original(desc, "inet"); if (node) { str = V_GET_STRING(node); if (str) { if (0 == strncasecmp(STR_STR(str), "ipv4", 4)) { ret->inet_type = BSP_INET_IPV4; } else if (0 == strncasecmp(STR_STR(str), "ipv6", 4)) { ret->inet_type = BSP_INET_IPV6; } else if (0 == strncasecmp(STR_STR(str), "local", 5)) { ret->inet_type = BSP_INET_LOCAL; } } } // Sock node = bsp_object_value_hash_original(desc, "sock"); if (node) { str = V_GET_STRING(node); if (str) { if (0 == strncasecmp(STR_STR(str), "tcp", 3)) { ret->sock_type = BSP_SOCK_TCP; } else if (0 == strncasecmp(STR_STR(str), "udp", 3)) { ret->sock_type = BSP_SOCK_UDP; } else if (0 == strncasecmp(STR_STR(str), "sctp", 4)) { ret->sock_type = BSP_SOCK_SCTP; } } } // Addr node = bsp_object_value_hash_original(desc, "addr"); if (node) { str = V_GET_STRING(node); if (str) { ret->addr = bsp_strndup(STR_STR(str), STR_LEN(str)); } } // Port node = bsp_object_value_hash_original(desc, "port"); if (node) { ret->port = (uint16_t) V_GET_INT(node); } // Type node = bsp_object_value_hash_original(desc, "type"); if (node) { str = V_GET_STRING(node); if (str) { if (0 == strncasecmp(STR_STR(str), "internal", 8)) { ret->prop.type = BSPD_SERVER_INTERNAL; } else if (0 == strncasecmp(STR_STR(str), "normal", 6)) { ret->prop.type = BSPD_SERVER_NORMAL; } else if (0 == strncasecmp(STR_STR(str), "http", 0)) { ret->prop.type = BSPD_SERVER_HTTP; } else if (0 == strncasecmp(STR_STR(str), "websocket", 9)) { ret->prop.type = BSPD_SERVER_WEBSOCKET; } else { // Unsupported } } } // Data type node = bsp_object_value_hash_original(desc, "data_type"); if (node) { str = V_GET_STRING(node); if (str) { if (0 == strncasecmp(STR_STR(str), "packet", 6)) { ret->prop.data_type = BSPD_DATA_PACKET; } } } // LUA hooks node = bsp_object_value_hash_original(desc, "lua_hook_connect"); if (node) { str = V_GET_STRING(node); if (str) { ret->prop.lua_hook_connect = bsp_strndup(STR_STR(str), STR_LEN(str)); } } node = bsp_object_value_hash_original(desc, "lua_hook_disconnect"); if (node) { str = V_GET_STRING(node); if (str) { ret->prop.lua_hook_connect = bsp_strndup(STR_STR(str), STR_LEN(str)); } } node = bsp_object_value_hash_original(desc, "lua_hook_data"); if (node) { str = V_GET_STRING(node); if (str) { ret->prop.lua_hook_data = bsp_strndup(STR_STR(str), STR_LEN(str)); } } return ret; }
static int do_filter(lua_State *s) { if (!s || lua_gettop(s) < 2 || !lua_istable(s, -1) || !lua_islightuserdata(s, -2)) { return 0; } struct word_filter_t *flt = (struct word_filter_t *) lua_touserdata(s, -2); if (!flt) { return 0; } struct hash_tree_node_t *root = _get_node(flt, flt->root), *curr = NULL; if (!root) { // No root node return 0; } const char *content = NULL, *replacement = NULL; size_t content_len = 0, n, j, seg_len = 0; int all = 0, matched = 0, start = 0, replace = 0; unsigned char code; BSP_STRING *res = NULL; lua_getfield(s, -1, "content"); if (lua_isstring(s, -1)) { content = lua_tolstring(s, -1, &content_len); } lua_pop(s, 1); lua_getfield(s, -1, "all"); if (lua_isboolean(s, -1)) { all = lua_toboolean(s, -1); } lua_pop(s, 1); lua_getfield(s, -1, "replace"); if (lua_isboolean(s, -1)) { replace = lua_toboolean(s, -1); } lua_pop(s, 1); lua_getfield(s, -1, "replacement"); if (lua_isstring(s, -1)) { replacement = lua_tostring(s, -1); } lua_pop(s, 1); lua_newtable(s); if (content && content_len) { if (replace) { res = new_string(NULL, 0); if (!replacement || !strlen(replacement)) { replacement = DEFAULT_REPLACEMENT; } } for (n = 0; n < content_len; n ++) { if (!all && matched) { break; } curr = root; start = n; for (j = n; j <= content_len; j ++) { if (curr->value) { // Get word matched ++; lua_pushinteger(s, matched); lua_newtable(s); lua_pushstring(s, "word"); lua_pushlstring(s, content + start, j - start); lua_settable(s, -3); lua_pushstring(s, "offset"); lua_pushinteger(s, start); lua_settable(s, -3); lua_pushstring(s, "length"); lua_pushinteger(s, j - start); lua_settable(s, -3); lua_pushstring(s, "dsp_length"); lua_pushinteger(s, curr->length); lua_settable(s, -3); lua_pushstring(s, "value"); lua_pushinteger(s, curr->value); lua_settable(s, -3); lua_settable(s, -3); seg_len = j - start; if (!all) { break; } } if (j == content_len) { break; } code = (unsigned char) content[j]; if (!curr->path[code]) { // No match break; } curr = _get_node(flt, curr->path[code]); if (!curr) { break; } } if (replace) { if (seg_len > 0) { string_printf(res, "%s", replacement); seg_len --; } else { string_printf(res, "%c", (unsigned char) content[n]); } } } if (replace) { lua_pushstring(s, "replaced"); lua_pushlstring(s, STR_STR(res), STR_LEN(res)); lua_settable(s, -3); del_string(res); } } return 1; }
// Start main loop int core_loop(void (* server_event)(BSP_CALLBACK *)) { BSP_THREAD *t = get_thread(MAIN_THREAD); if (!t) { trigger_exit(BSP_RTN_FATAL, "Main thread lost!"); } // Servers BSP_VALUE *val = object_get_hash_str(runtime_settings, "servers"); BSP_OBJECT *vobj = value_get_object(val); BSP_STRING *vstr = NULL; if (vobj && OBJECT_TYPE_ARRAY == vobj->type) { struct bsp_conf_server_t srv; BSP_OBJECT *vsrv = NULL; size_t varr_size = object_size(vobj), i; reset_object(vobj); for (i = 0; i < varr_size; i ++) { val = object_get_array(vobj, i); vsrv = value_get_object(val); if (vsrv && OBJECT_TYPE_HASH == vsrv->type) { // Default value memset(&srv, 0, sizeof(struct bsp_conf_server_t)); srv.server_inet = INET_TYPE_ANY; srv.server_sock = SOCK_TYPE_ANY; srv.def_client_type = CLIENT_TYPE_DATA; srv.def_data_type = DATA_TYPE_PACKET; val = object_get_hash_str(vsrv, "name"); vstr = value_get_string(val); srv.server_name = bsp_strndup(STR_STR(vstr), STR_LEN(vstr)); val = object_get_hash_str(vsrv, "inet"); vstr = value_get_string(val); if (vstr) { if (0 == strncasecmp(STR_STR(vstr), "ipv6", 4)) { srv.server_inet = INET_TYPE_IPV6; } else if (0 == strncasecmp(STR_STR(vstr), "ipv4", 4)) { srv.server_inet = INET_TYPE_IPV4; } else if (0 == strncasecmp(STR_STR(vstr), "local", 5)) { srv.server_inet = INET_TYPE_LOCAL; } } val = object_get_hash_str(vsrv, "sock"); vstr = value_get_string(val); if (vstr) { if (0 == strncasecmp(STR_STR(vstr), "tcp", 3)) { srv.server_sock = SOCK_TYPE_TCP; } else if (0 == strncasecmp(STR_STR(vstr), "udp", 3)) { srv.server_sock = SOCK_TYPE_UDP; } } val = object_get_hash_str(vsrv, "addr"); vstr = value_get_string(val); srv.server_addr = bsp_strndup(STR_STR(vstr), STR_LEN(vstr)); val = object_get_hash_str(vsrv, "port"); srv.server_port = (int) value_get_int(val); val = object_get_hash_str(vsrv, "heartbeat_check"); srv.heartbeat_check = (int) value_get_int(val); val = object_get_hash_str(vsrv, "debug_input"); srv.debug_hex_input = value_get_boolean(val); val = object_get_hash_str(vsrv, "debug_output"); srv.debug_hex_input = value_get_boolean(val); val = object_get_hash_str(vsrv, "max_clients"); srv.max_clients = (int) value_get_int(val); val = object_get_hash_str(vsrv, "max_packet_length"); srv.max_packet_length = (size_t) value_get_int(val); val = object_get_hash_str(vsrv, "websocket"); if (value_get_boolean(val)) { srv.def_client_type = CLIENT_TYPE_WEBSOCKET_HANDSHAKE; } val = object_get_hash_str(vsrv, "data_type"); vstr = value_get_string(val); if (vstr && 0 == strncasecmp(STR_STR(vstr), "stream", 6)) { srv.def_data_type = DATA_TYPE_STREAM; } // Add server BSP_SERVER *s; int srv_fds[MAX_SERVER_PER_CREATION], srv_ct, fd_type; int nfds = MAX_SERVER_PER_CREATION; nfds = new_server(srv.server_addr, srv.server_port, srv.server_inet, srv.server_sock, srv_fds, &nfds); for (srv_ct = 0; srv_ct < nfds; srv_ct ++) { fd_type = FD_TYPE_SOCKET_SERVER; s = (BSP_SERVER *) get_fd(srv_fds[srv_ct], &fd_type); if (s) { s->name = srv.server_name; s->heartbeat_check = srv.heartbeat_check; s->def_client_type = srv.def_client_type; s->def_data_type = srv.def_data_type; s->max_packet_length = srv.max_packet_length; s->max_clients = srv.max_clients; s->debug_hex_input = srv.debug_hex_input; s->debug_hex_output = srv.debug_hex_output; add_server(s); } else { bsp_free(srv.server_name); } } } } } // Server event if (server_event) { core_settings.on_srv_events = server_event; } // Create 1 Hz clock BSP_TIMER *tmr = new_timer(BASE_CLOCK_SEC, BASE_CLOCK_USEC, -1); tmr->on_timer = base_timer; core_settings.main_timer = tmr; dispatch_to_thread(tmr->fd, MAIN_THREAD); start_timer(tmr); // Let's go load_bootstrap(); trace_msg(TRACE_LEVEL_CORE, "Core : Main thread loop started"); thread_process((void *) t); return BSP_RTN_SUCCESS; }
// Parse runtime setting int _parse_runtime_setting(BSP_STRING *rs) { if (!rs) { return BSP_RTN_FATAL; } if (!core_settings.initialized) { init_core_setting(); } runtime_settings = json_nd_decode(rs); if (!runtime_settings || OBJECT_TYPE_HASH != runtime_settings->type) { trace_msg(TRACE_LEVEL_ERROR, "Core : Runtime setting parse error"); return BSP_RTN_ERROR_GENERAL; } trace_msg(TRACE_LEVEL_NOTICE, "Core : Runtime setting parsed"); BSP_OBJECT *vobj; BSP_STRING *vstr; BSP_VALUE *val; // Global settings val = object_get_hash_str(runtime_settings, "global"); vobj = value_get_object(val); if (vobj && OBJECT_TYPE_HASH == vobj->type) { val = object_get_hash_str(vobj, "instance_id"); if (val && BSP_VAL_INT == val->type) { core_settings.instance_id = (int) value_get_int(val); } val = object_get_hash_str(vobj, "static_workers"); if (val && BSP_VAL_INT == val->type) { core_settings.static_workers = value_get_int(val); if (core_settings.static_workers <= 0) { core_settings.static_workers = DEFAULT_STATIC_WORKERS; } } val = object_get_hash_str(vobj, "debug_output"); core_settings.debug_hex_output = value_get_boolean(val); val = object_get_hash_str(vobj, "debug_connector_input"); core_settings.debug_hex_connector_input = value_get_boolean(val); val = object_get_hash_str(vobj, "log_dir"); vstr = value_get_string(val); core_settings.log_dir = (vstr) ? bsp_strndup(STR_STR(vstr), STR_LEN(vstr)) : NULL; val = object_get_hash_str(vobj, "enable_log"); if (value_get_boolean(val) && core_settings.is_daemonize) { log_open(); core_settings.trace_recorder = log_add; } val = object_get_hash_str(vobj, "script_dir"); vstr = value_get_string(val); core_settings.script_dir = (vstr) ? bsp_strndup(STR_STR(vstr), STR_LEN(vstr)) : NULL; val = object_get_hash_str(vobj, "script_gc_interval"); if (val && BSP_VAL_INT == val->type) { core_settings.script_gc_interval = value_get_int(val); } } return BSP_RTN_SUCCESS; }
static void _dump_value(BSP_VALUE *val, int layer) { if (!val) { fprintf(stderr, "\033[1;35m### NO VALUE ###\033[0m\n"); return; } BSP_STRING *str = NULL; BSP_OBJECT *sub_obj = NULL; switch (val->type) { case BSP_VALUE_NULL : fprintf(stderr, "\033[1;31m(NULL)\033[0m\n"); break; case BSP_VALUE_INT8 : case BSP_VALUE_INT16 : case BSP_VALUE_INT32 : case BSP_VALUE_INT64 : case BSP_VALUE_INT29 : case BSP_VALUE_INT : fprintf(stderr, "\033[1;33m(INTEGER)\033[0m : %lld\n", (long long int) (V_GET_INT(val))); break; case BSP_VALUE_UINT8 : case BSP_VALUE_UINT16 : case BSP_VALUE_UINT32 : case BSP_VALUE_UINT64 : fprintf(stderr, "\033[1;33m(UNSIGNED INTEGER)\033[0m : %llu\n", (unsigned long long int) (V_GET_INT(val))); break; case BSP_VALUE_FLOAT : case BSP_VALUE_DOUBLE : fprintf(stderr, "\033[1;33m(FLOAT)\033[0m : %g\n", (double) V_GET_FLOAT(val)); break; case BSP_VALUE_BOOLEAN : fprintf(stderr, "\033[1;33m(BOOLEAN)\033[0m : %s\n", (BSP_TRUE == V_GET_BOOLEAN(val)) ? "true" : "false"); break; case BSP_VALUE_STRING : fprintf(stderr, "\033[1;32m(STRING)\033[0m : "); str = V_GET_STRING(val); if (str && STR_STR(str)) { write(STDERR_FILENO, STR_STR(str), STR_LEN(str)); } else { fprintf(stderr, "\033[1;31m### NULL_STRING ###\033[0m"); } fprintf(stderr, "\n"); break; case BSP_VALUE_OBJECT : fprintf(stderr, "\033[1;36m(OBJECT)\033[0m : "); sub_obj = V_GET_OBJECT(val); _dump_object(sub_obj, layer + 1); break; case BSP_VALUE_POINTER : fprintf(stderr, "\033[1;36m(POINTER)\033[0m : %p\n", V_GET_POINTER(val)); break; case BSP_VALUE_UNKNOWN : default : fprintf(stderr, "\033[1;31m(NOTHING)\033[0m\n"); break; } return; }
static void _dump_object(BSP_OBJECT *obj, int layer) { if (!obj) { return; } int i; size_t idx, total; BSP_VALUE *val; BSP_STRING *key; bsp_object_reset(obj); switch (obj->type) { case BSP_OBJECT_SINGLE : // Single fprintf(stderr, "\033[1;37mObject type : [SINGLE]\033[0m\n"); val = bsp_object_value_single(obj); _dump_value(val, layer); break; case BSP_OBJECT_ARRAY : // Array fprintf(stderr, "\033[1;37mObject type : [ARRAY]\033[0m\n"); total = bsp_object_size(obj); for (idx = 0; idx < total; idx ++) { for (i = 0; i <= layer; i ++) { fprintf(stderr, " "); } fprintf(stderr, "\033[1;35m%lld\033[0m\t=> ", (long long int) idx); val = bsp_object_value_array(obj, idx); _dump_value(val, layer); } fprintf(stderr, "\n"); break; case BSP_OBJECT_HASH : // Hash fprintf(stderr, "\033[1;37mObject type : [HASH]\033[0m\n"); val = bsp_object_curr(obj, (void **) &key); while (val && key) { for (i = 0; i <= layer; i ++) { fprintf(stderr, " "); } fprintf(stderr, "\033[1;33m"); write(STDERR_FILENO, STR_STR(key), STR_LEN(key)); fprintf(stderr, "\033[0m => "); _dump_value(val, layer); bsp_object_next(obj); val = bsp_object_curr(obj, (void **) &key); } fprintf(stderr, "\n"); break; case BSP_OBJECT_UNDETERMINED : default : // NullS fprintf(stderr, "\033[1;36mObject type : [UNKNOWN]\033[0m\n"); break; } return; }