mirror of
https://github.com/djhackersdev/saltytools.git
synced 2026-04-24 07:27:14 -05:00
660 lines
15 KiB
C
660 lines
15 KiB
C
#include <assert.h>
|
|
|
|
#include "573file/prop-type.h"
|
|
#include "573file/prop-xml-writer.h"
|
|
#include "573file/prop.h"
|
|
|
|
#include "util/iobuf.h"
|
|
#include "util/log.h"
|
|
#include "util/str.h"
|
|
|
|
enum prop_xml_escape {
|
|
PROP_XML_ESCAPE_ATTR,
|
|
PROP_XML_ESCAPE_TEXT,
|
|
};
|
|
|
|
static void prop_xml_write_attr_list(struct strbuf *dest, const struct prop *p);
|
|
static void prop_xml_write_children(struct strbuf *dest, const struct prop *p,
|
|
unsigned int indent);
|
|
static void prop_xml_write_escaped_string(struct strbuf *dest, const char *str,
|
|
enum prop_xml_escape ctx);
|
|
static void prop_xml_write_indent(struct strbuf *dest, unsigned int indent);
|
|
static void prop_xml_write_node(struct strbuf *dest, const struct prop *p,
|
|
unsigned int indent);
|
|
static void prop_xml_write_nonvoid(struct strbuf *dest, const struct prop *p,
|
|
unsigned int indent);
|
|
static void prop_xml_write_void(struct strbuf *dest, const struct prop *p,
|
|
unsigned int indent);
|
|
static void prop_xml_write_text(struct strbuf *dest, const struct prop *p,
|
|
enum prop_xml_escape ctx);
|
|
static void prop_xml_write_text_element(struct strbuf *dest,
|
|
enum prop_type type,
|
|
struct const_iobuf *src,
|
|
enum prop_xml_escape ctx);
|
|
static void prop_xml_write_text_s8(struct strbuf *dest,
|
|
struct const_iobuf *src);
|
|
static void prop_xml_write_text_s16(struct strbuf *dest,
|
|
struct const_iobuf *src);
|
|
static void prop_xml_write_text_s32(struct strbuf *dest,
|
|
struct const_iobuf *src);
|
|
static void prop_xml_write_text_s64(struct strbuf *dest,
|
|
struct const_iobuf *src);
|
|
static void prop_xml_write_text_u8(struct strbuf *dest,
|
|
struct const_iobuf *src);
|
|
static void prop_xml_write_text_u16(struct strbuf *dest,
|
|
struct const_iobuf *src);
|
|
static void prop_xml_write_text_u32(struct strbuf *dest,
|
|
struct const_iobuf *src);
|
|
static void prop_xml_write_text_u64(struct strbuf *dest,
|
|
struct const_iobuf *src);
|
|
static void prop_xml_write_text_bin(struct strbuf *dest,
|
|
struct const_iobuf *src);
|
|
static void prop_xml_write_text_str(struct strbuf *dest,
|
|
struct const_iobuf *src,
|
|
enum prop_xml_escape ctx);
|
|
static void prop_xml_write_text_ip4(struct strbuf *dest,
|
|
struct const_iobuf *src);
|
|
static void prop_xml_write_text_bool(struct strbuf *dest,
|
|
struct const_iobuf *src);
|
|
static void prop_xml_write_text_s32_tuple(struct strbuf *dest,
|
|
struct const_iobuf *src,
|
|
size_t count);
|
|
static void prop_xml_write_text_u16_tuple(struct strbuf *dest,
|
|
struct const_iobuf *src,
|
|
size_t count);
|
|
|
|
int prop_xml_write(const struct prop *p, char **out) {
|
|
struct strbuf buf;
|
|
char *chars;
|
|
|
|
assert(p != NULL);
|
|
assert(out != NULL);
|
|
|
|
*out = NULL;
|
|
|
|
buf.chars = NULL;
|
|
buf.nchars = 0;
|
|
buf.pos = 0;
|
|
|
|
prop_xml_write_node(&buf, p, 0);
|
|
chars = malloc(buf.pos + 1);
|
|
|
|
if (chars == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
buf.chars = chars;
|
|
buf.nchars = buf.pos + 1;
|
|
buf.pos = 0;
|
|
|
|
prop_xml_write_node(&buf, p, 0);
|
|
|
|
assert(buf.pos + 1 == buf.nchars);
|
|
|
|
*out = chars;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void prop_xml_write_node(struct strbuf *dest, const struct prop *p,
|
|
unsigned int indent) {
|
|
enum prop_type type;
|
|
|
|
assert(dest != NULL);
|
|
assert(p != NULL);
|
|
|
|
type = prop_get_type(p);
|
|
|
|
if (type == PROP_VOID) {
|
|
prop_xml_write_void(dest, p, indent);
|
|
} else {
|
|
prop_xml_write_nonvoid(dest, p, indent);
|
|
}
|
|
}
|
|
|
|
static void prop_xml_write_void(struct strbuf *dest, const struct prop *p,
|
|
unsigned int indent) {
|
|
const struct prop *first_child;
|
|
const char *name;
|
|
|
|
assert(dest != NULL);
|
|
assert(p != NULL);
|
|
|
|
name = prop_get_name(p);
|
|
|
|
prop_xml_write_indent(dest, indent);
|
|
strbuf_printf(dest, "<%s", name);
|
|
prop_xml_write_attr_list(dest, p);
|
|
|
|
first_child = prop_get_first_child_const(p);
|
|
|
|
if (first_child != NULL) {
|
|
strbuf_puts(dest, ">\n");
|
|
prop_xml_write_children(dest, p, indent + 1);
|
|
prop_xml_write_indent(dest, indent);
|
|
strbuf_printf(dest, "</%s>\n", name);
|
|
} else {
|
|
strbuf_puts(dest, "/>\n");
|
|
}
|
|
}
|
|
|
|
/* AVS doesn't understand this __value attribute here, this is my own
|
|
invention, but generally we don't see any property pages with mixed content
|
|
nodes being stored in XML format to begin with. */
|
|
|
|
static void prop_xml_write_nonvoid(struct strbuf *dest, const struct prop *p,
|
|
unsigned int indent) {
|
|
const struct prop *first_child;
|
|
const char *name;
|
|
const char *type_str;
|
|
enum prop_type type;
|
|
|
|
assert(dest != NULL);
|
|
assert(p != NULL);
|
|
|
|
name = prop_get_name(p);
|
|
type = prop_get_type(p);
|
|
type_str = prop_type_to_string(type);
|
|
|
|
prop_xml_write_indent(dest, indent);
|
|
strbuf_printf(dest, "<%s __type=\"%s\"", name, type_str);
|
|
|
|
if (prop_type_is_array(type)) {
|
|
strbuf_printf(dest, " __count=\"%lu\"", (unsigned long)prop_get_count(p));
|
|
}
|
|
|
|
first_child = prop_get_first_child_const(p);
|
|
|
|
if (first_child != NULL) {
|
|
strbuf_puts(dest, " __value=\"");
|
|
prop_xml_write_text(dest, p, PROP_XML_ESCAPE_ATTR);
|
|
strbuf_putc(dest, '"');
|
|
|
|
prop_xml_write_attr_list(dest, p);
|
|
strbuf_puts(dest, ">\n");
|
|
|
|
prop_xml_write_children(dest, p, indent + 1);
|
|
|
|
prop_xml_write_indent(dest, indent);
|
|
strbuf_printf(dest, "</%s>\n", name);
|
|
} else {
|
|
prop_xml_write_attr_list(dest, p);
|
|
strbuf_putc(dest, '>');
|
|
|
|
prop_xml_write_text(dest, p, PROP_XML_ESCAPE_TEXT);
|
|
|
|
strbuf_printf(dest, "</%s>\n", name);
|
|
}
|
|
}
|
|
|
|
static void prop_xml_write_children(struct strbuf *dest, const struct prop *p,
|
|
unsigned int indent) {
|
|
const struct prop *child;
|
|
|
|
assert(dest != NULL);
|
|
assert(p != NULL);
|
|
|
|
for (child = prop_get_first_child_const(p); child != NULL;
|
|
child = prop_get_next_sibling_const(child)) {
|
|
prop_xml_write_node(dest, child, indent);
|
|
}
|
|
}
|
|
|
|
static void prop_xml_write_attr_list(struct strbuf *dest,
|
|
const struct prop *p) {
|
|
const struct attr *a;
|
|
const char *attr_key;
|
|
const char *attr_val;
|
|
|
|
assert(dest != NULL);
|
|
assert(p != NULL);
|
|
|
|
for (a = prop_get_first_attr(p); a != NULL; a = attr_get_next_sibling(a)) {
|
|
attr_key = attr_get_key(a);
|
|
attr_val = attr_get_val(a);
|
|
|
|
strbuf_printf(dest, " %s=\"", attr_key);
|
|
prop_xml_write_escaped_string(dest, attr_val, PROP_XML_ESCAPE_ATTR);
|
|
strbuf_putc(dest, '"');
|
|
}
|
|
}
|
|
|
|
static void prop_xml_write_escaped_string(struct strbuf *dest, const char *str,
|
|
enum prop_xml_escape ctx) {
|
|
const char *pos;
|
|
char c;
|
|
|
|
assert(dest != NULL);
|
|
assert(str != NULL);
|
|
|
|
for (pos = str; *pos != '\0'; pos++) {
|
|
c = *pos;
|
|
|
|
switch (c) {
|
|
case '<':
|
|
strbuf_puts(dest, "<");
|
|
|
|
break;
|
|
|
|
case '>':
|
|
strbuf_puts(dest, ">");
|
|
|
|
break;
|
|
|
|
case '&':
|
|
strbuf_puts(dest, "&");
|
|
|
|
break;
|
|
|
|
case '\'':
|
|
if (ctx == PROP_XML_ESCAPE_ATTR) {
|
|
strbuf_puts(dest, "'");
|
|
} else {
|
|
strbuf_putc(dest, c);
|
|
}
|
|
|
|
break;
|
|
|
|
case '"':
|
|
if (ctx == PROP_XML_ESCAPE_ATTR) {
|
|
strbuf_puts(dest, """);
|
|
} else {
|
|
strbuf_putc(dest, c);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
strbuf_putc(dest, c);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void prop_xml_write_indent(struct strbuf *dest, unsigned int indent) {
|
|
unsigned int i;
|
|
|
|
assert(dest != NULL);
|
|
|
|
for (i = 0; i < indent; i++) {
|
|
strbuf_puts(dest, " ");
|
|
}
|
|
}
|
|
|
|
static void prop_xml_write_text(struct strbuf *dest, const struct prop *p,
|
|
enum prop_xml_escape ctx) {
|
|
struct const_iobuf value;
|
|
struct const_iobuf item;
|
|
enum prop_type type;
|
|
uint32_t count;
|
|
uint32_t i;
|
|
int item_size;
|
|
int r;
|
|
|
|
type = prop_get_type(p);
|
|
prop_borrow_value(p, &value);
|
|
|
|
if (prop_type_is_array(type)) {
|
|
item_size = prop_type_to_size(type);
|
|
count = prop_get_count(p);
|
|
|
|
assert(item_size > 0);
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (i > 0) {
|
|
strbuf_putc(dest, ' ');
|
|
}
|
|
|
|
r = iobuf_slice(&item, &value, item_size);
|
|
|
|
assert(r >= 0);
|
|
|
|
prop_xml_write_text_element(dest, type, &item, ctx);
|
|
}
|
|
} else {
|
|
prop_xml_write_text_element(dest, type, &value, ctx);
|
|
}
|
|
}
|
|
|
|
static void prop_xml_write_text_element(struct strbuf *dest,
|
|
enum prop_type type,
|
|
struct const_iobuf *src,
|
|
enum prop_xml_escape ctx) {
|
|
type &= ~PROP_ARRAY_FLAG;
|
|
|
|
switch (type) {
|
|
case PROP_S8:
|
|
prop_xml_write_text_s8(dest, src);
|
|
|
|
break;
|
|
|
|
case PROP_S16:
|
|
prop_xml_write_text_s16(dest, src);
|
|
|
|
break;
|
|
|
|
case PROP_S32:
|
|
prop_xml_write_text_s32(dest, src);
|
|
|
|
break;
|
|
|
|
case PROP_S64:
|
|
prop_xml_write_text_s64(dest, src);
|
|
|
|
break;
|
|
|
|
case PROP_U8:
|
|
prop_xml_write_text_u8(dest, src);
|
|
|
|
break;
|
|
|
|
case PROP_U16:
|
|
prop_xml_write_text_u16(dest, src);
|
|
|
|
break;
|
|
|
|
case PROP_U32:
|
|
prop_xml_write_text_u32(dest, src);
|
|
|
|
break;
|
|
|
|
case PROP_U64:
|
|
prop_xml_write_text_u64(dest, src);
|
|
|
|
break;
|
|
|
|
case PROP_BIN:
|
|
prop_xml_write_text_bin(dest, src);
|
|
|
|
break;
|
|
|
|
case PROP_STR:
|
|
prop_xml_write_text_str(dest, src, ctx);
|
|
|
|
break;
|
|
|
|
case PROP_IP4:
|
|
prop_xml_write_text_ip4(dest, src);
|
|
|
|
break;
|
|
|
|
case PROP_TIME:
|
|
/* Reuse the u32 writer for timestamps */
|
|
prop_xml_write_text_u32(dest, src);
|
|
|
|
break;
|
|
|
|
case PROP_2U16:
|
|
prop_xml_write_text_u16_tuple(dest, src, 2);
|
|
|
|
break;
|
|
|
|
case PROP_3S32:
|
|
prop_xml_write_text_s32_tuple(dest, src, 3);
|
|
|
|
break;
|
|
|
|
case PROP_4U16:
|
|
prop_xml_write_text_u16_tuple(dest, src, 4);
|
|
|
|
break;
|
|
|
|
case PROP_BOOL:
|
|
prop_xml_write_text_bool(dest, src);
|
|
|
|
break;
|
|
|
|
default:
|
|
log_write("Unsupported type: %#x", type);
|
|
|
|
abort();
|
|
}
|
|
}
|
|
|
|
static void prop_xml_write_text_s8(struct strbuf *dest,
|
|
struct const_iobuf *src) {
|
|
uint8_t value;
|
|
int r;
|
|
|
|
assert(dest != NULL);
|
|
assert(src != NULL);
|
|
|
|
r = iobuf_read_8(src, &value);
|
|
|
|
assert(r >= 0);
|
|
|
|
strbuf_printf(dest, "%i", (int8_t)value);
|
|
}
|
|
|
|
static void prop_xml_write_text_s16(struct strbuf *dest,
|
|
struct const_iobuf *src) {
|
|
uint16_t value;
|
|
int r;
|
|
|
|
assert(dest != NULL);
|
|
assert(src != NULL);
|
|
|
|
r = iobuf_read_be16(src, &value);
|
|
|
|
assert(r >= 0);
|
|
|
|
strbuf_printf(dest, "%i", (int16_t)value);
|
|
}
|
|
|
|
static void prop_xml_write_text_s32(struct strbuf *dest,
|
|
struct const_iobuf *src) {
|
|
uint32_t value;
|
|
int r;
|
|
|
|
assert(dest != NULL);
|
|
assert(src != NULL);
|
|
|
|
r = iobuf_read_be32(src, &value);
|
|
|
|
assert(r >= 0);
|
|
|
|
strbuf_printf(dest, "%i", (int32_t)value);
|
|
}
|
|
|
|
static void prop_xml_write_text_s64(struct strbuf *dest,
|
|
struct const_iobuf *src) {
|
|
uint64_t value;
|
|
int r;
|
|
|
|
assert(dest != NULL);
|
|
assert(src != NULL);
|
|
|
|
r = iobuf_read_be64(src, &value);
|
|
|
|
assert(r >= 0);
|
|
|
|
strbuf_printf(dest, "%lli", (long long)value);
|
|
}
|
|
|
|
static void prop_xml_write_text_u8(struct strbuf *dest,
|
|
struct const_iobuf *src) {
|
|
uint8_t value;
|
|
int r;
|
|
|
|
assert(dest != NULL);
|
|
assert(src != NULL);
|
|
|
|
r = iobuf_read_8(src, &value);
|
|
|
|
assert(r >= 0);
|
|
|
|
strbuf_printf(dest, "%i", value);
|
|
}
|
|
|
|
static void prop_xml_write_text_u16(struct strbuf *dest,
|
|
struct const_iobuf *src) {
|
|
uint16_t value;
|
|
int r;
|
|
|
|
assert(dest != NULL);
|
|
assert(src != NULL);
|
|
|
|
r = iobuf_read_be16(src, &value);
|
|
|
|
assert(r >= 0);
|
|
|
|
strbuf_printf(dest, "%u", value);
|
|
}
|
|
|
|
static void prop_xml_write_text_u32(struct strbuf *dest,
|
|
struct const_iobuf *src) {
|
|
uint32_t value;
|
|
int r;
|
|
|
|
assert(dest != NULL);
|
|
assert(src != NULL);
|
|
|
|
r = iobuf_read_be32(src, &value);
|
|
|
|
assert(r >= 0);
|
|
|
|
strbuf_printf(dest, "%u", value);
|
|
}
|
|
|
|
static void prop_xml_write_text_u64(struct strbuf *dest,
|
|
struct const_iobuf *src) {
|
|
uint64_t value;
|
|
int r;
|
|
|
|
assert(dest != NULL);
|
|
assert(src != NULL);
|
|
|
|
r = iobuf_read_be64(src, &value);
|
|
|
|
assert(r >= 0);
|
|
|
|
strbuf_printf(dest, "%llu", (unsigned long long)value);
|
|
}
|
|
|
|
static void prop_xml_write_text_bin(struct strbuf *dest,
|
|
struct const_iobuf *src) {
|
|
uint8_t byte;
|
|
uint8_t nibble;
|
|
uint8_t nibbles[2];
|
|
size_t i;
|
|
size_t j;
|
|
char c;
|
|
|
|
assert(dest != NULL);
|
|
assert(src != NULL);
|
|
|
|
for (i = src->pos; i < src->nbytes; i++) {
|
|
byte = src->bytes[i];
|
|
nibbles[0] = byte >> 4;
|
|
nibbles[1] = byte & 0x0f;
|
|
|
|
for (j = 0; j < 2; j++) {
|
|
nibble = nibbles[j];
|
|
|
|
if (nibble < 10) {
|
|
c = '0' + nibble;
|
|
} else {
|
|
c = 'a' + (nibble - 10);
|
|
}
|
|
|
|
strbuf_putc(dest, c);
|
|
}
|
|
}
|
|
|
|
src->pos = i;
|
|
}
|
|
|
|
static void prop_xml_write_text_str(struct strbuf *dest,
|
|
struct const_iobuf *src,
|
|
enum prop_xml_escape ctx) {
|
|
const char *chars;
|
|
size_t nchars;
|
|
|
|
assert(dest != NULL);
|
|
assert(src != NULL);
|
|
assert(src->pos <= src->nbytes);
|
|
|
|
chars = (const char *)&src->bytes[src->pos];
|
|
nchars = src->nbytes - src->pos;
|
|
|
|
assert(chars[nchars - 1] == '\0');
|
|
|
|
prop_xml_write_escaped_string(dest, chars, ctx);
|
|
src->pos += nchars;
|
|
}
|
|
|
|
static void prop_xml_write_text_ip4(struct strbuf *dest,
|
|
struct const_iobuf *src) {
|
|
const uint8_t *b;
|
|
|
|
assert(dest != NULL);
|
|
assert(src != NULL);
|
|
assert(src->pos + 4 <= src->nbytes);
|
|
|
|
b = src->bytes + src->pos;
|
|
strbuf_printf(dest, "%i.%i.%i.%i", b[0], b[1], b[2], b[3]);
|
|
}
|
|
|
|
static void prop_xml_write_text_bool(struct strbuf *dest,
|
|
struct const_iobuf *src) {
|
|
assert(dest != NULL);
|
|
assert(src != NULL);
|
|
assert(src->pos < src->nbytes);
|
|
|
|
if (src->bytes[src->pos]) {
|
|
strbuf_putc(dest, '1');
|
|
} else {
|
|
strbuf_putc(dest, '0');
|
|
}
|
|
}
|
|
|
|
/* I don't know how tuples are actually serialized to XML, the real format
|
|
might be different. */
|
|
|
|
static void prop_xml_write_text_s32_tuple(struct strbuf *dest,
|
|
struct const_iobuf *src,
|
|
size_t count) {
|
|
uint32_t value;
|
|
size_t i;
|
|
int r;
|
|
|
|
assert(dest != NULL);
|
|
assert(src != NULL);
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (i > 0) {
|
|
strbuf_putc(dest, ',');
|
|
}
|
|
|
|
r = iobuf_read_be32(src, &value);
|
|
|
|
assert(r >= 0);
|
|
|
|
strbuf_printf(dest, "%i", (int32_t)value);
|
|
}
|
|
}
|
|
|
|
static void prop_xml_write_text_u16_tuple(struct strbuf *dest,
|
|
struct const_iobuf *src,
|
|
size_t count) {
|
|
uint16_t value;
|
|
size_t i;
|
|
int r;
|
|
|
|
assert(dest != NULL);
|
|
assert(src != NULL);
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (i > 0) {
|
|
strbuf_putc(dest, ',');
|
|
}
|
|
|
|
r = iobuf_read_be16(src, &value);
|
|
|
|
assert(r >= 0);
|
|
|
|
strbuf_printf(dest, "%u", value);
|
|
}
|
|
}
|