/* Determine whether the node is a textual node. Textual nodes are never
* subject to formatting or indenting.
*/
static bool is_text(const xmlNodePtr node)
{
return
node->type == XML_TEXT_NODE ||
node->type == XML_CDATA_SECTION_NODE;
}
/* The blank text node children in an element are considered removable only if
* ALL the text node children of that element are blank (no mixed content), or
* there are no text node children at all.
*
* If there is only a single blank text node child, it is considered removable
* only if FORMAT_REMWSONLY is set.
*/
static bool blanks_are_removable(xmlNodePtr node, int opts)
{
xmlNodePtr cur;
int i;
if (xmlNodeGetSpacePreserve(node) == 1) {
return false;
}
for (cur = node->children, i = 0; cur; cur = cur->next, ++i) {
if (is_text(cur) && !xmlIsBlankNode(cur)) {
return false;
}
}
if (xmlIsBlankNode(cur)) {
xmlUnlinkNode(cur);
xmlFreeNode(cur);
}
cur = next;
}
}
/* Add indentation to nodes within non-blank nodes. */
static void indent(xmlNodePtr node)
{
xmlNodePtr cur;
int n = 0, i;
for (cur = node->parent; cur; cur = cur->parent) {
++n;
}
if (!node->prev) {
xmlAddPrevSibling(node, xmlNewText(BAD_CAST "\n"));
for (i = 1; i < n; ++i) {
xmlAddPrevSibling(node, xmlNewText(BAD_CAST xmlTreeIndentString));
}
}
for (i = node->next ? 1 : 2; i < n; ++i) {
xmlAddNextSibling(node, xmlNewText(BAD_CAST xmlTreeIndentString));
}
xmlAddNextSibling(node, xmlNewText(BAD_CAST "\n"));
}
/* Format XML nodes. */
static void format(xmlNodePtr node, int opts)
{
bool remblanks;
if ((remblanks = blanks_are_removable(node, opts))) {
remove_blanks(node);
}
if (remblanks || optset(opts, FORMAT_INDENT)) {
xmlNodePtr cur = node->children;