bbfdm/libbbfdm-ubus/pretty_print.c
2024-08-29 10:11:30 +00:00

445 lines
10 KiB
C

/*
* pretty_print.c: utils for pretty printing of results
*
* Copyright (C) 2020-2023 IOPSYS Software Solutions AB. All rights reserved.
*
* Author: Vivek Dutta <vivek.dutta@iopsys.eu>
*
* 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, b_len, p_len;
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 *) malloc(sizeof(*rnode));
if (!rnode) {
BBF_ERR("Out of memory!");
return;
}
rnode->key = (key) ? strdup(key) : strdup("");
rnode->cookie = cookie;
BBF_DEBUG("## ResSTACK ADD (%s) ##", rnode->key);
INIT_LIST_HEAD(&rnode->list);
list_add(&rnode->list, rlist);
}
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);
}