1 /* `dir', `vdir' and `ls' directory listing programs for GNU.
2    Copyright (C) 85, 88, 90, 91, 1995-2004 Free Software Foundation, Inc.
3 
4    lscolors.c -- Taken from ls.c and modified for viewglob's purposes.
5    Copyright (C) 2004, 2005 Stephen Bach
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2, or (at your option)
10    any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software Foundation,
19    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
20 
21 /* Written by Richard Stallman and David MacKenzie.  */
22 
23 /* Color support by Peter Anvin <Peter.Anvin@linux.org> and Dennis
24    Flaherty <dennisf@denix.elk.miles.com> based on original patches by
25    Greg Lee <lee@uhunix.uhcc.hawaii.edu>.  */
26 
27 
28 #include "common.h"
29 #include "lscolors.h"
30 #include <string.h>
31 #include <pango/pango.h>
32 #include <gdk/gdk.h>
33 #include <math.h>
34 
35 /* Null is a valid character in a color indicator (think about Epson
36    printers, for example) so we have to use a length/buffer string
37    type.  */
38 struct bin_str {
39 	size_t len;				/* Number of bytes */
40 	const char *string;		/* Pointer to the same */
41 	GString* gstr;			/* Progress. */
42 };
43 
44 struct color_ext_type
45   {
46     struct bin_str ext;		/* The extension we're looking for. */
47     struct bin_str seq;		/* The sequence to output when we do. */
48 	TermTextAttr tta;       /* The sequence in TermTextAttr form. */
49     struct color_ext_type *next;	/* Next in list */
50   };
51 
52 
53 static void create_termtextattrs(void);
54 static void create_pangoattrlists(gint size_modifier);
55 static void termtextattr_init(TermTextAttr* tta);
56 static void termtextattr_copy(TermTextAttr* dest, TermTextAttr* src);
57 static void termtextattr_check_reverse(TermTextAttr* tta);
58 static gboolean are_equal(TermTextAttr* a, TermTextAttr* b);
59 static TermTextAttr* scan_types_for_equivalency(TermTextAttr* tta);
60 static TermTextAttr* scan_exts_for_equivalency(TermTextAttr* tta);
61 static void parse_codes(struct bin_str* s, TermTextAttr* attr);
62 static PangoAttrList* create_pango_list(TermTextAttr* tta, gint size_modifier);
63 
64 /* Terminal colours map to the following. */
65 
66 static GdkColor map[] = {
67 	/* These work best on a light background */
68 	{ 0, 0x0000, 0x0000, 0x0000 }, /* TCC_NONE (not used) */
69 	{ 0, 0x0000, 0x0000, 0x0000 }, /* TCC_BLACK */
70 	{ 0, 0xc1c1, 0x1111, 0x2525 }, /* TCC_RED */
71 	{ 0, 0x5050, 0x8888, 0x1e1e }, /* TCC_GREEN */
72 	{ 0, 0xc4c4, 0xb4b4, 0x0000 }, /* TCC_YELLOW */
73 	{ 0, 0x1616, 0x6262, 0xa2a2 }, /* TCC_BLUE */
74 	{ 0, 0xefef, 0x7070, 0x9a9a }, /* TCC_MAGENTA */
75 	{ 0, 0x2c2c, 0xa3a3, 0xa4a4 }, /* TCC_CYAN */
76 	{ 0, 0xffff, 0xffff, 0xffff }, /* TCC_WHITE */
77 #if 0
78 	/* These work best on a black background */
79 	{ 0, 0x0000, 0x0000, 0x0000 }, /* TCC_NONE (not used) */
80 	{ 0, 0x0000, 0x0000, 0x0000 }, /* TCC_BLACK */
81 	{ 0, 0x9e9e, 0x1818, 0x2828 }, /* TCC_RED */
82 	{ 0, 0xaeae, 0xcece, 0x9191 }, /* TCC_GREEN */
83 	{ 0, 0xffff, 0xf7f7, 0x9696 }, /* TCC_YELLOW */
84 	{ 0, 0x4141, 0x8686, 0xbebe }, /* TCC_BLUE */
85 	{ 0, 0x9696, 0x3c3c, 0x5959 }, /* TCC_MAGENTA */
86 	{ 0, 0x7171, 0xbebe, 0xbebe }, /* TCC_CYAN */
87 	{ 0, 0xffff, 0xffff, 0xffff }, /* TCC_WHITE */
88 #endif
89 };
90 
91 
92 /* Buffer for color sequences */
93 static char *color_buf;
94 
95 static TermTextAttr type_ttas[FT_COUNT];
96 
97 /* Reorganized these to correspond with FileType in file-types.h. */
98 static const char *const indicator_name[]= {
99 	"fi", "ex", "di", "bd", "cd", "pi", "so", "ln", "lc",
100     "rc", "ec", "no", "mi", "or", "do", NULL
101 };
102 
103 /* Reorganized to correspond with FileType in file-types.h. */
104 #define LEN_STR_PAIR(s) sizeof (s) - 1, s, NULL
105 #define COLOR_INDICATOR_SIZE 15
106 static struct bin_str color_indicator[] = {
107 	{ LEN_STR_PAIR ("0") },         /* fi: File: default */
108 	{ LEN_STR_PAIR ("01;32") },     /* ex: Executable: bright green */
109 	{ LEN_STR_PAIR ("01;34") },     /* di: Directory: bright blue */
110 	{ LEN_STR_PAIR ("01;33") },     /* bd: Block device: bright yellow */
111 	{ LEN_STR_PAIR ("01;33") },     /* cd: Char device: bright yellow */
112 	{ LEN_STR_PAIR ("33") },        /* pi: Pipe: yellow/brown */
113 	{ LEN_STR_PAIR ("01;35") },     /* so: Socket: bright magenta */
114 	{ LEN_STR_PAIR ("01;36") },     /* ln: Symlink: bright cyan */
115 	{ LEN_STR_PAIR ("\033[") },     /* lc: Left of color sequence */
116 	{ LEN_STR_PAIR ("m") },         /* rc: Right of color sequence */
117 	{ 0, NULL },                    /* ec: End color (replaces lc+no+rc) */
118 	{ LEN_STR_PAIR ("0") },         /* no: Normal */
119 	{ 0, NULL },                    /* mi: Missing file: undefined */
120 	{ 0, NULL },                    /* or: Orphanned symlink: undefined */
121 	{ LEN_STR_PAIR ("01;35") }      /* do: Door: bright magenta */
122 };
123 
124 
125 /* FIXME: comment  */
126 static struct color_ext_type *color_ext_list = NULL;
127 
128 /* Nonzero means use colors to mark types.  Also define the different
129    colors as well as the stuff for the LS_COLORS environment variable.
130    The LS_COLORS variable is now in a termcap-like format.  */
131 static int print_with_color;
132 
133 static gboolean
get_funky_string(char ** dest,const char ** src,gboolean equals_end,size_t * output_count)134 get_funky_string (char **dest, const char **src, gboolean equals_end,
135 		  size_t *output_count)
136 {
137   int num;			/* For numerical codes */
138   size_t count;			/* Something to count with */
139   enum {
140     ST_GND, ST_BACKSLASH, ST_OCTAL, ST_HEX, ST_CARET, ST_END, ST_ERROR
141   } state;
142   const char *p;
143   char *q;
144 
145   p = *src;			/* We don't want to double-indirect */
146   q = *dest;			/* the whole darn time.  */
147 
148   count = 0;			/* No characters counted in yet.  */
149   num = 0;
150 
151   state = ST_GND;		/* Start in ground state.  */
152   while (state < ST_END)
153     {
154       switch (state)
155 	{
156 	case ST_GND:		/* Ground state (no escapes) */
157 	  switch (*p)
158 	    {
159 	    case ':':
160 	    case '\0':
161 	      state = ST_END;	/* End of string */
162 	      break;
163 	    case '\\':
164 	      state = ST_BACKSLASH; /* Backslash scape sequence */
165 	      ++p;
166 	      break;
167 	    case '^':
168 	      state = ST_CARET; /* Caret escape */
169 	      ++p;
170 	      break;
171 	    case '=':
172 	      if (equals_end)
173 		{
174 		  state = ST_END; /* End */
175 		  break;
176 		}
177 	      /* else fall through */
178 	    default:
179 	      *(q++) = *(p++);
180 	      ++count;
181 	      break;
182 	    }
183 	  break;
184 
185 	case ST_BACKSLASH:	/* Backslash escaped character */
186 	  switch (*p)
187 	    {
188 	    case '0':
189 	    case '1':
190 	    case '2':
191 	    case '3':
192 	    case '4':
193 	    case '5':
194 	    case '6':
195 	    case '7':
196 	      state = ST_OCTAL;	/* Octal sequence */
197 	      num = *p - '0';
198 	      break;
199 	    case 'x':
200 	    case 'X':
201 	      state = ST_HEX;	/* Hex sequence */
202 	      num = 0;
203 	      break;
204 	    case 'a':		/* Bell */
205 	      num = 7;		/* Not all C compilers know what \a means */
206 	      break;
207 	    case 'b':		/* Backspace */
208 	      num = '\b';
209 	      break;
210 	    case 'e':		/* Escape */
211 	      num = 27;
212 	      break;
213 	    case 'f':		/* Form feed */
214 	      num = '\f';
215 	      break;
216 	    case 'n':		/* Newline */
217 	      num = '\n';
218 	      break;
219 	    case 'r':		/* Carriage return */
220 	      num = '\r';
221 	      break;
222 	    case 't':		/* Tab */
223 	      num = '\t';
224 	      break;
225 	    case 'v':		/* Vtab */
226 	      num = '\v';
227 	      break;
228 	    case '?':		/* Delete */
229               num = 127;
230 	      break;
231 	    case '_':		/* Space */
232 	      num = ' ';
233 	      break;
234 	    case '\0':		/* End of string */
235 	      state = ST_ERROR;	/* Error! */
236 	      break;
237 	    default:		/* Escaped character like \ ^ : = */
238 	      num = *p;
239 	      break;
240 	    }
241 	  if (state == ST_BACKSLASH)
242 	    {
243 	      *(q++) = num;
244 	      ++count;
245 	      state = ST_GND;
246 	    }
247 	  ++p;
248 	  break;
249 
250 	case ST_OCTAL:		/* Octal sequence */
251 	  if (*p < '0' || *p > '7')
252 	    {
253 	      *(q++) = num;
254 	      ++count;
255 	      state = ST_GND;
256 	    }
257 	  else
258 	    num = (num << 3) + (*(p++) - '0');
259 	  break;
260 
261 	case ST_HEX:		/* Hex sequence */
262 	  switch (*p)
263 	    {
264 	    case '0':
265 	    case '1':
266 	    case '2':
267 	    case '3':
268 	    case '4':
269 	    case '5':
270 	    case '6':
271 	    case '7':
272 	    case '8':
273 	    case '9':
274 	      num = (num << 4) + (*(p++) - '0');
275 	      break;
276 	    case 'a':
277 	    case 'b':
278 	    case 'c':
279 	    case 'd':
280 	    case 'e':
281 	    case 'f':
282 	      num = (num << 4) + (*(p++) - 'a') + 10;
283 	      break;
284 	    case 'A':
285 	    case 'B':
286 	    case 'C':
287 	    case 'D':
288 	    case 'E':
289 	    case 'F':
290 	      num = (num << 4) + (*(p++) - 'A') + 10;
291 	      break;
292 	    default:
293 	      *(q++) = num;
294 	      ++count;
295 	      state = ST_GND;
296 	      break;
297 	    }
298 	  break;
299 
300 	case ST_CARET:		/* Caret escape */
301 	  state = ST_GND;	/* Should be the next state... */
302 	  if (*p >= '@' && *p <= '~')
303 	    {
304 	      *(q++) = *(p++) & 037;
305 	      ++count;
306 	    }
307 	  else if (*p == '?')
308 	    {
309 	      *(q++) = 127;
310 	      ++count;
311 	    }
312 	  else
313 	    state = ST_ERROR;
314 	  break;
315 
316 	default:
317 	  abort ();
318 	}
319     }
320 
321   *dest = q;
322   *src = p;
323   *output_count = count;
324 
325   return state != ST_ERROR;
326 }
327 
328 void
parse_ls_colors(gint size_modifier)329 parse_ls_colors (gint size_modifier)
330 {
331   const char *p;		/* Pointer to character being parsed */
332   char *buf;			/* color_buf buffer pointer */
333   int state;			/* State of parser */
334   int ind_no;			/* Indicator number */
335   char label[3];		/* Indicator label */
336   struct color_ext_type *ext;	/* Extension we are working on */
337 
338   if ((p = getenv ("LS_COLORS")) == NULL || *p == '\0')
339     return;
340 
341   ext = NULL;
342   strcpy (label, "??");
343 
344   /* This is an overly conservative estimate, but any possible
345      LS_COLORS string will *not* generate a color_buf longer than
346      itself, so it is a safe way of allocating a buffer in
347      advance.  */
348   buf = color_buf = g_strdup (p);
349 
350   state = 1;
351   while (state > 0)
352     {
353       switch (state)
354 	{
355 	case 1:		/* First label character */
356 	  switch (*p)
357 	    {
358 	    case ':':
359 	      ++p;
360 	      break;
361 
362 	    case '*':
363 	      /* Allocate new extension block and add to head of
364 		 linked list (this way a later definition will
365 		 override an earlier one, which can be useful for
366 		 having terminal-specific defs override global).  */
367 
368 	      ext = g_new(struct color_ext_type, 1);
369 	      ext->next = color_ext_list;
370 	      color_ext_list = ext;
371 
372 	      ++p;
373 	      ext->ext.string = buf;
374 
375 	      state = (get_funky_string (&buf, &p, TRUE, &ext->ext.len)
376 		       ? 4 : -1);
377 	      break;
378 
379 	    case '\0':
380 	      state = 0;	/* Done! */
381 	      break;
382 
383 	    default:	/* Assume it is file type label */
384 	      label[0] = *(p++);
385 	      state = 2;
386 	      break;
387 	    }
388 	  break;
389 
390 	case 2:		/* Second label character */
391 	  if (*p)
392 	    {
393 	      label[1] = *(p++);
394 	      state = 3;
395 	    }
396 	  else
397 	    state = -1;	/* Error */
398 	  break;
399 
400 	case 3:		/* Equal sign after indicator label */
401 	  state = -1;	/* Assume failure... */
402 	  if (*(p++) == '=')/* It *should* be... */
403 	    {
404 	      for (ind_no = 0; indicator_name[ind_no] != NULL; ++ind_no)
405 		{
406 		  if (STREQ (label, indicator_name[ind_no]))
407 		    {
408 		      color_indicator[ind_no].string = buf;
409 		      state = (get_funky_string (&buf, &p, FALSE,
410 						 &color_indicator[ind_no].len)
411 			       ? 1 : -1);
412 		      break;
413 		    }
414 		}
415 	      if (state == -1)
416 		g_printerr("gviewglob: Unrecognized LS_COLORS prefix: \"%s\".\n", label);
417 	    }
418 	 break;
419 
420 	case 4:		/* Equal sign after *.ext */
421 	  if (*(p++) == '=')
422 	    {
423 	      ext->seq.string = buf;
424 	      state = (get_funky_string (&buf, &p, FALSE, &ext->seq.len)
425 		       ? 1 : -1);
426 	    }
427 	  else
428 	    state = -1;
429 	  break;
430 	}
431     }
432 
433   if (state < 0)
434     {
435       struct color_ext_type *e;
436       struct color_ext_type *e2;
437 
438       g_printerr("gviewglob: Unparsable value for LS_COLORS environment variable.\n");
439       g_free (color_buf);
440       for (e = color_ext_list; e != NULL; /* empty */)
441 	{
442 	  e2 = e;
443 	  e = e->next;
444 	  g_free (e2);
445 	}
446       print_with_color = 0;
447     }
448 
449   /* Dealing with bin_str is very tiresome.  Convert
450 	 to useable GStrings. */
451   int i;
452   struct color_ext_type* iter;
453   for (i = 0; i < COLOR_INDICATOR_SIZE; i++) {
454 	  color_indicator[i].gstr = g_string_new_len(
455 			  color_indicator[i].string,
456 			  color_indicator[i].len);
457   }
458   for (iter = color_ext_list; iter; iter = iter->next) {
459 	  iter->ext.gstr = g_string_new_len(iter->ext.string, iter->ext.len);
460 	  iter->seq.gstr = g_string_new_len(iter->seq.string, iter->seq.len);
461   }
462 
463   create_termtextattrs();
464   create_pangoattrlists(size_modifier);
465 }
466 
467 
468 /* Create TermTextAttrs from the terminal colouring sequences. */
create_termtextattrs(void)469 static void create_termtextattrs(void) {
470 
471 	TermTextAttr normal;
472 	int i;
473 
474 	/* Initialize "no", just in case it's not included
475 	   in LS_COLORS. */
476 	termtextattr_init(&normal);
477 
478 	parse_codes(&color_indicator[11], &normal);  /* "no" */
479 
480 	/* All file types inherit from "no". */
481 	for (i = 0; i < FT_COUNT; i++) {
482 		termtextattr_copy(&type_ttas[i], &normal);
483 		parse_codes(&color_indicator[i], &type_ttas[i]);
484 	}
485 
486 	/* Now do the extensions. */
487 	struct color_ext_type* iter;
488 	for (iter = color_ext_list; iter; iter = iter->next) {
489 		/* Extensions only apply to regular files, apparently. */
490 		termtextattr_copy(&iter->tta, &type_ttas[FT_REGULAR]);
491 		parse_codes(&iter->seq, &iter->tta);
492 		termtextattr_check_reverse(&iter->tta);
493 	}
494 
495 	/* It's safe to reverse the file types now (if necessary). */
496 	for (i = 0; i < FT_COUNT; i++)
497 		termtextattr_check_reverse(&type_ttas[i]);
498 }
499 
500 
501 /* Create PangoAttrLists from the TermTextAttrs. */
create_pangoattrlists(gint size_modifier)502 static void create_pangoattrlists(gint size_modifier) {
503 	TermTextAttr* match;
504 	struct color_ext_type* iter;
505 	int i;
506 
507 	for (i = 0; i < FT_COUNT; i++) {
508 		/* It's safe to reverse the file types now (if necessary). */
509 		termtextattr_check_reverse(&type_ttas[i]);
510 		match = scan_types_for_equivalency(&type_ttas[i]);
511 		if (match)
512 			type_ttas[i].p_list = match->p_list;
513 		else
514 			type_ttas[i].p_list = create_pango_list(&type_ttas[i], size_modifier);
515 	}
516 	for (iter = color_ext_list; iter; iter = iter->next) {
517 		match = scan_exts_for_equivalency(&iter->tta);
518 		if (match)
519 			iter->tta.p_list = match->p_list;
520 		else
521 			iter->tta.p_list = create_pango_list(&iter->tta, size_modifier);
522 	}
523 }
524 
525 
526 /* If the given TermTextAttr has TAC_REVERSE, reverse its colors. */
termtextattr_check_reverse(TermTextAttr * tta)527 static void termtextattr_check_reverse(TermTextAttr* tta) {
528 
529 	if (tta->attr & TAC_REVERSE) {
530 		/* Switch foreground with background. */
531 		enum term_color_code temp;
532 		temp = tta->fg;
533 		tta->fg = tta->bg;
534 		tta->bg = temp;
535 
536 		/* Remove TAC_REVERSE now that it's been applied. */
537 		tta->attr &= (~TAC_REVERSE);
538 	}
539 }
540 
541 
542 /* Parse the code sequences in s and convert to a TermTextAttr. */
parse_codes(struct bin_str * s,TermTextAttr * tta)543 static void parse_codes(struct bin_str* s, TermTextAttr* tta) {
544 
545 	ptrdiff_t pos = 0;
546 	char* endptr = NULL;
547 	long code;
548 
549 	while (pos < s->len) {
550 		code = strtol(s->gstr->str + pos, &endptr, 10);
551 		if (s->gstr->str + pos == endptr) {
552 			/* Did not convert any characters. */
553 			if (pos + 1 == s->len) {
554 				/* We're at the end of the string. */
555 				break;
556 			}
557 			else {
558 				/* It's just a separator or something.  Skip it. */
559 				pos++;
560 			}
561 		}
562 		else {
563 			/* Got a code -- let's see what it is. */
564 			pos = endptr - s->gstr->str;
565 			switch (code) {
566 				case 0:
567 					tta->attr = 0;
568 					break;
569 				case 1:
570 					tta->attr |= TAC_BOLD;
571 					break;
572 				case 4:
573 					tta->attr |= TAC_UNDERSCORE;
574 					break;
575 				case 7:
576 					tta->attr |= TAC_REVERSE;
577 					break;
578 				case 30:
579 					tta->fg = TCC_BLACK;
580 					break;
581 				case 31:
582 					tta->fg = TCC_RED;
583 					break;
584 				case 32:
585 					tta->fg = TCC_GREEN;
586 					break;
587 				case 33:
588 					tta->fg = TCC_YELLOW;
589 					break;
590 				case 34:
591 					tta->fg = TCC_BLUE;
592 					break;
593 				case 35:
594 					tta->fg = TCC_MAGENTA;
595 					break;
596 				case 36:
597 					tta->fg = TCC_CYAN;
598 					break;
599 				case 37:
600 					tta->fg = TCC_WHITE;
601 					break;
602 				case 40:
603 					tta->bg = TCC_BLACK;
604 					break;
605 				case 41:
606 					tta->bg = TCC_RED;
607 					break;
608 				case 42:
609 					tta->bg = TCC_GREEN;
610 					break;
611 				case 43:
612 					tta->bg = TCC_YELLOW;
613 					break;
614 				case 44:
615 					tta->bg = TCC_BLUE;
616 					break;
617 				case 45:
618 					tta->bg = TCC_MAGENTA;
619 					break;
620 				case 46:
621 					tta->bg = TCC_CYAN;
622 					break;
623 				case 47:
624 					tta->bg = TCC_WHITE;
625 					break;
626 			}
627 		}
628 	}
629 }
630 
631 
632 /* Initialize the given TermTextAttr. */
termtextattr_init(TermTextAttr * tta)633 static void termtextattr_init(TermTextAttr* tta) {
634 	tta->fg = TCC_NONE;
635 	tta->bg = TCC_NONE;
636 	tta->attr = 0;
637 	tta->p_list = NULL;
638 }
639 
640 
641 /* Copy the attribute fields in src to the fields in dest. */
termtextattr_copy(TermTextAttr * dest,TermTextAttr * src)642 static void termtextattr_copy(TermTextAttr* dest, TermTextAttr* src) {
643 	dest->fg = src->fg;
644 	dest->bg = src->bg;
645 	dest->attr = src->attr;
646 	dest->p_list = NULL;     /* Don't copy this one. */
647 }
648 
649 
650 /* Scan through the type_ttas array for a matching tta. */
scan_types_for_equivalency(TermTextAttr * tta)651 static TermTextAttr* scan_types_for_equivalency(TermTextAttr* tta) {
652 	int i;
653 	TermTextAttr* retval = NULL;
654 
655 	for (i = 0; i < FT_COUNT; i++) {
656 		if (tta == &type_ttas[i]) {
657 			/* We've reached ourselves, so we're done. */
658 			break;
659 		}
660 		if (are_equal(tta, &type_ttas[i])) {
661 			retval = &type_ttas[i];
662 			break;
663 		}
664 	}
665 	return retval;
666 }
667 
668 
669 /* Scan throught the color_ext_list list for a matching tta. */
scan_exts_for_equivalency(TermTextAttr * tta)670 static TermTextAttr* scan_exts_for_equivalency(TermTextAttr* tta) {
671 	struct color_ext_type* iter;
672 	TermTextAttr* retval = NULL;
673 
674 	for (iter = color_ext_list; iter; iter = iter->next) {
675 		if (&iter->tta == tta) {
676 			/* We've reached ourselves, so we're done. */
677 			break;
678 		}
679 		else if (are_equal(tta, &iter->tta)) {
680 			retval = &iter->tta;
681 			break;
682 		}
683 	}
684 	return retval;
685 }
686 
687 
688 /* Compares the given TermTextAttrs for equivalency. */
are_equal(TermTextAttr * a,TermTextAttr * b)689 static gboolean are_equal(TermTextAttr* a, TermTextAttr* b) {
690 	gboolean is_same = TRUE;
691 	is_same &= a->fg == b->fg;
692 	is_same &= a->bg == b->bg;
693 	is_same &= a->attr == b->attr;
694 
695 	return is_same;
696 }
697 
698 
699 /* Convert the given TermTextAttr into a PangoAttrList. */
create_pango_list(TermTextAttr * tta,gint size_modifier)700 static PangoAttrList* create_pango_list(TermTextAttr* tta, gint size_modifier) {
701 
702 	PangoAttribute* p_attr;
703 	PangoAttrList* p_list = pango_attr_list_new();
704 	gboolean list_set = FALSE;
705 	gdouble scale_factor;
706 
707 	/* First set the font size. */
708 	if (size_modifier != 0) {
709 		if (size_modifier > 0)
710 			scale_factor = pow(PANGO_SCALE_LARGE, size_modifier);
711 		else
712 			scale_factor = pow(PANGO_SCALE_SMALL, -size_modifier);
713 		p_attr = pango_attr_scale_new(scale_factor);
714 		p_attr->start_index = 0;
715 		p_attr->end_index = G_MAXINT;
716 		pango_attr_list_insert(p_list, p_attr);
717 		list_set = TRUE;
718 	}
719 
720 	/* Foreground colour is not set through Pango so that
721 	   its color can change when active or selected
722 	   (see label_set_attributes()). */
723 
724 	/* Background colour */
725 	if (tta->bg > TCC_NONE && tta->bg <= TCC_WHITE) {
726 		p_attr = pango_attr_background_new(
727 				map[tta->bg].red,
728 				map[tta->bg].green,
729 				map[tta->bg].blue);
730 		p_attr->start_index = 0;
731 		p_attr->end_index = G_MAXINT;
732 		pango_attr_list_insert(p_list, p_attr);
733 		list_set = TRUE;
734 	}
735 
736 
737 	/* Bold */
738 	if (tta->attr & TAC_BOLD) {
739 		p_attr = pango_attr_weight_new(PANGO_WEIGHT_HEAVY);
740 		p_attr->start_index = 0;
741 		p_attr->end_index = G_MAXINT;
742 		pango_attr_list_insert(p_list, p_attr);
743 		list_set = TRUE;
744 	}
745 
746 	/* Underscore */
747 	if (tta->attr & TAC_UNDERSCORE) {
748 		p_attr = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
749 		p_attr->start_index = 0;
750 		p_attr->end_index = G_MAXINT;
751 		pango_attr_list_insert(p_list, p_attr);
752 		list_set = TRUE;
753 	}
754 
755 	if (list_set) {
756 		/* Probably don't need to do this, but it doesn't hurt since
757 		   the attribute lists are valid through the life of the program. */
758 		pango_attr_list_ref(p_list);
759 	}
760 	else {
761 		pango_attr_list_unref(p_list);
762 		p_list = NULL;
763 	}
764 
765 	return p_list;
766 }
767 
768 
769 /* Get a PangoAttrList for this label, based on its name and type.  */
label_set_attributes(gchar * name,FileType type,GtkLabel * label)770 void label_set_attributes(gchar* name, FileType type, GtkLabel* label) {
771 
772 	PangoAttrList* p_list;
773 	TermTextAttr* tta;
774 
775 	tta = &type_ttas[type];
776 
777 	p_list = tta->p_list;
778 	if (type == FT_REGULAR) {
779 		struct color_ext_type* iter;
780 		for (iter = color_ext_list; iter; iter = iter->next) {
781 			if (g_str_has_suffix(name, iter->ext.gstr->str)) {
782 				tta = &iter->tta;
783 				p_list = tta->p_list;
784 				break;
785 			}
786 		}
787 	}
788 
789 	if (p_list)
790 		gtk_label_set_attributes(label, p_list);
791 
792 	/* Foreground colour */
793 	if (tta->fg > TCC_NONE && tta->fg <= TCC_WHITE) {
794 		gtk_widget_modify_fg(
795 				GTK_WIDGET(label), GTK_STATE_NORMAL,&map[tta->fg]);
796 		/*gtk_widget_modify_fg(GTK_WIDGET(label), GTK_STATE_ACTIVE, &color);*/
797 	}
798 }
799 
800 
set_color(enum term_color_code code,GdkColor * color)801 void set_color(enum term_color_code code, GdkColor* color) {
802 	map[code].red = color->red;
803 	map[code].green = color->green;
804 	map[code].blue = color->blue;
805 }
806 
807