📄 ezxml.c
字号:
return &root->xml;
}
// a wrapper for ezxml_parse_fd that accepts a file name
ezxml_t ezxml_parse_file(const char *file)
{
int fd = open(file, O_RDONLY, 0);
ezxml_t xml = ezxml_parse_fd(fd);
if (fd >= 0) close(fd);
return xml;
}
#endif // CYGPKG_IO_FILEIO
// Encodes ampersand sequences appending the results to *dst, reallocating *dst
// if length excedes max. a is non-zero for attribute encoding. Returns *dst
char *ezxml_ampencode(const char *s, size_t len, char **dst, size_t *dlen,
size_t *max, short a)
{
const char *e;
for (e = s + len; s != e; s++) {
while (*dlen + 10 > *max) *dst = realloc(*dst, *max += EZXML_BUFSIZE);
switch (*s) {
case '\0': return *dst;
case '&': *dlen += sprintf(*dst + *dlen, "&"); break;
case '<': *dlen += sprintf(*dst + *dlen, "<"); break;
case '>': *dlen += sprintf(*dst + *dlen, ">"); break;
case '"': *dlen += sprintf(*dst + *dlen, (a) ? """ : "\""); break;
case '\n': *dlen += sprintf(*dst + *dlen, (a) ? "
" : "\n"); break;
case '\t': *dlen += sprintf(*dst + *dlen, (a) ? "	" : "\t"); break;
case '\r': *dlen += sprintf(*dst + *dlen, "
"); break;
default: (*dst)[(*dlen)++] = *s;
}
}
return *dst;
}
// Recursively converts each tag to xml appending it to *s. Reallocates *s if
// its length excedes max. start is the location of the previous tag in the
// parent tag's character content. Returns *s.
char *ezxml_toxml_r(ezxml_t xml, char **s, size_t *len, size_t *max,
size_t start, char ***attr)
{
int i, j;
char *txt = (xml->parent) ? xml->parent->txt : "";
size_t off = 0;
// parent character content up to this tag
*s = ezxml_ampencode(txt + start, xml->off - start, s, len, max, 0);
while (*len + strlen(xml->name) + 4 > *max) // reallocate s
*s = realloc(*s, *max += EZXML_BUFSIZE);
*len += sprintf(*s + *len, "<%s", xml->name); // open tag
for (i = 0; xml->attr[i]; i += 2) { // tag attributes
if (ezxml_attr(xml, xml->attr[i]) != xml->attr[i + 1]) continue;
while (*len + strlen(xml->attr[i]) + 7 > *max) // reallocate s
*s = realloc(*s, *max += EZXML_BUFSIZE);
*len += sprintf(*s + *len, " %s=\"", xml->attr[i]);
ezxml_ampencode(xml->attr[i + 1], -1, s, len, max, 1);
*len += sprintf(*s + *len, "\"");
}
for (i = 0; attr[i] && strcmp(attr[i][0], xml->name); i++);
for (j = 1; attr[i] && attr[i][j]; j += 3) { // default attributes
if (! attr[i][j + 1] || ezxml_attr(xml, attr[i][j]) != attr[i][j + 1])
continue; // skip duplicates and non-values
while (*len + strlen(attr[i][j]) + 7 > *max) // reallocate s
*s = realloc(*s, *max += EZXML_BUFSIZE);
*len += sprintf(*s + *len, " %s=\"", attr[i][j]);
ezxml_ampencode(attr[i][j + 1], -1, s, len, max, 1);
*len += sprintf(*s + *len, "\"");
}
*len += sprintf(*s + *len, ">");
*s = (xml->child) ? ezxml_toxml_r(xml->child, s, len, max, 0, attr) //child
: ezxml_ampencode(xml->txt, -1, s, len, max, 0); //data
while (*len + strlen(xml->name) + 4 > *max) // reallocate s
*s = realloc(*s, *max += EZXML_BUFSIZE);
*len += sprintf(*s + *len, "</%s>", xml->name); // close tag
while (txt[off] && off < xml->off) off++; // make sure off is within bounds
return (xml->ordered) ? ezxml_toxml_r(xml->ordered, s, len, max, off, attr)
: ezxml_ampencode(txt + off, -1, s, len, max, 0);
}
// converts an ezxml structure back to xml, returning it as a string that must
// be freed
char *ezxml_toxml(ezxml_t xml)
{
ezxml_t p = (xml) ? xml->parent : NULL, o = (xml) ? xml->ordered : NULL;
ezxml_root_t root = (ezxml_root_t)xml;
size_t len = 0, max = EZXML_BUFSIZE;
char *s = strcpy(malloc(max), ""), *t, *n;
int i, j, k;
if (! xml || ! xml->name) return realloc(s, len + 1);
while (root->xml.parent) root = (ezxml_root_t)root->xml.parent; // root tag
for (i = 0; ! p && root->pi[i]; i++) { // pre-root processing instructions
for (k = 2; root->pi[i][k - 1]; k++);
for (j = 1; (n = root->pi[i][j]); j++) {
if (root->pi[i][k][j - 1] == '>') continue; // not pre-root
while (len + strlen(t = root->pi[i][0]) + strlen(n) + 7 > max)
s = realloc(s, max += EZXML_BUFSIZE);
len += sprintf(s + len, "<?%s%s%s?>\n", t, *n ? " " : "", n);
}
}
xml->parent = xml->ordered = NULL;
s = ezxml_toxml_r(xml, &s, &len, &max, 0, root->attr);
xml->parent = p;
xml->ordered = o;
for (i = 0; ! p && root->pi[i]; i++) { // post-root processing instructions
for (k = 2; root->pi[i][k - 1]; k++);
for (j = 1; (n = root->pi[i][j]); j++) {
if (root->pi[i][k][j - 1] == '<') continue; // not post-root
while (len + strlen(t = root->pi[i][0]) + strlen(n) + 7 > max)
s = realloc(s, max += EZXML_BUFSIZE);
len += sprintf(s + len, "\n<?%s%s%s?>", t, *n ? " " : "", n);
}
}
return realloc(s, len + 1);
}
// free the memory allocated for the ezxml structure
void ezxml_free(ezxml_t xml)
{
ezxml_root_t root = (ezxml_root_t)xml;
int i, j;
char **a, *s;
if (! xml) return;
ezxml_free(xml->child);
ezxml_free(xml->ordered);
if (! xml->parent) { // free root tag allocations
for (i = 10; root->ent[i]; i += 2) // 0 - 9 are default entites (<>&"')
if ((s = root->ent[i + 1]) < root->s || s > root->e) free(s);
free(root->ent); // free list of general entities
for (i = 0; (a = root->attr[i]); i++) {
for (j = 1; a[j++]; j += 2) // free malloced attribute values
if (a[j] && (a[j] < root->s || a[j] > root->e)) free(a[j]);
free(a);
}
if (root->attr[0]) free(root->attr); // free default attribute list
for (i = 0; root->pi[i]; i++) {
for (j = 1; root->pi[i][j]; j++);
free(root->pi[i][j + 1]);
free(root->pi[i]);
}
if (root->pi[0]) free(root->pi); // free processing instructions
if (root->len == -1) free(root->m); // malloced xml data
#ifndef EZXML_NOMMAP
else if (root->len) munmap(root->m, root->len); // mem mapped xml data
#endif // EZXML_NOMMAP
if (root->u) free(root->u); // utf8 conversion
}
ezxml_free_attr(xml->attr); // tag attributes
if ((xml->flags & EZXML_TXTM)) free(xml->txt); // character content
if ((xml->flags & EZXML_NAMEM)) free(xml->name); // tag name
free(xml);
}
// return parser error message or empty string if none
const char *ezxml_error(ezxml_t xml)
{
while (xml && xml->parent) xml = xml->parent; // find root tag
return (xml) ? ((ezxml_root_t)xml)->err : "";
}
// returns a new empty ezxml structure with the given root tag name
ezxml_t ezxml_new(const char *name)
{
static char *ent[] = { "lt;", "<", "gt;", ">", "quot;", """,
"apos;", "'", "amp;", "&", NULL };
ezxml_root_t root = (ezxml_root_t)memset(malloc(sizeof(struct ezxml_root)),
'\0', sizeof(struct ezxml_root));
root->xml.name = (char *)name;
root->cur = &root->xml;
strcpy(root->err, root->xml.txt = "");
root->ent = memcpy(malloc(sizeof(ent)), ent, sizeof(ent));
root->attr = root->pi = (char ***)(root->xml.attr = EZXML_NIL);
return &root->xml;
}
// Adds a child tag. off is the offset of the child tag relative to the start
// of the parent tag's character content. returns the child tag
ezxml_t ezxml_add_child(ezxml_t xml, const char *name, size_t off)
{
ezxml_t cur, head, child;
if (! xml) return NULL;
child = (ezxml_t)memset(malloc(sizeof(struct ezxml)), '\0',
sizeof(struct ezxml));
child->name = (char *)name;
child->attr = EZXML_NIL;
child->off = off;
child->parent = xml;
child->txt = "";
if ((head = xml->child)) { // already have sub tags
if (head->off <= off) { // not first subtag
for (cur = head; cur->ordered && cur->ordered->off <= off;
cur = cur->ordered);
child->ordered = cur->ordered;
cur->ordered = child;
}
else { // first subtag
child->ordered = head;
xml->child = child;
}
for (cur = head; cur->sibling && strcmp(cur->name, name);
cur = cur->sibling); // find tag type
if (! strcmp(cur->name, name) && cur->off <= off) { //not first of type
while (cur->next && cur->next->off <= off) cur = cur->next;
child->next = cur->next;
cur->next = child;
}
else { // first tag of this type
if (cur->off > off) child->next = cur; // not only tag of this type
for (cur = head; cur->sibling && cur->sibling->off <= off;
cur = cur->sibling);
child->sibling = cur->sibling;
cur->sibling = child;
}
}
else xml->child = child; // only sub tag
return child;
}
// sets the character content for the given tag and returns the tag
ezxml_t ezxml_set_txt(ezxml_t xml, const char *txt)
{
if (! xml) return NULL;
if (xml->flags & EZXML_TXTM) free(xml->txt); // existing txt was malloced
xml->flags &= ~EZXML_TXTM;
xml->txt = (char *)txt;
return xml;
}
// Sets the given tag attribute or adds a new attribute if not found. A value
// of NULL will remove the specified attribute.
void ezxml_set_attr(ezxml_t xml, const char *name, const char *value)
{
int l = 0, c;
if (! xml) return;
while (xml->attr[l] && strcmp(xml->attr[l], name)) l += 2;
if (! xml->attr[l]) { // not found, add as new attribute
if (! value) return; // nothing to do
if (xml->attr == EZXML_NIL) { // first attribute
xml->attr = malloc(4 * sizeof(char *));
xml->attr[1] = strdup(""); // empty list of malloced names/vals
}
else xml->attr = realloc(xml->attr, (l + 4) * sizeof(char *));
xml->attr[l] = (char *)name; // set attribute name
xml->attr[l + 2] = NULL; // null terminate attribute list
xml->attr[l + 3] = realloc(xml->attr[l + 1],
(c = strlen(xml->attr[l + 1])) + 2);
strcpy(xml->attr[l + 3] + c, " "); // set name/value as not malloced
if (xml->flags & EZXML_DUP) xml->attr[l + 3][c] = EZXML_NAMEM;
}
else if (xml->flags & EZXML_DUP) free((char *)name); // name was strduped
for (c = l; xml->attr[c]; c += 2); // find end of attribute list
if (xml->attr[c + 1][l / 2] & EZXML_TXTM) free(xml->attr[l + 1]); //old val
if (xml->flags & EZXML_DUP) xml->attr[c + 1][l / 2] |= EZXML_TXTM;
else xml->attr[c + 1][l / 2] &= ~EZXML_TXTM;
if (value) xml->attr[l + 1] = (char *)value; // set attribute value
else { // remove attribute
if (xml->attr[c + 1][l / 2] & EZXML_NAMEM) free(xml->attr[l]);
memmove(xml->attr + l, xml->attr + l + 2, (c - l + 2) * sizeof(char*));
xml->attr = realloc(xml->attr, (c + 2) * sizeof(char *));
memmove(xml->attr[c + 1] + (l / 2), xml->attr[c + 1] + (l / 2) + 1,
(c / 2) - (l / 2)); // fix list of which name/vals are malloced
}
xml->flags &= ~EZXML_DUP; // clear strdup() flag
}
// sets a flag for the given tag and returns the tag
ezxml_t ezxml_set_flag(ezxml_t xml, short flag)
{
if (xml) xml->flags |= flag;
return xml;
}
// removes a tag along with all its subtags
void ezxml_remove(ezxml_t xml)
{
ezxml_t cur;
if (! xml) return; // nothing to do
if (xml->next) xml->next->sibling = xml->sibling; // patch sibling list
if (xml->parent) { // not root tag
cur = xml->parent->child; // find head of subtag list
if (cur == xml) xml->parent->child = xml->ordered; // first subtag
else { // not first subtag
while (cur->ordered != xml) cur = cur->ordered;
cur->ordered = cur->ordered->ordered; // patch ordered list
cur = xml->parent->child; // go back to head of subtag list
if (strcmp(cur->name, xml->name)) { // not in first sibling list
while (strcmp(cur->sibling->name, xml->name))
cur = cur->sibling;
if (cur->sibling == xml) { // first of a sibling list
cur->sibling = (xml->next) ? xml->next
: cur->sibling->sibling;
}
else cur = cur->sibling; // not first of a sibling list
}
while (cur->next && cur->next != xml) cur = cur->next;
if (cur->next) cur->next = cur->next->next; // patch next list
}
}
xml->ordered = NULL; // prevent ezxml_free() from clobbering ordered list
ezxml_free(xml);
}
#ifdef EZXML_TEST // test harness
int main(int argc, char **argv)
{
ezxml_t xml;
char *s;
int i;
if (argc != 2) return fprintf(stderr, "usage: %s xmlfile\n", argv[0]);
xml = ezxml_parse_file(argv[1]);
printf("%s\n", (s = ezxml_toxml(xml)));
free(s);
i = fprintf(stderr, "%s", ezxml_error(xml));
ezxml_free(xml);
return (i) ? 1 : 0;
}
#endif // EZXML_TEST
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -