Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Tree construction tester.
  3.  */
  4.  
  5. #define _GNU_SOURCE
  6.  
  7. #include <inttypes.h>
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11.  
  12. #include <hubbub/hubbub.h>
  13. #include <hubbub/parser.h>
  14. #include <hubbub/tree.h>
  15.  
  16. #include "utils/utils.h"
  17.  
  18. #include "testutils.h"
  19.  
  20. typedef struct attr_t attr_t;
  21. typedef struct node_t node_t;
  22. typedef struct buf_t buf_t;
  23.  
  24. struct attr_t {
  25.         hubbub_ns ns;
  26.         char *name;
  27.         char *value;
  28. };
  29.  
  30. struct node_t {
  31.         enum { DOCTYPE, COMMENT, ELEMENT, CHARACTER } type;
  32.  
  33.         union {
  34.                 struct {
  35.                         char *name;
  36.                         char *public_id;
  37.                         char *system_id;
  38.                 } doctype;
  39.  
  40.                 struct {
  41.                         hubbub_ns ns;
  42.                         char *name;
  43.                         attr_t *attrs;
  44.                         size_t n_attrs;
  45.                 } element;
  46.  
  47.                 char *content;          /**< For comments, characters **/
  48.         } data;
  49.  
  50.         node_t *next;
  51.         node_t *prev;
  52.  
  53.         node_t *child;
  54.         node_t *parent;
  55.  
  56.         uint32_t refcnt;
  57. };
  58.  
  59. struct buf_t {
  60.         char *buf;
  61.         size_t len;
  62.         size_t pos;
  63. };
  64.  
  65.  
  66. #define NUM_NAMESPACES 7
  67. const char * const ns_names[NUM_NAMESPACES] =
  68.                 { NULL, NULL /*html*/, "math", "svg", "xlink", "xml", "xmlns" };
  69.  
  70.  
  71. node_t *Document;
  72.  
  73.  
  74.  
  75. static void node_print(buf_t *buf, node_t *node, unsigned depth);
  76.  
  77. static hubbub_error create_comment(void *ctx, const hubbub_string *data, void **result);
  78. static hubbub_error create_doctype(void *ctx, const hubbub_doctype *doctype,
  79.                 void **result);
  80. static hubbub_error create_element(void *ctx, const hubbub_tag *tag, void **result);
  81. static hubbub_error create_text(void *ctx, const hubbub_string *data, void **result);
  82. static hubbub_error ref_node(void *ctx, void *node);
  83. static hubbub_error unref_node(void *ctx, void *node);
  84. static hubbub_error append_child(void *ctx, void *parent, void *child, void **result);
  85. static hubbub_error insert_before(void *ctx, void *parent, void *child, void *ref_child,
  86.                 void **result);
  87. static hubbub_error remove_child(void *ctx, void *parent, void *child, void **result);
  88. static hubbub_error clone_node(void *ctx, void *node, bool deep, void **result);
  89. static hubbub_error reparent_children(void *ctx, void *node, void *new_parent);
  90. static hubbub_error get_parent(void *ctx, void *node, bool element_only, void **result);
  91. static hubbub_error has_children(void *ctx, void *node, bool *result);
  92. static hubbub_error form_associate(void *ctx, void *form, void *node);
  93. static hubbub_error add_attributes(void *ctx, void *node,
  94.                 const hubbub_attribute *attributes, uint32_t n_attributes);
  95. static hubbub_error set_quirks_mode(void *ctx, hubbub_quirks_mode mode);
  96. static hubbub_error complete_script(void *ctx, void *script);
  97.  
  98. static void delete_node(node_t *node);
  99. static void delete_attr(attr_t *attr);
  100.  
  101. static hubbub_tree_handler tree_handler = {
  102.         create_comment,
  103.         create_doctype,
  104.         create_element,
  105.         create_text,
  106.         ref_node,
  107.         unref_node,
  108.         append_child,
  109.         insert_before,
  110.         remove_child,
  111.         clone_node,
  112.         reparent_children,
  113.         get_parent,
  114.         has_children,
  115.         form_associate,
  116.         add_attributes,
  117.         set_quirks_mode,
  118.         NULL,
  119.         complete_script,
  120.         NULL
  121. };
  122.  
  123. static void *myrealloc(void *ptr, size_t len, void *pw)
  124. {
  125.         void *ret;
  126.  
  127.         UNUSED(pw);
  128.  
  129.         /* A half-arsed attempt at filling freshly allocated space with junk. */
  130.         if (ptr == NULL) {
  131.                 ret = malloc(len);
  132.                 if (ret != NULL)
  133.                         memset(ret, 0xdf, len);
  134.         } else {
  135.                 ret = realloc(ptr, len);
  136.         }
  137.  
  138.         return ret;
  139. }
  140.  
  141.  
  142. /*
  143.  * Create, initialise, and return, a parser instance.
  144.  */
  145. static hubbub_parser *setup_parser(void)
  146. {
  147.         hubbub_parser *parser;
  148.         hubbub_parser_optparams params;
  149.  
  150.         assert(hubbub_parser_create("UTF-8", false, myrealloc, NULL, &parser) ==
  151.                         HUBBUB_OK);
  152.  
  153.         params.tree_handler = &tree_handler;
  154.         assert(hubbub_parser_setopt(parser, HUBBUB_PARSER_TREE_HANDLER,
  155.                         &params) == HUBBUB_OK);
  156.  
  157.         params.document_node = (void *)1;
  158.         assert(hubbub_parser_setopt(parser, HUBBUB_PARSER_DOCUMENT_NODE,
  159.                         &params) == HUBBUB_OK);
  160.  
  161.         params.enable_scripting = true;
  162.         assert(hubbub_parser_setopt(parser, HUBBUB_PARSER_ENABLE_SCRIPTING,
  163.                         &params) == HUBBUB_OK);
  164.  
  165.         return parser;
  166. }
  167.  
  168.  
  169.  
  170. /*** Buffer handling bits ***/
  171. static void buf_clear(buf_t *buf)
  172. {
  173.         if (!buf || !buf->buf) return;
  174.  
  175.         buf->buf[0] = '\0';
  176.         buf->pos = 0;
  177. }
  178.  
  179. static void buf_add(buf_t *buf, const char *str)
  180. {
  181.         size_t len = strlen(str) + 1;
  182.  
  183.         if (!buf) {
  184.                 printf("%s", str);
  185.                 return;
  186.         }
  187.  
  188.         if (buf->buf == NULL) {
  189.                 buf->len = ((len + 1024) / 1024) * 1024;
  190.                 buf->buf = calloc(1, buf->len);
  191.         }
  192.  
  193.         while (buf->pos + len > buf->len) {
  194.                 buf->len *= 2;
  195.                 buf->buf = realloc(buf->buf, buf->len);
  196.         }
  197.  
  198.         strcat(buf->buf, str);
  199.         buf->pos += len;
  200. }
  201.  
  202.  
  203.  
  204. /* States for reading in data from the tree construction file */
  205. enum reading_state {
  206.         ERASE_DATA,
  207.         EXPECT_DATA,
  208.         READING_DATA,
  209.         READING_DATA_AFTER_FIRST,
  210.         READING_ERRORS,
  211.         READING_TREE
  212. };
  213.  
  214. int main(int argc, char **argv)
  215. {
  216.         FILE *fp;
  217.         char line[2048];
  218.  
  219.         bool reprocess = false;
  220.         bool passed = true;
  221.  
  222.         hubbub_parser *parser = NULL;
  223.         enum reading_state state = EXPECT_DATA;
  224.  
  225.         buf_t expected = { NULL, 0, 0 };
  226.         buf_t got = { NULL, 0, 0 };
  227.  
  228.  
  229.         if (argc != 2) {
  230.                 printf("Usage: %s <filename>\n", argv[0]);
  231.                 return 1;
  232.         }
  233.  
  234.         fp = fopen(argv[1], "rb");
  235.         if (fp == NULL) {
  236.                 printf("Failed opening %s\n", argv[1]);
  237.                 return 1;
  238.         }
  239.  
  240.         /* We rely on lines not being anywhere near 2048 characters... */
  241.         while (reprocess || (passed && fgets(line, sizeof line, fp) == line)) {
  242.                 reprocess = false;
  243.  
  244.                 switch (state)
  245.                 {
  246.                 case ERASE_DATA:
  247.                         buf_clear(&got);
  248.                         buf_clear(&expected);
  249.  
  250.                         hubbub_parser_destroy(parser);
  251.                         while (Document) {
  252.                                 node_t *victim = Document;
  253.                                 Document = victim->next;
  254.                                 delete_node(victim);
  255.                         }
  256.                         Document = NULL;
  257.  
  258.                         state = EXPECT_DATA;
  259.  
  260.                 case EXPECT_DATA:
  261.                         if (strcmp(line, "#data\n") == 0) {
  262.                                 parser = setup_parser();
  263.                                 state = READING_DATA;
  264.                         }
  265.                         break;
  266.  
  267.                 case READING_DATA:
  268.                 case READING_DATA_AFTER_FIRST:
  269.                         if (strcmp(line, "#errors\n") == 0) {
  270.                                 assert(hubbub_parser_completed(parser) == HUBBUB_OK);
  271.                                 state = READING_ERRORS;
  272.                         } else {
  273.                                 size_t len = strlen(line);
  274.  
  275.                                 if (state == READING_DATA_AFTER_FIRST) {
  276.                                         assert(hubbub_parser_parse_chunk(parser,
  277.                                                 (uint8_t *)"\n",
  278.                                                 1) == HUBBUB_OK);
  279.                                 } else {
  280.                                         state = READING_DATA_AFTER_FIRST;
  281.                                 }
  282.  
  283.                                 printf(": %s", line);
  284.                                 assert(hubbub_parser_parse_chunk(parser, (uint8_t *)line,
  285.                                                 len - 1) == HUBBUB_OK);
  286.                         }
  287.                         break;
  288.  
  289.  
  290.                 case READING_ERRORS:
  291.                         if (strcmp(line, "#document-fragment\n") == 0) {
  292.                                 state = ERASE_DATA;
  293.                                 reprocess = true;
  294.                         }
  295.  
  296.                         if (strcmp(line, "#document\n") == 0)
  297.                                 state = READING_TREE;
  298.                         else {
  299.                         }
  300.                         break;
  301.  
  302.                 case READING_TREE:
  303.                         if (strcmp(line, "#data\n") == 0) {
  304.                                 node_print(&got, Document, 0);
  305.  
  306.                                 /* Trim off the last newline */
  307.                                 expected.buf[strlen(expected.buf) - 1] = '\0';
  308.  
  309.                                 passed = !strcmp(got.buf, expected.buf);
  310.                                 if (!passed) {
  311.                                         printf("expected:\n");
  312.                                         printf("%s", expected.buf);
  313.                                         printf("got:\n");
  314.                                         printf("%s", got.buf);
  315.                                 }
  316.  
  317.                                 state = ERASE_DATA;
  318.                                 reprocess = true;
  319.                         } else {
  320.                                 buf_add(&expected, line);
  321.                         }
  322.                         break;
  323.                 }
  324.         }
  325.  
  326.         if (Document != NULL) {
  327.                 node_print(&got, Document, 0);
  328.  
  329.                 passed = !strcmp(got.buf, expected.buf);
  330.                 if (!passed) {
  331.                         printf("expected:\n");
  332.                         printf("%s", expected.buf);
  333.                         printf("got:\n");
  334.                         printf("%s", got.buf);
  335.                 }
  336.  
  337.                 hubbub_parser_destroy(parser);
  338.                 while (Document) {
  339.                         node_t *victim = Document;
  340.                         Document = victim->next;
  341.                         delete_node(victim);
  342.                 }
  343.         }
  344.  
  345.         printf("%s\n", passed ? "PASS" : "FAIL");
  346.  
  347.         fclose(fp);
  348.  
  349.         free(got.buf);
  350.         free(expected.buf);
  351.  
  352.         return 0;
  353. }
  354.  
  355.  
  356. /*** Tree construction functions ***/
  357.  
  358. hubbub_error create_comment(void *ctx, const hubbub_string *data, void **result)
  359. {
  360.         node_t *node = calloc(1, sizeof *node);
  361.  
  362.         UNUSED(ctx);
  363.  
  364.         node->type = COMMENT;
  365.         node->data.content = strndup((const char *) data->ptr, data->len);
  366.         node->refcnt = 1;
  367.         node->refcnt = 1;
  368.  
  369.         *result = node;
  370.  
  371.         return HUBBUB_OK;
  372. }
  373.  
  374. hubbub_error create_doctype(void *ctx, const hubbub_doctype *doctype,
  375.                 void **result)
  376. {
  377.         node_t *node = calloc(1, sizeof *node);
  378.  
  379.         UNUSED(ctx);
  380.  
  381.         node->type = DOCTYPE;
  382.         node->data.doctype.name = strndup(
  383.                         (const char *) doctype->name.ptr,
  384.                         doctype->name.len);
  385.  
  386.         if (!doctype->public_missing) {
  387.                 node->data.doctype.public_id = strndup(
  388.                                 (const char *) doctype->public_id.ptr,
  389.                                 doctype->public_id.len);
  390.         }
  391.  
  392.         if (!doctype->system_missing) {
  393.                 node->data.doctype.system_id = strndup(
  394.                                 (const char *) doctype->system_id.ptr,
  395.                                 doctype->system_id.len);
  396.         }
  397.         node->refcnt = 1;
  398.  
  399.         *result = node;
  400.  
  401.         return HUBBUB_OK;
  402. }
  403.  
  404. hubbub_error create_element(void *ctx, const hubbub_tag *tag, void **result)
  405. {
  406.         size_t i;
  407.         node_t *node = calloc(1, sizeof *node);
  408.  
  409.         UNUSED(ctx);
  410.  
  411.         assert(tag->ns < NUM_NAMESPACES);
  412.  
  413.         node->type = ELEMENT;
  414.         node->data.element.ns = tag->ns;
  415.         node->data.element.name = strndup(
  416.                         (const char *) tag->name.ptr,
  417.                         tag->name.len);
  418.         node->data.element.n_attrs = tag->n_attributes;
  419.  
  420.         node->data.element.attrs = calloc(node->data.element.n_attrs,
  421.                         sizeof *node->data.element.attrs);
  422.  
  423.         for (i = 0; i < tag->n_attributes; i++) {
  424.                 attr_t *attr = &node->data.element.attrs[i];
  425.  
  426.                 assert(tag->attributes[i].ns < NUM_NAMESPACES);
  427.  
  428.                 attr->ns = tag->attributes[i].ns;
  429.  
  430.                 attr->name = strndup(
  431.                                 (const char *) tag->attributes[i].name.ptr,
  432.                                 tag->attributes[i].name.len);
  433.  
  434.                 attr->value = strndup(
  435.                                 (const char *) tag->attributes[i].value.ptr,
  436.                                 tag->attributes[i].value.len);
  437.         }
  438.         node->refcnt = 1;
  439.  
  440.         *result = node;
  441.  
  442.         return HUBBUB_OK;
  443. }
  444.  
  445. hubbub_error create_text(void *ctx, const hubbub_string *data, void **result)
  446. {
  447.         node_t *node = calloc(1, sizeof *node);
  448.  
  449.         UNUSED(ctx);
  450.  
  451.         node->type = CHARACTER;
  452.         node->data.content = strndup((const char *) data->ptr, data->len);
  453.         node->refcnt = 1;
  454.         node->refcnt = 1;
  455.  
  456.         *result = node;
  457.  
  458.         return HUBBUB_OK;
  459. }
  460.  
  461. hubbub_error ref_node(void *ctx, void *node)
  462. {
  463.         node_t *n = node;
  464.  
  465.         UNUSED(ctx);
  466.  
  467.         if (node != (void *) 1)
  468.                 n->refcnt++;
  469.  
  470.         return HUBBUB_OK;
  471. }
  472.  
  473. hubbub_error unref_node(void *ctx, void *node)
  474. {
  475.         node_t *n = node;
  476.  
  477.         UNUSED(ctx);
  478.  
  479.         if (n != (void *) 1) {
  480.                 assert(n->refcnt > 0);
  481.  
  482.                 n->refcnt--;
  483.  
  484.                 printf("Unreferencing node %p (%d) [%d : %s]\n", node,
  485.                                 n->refcnt, n->type,
  486.                                 n->type == ELEMENT ? n->data.element.name : "");
  487.  
  488.                 if (n->refcnt == 0 && n->parent == NULL) {
  489.                         delete_node(n);
  490.                 }
  491.         }
  492.  
  493.         return HUBBUB_OK;
  494. }
  495.  
  496. hubbub_error append_child(void *ctx, void *parent, void *child, void **result)
  497. {
  498.         node_t *tparent = parent;
  499.         node_t *tchild = child;
  500.         node_t *insert = NULL;
  501.  
  502.         tchild->next = tchild->prev = NULL;
  503.  
  504. #ifndef NDEBUG
  505.         printf("appending (%p):\n", (void *) tchild);
  506.         node_print(NULL, tchild, 0);
  507.         printf("to:\n");
  508.         if (parent != (void *)1)
  509.                 node_print(NULL, tparent, 0);
  510. #endif
  511.  
  512.         *result = child;
  513.  
  514.         if (parent == (void *)1) {
  515.                 if (Document) {
  516.                         insert = Document;
  517.                 } else {
  518.                         Document = tchild;
  519.                 }
  520.         } else {
  521.                 if (tparent->child == NULL) {
  522.                         tparent->child = tchild;
  523.                 } else {
  524.                         insert = tparent->child;
  525.                 }
  526.         }
  527.  
  528.         if (insert) {
  529.                 while (insert->next != NULL) {
  530.                         insert = insert->next;
  531.                 }
  532.  
  533.                 if (tchild->type == CHARACTER && insert->type == CHARACTER) {
  534.                         insert->data.content = realloc(insert->data.content,
  535.                                         strlen(insert->data.content) +
  536.                                         strlen(tchild->data.content) + 1);
  537.                         strcat(insert->data.content, tchild->data.content);
  538.                         *result = insert;
  539.                 } else {
  540.                         insert->next = tchild;
  541.                         tchild->prev = insert;
  542.                 }
  543.         }
  544.  
  545.         if (*result == child)
  546.                 tchild->parent = tparent;
  547.  
  548.         ref_node(ctx, *result);
  549.  
  550.         return HUBBUB_OK;
  551. }
  552.  
  553. /* insert 'child' before 'ref_child', under 'parent' */
  554. hubbub_error insert_before(void *ctx, void *parent, void *child,
  555.                 void *ref_child, void **result)
  556. {
  557.         node_t *tparent = parent;
  558.         node_t *tchild = child;
  559.         node_t *tref = ref_child;
  560.  
  561. #ifndef NDEBUG
  562.         printf("inserting (%p):\n", (void *) tchild);
  563.         node_print(NULL, tchild, 0);
  564.         printf("before:\n");
  565.         node_print(NULL, tref, 0);
  566.         printf("under:\n");
  567.         if (parent != (void *)1)
  568.                 node_print(NULL, tparent, 0);
  569. #endif
  570.  
  571.         if (tchild->type == CHARACTER && tref->prev &&
  572.                         tref->prev->type == CHARACTER) {
  573.                 node_t *insert = tref->prev;
  574.  
  575.                 insert->data.content = realloc(insert->data.content,
  576.                                 strlen(insert->data.content) +
  577.                                 strlen(tchild->data.content) + 1);
  578.                 strcat(insert->data.content, tchild->data.content);
  579.  
  580.                 *result = insert;
  581.         } else {
  582.                 tchild->parent = parent;
  583.  
  584.                 tchild->prev = tref->prev;
  585.                 tchild->next = tref;
  586.                 tref->prev = tchild;
  587.  
  588.                 if (tchild->prev)
  589.                         tchild->prev->next = tchild;
  590.                 else
  591.                         tparent->child = tchild;
  592.  
  593.                 *result = child;
  594.         }
  595.  
  596.         ref_node(ctx, *result);
  597.  
  598.         return HUBBUB_OK;
  599. }
  600.  
  601. hubbub_error remove_child(void *ctx, void *parent, void *child, void **result)
  602. {
  603.         node_t *tparent = parent;
  604.         node_t *tchild = child;
  605.  
  606.         assert(tparent->child);
  607.         assert(tchild->parent == tparent);
  608.  
  609.         printf("Removing child %p\n", child);
  610.  
  611.         if (tchild->parent->child == tchild) {
  612.                 tchild->parent->child = tchild->next;
  613.         }
  614.  
  615.         if (tchild->prev)
  616.                 tchild->prev->next = tchild->next;
  617.  
  618.         if (tchild->next)
  619.                 tchild->next->prev = tchild->prev;
  620.  
  621.         /* now reset all the child's pointers */
  622.         tchild->next = tchild->prev = tchild->parent = NULL;
  623.  
  624.         *result = child;
  625.  
  626.         ref_node(ctx, *result);
  627.  
  628.         return HUBBUB_OK;
  629. }
  630.  
  631. hubbub_error clone_node(void *ctx, void *node, bool deep, void **result)
  632. {
  633.         node_t *old_node = node;
  634.         node_t *new_node = calloc(1, sizeof *new_node);
  635.         node_t *last;
  636.         node_t *child;
  637.         size_t i;
  638.  
  639.         new_node->type = old_node->type;
  640.  
  641.         switch (old_node->type) {
  642.         case DOCTYPE:
  643.                 new_node->data.doctype.name =
  644.                                 strdup(old_node->data.doctype.name);
  645.                 if (old_node->data.doctype.public_id)
  646.                         new_node->data.doctype.public_id =
  647.                                 strdup(old_node->data.doctype.public_id);
  648.                 if (old_node->data.doctype.system_id)
  649.                         new_node->data.doctype.system_id =
  650.                                 strdup(old_node->data.doctype.system_id);
  651.                 break;
  652.         case COMMENT:
  653.         case CHARACTER:
  654.                 new_node->data.content = strdup(old_node->data.content);
  655.                 break;
  656.         case ELEMENT:
  657.                 new_node->data.element.ns = old_node->data.element.ns;
  658.                 new_node->data.element.name =
  659.                                 strdup(old_node->data.element.name);
  660.                 new_node->data.element.attrs =
  661.                                 calloc(old_node->data.element.n_attrs,
  662.                                         sizeof *new_node->data.element.attrs);
  663.                 for (i = 0; i < old_node->data.element.n_attrs; i++) {
  664.                         attr_t *attr = &new_node->data.element.attrs[i];
  665.  
  666.                         attr->ns = old_node->data.element.attrs[i].ns;
  667.                         attr->name =
  668.                                 strdup(old_node->data.element.attrs[i].name);
  669.                         attr->value =
  670.                                 strdup(old_node->data.element.attrs[i].value);
  671.                 }
  672.                 new_node->data.element.n_attrs = old_node->data.element.n_attrs;
  673.                 break;
  674.         }
  675.  
  676.         *result = new_node;
  677.  
  678.         new_node->child = new_node->parent =
  679.                         new_node->next = new_node->prev =
  680.                         NULL;
  681.  
  682.         new_node->refcnt = 1;
  683.  
  684.         if (deep == false)
  685.                 return 0;
  686.  
  687.         last = NULL;
  688.  
  689.         for (child = old_node->child; child != NULL; child = child->next) {
  690.                 node_t *n;
  691.  
  692.                 clone_node(ctx, child, true, (void **) (void *) &n);
  693.  
  694.                 n->refcnt = 0;
  695.  
  696.                 if (last == NULL) {
  697.                         new_node->child = n;
  698.                 } else {
  699.                         last->next = n;
  700.                         n->prev = last;
  701.                 }
  702.  
  703.                 n->parent = new_node;
  704.                 last = n;
  705.         }
  706.  
  707.         return HUBBUB_OK;
  708. }
  709.  
  710. /* Take all of the child nodes of "node" and append them to "new_parent" */
  711. hubbub_error reparent_children(void *ctx, void *node, void *new_parent)
  712. {
  713.         node_t *parent = new_parent;
  714.         node_t *old_parent = node;
  715.  
  716.         node_t *insert;
  717.         node_t *kids;
  718.  
  719.         UNUSED(ctx);
  720.  
  721.         kids = old_parent->child;
  722.         if (!kids) return 0;
  723.  
  724.         old_parent->child = NULL;
  725.  
  726.         insert = parent->child;
  727.         if (!insert) {
  728.                 parent->child = kids;
  729.         } else {
  730.                 while (insert->next != NULL) {
  731.                         insert = insert->next;
  732.                 }
  733.  
  734.                 insert->next = kids;
  735.                 kids->prev = insert;
  736.         }
  737.  
  738.         while (kids) {
  739.                 kids->parent = parent;
  740.                 kids = kids->next;
  741.         }
  742.  
  743.         return HUBBUB_OK;
  744. }
  745.  
  746. hubbub_error get_parent(void *ctx, void *node, bool element_only, void **result)
  747. {
  748.         UNUSED(element_only);
  749.  
  750.         *result = ((node_t *)node)->parent;
  751.  
  752.         if (*result != NULL)
  753.                 ref_node(ctx, *result);
  754.  
  755.         return HUBBUB_OK;
  756. }
  757.  
  758. hubbub_error has_children(void *ctx, void *node, bool *result)
  759. {
  760.         UNUSED(ctx);
  761.  
  762.         *result = ((node_t *)node)->child ? true : false;
  763.  
  764.         return HUBBUB_OK;
  765. }
  766.  
  767. hubbub_error form_associate(void *ctx, void *form, void *node)
  768. {
  769.         UNUSED(ctx);
  770.         UNUSED(form);
  771.         UNUSED(node);
  772.  
  773.         return HUBBUB_OK;
  774. }
  775.  
  776. hubbub_error add_attributes(void *ctx, void *vnode,
  777.                 const hubbub_attribute *attributes, uint32_t n_attributes)
  778. {
  779.         node_t *node = vnode;
  780.         size_t old_elems = node->data.element.n_attrs;
  781.         size_t i;
  782.  
  783.         UNUSED(ctx);
  784.  
  785.         node->data.element.n_attrs += n_attributes;
  786.  
  787.         node->data.element.attrs = realloc(node->data.element.attrs,
  788.                         node->data.element.n_attrs *
  789.                                 sizeof *node->data.element.attrs);
  790.  
  791.         for (i = 0; i < n_attributes; i++) {
  792.                 attr_t *attr = &node->data.element.attrs[old_elems + i];
  793.  
  794.                 assert(attributes[i].ns < NUM_NAMESPACES);
  795.  
  796.                 attr->ns = attributes[i].ns;
  797.  
  798.                 attr->name = strndup(
  799.                                 (const char *) attributes[i].name.ptr,
  800.                                 attributes[i].name.len);
  801.  
  802.                 attr->value = strndup(
  803.                                 (const char *) attributes[i].value.ptr,
  804.                                 attributes[i].value.len);
  805.         }
  806.  
  807.  
  808.         return HUBBUB_OK;
  809. }
  810.  
  811. hubbub_error set_quirks_mode(void *ctx, hubbub_quirks_mode mode)
  812. {
  813.         UNUSED(ctx);
  814.         UNUSED(mode);
  815.  
  816.         return HUBBUB_OK;
  817. }
  818.  
  819. hubbub_error complete_script(void *ctx, void *script)
  820. {
  821.         UNUSED(ctx);
  822.         UNUSED(script);
  823.  
  824.         return HUBBUB_OK;
  825. }
  826.  
  827.  
  828. /*** Serialising bits ***/
  829.  
  830. static int compare_attrs(const void *a, const void *b) {
  831.         const attr_t *first = a;
  832.         const attr_t *second = b;
  833.  
  834.         return strcmp(first->name, second->name);
  835. }
  836.  
  837.  
  838.  
  839.  
  840. static void indent(buf_t *buf, unsigned depth)
  841. {
  842.         unsigned int i;
  843.  
  844.         buf_add(buf, "| ");
  845.  
  846.         for (i = 0; i < depth; i++) {
  847.                 buf_add(buf, "  ");
  848.         }
  849. }
  850.  
  851. static void print_ns(buf_t *buf, hubbub_ns ns)
  852. {
  853.         if (ns_names[ns] != NULL) {
  854.                 buf_add(buf, ns_names[ns]);
  855.                 buf_add(buf, " ");
  856.         }
  857. }
  858.  
  859. static void node_print(buf_t *buf, node_t *node, unsigned depth)
  860. {
  861.         size_t i;
  862.  
  863.         if (!node) return;
  864.  
  865.         indent(buf, depth);
  866.  
  867.         switch (node->type)
  868.         {
  869.         case DOCTYPE:
  870.                 buf_add(buf, "<!DOCTYPE ");
  871.                 buf_add(buf, node->data.doctype.name);
  872.  
  873.                 if (node->data.doctype.public_id ||
  874.                                 node->data.doctype.system_id) {
  875.                         if (node->data.doctype.public_id) {
  876.                                 buf_add(buf, " \"");
  877.                                 buf_add(buf, node->data.doctype.public_id);
  878.                                 buf_add(buf, "\" ");
  879.                         } else {
  880.                                 buf_add(buf, "\"\" ");
  881.                         }
  882.                        
  883.                         if (node->data.doctype.system_id) {
  884.                                 buf_add(buf, " \"");
  885.                                 buf_add(buf, node->data.doctype.system_id);
  886.                                 buf_add(buf, "\"");
  887.                         } else {
  888.                                 buf_add(buf, "\"\"");
  889.                         }
  890.                 }
  891.  
  892.                 buf_add(buf, ">\n");
  893.                 break;
  894.         case ELEMENT:
  895.                 buf_add(buf, "<");
  896.                 print_ns(buf, node->data.element.ns);
  897.                 buf_add(buf, node->data.element.name);
  898.                 buf_add(buf, ">\n");
  899.  
  900.                 qsort(node->data.element.attrs, node->data.element.n_attrs,
  901.                                 sizeof *node->data.element.attrs,
  902.                                 compare_attrs);
  903.  
  904.                 for (i = 0; i < node->data.element.n_attrs; i++) {
  905.                         indent(buf, depth + 1);
  906.                         print_ns(buf, node->data.element.attrs[i].ns);
  907.                         buf_add(buf, node->data.element.attrs[i].name);
  908.                         buf_add(buf, "=");
  909.                         buf_add(buf, "\"");
  910.                         buf_add(buf, node->data.element.attrs[i].value);
  911.                         buf_add(buf, "\"\n");
  912.                 }
  913.  
  914.                 break;
  915.         case CHARACTER:
  916.                 buf_add(buf, "\"");
  917.                 buf_add(buf, node->data.content);
  918.                 buf_add(buf, "\"\n");
  919.                 break;
  920.         case COMMENT:
  921.                 buf_add(buf, "<!-- ");
  922.                 buf_add(buf, node->data.content);
  923.                 buf_add(buf, " -->\n");
  924.                 break;
  925.         default:
  926.                 printf("Unexpected node type %d\n", node->type);
  927.                 assert(0);
  928.         }
  929.  
  930.         if (node->child) {
  931.                 node_print(buf, node->child, depth + 1);
  932.         }
  933.  
  934.         if (node->next) {
  935.                 node_print(buf, node->next, depth);
  936.         }
  937. }
  938.  
  939. static void delete_node(node_t *node)
  940. {
  941.         size_t i;
  942.         node_t *c, *d;
  943.  
  944.         if (node == NULL)
  945.                 return;
  946.  
  947.         if (node->refcnt != 0) {
  948.                 printf("Node %p has non-zero refcount %d\n",
  949.                                 (void *) node, node->refcnt);
  950.                 assert(0);
  951.         }
  952.  
  953.         switch (node->type) {
  954.         case DOCTYPE:
  955.                 free(node->data.doctype.name);
  956.                 free(node->data.doctype.public_id);
  957.                 free(node->data.doctype.system_id);
  958.                 break;
  959.         case COMMENT:
  960.         case CHARACTER:
  961.                 free(node->data.content);
  962.                 break;
  963.         case ELEMENT:
  964.                 free(node->data.element.name);
  965.                 for (i = 0; i < node->data.element.n_attrs; i++)
  966.                         delete_attr(&node->data.element.attrs[i]);
  967.                 free(node->data.element.attrs);
  968.                 break;
  969.         }
  970.  
  971.         for (c = node->child; c != NULL; c = d) {
  972.                 d = c->next;
  973.  
  974.                 delete_node(c);
  975.         }
  976.  
  977.         memset(node, 0xdf, sizeof(node_t));
  978.  
  979.         free(node);
  980. }
  981.  
  982. static void delete_attr(attr_t *attr)
  983. {
  984.         if (attr == NULL)
  985.                 return;
  986.  
  987.         free(attr->name);
  988.         free(attr->value);
  989.  
  990.         memset(attr, 0xdf, sizeof(attr_t));
  991. }
  992.  
  993.