Subversion Repositories Kolibri OS

Rev

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

  1. /*
  2.  * Copyright 2005 Richard Wilson <info@tinct.net>
  3.  *
  4.  * This file is part of NetSurf, http://www.netsurf-browser.org/
  5.  *
  6.  * NetSurf is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License as published by
  8.  * the Free Software Foundation; version 2 of the License.
  9.  *
  10.  * NetSurf is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17.  */
  18.  
  19. /** \file
  20.  * HTML lists (implementation).
  21.  */
  22. #include <assert.h>
  23. #include <ctype.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <strings.h>
  27. #include "css/css.h"
  28. #include "render/list.h"
  29. #include "utils/log.h"
  30.  
  31.  
  32. struct list_counter {
  33.         char *name;                             /** Counter name */
  34.         struct list_counter_state *first;       /** First counter state */
  35.         struct list_counter_state *state;       /** Current counter state */
  36.         struct list_counter *next;              /** Next counter */
  37. };
  38.  
  39. struct list_counter_state {
  40.         int count;                              /** Current count */
  41.         struct list_counter_state *parent;      /** Parent counter, or NULL */
  42.         struct list_counter_state *next;        /** Next counter, or NULL */
  43. };
  44.  
  45. static struct list_counter *list_counter_pool = NULL;
  46. static char list_counter_workspace[16];
  47.  
  48. static const char *list_counter_roman[] = { "I", "IV", "V", "IX",
  49.                                             "X", "XL", "L", "XC",
  50.                                             "C", "CD", "D", "CM",
  51.                                             "M"};
  52. static const int list_counter_decimal[] = {    1,   4,   5,   9,
  53.                                               10,  40,  50,  90,
  54.                                              100, 400, 500, 900,
  55.                                              1000};
  56. #define ROMAN_DECIMAL_CONVERSIONS (sizeof(list_counter_decimal) \
  57.                 / sizeof(list_counter_decimal[0]))
  58.  
  59.  
  60. static struct list_counter *render_list_find_counter(const char *name);
  61. static char *render_list_encode_counter(struct list_counter_state *state,
  62.                 enum css_list_style_type_e style);
  63. static char *render_list_encode_roman(int value);
  64.  
  65. /*
  66. static void render_list_counter_output(char *name);
  67. */
  68.  
  69. /**
  70.  * Finds a counter from the current pool, or adds a new one.
  71.  *
  72.  * \param name  the name of the counter to find
  73.  * \return the counter, or NULL if it couldn't be found/created.
  74.  */
  75. static struct list_counter *render_list_find_counter(const char *name) {
  76.         struct list_counter *counter;
  77.  
  78.         assert(name);
  79.         /* find a current counter */
  80.         for (counter = list_counter_pool; counter; counter = counter->next)
  81.                 if (!strcasecmp(name, counter->name))
  82.                         return counter;
  83.  
  84.         /* create a new counter */
  85.         counter = calloc(1, sizeof(struct list_counter));
  86.         if (!counter) {
  87.                 LOG(("No memory for calloc()"));
  88.                 return NULL;
  89.         }
  90.         counter->name = malloc(strlen(name) + 1);
  91.         if (!counter->name) {
  92.                 LOG(("No memory for malloc()"));
  93.                 free(counter);
  94.                 return NULL;
  95.         }
  96.         strcpy(counter->name, name);
  97.         counter->next = list_counter_pool;
  98.         list_counter_pool = counter;
  99.         return counter;
  100. }
  101.  
  102.  
  103. /**
  104.  * Removes all counters from the current pool.
  105.  */
  106. void render_list_destroy_counters(void) {
  107.         struct list_counter *counter = list_counter_pool;
  108.         struct list_counter *next_counter;
  109.         struct list_counter_state *state;
  110.         struct list_counter_state *next_state;
  111.  
  112.         while (counter) {
  113.                 next_counter = counter->next;
  114.                 free(counter->name);
  115.                 state = counter->first;
  116.                 free(counter);
  117.                 counter = next_counter;
  118.                 while (state) {
  119.                         next_state = state->next;
  120.                         free(state);
  121.                         state = next_state;
  122.                 }
  123.         }
  124.         list_counter_pool = NULL;
  125. }
  126.  
  127.  
  128. /**
  129.  * Resets a counter in accordance with counter-reset (CSS 2.1/12.4).
  130.  *
  131.  * \param name   the name of the counter to reset
  132.  * \param value  the value to reset the counter to
  133.  * \return true on success, false on failure.
  134.  */
  135. bool render_list_counter_reset(const char *name, int value) {
  136.         struct list_counter *counter;
  137.         struct list_counter_state *state;
  138.         struct list_counter_state *link;
  139.  
  140.         assert(name);
  141.         counter = render_list_find_counter(name);
  142.         if (!counter)
  143.                 return false;
  144.         state = calloc(1, sizeof(struct list_counter_state));
  145.         if (!state) {
  146.                 LOG(("No memory for calloc()"));
  147.                 return false;
  148.         }
  149.         state->count = value;
  150.         state->parent = counter->state;
  151.         counter->state = state;
  152.         if (!counter->first) {
  153.                 counter->first = state;
  154.         } else {
  155.                 for (link = counter->first; link->next; link = link->next);
  156.                 link->next = state;
  157.         }
  158. /*      render_list_counter_output(name);
  159. */      return true;
  160. }
  161.  
  162.  
  163. /**
  164.  * Increments a counter in accordance with counter-increment (CSS 2.1/12.4).
  165.  *
  166.  * \param name   the name of the counter to reset
  167.  * \param value  the value to increment the counter by
  168.  * \return true on success, false on failure.
  169.  */
  170. bool render_list_counter_increment(const char *name, int value) {
  171.         struct list_counter *counter;
  172.  
  173.         assert(name);
  174.         counter = render_list_find_counter(name);
  175.         if (!counter)
  176.                 return false;
  177.         /* if no counter-reset used, it is assumed the counter has been reset
  178.          * to 0 by the root element. */
  179.         if (!counter->state) {
  180.                 if (counter->first) {
  181.                         counter->state = counter->first;
  182.                         counter->state->count = 0;
  183.                 } else {
  184.                         render_list_counter_reset(name, 0);
  185.                 }
  186.         }
  187.         if (counter->state)
  188.                 counter->state->count += value;
  189. /*      render_list_counter_output(name);
  190. */      return counter->state != NULL;
  191. }
  192.  
  193.  
  194. /**
  195.  * Ends the scope of a counter.
  196.  *
  197.  * \param name   the name of the counter to end the scope for
  198.  * \return true on success, false on failure.
  199.  */
  200. bool render_list_counter_end_scope(const char *name) {
  201.         struct list_counter *counter;
  202.  
  203.         assert(name);
  204.         counter = render_list_find_counter(name);
  205.         if ((!counter) || (!counter->state))
  206.                 return false;
  207.         counter->state = counter->state->parent;
  208. /*      render_list_counter_output(name);
  209. */      return true;
  210. }
  211.  
  212.  
  213. /**
  214.  * Returns a textual representation of a counter for counter() or counters()
  215.  * (CSS 2.1/12.2).
  216.  *
  217.  * \param css_counter  the counter to convert
  218.  * \return a textual representation of the counter, or NULL on failure
  219.  */
  220. char *render_list_counter(const css_computed_content_item *css_counter) {
  221.         struct list_counter *counter;
  222.         struct list_counter_state *state;
  223.         char *compound = NULL;
  224.         char *merge, *extend;
  225.         lwc_string *name = NULL, *sep = NULL;
  226.         uint8_t style;
  227.  
  228.         assert(css_counter);
  229.  
  230.         if (css_counter->type == CSS_COMPUTED_CONTENT_COUNTER) {
  231.                 name = css_counter->data.counter.name;
  232.                 style = css_counter->data.counter.style;
  233.         } else {
  234.                 assert(css_counter->type == CSS_COMPUTED_CONTENT_COUNTERS);
  235.  
  236.                 name = css_counter->data.counters.name;
  237.                 sep = css_counter->data.counters.sep;
  238.                 style = css_counter->data.counters.style;
  239.         }
  240.  
  241.         counter = render_list_find_counter(lwc_string_data(name));
  242.         if (!counter) {
  243.                 LOG(("Failed to find/create counter for conversion"));
  244.                 return NULL;
  245.         }
  246.  
  247.         /* handle counter() first */
  248.         if (sep == NULL)
  249.                 return render_list_encode_counter(counter->state, style);
  250.  
  251.         /* loop through all states for counters() */
  252.         for (state = counter->first; state; state = state->next) {
  253.                 merge = render_list_encode_counter(state, style);
  254.                 if (!merge) {
  255.                         free(compound);
  256.                         return NULL;
  257.                 }
  258.                 if (!compound) {
  259.                         compound = merge;
  260.                 } else {
  261.                         extend = realloc(compound, strlen(compound) +
  262.                                         strlen(merge) + 1);
  263.                         if (!extend) {
  264.                                 LOG(("No memory for realloc()"));
  265.                                 free(compound);
  266.                                 free(merge);
  267.                                 return NULL;
  268.                         }
  269.                         compound = extend;
  270.                         strcat(compound, merge);
  271.                 }
  272.                 if (state->next) {
  273.                         merge = realloc(compound, strlen(compound) +
  274.                                         lwc_string_length(sep) + 1);
  275.                         if (!merge) {
  276.                                 LOG(("No memory for realloc()"));
  277.                                 free(compound);
  278.                                 return NULL;
  279.                         }
  280.                         compound = merge;
  281.                         strcat(compound, lwc_string_data(sep));
  282.                 }
  283.         }
  284.         return compound;
  285. }
  286.  
  287.  
  288. /**
  289.  * Returns a textual representation of a counter state in a specified style.
  290.  *
  291.  * \param state  the counter state to represent
  292.  * \param style  the counter style to use
  293.  * \return a textual representation of the counter state, or NULL on failure
  294.  */
  295. static char *render_list_encode_counter(struct list_counter_state *state,
  296.                 enum css_list_style_type_e style) {
  297.         char *result = NULL;
  298.         int i;
  299.  
  300.         /* no counter state means that the counter is currently out of scope */
  301.         if (!state) {
  302.                 result = malloc(1);
  303.                 if (!result)
  304.                         return NULL;
  305.                 result[0] = '\0';
  306.                 return result;
  307.         }
  308.  
  309.         /* perform the relevant encoding to upper case where applicable */
  310.         switch (style) {
  311.                 case CSS_LIST_STYLE_TYPE_LOWER_ALPHA:
  312.                 case CSS_LIST_STYLE_TYPE_UPPER_ALPHA:
  313.                         if (state->count <= 0)
  314.                                 result = calloc(1, 1);
  315.                         else
  316.                                 result = malloc(2);
  317.                         if (!result)
  318.                                 return NULL;
  319.                         if (state->count > 0) {
  320.                                 result[0] = 'A' + (state->count - 1) % 26;
  321.                                 result[1] = '\0';
  322.                         }
  323.                         break;
  324.                 case CSS_LIST_STYLE_TYPE_DISC:
  325.                 case CSS_LIST_STYLE_TYPE_CIRCLE:
  326.                 case CSS_LIST_STYLE_TYPE_SQUARE:
  327.                         result = malloc(2);
  328.                         if (!result)
  329.                                 return NULL;
  330.                         result[0] = '?';
  331.                         result[1] = '\0';
  332.                         break;
  333.                 case CSS_LIST_STYLE_TYPE_LOWER_ROMAN:
  334.                 case CSS_LIST_STYLE_TYPE_UPPER_ROMAN:
  335.                         result = render_list_encode_roman(state->count);
  336.                         if (!result)
  337.                                 return NULL;
  338.                         break;
  339.                 case CSS_LIST_STYLE_TYPE_DECIMAL:
  340.                         snprintf(list_counter_workspace,
  341.                                         sizeof list_counter_workspace,
  342.                                         "%i", state->count);
  343.                         result = malloc(strlen(list_counter_workspace) + 1);
  344.                         if (!result)
  345.                                 return NULL;
  346.                         strcpy(result, list_counter_workspace);
  347.                         break;
  348.                 case CSS_LIST_STYLE_TYPE_NONE:
  349.                         result = malloc(1);
  350.                         if (!result)
  351.                                 return NULL;
  352.                         result[0] = '\0';
  353.                         break;
  354.                 default:
  355.                         break;
  356.         }
  357.  
  358.         /* perform case conversion */
  359.         if ((style == CSS_LIST_STYLE_TYPE_LOWER_ALPHA) ||
  360.                         (style == CSS_LIST_STYLE_TYPE_LOWER_ROMAN))
  361.                 for (i = 0; result[i]; i++)
  362.                         result[i] = tolower(result[i]);
  363.  
  364.         return result;
  365. }
  366.  
  367.  
  368. /**
  369.  * Encodes a value in roman numerals.
  370.  * For values that cannot be represented (ie <=0) an empty string is returned.
  371.  *
  372.  * \param value  the value to represent
  373.  * \return a string containing the representation, or NULL on failure
  374.  */
  375. static char *render_list_encode_roman(int value) {
  376.         int i, overflow, p = 0;
  377.         char temp[10];
  378.         char *result;
  379.  
  380.         /* zero and below is returned as an empty string and not erred as
  381.          * if it is counters() will fail to complete other scopes. */
  382.         if (value <= 0) {
  383.                 result = malloc(1);
  384.                 if (!result)
  385.                         return NULL;
  386.                 result[0] = '\0';
  387.                 return result;
  388.         }
  389.  
  390.         /* we only calculate 1->999 and then add a M for each thousand */
  391.         overflow = value / 1000;
  392.         value = value % 1000;
  393.  
  394.         /* work backwards through the array */
  395.         for (i = ROMAN_DECIMAL_CONVERSIONS - 1; i >= 0; i--) {
  396.                 while (value >= list_counter_decimal[0]) {
  397.                         if (value - list_counter_decimal[i] <
  398.                                         list_counter_decimal[0] - 1)
  399.                                 break;
  400.                         value -= list_counter_decimal[i];
  401.                         temp[p++] = list_counter_roman[i][0];
  402.                         if (i & 0x1)
  403.                                 temp[p++] = list_counter_roman[i][1];
  404.                 }
  405.         }
  406.         temp[p] = '\0';
  407.  
  408.         /* create a copy for the caller including thousands */
  409.         result = malloc(p + overflow + 1);
  410.         if (!result)
  411.                 return NULL;
  412.         for (i = 0; i < overflow; i++)
  413.                 result[i] = 'M';
  414.         strcpy(&result[overflow], temp);
  415.         return result;
  416. }
  417.  
  418.  
  419.  
  420.  
  421.  
  422.  
  423.  
  424.  
  425. void render_list_test(void) {
  426.         /* example given in CSS2.1/12.4.1 */
  427. /*      render_list_counter_reset("item", 0);
  428.         render_list_counter_increment("item", 1);
  429.         render_list_counter_increment("item", 1);
  430.         render_list_counter_reset("item", 0);
  431.         render_list_counter_increment("item", 1);
  432.         render_list_counter_increment("item", 1);
  433.         render_list_counter_increment("item", 1);
  434.         render_list_counter_reset("item", 0);
  435.         render_list_counter_increment("item", 1);
  436.         render_list_counter_end_scope("item");
  437.         render_list_counter_reset("item", 0);
  438.         render_list_counter_increment("item", 1);
  439.         render_list_counter_end_scope("item");
  440.         render_list_counter_increment("item", 1);
  441.         render_list_counter_end_scope("item");
  442.         render_list_counter_increment("item", 1);
  443.         render_list_counter_increment("item", 1);
  444.         render_list_counter_end_scope("item");
  445.         render_list_counter_reset("item", 0);
  446.         render_list_counter_increment("item", 1);
  447.         render_list_counter_increment("item", 1);
  448.         render_list_counter_end_scope("item");
  449. */
  450. }
  451. /*
  452. static void render_list_counter_output(char *name) {
  453.         struct list_counter *counter;
  454.         char *result;
  455.         struct css_counter css_counter;
  456.  
  457.         assert(name);
  458.         counter = render_list_find_counter(name);
  459.         if (!counter) {
  460.                 fprintf(stderr, "Unable to create counter '%s'\n", name);
  461.                 return;
  462.         }
  463.  
  464.         css_counter.name = name;
  465.         css_counter.style = CSS_LIST_STYLE_TYPE_LOWER_ALPHA;
  466.         css_counter.separator = NULL;
  467.         result = render_list_counter(&css_counter);
  468.         if (!result) {
  469.                 fprintf(stderr, "Failed to output counter('%s')\n", name);
  470.         } else {
  471.                 fprintf(stderr, "counter('%s') is '%s'\n", name, result);
  472.                 free(result);
  473.         }
  474.         css_counter.separator = ".";
  475.         result = render_list_counter(&css_counter);
  476.         if (!result) {
  477.                 fprintf(stderr, "Failed to output counters('%s', '.')\n", name);
  478.         } else {
  479.                 fprintf(stderr, "counters('%s', '.') is '%s'\n", name, result);
  480.                 free(result);
  481.         }
  482. }
  483. */
  484.