Subversion Repositories Kolibri OS

Rev

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

  1. #include <ctype.h>
  2. #include <inttypes.h>
  3. #include <stdbool.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7.  
  8. #include <libcss/libcss.h>
  9.  
  10. #include "stylesheet.h"
  11. #include "utils/utils.h"
  12.  
  13. #include "testutils.h"
  14.  
  15. /** \todo at some point, we need to extend this to handle nested blocks */
  16. typedef struct exp_entry {
  17.         int type;
  18. #define MAX_RULE_NAME_LEN (128)
  19.         char name[MAX_RULE_NAME_LEN];
  20.         size_t bclen;
  21.         size_t bcused;
  22.         uint8_t *bytecode;
  23.  
  24.         size_t stlen;
  25.         size_t stused;
  26.         struct stentry {
  27.                 size_t off;
  28.                 char *string;
  29.         } *stringtab;
  30. } exp_entry;
  31.  
  32. typedef struct line_ctx {
  33.         size_t buflen;
  34.         size_t bufused;
  35.         uint8_t *buf;
  36.  
  37.         size_t explen;
  38.         size_t expused;
  39.         exp_entry *exp;
  40.  
  41.         bool indata;
  42.         bool inerrors;
  43.         bool inexp;
  44.  
  45.         bool inrule;
  46. } line_ctx;
  47.  
  48. static bool handle_line(const char *data, size_t datalen, void *pw);
  49. static void css__parse_expected(line_ctx *ctx, const char *data, size_t len);
  50. static void run_test(const uint8_t *data, size_t len,
  51.                 exp_entry *exp, size_t explen);
  52. static bool validate_rule_selector(css_rule_selector *s, exp_entry *e);
  53. static void validate_rule_charset(css_rule_charset *s, exp_entry *e,
  54.                 int testnum);
  55. static void validate_rule_import(css_rule_import *s, exp_entry *e,
  56.                 int testnum);
  57.  
  58. static void dump_selector_list(css_selector *list, char **ptr);
  59. static void dump_selector(css_selector *selector, char **ptr);
  60. static void dump_selector_detail(css_selector_detail *detail, char **ptr);
  61. static void dump_string(lwc_string *string, char **ptr);
  62.  
  63. static void *myrealloc(void *data, size_t len, void *pw)
  64. {
  65.         UNUSED(pw);
  66.        
  67.         return realloc(data, len);
  68. }
  69.  
  70. static css_error resolve_url(void *pw,
  71.                 const char *base, lwc_string *rel, lwc_string **abs)
  72. {
  73.         UNUSED(pw);
  74.         UNUSED(base);
  75.  
  76.         /* About as useless as possible */
  77.         *abs = lwc_string_ref(rel);
  78.  
  79.         return CSS_OK;
  80. }
  81.  
  82. static bool fail_because_lwc_leaked = false;
  83.  
  84. static void
  85. printing_lwc_iterator(lwc_string *str, void *pw)
  86. {
  87.         UNUSED(pw);
  88.        
  89.         printf(" DICT: %*s\n", (int)(lwc_string_length(str)), lwc_string_data(str));
  90.         fail_because_lwc_leaked = true;
  91. }
  92.  
  93. static void destroy_expected(line_ctx *ctx)
  94. {
  95.         while (ctx->expused > 0) {
  96.                 exp_entry *victim = &ctx->exp[--ctx->expused];
  97.  
  98.                 if (victim->bytecode != NULL)
  99.                         free(victim->bytecode);
  100.  
  101.                 while (victim->stused > 0) {
  102.                         free(victim->stringtab[--victim->stused].string);
  103.                 }
  104.  
  105.                 free(victim->stringtab);
  106.         }
  107. }
  108.  
  109. int main(int argc, char **argv)
  110. {
  111.         line_ctx ctx;
  112.  
  113.         if (argc != 2) {
  114.                 printf("Usage: %s <filename>\n", argv[0]);
  115.                 return 1;
  116.         }
  117.  
  118.         ctx.buflen = css__parse_filesize(argv[1]);
  119.         if (ctx.buflen == 0)
  120.                 return 1;
  121.  
  122.         ctx.buf = malloc(ctx.buflen);
  123.         if (ctx.buf == NULL) {
  124.                 printf("Failed allocating %u bytes\n",
  125.                                 (unsigned int) ctx.buflen);
  126.                 return 1;
  127.         }
  128.  
  129.         ctx.buf[0] = '\0';
  130.         ctx.bufused = 0;
  131.         ctx.explen = 0;
  132.         ctx.expused = 0;
  133.         ctx.exp = NULL;
  134.         ctx.indata = false;
  135.         ctx.inerrors = false;
  136.         ctx.inexp = false;
  137.  
  138.         assert(css__parse_testfile(argv[1], handle_line, &ctx) == true);
  139.  
  140.         /* and run final test */
  141.         if (ctx.bufused > 0)
  142.                 run_test(ctx.buf, ctx.bufused, ctx.exp, ctx.expused);
  143.  
  144.         free(ctx.buf);
  145.  
  146.         destroy_expected(&ctx);
  147.         free(ctx.exp);
  148.  
  149.         lwc_iterate_strings(printing_lwc_iterator, NULL);
  150.        
  151.         assert(fail_because_lwc_leaked == false);
  152.        
  153.         printf("PASS\n");
  154.  
  155.         return 0;
  156. }
  157.  
  158. bool handle_line(const char *data, size_t datalen, void *pw)
  159. {
  160.         line_ctx *ctx = (line_ctx *) pw;
  161.  
  162.         if (data[0] == '#') {
  163.                 if (ctx->inexp) {
  164.                         /* This marks end of testcase, so run it */
  165.  
  166.                         run_test(ctx->buf, ctx->bufused,
  167.                                         ctx->exp, ctx->expused);
  168.  
  169.                         ctx->buf[0] = '\0';
  170.                         ctx->bufused = 0;
  171.  
  172.                         destroy_expected(ctx);
  173.                 }
  174.  
  175.                 if (ctx->indata && strncasecmp(data+1, "errors", 6) == 0) {
  176.                         ctx->indata = false;
  177.                         ctx->inerrors = true;
  178.                         ctx->inexp = false;
  179.                 } else if (ctx->inerrors &&
  180.                                 strncasecmp(data+1, "expected", 8) == 0) {
  181.                         ctx->indata = false;
  182.                         ctx->inerrors = false;
  183.                         ctx->inexp = true;
  184.                         ctx->inrule = false;
  185.                 } else if (ctx->inexp && strncasecmp(data+1, "data", 4) == 0) {
  186.                         ctx->indata = true;
  187.                         ctx->inerrors = false;
  188.                         ctx->inexp = false;
  189.                 } else if (ctx->indata) {
  190.                         memcpy(ctx->buf + ctx->bufused, data, datalen);
  191.                         ctx->bufused += datalen;
  192.                 } else {
  193.                         ctx->indata = (strncasecmp(data+1, "data", 4) == 0);
  194.                         ctx->inerrors = (strncasecmp(data+1, "errors", 6) == 0);
  195.                         ctx->inexp = (strncasecmp(data+1, "expected", 8) == 0);
  196.                 }
  197.         } else {
  198.                 if (ctx->indata) {
  199.                         memcpy(ctx->buf + ctx->bufused, data, datalen);
  200.                         ctx->bufused += datalen;
  201.                 }
  202.                 if (ctx->inexp) {
  203.                         if (data[datalen - 1] == '\n')
  204.                                 datalen -= 1;
  205.  
  206.                         css__parse_expected(ctx, data, datalen);
  207.                 }
  208.         }
  209.  
  210.         return true;
  211. }
  212.  
  213. void css__parse_expected(line_ctx *ctx, const char *data, size_t len)
  214. {
  215.         /* Ignore blanks or lines that don't start with | */
  216.         if (len == 0 || data[0] != '|')
  217.                 return;
  218.  
  219.         if (ctx->inrule == false) {
  220.                 char *name;
  221.                 int type;
  222.                
  223. start_rule:
  224.                 type = strtol(data + 1, &name, 10);
  225.  
  226.                 while (isspace(*name))
  227.                         name++;
  228.  
  229.                 /* Append to list of expected rules */
  230.                 if (ctx->expused == ctx->explen) {
  231.                         size_t num = ctx->explen == 0 ? 4 : ctx->explen;
  232.  
  233.                         exp_entry *temp = realloc(ctx->exp,
  234.                                         num * 2 * sizeof(exp_entry));
  235.                         if (temp == NULL) {
  236.                                 assert(0 && "No memory for expected rules");
  237.                         }
  238.  
  239.                         ctx->exp = temp;
  240.                         ctx->explen = num * 2;
  241.                 }
  242.  
  243.                 ctx->exp[ctx->expused].type = type;
  244.                 memcpy(ctx->exp[ctx->expused].name, name,
  245.                                 min(len - (name - data), MAX_RULE_NAME_LEN));
  246.                 ctx->exp[ctx->expused].name[min(len - (name - data),
  247.                                 MAX_RULE_NAME_LEN - 1)] = '\0';
  248.                 ctx->exp[ctx->expused].bclen = 0;
  249.                 ctx->exp[ctx->expused].bcused = 0;
  250.                 ctx->exp[ctx->expused].bytecode = NULL;
  251.                 ctx->exp[ctx->expused].stlen = 0;
  252.                 ctx->exp[ctx->expused].stused = 0;
  253.                 ctx->exp[ctx->expused].stringtab =  NULL;
  254.  
  255.                 ctx->expused++;
  256.                
  257.                 ctx->inrule = true;
  258.         } else {
  259.                 char *next = (char *) data + 1;
  260.                 exp_entry *rule = &ctx->exp[ctx->expused - 1];
  261.  
  262.                 if (data[2] != ' ') {
  263.                         ctx->inrule = false;
  264.                         goto start_rule;
  265.                 }
  266.  
  267.                 while (next < data + len) {
  268.                         /* Skip whitespace */
  269.                         while (next < data + len && isspace(*next))
  270.                                 next++;
  271.  
  272.                         if (next == data + len)
  273.                                 break;
  274.  
  275.                         if (rule->bcused >= rule->bclen) {
  276.                                 size_t num = rule->bcused == 0 ? 4 :
  277.                                                 rule->bcused;
  278.  
  279.                                 uint8_t *temp = realloc(rule->bytecode,
  280.                                                 num * 2);
  281.                                 if (temp == NULL) {
  282.                                         assert(0 && "No memory for bytecode");
  283.                                 }
  284.  
  285.                                 rule->bytecode = temp;
  286.                                 rule->bclen = num * 2;
  287.                         }
  288.  
  289.                         if (*next == 'P') {
  290.                                 /* Pointer */
  291.                                 const char *str;
  292.  
  293.                                 while (next < data + len && *next != '(')
  294.                                         next++;
  295.                                 str = next + 1;
  296.                                 while (next < data + len && *next != ')')
  297.                                         next++;
  298.                                 next++;
  299.  
  300.                                 if (rule->stused >= rule->stlen) {
  301.                                         size_t num = rule->stused == 0 ? 4 :
  302.                                                         rule->stused;
  303.  
  304.                                         struct stentry *temp = realloc(
  305.                                                 rule->stringtab,
  306.                                                 num * 2 * sizeof(struct stentry));
  307.                                         if (temp == NULL) {
  308.                                                 assert(0 &&
  309.                                                 "No memory for string table");
  310.                                         }
  311.  
  312.                                         rule->stringtab = temp;
  313.                                         rule->stlen = num * 2;
  314.                                 }
  315.  
  316.                                 rule->stringtab[rule->stused].off =
  317.                                                 rule->bcused;
  318.                                 rule->stringtab[rule->stused].string =
  319.                                                 malloc(next - str);
  320.                                 assert(rule->stringtab[rule->stused].string !=
  321.                                                 NULL);
  322.                                 memcpy(rule->stringtab[rule->stused].string,
  323.                                                 str, next - str - 1);
  324.                                 rule->stringtab[rule->stused].string[
  325.                                                 next - str - 1]  = '\0';
  326.  
  327.                                 rule->bcused += sizeof(css_code_t);
  328.                                 rule->stused++;
  329.                         } else {
  330.                                 /* Assume hexnum */
  331.                                 uint32_t val = strtoul(next, &next, 16);
  332.  
  333.                                 /* Append to bytecode */
  334.                                 memcpy(rule->bytecode + rule->bcused,
  335.                                                 &val, sizeof(val));
  336.                                 rule->bcused += sizeof(val);
  337.                         }
  338.                 }
  339.         }
  340. }
  341.  
  342. static void report_fail(const uint8_t *data, size_t datalen, exp_entry *e)
  343. {
  344.         uint32_t bcoff;
  345.  
  346.         printf("    Data: %.*s\n", (int)datalen, data);
  347.  
  348.         printf("    Expected entry:\n");
  349.         printf("        entry type:%d name:%s\n", e->type, e->name);
  350.         printf("        bytecode len:%ld used:%ld\n", e->bclen, e->bcused);
  351.         printf("        bytecode ");
  352.         for (bcoff = 0; bcoff < e->bcused; bcoff++) {
  353.                 printf("%.2x ", ((uint8_t *) e->bytecode)[bcoff]);
  354.         }
  355.         printf("\n        string table len:%ld used %ld\n", e->stlen, e->stused);
  356. /*
  357.         struct stentry {
  358.                 size_t off;
  359.                 char *string;
  360.         } *stringtab;
  361. */
  362. }
  363.  
  364. void run_test(const uint8_t *data, size_t len, exp_entry *exp, size_t explen)
  365. {
  366.         css_stylesheet_params params;
  367.         css_stylesheet *sheet;
  368.         css_rule *rule;
  369.         css_error error;
  370.         size_t e;
  371.         static int testnum;
  372.         bool failed;
  373.  
  374.         params.params_version = CSS_STYLESHEET_PARAMS_VERSION_1;
  375.         params.level = CSS_LEVEL_21;
  376.         params.charset = "UTF-8";
  377.         params.url = "foo";
  378.         params.title = NULL;
  379.         params.allow_quirks = false;
  380.         params.inline_style = false;
  381.         params.resolve = resolve_url;
  382.         params.resolve_pw = NULL;
  383.         params.import = NULL;
  384.         params.import_pw = NULL;
  385.         params.color = NULL;
  386.         params.color_pw = NULL;
  387.         params.font = NULL;
  388.         params.font_pw = NULL;
  389.  
  390.         assert(css_stylesheet_create(&params, myrealloc, NULL,
  391.                         &sheet) == CSS_OK);
  392.  
  393.         error = css_stylesheet_append_data(sheet, data, len);
  394.         if (error != CSS_OK && error != CSS_NEEDDATA) {
  395.                 printf("Failed appending data: %d\n", error);
  396.                 assert(0);
  397.         }
  398.  
  399.         error = css_stylesheet_data_done(sheet);
  400.         assert(error == CSS_OK || error == CSS_IMPORTS_PENDING);
  401.  
  402.         while (error == CSS_IMPORTS_PENDING) {
  403.                 lwc_string *url;
  404.                 uint64_t media;
  405.  
  406.                 error = css_stylesheet_next_pending_import(sheet,
  407.                                 &url, &media);
  408.                 assert(error == CSS_OK || error == CSS_INVALID);
  409.  
  410.                 if (error == CSS_OK) {
  411.                         css_stylesheet *import;
  412.                         char *buf = alloca(lwc_string_length(url) + 1);
  413.  
  414.                         memcpy(buf, lwc_string_data(url),
  415.                                         lwc_string_length(url));
  416.                         buf[lwc_string_length(url)] = '\0';
  417.  
  418.                         params.url = buf;
  419.  
  420.                         assert(css_stylesheet_create(&params,
  421.                                 myrealloc, NULL, &import) == CSS_OK);
  422.  
  423.                         assert(css_stylesheet_register_import(sheet,
  424.                                 import) == CSS_OK);
  425.  
  426.                         error = CSS_IMPORTS_PENDING;
  427.                         lwc_string_unref(url);
  428.                 }
  429.         }
  430.  
  431.         e = 0;
  432.         testnum++;
  433.  
  434.         printf("Test %d: ", testnum);
  435.  
  436.         if (sheet->rule_count != explen) {
  437.                 printf("%d: Got %d rules. Expected %u\n",
  438.                                 testnum, sheet->rule_count, (int) explen);
  439.                 assert(0 && "Unexpected number of rules");
  440.         }
  441.  
  442.         for (rule = sheet->rule_list; rule != NULL; rule = rule->next, e++) {
  443.                 if (rule->type != exp[e].type) {
  444.                         printf("%d: Got type %d. Expected %d\n",
  445.                                 testnum, rule->type, exp[e].type);
  446.                         assert(0 && "Types differ");
  447.                 }
  448.  
  449.                 switch (rule->type) {
  450.                 case CSS_RULE_SELECTOR:
  451.                         failed = validate_rule_selector((css_rule_selector *) rule, &exp[e]);
  452.                         break;
  453.                 case CSS_RULE_CHARSET:
  454.                         validate_rule_charset((css_rule_charset *) rule,
  455.                                         &exp[e], testnum);
  456.                         failed = false;
  457.                         break;
  458.                 case CSS_RULE_IMPORT:
  459.                         validate_rule_import((css_rule_import *) rule,
  460.                                         &exp[e], testnum);
  461.                         failed = false;
  462.                         break;
  463.                 default:
  464.                         printf("%d: Unhandled rule type %d\n",
  465.                                 testnum, rule->type);
  466.                         failed = false;
  467.                         break;
  468.                 }
  469.  
  470.                 if (failed) {
  471.                         report_fail(data, len, &exp[e]);
  472.                         assert(0);
  473.                 }
  474.         }
  475.  
  476.         assert(e == explen);
  477.  
  478.         css_stylesheet_destroy(sheet);
  479.  
  480.         printf("PASS\n");
  481. }
  482.  
  483.  
  484. bool validate_rule_selector(css_rule_selector *s, exp_entry *e)
  485. {
  486.         char name[MAX_RULE_NAME_LEN];
  487.         char *ptr = name;
  488.         uint32_t i;
  489.  
  490.         /* Build selector string */
  491.         for (i = 0; i < s->base.items; i++) {
  492.                 dump_selector_list(s->selectors[i], &ptr);
  493.                 if (i != (uint32_t) (s->base.items - 1)) {
  494.                         memcpy(ptr, ", ", 2);
  495.                         ptr += 2;
  496.                 }
  497.         }
  498.         *ptr = '\0';
  499.  
  500.         /* Compare with expected selector */
  501.         if (strcmp(e->name, name) != 0) {
  502.                 printf("FAIL Mismatched names\n"
  503.                        "     Got name '%s'. Expected '%s'\n",
  504.                        name, e->name);
  505.                 return true;
  506.         }
  507.  
  508.         /* Now compare bytecode */
  509.         if (e->bytecode != NULL && s->style == NULL) {
  510.                 printf("FAIL No bytecode\n"
  511.                        "    Expected bytecode but none created\n");
  512.                 return true;
  513.         } else if (e->bytecode == NULL && s->style != NULL) {
  514.                 printf("FAIL Unexpected bytecode\n"
  515.                        "    No bytecode expected but some created\n");
  516.                 return true;
  517.         } else if (e->bytecode != NULL && s->style != NULL) {
  518.                 size_t i;
  519.  
  520.                 if ((s->style->used * sizeof(css_code_t)) != e->bcused) {
  521.                         printf("FAIL Bytecode lengths differ\n"
  522.                                "    Got length %ld, Expected %u\n",
  523.                                 (s->style->used * sizeof(css_code_t)),
  524.                                 (int) e->bcused);
  525.                         return true;
  526.                 }
  527.  
  528.                 for (i = 0; i < e->bcused; i++) {
  529.                         size_t j;
  530.  
  531.                         for (j = 0; j < e->stused; j++) {
  532.                                 if (e->stringtab[j].off == i)
  533.                                         break;
  534.                         }
  535.  
  536.                         if (j != e->stused) {
  537.                                 /* String */
  538.                                 lwc_string *p;
  539.  
  540.                                 css__stylesheet_string_get(s->style->sheet, (s->style->bytecode[i / sizeof(css_code_t)]), &p);
  541.  
  542.                                 if (lwc_string_length(p) !=
  543.                                         strlen(e->stringtab[j].string) ||
  544.                                         memcmp(lwc_string_data(p),
  545.                                                 e->stringtab[j].string,
  546.                                                 lwc_string_length(p)) != 0) {
  547.                                         printf("FAIL Strings differ\n"
  548.                                                "    Got string '%.*s'. "
  549.                                                "Expected '%s'\n",
  550.                                                 (int) lwc_string_length(p),
  551.                                                 lwc_string_data(p),
  552.                                                 e->stringtab[j].string);
  553.                                         return true;
  554.                                 }
  555.  
  556.                                 i += sizeof (css_code_t) - 1;
  557.                         } else if (((uint8_t *) s->style->bytecode)[i] !=
  558.                                         e->bytecode[i]) {
  559.                                 printf("FAIL Bytecode differs\n"
  560.                                        "    Bytecode differs at %u\n    ",
  561.                                         (int) i);
  562.                                 while (i < e->bcused) {
  563.                                         printf("%.2x ",
  564.                                                 ((uint8_t *) s->style->bytecode)[i]);
  565.                                         i++;
  566.                                 }
  567.                                 printf("\n");
  568.                                 return true;
  569.                         }
  570.                 }
  571.         }
  572.         return false;
  573. }
  574.  
  575. void validate_rule_charset(css_rule_charset *s, exp_entry *e, int testnum)
  576. {
  577.         char name[MAX_RULE_NAME_LEN];
  578.         char *ptr = name;
  579.  
  580.         dump_string(s->encoding, &ptr);
  581.         *ptr = '\0';
  582.  
  583.         if (strcmp(name, e->name) != 0) {
  584.                 printf("%d: Got charset '%s'. Expected '%s'\n",
  585.                         testnum, name, e->name);
  586.                 assert(0 && "Mismatched charsets");
  587.         }
  588. }
  589.  
  590. void validate_rule_import(css_rule_import *s, exp_entry *e, int testnum)
  591. {
  592.         if (strncmp(lwc_string_data(s->url), e->name,
  593.                     lwc_string_length(s->url)) != 0) {
  594.                 printf("%d: Got URL '%.*s'. Expected '%s'\n",
  595.                         testnum, (int) lwc_string_length(s->url),
  596.                         lwc_string_data(s->url),
  597.                 e->name);
  598.                 assert(0 && "Mismatched URLs");
  599.         }
  600.  
  601.         css_stylesheet_destroy(s->sheet);
  602. }
  603.  
  604. void dump_selector_list(css_selector *list, char **ptr)
  605. {
  606.         if (list->combinator != NULL) {
  607.                 dump_selector_list(list->combinator, ptr);
  608.         }
  609.  
  610.         switch (list->data.comb) {
  611.         case CSS_COMBINATOR_NONE:
  612.                 break;
  613.         case CSS_COMBINATOR_ANCESTOR:
  614.                 (*ptr)[0] = ' ';
  615.                 *ptr += 1;
  616.                 break;
  617.         case CSS_COMBINATOR_PARENT:
  618.                 memcpy(*ptr, " > ", 3);
  619.                 *ptr += 3;
  620.                 break;
  621.         case CSS_COMBINATOR_SIBLING:
  622.                 memcpy(*ptr, " + ", 3);
  623.                 *ptr += 3;
  624.                 break;
  625.         case CSS_COMBINATOR_GENERIC_SIBLING:
  626.                 memcpy(*ptr, " ~ ", 3);
  627.                 *ptr += 3;
  628.                 break;
  629.         }
  630.  
  631.         dump_selector(list, ptr);
  632. }
  633.  
  634. void dump_selector(css_selector *selector, char **ptr)
  635. {
  636.         css_selector_detail *d = &selector->data;
  637.  
  638.         while (true) {
  639.                 dump_selector_detail(d, ptr);
  640.  
  641.                 if (d->next == 0)
  642.                         break;
  643.  
  644.                 d++;
  645.         }
  646. }
  647.  
  648. void dump_selector_detail(css_selector_detail *detail, char **ptr)
  649. {
  650.         if (detail->negate)
  651.                 *ptr += sprintf(*ptr, ":not(");
  652.  
  653.         switch (detail->type) {
  654.         case CSS_SELECTOR_ELEMENT:
  655.                 if (lwc_string_length(detail->qname.name) == 1 &&
  656.                     lwc_string_data(detail->qname.name)[0] == '*' &&
  657.                                 detail->next == 0) {
  658.                         dump_string(detail->qname.name, ptr);
  659.                 } else if (lwc_string_length(detail->qname.name) != 1 ||
  660.                            lwc_string_data(detail->qname.name)[0] != '*') {
  661.                         dump_string(detail->qname.name, ptr);
  662.                 }
  663.                 break;
  664.         case CSS_SELECTOR_CLASS:
  665.                 **ptr = '.';
  666.                 *ptr += 1;
  667.                 dump_string(detail->qname.name, ptr);
  668.                 break;
  669.         case CSS_SELECTOR_ID:
  670.                 **ptr = '#';
  671.                 *ptr += 1;
  672.                 dump_string(detail->qname.name, ptr);
  673.                 break;
  674.         case CSS_SELECTOR_PSEUDO_CLASS:
  675.         case CSS_SELECTOR_PSEUDO_ELEMENT:
  676.                 **ptr = ':';
  677.                 *ptr += 1;
  678.                 dump_string(detail->qname.name, ptr);
  679.                 if (detail->value_type == CSS_SELECTOR_DETAIL_VALUE_STRING) {
  680.                         if (detail->value.string != NULL) {
  681.                                 **ptr = '(';
  682.                                 *ptr += 1;
  683.                                 dump_string(detail->value.string, ptr);
  684.                                 **ptr = ')';
  685.                                 *ptr += 1;
  686.                         }
  687.                 } else {
  688.                         *ptr += sprintf(*ptr, "(%dn+%d)",
  689.                                         detail->value.nth.a,
  690.                                         detail->value.nth.b);
  691.                 }
  692.                 break;
  693.         case CSS_SELECTOR_ATTRIBUTE:
  694.                 **ptr = '[';
  695.                 *ptr += 1;
  696.                 dump_string(detail->qname.name, ptr);
  697.                 **ptr = ']';
  698.                 *ptr += 1;
  699.                 break;
  700.         case CSS_SELECTOR_ATTRIBUTE_EQUAL:
  701.                 **ptr = '[';
  702.                 *ptr += 1;
  703.                 dump_string(detail->qname.name, ptr);
  704.                 (*ptr)[0] = '=';
  705.                 (*ptr)[1] = '"';
  706.                 *ptr += 2;
  707.                 dump_string(detail->value.string, ptr);
  708.                 (*ptr)[0] = '"';
  709.                 (*ptr)[1] = ']';
  710.                 *ptr += 2;
  711.                 break;
  712.         case CSS_SELECTOR_ATTRIBUTE_DASHMATCH:
  713.                 **ptr = '[';
  714.                 *ptr += 1;
  715.                 dump_string(detail->qname.name, ptr);
  716.                 (*ptr)[0] = '|';
  717.                 (*ptr)[1] = '=';
  718.                 (*ptr)[2] = '"';
  719.                 *ptr += 3;
  720.                 dump_string(detail->value.string, ptr);
  721.                 (*ptr)[0] = '"';
  722.                 (*ptr)[1] = ']';
  723.                 *ptr += 2;
  724.                 break;
  725.         case CSS_SELECTOR_ATTRIBUTE_INCLUDES:
  726.                 **ptr = '[';
  727.                 *ptr += 1;
  728.                 dump_string(detail->qname.name, ptr);
  729.                 (*ptr)[0] = '~';
  730.                 (*ptr)[1] = '=';
  731.                 (*ptr)[2] = '"';
  732.                 *ptr += 3;
  733.                 dump_string(detail->value.string, ptr);
  734.                 (*ptr)[0] = '"';
  735.                 (*ptr)[1] = ']';
  736.                 *ptr += 2;
  737.                 break;
  738.         case CSS_SELECTOR_ATTRIBUTE_PREFIX:
  739.                 **ptr = '[';
  740.                 *ptr += 1;
  741.                 dump_string(detail->qname.name, ptr);
  742.                 (*ptr)[0] = '^';
  743.                 (*ptr)[1] = '=';
  744.                 (*ptr)[2] = '"';
  745.                 *ptr += 3;
  746.                 dump_string(detail->value.string, ptr);
  747.                 (*ptr)[0] = '"';
  748.                 (*ptr)[1] = ']';
  749.                 *ptr += 2;
  750.                 break;
  751.         case CSS_SELECTOR_ATTRIBUTE_SUFFIX:
  752.                 **ptr = '[';
  753.                 *ptr += 1;
  754.                 dump_string(detail->qname.name, ptr);
  755.                 (*ptr)[0] = '$';
  756.                 (*ptr)[1] = '=';
  757.                 (*ptr)[2] = '"';
  758.                 *ptr += 3;
  759.                 dump_string(detail->value.string, ptr);
  760.                 (*ptr)[0] = '"';
  761.                 (*ptr)[1] = ']';
  762.                 *ptr += 2;
  763.                 break;
  764.         case CSS_SELECTOR_ATTRIBUTE_SUBSTRING:
  765.                 **ptr = '[';
  766.                 *ptr += 1;
  767.                 dump_string(detail->qname.name, ptr);
  768.                 (*ptr)[0] = '*';
  769.                 (*ptr)[1] = '=';
  770.                 (*ptr)[2] = '"';
  771.                 *ptr += 3;
  772.                 dump_string(detail->value.string, ptr);
  773.                 (*ptr)[0] = '"';
  774.                 (*ptr)[1] = ']';
  775.                 *ptr += 2;
  776.                 break;
  777.         }
  778.  
  779.         if (detail->negate)
  780.                 *ptr += sprintf(*ptr, ")");
  781. }
  782.  
  783. void dump_string(lwc_string *string, char **ptr)
  784. {
  785.         *ptr += sprintf(*ptr, "%.*s", (int) lwc_string_length(string),
  786.                         lwc_string_data(string));
  787. }
  788.