-
Romain Jegat authoredRomain Jegat authored
roxml.c 19.70 KiB
/** \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;
}