diff --git a/apteryx-xml.h b/apteryx-xml.h index 6d472a5..3cfc66d 100644 --- a/apteryx-xml.h +++ b/apteryx-xml.h @@ -93,6 +93,9 @@ char *sch_translate_from (sch_node * node, char *value); bool sch_validate_pattern (sch_node * node, const char *value); gboolean sch_match_name (const char *s1, const char *s2); bool sch_ns_match (sch_node *node, void *ns); +GNode *sch_translate_input (sch_instance * instance, GNode *node, int flags, + void **xlat_data, sch_node **rschema); +GNode *sch_translate_output (sch_instance * instance, GNode *node, int flags, void *xlat_data); /* Data translation/manipulation */ typedef enum diff --git a/models/xlat_test.lua b/models/xlat_test.lua new file mode 100644 index 0000000..9c6c619 --- /dev/null +++ b/models/xlat_test.lua @@ -0,0 +1,29 @@ +xlat_data = {} + +xlat_data.translate_type = function(field, value, dir) + local value1 = value + if field == 'type' then + value1 = tonumber (value) + print("field " .. field) + print("value1 " .. value1) + print("dir " .. dir) + if dir == 'out' then + if value == '1' then + value1 = '3' + else + value1 = '4' + end + print("value1 " .. value1) + else + if value == '3' then + value1 = '1' + else + value1 = '2' + end + end + end + return value1 +end + +return xlat_data + diff --git a/models/xlat_test.xlat b/models/xlat_test.xlat new file mode 100644 index 0000000..e4f297e --- /dev/null +++ b/models/xlat_test.xlat @@ -0,0 +1,7 @@ +/xlat-test/xlat-animals /test/animals/animal +/xlat-test/xlat-animals/xlat-animal /test/animals/animal +/xlat-test/xlat-animals/xlat-animal/*/name /test/animals/animal/*/name +/xlat-test/xlat-animals/xlat-animal/*/type /test/animals/animal/*/type xlat_data.translate_type +/xlat-test/xlat-animals/xlat-animal/*/colour /test/animals/animal/*/colour +/xlat-test/xlat-animals/xlat-animal/*/food /test/animals/animal/*/food +/xlat-test/xlat-animals/xlat-animal/*/toys /test/animals/animal/*/toys \ No newline at end of file diff --git a/models/xlat_test.xml b/models/xlat_test.xml new file mode 100644 index 0000000..9c61ab3 --- /dev/null +++ b/models/xlat_test.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/schema.c b/schema.c index 89452c5..ba74306 100644 --- a/schema.c +++ b/schema.c @@ -32,6 +32,9 @@ #include #include #include +#include +#include +#include #include "apteryx-xml.h" /* Error handling and debug */ @@ -60,7 +63,10 @@ typedef struct _sch_instance GList *models_list; GHashTable *map_hash_table; GHashTable *model_hash_table; + GHashTable *xlat_hash_table; GList *regexes; + /* Lua state */ + lua_State *ls; } sch_instance; typedef struct _sch_xml_to_gnode_parms_s @@ -77,6 +83,20 @@ typedef struct _sch_xml_to_gnode_parms_s GList *out_replaces; } _sch_xml_to_gnode_parms; +typedef struct _sch_xlat_root +{ + GList *matchs; +} sch_xlat_root; + +typedef struct _sch_xlat_data +{ + sch_xlat_root *root; + GNode *ext_tree; + GNode *int_tree; + char *lua_module; + char *lua_func; +} sch_xlat_data; + /* Retrieve the last error code */ sch_err sch_last_err (void) @@ -91,6 +111,49 @@ sch_last_errmsg (void) return tl_errmsg; } +static char * +first_name_in_path (const char *path) +{ + const char *in = path; + char *slash; + char *name; + + if (!in || in[0] == '\0') + return NULL; + + name = strdup (in); + slash = strchr (name + 1, '/'); + if (slash) + *slash = '\0'; + return name; +} + +static void +sch_lua_error (lua_State *ls, int res) +{ + switch (res) + { + case LUA_ERRRUN: + syslog (LOG_ERR, "LUA: %s\n", lua_tostring (ls, -1)); + break; + case LUA_ERRSYNTAX: + syslog (LOG_ERR, "LUA: %s\n", lua_tostring (ls, -1)); + break; + case LUA_ERRMEM: + syslog (LOG_ERR, "LUA: Memory allocation error\n"); + break; + case LUA_ERRERR: + syslog (LOG_ERR, "LUA: Error handler error\n"); + break; + case LUA_ERRFILE: + syslog (LOG_ERR, "LUA: Couldn't open file\n"); + break; + default: + syslog (LOG_ERR, "LUA: Unknown error\n"); + break; + } +} + /* List full paths for all schema files in the search path */ static void list_schema_files (GList ** files, const char *path) @@ -113,7 +176,8 @@ list_schema_files (GList ** files, const char *path) char *filename; if ((fnmatch ("*.xml", ep->d_name, FNM_PATHNAME) != 0) && (fnmatch ("*.xml.gz", ep->d_name, FNM_PATHNAME) != 0) && - (fnmatch ("*.map", ep->d_name, FNM_PATHNAME) != 0)) + (fnmatch ("*.map", ep->d_name, FNM_PATHNAME) != 0) && + (fnmatch ("*.xlat", ep->d_name, FNM_PATHNAME) != 0)) { continue; } @@ -551,6 +615,136 @@ sch_load_namespace_mappings (sch_instance *instance, const char *filename) g_free (buf); } +GNode * +path_to_tree (char *path) +{ + GNode *ret_node = NULL; + GNode *node = NULL;; + gchar **path_split; + int i; + int split_len; + + path_split = g_strsplit (path, "/", -1); + split_len = g_strv_length (path_split); + for (i = 0; i < split_len; i++) + { + if (i == 0 && strlen (path_split[0]) == 0) + { + if (split_len > 1) + { + char *tmp = path_split[1]; + path_split[1] = g_strdup_printf ("/%s", tmp); + g_free (tmp); + } + continue; + } + if (!node) + { + node = g_node_new (g_strdup (path_split[i])); + ret_node = node; + } + else + node = g_node_append_data (node, g_strdup (path_split[i])); + } + g_strfreev (path_split); + + return ret_node; +} + +static void +free_xlat_root (gpointer data) +{ + sch_xlat_root *root = (sch_xlat_root *) data; + sch_xlat_data *xlat_data; + GList *list; + + for (list = g_list_first (root->matchs); list; list = g_list_next (list)) + { + xlat_data = list->data; + apteryx_free_tree (xlat_data->ext_tree); + apteryx_free_tree (xlat_data->int_tree); + g_free (xlat_data->lua_module); + g_free (xlat_data->lua_func); + g_free (xlat_data); + list->data = NULL; + } + g_list_free (root->matchs); + g_free (root); +} + +static void +sch_load_model_xlat (sch_instance *instance, const char *filename) +{ + FILE *fp = NULL; + gchar **xlat_paths; + char *buf; + char *name; + sch_xlat_root *root; + sch_xlat_data *xlat_data; + + if (!instance) + return; + + buf = g_malloc0 (READ_BUF_SIZE); + fp = fopen (filename, "r"); + if (fp && buf) + { + if (!instance->xlat_hash_table) + instance->xlat_hash_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_xlat_root); + + while (fgets (buf, READ_BUF_SIZE, fp) != NULL) + { + /* Skip comment lines */ + if (buf[0] == '#') + continue; + + /* Remove any trailing LF */ + buf[strcspn(buf, "\n")] = '\0'; + /* xlat file format: request_path translate_path */ + xlat_paths = g_strsplit (buf, " ", 3); + if (xlat_paths[0] && xlat_paths[1]) + { + void *old_key; + + name = first_name_in_path (xlat_paths[0]); + xlat_data = g_malloc0 (sizeof (sch_xlat_data)); + xlat_data->ext_tree = path_to_tree (xlat_paths[0]); + xlat_data->int_tree = path_to_tree (xlat_paths[1]); + if (xlat_paths[2]) + { + gchar **func_parts = g_strsplit (xlat_paths[2], ".", 2); + if (func_parts[0] && func_parts[1]) + { + xlat_data->lua_module = g_strdup (func_parts[0]); + xlat_data->lua_func = g_strdup (func_parts[1]); + } + g_strfreev (func_parts); + } + + /* Look up this node name to check for duplicates. */ + if (g_hash_table_lookup_extended (instance->xlat_hash_table, name, + &old_key, (gpointer *) &root)) + { + /* insert data into GList paths */ + xlat_data->root = root; + root->matchs = g_list_prepend (root->matchs, xlat_data); + g_free (name); + } + else + { + root = g_malloc0 (sizeof (sch_xlat_root)); + xlat_data->root = root; + root->matchs = g_list_prepend (root->matchs, xlat_data); + g_hash_table_insert (instance->xlat_hash_table, name, root); + } + } + g_strfreev (xlat_paths); + } + fclose (fp); + } + g_free (buf); +} + static void sch_load_model_list (sch_instance *instance, const char *path, const char *model_list_filename) { @@ -602,6 +796,69 @@ sch_load_model_list (sch_instance *instance, const char *path, const char *model g_free (buf); } +static bool +sch_load_script_files (sch_instance *instance, const char *path) +{ + struct dirent *entry; + DIR *dir; + bool res = true; + + /* Initialise the Lua state */ + instance->ls = luaL_newstate (); + if (!instance->ls) + { + syslog (LOG_ERR, "XML: Failed to instantiate Lua interpreter\n"); + return false; + } + + /* Load required libraries */ + luaL_openlibs (instance->ls); + + /* Find all the LUA files in this folder */ + dir = opendir (path); + if (dir == NULL) + return true; + + /* Load and execute all LUA files */ + for (entry = readdir (dir); entry; entry = readdir (dir)) + { + const char *ext = strrchr (entry->d_name, '.'); + if (ext && strcmp (".lua", ext) == 0) + { + char *filename = g_strdup_printf ("%s/%s", path, entry->d_name); + int error; + + /* Execute the script */ + lua_getglobal (instance->ls, "debug"); + lua_getfield (instance->ls, -1, "traceback"); + error = luaL_loadfile (instance->ls, filename); + if (error == 0) + error = lua_pcall (instance->ls, 0, 0, 0); + + if (error != 0) + sch_lua_error (instance->ls, error); + + while (lua_gettop (instance->ls)) + lua_pop (instance->ls, 1); + + /* Stop processing files if there has been an error */ + if (error != 0) + { + syslog (LOG_ERR, "XML: Invalid file \"%s\"\n", filename); + g_free (filename); + res = false; + goto exit; + } + g_free (filename); + } + } + + exit: + closedir (dir); + return res; +} + + /* Parse all XML files in the search path and merge trees */ static sch_instance * _sch_load (const char *path, const char *model_list_filename) @@ -638,6 +895,11 @@ _sch_load (const char *path, const char *model_list_filename) sch_load_namespace_mappings (instance, filename); continue; } + if (g_strcmp0 (ext, ".xlat") == 0) + { + sch_load_model_xlat (instance, filename); + continue; + } xmlDoc *doc_new = xmlParseFile (filename); if (doc_new == NULL) { @@ -670,6 +932,8 @@ _sch_load (const char *path, const char *model_list_filename) /* Store a link back to the instance in the xmlDoc stucture */ instance->doc->_private = (void *) instance; + sch_load_script_files (instance, path); + return instance; } @@ -755,8 +1019,13 @@ sch_free (sch_instance * instance) g_hash_table_destroy (instance->map_hash_table); if (instance->model_hash_table) g_hash_table_destroy (instance->model_hash_table); + if (instance->xlat_hash_table) + g_hash_table_destroy (instance->xlat_hash_table); if (instance->regexes) g_list_free_full (instance->regexes, (GDestroyNotify) free_regex); + if (instance->ls) + lua_close (instance->ls); + g_free (instance); } } @@ -2753,7 +3022,8 @@ _sch_gnode_to_xml (sch_instance * instance, sch_node * schema, xmlNs *ns, xmlNod } /* Record any changes to the namespace (including the root node) */ - if (data && schema && (!pschema || ((xmlNode *)pschema)->ns != ((xmlNode *)schema)->ns)) + if (data && schema && ((xmlNode *)schema)->ns && + (!pschema || ((xmlNode *)pschema)->ns != ((xmlNode *)schema)->ns)) { /* Dont store a prefix as we set the default xmlns at each node */ xmlNsPtr nns = xmlNewNs (data, ((xmlNode *)schema)->ns->href, NULL); @@ -2873,12 +3143,41 @@ _operation_ok (_sch_xml_to_gnode_parms *_parms, xmlNode *xml, char *curr_op, cha static void _perform_actions (_sch_xml_to_gnode_parms *_parms, int depth, char *curr_op, char *new_op, char *new_xpath) { + void *xlat_data = NULL; + char *tmp_xpath; + /* Do nothing if not an edit, or operation not changing, unless depth is 0. */ if (!_parms->in_is_edit || (g_strcmp0 (curr_op, new_op) == 0 && depth != 0)) { return; } + tmp_xpath = NULL; + if (_parms->in_instance->xlat_hash_table && g_strcmp0 (new_op, "merge") != 0) + { + char *name = first_name_in_path (new_xpath); + if (g_hash_table_lookup (_parms->in_instance->xlat_hash_table, name)) + { + GNode *orig_query = path_to_tree (new_xpath); + int flags = _parms->in_flags; + + xlat_data = NULL; + GNode *node = sch_translate_input (_parms->in_instance, orig_query, flags, &xlat_data, NULL); + if (node != orig_query) + { + GNode *last = node; + while (last->children) + last = last->children; + tmp_xpath = apteryx_node_path (last); + new_xpath = tmp_xpath; + apteryx_free_tree (node); + } + else + apteryx_free_tree (orig_query); + } + g_free (name); + } + /* Handle operations. */ if (g_strcmp0 (new_op, "delete") == 0) { @@ -2900,6 +3199,9 @@ _perform_actions (_sch_xml_to_gnode_parms *_parms, int depth, char *curr_op, cha _parms->out_replaces = g_list_append (_parms->out_replaces, g_strdup (new_xpath)); DEBUG (_parms->in_flags, "replace <%s>\n", new_xpath); } + + if (tmp_xpath) + g_free (tmp_xpath); } static GNode * @@ -4065,3 +4367,553 @@ sch_json_to_gnode (sch_instance * instance, sch_node * schema, json_t * json, in } return root; } + +static gpointer +copy_node_data (gconstpointer src, gpointer dummy) +{ + char *data = NULL; + + if (src) + data = g_strdup (src); + return data; +} + +static void +sch_translate_data (sch_instance * instance, GNode *node, sch_xlat_data *xlat_data, int flags, bool incoming) +{ + int ret; + int s_0 = lua_gettop (instance->ls); + + lua_getglobal (instance->ls, xlat_data->lua_module); + lua_getfield (instance->ls, -1, xlat_data->lua_func); + if (!lua_isnil (instance->ls, -1)) + { + lua_pushstring (instance->ls, APTERYX_NAME (node)); + lua_pushstring (instance->ls, APTERYX_NAME (node->children)); + lua_pushstring (instance->ls, incoming ? "in" : "out"); + ret = lua_pcall (instance->ls, 3, 1, 0); + if (ret) + sch_lua_error (instance->ls, ret); + else if (lua_gettop (instance->ls) != (s_0 + 2)) + { + syslog (LOG_ERR, "XML LUA: Stack not zero(%d) after function: %s.%s\n", + lua_gettop (instance->ls), xlat_data->lua_module, + xlat_data->lua_func); + lua_pop (instance->ls, 1); + } + else + { + const char *value = lua_tostring (instance->ls, -1); + char *old_value = (char *) node->children->data; + node->children->data = g_strdup (value); + DEBUG (flags, "function: %s old value %s new value %s\n", xlat_data->lua_func, old_value, value); + g_free (old_value); + } + } + else + { + lua_pop(instance->ls, 1); + return; + } +} + +static bool +sch_translate_child_exists (GNode *parent, char *name, GNode **ret_node) +{ + GNode *child; + + for (child = parent->children; child; child = child->next) + { + if (g_strcmp0 (APTERYX_NAME (child), name) == 0) + { + *ret_node = child; + return true; + } + } + + *ret_node = NULL; + return false; +} + +static bool +sch_translate_sibling_exists (GNode *node, char *name, GNode **ret_node) +{ + while (node) + { + if (g_strcmp0 (APTERYX_NAME (node), name) == 0) + { + *ret_node = node; + return true; + } + node = node->next; + } + + *ret_node = NULL; + return false; +} + +static bool +xlat_tree_match (GNode *tree, GNode *pattern, GNode **last_match_node) +{ + bool match = false; + GNode *node1 = tree; + GNode *node2 = pattern; + GNode *ret_node; + GNode *last_valid_node = NULL; + + while (node1 && node2) + { + match = true; + if (!sch_translate_sibling_exists (node1, APTERYX_NAME (node2), &ret_node) && + g_strcmp0 ((char *) node2->data, "*") != 0) + return false; + if (ret_node) + { + last_valid_node = ret_node; + node1 = ret_node->children; + ret_node = NULL; + } + else + { + last_valid_node = node1; + node1 = node1->children; + } + node2 = node2->children; + } + + if (node2) + match = false; + else + *last_match_node = last_valid_node; + + return match; +} + +static char * +sch_translate_get_wildcard_data (GNode *node, GNode *tree, uint32_t pos) +{ + GNode *nnode = tree; + uint32_t matches = 0; + + while (nnode && node) + { + if (g_strcmp0 (APTERYX_NAME (nnode), "*") == 0) + { + matches++; + if (matches == pos) + return g_strdup (APTERYX_NAME (node)); + } + + nnode = nnode->children; + node = node->children; + } + return NULL; +} + +static inline gboolean +_sch_translate_count_nodes (GNode *node, void *data) +{ + int *num_nodes = data; + + if (node->data) + *num_nodes = *num_nodes + 1; + + return false; +} + +static inline gboolean +_sch_translate_mark_children (GNode *node, gpointer data) +{ + GHashTable *node_table = data; + if (!g_hash_table_lookup (node_table, node)) + g_hash_table_insert (node_table, node, node); + + return false; +} + +static void +sch_translate_mark_children (GHashTable *node_table, GNode *node) +{ + if (node->children) + { + if (!g_hash_table_lookup (node_table, node)) + g_node_traverse (node->children, G_PRE_ORDER, G_TRAVERSE_ALL, -1, + _sch_translate_mark_children, node_table); + } +} + +static void +sch_translate_mark_node (GHashTable *node_table, GNode *node) +{ + GNode *parent; + + if (!g_hash_table_lookup (node_table, node)) + { + g_hash_table_insert (node_table, node, node); + parent = node->parent; + while (parent) + { + if (!g_hash_table_lookup (node_table, parent)) + g_hash_table_insert (node_table, parent, parent); + + parent = parent->parent; + } + } +} + +static sch_node * +sch_translate_schema (sch_instance * instance, GNode *node, int flags) +{ + sch_node *schema; + char *colon; + xmlNs *ns = NULL; + char *name = APTERYX_NAME (node); + if (name[0] == '/') + name = name + 1; + + /* Check for a change in namespace */ + schema = xmlDocGetRootElement (instance->doc); + colon = strchr (name, ':'); + if (colon) + { + char *namespace = g_strndup (name, colon - name); + xmlNs *nns = sch_lookup_ns (instance, schema, namespace, flags, false); + free (namespace); + if (nns) + { + /* We found a namespace. Skip the prefix */ + name = colon + 1; + ns = nns; + } + } + + /* Find schema node */ + schema = _sch_node_child (ns, schema, name); + if (schema == NULL) + { + ERROR (flags, SCH_E_NOSCHEMANODE, "No schema match for node %s\n", name); + return NULL; + } + return schema; +} + +static bool +sch_translate_input_matches (sch_instance * instance, GHashTable *node_table, GNode *start_node, + int flags, bool exact, GNode **node, sch_xlat_data *x_data, + sch_node **rschema) +{ + GNode *nnode; + GNode *int_node; + GNode *ret_node; + GNode *remaining = NULL; + GNode *last_match_node = NULL; + bool match = false; + + if (xlat_tree_match (start_node, x_data->ext_tree, &last_match_node)) + { + if (g_hash_table_lookup (node_table, last_match_node)) + return match; + + sch_translate_mark_node (node_table, last_match_node); + if (last_match_node && last_match_node->children) + { + remaining = g_node_copy_deep (last_match_node->children, copy_node_data, NULL); + sch_translate_mark_children (node_table, last_match_node); + } + + int_node = x_data->int_tree; + if (rschema && !*rschema) + { + *rschema = sch_translate_schema (instance, int_node, flags); + } + + if (!*node) + *node = g_node_new (g_strdup (APTERYX_NAME (int_node))); + nnode = *node; + int_node = int_node->children; + while (int_node) + { + if (g_strcmp0 (APTERYX_NAME (int_node), "*") == 0) + { + char *str = sch_translate_get_wildcard_data (start_node, x_data->ext_tree, 1); + if (sch_translate_child_exists (nnode, str, &ret_node)) + { + g_free (str); + nnode = ret_node; + } + else + nnode = g_node_append_data (nnode, str); + } + else + { + if (sch_translate_child_exists (nnode, APTERYX_NAME (int_node), &ret_node)) + nnode = ret_node; + else + nnode = g_node_append_data (nnode, g_strdup (APTERYX_NAME (int_node))); + } + + int_node = int_node->children; + } + if (remaining) + { + if (sch_translate_child_exists (nnode, APTERYX_NAME (remaining), &ret_node)) + { + nnode = g_node_append (ret_node, remaining); + } + else + nnode = g_node_append (nnode, remaining); + } + + if (x_data->lua_module && nnode) + sch_translate_data (instance, nnode->parent, x_data, flags, true); + + match = true; + } + + return match; +} + +GNode * +sch_translate_input (sch_instance * instance, GNode *node, int flags, void **xlat_data, sch_node **rschema) +{ + GNode *ret_node = NULL; + char *name; + sch_xlat_root *root; + GHashTable *node_table = NULL; + GList *list; + sch_xlat_data *x_data; + bool match; + int num_nodes = 0; + int query_depth = 0; + + *xlat_data = NULL; + if (!instance->xlat_hash_table || !node) + return node; + + name = APTERYX_NAME (node); + root = g_hash_table_lookup (instance->xlat_hash_table, name); + + if (!root) + return node; + + if (rschema) + { + *rschema = NULL; + query_depth = g_node_max_height (node); + } + + node_table = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); + g_node_traverse (node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, _sch_translate_count_nodes, &num_nodes); + + for (list = g_list_first (root->matchs); list; list = g_list_next (list)) + { + x_data = list->data; + match = sch_translate_input_matches (instance, node_table, node, flags, true, + &ret_node, x_data, rschema); + if (match) + { + if (!*xlat_data) + *xlat_data = x_data; + + if (num_nodes <= g_hash_table_size (node_table)) + break; + } + } + g_hash_table_destroy (node_table); + + if (ret_node) + { + /* Restconf delete requires the translated schema node */ + if (rschema && *rschema) + { + sch_node *schema = *rschema; + GNode *node1 = ret_node; + for (int depth = 1; depth < query_depth && node1; depth++) + { + node1 = node1->children; + if (node1) + { + schema = _sch_node_child (NULL, schema, APTERYX_NAME (node1)); + } + } + if (schema) + *rschema = schema; + } + apteryx_free_tree (node); + + return ret_node; + } + + return node; +} + +static char * +sch_translate_get_wildcard_data_from_bottom (GNode *node, GNode *tree) +{ + GNode *nnode = tree; + + /* Move to the bottom of the tree */ + while (nnode && nnode->children) + nnode = nnode->children; + + while (nnode && node) + { + if (g_strcmp0 (APTERYX_NAME (nnode), "*") == 0) + return g_strdup (APTERYX_NAME (node)); + + nnode = nnode->parent; + node = node->parent; + } + return NULL; +} + +static void +sch_translate_merge_output (GNode **ret_node, GNode *node, GHashTable *node_table, + sch_xlat_data *xlat_data) +{ + GNode *nnode; + GNode *node1; + GNode *copy; + + /* Write/move down the output tree until we hit a wildcard or end of path */ + // nnode = ret_node; + node1 = xlat_data->ext_tree; + if (!*ret_node) + *ret_node = g_node_new (g_strdup (APTERYX_NAME (node1))); + nnode = *ret_node; + while (node1) + { + if (g_strcmp0 (APTERYX_NAME (node1), "*") == 0) + break; + + if (nnode && g_strcmp0 (APTERYX_NAME (nnode), APTERYX_NAME (node1)) == 0) + { + if (nnode->children && node1->children && g_strcmp0 (APTERYX_NAME (node1->children), "*") != 0) + nnode = nnode->children; + } + else + nnode = g_node_append_data (nnode, g_strdup (APTERYX_NAME (node1))); + + node1 = node1->children; + } + + /* Move down the input tree until we hit a wildcard or end of path */ + if (node1) + { + char *str = sch_translate_get_wildcard_data_from_bottom (node, xlat_data->int_tree); + if (str) + { + GNode *new_nnode; + if (sch_translate_child_exists (nnode, str, &new_nnode)) + { + nnode = new_nnode; + g_free (str); + } + else + nnode = g_node_append_data (nnode, str); + } + + node1 = node1->children; + } + + /* We should now be synchronized at a wildcard in both int and external trees, + add in fields after the wild card */ + while (node1 && node1->children) + { + GNode *new_nnode; + + if (nnode && sch_translate_child_exists (nnode, APTERYX_NAME (node1), &new_nnode)) + nnode = new_nnode; + else + nnode = g_node_append_data (nnode, g_strdup (APTERYX_NAME (node1))); + + node1 = node1->children; + } + + /* Now copy the exact match node to the output tree */ + copy = g_node_copy_deep (node, copy_node_data, NULL); + sch_translate_mark_node (node_table, node); + sch_translate_mark_children (node_table, node); + + /* Check if a field name change is required */ + if (node1) + { + if (g_strcmp0 (APTERYX_NAME (copy), APTERYX_NAME (node1))) + { + g_free (copy->data); + copy->data = g_strdup (APTERYX_NAME (node1)); + } + } + + nnode = g_node_append (nnode, copy); +} + +static void +sch_translate_output_matches (sch_instance * instance, GNode **ret_node, GNode *node, + GNode *int_node, GHashTable *node_table, int flags, + sch_xlat_data *xlat_data) +{ + GNode *child; + GNode *next_child; + + if (g_strcmp0 ((char *) node->data, (char *) int_node->data) && + g_strcmp0 ((char *) int_node->data, "*")) + return; + + if (!int_node->children && g_hash_table_lookup (node_table, node)) + return; + + int_node = int_node->children; + if (!int_node) + { + if (xlat_data->lua_module && node->children) + sch_translate_data (instance, node, xlat_data, flags, false); + + /* We have an exact match, translate the path into the output tree */ + sch_translate_merge_output (ret_node, node, node_table, xlat_data); + + return; + } + + for (child = node->children; child; child = next_child) + { + next_child = child->next; + sch_translate_output_matches (instance, ret_node, child, int_node, node_table, + flags, xlat_data); + } +} + +GNode * +sch_translate_output (sch_instance * instance, GNode *node, int flags, void *xlat_data) +{ + sch_xlat_data *x_data = (sch_xlat_data *) xlat_data; + sch_xlat_root *root; + GList *list; + GHashTable *node_table = NULL; + int num_nodes; + GNode *ret_node = NULL; + + if (!instance->xlat_hash_table || !node) + return node; + + if (!x_data) + return node; + + root = x_data->root; + node_table = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL); + + num_nodes = g_node_n_nodes (node, G_TRAVERSE_ALL); + for (list = g_list_first (root->matchs); list; list = g_list_next (list)) + { + x_data = list->data; + sch_translate_output_matches (instance, &ret_node, node, x_data->int_tree, node_table, + flags, x_data); + if (num_nodes <= g_hash_table_size (node_table)) + break; + } + + g_hash_table_destroy (node_table); + apteryx_free_tree (node); + + return ret_node; +}