/** \file roxml.c * \brief source for libroxml.so * * This is the source file for lib libroxml.so * \author blunderer <blunderer@blunderer.org> * \date 23 Dec 2008 * * Copyright (C) 2009 blunderer * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * The author added a static linking exception, see License.txt. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "roxml-internal.h" void ROXML_API roxml_release(void *data) { memory_cell_t *ptr = &head_cell; memory_cell_t *to_delete = NULL; if (data == RELEASE_LAST) { while ((ptr->prev != NULL) && (ptr->prev->id != pthread_self())) { ptr = ptr->prev; } if (ptr->prev == NULL) { return; } to_delete = ptr->prev; if (to_delete->next) { to_delete->prev->next = to_delete->next; to_delete->next->prev = to_delete->prev; } else { if (to_delete->prev != &head_cell) { head_cell.prev = to_delete->prev; } else { head_cell.prev = NULL; } to_delete->prev->next = NULL; } if (PTR_IS_STAR(to_delete)) { int i = 0; for (i = 0; i < to_delete->occ; i++) { free(((void **)(to_delete->ptr))[i]); } } if (to_delete->type != PTR_NONE) { free(to_delete->ptr); to_delete->type = PTR_NONE; free(to_delete); } } else if (data == RELEASE_ALL) { head_cell.prev = NULL; while ((head_cell.next != NULL)) { to_delete = head_cell.next; if (to_delete->next) { to_delete->next->prev = &head_cell; } head_cell.next = to_delete->next; if (PTR_IS_STAR(to_delete)) { int i = 0; for (i = 0; i < to_delete->occ; i++) { free(((void **)(to_delete->ptr))[i]); } } free(to_delete->ptr); to_delete->ptr = NULL; to_delete->type = PTR_NONE; free(to_delete); } } else { while ((ptr->next != NULL) && (ptr->next->ptr != data)) { ptr = ptr->next; } if (ptr->next == NULL) { return; } to_delete = ptr->next; if (to_delete->next) { to_delete->next->prev = ptr; } else { if (ptr == &head_cell) { head_cell.prev = NULL; } else { head_cell.prev = to_delete->prev; } } ptr->next = to_delete->next; if (PTR_IS_STAR(to_delete)) { int i = 0; for (i = 0; i < to_delete->occ; i++) { free(((void **)(to_delete->ptr))[i]); } } free(to_delete->ptr); to_delete->type = PTR_NONE; free(to_delete); } if (head_cell.next == &head_cell) { head_cell.next = NULL; } if (head_cell.prev == &head_cell) { head_cell.prev = NULL; } } char ROXML_API *roxml_get_content(node_t *n, char *buffer, int bufsize, int *size) { int total = 0; char *content = buffer; if (n == NULL) { if (size) { *size = 0; } if (buffer) { strcpy(buffer, ""); return buffer; } return NULL; } else if (n->type & ROXML_ELM_NODE) { node_t *ptr = n->chld; while (ptr) { if ((ptr->type & ROXML_NODE_TYPES) == ROXML_TXT_NODE) { total += ptr->end - ptr->pos; } ptr = ptr->sibl; } if (content == NULL) { content = roxml_malloc(sizeof(char), total + 1, PTR_CHAR); bufsize = total + 1; } if (content == NULL) { return NULL; } total = 0; ptr = n->chld; while (ptr) { if ((ptr->type & ROXML_NODE_TYPES) == ROXML_TXT_NODE) { int ret_len = 0; int read_size = ptr->end - ptr->pos; if (total + read_size > bufsize - 1) { read_size = bufsize - total - 1; } ret_len += roxml_read(ptr->pos, read_size, content + total, ptr); total += ret_len; } ptr = ptr->sibl; } } else { node_t *target = n; int read_size = 0; int name_len = 0; int spec_offset = 0; ROXML_GET_BASE_BUFFER(name); roxml_get_name(n, name, ROXML_BASE_LEN); name_len = strlen(name); ROXML_PUT_BASE_BUFFER(name); if (n->type & ROXML_DOCTYPE_NODE) { total = target->end - target->pos - name_len - 2; spec_offset = target->pos + name_len + 2; } else if (n->type & ROXML_TXT_NODE) { total = target->end - target->pos; spec_offset = target->pos; } else if (n->type & ROXML_CMT_NODE) { total = target->end - target->pos - 4; spec_offset = target->pos + 4; } else if (n->type & ROXML_PI_NODE) { total = target->end - target->pos - name_len - 3; spec_offset = target->pos + name_len + 3; } else if (n->type & ROXML_ATTR_NODE) { target = n->chld; if (target) { spec_offset = target->pos; total = target->end - target->pos; } else { spec_offset = 0; total = 0; } } if (content == NULL) { content = roxml_malloc(sizeof(char), total + 1, PTR_CHAR); bufsize = total + 1; } if (content == NULL) { return NULL; } read_size = total; if (read_size > bufsize - 1) { read_size = bufsize - 1; } total = roxml_read(spec_offset, read_size, content, target); } content[total] = '\0'; if (size) { *size = total + 1; } return content; } char ROXML_API *roxml_get_name(node_t *n, char *buffer, int size) { int offset = 0; ROXML_GET_BASE_BUFFER(tmp_name); memset(tmp_name, 0, ROXML_BASE_LEN * sizeof(char)); if (buffer) { memset(buffer, 0, size * sizeof(char)); } if (n == NULL) { if (buffer) { strcpy(buffer, ""); } ROXML_PUT_BASE_BUFFER(tmp_name); return buffer; } if (n->prnt == NULL) { strcpy(tmp_name, "documentRoot"); } else if (n->type & ROXML_NS_NODE) { roxml_ns_t *ns = (roxml_ns_t *) n->priv; if (ns) { strncpy(tmp_name, ns->alias, ROXML_BASE_LEN-1); } else { tmp_name[0] = '\0'; } } else if ((n->type & ROXML_TXT_NODE) || (n->type & ROXML_CMT_NODE)) { ROXML_PUT_BASE_BUFFER(tmp_name); if (buffer) { strcpy(buffer, ""); return buffer; } return NULL; } else { int count = 0; int total = 0; int spec_offset = 0; if (n->type & ROXML_PI_NODE) { spec_offset = 2; } else if (n->type & ROXML_DOCTYPE_NODE) { spec_offset = 1; } if ((total = roxml_read(n->pos + spec_offset, ROXML_BASE_LEN, tmp_name, n)) > 0) { while (ROXML_WHITE(tmp_name[offset]) || tmp_name[offset] == '<') { offset++; } count = offset; } else { count = 0; } if (n->type & ROXML_PI_NODE) { for (; count < total; count++) { if (ROXML_WHITE(tmp_name[count])) { break; } else if ((tmp_name[count] == '?') && (tmp_name[count + 1] == '>')) { break; } } } else if (n->type & ROXML_ELM_NODE) { for (; count < total; count++) { if (ROXML_WHITE(tmp_name[count])) { break; } else if ((tmp_name[count] == '/') && (tmp_name[count + 1] == '>')) { break; } else if (tmp_name[count] == '>') { break; } } } else if (n->type & ROXML_ATTR_NODE) { for (; count < total; count++) { if (ROXML_WHITE(tmp_name[count])) { break; } else if (tmp_name[count] == '=') { break; } else if (tmp_name[count] == '>') { break; } else if ((tmp_name[count] == '/') && (tmp_name[count + 1] == '>')) { break; } } } else if (n->type & ROXML_DOCTYPE_NODE) { for (; count < total; count++) { if (ROXML_WHITE(tmp_name[count])) { break; } else if (tmp_name[count] == '>') { break; } } } tmp_name[count] = '\0'; } if (buffer == NULL) { buffer = (char *)roxml_malloc(sizeof(char), strlen(tmp_name) - offset + 1, PTR_CHAR); strcpy(buffer, tmp_name + offset); } else { if (strlen(tmp_name) - offset < (unsigned int)size) { size = strlen(tmp_name) - offset; } strncpy(buffer, tmp_name + offset, size); } ROXML_PUT_BASE_BUFFER(tmp_name); return buffer; } void ROXML_API roxml_close(node_t *n) { node_t *root = n; if (root == NULL) { return; } while (root->prnt != NULL) { root = root->prnt; } roxml_del_tree(root->chld); roxml_del_tree(root->sibl); if ((root->type & ROXML_FILE) == ROXML_FILE) { fclose(root->src.fil); } roxml_free_node(root); } int ROXML_API roxml_get_nodes_nb(node_t *n, int type) { node_t *ptr = n; int nb = -1; if (n) { nb = 0; if (ptr->chld) { ptr = ptr->chld; do { if (ptr->type & type) { nb++; } ptr = ptr->sibl; } while (ptr); } if (type & ROXML_ATTR_NODE) { ptr = n->attr; while (ptr) { nb++; ptr = ptr->sibl; } } } return nb; } node_t ROXML_API *roxml_get_nodes(node_t *n, int type, char *name, int nth) { node_t *ptr = NULL; if (n == NULL) { return NULL; } if (name == NULL) { int count = 0; if (n->ns && (type & ROXML_NS_NODE)) { ptr = n->ns; if (nth == 0) { return ptr; } } else if (n->attr && (type & ROXML_ATTR_NODE)) { ptr = n->attr; if (nth == 0) { return ptr; } while ((ptr->sibl) && (nth > count)) { ptr = ptr->sibl; count++; } } else { ptr = n->chld; while (ptr && !((ptr->type & ROXML_NODE_TYPES) & type)) { ptr = ptr->sibl; } } if (nth > count) { ptr = n->chld; while (ptr && !((ptr->type & ROXML_NODE_TYPES) & type)) { ptr = ptr->sibl; } while (ptr && (ptr->sibl) && (nth > count)) { ptr = ptr->sibl; if ((ptr->type & ROXML_NODE_TYPES) & type) { count++; } } } if (nth > count) { return NULL; } } else { if (n->attr && (type & ROXML_ATTR_NODE)) { ptr = n->attr; while (ptr) { int ans = strcmp(roxml_get_name(ptr, NULL, 0), name); roxml_release(RELEASE_LAST); if (ans == 0) { return ptr; } ptr = ptr->sibl; } } ptr = n->chld; while (ptr) { if ((ptr->type & ROXML_NODE_TYPES) & type) { int ans = strcmp(roxml_get_name(ptr, NULL, 0), name); roxml_release(RELEASE_LAST); if (ans == 0) { return ptr; } } ptr = ptr->sibl; } } return ptr; } node_t ROXML_API *roxml_set_ns(node_t *n, node_t * ns) { node_t *attr = NULL; node_t *chld = NULL; if (!n) { return NULL; } if (ns) { node_t *common_parent = n; while (common_parent && common_parent != ns->prnt) { common_parent = common_parent->prnt; } if (common_parent != ns->prnt) { return NULL; } } n->ns = ns; chld = n->chld; while (chld) { roxml_set_ns(chld, ns); chld = chld->sibl; } attr = n->attr; while (attr) { if ((attr->type & ROXML_NS_NODE) == 0) { attr->ns = ns; } attr = attr->sibl; } return n; } node_t ROXML_API *roxml_get_ns(node_t *n) { return roxml_get_nodes(n, ROXML_NS_NODE, NULL, 0); } int ROXML_API roxml_get_pi_nb(node_t *n) { return roxml_get_nodes_nb(n, ROXML_PI_NODE); } node_t ROXML_API *roxml_get_pi(node_t *n, int nth) { return roxml_get_nodes(n, ROXML_PI_NODE, NULL, nth); } int ROXML_API roxml_get_cmt_nb(node_t *n) { return roxml_get_nodes_nb(n, ROXML_CMT_NODE); } node_t ROXML_API *roxml_get_cmt(node_t *n, int nth) { return roxml_get_nodes(n, ROXML_CMT_NODE, NULL, nth); } int ROXML_API roxml_get_txt_nb(node_t *n) { return roxml_get_nodes_nb(n, ROXML_TXT_NODE); } node_t ROXML_API *roxml_get_txt(node_t *n, int nth) { return roxml_get_nodes(n, ROXML_TXT_NODE, NULL, nth); } int ROXML_API roxml_get_attr_nb(node_t *n) { return roxml_get_nodes_nb(n, ROXML_ATTR_NODE); } node_t ROXML_API *roxml_get_attr(node_t *n, char *name, int nth) { return roxml_get_nodes(n, ROXML_ATTR_NODE, name, nth); } int ROXML_API roxml_get_chld_nb(node_t *n) { return roxml_get_nodes_nb(n, ROXML_ELM_NODE); } node_t ROXML_API *roxml_get_chld(node_t *n, char *name, int nth) { return roxml_get_nodes(n, ROXML_ELM_NODE, name, nth); } node_t ROXML_API *roxml_get_prev_sibling(node_t *n) { node_t *prev = NULL; node_t *prev_elm = NULL; if (n && n->prnt) { prev = n->prnt->chld; while (prev && prev != n) { if ((prev->type & ROXML_NODE_TYPES) == ROXML_ELM_NODE) { prev_elm = prev; } prev = prev->sibl; } } return prev_elm; } node_t ROXML_API *roxml_get_next_sibling(node_t *n) { if (n) { while (n->sibl && (n->sibl->type & ROXML_NODE_TYPES) != ROXML_ELM_NODE) { n = n->sibl; } return n->sibl; } return NULL; } node_t ROXML_API *roxml_get_parent(node_t *n) { if (n) { if (n->prnt == NULL) { return n; } else { return n->prnt; } } return NULL; } node_t ROXML_API *roxml_get_root(node_t *n) { node_t *root = NULL; if (n) { root = n; while (root->prnt) root = root->prnt; } return root; } int ROXML_API roxml_get_type(node_t *n) { if (n) { return (n->type & ROXML_NODE_TYPES); } return 0; } int ROXML_API roxml_get_node_position(node_t *n) { int idx = 1; char name[256]; node_t *prnt; node_t *first; if (n == NULL) { return 0; } roxml_get_name(n, name, 256); prnt = n->prnt; if (!prnt) { return 1; } first = prnt->chld; while ((first) && (first != n)) { char twin[256]; roxml_get_name(first, twin, 256); if (strcmp(name, twin) == 0) { idx++; } first = first->sibl; } return idx; } node_t ROXML_API *roxml_load_fd(int fd) { FILE *file = NULL; node_t *current_node = NULL; if (fd < 0) { return NULL; } file = fdopen(fd, "r"); if (file == NULL) { return NULL; } current_node = roxml_create_node(0, file, ROXML_ELM_NODE | ROXML_FILE); current_node = roxml_append_node(NULL, current_node); return roxml_load(current_node, file, NULL); } node_t ROXML_API *roxml_load_doc(char *filename) { node_t *current_node = NULL; FILE *file = fopen(filename, "rb"); if (file == NULL) { return NULL; } current_node = roxml_create_node(0, file, ROXML_ELM_NODE | ROXML_FILE); current_node = roxml_append_node(NULL, current_node); return roxml_load(current_node, file, NULL); } node_t ROXML_API *roxml_load_buf(char *buffer) { node_t *current_node = NULL; if (buffer == NULL) { return NULL; } current_node = roxml_create_node(0, buffer, ROXML_ELM_NODE | ROXML_BUFF); current_node = roxml_append_node(NULL, current_node); return roxml_load(current_node, NULL, buffer); } node_t ROXML_API **roxml_xpath(node_t *n, char *path, int *nb_ans) { int count = 0; node_t **node_set = NULL; #if(CONFIG_XML_XPATH_ENGINE==1) int index = 0; xpath_node_t *xpath = NULL; node_t *root = n; char *full_path_to_find; if (n == NULL) { if (nb_ans) { *nb_ans = 0; } return NULL; } root = roxml_get_root(n); full_path_to_find = strdup(path); index = roxml_parse_xpath(full_path_to_find, &xpath, 0); if (index >= 0) { node_set = roxml_exec_xpath(root, n, xpath, index, &count); roxml_free_xpath(xpath, index); free(full_path_to_find); if (count == 0) { roxml_release(node_set); node_set = NULL; } } #endif /* CONFIG_XML_XPATH_ENGINE*/ if (nb_ans) { *nb_ans = count; } return node_set; } void ROXML_API roxml_del_node(node_t *n) { #if(CONFIG_XML_READ_WRITE==1) if (n == NULL) return; if ((n->type & ROXML_ELM_NODE) || (n->type & ROXML_DOCTYPE_NODE) || (n->type & ROXML_PI_NODE) || (n->type & ROXML_CMT_NODE)) { roxml_del_std_node(n); } else if (n->type & ROXML_ATTR_NODE) { roxml_del_arg_node(n); } else if (n->type & ROXML_TXT_NODE) { roxml_del_txt_node(n); } roxml_free_node(n); #endif /* CONFIG_XML_READ_WRITE */ } int ROXML_API roxml_commit_changes(node_t *n, char *dest, char **buffer, int human) { int len = 0; #if(CONFIG_XML_COMMIT_XML_TREE==1) int size = 0; if (n) { FILE *fout = NULL; if (dest) { fout = fopen(dest, "w"); } else if (buffer) { *buffer = (char *)malloc(ROXML_BASE_LEN); memset(*buffer, 0, ROXML_BASE_LEN); } if (fout || buffer) { len = ROXML_BASE_LEN; if ((n->prnt == NULL) || (n->prnt && n->prnt->prnt == NULL)) { if (n->prnt) { n = n->prnt->chld; } else { n = n->chld; } while (n) { roxml_write_node(n, fout, buffer, human, 0, &size, &len); n = n->sibl; } } else { roxml_write_node(n, fout, buffer, human, 0, &size, &len); } if (buffer) { char *ptr = NULL; len -= ROXML_BASE_LEN; ptr = *buffer + len; len += strlen(ptr); } else if (fout) { len = ftell(fout); fclose(fout); } } } #endif /* CONFIG_XML_COMMIT_XML_TREE */ return len; } node_t ROXML_API *roxml_add_node(node_t *parent, int position, int type, char *name, char *value) { node_t *new_node = NULL; #if(CONFIG_XML_READ_WRITE==1) int name_l = 0; int end_node = 0; int content_l = 0; int content_pos = 0; int end_content = 0; char *buffer = NULL; if (parent) { if (parent->type & ROXML_ATTR_NODE) { if (((type & ROXML_TXT_NODE) == 0) || (parent->chld)) { return NULL; } } else if ((parent->type & ROXML_ELM_NODE) == 0) { if (parent->prnt && (parent->prnt->type & ROXML_ELM_NODE)) { parent = parent->prnt; } else { return NULL; } } } if (value) { content_l = strlen(value); } if (name) { name_l = strlen(name); } if (type & ROXML_ATTR_NODE) { int xmlns_l = 0; if (!name || !value) { return NULL; } if (type & ROXML_NS_NODE) { if (name_l > 0) { xmlns_l = 6; } else { xmlns_l = 5; } buffer = (char *)malloc(sizeof(char) * (name_l + content_l + xmlns_l + 4)); sprintf(buffer, "xmlns%s%s=\"%s\"", name_l ? ":" : "", name, value); } else { buffer = (char *)malloc(sizeof(char) * (name_l + content_l + 4)); sprintf(buffer, "%s=\"%s\"", name, value); } content_pos = name_l + 2 + xmlns_l; end_node = name_l + 1 + xmlns_l; end_content = name_l + content_l + 2 + xmlns_l; } else if (type & ROXML_CMT_NODE) { if (!value) { return NULL; } buffer = (char *)malloc(sizeof(char) * (content_l + 8)); sprintf(buffer, "<!--%s-->", value); content_pos = 0; end_node = content_l + 4; end_content = content_l + 4; } else if (type & ROXML_PI_NODE) { if (!name) { return NULL; } if (content_l) { buffer = (char *)malloc(sizeof(char) * (name_l + content_l + 8)); sprintf(buffer, "<?%s %s?>", name, value); end_node = name_l + content_l + 3; end_content = name_l + content_l + 5; } else { buffer = (char *)malloc(sizeof(char) * (name_l + 7)); sprintf(buffer, "<?%s?>", name); end_node = name_l + 2; end_content = name_l + 4; } content_pos = 0; } else if (type & ROXML_TXT_NODE) { if (!value) { return NULL; } buffer = (char *)malloc(sizeof(char) * (content_l + 1)); sprintf(buffer, "%s", value); content_pos = 0; end_node = content_l + 1; end_content = content_l + 1; } else if (type & ROXML_ELM_NODE) { if (!name) { return NULL; } if (value) { buffer = (char *)malloc(sizeof(char) * (name_l * 2 + content_l + 6)); sprintf(buffer, "<%s>%s</%s>", name, value, name); content_pos = name_l + 2; end_node = name_l + content_l + 2; end_content = end_node; } else { buffer = (char *)malloc(sizeof(char) * (name_l + 5)); sprintf(buffer, "<%s />", name); } } else { return NULL; } new_node = roxml_create_node(0, buffer, type | ROXML_PENDING | ROXML_BUFF); new_node->end = end_node; if (type & ROXML_NS_NODE) { roxml_ns_t *ns = calloc(1, sizeof(roxml_ns_t) + name_l + 1); ns->id = ROXML_NS_ID; ns->alias = (char *)ns + sizeof(roxml_ns_t); if (name) strcpy(ns->alias, name); new_node->priv = ns; } if (((type & ROXML_ELM_NODE) && content_l) || (type & ROXML_ATTR_NODE)) { node_t *new_txt = roxml_create_node(content_pos, buffer, ROXML_TXT_NODE | ROXML_PENDING | ROXML_BUFF); roxml_append_node(new_node, new_txt); new_txt->end = end_content; } if (parent == NULL) { xpath_tok_table_t *table = (xpath_tok_table_t *) calloc(1, sizeof(xpath_tok_table_t)); parent = roxml_create_node(0, NULL, ROXML_ELM_NODE | ROXML_PENDING | ROXML_BUFF); parent->end = 1; table->id = ROXML_REQTABLE_ID; table->ids[ROXML_REQTABLE_ID] = 1; pthread_mutex_init(&table->mut, NULL); parent->priv = (void *)table; roxml_append_node(parent, new_node); } else { roxml_parent_node(parent, new_node, position); } #endif /* CONFIG_XML_READ_WRITE */ return new_node; }