• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/gettext-0.17/gettext-tools/gnulib-lib/
1/* Output stream for attributed text, producing ANSI escape sequences.
2   Copyright (C) 2006-2007 Free Software Foundation, Inc.
3   Written by Bruno Haible <bruno@clisp.org>, 2006.
4
5   This program is free software: you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 3 of the License, or
8   (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18#include <config.h>
19
20/* Specification.  */
21#include "term-ostream.h"
22
23#include <assert.h>
24#include <errno.h>
25#include <signal.h>
26#include <stdbool.h>
27#include <stdlib.h>
28#include <string.h>
29
30#include "error.h"
31#include "fatal-signal.h"
32#include "full-write.h"
33#include "terminfo.h"
34#include "xalloc.h"
35#include "xsize.h"
36#include "gettext.h"
37
38#define _(str) gettext (str)
39
40#if HAVE_TPARAM
41/* GNU termcap's tparam() function requires a buffer argument.  Make it so
42   large that there is no risk that tparam() needs to call malloc().  */
43static char tparambuf[100];
44/* Define tparm in terms of tparam.  In the scope of this file, it is called
45   with at most one argument after the string.  */
46# define tparm(str, arg1) \
47  tparam (str, tparambuf, sizeof (tparambuf), arg1)
48#endif
49
50#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
51
52
53/* =========================== Color primitives =========================== */
54
55/* A color in RGB format.  */
56typedef struct
57{
58  unsigned int red   : 8; /* range 0..255 */
59  unsigned int green : 8; /* range 0..255 */
60  unsigned int blue  : 8; /* range 0..255 */
61} rgb_t;
62
63/* A color in HSV (a.k.a. HSB) format.  */
64typedef struct
65{
66  float hue;        /* normalized to interval [0,6) */
67  float saturation; /* normalized to interval [0,1] */
68  float brightness; /* a.k.a. value, normalized to interval [0,1] */
69} hsv_t;
70
71/* Conversion of a color in RGB to HSV format.  */
72static void
73rgb_to_hsv (rgb_t c, hsv_t *result)
74{
75  unsigned int r = c.red;
76  unsigned int g = c.green;
77  unsigned int b = c.blue;
78
79  if (r > g)
80    {
81      if (b > r)
82	{
83	  /* b > r > g, so max = b, min = g */
84	  result->hue = 4.0f + (float) (r - g) / (float) (b - g);
85	  result->saturation = 1.0f - (float) g / (float) b;
86	  result->brightness = (float) b / 255.0f;
87	}
88      else if (b <= g)
89	{
90	  /* r > g >= b, so max = r, min = b */
91	  result->hue = 0.0f + (float) (g - b) / (float) (r - b);
92	  result->saturation = 1.0f - (float) b / (float) r;
93	  result->brightness = (float) r / 255.0f;
94	}
95      else
96	{
97	  /* r >= b > g, so max = r, min = g */
98	  result->hue = 6.0f - (float) (b - g) / (float) (r - g);
99	  result->saturation = 1.0f - (float) g / (float) r;
100	  result->brightness = (float) r / 255.0f;
101	}
102    }
103  else
104    {
105      if (b > g)
106	{
107	  /* b > g >= r, so max = b, min = r */
108	  result->hue = 4.0f - (float) (g - r) / (float) (b - r);
109	  result->saturation = 1.0f - (float) r / (float) b;
110	  result->brightness = (float) b / 255.0f;
111	}
112      else if (b < r)
113	{
114	  /* g >= r > b, so max = g, min = b */
115	  result->hue = 2.0f - (float) (r - b) / (float) (g - b);
116	  result->saturation = 1.0f - (float) b / (float) g;
117	  result->brightness = (float) g / 255.0f;
118	}
119      else if (g > r)
120	{
121	  /* g >= b >= r, g > r, so max = g, min = r */
122	  result->hue = 2.0f + (float) (b - r) / (float) (g - r);
123	  result->saturation = 1.0f - (float) r / (float) g;
124	  result->brightness = (float) g / 255.0f;
125	}
126      else
127	{
128	  /* r = g = b.  A grey color.  */
129	  result->hue = 0; /* arbitrary */
130	  result->saturation = 0;
131	  result->brightness = (float) r / 255.0f;
132	}
133    }
134}
135
136/* Square of distance of two colors.  */
137static float
138color_distance (const hsv_t *color1, const hsv_t *color2)
139{
140#if 0
141  /* Formula taken from "John Smith: Color Similarity",
142       http://www.ctr.columbia.edu/~jrsmith/html/pubs/acmmm96/node8.html.  */
143  float angle1 = color1->hue * 1.04719755f; /* normalize to [0,2��] */
144  float angle2 = color2->hue * 1.04719755f; /* normalize to [0,2��] */
145  float delta_x = color1->saturation * cosf (angle1)
146		  - color2->saturation * cosf (angle2);
147  float delta_y = color1->saturation * sinf (angle1)
148		  - color2->saturation * sinf (angle2);
149  float delta_v = color1->brightness
150		  - color2->brightness;
151
152  return delta_x * delta_x + delta_y * delta_y + delta_v * delta_v;
153#else
154  /* Formula that considers hue differences with more weight than saturation
155     or brightness differences, like the human eye does.  */
156  float delta_hue =
157    (color1->hue >= color2->hue
158     ? (color1->hue - color2->hue >= 3.0f
159	? 6.0f + color2->hue - color1->hue
160	: color1->hue - color2->hue)
161     : (color2->hue - color1->hue >= 3.0f
162	? 6.0f + color1->hue - color2->hue
163	: color2->hue - color1->hue));
164  float min_saturation =
165    (color1->saturation < color2->saturation
166     ? color1->saturation
167     : color2->saturation);
168  float delta_saturation = color1->saturation - color2->saturation;
169  float delta_brightness = color1->brightness - color2->brightness;
170
171  return delta_hue * delta_hue * min_saturation
172	 + delta_saturation * delta_saturation * 0.2f
173	 + delta_brightness * delta_brightness * 0.8f;
174#endif
175}
176
177/* Return the index of the color in a color table that is nearest to a given
178   color.  */
179static unsigned int
180nearest_color (rgb_t given, const rgb_t *table, unsigned int table_size)
181{
182  hsv_t given_hsv;
183  unsigned int best_index;
184  float best_distance;
185  unsigned int i;
186
187  assert (table_size > 0);
188
189  rgb_to_hsv (given, &given_hsv);
190
191  best_index = 0;
192  best_distance = 1000000.0f;
193  for (i = 0; i < table_size; i++)
194    {
195      hsv_t i_hsv;
196
197      rgb_to_hsv (table[i], &i_hsv);
198
199      /* Avoid converting a color to grey, or fading out a color too much.  */
200      if (i_hsv.saturation > given_hsv.saturation * 0.5f)
201	{
202	  float distance = color_distance (&given_hsv, &i_hsv);
203	  if (distance < best_distance)
204	    {
205	      best_index = i;
206	      best_distance = distance;
207	    }
208	}
209    }
210
211#if 0 /* Debugging code */
212  hsv_t best_hsv;
213  rgb_to_hsv (table[best_index], &best_hsv);
214  fprintf (stderr, "nearest: (%d,%d,%d) = (%f,%f,%f)\n    -> (%f,%f,%f) = (%d,%d,%d)\n",
215                   given.red, given.green, given.blue,
216                   (double)given_hsv.hue, (double)given_hsv.saturation, (double)given_hsv.brightness,
217                   (double)best_hsv.hue, (double)best_hsv.saturation, (double)best_hsv.brightness,
218                   table[best_index].red, table[best_index].green, table[best_index].blue);
219#endif
220
221  return best_index;
222}
223
224/* The luminance of a color.  This is the brightness of the color, as it
225   appears to the human eye.  This must be used in color to grey conversion.  */
226static float
227color_luminance (int r, int g, int b)
228{
229  /* Use the luminance model used by NTSC and JPEG.
230     Taken from http://www.fho-emden.de/~hoffmann/gray10012001.pdf .
231     No need to care about rounding errors leading to luminance > 1;
232     this cannot happen.  */
233  return (0.299f * r + 0.587f * g + 0.114f * b) / 255.0f;
234}
235
236
237/* ============================= Color models ============================= */
238
239/* The color model used by the terminal.  */
240typedef enum
241{
242  cm_monochrome,	/* No colors.  */
243  cm_common8,		/* Usual terminal with at least 8 colors.  */
244  cm_xterm8,		/* TERM=xterm, with 8 colors.  */
245  cm_xterm16,		/* TERM=xterm-16color, with 16 colors.  */
246  cm_xterm88,		/* TERM=xterm-88color, with 88 colors.  */
247  cm_xterm256		/* TERM=xterm-256color, with 256 colors.  */
248} colormodel_t;
249
250/* ----------------------- cm_monochrome color model ----------------------- */
251
252/* A non-default color index doesn't exist in this color model.  */
253static inline term_color_t
254rgb_to_color_monochrome ()
255{
256  return COLOR_DEFAULT;
257}
258
259/* ------------------------ cm_common8 color model ------------------------ */
260
261/* A non-default color index is in the range 0..7.
262                       RGB components
263   COLOR_BLACK         000
264   COLOR_BLUE          001
265   COLOR_GREEN         010
266   COLOR_CYAN          011
267   COLOR_RED           100
268   COLOR_MAGENTA       101
269   COLOR_YELLOW        110
270   COLOR_WHITE         111 */
271static const rgb_t colors_of_common8[8] =
272{
273  /* R    G    B        grey  index */
274  {   0,   0,   0 }, /* 0.000   0 */
275  {   0,   0, 255 },
276  {   0, 255,   0 },
277  {   0, 255, 255 },
278  { 255,   0,   0 },
279  { 255,   0, 255 },
280  { 255, 255,   0 },
281  { 255, 255, 255 }  /* 1.000   7 */
282};
283
284static inline term_color_t
285rgb_to_color_common8 (int r, int g, int b)
286{
287  rgb_t color;
288  hsv_t hsv;
289
290  color.red = r; color.green = g; color.blue = b;
291  rgb_to_hsv (color, &hsv);
292
293  if (hsv.saturation < 0.065f)
294    {
295      /* Greyscale approximation.  */
296      float luminance = color_luminance (r, g, b);
297      if (luminance < 0.500f)
298	return 0;
299      else
300	return 7;
301    }
302  else
303    /* Color approximation.  */
304    return nearest_color (color, colors_of_common8, 8);
305}
306
307/* Convert a cm_common8 color in RGB encoding to BGR encoding.
308   See the ncurses terminfo(5) manual page, section "Color Handling", for an
309   explanation why this is needed.  */
310static inline int
311color_bgr (term_color_t color)
312{
313  return ((color & 4) >> 2) | (color & 2) | ((color & 1) << 2);
314}
315
316/* ------------------------- cm_xterm8 color model ------------------------- */
317
318/* A non-default color index is in the range 0..7.
319                       BGR components
320   COLOR_BLACK         000
321   COLOR_RED           001
322   COLOR_GREEN         010
323   COLOR_YELLOW        011
324   COLOR_BLUE          100
325   COLOR_MAGENTA       101
326   COLOR_CYAN          110
327   COLOR_WHITE         111 */
328static const rgb_t colors_of_xterm8[8] =
329{
330  /* The real xterm's colors are dimmed; assume full-brightness instead.  */
331  /* R    G    B        grey  index */
332  {   0,   0,   0 }, /* 0.000   0 */
333  { 255,   0,   0 },
334  {   0, 255,   0 },
335  { 255, 255,   0 },
336  {   0,   0, 255 },
337  { 255,   0, 255 },
338  {   0, 255, 255 },
339  { 255, 255, 255 }  /* 1.000   7 */
340};
341
342static inline term_color_t
343rgb_to_color_xterm8 (int r, int g, int b)
344{
345  rgb_t color;
346  hsv_t hsv;
347
348  color.red = r; color.green = g; color.blue = b;
349  rgb_to_hsv (color, &hsv);
350
351  if (hsv.saturation < 0.065f)
352    {
353      /* Greyscale approximation.  */
354      float luminance = color_luminance (r, g, b);
355      if (luminance < 0.500f)
356	return 0;
357      else
358	return 7;
359    }
360  else
361    /* Color approximation.  */
362    return nearest_color (color, colors_of_xterm8, 8);
363}
364
365/* ------------------------ cm_xterm16 color model ------------------------ */
366
367/* A non-default color index is in the range 0..15.
368   The RGB values come from xterm's XTerm-col.ad.  */
369static const rgb_t colors_of_xterm16[16] =
370{
371  /* R    G    B        grey  index */
372  {   0,   0,   0 }, /* 0.000   0 */
373  { 205,   0,   0 },
374  {   0, 205,   0 },
375  { 205, 205,   0 },
376  {   0,   0, 205 },
377  { 205,   0, 205 },
378  {   0, 205, 205 },
379  { 229, 229, 229 }, /* 0.898   7 */
380  {  77,  77,  77 }, /* 0.302   8 */
381  { 255,   0,   0 },
382  {   0, 255,   0 },
383  { 255, 255,   0 },
384  {   0,   0, 255 },
385  { 255,   0, 255 },
386  {   0, 255, 255 },
387  { 255, 255, 255 }  /* 1.000  15 */
388};
389
390static inline term_color_t
391rgb_to_color_xterm16 (int r, int g, int b)
392{
393  rgb_t color;
394  hsv_t hsv;
395
396  color.red = r; color.green = g; color.blue = b;
397  rgb_to_hsv (color, &hsv);
398
399  if (hsv.saturation < 0.065f)
400    {
401      /* Greyscale approximation.  */
402      float luminance = color_luminance (r, g, b);
403      if (luminance < 0.151f)
404	return 0;
405      else if (luminance < 0.600f)
406	return 8;
407      else if (luminance < 0.949f)
408	return 7;
409      else
410	return 15;
411    }
412  else
413    /* Color approximation.  */
414    return nearest_color (color, colors_of_xterm16, 16);
415}
416
417/* ------------------------ cm_xterm88 color model ------------------------ */
418
419/* A non-default color index is in the range 0..87.
420   Colors 0..15 are the same as in the cm_xterm16 color model.
421   Colors 16..87 are defined in xterm's 88colres.h.  */
422
423static const rgb_t colors_of_xterm88[88] =
424{
425  /* R    G    B        grey  index */
426  {   0,   0,   0 }, /* 0.000   0 */
427  { 205,   0,   0 },
428  {   0, 205,   0 },
429  { 205, 205,   0 },
430  {   0,   0, 205 },
431  { 205,   0, 205 },
432  {   0, 205, 205 },
433  { 229, 229, 229 }, /* 0.898   7 */
434  {  77,  77,  77 }, /* 0.302   8 */
435  { 255,   0,   0 },
436  {   0, 255,   0 },
437  { 255, 255,   0 },
438  {   0,   0, 255 },
439  { 255,   0, 255 },
440  {   0, 255, 255 },
441  { 255, 255, 255 }, /* 1.000  15 */
442  {   0,   0,   0 }, /* 0.000  16 */
443  {   0,   0, 139 },
444  {   0,   0, 205 },
445  {   0,   0, 255 },
446  {   0, 139,   0 },
447  {   0, 139, 139 },
448  {   0, 139, 205 },
449  {   0, 139, 255 },
450  {   0, 205,   0 },
451  {   0, 205, 139 },
452  {   0, 205, 205 },
453  {   0, 205, 255 },
454  {   0, 255,   0 },
455  {   0, 255, 139 },
456  {   0, 255, 205 },
457  {   0, 255, 255 },
458  { 139,   0,   0 },
459  { 139,   0, 139 },
460  { 139,   0, 205 },
461  { 139,   0, 255 },
462  { 139, 139,   0 },
463  { 139, 139, 139 }, /* 0.545  37 */
464  { 139, 139, 205 },
465  { 139, 139, 255 },
466  { 139, 205,   0 },
467  { 139, 205, 139 },
468  { 139, 205, 205 },
469  { 139, 205, 255 },
470  { 139, 255,   0 },
471  { 139, 255, 139 },
472  { 139, 255, 205 },
473  { 139, 255, 255 },
474  { 205,   0,   0 },
475  { 205,   0, 139 },
476  { 205,   0, 205 },
477  { 205,   0, 255 },
478  { 205, 139,   0 },
479  { 205, 139, 139 },
480  { 205, 139, 205 },
481  { 205, 139, 255 },
482  { 205, 205,   0 },
483  { 205, 205, 139 },
484  { 205, 205, 205 }, /* 0.804  58 */
485  { 205, 205, 255 },
486  { 205, 255,   0 },
487  { 205, 255, 139 },
488  { 205, 255, 205 },
489  { 205, 255, 255 },
490  { 255,   0,   0 },
491  { 255,   0, 139 },
492  { 255,   0, 205 },
493  { 255,   0, 255 },
494  { 255, 139,   0 },
495  { 255, 139, 139 },
496  { 255, 139, 205 },
497  { 255, 139, 255 },
498  { 255, 205,   0 },
499  { 255, 205, 139 },
500  { 255, 205, 205 },
501  { 255, 205, 255 },
502  { 255, 255,   0 },
503  { 255, 255, 139 },
504  { 255, 255, 205 },
505  { 255, 255, 255 }, /* 1.000  79 */
506  {  46,  46,  46 }, /* 0.180  80 */
507  {  92,  92,  92 }, /* 0.361  81 */
508  { 115, 115, 115 }, /* 0.451  82 */
509  { 139, 139, 139 }, /* 0.545  83 */
510  { 162, 162, 162 }, /* 0.635  84 */
511  { 185, 185, 185 }, /* 0.725  85 */
512  { 208, 208, 208 }, /* 0.816  86 */
513  { 231, 231, 231 }  /* 0.906  87 */
514};
515
516static inline term_color_t
517rgb_to_color_xterm88 (int r, int g, int b)
518{
519  rgb_t color;
520  hsv_t hsv;
521
522  color.red = r; color.green = g; color.blue = b;
523  rgb_to_hsv (color, &hsv);
524
525  if (hsv.saturation < 0.065f)
526    {
527      /* Greyscale approximation.  */
528      float luminance = color_luminance (r, g, b);
529      if (luminance < 0.090f)
530	return 0;
531      else if (luminance < 0.241f)
532	return 80;
533      else if (luminance < 0.331f)
534	return 8;
535      else if (luminance < 0.406f)
536	return 81;
537      else if (luminance < 0.498f)
538	return 82;
539      else if (luminance < 0.585f)
540	return 37;
541      else if (luminance < 0.680f)
542	return 84;
543      else if (luminance < 0.764f)
544	return 85;
545      else if (luminance < 0.810f)
546	return 58;
547      else if (luminance < 0.857f)
548	return 86;
549      else if (luminance < 0.902f)
550	return 7;
551      else if (luminance < 0.953f)
552	return 87;
553      else
554	return 15;
555    }
556  else
557    /* Color approximation.  */
558    return nearest_color (color, colors_of_xterm88, 88);
559}
560
561/* ------------------------ cm_xterm256 color model ------------------------ */
562
563/* A non-default color index is in the range 0..255.
564   Colors 0..15 are the same as in the cm_xterm16 color model.
565   Colors 16..255 are defined in xterm's 256colres.h.  */
566
567static const rgb_t colors_of_xterm256[256] =
568{
569  /* R    G    B        grey  index */
570  {   0,   0,   0 }, /* 0.000   0 */
571  { 205,   0,   0 },
572  {   0, 205,   0 },
573  { 205, 205,   0 },
574  {   0,   0, 205 },
575  { 205,   0, 205 },
576  {   0, 205, 205 },
577  { 229, 229, 229 }, /* 0.898   7 */
578  {  77,  77,  77 }, /* 0.302   8 */
579  { 255,   0,   0 },
580  {   0, 255,   0 },
581  { 255, 255,   0 },
582  {   0,   0, 255 },
583  { 255,   0, 255 },
584  {   0, 255, 255 },
585  { 255, 255, 255 }, /* 1.000  15 */
586  {   0,   0,   0 }, /* 0.000  16 */
587  {   0,   0,  42 },
588  {   0,   0,  85 },
589  {   0,   0, 127 },
590  {   0,   0, 170 },
591  {   0,   0, 212 },
592  {   0,  42,   0 },
593  {   0,  42,  42 },
594  {   0,  42,  85 },
595  {   0,  42, 127 },
596  {   0,  42, 170 },
597  {   0,  42, 212 },
598  {   0,  85,   0 },
599  {   0,  85,  42 },
600  {   0,  85,  85 },
601  {   0,  85, 127 },
602  {   0,  85, 170 },
603  {   0,  85, 212 },
604  {   0, 127,   0 },
605  {   0, 127,  42 },
606  {   0, 127,  85 },
607  {   0, 127, 127 },
608  {   0, 127, 170 },
609  {   0, 127, 212 },
610  {   0, 170,   0 },
611  {   0, 170,  42 },
612  {   0, 170,  85 },
613  {   0, 170, 127 },
614  {   0, 170, 170 },
615  {   0, 170, 212 },
616  {   0, 212,   0 },
617  {   0, 212,  42 },
618  {   0, 212,  85 },
619  {   0, 212, 127 },
620  {   0, 212, 170 },
621  {   0, 212, 212 },
622  {  42,   0,   0 },
623  {  42,   0,  42 },
624  {  42,   0,  85 },
625  {  42,   0, 127 },
626  {  42,   0, 170 },
627  {  42,   0, 212 },
628  {  42,  42,   0 },
629  {  42,  42,  42 }, /* 0.165  59 */
630  {  42,  42,  85 },
631  {  42,  42, 127 },
632  {  42,  42, 170 },
633  {  42,  42, 212 },
634  {  42,  85,   0 },
635  {  42,  85,  42 },
636  {  42,  85,  85 },
637  {  42,  85, 127 },
638  {  42,  85, 170 },
639  {  42,  85, 212 },
640  {  42, 127,   0 },
641  {  42, 127,  42 },
642  {  42, 127,  85 },
643  {  42, 127, 127 },
644  {  42, 127, 170 },
645  {  42, 127, 212 },
646  {  42, 170,   0 },
647  {  42, 170,  42 },
648  {  42, 170,  85 },
649  {  42, 170, 127 },
650  {  42, 170, 170 },
651  {  42, 170, 212 },
652  {  42, 212,   0 },
653  {  42, 212,  42 },
654  {  42, 212,  85 },
655  {  42, 212, 127 },
656  {  42, 212, 170 },
657  {  42, 212, 212 },
658  {  85,   0,   0 },
659  {  85,   0,  42 },
660  {  85,   0,  85 },
661  {  85,   0, 127 },
662  {  85,   0, 170 },
663  {  85,   0, 212 },
664  {  85,  42,   0 },
665  {  85,  42,  42 },
666  {  85,  42,  85 },
667  {  85,  42, 127 },
668  {  85,  42, 170 },
669  {  85,  42, 212 },
670  {  85,  85,   0 },
671  {  85,  85,  42 },
672  {  85,  85,  85 }, /* 0.333 102 */
673  {  85,  85, 127 },
674  {  85,  85, 170 },
675  {  85,  85, 212 },
676  {  85, 127,   0 },
677  {  85, 127,  42 },
678  {  85, 127,  85 },
679  {  85, 127, 127 },
680  {  85, 127, 170 },
681  {  85, 127, 212 },
682  {  85, 170,   0 },
683  {  85, 170,  42 },
684  {  85, 170,  85 },
685  {  85, 170, 127 },
686  {  85, 170, 170 },
687  {  85, 170, 212 },
688  {  85, 212,   0 },
689  {  85, 212,  42 },
690  {  85, 212,  85 },
691  {  85, 212, 127 },
692  {  85, 212, 170 },
693  {  85, 212, 212 },
694  { 127,   0,   0 },
695  { 127,   0,  42 },
696  { 127,   0,  85 },
697  { 127,   0, 127 },
698  { 127,   0, 170 },
699  { 127,   0, 212 },
700  { 127,  42,   0 },
701  { 127,  42,  42 },
702  { 127,  42,  85 },
703  { 127,  42, 127 },
704  { 127,  42, 170 },
705  { 127,  42, 212 },
706  { 127,  85,   0 },
707  { 127,  85,  42 },
708  { 127,  85,  85 },
709  { 127,  85, 127 },
710  { 127,  85, 170 },
711  { 127,  85, 212 },
712  { 127, 127,   0 },
713  { 127, 127,  42 },
714  { 127, 127,  85 },
715  { 127, 127, 127 }, /* 0.498 145 */
716  { 127, 127, 170 },
717  { 127, 127, 212 },
718  { 127, 170,   0 },
719  { 127, 170,  42 },
720  { 127, 170,  85 },
721  { 127, 170, 127 },
722  { 127, 170, 170 },
723  { 127, 170, 212 },
724  { 127, 212,   0 },
725  { 127, 212,  42 },
726  { 127, 212,  85 },
727  { 127, 212, 127 },
728  { 127, 212, 170 },
729  { 127, 212, 212 },
730  { 170,   0,   0 },
731  { 170,   0,  42 },
732  { 170,   0,  85 },
733  { 170,   0, 127 },
734  { 170,   0, 170 },
735  { 170,   0, 212 },
736  { 170,  42,   0 },
737  { 170,  42,  42 },
738  { 170,  42,  85 },
739  { 170,  42, 127 },
740  { 170,  42, 170 },
741  { 170,  42, 212 },
742  { 170,  85,   0 },
743  { 170,  85,  42 },
744  { 170,  85,  85 },
745  { 170,  85, 127 },
746  { 170,  85, 170 },
747  { 170,  85, 212 },
748  { 170, 127,   0 },
749  { 170, 127,  42 },
750  { 170, 127,  85 },
751  { 170, 127, 127 },
752  { 170, 127, 170 },
753  { 170, 127, 212 },
754  { 170, 170,   0 },
755  { 170, 170,  42 },
756  { 170, 170,  85 },
757  { 170, 170, 127 },
758  { 170, 170, 170 }, /* 0.667 188 */
759  { 170, 170, 212 },
760  { 170, 212,   0 },
761  { 170, 212,  42 },
762  { 170, 212,  85 },
763  { 170, 212, 127 },
764  { 170, 212, 170 },
765  { 170, 212, 212 },
766  { 212,   0,   0 },
767  { 212,   0,  42 },
768  { 212,   0,  85 },
769  { 212,   0, 127 },
770  { 212,   0, 170 },
771  { 212,   0, 212 },
772  { 212,  42,   0 },
773  { 212,  42,  42 },
774  { 212,  42,  85 },
775  { 212,  42, 127 },
776  { 212,  42, 170 },
777  { 212,  42, 212 },
778  { 212,  85,   0 },
779  { 212,  85,  42 },
780  { 212,  85,  85 },
781  { 212,  85, 127 },
782  { 212,  85, 170 },
783  { 212,  85, 212 },
784  { 212, 127,   0 },
785  { 212, 127,  42 },
786  { 212, 127,  85 },
787  { 212, 127, 127 },
788  { 212, 127, 170 },
789  { 212, 127, 212 },
790  { 212, 170,   0 },
791  { 212, 170,  42 },
792  { 212, 170,  85 },
793  { 212, 170, 127 },
794  { 212, 170, 170 },
795  { 212, 170, 212 },
796  { 212, 212,   0 },
797  { 212, 212,  42 },
798  { 212, 212,  85 },
799  { 212, 212, 127 },
800  { 212, 212, 170 },
801  { 212, 212, 212 }, /* 0.831 231 */
802  {   8,   8,   8 }, /* 0.031 232 */
803  {  18,  18,  18 }, /* 0.071 233 */
804  {  28,  28,  28 }, /* 0.110 234 */
805  {  38,  38,  38 }, /* 0.149 235 */
806  {  48,  48,  48 }, /* 0.188 236 */
807  {  58,  58,  58 }, /* 0.227 237 */
808  {  68,  68,  68 }, /* 0.267 238 */
809  {  78,  78,  78 }, /* 0.306 239 */
810  {  88,  88,  88 }, /* 0.345 240 */
811  {  98,  98,  98 }, /* 0.384 241 */
812  { 108, 108, 108 }, /* 0.424 242 */
813  { 118, 118, 118 }, /* 0.463 243 */
814  { 128, 128, 128 }, /* 0.502 244 */
815  { 138, 138, 138 }, /* 0.541 245 */
816  { 148, 148, 148 }, /* 0.580 246 */
817  { 158, 158, 158 }, /* 0.620 247 */
818  { 168, 168, 168 }, /* 0.659 248 */
819  { 178, 178, 178 }, /* 0.698 249 */
820  { 188, 188, 188 }, /* 0.737 250 */
821  { 198, 198, 198 }, /* 0.776 251 */
822  { 208, 208, 208 }, /* 0.816 252 */
823  { 218, 218, 218 }, /* 0.855 253 */
824  { 228, 228, 228 }, /* 0.894 254 */
825  { 238, 238, 238 }  /* 0.933 255 */
826};
827
828static inline term_color_t
829rgb_to_color_xterm256 (int r, int g, int b)
830{
831  rgb_t color;
832  hsv_t hsv;
833
834  color.red = r; color.green = g; color.blue = b;
835  rgb_to_hsv (color, &hsv);
836
837  if (hsv.saturation < 0.065f)
838    {
839      /* Greyscale approximation.  */
840      float luminance = color_luminance (r, g, b);
841      if (luminance < 0.015f)
842	return 0;
843      else if (luminance < 0.051f)
844	return 232;
845      else if (luminance < 0.090f)
846	return 233;
847      else if (luminance < 0.129f)
848	return 234;
849      else if (luminance < 0.157f)
850	return 235;
851      else if (luminance < 0.177f)
852	return 59;
853      else if (luminance < 0.207f)
854	return 236;
855      else if (luminance < 0.247f)
856	return 237;
857      else if (luminance < 0.284f)
858	return 238;
859      else if (luminance < 0.304f)
860	return 8;
861      else if (luminance < 0.319f)
862	return 239;
863      else if (luminance < 0.339f)
864	return 102;
865      else if (luminance < 0.364f)
866	return 240;
867      else if (luminance < 0.404f)
868	return 241;
869      else if (luminance < 0.443f)
870	return 242;
871      else if (luminance < 0.480f)
872	return 243;
873      else if (luminance < 0.500f)
874	return 145;
875      else if (luminance < 0.521f)
876	return 244;
877      else if (luminance < 0.560f)
878	return 245;
879      else if (luminance < 0.600f)
880	return 246;
881      else if (luminance < 0.639f)
882	return 247;
883      else if (luminance < 0.663f)
884	return 248;
885      else if (luminance < 0.682f)
886	return 188;
887      else if (luminance < 0.717f)
888	return 249;
889      else if (luminance < 0.756f)
890	return 250;
891      else if (luminance < 0.796f)
892	return 251;
893      else if (luminance < 0.823f)
894	return 252;
895      else if (luminance < 0.843f)
896	return 231;
897      else if (luminance < 0.874f)
898	return 253;
899      else if (luminance < 0.896f)
900	return 254;
901      else if (luminance < 0.915f)
902	return 7;
903      else if (luminance < 0.966f)
904	return 255;
905      else
906	return 15;
907    }
908  else
909    /* Color approximation.  */
910    return nearest_color (color, colors_of_xterm256, 256);
911}
912
913
914/* ============================= attributes_t ============================= */
915
916/* ANSI C and ISO C99 6.7.2.1.(4) forbid use of bit fields for types other
917   than 'int' or 'unsigned int'.
918   On the other hand, C++ forbids conversion between enum types and integer
919   types without an explicit cast.  */
920#ifdef __cplusplus
921# define BITFIELD_TYPE(orig_type,integer_type) orig_type
922#else
923# define BITFIELD_TYPE(orig_type,integer_type) integer_type
924#endif
925
926/* Attributes that can be set on a character.  */
927typedef struct
928{
929  BITFIELD_TYPE(term_color_t,     signed int)   color     : 9;
930  BITFIELD_TYPE(term_color_t,     signed int)   bgcolor   : 9;
931  BITFIELD_TYPE(term_weight_t,    unsigned int) weight    : 1;
932  BITFIELD_TYPE(term_posture_t,   unsigned int) posture   : 1;
933  BITFIELD_TYPE(term_underline_t, unsigned int) underline : 1;
934} attributes_t;
935
936
937/* ============================ term_ostream_t ============================ */
938
939struct term_ostream : struct ostream
940{
941fields:
942  /* The file descriptor used for output.  Note that ncurses termcap emulation
943     uses the baud rate information from file descriptor 1 (stdout) if it is
944     a tty, or from file descriptor 2 (stderr) otherwise.  */
945  int fd;
946  char *filename;
947  /* Values from the terminal type's terminfo/termcap description.
948     See terminfo(5) for details.  */
949				/* terminfo  termcap */
950  int max_colors;		/* colors    Co */
951  int no_color_video;		/* ncv       NC */
952  char *set_a_foreground;	/* setaf     AF */
953  char *set_foreground;		/* setf      Sf */
954  char *set_a_background;	/* setab     AB */
955  char *set_background;		/* setb      Sb */
956  char *orig_pair;		/* op        op */
957  char *enter_bold_mode;	/* bold      md */
958  char *enter_italics_mode;	/* sitm      ZH */
959  char *exit_italics_mode;	/* ritm      ZR */
960  char *enter_underline_mode;	/* smul      us */
961  char *exit_underline_mode;	/* rmul      ue */
962  char *exit_attribute_mode;	/* sgr0      me */
963  /* Inferred values.  */
964  bool supports_foreground;
965  bool supports_background;
966  colormodel_t colormodel;
967  bool supports_weight;
968  bool supports_posture;
969  bool supports_underline;
970  /* Variable state.  */
971  char *buffer;			/* Buffer for the current line.  */
972  attributes_t *attrbuffer;	/* Buffer for the simplified attributes; same
973				   length as buffer.  */
974  size_t buflen;		/* Number of bytes stored so far.  */
975  size_t allocated;		/* Allocated size of the buffer.  */
976  attributes_t curr_attr;	/* Current attributes.  */
977  attributes_t simp_attr;	/* Simplified current attributes.  */
978};
979
980/* Simplify attributes, according to the terminal's capabilities.  */
981static attributes_t
982simplify_attributes (term_ostream_t stream, attributes_t attr)
983{
984  if ((attr.color != COLOR_DEFAULT || attr.bgcolor != COLOR_DEFAULT)
985      && stream->no_color_video > 0)
986    {
987      /* When colors and attributes can not be represented simultaneously,
988	 we give preference to the color.  */
989      if (stream->no_color_video & 2)
990	/* Colors conflict with underlining.  */
991	attr.underline = UNDERLINE_OFF;
992      if (stream->no_color_video & 32)
993	/* Colors conflict with bold weight.  */
994	attr.weight = WEIGHT_NORMAL;
995    }
996  if (!stream->supports_foreground)
997    attr.color = COLOR_DEFAULT;
998  if (!stream->supports_background)
999    attr.bgcolor = COLOR_DEFAULT;
1000  if (!stream->supports_weight)
1001    attr.weight = WEIGHT_DEFAULT;
1002  if (!stream->supports_posture)
1003    attr.posture = POSTURE_DEFAULT;
1004  if (!stream->supports_underline)
1005    attr.underline = UNDERLINE_DEFAULT;
1006  return attr;
1007}
1008
1009/* While a line is being output, we need to be careful to restore the
1010   terminal's settings in case of a fatal signal or an exit() call.  */
1011
1012/* File descriptor to which out_char shall output escape sequences.  */
1013static int out_fd = -1;
1014
1015/* Filename of out_fd.  */
1016static const char *out_filename;
1017
1018/* Output a single char to out_fd.  Ignore errors.  */
1019static int
1020out_char_unchecked (int c)
1021{
1022  char bytes[1];
1023
1024  bytes[0] = (char)c;
1025  full_write (out_fd, bytes, 1);
1026  return 0;
1027}
1028
1029/* State that informs the exit handler what to do.  */
1030static const char *restore_colors;
1031static const char *restore_weight;
1032static const char *restore_posture;
1033static const char *restore_underline;
1034
1035/* The exit handler.  */
1036static void
1037restore (void)
1038{
1039  /* Only do something while some output was interrupted.  */
1040  if (out_fd >= 0)
1041    {
1042      if (restore_colors != NULL)
1043	tputs (restore_colors, 1, out_char_unchecked);
1044      if (restore_weight != NULL)
1045	tputs (restore_weight, 1, out_char_unchecked);
1046      if (restore_posture != NULL)
1047	tputs (restore_posture, 1, out_char_unchecked);
1048      if (restore_underline != NULL)
1049	tputs (restore_underline, 1, out_char_unchecked);
1050    }
1051}
1052
1053/* The list of signals whose default behaviour is to stop the program.  */
1054static int stopping_signals[] =
1055  {
1056#ifdef SIGTSTP
1057    SIGTSTP,
1058#endif
1059#ifdef SIGTTIN
1060    SIGTTIN,
1061#endif
1062#ifdef SIGTTOU
1063    SIGTTOU,
1064#endif
1065    0
1066  };
1067
1068#define num_stopping_signals (SIZEOF (stopping_signals) - 1)
1069
1070static sigset_t stopping_signal_set;
1071
1072static void
1073init_stopping_signal_set ()
1074{
1075  static bool stopping_signal_set_initialized = false;
1076  if (!stopping_signal_set_initialized)
1077    {
1078      size_t i;
1079
1080      sigemptyset (&stopping_signal_set);
1081      for (i = 0; i < num_stopping_signals; i++)
1082	sigaddset (&stopping_signal_set, stopping_signals[i]);
1083
1084      stopping_signal_set_initialized = true;
1085    }
1086}
1087
1088/* Temporarily delay the stopping signals.  */
1089static inline void
1090block_stopping_signals ()
1091{
1092  init_stopping_signal_set ();
1093  sigprocmask (SIG_BLOCK, &stopping_signal_set, NULL);
1094}
1095
1096/* Stop delaying the stopping signals.  */
1097static inline void
1098unblock_stopping_signals ()
1099{
1100  init_stopping_signal_set ();
1101  sigprocmask (SIG_UNBLOCK, &stopping_signal_set, NULL);
1102}
1103
1104/* Compare two sets of attributes for equality.  */
1105static inline bool
1106equal_attributes (attributes_t attr1, attributes_t attr2)
1107{
1108  return (attr1.color == attr2.color
1109	  && attr1.bgcolor == attr2.bgcolor
1110	  && attr1.weight == attr2.weight
1111	  && attr1.posture == attr2.posture
1112	  && attr1.underline == attr2.underline);
1113}
1114
1115/* Signal error after full_write failed.  */
1116static void
1117out_error ()
1118{
1119  error (EXIT_FAILURE, errno, _("error writing to %s"), out_filename);
1120}
1121
1122/* Output a single char to out_fd.  */
1123static int
1124out_char (int c)
1125{
1126  char bytes[1];
1127
1128  bytes[0] = (char)c;
1129  /* We have to write directly to the file descriptor, not to a buffer with
1130     the same destination, because of the padding and sleeping that tputs()
1131     does.  */
1132  if (full_write (out_fd, bytes, 1) < 1)
1133    out_error ();
1134  return 0;
1135}
1136
1137/* Output escape sequences to switch from OLD_ATTR to NEW_ATTR.  */
1138static void
1139out_attr_change (term_ostream_t stream,
1140		 attributes_t old_attr, attributes_t new_attr)
1141{
1142  bool cleared_attributes;
1143
1144  /* We don't know the default colors of the terminal.  The only way to switch
1145     back to a default color is to use stream->orig_pair.  */
1146  if ((new_attr.color == COLOR_DEFAULT && old_attr.color != COLOR_DEFAULT)
1147      || (new_attr.bgcolor == COLOR_DEFAULT && old_attr.bgcolor != COLOR_DEFAULT))
1148    {
1149      assert (stream->supports_foreground || stream->supports_background);
1150      tputs (stream->orig_pair, 1, out_char);
1151      old_attr.color = COLOR_DEFAULT;
1152      old_attr.bgcolor = COLOR_DEFAULT;
1153    }
1154
1155  /* To turn off WEIGHT_BOLD, the only way is to output the exit_attribute_mode
1156     sequence.  (With xterm, you can also do it with "Esc [ 0 m", but this
1157     escape sequence is not contained in the terminfo description.)  It may
1158     also clear the colors; this is the case e.g. when TERM="xterm" or
1159     TERM="ansi".
1160     To turn off UNDERLINE_ON, we can use the exit_underline_mode or the
1161     exit_attribute_mode sequence.  In the latter case, it will not only
1162     turn off UNDERLINE_ON, but also the other attributes, and possibly also
1163     the colors.
1164     To turn off POSTURE_ITALIC, we can use the exit_italics_mode or the
1165     exit_attribute_mode sequence.  Again, in the latter case, it will not
1166     only turn off POSTURE_ITALIC, but also the other attributes, and possibly
1167     also the colors.
1168     There is no point in setting an attribute just before emitting an
1169     escape sequence that may again turn off the attribute.  Therefore we
1170     proceed in two steps: First, clear the attributes that need to be
1171     cleared; then - taking into account that this may have cleared all
1172     attributes and all colors - set the colors and the attributes.
1173     The variable 'cleared_attributes' tells whether an escape sequence
1174     has been output that may have cleared all attributes and all color
1175     settings.  */
1176  cleared_attributes = false;
1177  if (old_attr.posture != POSTURE_NORMAL
1178      && new_attr.posture == POSTURE_NORMAL
1179      && stream->exit_italics_mode != NULL)
1180    {
1181      tputs (stream->exit_italics_mode, 1, out_char);
1182      old_attr.posture = POSTURE_NORMAL;
1183      cleared_attributes = true;
1184    }
1185  if (old_attr.underline != UNDERLINE_OFF
1186      && new_attr.underline == UNDERLINE_OFF
1187      && stream->exit_underline_mode != NULL)
1188    {
1189      tputs (stream->exit_underline_mode, 1, out_char);
1190      old_attr.underline = UNDERLINE_OFF;
1191      cleared_attributes = true;
1192    }
1193  if ((old_attr.weight != WEIGHT_NORMAL
1194       && new_attr.weight == WEIGHT_NORMAL)
1195      || (old_attr.posture != POSTURE_NORMAL
1196	  && new_attr.posture == POSTURE_NORMAL
1197	  /* implies stream->exit_italics_mode == NULL */)
1198      || (old_attr.underline != UNDERLINE_OFF
1199	  && new_attr.underline == UNDERLINE_OFF
1200	  /* implies stream->exit_underline_mode == NULL */))
1201    {
1202      tputs (stream->exit_attribute_mode, 1, out_char);
1203      /* We don't know exactly what effects exit_attribute_mode has, but
1204	 this is the minimum effect:  */
1205      old_attr.weight = WEIGHT_NORMAL;
1206      if (stream->exit_italics_mode == NULL)
1207	old_attr.posture = POSTURE_NORMAL;
1208      if (stream->exit_underline_mode == NULL)
1209	old_attr.underline = UNDERLINE_OFF;
1210      cleared_attributes = true;
1211    }
1212
1213  /* Turn on the colors.  */
1214  if (new_attr.color != old_attr.color
1215      || (cleared_attributes && new_attr.color != COLOR_DEFAULT))
1216    {
1217      assert (stream->supports_foreground);
1218      assert (new_attr.color != COLOR_DEFAULT);
1219      switch (stream->colormodel)
1220	{
1221	case cm_common8:
1222	  assert (new_attr.color >= 0 && new_attr.color < 8);
1223	  if (stream->set_a_foreground != NULL)
1224	    tputs (tparm (stream->set_a_foreground,
1225			  color_bgr (new_attr.color)),
1226		   1, out_char);
1227	  else
1228	    tputs (tparm (stream->set_foreground, new_attr.color),
1229		   1, out_char);
1230	  break;
1231	/* When we are dealing with an xterm, there is no need to go through
1232	   tputs() because we know there is no padding and sleeping.  */
1233	case cm_xterm8:
1234	  assert (new_attr.color >= 0 && new_attr.color < 8);
1235	  {
1236	    char bytes[5];
1237	    bytes[0] = 0x1B; bytes[1] = '[';
1238	    bytes[2] = '3'; bytes[3] = '0' + new_attr.color;
1239	    bytes[4] = 'm';
1240	    if (full_write (out_fd, bytes, 5) < 5)
1241	      out_error ();
1242	  }
1243	  break;
1244	case cm_xterm16:
1245	  assert (new_attr.color >= 0 && new_attr.color < 16);
1246	  {
1247	    char bytes[5];
1248	    bytes[0] = 0x1B; bytes[1] = '[';
1249	    if (new_attr.color < 8)
1250	      {
1251		bytes[2] = '3'; bytes[3] = '0' + new_attr.color;
1252	      }
1253	    else
1254	      {
1255		bytes[2] = '9'; bytes[3] = '0' + (new_attr.color - 8);
1256	      }
1257	    bytes[4] = 'm';
1258	    if (full_write (out_fd, bytes, 5) < 5)
1259	      out_error ();
1260	  }
1261	  break;
1262	case cm_xterm88:
1263	  assert (new_attr.color >= 0 && new_attr.color < 88);
1264	  {
1265	    char bytes[10];
1266	    char *p;
1267	    bytes[0] = 0x1B; bytes[1] = '[';
1268	    bytes[2] = '3'; bytes[3] = '8'; bytes[4] = ';';
1269	    bytes[5] = '5'; bytes[6] = ';';
1270	    p = bytes + 7;
1271	    if (new_attr.color >= 10)
1272	      *p++ = '0' + (new_attr.color / 10);
1273	    *p++ = '0' + (new_attr.color % 10);
1274	    *p++ = 'm';
1275	    if (full_write (out_fd, bytes, p - bytes) < p - bytes)
1276	      out_error ();
1277	  }
1278	  break;
1279	case cm_xterm256:
1280	  assert (new_attr.color >= 0 && new_attr.color < 256);
1281	  {
1282	    char bytes[11];
1283	    char *p;
1284	    bytes[0] = 0x1B; bytes[1] = '[';
1285	    bytes[2] = '3'; bytes[3] = '8'; bytes[4] = ';';
1286	    bytes[5] = '5'; bytes[6] = ';';
1287	    p = bytes + 7;
1288	    if (new_attr.color >= 100)
1289	      *p++ = '0' + (new_attr.color / 100);
1290	    if (new_attr.color >= 10)
1291	      *p++ = '0' + ((new_attr.color % 100) / 10);
1292	    *p++ = '0' + (new_attr.color % 10);
1293	    *p++ = 'm';
1294	    if (full_write (out_fd, bytes, p - bytes) < p - bytes)
1295	      out_error ();
1296	  }
1297	  break;
1298	default:
1299	  abort ();
1300	}
1301    }
1302  if (new_attr.bgcolor != old_attr.bgcolor
1303      || (cleared_attributes && new_attr.bgcolor != COLOR_DEFAULT))
1304    {
1305      assert (stream->supports_background);
1306      assert (new_attr.bgcolor != COLOR_DEFAULT);
1307      switch (stream->colormodel)
1308	{
1309	case cm_common8:
1310	  assert (new_attr.bgcolor >= 0 && new_attr.bgcolor < 8);
1311	  if (stream->set_a_background != NULL)
1312	    tputs (tparm (stream->set_a_background,
1313			  color_bgr (new_attr.bgcolor)),
1314		   1, out_char);
1315	  else
1316	    tputs (tparm (stream->set_background, new_attr.bgcolor),
1317		   1, out_char);
1318	  break;
1319	/* When we are dealing with an xterm, there is no need to go through
1320	   tputs() because we know there is no padding and sleeping.  */
1321	case cm_xterm8:
1322	  assert (new_attr.bgcolor >= 0 && new_attr.bgcolor < 8);
1323	  {
1324	    char bytes[5];
1325	    bytes[0] = 0x1B; bytes[1] = '[';
1326	    bytes[2] = '4'; bytes[3] = '0' + new_attr.bgcolor;
1327	    bytes[4] = 'm';
1328	    if (full_write (out_fd, bytes, 5) < 5)
1329	      out_error ();
1330	  }
1331	  break;
1332	case cm_xterm16:
1333	  assert (new_attr.bgcolor >= 0 && new_attr.bgcolor < 16);
1334	  {
1335	    char bytes[6];
1336	    bytes[0] = 0x1B; bytes[1] = '[';
1337	    if (new_attr.bgcolor < 8)
1338	      {
1339		bytes[2] = '4'; bytes[3] = '0' + new_attr.bgcolor;
1340		bytes[4] = 'm';
1341		if (full_write (out_fd, bytes, 5) < 5)
1342		  out_error ();
1343	      }
1344	    else
1345	      {
1346		bytes[2] = '1'; bytes[3] = '0';
1347		bytes[4] = '0' + (new_attr.bgcolor - 8); bytes[5] = 'm';
1348		if (full_write (out_fd, bytes, 6) < 6)
1349		  out_error ();
1350	      }
1351	  }
1352	  break;
1353	case cm_xterm88:
1354	  assert (new_attr.bgcolor >= 0 && new_attr.bgcolor < 88);
1355	  {
1356	    char bytes[10];
1357	    char *p;
1358	    bytes[0] = 0x1B; bytes[1] = '[';
1359	    bytes[2] = '4'; bytes[3] = '8'; bytes[4] = ';';
1360	    bytes[5] = '5'; bytes[6] = ';';
1361	    p = bytes + 7;
1362	    if (new_attr.bgcolor >= 10)
1363	      *p++ = '0' + (new_attr.bgcolor / 10);
1364	    *p++ = '0' + (new_attr.bgcolor % 10);
1365	    *p++ = 'm';
1366	    if (full_write (out_fd, bytes, p - bytes) < p - bytes)
1367	      out_error ();
1368	  }
1369	  break;
1370	case cm_xterm256:
1371	  assert (new_attr.bgcolor >= 0 && new_attr.bgcolor < 256);
1372	  {
1373	    char bytes[11];
1374	    char *p;
1375	    bytes[0] = 0x1B; bytes[1] = '[';
1376	    bytes[2] = '4'; bytes[3] = '8'; bytes[4] = ';';
1377	    bytes[5] = '5'; bytes[6] = ';';
1378	    p = bytes + 7;
1379	    if (new_attr.bgcolor >= 100)
1380	      *p++ = '0' + (new_attr.bgcolor / 100);
1381	    if (new_attr.bgcolor >= 10)
1382	      *p++ = '0' + ((new_attr.bgcolor % 100) / 10);
1383	    *p++ = '0' + (new_attr.bgcolor % 10);
1384	    *p++ = 'm';
1385	    if (full_write (out_fd, bytes, p - bytes) < p - bytes)
1386	      out_error ();
1387	  }
1388	  break;
1389	default:
1390	  abort ();
1391	}
1392    }
1393
1394  if (new_attr.weight != old_attr.weight
1395      || (cleared_attributes && new_attr.weight != WEIGHT_DEFAULT))
1396    {
1397      assert (stream->supports_weight);
1398      assert (new_attr.weight != WEIGHT_DEFAULT);
1399      /* This implies:  */
1400      assert (new_attr.weight == WEIGHT_BOLD);
1401      tputs (stream->enter_bold_mode, 1, out_char);
1402    }
1403  if (new_attr.posture != old_attr.posture
1404      || (cleared_attributes && new_attr.posture != POSTURE_DEFAULT))
1405    {
1406      assert (stream->supports_posture);
1407      assert (new_attr.posture != POSTURE_DEFAULT);
1408      /* This implies:  */
1409      assert (new_attr.posture == POSTURE_ITALIC);
1410      tputs (stream->enter_italics_mode, 1, out_char);
1411    }
1412  if (new_attr.underline != old_attr.underline
1413      || (cleared_attributes && new_attr.underline != UNDERLINE_DEFAULT))
1414    {
1415      assert (stream->supports_underline);
1416      assert (new_attr.underline != UNDERLINE_DEFAULT);
1417      /* This implies:  */
1418      assert (new_attr.underline == UNDERLINE_ON);
1419      tputs (stream->enter_underline_mode, 1, out_char);
1420    }
1421}
1422
1423/* Output the buffered line atomically.
1424   The terminal is assumed to have the default state (regarding colors and
1425   attributes) before this call.  It is left in default state after this
1426   call (regardless of stream->curr_attr).  */
1427static void
1428output_buffer (term_ostream_t stream)
1429{
1430  attributes_t default_attr;
1431  attributes_t attr;
1432  const char *cp;
1433  const attributes_t *ap;
1434  size_t len;
1435  size_t n;
1436
1437  default_attr.color = COLOR_DEFAULT;
1438  default_attr.bgcolor = COLOR_DEFAULT;
1439  default_attr.weight = WEIGHT_DEFAULT;
1440  default_attr.posture = POSTURE_DEFAULT;
1441  default_attr.underline = UNDERLINE_DEFAULT;
1442
1443  attr = default_attr;
1444
1445  cp = stream->buffer;
1446  ap = stream->attrbuffer;
1447  len = stream->buflen;
1448
1449  /* See how much we can output without blocking signals.  */
1450  for (n = 0; n < len && equal_attributes (ap[n], attr); n++)
1451    ;
1452  if (n > 0)
1453    {
1454      if (full_write (stream->fd, cp, n) < n)
1455	error (EXIT_FAILURE, errno, _("error writing to %s"), stream->filename);
1456      cp += n;
1457      ap += n;
1458      len -= n;
1459    }
1460  if (len > 0)
1461    {
1462      /* Block fatal signals, so that a SIGINT or similar doesn't interrupt
1463	 us without the possibility of restoring the terminal's state.  */
1464      block_fatal_signals ();
1465      /* Likewise for SIGTSTP etc.  */
1466      block_stopping_signals ();
1467
1468      /* Enable the exit handler for restoring the terminal's state.  */
1469      restore_colors =
1470	(stream->supports_foreground || stream->supports_background
1471	 ? stream->orig_pair
1472	 : NULL);
1473      restore_weight =
1474	(stream->supports_weight ? stream->exit_attribute_mode : NULL);
1475      restore_posture =
1476	(stream->supports_posture
1477	 ? (stream->exit_italics_mode != NULL
1478	    ? stream->exit_italics_mode
1479	    : stream->exit_attribute_mode)
1480	 : NULL);
1481      restore_underline =
1482	(stream->supports_underline
1483	 ? (stream->exit_underline_mode != NULL
1484	    ? stream->exit_underline_mode
1485	    : stream->exit_attribute_mode)
1486	 : NULL);
1487      out_fd = stream->fd;
1488      out_filename = stream->filename;
1489
1490      while (len > 0)
1491	{
1492	  /* Activate the attributes in *ap.  */
1493	  out_attr_change (stream, attr, *ap);
1494	  attr = *ap;
1495	  /* See how many characters we can output without further attribute
1496	     changes.  */
1497	  for (n = 1; n < len && equal_attributes (ap[n], attr); n++)
1498	    ;
1499	  if (full_write (stream->fd, cp, n) < n)
1500	    error (EXIT_FAILURE, errno, _("error writing to %s"),
1501		   stream->filename);
1502	  cp += n;
1503	  ap += n;
1504	  len -= n;
1505	}
1506
1507      /* Switch back to the default attributes.  */
1508      out_attr_change (stream, attr, default_attr);
1509
1510      /* Disable the exit handler.  */
1511      out_fd = -1;
1512      out_filename = NULL;
1513
1514      /* Unblock fatal and stopping signals.  */
1515      unblock_stopping_signals ();
1516      unblock_fatal_signals ();
1517    }
1518  stream->buflen = 0;
1519}
1520
1521/* Implementation of ostream_t methods.  */
1522
1523static term_color_t
1524term_ostream::rgb_to_color (term_ostream_t stream, int red, int green, int blue)
1525{
1526  switch (stream->colormodel)
1527    {
1528    case cm_monochrome:
1529      return rgb_to_color_monochrome ();
1530    case cm_common8:
1531      return rgb_to_color_common8 (red, green, blue);
1532    case cm_xterm8:
1533      return rgb_to_color_xterm8 (red, green, blue);
1534    case cm_xterm16:
1535      return rgb_to_color_xterm16 (red, green, blue);
1536    case cm_xterm88:
1537      return rgb_to_color_xterm88 (red, green, blue);
1538    case cm_xterm256:
1539      return rgb_to_color_xterm256 (red, green, blue);
1540    default:
1541      abort ();
1542    }
1543}
1544
1545static void
1546term_ostream::write_mem (term_ostream_t stream, const void *data, size_t len)
1547{
1548  const char *cp = (const char *) data;
1549  while (len > 0)
1550    {
1551      /* Look for the next newline.  */
1552      const char *newline = (const char *) memchr (cp, '\n', len);
1553      size_t n = (newline != NULL ? newline - cp : len);
1554
1555      /* Copy n bytes into the buffer.  */
1556      if (n > stream->allocated - stream->buflen)
1557	{
1558	  size_t new_allocated =
1559	    xmax (xsum (stream->buflen, n),
1560		  xsum (stream->allocated, stream->allocated));
1561	  if (size_overflow_p (new_allocated))
1562	    error (EXIT_FAILURE, 0,
1563		   _("%s: too much output, buffer size overflow"),
1564		   "term_ostream");
1565	  stream->buffer = (char *) xrealloc (stream->buffer, new_allocated);
1566	  stream->attrbuffer =
1567	    (attributes_t *)
1568	    xrealloc (stream->attrbuffer,
1569		      new_allocated * sizeof (attributes_t));
1570	  stream->allocated = new_allocated;
1571	}
1572      memcpy (stream->buffer + stream->buflen, cp, n);
1573      {
1574	attributes_t attr = stream->simp_attr;
1575	attributes_t *ap = stream->attrbuffer + stream->buflen;
1576	attributes_t *ap_end = ap + n;
1577	for (; ap < ap_end; ap++)
1578	  *ap = attr;
1579      }
1580      stream->buflen += n;
1581
1582      if (newline != NULL)
1583	{
1584	  output_buffer (stream);
1585	  if (full_write (stream->fd, "\n", 1) < 1)
1586	    error (EXIT_FAILURE, errno, _("error writing to %s"),
1587		   stream->filename);
1588	  cp += n + 1; /* cp = newline + 1; */
1589	  len -= n + 1;
1590	}
1591      else
1592	break;
1593    }
1594}
1595
1596static void
1597term_ostream::flush (term_ostream_t stream)
1598{
1599  output_buffer (stream);
1600}
1601
1602static void
1603term_ostream::free (term_ostream_t stream)
1604{
1605  term_ostream_flush (stream);
1606  free (stream->filename);
1607  if (stream->set_a_foreground != NULL)
1608    free (stream->set_a_foreground);
1609  if (stream->set_foreground != NULL)
1610    free (stream->set_foreground);
1611  if (stream->set_a_background != NULL)
1612    free (stream->set_a_background);
1613  if (stream->set_background != NULL)
1614    free (stream->set_background);
1615  if (stream->orig_pair != NULL)
1616    free (stream->orig_pair);
1617  if (stream->enter_bold_mode != NULL)
1618    free (stream->enter_bold_mode);
1619  if (stream->enter_italics_mode != NULL)
1620    free (stream->enter_italics_mode);
1621  if (stream->exit_italics_mode != NULL)
1622    free (stream->exit_italics_mode);
1623  if (stream->enter_underline_mode != NULL)
1624    free (stream->enter_underline_mode);
1625  if (stream->exit_underline_mode != NULL)
1626    free (stream->exit_underline_mode);
1627  if (stream->exit_attribute_mode != NULL)
1628    free (stream->exit_attribute_mode);
1629  free (stream->buffer);
1630  free (stream);
1631}
1632
1633/* Implementation of term_ostream_t methods.  */
1634
1635term_color_t
1636term_ostream::get_color (term_ostream_t stream)
1637{
1638  return stream->curr_attr.color;
1639}
1640
1641void
1642term_ostream::set_color (term_ostream_t stream, term_color_t color)
1643{
1644  stream->curr_attr.color = color;
1645  stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
1646}
1647
1648term_color_t
1649term_ostream::get_bgcolor (term_ostream_t stream)
1650{
1651  return stream->curr_attr.bgcolor;
1652}
1653
1654void
1655term_ostream::set_bgcolor (term_ostream_t stream, term_color_t color)
1656{
1657  stream->curr_attr.bgcolor = color;
1658  stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
1659}
1660
1661term_weight_t
1662term_ostream::get_weight (term_ostream_t stream)
1663{
1664  return stream->curr_attr.weight;
1665}
1666
1667void
1668term_ostream::set_weight (term_ostream_t stream, term_weight_t weight)
1669{
1670  stream->curr_attr.weight = weight;
1671  stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
1672}
1673
1674term_posture_t
1675term_ostream::get_posture (term_ostream_t stream)
1676{
1677  return stream->curr_attr.posture;
1678}
1679
1680void
1681term_ostream::set_posture (term_ostream_t stream, term_posture_t posture)
1682{
1683  stream->curr_attr.posture = posture;
1684  stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
1685}
1686
1687term_underline_t
1688term_ostream::get_underline (term_ostream_t stream)
1689{
1690  return stream->curr_attr.underline;
1691}
1692
1693void
1694term_ostream::set_underline (term_ostream_t stream, term_underline_t underline)
1695{
1696  stream->curr_attr.underline = underline;
1697  stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
1698}
1699
1700/* Constructor.  */
1701
1702static inline char *
1703xstrdup0 (const char *str)
1704{
1705  if (str == NULL)
1706    return NULL;
1707#if HAVE_TERMINFO
1708  if (str == (const char *)(-1))
1709    return NULL;
1710#endif
1711  return xstrdup (str);
1712}
1713
1714term_ostream_t
1715term_ostream_create (int fd, const char *filename)
1716{
1717  term_ostream_t stream = XMALLOC (struct term_ostream_representation);
1718  const char *term;
1719
1720  stream->base.vtable = &term_ostream_vtable;
1721  stream->fd = fd;
1722  stream->filename = xstrdup (filename);
1723
1724  /* Defaults.  */
1725  stream->max_colors = -1;
1726  stream->no_color_video = -1;
1727  stream->set_a_foreground = NULL;
1728  stream->set_foreground = NULL;
1729  stream->set_a_background = NULL;
1730  stream->set_background = NULL;
1731  stream->orig_pair = NULL;
1732  stream->enter_bold_mode = NULL;
1733  stream->enter_italics_mode = NULL;
1734  stream->exit_italics_mode = NULL;
1735  stream->enter_underline_mode = NULL;
1736  stream->exit_underline_mode = NULL;
1737  stream->exit_attribute_mode = NULL;
1738
1739  /* Retrieve the terminal type.  */
1740  term = getenv ("TERM");
1741  if (term != NULL && term[0] != '\0')
1742    {
1743      /* When the terminfo function are available, we prefer them over the
1744	 termcap functions because
1745	   1. they don't risk a buffer overflow,
1746	   2. on OSF/1, for TERM=xterm, the tiget* functions provide access
1747	      to the number of colors and the color escape sequences, whereas
1748	      the tget* functions don't provide them.  */
1749#if HAVE_TERMINFO
1750      int err = 1;
1751
1752      if (setupterm (term, fd, &err) || err == 1)
1753	{
1754	  /* Retrieve particular values depending on the terminal type.  */
1755	  stream->max_colors = tigetnum ("colors");
1756	  stream->no_color_video = tigetnum ("ncv");
1757	  stream->set_a_foreground = xstrdup0 (tigetstr ("setaf"));
1758	  stream->set_foreground = xstrdup0 (tigetstr ("setf"));
1759	  stream->set_a_background = xstrdup0 (tigetstr ("setab"));
1760	  stream->set_background = xstrdup0 (tigetstr ("setb"));
1761	  stream->orig_pair = xstrdup0 (tigetstr ("op"));
1762	  stream->enter_bold_mode = xstrdup0 (tigetstr ("bold"));
1763	  stream->enter_italics_mode = xstrdup0 (tigetstr ("sitm"));
1764	  stream->exit_italics_mode = xstrdup0 (tigetstr ("ritm"));
1765	  stream->enter_underline_mode = xstrdup0 (tigetstr ("smul"));
1766	  stream->exit_underline_mode = xstrdup0 (tigetstr ("rmul"));
1767	  stream->exit_attribute_mode = xstrdup0 (tigetstr ("sgr0"));
1768	}
1769#elif HAVE_TERMCAP
1770      struct { char buf[1024]; char canary[4]; } termcapbuf;
1771      int retval;
1772
1773      /* Call tgetent, being defensive against buffer overflow.  */
1774      memcpy (termcapbuf.canary, "CnRy", 4);
1775      retval = tgetent (termcapbuf.buf, term);
1776      if (memcmp (termcapbuf.canary, "CnRy", 4) != 0)
1777	/* Buffer overflow!  */
1778	abort ();
1779
1780      if (retval > 0)
1781	{
1782	  struct { char buf[1024]; char canary[4]; } termentrybuf;
1783	  char *termentryptr;
1784
1785	  /* Prepare for calling tgetstr, being defensive against buffer
1786	     overflow.  ncurses' tgetstr() supports a second argument NULL,
1787	     but NetBSD's tgetstr() doesn't.  */
1788	  memcpy (termentrybuf.canary, "CnRz", 4);
1789	  #define TEBP ((termentryptr = termentrybuf.buf), &termentryptr)
1790
1791	  /* Retrieve particular values depending on the terminal type.  */
1792	  stream->max_colors = tgetnum ("Co");
1793	  stream->no_color_video = tgetnum ("NC");
1794	  stream->set_a_foreground = xstrdup0 (tgetstr ("AF", TEBP));
1795	  stream->set_foreground = xstrdup0 (tgetstr ("Sf", TEBP));
1796	  stream->set_a_background = xstrdup0 (tgetstr ("AB", TEBP));
1797	  stream->set_background = xstrdup0 (tgetstr ("Sb", TEBP));
1798	  stream->orig_pair = xstrdup0 (tgetstr ("op", TEBP));
1799	  stream->enter_bold_mode = xstrdup0 (tgetstr ("md", TEBP));
1800	  stream->enter_italics_mode = xstrdup0 (tgetstr ("ZH", TEBP));
1801	  stream->exit_italics_mode = xstrdup0 (tgetstr ("ZR", TEBP));
1802	  stream->enter_underline_mode = xstrdup0 (tgetstr ("us", TEBP));
1803	  stream->exit_underline_mode = xstrdup0 (tgetstr ("ue", TEBP));
1804	  stream->exit_attribute_mode = xstrdup0 (tgetstr ("me", TEBP));
1805
1806# ifdef __BEOS__
1807	  /* The BeOS termcap entry for "beterm" is broken: For "AF" and "AB"
1808	     it contains balues in terminfo syntax but the system's tparam()
1809	     function understands only the termcap syntax.  */
1810	  if (stream->set_a_foreground != NULL
1811	      && strcmp (stream->set_a_foreground, "\033[3%p1%dm") == 0)
1812	    {
1813	      free (stream->set_a_foreground);
1814	      stream->set_a_foreground = xstrdup ("\033[3%dm");
1815	    }
1816	  if (stream->set_a_background != NULL
1817	      && strcmp (stream->set_a_background, "\033[4%p1%dm") == 0)
1818	    {
1819	      free (stream->set_a_background);
1820	      stream->set_a_background = xstrdup ("\033[4%dm");
1821	    }
1822# endif
1823
1824	  /* The termcap entry for cygwin is broken: It has no "ncv" value,
1825	     but bold and underline are actually rendered through colors.  */
1826	  if (strcmp (term, "cygwin") == 0)
1827	    stream->no_color_video |= 2 | 32;
1828
1829	  /* Done with tgetstr.  Detect possible buffer overflow.  */
1830	  #undef TEBP
1831	  if (memcmp (termentrybuf.canary, "CnRz", 4) != 0)
1832	    /* Buffer overflow!  */
1833	    abort ();
1834	}
1835#else
1836    /* Fallback code for platforms with neither the terminfo nor the termcap
1837       functions, such as mingw.
1838       Assume the ANSI escape sequences.  Extracted through
1839       "TERM=ansi infocmp", replacing \E with \033.  */
1840      stream->max_colors = 8;
1841      stream->no_color_video = 3;
1842      stream->set_a_foreground = xstrdup ("\033[3%p1%dm");
1843      stream->set_a_background = xstrdup ("\033[4%p1%dm");
1844      stream->orig_pair = xstrdup ("\033[39;49m");
1845      stream->enter_bold_mode = xstrdup ("\033[1m");
1846      stream->enter_underline_mode = xstrdup ("\033[4m");
1847      stream->exit_underline_mode = xstrdup ("\033[m");
1848      stream->exit_attribute_mode = xstrdup ("\033[0;10m");
1849#endif
1850
1851      /* AIX 4.3.2, IRIX 6.5, HP-UX 11, Solaris 7..10 all lack the
1852	 description of color capabilities of "xterm" and "xterms"
1853	 in their terminfo database.  But it is important to have
1854	 color in xterm.  So we provide the color capabilities here.  */
1855      if (stream->max_colors <= 1
1856	  && (strcmp (term, "xterm") == 0 || strcmp (term, "xterms") == 0))
1857	{
1858	  stream->max_colors = 8;
1859	  stream->set_a_foreground = xstrdup ("\033[3%p1%dm");
1860	  stream->set_a_background = xstrdup ("\033[4%p1%dm");
1861	  stream->orig_pair = xstrdup ("\033[39;49m");
1862	}
1863    }
1864
1865  /* Infer the capabilities.  */
1866  stream->supports_foreground =
1867    (stream->max_colors >= 8
1868     && (stream->set_a_foreground != NULL || stream->set_foreground != NULL)
1869     && stream->orig_pair != NULL);
1870  stream->supports_background =
1871    (stream->max_colors >= 8
1872     && (stream->set_a_background != NULL || stream->set_background != NULL)
1873     && stream->orig_pair != NULL);
1874  stream->colormodel =
1875    (stream->supports_foreground || stream->supports_background
1876     ? (term != NULL
1877	&& (/* Recognize xterm-16color, xterm-88color, xterm-256color.  */
1878	    (strlen (term) >= 5 && memcmp (term, "xterm", 5) == 0)
1879	    || /* Recognize rxvt-16color.  */
1880	       (strlen (term) >= 4 && memcmp (term, "rxvt", 7) == 0)
1881	    || /* Recognize konsole-16color.  */
1882	       (strlen (term) >= 7 && memcmp (term, "konsole", 7) == 0))
1883	? (stream->max_colors == 256 ? cm_xterm256 :
1884	   stream->max_colors == 88 ? cm_xterm88 :
1885	   stream->max_colors == 16 ? cm_xterm16 :
1886	   cm_xterm8)
1887	: cm_common8)
1888     : cm_monochrome);
1889  stream->supports_weight =
1890    (stream->enter_bold_mode != NULL && stream->exit_attribute_mode != NULL);
1891  stream->supports_posture =
1892    (stream->enter_italics_mode != NULL
1893     && (stream->exit_italics_mode != NULL
1894	 || stream->exit_attribute_mode != NULL));
1895  stream->supports_underline =
1896    (stream->enter_underline_mode != NULL
1897     && (stream->exit_underline_mode != NULL
1898	 || stream->exit_attribute_mode != NULL));
1899
1900  /* Initialize the buffer.  */
1901  stream->allocated = 120;
1902  stream->buffer = XNMALLOC (stream->allocated, char);
1903  stream->attrbuffer = XNMALLOC (stream->allocated, attributes_t);
1904  stream->buflen = 0;
1905
1906  /* Initialize the current attributes.  */
1907  stream->curr_attr.color = COLOR_DEFAULT;
1908  stream->curr_attr.bgcolor = COLOR_DEFAULT;
1909  stream->curr_attr.weight = WEIGHT_DEFAULT;
1910  stream->curr_attr.posture = POSTURE_DEFAULT;
1911  stream->curr_attr.underline = UNDERLINE_DEFAULT;
1912  stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
1913
1914  /* Register an exit handler.  */
1915  {
1916    static bool registered = false;
1917    if (!registered)
1918      {
1919	atexit (restore);
1920	registered = true;
1921      }
1922  }
1923
1924  return stream;
1925}
1926