Imported Upstream version 0.19.7
[platform/upstream/gettext.git] / gnulib-local / lib / term-ostream.oo.c
1 /* Output stream for attributed text, producing ANSI escape sequences.
2    Copyright (C) 2006-2008, 2015 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().  */
43 static 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.  */
56 typedef 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.  */
64 typedef 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.  */
72 static void
73 rgb_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.  */
137 static float
138 color_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.  */
179 static unsigned int
180 nearest_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.  */
226 static float
227 color_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.  */
240 typedef 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.  */
253 static inline term_color_t
254 rgb_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 */
271 static 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
284 static inline term_color_t
285 rgb_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.  */
310 static inline int
311 color_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 */
328 static 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
342 static inline term_color_t
343 rgb_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.  */
369 static 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
390 static inline term_color_t
391 rgb_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
423 static 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
516 static inline term_color_t
517 rgb_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
567 static 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
828 static inline term_color_t
829 rgb_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.  */
927 typedef 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
939 struct term_ostream : struct ostream
940 {
941 fields:
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.  */
981 static attributes_t
982 simplify_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.  */
1013 static int out_fd = -1;
1014
1015 /* Filename of out_fd.  */
1016 static const char *out_filename;
1017
1018 /* Output a single char to out_fd.  Ignore errors.  */
1019 static int
1020 out_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.  */
1030 static const char *restore_colors;
1031 static const char *restore_weight;
1032 static const char *restore_posture;
1033 static const char *restore_underline;
1034
1035 /* The exit handler.  */
1036 static void
1037 restore (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.  */
1054 static 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
1070 static sigset_t stopping_signal_set;
1071
1072 static void
1073 init_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.  */
1089 static inline void
1090 block_stopping_signals ()
1091 {
1092   init_stopping_signal_set ();
1093   sigprocmask (SIG_BLOCK, &stopping_signal_set, NULL);
1094 }
1095
1096 /* Stop delaying the stopping signals.  */
1097 static inline void
1098 unblock_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.  */
1105 static inline bool
1106 equal_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.  */
1116 static void
1117 out_error ()
1118 {
1119   error (EXIT_FAILURE, errno, _("error writing to %s"), out_filename);
1120 }
1121
1122 /* Output a single char to out_fd.  */
1123 static int
1124 out_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.  */
1138 static void
1139 out_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).  */
1427 static void
1428 output_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
1523 static term_color_t
1524 term_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
1545 static void
1546 term_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
1596 static void
1597 term_ostream::flush (term_ostream_t stream)
1598 {
1599   output_buffer (stream);
1600 }
1601
1602 static void
1603 term_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
1635 static term_color_t
1636 term_ostream::get_color (term_ostream_t stream)
1637 {
1638   return stream->curr_attr.color;
1639 }
1640
1641 static void
1642 term_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
1648 static term_color_t
1649 term_ostream::get_bgcolor (term_ostream_t stream)
1650 {
1651   return stream->curr_attr.bgcolor;
1652 }
1653
1654 static void
1655 term_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
1661 static term_weight_t
1662 term_ostream::get_weight (term_ostream_t stream)
1663 {
1664   return stream->curr_attr.weight;
1665 }
1666
1667 static void
1668 term_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
1674 static term_posture_t
1675 term_ostream::get_posture (term_ostream_t stream)
1676 {
1677   return stream->curr_attr.posture;
1678 }
1679
1680 static void
1681 term_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
1687 static term_underline_t
1688 term_ostream::get_underline (term_ostream_t stream)
1689 {
1690   return stream->curr_attr.underline;
1691 }
1692
1693 static void
1694 term_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
1702 static inline char *
1703 xstrdup0 (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
1714 term_ostream_t
1715 term_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 }