1 /*
2  * $LynxId: LYStyle.c,v 1.73 2013/06/02 19:17:06 tom Exp $
3  *
4  * character level styles for Lynx
5  * (c) 1996 Rob Partington -- donated to the Lyncei (if they want it :-)
6  */
7 #include <HTUtils.h>
8 #include <HTML.h>
9 #include <LYGlobalDefs.h>
10 
11 #include <LYStructs.h>
12 #include <LYReadCFG.h>
13 #include <LYCurses.h>
14 #include <LYCharUtils.h>
15 #include <LYUtils.h>		/* defines TABLESIZE */
16 #include <AttrList.h>
17 #include <SGML.h>
18 #include <HTMLDTD.h>
19 
20 /* Hash table definitions */
21 #include <LYHash.h>
22 #include <LYStyle.h>
23 
24 #include <LYexit.h>
25 #include <LYLeaks.h>
26 #include <LYStrings.h>
27 #include <LYHash.h>
28 
29 #define CTRACE1(p) CTRACE2(TRACE_CFG || TRACE_STYLE, p)
30 
31 #ifdef USE_COLOR_STYLE
32 
33 static void style_initialiseHashTable(void);
34 
35 /* because curses isn't started when we parse the config file, we
36  * need to remember the STYLE: lines we encounter and parse them
37  * after curses has started
38  */
39 static HTList *lss_styles = NULL;
40 
41 #define CACHEW 128
42 #define CACHEH 64
43 
44 static unsigned *cached_styles_ptr = NULL;
45 static int cached_styles_rows = 0;
46 static int cached_styles_cols = 0;
47 
48 /* stack of attributes during page rendering */
49 int last_styles[MAX_LAST_STYLES + 1] =
50 {0};
51 int last_colorattr_ptr = 0;
52 
53 bucket hashStyles[CSHASHSIZE];
54 
55 int cached_tag_styles[HTML_ELEMENTS];
56 int current_tag_style;
57 BOOL force_current_tag_style = FALSE;
58 char *forced_classname;
59 BOOL force_classname;
60 
61 /* Remember the hash codes for common elements */
62 int s_a = NOSTYLE;
63 int s_aedit = NOSTYLE;
64 int s_aedit_arr = NOSTYLE;
65 int s_aedit_pad = NOSTYLE;
66 int s_aedit_sel = NOSTYLE;
67 int s_alert = NOSTYLE;
68 int s_alink = NOSTYLE;
69 int s_curedit = NOSTYLE;
70 int s_forw_backw = NOSTYLE;
71 int s_hot_paste = NOSTYLE;
72 int s_menu_active = NOSTYLE;
73 int s_menu_bg = NOSTYLE;
74 int s_menu_entry = NOSTYLE;
75 int s_menu_frame = NOSTYLE;
76 int s_menu_number = NOSTYLE;
77 int s_menu_sb = NOSTYLE;
78 int s_normal = NOSTYLE;
79 int s_prompt_edit = NOSTYLE;
80 int s_prompt_edit_arr = NOSTYLE;
81 int s_prompt_edit_pad = NOSTYLE;
82 int s_prompt_sel = NOSTYLE;
83 int s_status = NOSTYLE;
84 int s_title = NOSTYLE;
85 int s_whereis = NOSTYLE;
86 
87 #ifdef USE_SCROLLBAR
88 int s_sb_aa = NOSTYLE;
89 int s_sb_bar = NOSTYLE;
90 int s_sb_bg = NOSTYLE;
91 int s_sb_naa = NOSTYLE;
92 #endif
93 
94 /* start somewhere safe */
95 #define MAX_COLOR 16
96 static int colorPairs = 0;
97 
98 #ifdef USE_BLINK
99 #  define MAX_BLINK	2
100 #  define M_BLINK	A_BLINK
101 #else
102 #  define MAX_BLINK	1
103 #  define M_BLINK	0
104 #endif
105 
106 #define MAX_PAIR 255		/* because our_pairs[] type is unsigned-char */
107 static unsigned char our_pairs[2]
108 [MAX_BLINK]
109 [MAX_COLOR + 1]
110 [MAX_COLOR + 1];
111 
new_bucket(const char * name)112 static bucket *new_bucket(const char *name)
113 {
114     bucket *result = typecalloc(bucket);
115 
116     if (!result)
117 	outofmem(__FILE__, "new_bucket");
118     StrAllocCopy(result->name, name);
119     return result;
120 }
121 
122 #if OMIT_SCN_KEEPING
special_bucket(void)123 bucket *special_bucket(void)
124 {
125     return new_bucket("<special>");
126 }
127 #endif
128 
nostyle_bucket(void)129 bucket *nostyle_bucket(void)
130 {
131     return new_bucket("<NOSTYLE>");
132 }
133 
TrimLowercase(char * buffer)134 static char *TrimLowercase(char *buffer)
135 {
136     LYRemoveBlanks(buffer);
137     strtolower(buffer);
138     return buffer;
139 }
140 
141 /*
142  * Parse a string containing a combination of video attributes and color.
143  */
parse_either(const char * attrs,int dft_color,int * monop,int * colorp)144 static void parse_either(const char *attrs,
145 			 int dft_color,
146 			 int *monop,
147 			 int *colorp)
148 {
149     int value;
150     char *temp_attrs = NULL;
151 
152     if (StrAllocCopy(temp_attrs, attrs) != NULL) {
153 	char *to_free = temp_attrs;
154 
155 	while (*temp_attrs != '\0') {
156 	    char *next = strchr(temp_attrs, '+');
157 	    char save = (char) ((next != NULL) ? *next : '\0');
158 
159 	    if (next == NULL)
160 		next = temp_attrs + strlen(temp_attrs);
161 
162 	    if (save != 0)
163 		*next = '\0';
164 	    if ((value = string_to_attr(temp_attrs)) != 0)
165 		*monop |= value;
166 	    else if (colorp != 0
167 		     && (value = check_color(temp_attrs, dft_color)) != ERR_COLOR)
168 		*colorp = value;
169 
170 	    temp_attrs = next;
171 	    if (save != '\0')
172 		*temp_attrs++ = save;
173 	}
174 	FREE(to_free);
175     }
176 }
177 
178 /* icky parsing of the style options */
parse_attributes(const char * mono,const char * fg,const char * bg,int style,const char * element)179 static void parse_attributes(const char *mono,
180 			     const char *fg,
181 			     const char *bg,
182 			     int style,
183 			     const char *element)
184 {
185     int mA = A_NORMAL;
186     int fA = default_fg;
187     int bA = default_bg;
188     int cA = A_NORMAL;
189     int newstyle = hash_code(element);
190 
191     CTRACE2(TRACE_STYLE, (tfp, "CSS(PA):style d=%d / h=%d, e=%s\n",
192 			  style, newstyle, element));
193 
194     parse_either(mono, ERR_COLOR, &mA, (int *) 0);
195     parse_either(bg, default_bg, &cA, &bA);
196     parse_either(fg, default_fg, &cA, &fA);
197 
198     if (style == -1) {		/* default */
199 	CTRACE2(TRACE_STYLE, (tfp, "CSS(DEF):default_fg=%d, default_bg=%d\n",
200 			      fA, bA));
201 	default_fg = fA;
202 	default_bg = bA;
203 	default_color_reset = TRUE;
204 	return;
205     }
206     if (fA == NO_COLOR) {
207 	bA = NO_COLOR;
208     } else if (COLORS) {
209 #ifdef USE_BLINK
210 	if (term_blink_is_boldbg) {
211 	    if (fA >= COLORS)
212 		cA = A_BOLD;
213 	    if (bA >= COLORS)
214 		cA |= M_BLINK;
215 	} else
216 #endif
217 	if (fA >= COLORS || bA >= COLORS)
218 	    cA = A_BOLD;
219 	if (fA >= COLORS)
220 	    fA %= COLORS;
221 	if (bA >= COLORS)
222 	    bA %= COLORS;
223     } else {
224 	cA = A_BOLD;
225 	fA = NO_COLOR;
226 	bA = NO_COLOR;
227     }
228 
229     /*
230      * If we have colour, and space to create a new colour attribute,
231      * and we have a valid colour description, then add this style
232      */
233     if (lynx_has_color && colorPairs < COLOR_PAIRS - 1 && fA != NO_COLOR) {
234 	int curPair = 0;
235 	int iFg = (1 + (fA >= 0 ? fA : 0));
236 	int iBg = (1 + (bA >= 0 ? bA : 0));
237 	int iBold = !!((unsigned) cA & A_BOLD);
238 	int iBlink = !!((unsigned) cA & M_BLINK);
239 
240 	CTRACE2(TRACE_STYLE, (tfp, "parse_attributes %d/%d %d/%d %#x\n",
241 			      fA, default_fg, bA, default_bg, cA));
242 	if (fA < MAX_COLOR
243 	    && bA < MAX_COLOR
244 #ifdef USE_CURSES_PAIR_0
245 	    && (cA != A_NORMAL || fA != default_fg || bA != default_bg)
246 #endif
247 	    && curPair < MAX_PAIR) {
248 	    if (our_pairs[iBold][iBlink][iFg][iBg] != 0) {
249 		curPair = our_pairs[iBold][iBlink][iFg][iBg];
250 	    } else {
251 		curPair = ++colorPairs;
252 		init_pair((short) curPair, (short) fA, (short) bA);
253 		our_pairs[iBold][iBlink][iFg][iBg] = UCH(curPair);
254 	    }
255 	}
256 	CTRACE2(TRACE_STYLE, (tfp, "CSS(CURPAIR):%d\n", curPair));
257 	if (style < DSTYLE_ELEMENTS)
258 	    setStyle(style, COLOR_PAIR(curPair) | cA, cA, mA);
259 	setHashStyle(newstyle, COLOR_PAIR(curPair) | cA, cA, mA, element);
260     } else {
261 	if (lynx_has_color && fA != NO_COLOR) {
262 	    CTRACE2(TRACE_STYLE,
263 		    (tfp, "CSS(NC): maximum of %d colorpairs exhausted\n",
264 		     COLOR_PAIRS - 1));
265 	}
266 	/* only mono is set */
267 	if (style < DSTYLE_ELEMENTS)
268 	    setStyle(style, -1, -1, mA);
269 	setHashStyle(newstyle, -1, -1, mA, element);
270     }
271 }
272 
273 /* parse a style option of the format
274  * STYLE:<OBJECT>:FG:BG
275  */
parse_style(char * param)276 static void parse_style(char *param)
277 {
278     /* *INDENT-OFF* */
279     static struct {
280 	const char *name;
281 	int style;
282 	int *set_hash;
283     } table[] = {
284 	{ "default",		-1,			0 }, /* default fg/bg */
285 	{ "alink",		DSTYLE_ALINK,		0 }, /* active link */
286 	{ "a",			DSTYLE_LINK,		0 }, /* normal link */
287 	{ "a",			HTML_A,			0 }, /* normal link */
288 	{ "status",		DSTYLE_STATUS,		0 }, /* status bar */
289 	{ "label",		DSTYLE_OPTION,		0 }, /* [INLINE]s */
290 	{ "value",		DSTYLE_VALUE,		0 }, /* [INLINE]s */
291 	{ "normal",		DSTYLE_NORMAL,		0 },
292 	{ "candy",		DSTYLE_CANDY,		0 }, /* [INLINE]s */
293 	{ "whereis",		DSTYLE_WHEREIS,		&s_whereis },
294 	{ "edit.active.pad",	DSTYLE_ELEMENTS,	&s_aedit_pad },
295 	{ "edit.active.arrow",	DSTYLE_ELEMENTS,	&s_aedit_arr },
296 	{ "edit.active.marked",	DSTYLE_ELEMENTS,	&s_aedit_sel },
297 	{ "edit.active",	DSTYLE_ELEMENTS,	&s_aedit },
298 	{ "edit.current",	DSTYLE_ELEMENTS,	&s_curedit },
299 	{ "edit.prompt.pad",	DSTYLE_ELEMENTS,	&s_prompt_edit_pad },
300 	{ "edit.prompt.arrow",	DSTYLE_ELEMENTS,	&s_prompt_edit_arr },
301 	{ "edit.prompt.marked",	DSTYLE_ELEMENTS,	&s_prompt_sel },
302 	{ "edit.prompt",	DSTYLE_ELEMENTS,	&s_prompt_edit },
303 	{ "forwbackw.arrow",	DSTYLE_ELEMENTS,	&s_forw_backw },
304 	{ "hot.paste",		DSTYLE_ELEMENTS,	&s_hot_paste },
305 	{ "menu.frame",		DSTYLE_ELEMENTS,	&s_menu_frame },
306 	{ "menu.bg",		DSTYLE_ELEMENTS,	&s_menu_bg },
307 	{ "menu.n",		DSTYLE_ELEMENTS,	&s_menu_number },
308 	{ "menu.entry",		DSTYLE_ELEMENTS,	&s_menu_entry },
309 	{ "menu.active",	DSTYLE_ELEMENTS,	&s_menu_active },
310 	{ "menu.sb",		DSTYLE_ELEMENTS,	&s_menu_sb },
311     };
312     /* *INDENT-ON* */
313 
314     unsigned n;
315     BOOL found = FALSE;
316 
317     char *buffer = 0;
318     char *tmp = 0;
319     char *element, *mono;
320     const char *fg, *bg;
321 
322     if (param == 0)
323 	return;
324     CTRACE2(TRACE_STYLE, (tfp, "parse_style(%s)\n", param));
325     StrAllocCopy(buffer, param);
326     if (buffer == 0)
327 	return;
328 
329     TrimLowercase(buffer);
330     if ((tmp = strchr(buffer, ':')) == 0) {
331 	fprintf(stderr, gettext("\
332 Syntax Error parsing style in lss file:\n\
333 [%s]\n\
334 The line must be of the form:\n\
335 OBJECT:MONO:COLOR (ie em:bold:brightblue:white)\n\
336 where OBJECT is one of EM,STRONG,B,I,U,BLINK etc.\n\n"), buffer);
337 	exit_immediately(EXIT_FAILURE);
338     }
339     *tmp = '\0';
340     element = buffer;
341 
342     mono = tmp + 1;
343     tmp = strchr(mono, ':');
344 
345     if (!tmp) {
346 	fg = "nocolor";
347 	bg = "nocolor";
348     } else {
349 	*tmp = '\0';
350 	fg = tmp + 1;
351 	tmp = strchr(fg, ':');
352 	if (!tmp)
353 	    bg = "default";
354 	else {
355 	    *tmp = '\0';
356 	    bg = tmp + 1;
357 	}
358     }
359 
360     CTRACE2(TRACE_STYLE, (tfp, "CSSPARSE:%s => %d %s\n",
361 			  element, hash_code(element),
362 			  (hashStyles[hash_code(element)].name ? "used" : "")));
363 
364     /*
365      * We use some pseudo-elements, so catch these first
366      */
367     for (n = 0; n < TABLESIZE(table); n++) {
368 	if (!strcasecomp(element, table[n].name)) {
369 	    parse_attributes(mono, fg, bg, table[n].style, table[n].name);
370 	    if (table[n].set_hash != 0)
371 		*(table[n].set_hash) = hash_code(table[n].name);
372 	    found = TRUE;
373 	    break;
374 	}
375     }
376 
377     if (found) {
378 	if (!strcasecomp(element, "normal")) {
379 	    /* added - kw */
380 	    parse_attributes(mono, fg, bg, DSTYLE_NORMAL, "html");
381 	    s_normal = hash_code("html");	/* rather bizarre... - kw */
382 
383 	    LYnormalColor();
384 	}
385     } else {
386 	/* It must be a HTML element, so look through the list until we find it. */
387 	int element_number = -1;
388 	HTTag *t = SGMLFindTag(&HTML_dtd, element);
389 
390 	if (t && t->name) {
391 	    element_number = (int) (t - HTML_dtd.tags);
392 	}
393 	if (element_number >= HTML_A &&
394 	    element_number < HTML_ELEMENTS) {
395 	    parse_attributes(mono, fg, bg, element_number + STARTAT, element);
396 	} else {
397 	    parse_attributes(mono, fg, bg, DSTYLE_ELEMENTS, element);
398 	}
399     }
400     FREE(buffer);
401 }
402 
style_deleteStyleList(void)403 static void style_deleteStyleList(void)
404 {
405     LYFreeStringList(lss_styles);
406     lss_styles = NULL;
407 }
408 
free_colorstylestuff(void)409 static void free_colorstylestuff(void)
410 {
411     style_initialiseHashTable();
412     style_deleteStyleList();
413     memset(our_pairs, 0, sizeof(our_pairs));
414     FreeCachedStyles();
415 }
416 
417 /*
418  * Initialise the default style sheet to match the vanilla-curses lynx.
419  */
initialise_default_stylesheet(void)420 static void initialise_default_stylesheet(void)
421 {
422     /* Use the data setup in USE_COLOR_TABLE */
423     /* *INDENT-OFF* */
424     static const struct {
425 	int		color;	/* index into lynx_color_pairs[] */
426 	const char	*type;
427     } table2[] = {
428 	/*
429 	 * non-color-style colors encode bold/reverse/underline as a 0-7
430 	 * index like this:
431 	 *  b,r,u 0
432 	 *  b,r,U 1
433 	 *  b,R,u 2
434 	 *  b,R,U 3
435 	 *  B,r,u 4
436 	 *  B,r,U 5
437 	 *  B,R,u 6
438 	 *  B,R,U 7
439 	 */
440 	{ 0,	"normal" },
441 	{ 1,	"a" },
442 	{ 2,	"status" },
443 	{ 4,	"b" },
444 	{ 4,	"blink" },
445 	{ 4,	"cite" },
446 	{ 4,	"del" },
447 	{ 4,	"em" },
448 	{ 4,	"i" },
449 	{ 4,	"ins" },
450 	{ 4,	"strike" },
451 	{ 4,	"strong" },
452 	{ 4,	"u" },
453 #if 0
454 	{ 5,	"a.b" },
455 	{ 5,	"b.a" },
456 	{ 5,	"var.a" },
457 #endif
458 	{ 6,	"alink" },
459 	{ 7,	"whereis" },
460 #if 0
461 	{ 0,	"h2.link" },
462 	{ 0,	"link.h2" },
463 #endif
464 #ifdef USE_PRETTYSRC
465 	/* FIXME: HTL_tagspecs_defaults[] has similar info */
466 	{ 4,	"span.htmlsrc_comment" },
467 	{ 4,	"span.htmlsrc_tag" },
468 	{ 4,	"span.htmlsrc_attrib" },
469 	{ 4,	"span.htmlsrc_attrval" },
470 	{ 4,	"span.htmlsrc_abracket" },
471 	{ 4,	"span.htmlsrc_entity" },
472 	{ 4,	"span.htmlsrc_href" },
473 	{ 4,	"span.htmlsrc_entire" },
474 	{ 4,	"span.htmlsrc_badseq" },
475 	{ 4,	"span.htmlsrc_badtag" },
476 	{ 4,	"span.htmlsrc_badattr" },
477 	{ 4,	"span.htmlsrc_sgmlspecial" },
478 #endif
479     };
480     /* *INDENT-ON* */
481 
482     unsigned n;
483     char *normal = LYgetTableString(0);
484     char *strong = LYgetTableString(4);
485 
486     CTRACE1((tfp, "initialise_default_stylesheet\n"));
487 
488     /*
489      * For debugging this function, create hash codes for all of the tags.
490      * That makes it simpler to find the cases that are overlooked in the
491      * table.
492      */
493     for (n = 0; n < (unsigned) HTML_dtd.number_of_tags; ++n) {
494 	char *name = 0;
495 
496 	HTSprintf0(&name, "%s:%s", HTML_dtd.tags[n].name, normal);
497 	parse_style(name);
498 	FREE(name);
499     }
500 
501     for (n = 0; n < TABLESIZE(table2); ++n) {
502 	int code = table2[n].color;
503 	char *name = 0;
504 	char *value = 0;
505 
506 	switch (code) {
507 	case 0:
508 	    value = normal;
509 	    break;
510 	case 4:
511 	    value = strong;
512 	    break;
513 	default:
514 	    value = LYgetTableString(code);
515 	    break;
516 	}
517 	HTSprintf0(&name, "%s:%s", table2[n].type, value);
518 	parse_style(name);
519 	FREE(name);
520 	if (value != normal && value != strong && value != 0)
521 	    free(value);
522     }
523     FREE(normal);
524     FREE(strong);
525 }
526 
527 /* Set all the buckets in the hash table to be empty */
style_initialiseHashTable(void)528 static void style_initialiseHashTable(void)
529 {
530     int i;
531     static int firsttime = 1;
532 
533     for (i = 0; i < CSHASHSIZE; i++) {
534 	if (firsttime)
535 	    hashStyles[i].name = NULL;
536 	else
537 	    FREE(hashStyles[i].name);
538 	hashStyles[i].color = 0;
539 	hashStyles[i].cattr = 0;
540 	hashStyles[i].mono = 0;
541     }
542     if (firsttime) {
543 	firsttime = 0;
544 #ifdef LY_FIND_LEAKS
545 	atexit(free_colorstylestuff);
546 #endif
547     }
548     s_alink = hash_code("alink");
549     s_a = hash_code("a");
550     s_status = hash_code("status");
551     s_alert = hash_code("alert");
552     s_title = hash_code("title");
553 #ifdef USE_SCROLLBAR
554     s_sb_bar = hash_code("scroll.bar");
555     s_sb_bg = hash_code("scroll.back");
556     s_sb_aa = hash_code("scroll.arrow");
557     s_sb_naa = hash_code("scroll.noarrow");
558 #endif
559 }
560 
parse_userstyles(void)561 void parse_userstyles(void)
562 {
563     char *name;
564     HTList *cur = LYuse_color_style ? lss_styles : 0;
565 
566     colorPairs = 0;
567     style_initialiseHashTable();
568 
569     if (HTList_isEmpty(cur)) {
570 	initialise_default_stylesheet();
571     } else {
572 	while ((name = (char *) HTList_nextObject(cur)) != NULL) {
573 	    CTRACE2(TRACE_STYLE, (tfp, "LSS:%s\n",
574 				  (name
575 				   ? name
576 				   : "!?! empty !?!")));
577 	    if (name != NULL)
578 		parse_style(name);
579 	}
580     }
581 
582 #define dft_style(a,b) if (a == NOSTYLE) a = b
583     /* *INDENT-OFF* */
584     dft_style(s_prompt_edit,		s_normal);
585     dft_style(s_prompt_edit_arr,	s_prompt_edit);
586     dft_style(s_prompt_edit_pad,	s_prompt_edit);
587     dft_style(s_prompt_sel,		s_prompt_edit);
588     dft_style(s_aedit,			s_alink);
589     dft_style(s_aedit_arr,		s_aedit);
590     dft_style(s_aedit_pad,		s_aedit);
591     dft_style(s_curedit,		s_aedit);
592     dft_style(s_aedit_sel,		s_aedit);
593     dft_style(s_menu_bg,		s_normal);
594     dft_style(s_menu_entry,		s_menu_bg);
595     dft_style(s_menu_frame,		s_menu_bg);
596     dft_style(s_menu_number,		s_menu_bg);
597     dft_style(s_menu_active,		s_alink);
598     /* *INDENT-ON* */
599 
600 }
601 
602 /* Add a STYLE: option line to our list.  Process "default:" early
603  * for it to have the same semantic as other lines: works at any place
604  * of the style file, the first line overrides the later ones.
605  */
HStyle_addStyle(char * buffer)606 static void HStyle_addStyle(char *buffer)
607 {
608     char *name = NULL;
609 
610     CTRACE1((tfp, "HStyle_addStyle(%s)\n", buffer));
611 
612     StrAllocCopy(name, buffer);
613     TrimLowercase(name);
614 
615     if (lss_styles == NULL)
616 	lss_styles = HTList_new();
617 
618     if (!strncasecomp(name, "default:", 8)) {
619 	/* default fg/bg */
620 	CTRACE2(TRACE_STYLE, (tfp, "READCSS.default%s:%s\n",
621 			      (default_color_reset ? ".ignore" : ""),
622 			      name ? name : "!?! empty !?!"));
623 	if (!default_color_reset)
624 	    parse_style(name);
625 	FREE(name);
626 	return;			/* do not need to process it again */
627     }
628     CTRACE2(TRACE_STYLE, (tfp, "READCSS:%s\n", name ? name : "!?! empty !?!"));
629     HTList_addObject(lss_styles, name);
630 }
631 
style_readFromFileREC(char * lss_filename,char * parent_filename)632 static int style_readFromFileREC(char *lss_filename,
633 				 char *parent_filename)
634 {
635     FILE *fh;
636     char *buffer = NULL;
637 
638     CTRACE2(TRACE_STYLE, (tfp, "CSS:Reading styles from file: %s\n",
639 			  lss_filename ? lss_filename : "?!? empty ?!?"));
640     if (isEmpty(lss_filename))
641 	return -1;
642     if ((fh = LYOpenCFG(lss_filename, parent_filename, LYNX_LSS_FILE)) == 0) {
643 	/* this should probably be an alert or something */
644 	CTRACE2(TRACE_STYLE, (tfp,
645 			      "CSS:Can't open style file '%s', using defaults\n", lss_filename));
646 	return -1;
647     }
648 
649     if (parent_filename == 0) {
650 	free_colorstylestuff();
651     }
652 
653     while (LYSafeGets(&buffer, fh) != NULL) {
654 	LYTrimTrailing(buffer);
655 	LYTrimTail(buffer);
656 	LYTrimHead(buffer);
657 	if (!strncasecomp(buffer, "include:", 8))
658 	    style_readFromFileREC(LYSkipBlanks(buffer + 8), lss_filename);
659 	else if (buffer[0] != '#' && strlen(buffer) != 0)
660 	    HStyle_addStyle(buffer);
661     }
662 
663     LYCloseInput(fh);
664     if ((parent_filename == 0) && LYCursesON)
665 	parse_userstyles();
666     return 0;
667 }
668 
style_readFromFile(char * filename)669 int style_readFromFile(char *filename)
670 {
671     return style_readFromFileREC(filename, (char *) 0);
672 }
673 
674 /* Used in HTStructured methods: - kw */
675 
TrimColorClass(const char * tagname,char * styleclassname,int * phcode)676 void TrimColorClass(const char *tagname,
677 		    char *styleclassname,
678 		    int *phcode)
679 {
680     char *end, *start = NULL, *lookfrom;
681     char tmp[64];
682 
683     sprintf(tmp, ";%.*s", (int) sizeof(tmp) - 3, tagname);
684     TrimLowercase(tmp);
685 
686     if ((lookfrom = styleclassname) != 0) {
687 	do {
688 	    end = start;
689 	    start = strstr(lookfrom, tmp);
690 	    if (start)
691 		lookfrom = start + 1;
692 	}
693 	while (start);
694 	/* trim the last matching element off the end
695 	 * - should match classes here as well (rp)
696 	 */
697 	if (end)
698 	    *end = '\0';
699     }
700     *phcode = hash_code(lookfrom && *lookfrom ? lookfrom : &tmp[1]);
701 }
702 
703 /* This function is designed as faster analog to TrimColorClass.
704  * It assumes that tag_name is present in stylename! -HV
705  */
FastTrimColorClass(const char * tag_name,unsigned name_len,char * stylename,char ** pstylename_end,int * phcode)706 void FastTrimColorClass(const char *tag_name,
707 			unsigned name_len,
708 			char *stylename,
709 			char **pstylename_end,	/*will be modified */
710 			int *phcode)	/*will be modified */
711 {
712     char *tag_start = *pstylename_end;
713     BOOLEAN found = FALSE;
714 
715     CTRACE2(TRACE_STYLE,
716 	    (tfp, "STYLE.fast-trim: [%s] from [%s]: ",
717 	     tag_name, stylename));
718     while (tag_start >= stylename) {
719 	for (; (tag_start >= stylename) && (*tag_start != ';'); --tag_start) ;
720 	if (!strncasecomp(tag_start + 1, tag_name, (int) name_len)) {
721 	    found = TRUE;
722 	    break;
723 	}
724 	--tag_start;
725     }
726     if (found) {
727 	*tag_start = '\0';
728 	*pstylename_end = tag_start;
729     }
730     CTRACE2(TRACE_STYLE, (tfp, found ? "success.\n" : "failed.\n"));
731     *phcode = hash_code(tag_start + 1);
732 }
733 
734 /* This is called each time lss styles are read. It will fill
735  * each element of 'cached_tag_styles' -HV
736  */
cache_tag_styles(void)737 void cache_tag_styles(void)
738 {
739     char buf[200];
740     int i;
741 
742     for (i = 0; i < HTML_ELEMENTS; ++i) {
743 	LYStrNCpy(buf, HTML_dtd.tags[i].name, sizeof(buf) - 1);
744 	LYLowerCase(buf);
745 	cached_tag_styles[i] = hash_code(buf);
746     }
747 }
748 
749 #define SIZEOF_CACHED_STYLES (unsigned) (cached_styles_rows * cached_styles_cols)
750 
RefCachedStyle(int y,int x)751 static unsigned *RefCachedStyle(int y, int x)
752 {
753     unsigned *result = 0;
754 
755     if (cached_styles_ptr == 0) {
756 	cached_styles_rows = display_lines;
757 	cached_styles_cols = LYcols;
758 	cached_styles_ptr = typecallocn(unsigned, SIZEOF_CACHED_STYLES);
759     }
760     if (y >= 0 &&
761 	x >= 0 &&
762 	y < cached_styles_rows &&
763 	x < cached_styles_cols) {
764 	result = cached_styles_ptr + (y * cached_styles_cols) + x;
765     }
766     return result;
767 }
768 
ValidCachedStyle(int y,int x)769 BOOL ValidCachedStyle(int y, int x)
770 {
771     return (BOOL) (RefCachedStyle(y, x) != 0);
772 }
773 
GetCachedStyle(int y,int x)774 unsigned GetCachedStyle(int y, int x)
775 {
776     unsigned value = 0;
777     unsigned *cache = RefCachedStyle(y, x);
778 
779     if (cache != 0) {
780 	value = *cache;
781     }
782     return value;
783 }
784 
SetCachedStyle(int y,int x,unsigned value)785 void SetCachedStyle(int y, int x, unsigned value)
786 {
787     unsigned *cache = RefCachedStyle(y, x);
788 
789     if (cache != 0) {
790 	*cache = value;
791     }
792 }
793 
ResetCachedStyles(void)794 void ResetCachedStyles(void)
795 {
796     if (cached_styles_ptr != NULL) {
797 	memset(cached_styles_ptr, 0, sizeof(unsigned) * SIZEOF_CACHED_STYLES);
798     }
799 }
800 
FreeCachedStyles(void)801 void FreeCachedStyles(void)
802 {
803     if (cached_styles_ptr != NULL) {
804 	FREE(cached_styles_ptr);
805 	cached_styles_rows = 0;
806 	cached_styles_cols = 0;
807     }
808 }
809 
810 /*
811  * Recompute the pairs associated with the color style.
812  */
update_color_style(void)813 void update_color_style(void)
814 {
815     CTRACE((tfp, "update_color_style %p\n", lss_styles));
816     memset(our_pairs, 0, sizeof(our_pairs));
817     parse_userstyles();
818 }
819 
820 #endif /* USE_COLOR_STYLE */
821