/* * pretty_print.c: utils for pretty printing of results * * Copyright (C) 2020-2023 IOPSYS Software Solutions AB. All rights reserved. * * Author: Vivek Dutta * * See LICENSE file for license related information. */ #include "common.h" #include "get_helper.h" #include "pretty_print.h" // private function and structures struct resultstack { void *cookie; char *key; struct list_head list; }; static bool is_search_by_reference(char *path) { BBF_DEBUG("Entry |%s|", path); if (match(path, "[+]+", 0, NULL)) { size_t pindex = 0, bindex = 0; char *last_plus, *last_bracket; last_bracket = strrchr(path, ']'); if (!last_bracket) return true; last_plus = strrchr(path, '+'); pindex = (size_t)labs(last_plus - path); bindex = (size_t)labs(last_bracket - path); if (pindex > bindex) return true; } return false; } //if matched start will have first match index, end will have end index static bool is_res_required(char *str, size_t *start, size_t *len) { BBF_DEBUG("Entry |%s|", str); if (match(str, GLOB_CHAR, 0, NULL)) { size_t s_len = 0, b_len = 0, p_len = 0; char *star, *b_start, *b_end, *plus; char temp_char[MAX_DM_KEY_LEN] = {'\0'}; s_len = DM_STRLEN(str); b_len = s_len; p_len = s_len; star = strchr(str, '*'); b_start = strchr(str, '['); b_end = strchr(str, ']'); plus = strchr(str, '+'); if (star) s_len = (size_t)labs(star - str); if (b_start) b_len = (size_t)labs(b_start - str); if (plus) p_len = (size_t)labs(plus - str); *start = MIN(MIN(s_len, p_len), b_len); if (*start == s_len) { *len = 1; } else if (*start == p_len) { size_t i = 0, index = 0; while ((str+i) != plus) { if (str[i] == DELIM) index = i; ++i; } *start = index+1; *len = p_len - index; } else { *len = (size_t)labs(b_end - b_start); } // Check if naming with aliases used snprintf(temp_char, *len+1, "%s", str + *start); if (match(temp_char, GLOB_EXPR, 0, NULL)) return true; if (match(temp_char, "[*+]+", 0, NULL)) return true; } *start = DM_STRLEN(str); return false; } static size_t get_glob_len(char *path) { size_t m_index = 0, m_len = 0, ret = 0; size_t plen = DM_STRLEN(path); char temp_name[MAX_DM_KEY_LEN] = {'\0'}; char *end = NULL; BBF_DEBUG("Entry"); if (is_res_required(path, &m_index, &m_len)) { if (m_index <= MAX_DM_KEY_LEN) snprintf(temp_name, m_index, "%s", path); end = strrchr(temp_name, DELIM); if (end != NULL) ret = m_index - DM_STRLEN(end); } else { char name[MAX_DM_KEY_LEN] = {'\0'}; if (plen == 0) return ret; if (path[plen - 1] == DELIM) { if (plen <= MAX_DM_KEY_LEN) snprintf(name, plen, "%s", path); } else { ret = 1; if (plen < MAX_DM_KEY_LEN) snprintf(name, plen + 1, "%s", path); } end = strrchr(name, DELIM); if (end == NULL) return ret; ret = ret + DM_STRLEN(path) - DM_STRLEN(end); if (is_node_instance(end+1)) { int copy_len = plen - DM_STRLEN(end); if (copy_len <= MAX_DM_KEY_LEN) snprintf(temp_name, copy_len, "%s", path); end = strrchr(temp_name, DELIM); if (end != NULL) ret = ret - DM_STRLEN(end); } } return(ret); } static void resulting(uint8_t maxdepth, char *path, struct dmctx *bbf_ctx, struct list_head *pv_local) { struct blob_attr *cur = NULL; size_t rem = 0; uint8_t count; size_t plen = get_glob_len(bbf_ctx->in_param); size_t path_len = DM_STRLEN(path); if (path_len == 0) return; blobmsg_for_each_attr(cur, bbf_ctx->bb.head, rem) { struct blob_attr *tb[3] = {0}; const struct blobmsg_policy p[3] = { { "path", BLOBMSG_TYPE_STRING }, { "data", BLOBMSG_TYPE_STRING }, { "type", BLOBMSG_TYPE_STRING } }; blobmsg_parse(p, 3, tb, blobmsg_data(cur), blobmsg_len(cur)); char *name = (tb[0]) ? blobmsg_get_string(tb[0]) : ""; char *data = (tb[1]) ? blobmsg_get_string(tb[1]) : ""; char *type = (tb[2]) ? blobmsg_get_string(tb[2]) : ""; if (match(name, path, 0, NULL)) { if (is_search_by_reference(bbf_ctx->in_param)) plen = 0; if (maxdepth > 4 || maxdepth == 0) { add_pv_list(name + plen, data, type, pv_local); } else { count = count_delim(name + path_len); if (count < maxdepth) add_pv_list(name + plen, data, type, pv_local); } } } } static void add_data_blob(struct blob_buf *bb, char *param, char *value, char *type) { if (param == NULL || value == NULL || type == NULL) return; BBF_DEBUG("# Adding BLOB (%s)::(%s)", param, value); switch (get_dm_type(type)) { case DMT_UNINT: blobmsg_add_u64(bb, param, (uint32_t)strtoul(value, NULL, 10)); break; case DMT_INT: blobmsg_add_u32(bb, param, (int)strtol(value, NULL, 10)); break; case DMT_LONG: blobmsg_add_u64(bb, param, strtoll(value, NULL, 10)); break; case DMT_UNLONG: blobmsg_add_u64(bb, param, (uint64_t)strtoull(value, NULL, 10)); break; case DMT_BOOL: if (get_boolean_string(value)) blobmsg_add_u8(bb, param, true); else blobmsg_add_u8(bb, param, false); break; default: //"xsd:hexbin" "xsd:dateTime" "xsd:string" bb_add_string(bb, param, value); break; } } static void free_result_list(struct list_head *head) { struct resultstack *iter = NULL, *node = NULL; list_for_each_entry_safe(iter, node, head, list) { free(iter->key); list_del(&iter->list); free(iter); } } static void free_result_node(struct resultstack *rnode) { if (rnode) { BBF_DEBUG("## ResStack DEL(%s)", rnode->key); free(rnode->key); list_del(&rnode->list); free(rnode); } } static void add_result_node(struct list_head *rlist, char *key, char *cookie) { struct resultstack *rnode = NULL; rnode = (struct resultstack *)calloc(1, sizeof(*rnode)); if (!rnode) { BBF_ERR("Out of memory!"); return; } INIT_LIST_HEAD(&rnode->list); list_add(&rnode->list, rlist); rnode->key = (key) ? strdup(key) : strdup(""); rnode->cookie = cookie; BBF_DEBUG("## ResSTACK ADD (%s) ##", rnode->key); } static bool is_leaf_element(char *path) { char *ptr = NULL; if (!path) return true; ptr = strchr(path, DELIM); return (ptr == NULL); } static bool get_next_element(char *path, char *param) { char *ptr; size_t len; if (!path) return false; len = DM_STRLEN(path); ptr = strchr(path, DELIM); if (ptr) strncpyt(param, path, (size_t)labs(ptr - path) + 1); else strncpyt(param, path, len + 1); return true; } static bool is_same_group(char *path, char *group) { return (strncmp(path, group, DM_STRLEN(group)) == 0); } static bool add_paths_to_stack(struct blob_buf *bb, char *path, size_t begin, struct pvNode *pv, struct list_head *result_stack) { char key[MAX_DM_KEY_LEN], param[MAX_DM_PATH], *ptr; size_t parsed_len = 0; void *c; char *k; ptr = path + begin; if (is_leaf_element(ptr)) { add_data_blob(bb, ptr, pv->val, pv->type); return true; } while (get_next_element(ptr, key)) { parsed_len += DM_STRLEN(key) + 1; ptr += DM_STRLEN(key) + 1; if (is_leaf_element(ptr)) { strncpyt(param, path, begin + parsed_len + 1); if (is_node_instance(key)) c = blobmsg_open_table(bb, NULL); else c = blobmsg_open_table(bb, key); k = param; add_result_node(result_stack, k, c); add_data_blob(bb, ptr, pv->val, pv->type); break; } strncpyt(param, pv->param, begin + parsed_len + 1); if (is_node_instance(ptr)) c = blobmsg_open_array(bb, key); else c = blobmsg_open_table(bb, key); k = param; add_result_node(result_stack, k, c); } return true; } // public functions void prepare_result_blob(struct blob_buf *bb, struct list_head *pv_list) { char *ptr; size_t len; struct pvNode *pv; struct resultstack *rnode; LIST_HEAD(result_stack); if (!bb || !pv_list) return; if (list_empty(pv_list)) return; size_t count = 0; list_for_each_entry(pv, pv_list, list) { count++; } struct pvNode *sortedPV = sort_pv_path(pv_list, count); if (sortedPV == NULL) return; for (size_t i = 0; i < count; i++) { pv = &sortedPV[i]; ptr = pv->param; if (list_empty(&result_stack)) { BBF_DEBUG("stack empty Processing (%s)", ptr); add_paths_to_stack(bb, pv->param, 0, pv, &result_stack); } else { bool is_done = false; while (is_done == false) { rnode = list_entry(result_stack.next, struct resultstack, list); if (is_same_group(ptr, rnode->key)) { len = DM_STRLEN(rnode->key); ptr = ptr + len; BBF_DEBUG("GROUP (%s), ptr(%s), len(%zu)", pv->param, ptr, len); add_paths_to_stack(bb, pv->param, len, pv, &result_stack); is_done = true; } else { // Get the latest entry before deleting it BBF_DEBUG("DIFF GROUP pv(%s), param(%s)", pv->param, ptr); blobmsg_close_table(bb, rnode->cookie); free_result_node(rnode); if (list_empty(&result_stack)) { add_paths_to_stack(bb, pv->param, 0, pv, &result_stack); is_done = true; } } } } } free(sortedPV); // Close the stack entry if left list_for_each_entry(rnode, &result_stack, list) { blobmsg_close_table(bb, rnode->cookie); } free_result_list(&result_stack); } void prepare_raw_result(struct blob_buf *bb, struct dmctx *bbf_ctx, struct list_head *rslvd) { struct pathNode *iter = NULL; struct blob_attr *cur = NULL; size_t rem = 0; list_for_each_entry(iter, rslvd, list) { if (DM_STRLEN(iter->path) == 0) continue; blobmsg_for_each_attr(cur, bbf_ctx->bb.head, rem) { struct blob_attr *tb[1] = {0}; const struct blobmsg_policy p[1] = { { "path", BLOBMSG_TYPE_STRING } }; blobmsg_parse(p, 1, tb, blobmsg_data(cur), blobmsg_len(cur)); char *name = (tb[0]) ? blobmsg_get_string(tb[0]) : ""; if (match(name, iter->path, 0, NULL)) { blobmsg_add_blob(bb, cur); } } } } void prepare_pretty_result(uint8_t maxdepth, struct blob_buf *bb, struct dmctx *bbf_ctx, struct list_head *rslvd) { struct pathNode *iter = NULL; LIST_HEAD(pv_local); BBF_DEBUG("################### DATA to PROCESS ##################"); list_for_each_entry(iter, rslvd, list) { BBF_DEBUG("## %s ##", iter->path); resulting(maxdepth, iter->path, bbf_ctx, &pv_local); } BBF_DEBUG("######################################################"); prepare_result_blob(bb, &pv_local); free_pv_list(&pv_local); }