summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordefanor <defanor@uberspace.net>2023-09-05 22:45:26 +0300
committerdefanor <defanor@uberspace.net>2023-09-07 18:00:36 +0300
commit1e01dbfc114e4ff96c428d1db38a4908ba8d0438 (patch)
treec36d4db88486de75285f346c4679bf6e934a7715
parent39b84f12e7f0488157eb64261c8585927a91e194 (diff)
Implement XML serialization in C and in Rust
Continuing replacement of libxml2, planning to use libexpat or a Rust XML parser as an alternative for XML parsing.
-rw-r--r--emacs/xml_interface.c2
-rw-r--r--examples/basic.c4
-rw-r--r--src/Makefile.am3
-rw-r--r--src/rexmpp.c4
-rw-r--r--src/rexmpp_openpgp.c2
-rw-r--r--src/rexmpp_utf8.h93
-rw-r--r--src/rexmpp_xml.c236
-rw-r--r--src/rexmpp_xml.h4
-rw-r--r--src/rexmpp_xml.rs246
9 files changed, 561 insertions, 33 deletions
diff --git a/emacs/xml_interface.c b/emacs/xml_interface.c
index 3dabf96..76e888c 100644
--- a/emacs/xml_interface.c
+++ b/emacs/xml_interface.c
@@ -26,7 +26,7 @@ and EOF ones, to simplify reading with libxml2.
void print_xml (rexmpp_xml_t *node) {
- char *s = rexmpp_xml_serialize(node);
+ char *s = rexmpp_xml_serialize(node, 0);
printf("%s%c\n", s, '\0');
free(s);
}
diff --git a/examples/basic.c b/examples/basic.c
index 66c54f6..0b77145 100644
--- a/examples/basic.c
+++ b/examples/basic.c
@@ -68,7 +68,7 @@ int my_sasl_property_cb (rexmpp_t *s, rexmpp_sasl_property prop) {
/* An XML in callback, printing what was received. */
int my_xml_in_cb (rexmpp_t *s, rexmpp_xml_t *node) {
- char *xml_buf = rexmpp_xml_serialize(node);
+ char *xml_buf = rexmpp_xml_serialize(node, 0);
printf("recv: %s\n", xml_buf);
free(xml_buf);
return 0;
@@ -76,7 +76,7 @@ int my_xml_in_cb (rexmpp_t *s, rexmpp_xml_t *node) {
/* An XML out callback, printing what is about to be sent. */
int my_xml_out_cb (rexmpp_t *s, rexmpp_xml_t *node) {
- char *xml_buf = rexmpp_xml_serialize(node);
+ char *xml_buf = rexmpp_xml_serialize(node, 0);
printf("send: %s\n", xml_buf);
free(xml_buf);
return 0;
diff --git a/src/Makefile.am b/src/Makefile.am
index 9e38c41..5a5cbc3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -21,7 +21,8 @@ librexmpp_la_SOURCES = rexmpp_roster.h rexmpp_roster.c \
rexmpp_jingle.h rexmpp_jingle.c \
rexmpp_base64.h rexmpp_base64.c \
rexmpp_sasl.h rexmpp_sasl.c \
- rexmpp_xml.h rexmpp_xml.c
+ rexmpp_xml.h rexmpp_xml.c \
+ rexmpp_utf8.h
include_HEADERS = config.h rexmpp_roster.h rexmpp_tcp.h rexmpp_socks.h rexmpp.h \
rexmpp_dns.h rexmpp_tls.h rexmpp_jid.h rexmpp_openpgp.h rexmpp_console.h \
diff --git a/src/rexmpp.c b/src/rexmpp.c
index a647245..77413f1 100644
--- a/src/rexmpp.c
+++ b/src/rexmpp.c
@@ -941,7 +941,7 @@ rexmpp_err_t rexmpp_send_continue (rexmpp_t *s)
s->send_buffer = NULL;
if (s->send_queue != NULL) {
rexmpp_xml_t *node = s->send_queue;
- unsigned char *buf = rexmpp_xml_serialize(node);
+ unsigned char *buf = rexmpp_xml_serialize(node, 0);
ret = rexmpp_send_start(s, buf, strlen(buf));
free(buf);
if (ret != REXMPP_SUCCESS) {
@@ -1034,7 +1034,7 @@ rexmpp_err_t rexmpp_send (rexmpp_t *s, rexmpp_xml_t *node)
}
if (s->send_buffer == NULL) {
- unsigned char *buf = rexmpp_xml_serialize(node);
+ unsigned char *buf = rexmpp_xml_serialize(node, 0);
ret = rexmpp_send_raw(s, buf, strlen(buf));
free(buf);
rexmpp_xml_free(node);
diff --git a/src/rexmpp_openpgp.c b/src/rexmpp_openpgp.c
index 0e2f634..c11d031 100644
--- a/src/rexmpp_openpgp.c
+++ b/src/rexmpp_openpgp.c
@@ -752,7 +752,7 @@ char *rexmpp_openpgp_payload (rexmpp_t *s,
}
/* Serialize the resulting XML. */
- char *plaintext = rexmpp_xml_serialize(elem);
+ char *plaintext = rexmpp_xml_serialize(elem, 0);
rexmpp_xml_free(elem);
/* Encrypt, base64-encode. */
diff --git a/src/rexmpp_utf8.h b/src/rexmpp_utf8.h
new file mode 100644
index 0000000..4096aac
--- /dev/null
+++ b/src/rexmpp_utf8.h
@@ -0,0 +1,93 @@
+/**
+ @file rexmpp_utf8.h
+ @brief UTF-8 helper functions
+ @author defanor <defanor@uberspace.net>
+ @date 2023
+ @copyright MIT license.
+*/
+
+#ifndef REXMPP_UTF8_H
+#define REXMPP_UTF8_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef HAVE_ICU
+
+#include <unicode/utf8.h>
+#define REXMPP_U8_NEXT U8_NEXT
+
+#else
+
+#define REXMPP_U8_NEXT(str, pos, len, out) \
+ rexmpp_utf8_next(str, &pos, len, &out);
+
+/**
+ @brief Similar to libicu's U8_NEXT macros: reads a single UTF-8
+ code point, advances the position.
+ @param[in] str A string to read.
+ @param[in,out] pos Byte position within the string. Advanced by the
+ number of bytes read to produce a code point, not advanced on
+ failure.
+ @param[in] len String length.
+ @param[in,out] out A pointer to the location for writing the code
+ point.
+ @returns 0 on failure, 1 on success.
+*/
+inline static
+void rexmpp_utf8_next (const uint8_t *str,
+ size_t *pos,
+ size_t len,
+ int32_t *out)
+{
+ if (*pos >= len) {
+ *out = -1;
+ return;
+ }
+
+ if ((str[*pos] & 0x80) == 0
+ && *pos + 1 <= len)
+ /* U+0000 to U+007F: 0xxxxxxx */
+ {
+ *out = str[*pos];
+ *pos = *pos + 1;
+ } else if ((str[*pos] & 0xe0) == 0xc0
+ && *pos + 2 <= len
+ && (str[*pos + 1] & 0xc0) == 0x80)
+ /* U+0080 to U+07FF: 110xxxxx 10xxxxxx */
+ {
+ *out = (((int32_t)(str[*pos] & 0x1f) << 6)
+ | ((int32_t)str[*pos + 1] & 0x3f));
+ *pos = *pos + 2;
+ } else if ((str[*pos] & 0xf0) == 0xe0
+ && *pos + 3 <= len
+ && (str[*pos + 1] & 0xc0) == 0x80
+ && (str[*pos + 2] & 0xc0) == 0x80)
+ /* U+0800 to U+FFFF: 1110xxxx 10xxxxxx 10xxxxxx */
+ {
+ *out = (((((int32_t)(str[*pos] & 0xf) << 6)
+ | ((int32_t)str[*pos + 1] & 0x3f)) << 6)
+ | ((int32_t)str[*pos + 2] & 0x3f));
+ *pos = *pos + 3;
+ } else if ((str[*pos] & 0xf8) == 0xf0
+ && *pos + 4 <= len
+ && (str[*pos + 1] & 0xc0) == 0x80
+ && (str[*pos + 2] & 0xc0) == 0x80
+ && (str[*pos + 3] & 0xc0) == 0x80)
+ /* U+10000 to U+10FFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+ {
+ *out = (((((((int32_t)(str[*pos] & 7) << 6)
+ | ((int32_t)str[*pos + 1] & 0x3f)) << 6)
+ | (((int32_t)str[*pos + 2] & 0x3f))) << 6)
+ | ((int32_t)str[*pos + 3] & 0x3f));
+ *pos = *pos + 4;
+ } else
+ /* Invalid UTF-8 */
+ {
+ *out = -1;
+ }
+}
+
+#endif /* HAVE_ICU */
+
+#endif /* REXMPP_UTF8_H */
diff --git a/src/rexmpp_xml.c b/src/rexmpp_xml.c
index f384470..7e428e2 100644
--- a/src/rexmpp_xml.c
+++ b/src/rexmpp_xml.c
@@ -7,9 +7,11 @@
*/
#include <string.h>
+#include <stdio.h>
#include <libxml/tree.h>
#include <libxml/xmlsave.h>
#include "rexmpp.h"
+#include "rexmpp_utf8.h"
#include "rexmpp_xml.h"
#ifndef USE_RUST
@@ -353,6 +355,206 @@ int rexmpp_xml_remove_attr (rexmpp_xml_t *node,
const char *name) {
return rexmpp_xml_remove_attr_ns(node, name, NULL);
}
+
+/* Adds a character, grows the string as needed. */
+inline char *rexmpp_str_putc (char *str, size_t *len, char c) {
+ char *ret = str;
+ if ((*len) % 1024 == 0) {
+ ret = realloc(str, (*len) + 1024);
+ if (ret == NULL) {
+ /* A failure to realloc. */
+ if (str != NULL) {
+ free(str);
+ }
+ return NULL;
+ }
+ }
+ ret[*len] = c;
+ *len = (*len) + 1;
+ return ret;
+}
+
+inline char *rexmpp_str_putc_escaped (char *str, size_t *len, char c) {
+ char *ret = str;
+ char buf[7];
+ char *esc = buf;
+ size_t i = 0;
+ size_t esc_len;
+ if (c == '<') {
+ esc = "&lt;";
+ } else if (c == '>') {
+ esc = "&gt;";
+ } else if (c == '&') {
+ esc = "&amp;";
+ } else if (c == '\'') {
+ esc = "&apos;";
+ } else if (c == '"') {
+ esc = "&quot;";
+ } else {
+ snprintf(esc, 7, "&#%u;", c);
+ }
+ esc_len = strlen(esc);
+ while (i < esc_len) {
+ ret = rexmpp_str_putc(ret, len, esc[i]);
+ i++;
+ }
+ return ret;
+}
+
+char *rexmpp_xml_print_name (char *str, size_t *len, const char *name) {
+ char *ret = str;
+ size_t name_len = strlen(name);
+ size_t i = 0;
+ int32_t c = 0; /* matches ICU's UChar32 */
+ size_t prev_i = 0, j;
+ do {
+ REXMPP_U8_NEXT(name, i, name_len, c);
+ if (c >= 0) {
+ if (c == ':'
+ || (c >= 'A' && c <= 'Z')
+ || c == '_'
+ || (c >= 'a' && c <= 'z')
+ || (c >= 0xC0 && c <= 0xD6)
+ || (c >= 0xD8 && c <= 0xF6)
+ || (c >= 0xF8 && c <= 0x2FF)
+ || (c >= 0x370 && c <= 0x37D)
+ || (c >= 0x37F && c <= 0x1FFF)
+ || (c >= 0x200C && c <= 0x200D)
+ || (c >= 0x2070 && c <= 0x218F)
+ || (c >= 0x2C00 && c <= 0x2FEF)
+ || (c >= 0x3001 && c <= 0xD7FF)
+ || (c >= 0xF900 && c <= 0xFDCF)
+ || (c >= 0xFDF0 && c <= 0xFFF0)
+ || (c >= 0x10000 && c <= 0xEFFFF)
+ || ((i > 0) &&
+ (c == '-'
+ || c == '.'
+ || (c >= '0' && c <= '9')
+ || c == 0xB7
+ || (c >= 0x0300 && c <= 0x036F)
+ || (c >= 0x203F && c <= 0x2040))))
+ {
+ /* Print the allowed characters. */
+ for (j = prev_i; j < i; j++) {
+ ret = rexmpp_str_putc(ret, len, name[j]);
+ }
+ }
+ } else {
+ /* Skip invalid characters. */
+ i++;
+ }
+ prev_i = i;
+ } while (i < name_len);
+ return ret;
+}
+
+char *rexmpp_xml_print_text (char *str, size_t *len, const char *text) {
+ char *ret = str;
+ size_t i = 0;
+ size_t text_len = strlen(text);
+ while (i < text_len && ret != NULL) {
+ char c = text[i];
+ if (strchr("<&>'\"", c)) {
+ /* Escape the few special characters. */
+ ret = rexmpp_str_putc_escaped(ret, len, c);
+ } else {
+ /* Write others as is. */
+ ret = rexmpp_str_putc(ret, len, c);
+ }
+ i++;
+ }
+ return ret;
+}
+
+char *rexmpp_xml_print_raw (char *str, size_t *len, const char *text) {
+ char *ret = str;
+ size_t i = 0;
+ size_t text_len = strlen(text);
+ while (i < text_len && ret != NULL) {
+ char c = text[i];
+ ret = rexmpp_str_putc(ret, len, c);
+ i++;
+ }
+ return ret;
+}
+
+inline char *rexmpp_xml_print_indent (char *str,
+ size_t *len,
+ int indent) {
+ if (indent <= 0) {
+ return str;
+ }
+ int i;
+ char *ret = str;
+ for (i = 0; i < indent * 2; i++) {
+ ret = rexmpp_str_putc(ret, len, ' ');
+ }
+ return ret;
+}
+
+char *rexmpp_xml_print (char *str,
+ size_t *len,
+ const rexmpp_xml_t *node,
+ int indent) {
+ char *ret = str;
+ if (node->type == REXMPP_XML_TEXT) {
+ ret = rexmpp_xml_print_text(ret, len, node->alt.text);
+ } else if (node->type == REXMPP_XML_ELEMENT) {
+ if (indent > 0) {
+ ret = rexmpp_str_putc(ret, len, '\n');
+ ret = rexmpp_xml_print_indent(ret, len, indent);
+ }
+ ret = rexmpp_str_putc(ret, len, '<');
+ ret = rexmpp_xml_print_name(ret, len, node->alt.elem.qname.name);
+ if (node->alt.elem.qname.namespace != NULL) {
+ ret = rexmpp_xml_print_raw(ret, len, " xmlns=\"");
+ ret = rexmpp_xml_print_text(ret, len, node->alt.elem.qname.namespace);
+ ret = rexmpp_str_putc(ret, len, '"');
+ }
+ if (node->alt.elem.attributes != NULL) {
+ rexmpp_xml_attr_t *attr;
+ for (attr = node->alt.elem.attributes; attr != NULL; attr = attr->next) {
+ ret = rexmpp_str_putc(ret, len, ' ');
+ /* Ignoring namespaces here for now. */
+ ret = rexmpp_xml_print_name(ret, len, attr->qname.name);
+ ret = rexmpp_xml_print_raw(ret, len, "=\"");
+ ret = rexmpp_xml_print_text(ret, len, attr->value);
+ ret = rexmpp_str_putc(ret, len, '"');
+ }
+ }
+ if (node->alt.elem.children == NULL) {
+ ret = rexmpp_xml_print_raw(ret, len, "/>");
+ } else {
+ ret = rexmpp_str_putc(ret, len, '>');
+ rexmpp_xml_t *child;
+ int last_child_is_textual = 0;
+ for (child = rexmpp_xml_children(node);
+ child != NULL;
+ child = child->next)
+ {
+ ret = rexmpp_xml_print(ret, len, child,
+ indent > -1 ? indent + 1 : -1);
+ last_child_is_textual = child->type == REXMPP_XML_TEXT;
+ }
+ if (indent >= 0 && ! last_child_is_textual) {
+ ret = rexmpp_str_putc(ret, len, '\n');
+ ret = rexmpp_xml_print_indent(ret, len, indent);
+ }
+ ret = rexmpp_xml_print_raw(ret, len, "</");
+ ret = rexmpp_xml_print_name(ret, len, node->alt.elem.qname.name);
+ ret = rexmpp_str_putc(ret, len, '>');
+ }
+ }
+ return ret;
+}
+
+char *rexmpp_xml_serialize (const rexmpp_xml_t *node, int pretty) {
+ size_t s_len = 0;
+ char *s = NULL;
+ s = rexmpp_xml_print(s, &s_len, node, pretty ? 0 : -1);
+ s = rexmpp_str_putc(s, &s_len, '\0');
+ return s;
+}
#endif
rexmpp_xml_t *
@@ -368,19 +570,6 @@ rexmpp_xml_add_id (rexmpp_t *s,
return node;
}
-char *rexmpp_xml_serialize (rexmpp_xml_t *node) {
- xmlNodePtr node_libxml2 = rexmpp_xml_to_libxml2(node);
- xmlBufferPtr buf = xmlBufferCreate();
- xmlSaveCtxtPtr ctx = xmlSaveToBuffer(buf, "utf-8", 0);
- xmlSaveTree(ctx, node_libxml2);
- xmlSaveFlush(ctx);
- xmlSaveClose(ctx);
- unsigned char *out = xmlBufferDetach(buf);
- xmlBufferFree(buf);
- xmlFreeNode(node_libxml2);
- return out;
-}
-
xmlNodePtr rexmpp_xml_parse_libxml2 (const char *str, int str_len) {
xmlNodePtr elem = NULL;
xmlDocPtr doc = xmlReadMemory(str, str_len, "", "utf-8", XML_PARSE_NONET);
@@ -409,16 +598,19 @@ rexmpp_xml_t *rexmpp_xml_read_file (const char *path) {
return ret;
}
+#ifndef USE_RUST
int rexmpp_xml_write_file (const char *path, rexmpp_xml_t* node) {
- xmlDocPtr doc = xmlNewDoc("1.0");
- xmlNodePtr node_lxml2 = rexmpp_xml_to_libxml2(node);
- xmlDocSetRootElement(doc, node_lxml2);
- xmlSaveFileEnc(path, doc, "utf-8");
- xmlFreeDoc(doc);
+ FILE *fd = fopen(path, "w");
+ if (fd == NULL) {
+ return -1;
+ }
+ char *serialized = rexmpp_xml_serialize(node, 1);
+ fputs("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n", fd);
+ fputs(serialized, fd);
+ fclose(fd);
return 0;
}
-#ifndef USE_RUST
unsigned int rexmpp_xml_siblings_count (rexmpp_xml_t *node) {
unsigned int i = 0;
for (i = 0; node != NULL; i++) {
@@ -546,8 +738,8 @@ rexmpp_xml_find_child (rexmpp_xml_t *node,
int rexmpp_xml_eq (rexmpp_xml_t *n1, rexmpp_xml_t *n2) {
/* Just serialize and compare strings for now: awkward, but
simple. */
- char *n1str = rexmpp_xml_serialize(n1);
- char *n2str = rexmpp_xml_serialize(n2);
+ char *n1str = rexmpp_xml_serialize(n1, 0);
+ char *n2str = rexmpp_xml_serialize(n2, 0);
int eq = (strcmp(n1str, n2str) == 0);
free(n1str);
free(n2str);
@@ -555,7 +747,7 @@ int rexmpp_xml_eq (rexmpp_xml_t *n1, rexmpp_xml_t *n2) {
}
#ifndef USE_RUST
-rexmpp_xml_t *rexmpp_xml_children (rexmpp_xml_t *node) {
+rexmpp_xml_t *rexmpp_xml_children (const rexmpp_xml_t *node) {
if (node != NULL && node->type == REXMPP_XML_ELEMENT) {
return node->alt.elem.children;
}
diff --git a/src/rexmpp_xml.h b/src/rexmpp_xml.h
index dbd8ea8..0a3cb63 100644
--- a/src/rexmpp_xml.h
+++ b/src/rexmpp_xml.h
@@ -148,7 +148,7 @@ rexmpp_xml_add_id (rexmpp_t *s,
@param[in] node An XML node.
@returns A string (must be freed by the caller).
*/
-char *rexmpp_xml_serialize (rexmpp_xml_t *node);
+char *rexmpp_xml_serialize (const rexmpp_xml_t *node, int pretty);
/**
@brief Count the number of siblings after a given node.
@@ -207,7 +207,7 @@ rexmpp_xml_t *rexmpp_xml_find_child (rexmpp_xml_t *node,
const char *namespace,
const char *name);
-rexmpp_xml_t *rexmpp_xml_children (rexmpp_xml_t *node);
+rexmpp_xml_t *rexmpp_xml_children (const rexmpp_xml_t *node);
char *rexmpp_xml_text (rexmpp_xml_t *node);
diff --git a/src/rexmpp_xml.rs b/src/rexmpp_xml.rs
index 56de5ff..30f5f38 100644
--- a/src/rexmpp_xml.rs
+++ b/src/rexmpp_xml.rs
@@ -4,9 +4,11 @@ use std::os::raw::{c_char, c_int, c_void, c_uint};
use std::ptr;
use std::ffi::{CStr, CString};
use std::clone::Clone;
+use std::fs::File;
+use std::io::Write;
+
// extern {
-// fn rexmpp_xml_serialize (node: *mut RexmppXML) -> *mut c_char;
// fn rexmpp_xml_parse (str: *mut c_char, str_len: c_int) -> *mut RexmppXML;
// }
@@ -370,6 +372,212 @@ fn rexmpp_xml_remove_attr (node: *mut RexmppXML,
}
+fn rexmpp_xml_push_escaped (c: char, s: &mut String) {
+ if c == '<' {
+ s.push_str("&lt;")
+ } else if c == '>' {
+ s.push_str("&gt;")
+ } else if c == '&' {
+ s.push_str("&amp;")
+ } else if c == '\'' {
+ s.push_str("&apos;")
+ } else if c == '"' {
+ s.push_str("&quot;")
+ } else {
+ s.push_str(format!("&#{};", u32::from(c)).as_str());
+ };
+}
+
+fn rexmpp_xml_print_text (c: char, s: &mut String) {
+ if "<&>'\"".chars().any(|sc| sc == c) {
+ rexmpp_xml_push_escaped(c, s);
+ } else {
+ s.push(c);
+ }
+}
+
+fn rexmpp_xml_print_name (i: usize, c: char, s: &mut String) {
+ if c == ':'
+ || (c >= 'A' && c <= 'Z')
+ || c == '_'
+ || (c >= 'a' && c <= 'z')
+ || (c >= '\u{C0}' && c <= '\u{D6}')
+ || (c >= '\u{D8}' && c <= '\u{F6}')
+ || (c >= '\u{F8}' && c <= '\u{2FF}')
+ || (c >= '\u{370}' && c <= '\u{37D}')
+ || (c >= '\u{37F}' && c <= '\u{1FFF}')
+ || (c >= '\u{200C}' && c <= '\u{200D}')
+ || (c >= '\u{2070}' && c <= '\u{218F}')
+ || (c >= '\u{2C00}' && c <= '\u{2FEF}')
+ || (c >= '\u{3001}' && c <= '\u{D7FF}')
+ || (c >= '\u{F900}' && c <= '\u{FDCF}')
+ || (c >= '\u{FDF0}' && c <= '\u{FFF0}')
+ || (c >= '\u{10000}' && c <= '\u{EFFFF}')
+ || ((i > 0) &&
+ (c == '-'
+ || c == '.'
+ || (c >= '0' && c <= '9')
+ || c == '\u{B7}'
+ || (c >= '\u{0300}' && c <= '\u{036F}')
+ || (c >= '\u{203F}' && c <= '\u{2040}')))
+ {
+ // Print the allowed characters.
+ s.push(c);
+ }
+}
+
+fn rexmpp_xml_print_indent (indent: i32, s: &mut String) {
+ let mut i = 0;
+ while i < indent {
+ s.push_str(" ");
+ i = i + 1;
+ }
+}
+
+fn rexmpp_xml_print (node_ptr: *const RexmppXML,
+ ret: &mut String,
+ indent: i32)
+ -> ()
+{
+ unsafe {
+ let node : RexmppXML = *node_ptr;
+ match node {
+ RexmppXML { node_type : NodeType::Text,
+ alt : RexmppXMLAlt { text: text_ptr },
+ next: _} => {
+ let text_cstr : &CStr = CStr::from_ptr(text_ptr);
+ let text_str : String =
+ String::from_utf8_lossy(text_cstr.to_bytes())
+ .to_string();
+ // let mut escaped = String::with_capacity(text_str.capacity());
+ text_str.chars().
+ for_each(|c| rexmpp_xml_print_text(c, ret));
+ },
+ RexmppXML { node_type : NodeType::Element,
+ alt : RexmppXMLAlt { elem: element },
+ next: _} => {
+ // let mut ret = String::with_capacity(1024);
+ let name_cstr : &CStr =
+ CStr::from_ptr(element.qname.name);
+ let name_str : String =
+ String::from_utf8_lossy(name_cstr.to_bytes())
+ .to_string();
+ if indent > 0 {
+ ret.push('\n');
+ rexmpp_xml_print_indent(indent, ret);
+ }
+ ret.push('<');
+ name_str.char_indices().
+ for_each(|(i, c)| rexmpp_xml_print_name(i, c, ret));
+ if element.qname.namespace != ptr::null_mut() {
+ let namespace_cstr : &CStr =
+ CStr::from_ptr(element.qname.namespace);
+ let namespace_str : String =
+ String::from_utf8_lossy(namespace_cstr.to_bytes())
+ .to_string();
+ ret.push_str(" xmlns=\"");
+ namespace_str.chars().
+ for_each(|c| rexmpp_xml_print_text(c, ret));
+ ret.push('"');
+ }
+ if element.attributes != ptr::null_mut() {
+ let mut attr_ptr : *mut RexmppXMLAttribute =
+ element.attributes;
+ while attr_ptr != ptr::null_mut() {
+ let attr : RexmppXMLAttribute = *attr_ptr;
+ let attr_name_cstr =
+ CStr::from_ptr(attr.qname.name);
+ let attr_name_str =
+ String::from_utf8_lossy(attr_name_cstr.to_bytes())
+ .to_string();
+ // TODO: handle attribute namespaces someday.
+ let attr_value_cstr =
+ CStr::from_ptr(attr.value);
+ let attr_value_str =
+ String::from_utf8_lossy(attr_value_cstr.to_bytes())
+ .to_string();
+ ret.push(' ');
+ attr_name_str.char_indices().
+ for_each(|(i, c)|
+ rexmpp_xml_print_name(i, c, ret));
+ ret.push_str("=\"");
+ attr_value_str.chars().
+ for_each(|c| rexmpp_xml_print_text(c, ret));
+ ret.push('"');
+ attr_ptr = (*attr_ptr).next;
+ }
+ }
+ if element.children == ptr::null_mut() {
+ ret.push_str("/>");
+ } else {
+ ret.push('>');
+ let mut child = rexmpp_xml_children(node_ptr);
+ let mut last_child_is_textual = false;
+ while child != ptr::null_mut() {
+ rexmpp_xml_print(child, ret,
+ if indent > -1 { indent + 1 }
+ else { -1 } );
+ last_child_is_textual =
+ (*child).node_type == NodeType::Text;
+ child = (*child).next;
+ }
+ if indent > 0 && ! last_child_is_textual {
+ ret.push('\n');
+ rexmpp_xml_print_indent(indent, ret);
+ }
+ ret.push_str("</");
+ name_str.char_indices().
+ for_each(|(i, c)|
+ rexmpp_xml_print_name(i, c, ret));
+ ret.push('>');
+ }
+ }
+ }
+ }
+}
+
+fn rexmpp_xml_serialize_str (node_ptr: *const RexmppXML,
+ pretty: bool)
+ -> String
+{
+ let mut out = String::with_capacity(4096);
+ rexmpp_xml_print(node_ptr, &mut out, if pretty { 0 } else { -1 });
+ return out;
+}
+
+#[no_mangle]
+extern "C"
+fn rexmpp_xml_serialize (node_ptr: *const RexmppXML,
+ pretty: bool)
+ -> *mut c_char
+{
+ let out = rexmpp_xml_serialize_str(node_ptr, pretty);
+ match CString::new(out) {
+ Ok(cstr) => unsafe { strdup(cstr.as_ptr()) },
+ Err(_) => ptr::null_mut()
+ }
+}
+
+#[no_mangle]
+extern "C"
+fn rexmpp_xml_write_file (path: *const c_char,
+ node: *const RexmppXML)
+ -> c_int
+{
+ let path_cstr : &CStr = unsafe { CStr::from_ptr(path) };
+ let path_str : String =
+ String::from_utf8_lossy(path_cstr.to_bytes())
+ .to_string();
+ match File::create(path_str) {
+ Ok(mut fd) => {
+ fd.write_all(b"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
+ fd.write_all(rexmpp_xml_serialize_str(node, false).as_bytes());
+ },
+ Err(_) => { return -1; }
+ }
+ return 0;
+}
+
#[no_mangle]
extern "C"
fn rexmpp_xml_siblings_count (mut node: *const RexmppXML) -> c_uint {
@@ -567,7 +775,7 @@ fn rexmpp_xml_find_child (node_ptr: *mut RexmppXML,
#[no_mangle]
extern "C"
-fn rexmpp_xml_children (node: *mut RexmppXML)
+fn rexmpp_xml_children (node: *const RexmppXML)
-> *mut RexmppXML {
if node != ptr::null_mut()
&& unsafe { (*node).node_type } == NodeType::Element {
@@ -624,3 +832,37 @@ fn rexmpp_xml_text_child (node: *mut RexmppXML)
-> *mut c_char {
rexmpp_xml_text(rexmpp_xml_children(node))
}
+
+#[no_mangle]
+extern "C"
+fn rexmpp_xml_reverse (mut node: *mut RexmppXML)
+ -> *mut RexmppXML {
+ let mut next;
+ let mut prev = ptr::null_mut();
+ while node != ptr::null_mut() {
+ unsafe {
+ next = (*node).next;
+ (*node).next = prev;
+ prev = node;
+ node = next;
+ }
+ }
+ return prev;
+}
+
+#[no_mangle]
+extern "C"
+fn rexmpp_xml_reverse_all (node: *mut RexmppXML)
+ -> *mut RexmppXML {
+ let mut cur = node;
+ while cur != ptr::null_mut() {
+ unsafe {
+ if (*cur).node_type == NodeType::Element {
+ (*cur).alt.elem.children =
+ rexmpp_xml_reverse_all((*cur).alt.elem.children);
+ }
+ cur = (*cur).next;
+ }
+ }
+ return node;
+}