about summary refs log tree commit diff stats
path: root/src/LYStyle.c
diff options
context:
space:
mode:
authorThomas E. Dickey <dickey@invisible-island.net>2010-05-03 00:45:10 -0400
committerThomas E. Dickey <dickey@invisible-island.net>2010-05-03 00:45:10 -0400
commit903885454167e86ce4cb967f901cbaf741f21501 (patch)
tree90a46f9f1e6c6194c8f43bbb4aa81e1e50e7e2fe /src/LYStyle.c
parentdc748b1c47baadafae2c90f0e188927b11b7e029 (diff)
downloadlynx-snapshots-903885454167e86ce4cb967f901cbaf741f21501.tar.gz
snapshot of project "lynx", label v2-8-8dev_3c
Diffstat (limited to 'src/LYStyle.c')
-rw-r--r--src/LYStyle.c797
1 files changed, 797 insertions, 0 deletions
diff --git a/src/LYStyle.c b/src/LYStyle.c
new file mode 100644
index 00000000..3378d5da
--- /dev/null
+++ b/src/LYStyle.c
@@ -0,0 +1,797 @@
+/*
+ * $LynxId: LYStyle.c,v 1.67 2010/05/02 22:23:21 tom Exp $
+ *
+ * character level styles for Lynx
+ * (c) 1996 Rob Partington -- donated to the Lyncei (if they want it :-)
+ */
+#include <HTUtils.h>
+#include <HTML.h>
+#include <LYGlobalDefs.h>
+
+#include <LYStructs.h>
+#include <LYReadCFG.h>
+#include <LYCurses.h>
+#include <LYCharUtils.h>
+#include <LYUtils.h>		/* defines TABLESIZE */
+#include <AttrList.h>
+#include <SGML.h>
+#include <HTMLDTD.h>
+
+/* Hash table definitions */
+#include <LYHash.h>
+#include <LYStyle.h>
+
+#include <LYexit.h>
+#include <LYLeaks.h>
+#include <LYStrings.h>
+#include <LYHash.h>
+
+#define CTRACE1(p) CTRACE2(TRACE_CFG || TRACE_STYLE, p)
+
+#ifdef USE_COLOR_STYLE
+
+static void style_initialiseHashTable(void);
+
+/* because curses isn't started when we parse the config file, we
+ * need to remember the STYLE: lines we encounter and parse them
+ * after curses has started
+ */
+static HTList *lss_styles = NULL;
+
+#define CACHEW 128
+#define CACHEH 64
+
+static unsigned *cached_styles_ptr = NULL;
+static int cached_styles_rows = 0;
+static int cached_styles_cols = 0;
+
+/* stack of attributes during page rendering */
+int last_styles[MAX_LAST_STYLES + 1] =
+{0};
+int last_colorattr_ptr = 0;
+
+bucket hashStyles[CSHASHSIZE];
+bucket special_bucket =
+{
+    "<special>",		/* in order something to be in trace. */
+    0, 0, 0, 0, NULL
+};
+bucket nostyle_bucket =
+{
+    "<NOSTYLE>",		/* in order something to be in trace. */
+    0, 0, 0, 0, NULL
+};
+
+int cached_tag_styles[HTML_ELEMENTS];
+int current_tag_style;
+BOOL force_current_tag_style = FALSE;
+char *forced_classname;
+BOOL force_classname;
+
+/* Remember the hash codes for common elements */
+int s_a = NOSTYLE;
+int s_aedit = NOSTYLE;
+int s_aedit_arr = NOSTYLE;
+int s_aedit_pad = NOSTYLE;
+int s_aedit_sel = NOSTYLE;
+int s_alert = NOSTYLE;
+int s_alink = NOSTYLE;
+int s_curedit = NOSTYLE;
+int s_forw_backw = NOSTYLE;
+int s_hot_paste = NOSTYLE;
+int s_menu_active = NOSTYLE;
+int s_menu_bg = NOSTYLE;
+int s_menu_entry = NOSTYLE;
+int s_menu_frame = NOSTYLE;
+int s_menu_number = NOSTYLE;
+int s_menu_sb = NOSTYLE;
+int s_normal = NOSTYLE;
+int s_prompt_edit = NOSTYLE;
+int s_prompt_edit_arr = NOSTYLE;
+int s_prompt_edit_pad = NOSTYLE;
+int s_prompt_sel = NOSTYLE;
+int s_status = NOSTYLE;
+int s_title = NOSTYLE;
+int s_whereis = NOSTYLE;
+
+#ifdef USE_SCROLLBAR
+int s_sb_aa = NOSTYLE;
+int s_sb_bar = NOSTYLE;
+int s_sb_bg = NOSTYLE;
+int s_sb_naa = NOSTYLE;
+#endif
+
+/* start somewhere safe */
+#define MAX_COLOR 16
+static int colorPairs = 0;
+
+#ifdef USE_BLINK
+#  define MAX_BLINK	2
+#  define M_BLINK	A_BLINK
+#else
+#  define MAX_BLINK	1
+#  define M_BLINK	0
+#endif
+
+#define MAX_PAIR 255		/* because our_pairs[] type is unsigned-char */
+static unsigned char our_pairs[2]
+[MAX_BLINK]
+[MAX_COLOR + 1]
+[MAX_COLOR + 1];
+
+static char *TrimLowercase(char *buffer)
+{
+    LYRemoveBlanks(buffer);
+    strtolower(buffer);
+    return buffer;
+}
+
+/*
+ * Parse a string containing a combination of video attributes and color.
+ */
+static void parse_either(const char *attrs,
+			 int dft_color,
+			 int *monop,
+			 int *colorp)
+{
+    int value;
+    char *temp_attrs = NULL;
+
+    if (StrAllocCopy(temp_attrs, attrs) != NULL) {
+	char *to_free = temp_attrs;
+
+	while (*temp_attrs != '\0') {
+	    char *next = strchr(temp_attrs, '+');
+	    char save = (char) ((next != NULL) ? *next : '\0');
+
+	    if (next == NULL)
+		next = temp_attrs + strlen(temp_attrs);
+
+	    if (save != 0)
+		*next = '\0';
+	    if ((value = string_to_attr(temp_attrs)) != 0)
+		*monop |= value;
+	    else if (colorp != 0
+		     && (value = check_color(temp_attrs, dft_color)) != ERR_COLOR)
+		*colorp = value;
+
+	    temp_attrs = next;
+	    if (save != '\0')
+		*temp_attrs++ = save;
+	}
+	FREE(to_free);
+    }
+}
+
+/* icky parsing of the style options */
+static void parse_attributes(const char *mono,
+			     const char *fg,
+			     const char *bg,
+			     int style,
+			     const char *element)
+{
+    int mA = A_NORMAL;
+    int fA = default_fg;
+    int bA = default_bg;
+    int cA = A_NORMAL;
+    int newstyle = hash_code(element);
+
+    CTRACE2(TRACE_STYLE, (tfp, "CSS(PA):style d=%d / h=%d, e=%s\n",
+			  style, newstyle, element));
+
+    parse_either(mono, ERR_COLOR, &mA, (int *) 0);
+    parse_either(bg, default_bg, &cA, &bA);
+    parse_either(fg, default_fg, &cA, &fA);
+
+    if (style == -1) {		/* default */
+	CTRACE2(TRACE_STYLE, (tfp, "CSS(DEF):default_fg=%d, default_bg=%d\n",
+			      fA, bA));
+	default_fg = fA;
+	default_bg = bA;
+	default_color_reset = TRUE;
+	return;
+    }
+    if (fA == NO_COLOR) {
+	bA = NO_COLOR;
+    } else if (COLORS) {
+#ifdef USE_BLINK
+	if (term_blink_is_boldbg) {
+	    if (fA >= COLORS)
+		cA = A_BOLD;
+	    if (bA >= COLORS)
+		cA |= M_BLINK;
+	} else
+#endif
+	if (fA >= COLORS || bA >= COLORS)
+	    cA = A_BOLD;
+	if (fA >= COLORS)
+	    fA %= COLORS;
+	if (bA >= COLORS)
+	    bA %= COLORS;
+    } else {
+	cA = A_BOLD;
+	fA = NO_COLOR;
+	bA = NO_COLOR;
+    }
+
+    /*
+     * If we have colour, and space to create a new colour attribute,
+     * and we have a valid colour description, then add this style
+     */
+    if (lynx_has_color && colorPairs < COLOR_PAIRS - 1 && fA != NO_COLOR) {
+	int curPair = 0;
+	int iFg = (1 + (fA >= 0 ? fA : 0));
+	int iBg = (1 + (bA >= 0 ? bA : 0));
+	int iBold = !!(cA & A_BOLD);
+	int iBlink = !!(cA & M_BLINK);
+
+	CTRACE2(TRACE_STYLE, (tfp, "parse_attributes %d/%d %d/%d %#x\n",
+			      fA, default_fg, bA, default_bg, cA));
+	if (fA < MAX_COLOR
+	    && bA < MAX_COLOR
+#ifdef USE_CURSES_PAIR_0
+	    && (cA != A_NORMAL || fA != default_fg || bA != default_bg)
+#endif
+	    && curPair < MAX_PAIR) {
+	    if (our_pairs[iBold][iBlink][iFg][iBg] != 0) {
+		curPair = our_pairs[iBold][iBlink][iFg][iBg];
+	    } else {
+		curPair = ++colorPairs;
+		init_pair((short) curPair, (short) fA, (short) bA);
+		our_pairs[iBold][iBlink][iFg][iBg] = UCH(curPair);
+	    }
+	}
+	CTRACE2(TRACE_STYLE, (tfp, "CSS(CURPAIR):%d\n", curPair));
+	if (style < DSTYLE_ELEMENTS)
+	    setStyle(style, COLOR_PAIR(curPair) | cA, cA, mA);
+	setHashStyle(newstyle, COLOR_PAIR(curPair) | cA, cA, mA, element);
+    } else {
+	if (lynx_has_color && fA != NO_COLOR) {
+	    CTRACE2(TRACE_STYLE,
+		    (tfp, "CSS(NC): maximum of %d colorpairs exhausted\n",
+		     COLOR_PAIRS - 1));
+	}
+	/* only mono is set */
+	if (style < DSTYLE_ELEMENTS)
+	    setStyle(style, -1, -1, mA);
+	setHashStyle(newstyle, -1, -1, mA, element);
+    }
+}
+
+/* parse a style option of the format
+ * STYLE:<OBJECT>:FG:BG
+ */
+static void parse_style(char *param)
+{
+    /* *INDENT-OFF* */
+    static struct {
+	const char *name;
+	int style;
+	int *set_hash;
+    } table[] = {
+	{ "default",		-1,			0 }, /* default fg/bg */
+	{ "alink",		DSTYLE_ALINK,		0 }, /* active link */
+	{ "a",			DSTYLE_LINK,		0 }, /* normal link */
+	{ "a",			HTML_A,			0 }, /* normal link */
+	{ "status",		DSTYLE_STATUS,		0 }, /* status bar */
+	{ "label",		DSTYLE_OPTION,		0 }, /* [INLINE]'s */
+	{ "value",		DSTYLE_VALUE,		0 }, /* [INLINE]'s */
+	{ "normal",		DSTYLE_NORMAL,		0 },
+	{ "candy",		DSTYLE_CANDY,		0 }, /* [INLINE]'s */
+	{ "whereis",		DSTYLE_WHEREIS,		&s_whereis },
+	{ "edit.active.pad",	DSTYLE_ELEMENTS,	&s_aedit_pad },
+	{ "edit.active.arrow",	DSTYLE_ELEMENTS,	&s_aedit_arr },
+	{ "edit.active.marked",	DSTYLE_ELEMENTS,	&s_aedit_sel },
+	{ "edit.active",	DSTYLE_ELEMENTS,	&s_aedit },
+	{ "edit.current",	DSTYLE_ELEMENTS,	&s_curedit },
+	{ "edit.prompt.pad",	DSTYLE_ELEMENTS,	&s_prompt_edit_pad },
+	{ "edit.prompt.arrow",	DSTYLE_ELEMENTS,	&s_prompt_edit_arr },
+	{ "edit.prompt.marked",	DSTYLE_ELEMENTS,	&s_prompt_sel },
+	{ "edit.prompt",	DSTYLE_ELEMENTS,	&s_prompt_edit },
+	{ "forwbackw.arrow",	DSTYLE_ELEMENTS,	&s_forw_backw },
+	{ "hot.paste",		DSTYLE_ELEMENTS,	&s_hot_paste },
+	{ "menu.frame",		DSTYLE_ELEMENTS,	&s_menu_frame },
+	{ "menu.bg",		DSTYLE_ELEMENTS,	&s_menu_bg },
+	{ "menu.n",		DSTYLE_ELEMENTS,	&s_menu_number },
+	{ "menu.entry",		DSTYLE_ELEMENTS,	&s_menu_entry },
+	{ "menu.active",	DSTYLE_ELEMENTS,	&s_menu_active },
+	{ "menu.sb",		DSTYLE_ELEMENTS,	&s_menu_sb },
+    };
+    /* *INDENT-ON* */
+
+    unsigned n;
+    BOOL found = FALSE;
+
+    char *buffer = 0;
+    char *tmp = 0;
+    char *element, *mono;
+    const char *fg, *bg;
+
+    if (param == 0)
+	return;
+    CTRACE2(TRACE_STYLE, (tfp, "parse_style(%s)\n", param));
+    StrAllocCopy(buffer, param);
+    if (buffer == 0)
+	return;
+
+    TrimLowercase(buffer);
+    if ((tmp = strchr(buffer, ':')) == 0) {
+	fprintf(stderr, gettext("\
+Syntax Error parsing style in lss file:\n\
+[%s]\n\
+The line must be of the form:\n\
+OBJECT:MONO:COLOR (ie em:bold:brightblue:white)\n\
+where OBJECT is one of EM,STRONG,B,I,U,BLINK etc.\n\n"), buffer);
+	exit_immediately(EXIT_FAILURE);
+    }
+    *tmp = '\0';
+    element = buffer;
+
+    mono = tmp + 1;
+    tmp = strchr(mono, ':');
+
+    if (!tmp) {
+	fg = "nocolor";
+	bg = "nocolor";
+    } else {
+	*tmp = '\0';
+	fg = tmp + 1;
+	tmp = strchr(fg, ':');
+	if (!tmp)
+	    bg = "default";
+	else {
+	    *tmp = '\0';
+	    bg = tmp + 1;
+	}
+    }
+
+    CTRACE2(TRACE_STYLE, (tfp, "CSSPARSE:%s => %d %s\n",
+			  element, hash_code(element),
+			  (hashStyles[hash_code(element)].name ? "used" : "")));
+
+    /*
+     * We use some pseudo-elements, so catch these first
+     */
+    for (n = 0; n < TABLESIZE(table); n++) {
+	if (!strcasecomp(element, table[n].name)) {
+	    parse_attributes(mono, fg, bg, table[n].style, table[n].name);
+	    if (table[n].set_hash != 0)
+		*(table[n].set_hash) = hash_code(table[n].name);
+	    found = TRUE;
+	    break;
+	}
+    }
+
+    if (found) {
+	if (!strcasecomp(element, "normal")) {
+	    /* added - kw */
+	    parse_attributes(mono, fg, bg, DSTYLE_NORMAL, "html");
+	    s_normal = hash_code("html");	/* rather bizarre... - kw */
+
+	    LYnormalColor();
+	}
+    } else {
+	/* It must be a HTML element, so look through the list until we find it. */
+	int element_number = -1;
+	HTTag *t = SGMLFindTag(&HTML_dtd, element);
+
+	if (t && t->name) {
+	    element_number = t - HTML_dtd.tags;
+	}
+	if (element_number >= HTML_A &&
+	    element_number < HTML_ELEMENTS) {
+	    parse_attributes(mono, fg, bg, element_number + STARTAT, element);
+	} else {
+	    parse_attributes(mono, fg, bg, DSTYLE_ELEMENTS, element);
+	}
+    }
+    FREE(buffer);
+}
+
+static void style_deleteStyleList(void)
+{
+    LYFreeStringList(lss_styles);
+    lss_styles = NULL;
+}
+
+static void free_colorstylestuff(void)
+{
+    style_initialiseHashTable();
+    style_deleteStyleList();
+    memset(our_pairs, 0, sizeof(our_pairs));
+    FreeCachedStyles();
+}
+
+/*
+ * Initialise the default style sheet to match the vanilla-curses lynx.
+ */
+static void initialise_default_stylesheet(void)
+{
+    /* Use the data setup in USE_COLOR_TABLE */
+    /* *INDENT-OFF* */
+    static const struct {
+	int		color;	/* index into lynx_color_pairs[] */
+	const char	*type;
+    } table2[] = {
+	/*
+	 * non-color-style colors encode bold/reverse/underline as a 0-7
+	 * index like this:
+	 *  b,r,u 0
+	 *  b,r,U 1
+	 *  b,R,u 2
+	 *  b,R,U 3
+	 *  B,r,u 4
+	 *  B,r,U 5
+	 *  B,R,u 6
+	 *  B,R,U 7
+	 */
+	{ 0,	"normal" },
+	{ 1,	"a" },
+	{ 2,	"status" },
+	{ 4,	"b" },
+	{ 4,	"blink" },
+	{ 4,	"cite" },
+	{ 4,	"del" },
+	{ 4,	"em" },
+	{ 4,	"i" },
+	{ 4,	"ins" },
+	{ 4,	"strike" },
+	{ 4,	"strong" },
+	{ 4,	"u" },
+#if 0
+	{ 5,	"a.b" },
+	{ 5,	"b.a" },
+	{ 5,	"var.a" },
+#endif
+	{ 6,	"alink" },
+	{ 7,	"whereis" },
+#if 0
+	{ 0,	"h2.link" },
+	{ 0,	"link.h2" },
+#endif
+#ifdef USE_PRETTYSRC
+	/* FIXME: HTL_tagspecs_defaults[] has similar info */
+	{ 4,	"span.htmlsrc_comment" },
+	{ 4,	"span.htmlsrc_tag" },
+	{ 4,	"span.htmlsrc_attrib" },
+	{ 4,	"span.htmlsrc_attrval" },
+	{ 4,	"span.htmlsrc_abracket" },
+	{ 4,	"span.htmlsrc_entity" },
+	{ 4,	"span.htmlsrc_href" },
+	{ 4,	"span.htmlsrc_entire" },
+	{ 4,	"span.htmlsrc_badseq" },
+	{ 4,	"span.htmlsrc_badtag" },
+	{ 4,	"span.htmlsrc_badattr" },
+	{ 4,	"span.htmlsrc_sgmlspecial" },
+#endif
+    };
+    /* *INDENT-ON* */
+
+    unsigned n;
+    char *normal = LYgetTableString(0);
+    char *strong = LYgetTableString(4);
+
+    CTRACE1((tfp, "initialise_default_stylesheet\n"));
+
+    /*
+     * For debugging this function, create hash codes for all of the tags.
+     * That makes it simpler to find the cases that are overlooked in the
+     * table.
+     */
+    for (n = 0; n < (unsigned) HTML_dtd.number_of_tags; ++n) {
+	char *name = 0;
+
+	HTSprintf0(&name, "%s:%s", HTML_dtd.tags[n].name, normal);
+	parse_style(name);
+	FREE(name);
+    }
+
+    for (n = 0; n < TABLESIZE(table2); ++n) {
+	int code = table2[n].color;
+	char *name = 0;
+	char *value = 0;
+
+	switch (code) {
+	case 0:
+	    value = normal;
+	    break;
+	case 4:
+	    value = strong;
+	    break;
+	default:
+	    value = LYgetTableString(code);
+	    break;
+	}
+	HTSprintf0(&name, "%s:%s", table2[n].type, value);
+	parse_style(name);
+	FREE(name);
+	if (value != normal && value != strong && value != 0)
+	    free(value);
+    }
+    FREE(normal);
+    FREE(strong);
+}
+
+/* Set all the buckets in the hash table to be empty */
+static void style_initialiseHashTable(void)
+{
+    int i;
+    static int firsttime = 1;
+
+    for (i = 0; i < CSHASHSIZE; i++) {
+	if (firsttime)
+	    hashStyles[i].name = NULL;
+	else
+	    FREE(hashStyles[i].name);
+	hashStyles[i].color = 0;
+	hashStyles[i].cattr = 0;
+	hashStyles[i].mono = 0;
+    }
+    if (firsttime) {
+	firsttime = 0;
+#ifdef LY_FIND_LEAKS
+	atexit(free_colorstylestuff);
+#endif
+    }
+    s_alink = hash_code("alink");
+    s_a = hash_code("a");
+    s_status = hash_code("status");
+    s_alert = hash_code("alert");
+    s_title = hash_code("title");
+#ifdef USE_SCROLLBAR
+    s_sb_bar = hash_code("scroll.bar");
+    s_sb_bg = hash_code("scroll.back");
+    s_sb_aa = hash_code("scroll.arrow");
+    s_sb_naa = hash_code("scroll.noarrow");
+#endif
+}
+
+void parse_userstyles(void)
+{
+    char *name;
+    HTList *cur = lss_styles;
+
+    colorPairs = 0;
+    style_initialiseHashTable();
+
+    if (HTList_isEmpty(cur)) {
+	initialise_default_stylesheet();
+    } else {
+	while ((name = (char *) HTList_nextObject(cur)) != NULL) {
+	    CTRACE2(TRACE_STYLE, (tfp, "LSS:%s\n",
+				  (name
+				   ? name
+				   : "!?! empty !?!")));
+	    if (name != NULL)
+		parse_style(name);
+	}
+    }
+
+#define dft_style(a,b) if (a == NOSTYLE) a = b
+    /* *INDENT-OFF* */
+    dft_style(s_prompt_edit,		s_normal);
+    dft_style(s_prompt_edit_arr,	s_prompt_edit);
+    dft_style(s_prompt_edit_pad,	s_prompt_edit);
+    dft_style(s_prompt_sel,		s_prompt_edit);
+    dft_style(s_aedit,			s_alink);
+    dft_style(s_aedit_arr,		s_aedit);
+    dft_style(s_aedit_pad,		s_aedit);
+    dft_style(s_curedit,		s_aedit);
+    dft_style(s_aedit_sel,		s_aedit);
+    dft_style(s_menu_bg,		s_normal);
+    dft_style(s_menu_entry,		s_menu_bg);
+    dft_style(s_menu_frame,		s_menu_bg);
+    dft_style(s_menu_number,		s_menu_bg);
+    dft_style(s_menu_active,		s_alink);
+    /* *INDENT-ON* */
+
+}
+
+/* Add a STYLE: option line to our list.  Process "default:" early
+ * for it to have the same semantic as other lines: works at any place
+ * of the style file, the first line overrides the later ones.
+ */
+static void HStyle_addStyle(char *buffer)
+{
+    char *name = NULL;
+
+    CTRACE1((tfp, "HStyle_addStyle(%s)\n", buffer));
+
+    StrAllocCopy(name, buffer);
+    TrimLowercase(name);
+
+    if (lss_styles == NULL)
+	lss_styles = HTList_new();
+
+    if (!strncasecomp(name, "default:", 8)) {
+	/* default fg/bg */
+	CTRACE2(TRACE_STYLE, (tfp, "READCSS.default%s:%s\n",
+			      (default_color_reset ? ".ignore" : ""),
+			      name ? name : "!?! empty !?!"));
+	if (!default_color_reset)
+	    parse_style(name);
+	return;			/* do not need to process it again */
+    }
+    CTRACE2(TRACE_STYLE, (tfp, "READCSS:%s\n", name ? name : "!?! empty !?!"));
+    HTList_addObject(lss_styles, name);
+}
+
+static int style_readFromFileREC(char *lss_filename,
+				 char *parent_filename)
+{
+    FILE *fh;
+    char *buffer = NULL;
+
+    CTRACE2(TRACE_STYLE, (tfp, "CSS:Reading styles from file: %s\n",
+			  lss_filename ? lss_filename : "?!? empty ?!?"));
+    if (isEmpty(lss_filename))
+	return -1;
+    if ((fh = LYOpenCFG(lss_filename, parent_filename, LYNX_LSS_FILE)) == 0) {
+	/* this should probably be an alert or something */
+	CTRACE2(TRACE_STYLE, (tfp,
+			      "CSS:Can't open style file '%s', using defaults\n", lss_filename));
+	return -1;
+    }
+
+    if (parent_filename == 0) {
+	free_colorstylestuff();
+    }
+
+    while (LYSafeGets(&buffer, fh) != NULL) {
+	LYTrimTrailing(buffer);
+	LYTrimTail(buffer);
+	LYTrimHead(buffer);
+	if (!strncasecomp(buffer, "include:", 8))
+	    style_readFromFileREC(LYSkipBlanks(buffer + 8), lss_filename);
+	else if (buffer[0] != '#' && strlen(buffer) != 0)
+	    HStyle_addStyle(buffer);
+    }
+
+    LYCloseInput(fh);
+    if ((parent_filename == 0) && LYCursesON)
+	parse_userstyles();
+    return 0;
+}
+
+int style_readFromFile(char *filename)
+{
+    return style_readFromFileREC(filename, (char *) 0);
+}
+
+/* Used in HTStructured methods: - kw */
+
+void TrimColorClass(const char *tagname,
+		    char *styleclassname,
+		    int *phcode)
+{
+    char *end, *start = NULL, *lookfrom;
+    char tmp[64];
+
+    sprintf(tmp, ";%.*s", (int) sizeof(tmp) - 3, tagname);
+    TrimLowercase(tmp);
+
+    if ((lookfrom = styleclassname) != 0) {
+	do {
+	    end = start;
+	    start = strstr(lookfrom, tmp);
+	    if (start)
+		lookfrom = start + 1;
+	}
+	while (start);
+	/* trim the last matching element off the end
+	 * - should match classes here as well (rp)
+	 */
+	if (end)
+	    *end = '\0';
+    }
+    *phcode = hash_code(lookfrom && *lookfrom ? lookfrom : &tmp[1]);
+}
+
+/* This function is designed as faster analog to TrimColorClass.
+ * It assumes that tag_name is present in stylename! -HV
+ */
+void FastTrimColorClass(const char *tag_name,
+			unsigned name_len,
+			char *stylename,
+			char **pstylename_end,	/*will be modified */
+			int *phcode)	/*will be modified */
+{
+    char *tag_start = *pstylename_end;
+    BOOLEAN found = FALSE;
+
+    CTRACE2(TRACE_STYLE,
+	    (tfp, "STYLE.fast-trim: [%s] from [%s]: ",
+	     tag_name, stylename));
+    while (tag_start >= stylename) {
+	for (; (tag_start >= stylename) && (*tag_start != ';'); --tag_start) ;
+	if (!strncasecomp(tag_start + 1, tag_name, (int) name_len)) {
+	    found = TRUE;
+	    break;
+	}
+	--tag_start;
+    }
+    if (found) {
+	*tag_start = '\0';
+	*pstylename_end = tag_start;
+    }
+    CTRACE2(TRACE_STYLE, (tfp, found ? "success.\n" : "failed.\n"));
+    *phcode = hash_code(tag_start + 1);
+}
+
+/* This is called each time lss styles are read. It will fill
+ * each element of 'cached_tag_styles' -HV
+ */
+void cache_tag_styles(void)
+{
+    char buf[200];
+    int i;
+
+    for (i = 0; i < HTML_ELEMENTS; ++i) {
+	LYstrncpy(buf, HTML_dtd.tags[i].name, sizeof(buf) - 1);
+	LYLowerCase(buf);
+	cached_tag_styles[i] = hash_code(buf);
+    }
+}
+
+#define SIZEOF_CACHED_STYLES (unsigned) (cached_styles_rows * cached_styles_cols)
+
+static unsigned *RefCachedStyle(int y, int x)
+{
+    unsigned *result = 0;
+
+    if (cached_styles_ptr == 0) {
+	cached_styles_rows = display_lines;
+	cached_styles_cols = LYcols;
+	cached_styles_ptr = typecallocn(unsigned, SIZEOF_CACHED_STYLES);
+    }
+    if (y >= 0 &&
+	x >= 0 &&
+	y < cached_styles_rows &&
+	x < cached_styles_cols) {
+	result = cached_styles_ptr + (y * cached_styles_cols) + x;
+    }
+    return result;
+}
+
+BOOL ValidCachedStyle(int y, int x)
+{
+    return (BOOL) (RefCachedStyle(y, x) != 0);
+}
+
+unsigned GetCachedStyle(int y, int x)
+{
+    unsigned value = 0;
+    unsigned *cache = RefCachedStyle(y, x);
+
+    if (cache != 0) {
+	value = *cache;
+    }
+    return value;
+}
+
+void SetCachedStyle(int y, int x, unsigned value)
+{
+    unsigned *cache = RefCachedStyle(y, x);
+
+    if (cache != 0) {
+	*cache = value;
+    }
+}
+
+void ResetCachedStyles(void)
+{
+    if (cached_styles_ptr != NULL) {
+	memset(cached_styles_ptr, 0, sizeof(unsigned) * SIZEOF_CACHED_STYLES);
+    }
+}
+
+void FreeCachedStyles(void)
+{
+    if (cached_styles_ptr != NULL) {
+	FREE(cached_styles_ptr);
+	cached_styles_rows = 0;
+	cached_styles_cols = 0;
+    }
+}
+
+#endif /* USE_COLOR_STYLE */