summaryrefslogtreecommitdiffstats
path: root/src/LYStyle.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/LYStyle.c')
-rw-r--r--src/LYStyle.c970
1 files changed, 970 insertions, 0 deletions
diff --git a/src/LYStyle.c b/src/LYStyle.c
new file mode 100644
index 0000000..77be188
--- /dev/null
+++ b/src/LYStyle.c
@@ -0,0 +1,970 @@
+/*
+ * $LynxId: LYStyle.c,v 1.111 2021/06/09 22:00:35 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 <LYOptions.h>
+#include <LYPrettySrc.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 HTList *list_of_lss_files;
+
+/* 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;
+static BOOL empty_lss_list = FALSE; /* true if list explicitly emptied */
+
+/* stack of attributes during page rendering */
+int last_styles[MAX_LAST_STYLES + 1] =
+{0};
+int last_colorattr_ptr = 0;
+
+bucket hashStyles[CSHASHSIZE];
+
+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 void style_initialiseHashTable(void);
+
+static bucket *new_bucket(const char *name)
+{
+ bucket *result = typecalloc(bucket);
+
+ if (!result)
+ outofmem(__FILE__, "new_bucket");
+ StrAllocCopy(result->name, name);
+ return result;
+}
+
+bucket *nostyle_bucket(void)
+{
+ return new_bucket("<NOSTYLE>");
+}
+
+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 = color_style_1(element);
+ int colored_attr;
+
+ 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 = !!((unsigned) cA & A_BOLD);
+ int iBlink = !!((unsigned) cA & M_BLINK);
+
+ CTRACE2(TRACE_STYLE, (tfp, "parse_attributes %d/%d %d/%d %#x\n",
+ fA, default_fg, bA, default_bg, (unsigned) 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));
+ colored_attr = ((int) COLOR_PAIR(curPair)) | ((int) cA);
+ if (style < DSTYLE_ELEMENTS)
+ setStyle(style, colored_attr, cA, mA);
+ setHashStyle(newstyle, colored_attr, 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, color_style_1(element),
+ (hashStyles[color_style_1(element)].used)
+ ? "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) = color_style_1(table[n].name);
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (found) {
+ if (!strcasecomp(element, "normal")) {
+ /* added - kw */
+ parse_attributes(mono, fg, bg, DSTYLE_NORMAL, "html");
+ s_normal = color_style_1("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 = (int) (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_lss_list(void)
+{
+ LSS_NAMES *obj;
+
+ while ((obj = HTList_objectAt(list_of_lss_files, 0)) != 0) {
+ FREE(obj->given);
+ FREE(obj->actual);
+ FREE(obj);
+ if (!HTList_removeObject(list_of_lss_files, obj)) {
+ break;
+ }
+ }
+ HTList_delete(list_of_lss_files);
+}
+
+static void free_colorstylestuff(void)
+{
+ if (TRACE_STYLE) {
+ report_hashStyles();
+ }
+ style_initialiseHashTable();
+ free_hashStyles();
+ style_deleteStyleList();
+ memset(our_pairs, 0, sizeof(our_pairs));
+ FreeCachedStyles();
+}
+
+/* Set all the buckets in the hash table to be empty */
+static void style_initialiseHashTable(void)
+{
+ int i;
+ static BOOL firsttime = TRUE;
+
+ for (i = 0; i < CSHASHSIZE; i++) {
+ hashStyles[i].used = FALSE;
+ }
+ if (firsttime) {
+ firsttime = FALSE;
+#ifdef LY_FIND_LEAKS
+ atexit(free_colorstylestuff);
+ atexit(free_colorstyle_leaks);
+#endif
+ }
+ s_alink = color_style_1("alink");
+ s_a = color_style_1("a");
+ s_status = color_style_1("status");
+ s_alert = color_style_1("alert");
+ s_title = color_style_1("title");
+#ifdef USE_SCROLLBAR
+ s_sb_bar = color_style_1("scroll.bar");
+ s_sb_bg = color_style_1("scroll.back");
+ s_sb_aa = color_style_1("scroll.arrow");
+ s_sb_naa = color_style_1("scroll.noarrow");
+#endif
+}
+
+/*
+ * 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" },
+ { 5, "input" },
+ { 6, "alink" },
+ { 7, "whereis" },
+#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);
+}
+
+void parse_userstyles(void)
+{
+ char *name;
+ HTList *cur = LYuse_color_style ? lss_styles : 0;
+
+ 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);
+ FREE(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 = color_style_1(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 = color_style_1(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)
+{
+ int i;
+
+ for (i = 0; i < HTML_ELEMENTS; ++i) {
+ cached_tag_styles[i] = color_style_1(HTML_dtd.tags[i].name);
+ }
+}
+
+#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;
+ }
+}
+
+/*
+ * Recompute the pairs associated with the color style.
+ */
+void update_color_style(void)
+{
+ CTRACE((tfp, "update_color_style %p\n", (void *) lss_styles));
+ memset(our_pairs, 0, sizeof(our_pairs));
+ parse_userstyles();
+}
+
+static char *find_lss_file(const char *nominal)
+{
+ return LYFindConfigFile(nominal, LYNX_LSS_FILE);
+}
+
+void clear_lss_list(void)
+{
+ CTRACE((tfp, "clear_lss_list()\n"));
+ free_lss_list();
+ empty_lss_list = TRUE;
+}
+
+/*
+ * Add an entry to the lss-list, and cache the resolved filename if known.
+ */
+void add_to_lss_list(const char *source, const char *resolved)
+{
+ LSS_NAMES *obj;
+ LSS_NAMES *chk;
+ BOOLEAN found = FALSE;
+ int position = 0;
+
+#ifdef LY_FIND_LEAKS
+ atexit(free_colorstyle_leaks);
+#endif
+
+ CTRACE((tfp, "add_to_lss_list(\"%s\", \"%s\")\n",
+ NonNull(source),
+ NonNull(resolved)));
+
+ if (list_of_lss_files == 0) {
+ list_of_lss_files = HTList_new();
+ }
+
+ while ((chk = HTList_objectAt(list_of_lss_files, position++)) != 0) {
+ if (!strcmp(source, chk->given)) {
+ found = TRUE;
+ if (resolved && !chk->actual) {
+ StrAllocCopy(chk->actual, resolved);
+ }
+ break;
+ }
+ }
+
+ if (!found) {
+ obj = typecalloc(LSS_NAMES);
+ if (obj == NULL)
+ outofmem(__FILE__, "add_to_lss_list");
+
+ StrAllocCopy(obj->given, source);
+ StrAllocCopy(obj->actual, resolved);
+ HTList_appendObject(list_of_lss_files, obj);
+ empty_lss_list = FALSE;
+ }
+}
+
+/*
+ * This is called after reading lynx.cfg, to set the initial value for the
+ * lss-file, and read its data.
+ */
+void init_color_styles(char **from_cmdline, const char *default_styles)
+{
+ char *user_lss_file = *from_cmdline;
+ char *cp;
+
+ /*
+ * If a command-line "-lss" option was given, or if an environment variable
+ * is found, use that in preference to data from lynx.cfg
+ */
+ if (user_lss_file == 0)
+ user_lss_file = LYGetEnv("LYNX_LSS");
+ if (user_lss_file == 0)
+ user_lss_file = LYGetEnv("lynx_lss");
+ if (user_lss_file != 0)
+ empty_lss_list = (*user_lss_file == '\0');
+
+ /*
+ * If the color-style is explicitly emptied, go no further.
+ */
+ if (empty_lss_list) {
+ CTRACE((tfp, "init_color_styles: overridden/empty\n"));
+ return;
+ } else if (list_of_lss_files == 0) {
+ char *source = 0;
+ char *config;
+
+ StrAllocCopy(source, default_styles);
+ config = source;
+ while ((cp = LYstrsep(&config, ";")) != 0) {
+ char *target;
+
+ target = find_lss_file(LYPathLeaf(cp));
+ if (target != 0) {
+ add_to_lss_list(cp, target);
+ FREE(target);
+ }
+ }
+ FREE(source);
+ }
+
+ if (user_lss_file != 0) {
+ FREE(lynx_lss_file);
+ lynx_lss_file = find_lss_file(cp = user_lss_file);
+ *from_cmdline = 0;
+ } else {
+ lynx_lss_file = find_lss_file(cp = DeConst(LYNX_LSS_FILE));
+ }
+ CTRACE1((tfp, "init_color_styles(%s)\n", NonNull(lynx_lss_file)));
+
+ if (isEmpty(lynx_lss_file))
+ return;
+ /*
+ * If the lynx-style file is not available, inform the user and exit.
+ */
+ if (!LYCanReadFile(lynx_lss_file)) {
+ fprintf(stderr, gettext("\nLynx file \"%s\" is not available.\n\n"),
+ NonNull(cp));
+ exit_immediately(EXIT_FAILURE);
+ }
+
+ /*
+ * Otherwise, load the initial lss-file and add it to the list for the
+ * options menu.
+ */
+ style_readFromFile(lynx_lss_file);
+ add_to_lss_list(LYPathLeaf(lynx_lss_file), lynx_lss_file);
+#ifndef NO_OPTION_FORMS
+ build_lss_enum(list_of_lss_files);
+#endif
+}
+
+void reinit_color_styles(void)
+{
+#ifdef USE_PRETTYSRC
+ int cs;
+
+ for (cs = 0; cs < HTL_num_lexemes; ++cs) {
+ html_src_clean_item((HTlexeme) cs);
+ }
+#endif
+ free_colorstylestuff();
+ style_readFromFile(lynx_lss_file);
+}
+
+#endif /* USE_COLOR_STYLE */