summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/xine-utils/xmlparser.c103
-rw-r--r--src/xine-utils/xmlparser.h15
2 files changed, 89 insertions, 29 deletions
diff --git a/src/xine-utils/xmlparser.c b/src/xine-utils/xmlparser.c
index 326e21997..fe78e8e88 100644
--- a/src/xine-utils/xmlparser.c
+++ b/src/xine-utils/xmlparser.c
@@ -80,8 +80,11 @@ static xml_node_t * new_xml_node(void) {
return new_node;
}
+static const char cdata[] = CDATA_MARKER;
+
static void free_xml_node(xml_node_t * node) {
- free (node->name);
+ if (node->name != cdata)
+ free (node->name);
free (node->data);
free(node);
}
@@ -180,9 +183,42 @@ typedef enum {
STATE_CDATA,
} parser_state_t;
+static xml_node_t *xml_parser_append_text (xml_node_t *node, xml_node_t *subnode, const char *text, int flags)
+{
+ if (!text || !*text)
+ return subnode; /* empty string -> nothing to do */
+
+ if ((flags & XML_PARSER_MULTI_TEXT) && subnode) {
+ /* we have a subtree, so we can't use node->data */
+ if (subnode->name == cdata) {
+ /* most recent node is CDATA - append to it */
+ char *newtext;
+ asprintf (&newtext, "%s%s", subnode->data, text);
+ free (subnode->data);
+ subnode->data = newtext;
+ } else {
+ /* most recent node is not CDATA - add a sibling */
+ subnode->next = new_xml_node ();
+ subnode->next->name = cdata;
+ subnode->next->data = strdup (text);
+ subnode = subnode->next;
+ }
+ } else if (node->data) {
+ /* "no" subtree, but we have existing text - append to it */
+ char *newtext;
+ asprintf (&newtext, "%s%s", node->data, text);
+ free (node->data);
+ node->data = newtext;
+ } else
+ /* no text, "no" subtree - duplicate & assign */
+ node->data = strdup (text);
+
+ return subnode;
+}
+
#define Q_STATE(CURRENT,NEW) (STATE_##NEW + state - STATE_##CURRENT)
-static int xml_parser_get_node_internal (xml_node_t *current_node, char *root_names[], int rec, int relaxed)
+static int xml_parser_get_node_internal (xml_node_t *current_node, char *root_names[], int rec, int flags)
{
char tok[TOKEN_SIZE];
char property_name[TOKEN_SIZE];
@@ -233,16 +269,11 @@ static int xml_parser_get_node_internal (xml_node_t *current_node, char *root_na
break;
case (T_DATA):
/* current data */
- if (current_node->data) {
- /* Append to existing text. FIXME - should use a child node */
- char *data, *decoded = lexer_decode_entities (tok);
- asprintf (&data, "%s%s", current_node->data, decoded);
+ {
+ char *decoded = lexer_decode_entities (tok);
+ current_subtree = xml_parser_append_text (current_node, current_subtree, decoded, flags);
free (decoded);
- free (current_node->data);
- current_node->data = data;
}
- else
- current_node->data = lexer_decode_entities (tok);
lprintf("info: node data : %s\n", current_node->data);
break;
default:
@@ -296,7 +327,7 @@ static int xml_parser_get_node_internal (xml_node_t *current_node, char *root_na
subtree->props = properties;
lprintf("info: rec %d new subtree %s\n", rec, node_name);
root_names[rec + 1] = node_name;
- parse_res = xml_parser_get_node_internal(subtree, root_names, rec + 1, relaxed);
+ parse_res = xml_parser_get_node_internal(subtree, root_names, rec + 1, flags);
if (parse_res == -1 || parse_res > 0) {
return parse_res;
}
@@ -379,7 +410,7 @@ static int xml_parser_get_node_internal (xml_node_t *current_node, char *root_na
}
if (strcmp(tok, root_names[rec]) == 0) {
state = STATE_TAG_TERM;
- } else if (relaxed) {
+ } else if (flags & XML_PARSER_RELAXED) {
int r = rec;
while (--r >= 0)
if (strcmp(tok, root_names[r]) == 0) {
@@ -546,16 +577,8 @@ static int xml_parser_get_node_internal (xml_node_t *current_node, char *root_na
case STATE_CDATA:
switch (res) {
case (T_CDATA_STOP):
- if (current_node->data) {
- /* Append to existing text. FIXME - should use a child node */
- char *data;
- asprintf (&data, "%s%s", current_node->data, tok);
- free (current_node->data);
- current_node->data = data;
- }
- else
- current_node->data = strdup (tok);
- lprintf("info: node cdata : %s\n", current_node->data);
+ current_subtree = xml_parser_append_text (current_node, current_subtree, tok, flags);
+ lprintf("info: node cdata : %s\n", tok);
state = STATE_IDLE;
break;
default:
@@ -594,22 +617,40 @@ static int xml_parser_get_node_internal (xml_node_t *current_node, char *root_na
}
}
-static int xml_parser_get_node (xml_node_t *current_node, int relaxed)
+static int xml_parser_get_node (xml_node_t *current_node, int flags)
{
char *root_names[MAX_RECURSION + 1];
root_names[0] = "";
- return xml_parser_get_node_internal (current_node, root_names, 0, relaxed);
+ return xml_parser_get_node_internal (current_node, root_names, 0, flags);
}
-int xml_parser_build_tree_relaxed(xml_node_t **root_node, int relaxed) {
- xml_node_t *tmp_node, *pri_node, *q_node = NULL;
+int xml_parser_build_tree_with_options(xml_node_t **root_node, int flags) {
+ xml_node_t *tmp_node, *pri_node, *q_node;
int res;
tmp_node = new_xml_node();
- res = xml_parser_get_node(tmp_node, relaxed);
+ res = xml_parser_get_node(tmp_node, flags);
+
+ /* delete any top-level [CDATA] nodes */;
+ pri_node = tmp_node->child;
+ q_node = NULL;
+ while (pri_node) {
+ if (pri_node->name == cdata) {
+ xml_node_t *old = pri_node;
+ if (q_node)
+ q_node->next = pri_node->next;
+ else
+ q_node = pri_node;
+ pri_node = pri_node->next;
+ free_xml_node (old);
+ } else {
+ q_node = pri_node;
+ pri_node = pri_node->next;
+ }
+ }
/* find first non-<?...?> node */;
- for (pri_node = tmp_node->child;
+ for (pri_node = tmp_node->child, q_node = NULL;
pri_node && pri_node->name[0] == '?';
pri_node = pri_node->next)
q_node = pri_node; /* last <?...?> node (eventually), or NULL */
@@ -631,8 +672,12 @@ int xml_parser_build_tree_relaxed(xml_node_t **root_node, int relaxed) {
return res;
}
+int xml_parser_build_tree_relaxed(xml_node_t **root_node, int relaxed) {
+ return xml_parser_build_tree_with_options (root_node, relaxed ? XML_PARSER_RELAXED : 0);
+}
+
int xml_parser_build_tree(xml_node_t **root_node) {
- return xml_parser_build_tree_relaxed (root_node, 0);
+ return xml_parser_build_tree_with_options (root_node, 0);
}
const char *xml_parser_get_property (const xml_node_t *node, const char *name) {
diff --git a/src/xine-utils/xmlparser.h b/src/xine-utils/xmlparser.h
index 8d4915abf..364cc4c2b 100644
--- a/src/xine-utils/xmlparser.h
+++ b/src/xine-utils/xmlparser.h
@@ -37,6 +37,12 @@
#define XML_PARSER_OK 0
#define XML_PARSER_ERROR 1
+/* xml_parser_build_tree_with_options flag bits */
+#define XML_PARSER_RELAXED 1
+#define XML_PARSER_MULTI_TEXT 2
+
+/* node name for extra text chunks */
+#define CDATA_MARKER "[CDATA]"
/* xml property */
typedef struct xml_property_s {
@@ -46,6 +52,14 @@ typedef struct xml_property_s {
} xml_property_t;
/* xml node */
+/* .data contains any text which precedes any subtree elements;
+ * subtree elements may also contain only text; if so, name is "[CDATA]".
+ * e.g. <a>b<c />d</a>
+ * node1: .name="a" .data="b" .child=node2 .next=NULL
+ * node2: .name="c" .data=NULL .child=NULL .next=node3
+ * node3: .name="[CDATA]" .data="d" .child=NULL .next=NULL
+ * Adjacent text items are merged.
+ */
typedef struct xml_node_s {
char *name;
char *data;
@@ -58,6 +72,7 @@ void xml_parser_init(const char * buf, int size, int mode) XINE_PROTECTED;
int xml_parser_build_tree(xml_node_t **root_node) XINE_PROTECTED;
int xml_parser_build_tree_relaxed(xml_node_t **root_node, int relaxed) XINE_PROTECTED;
+int xml_parser_build_tree_with_options(xml_node_t **root_node, int flags) XINE_PROTECTED;
void xml_parser_free_tree(xml_node_t *root_node) XINE_PROTECTED;