1 /* DO NOT EDIT! GENERATED AUTOMATICALLY! */
2 
3 #if !IS_CPLUSPLUS
4 #define term_ostream_representation any_ostream_representation
5 #endif
6 #line 1 "term-ostream.oo.c"
7 /* Output stream for attributed text, producing ANSI escape sequences.
8    Copyright (C) 2006-2008, 2017, 2019-2020 Free Software Foundation, Inc.
9    Written by Bruno Haible <bruno@clisp.org>, 2006.
10 
11    This program is free software: you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 3 of the License, or
14    (at your option) any later version.
15 
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20 
21    You should have received a copy of the GNU General Public License
22    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
23 
24 #include <config.h>
25 
26 /* Specification.  */
27 #include "term-ostream.h"
28 
29 #include <assert.h>
30 #include <errno.h>
31 #include <stdbool.h>
32 #include <stdint.h>
33 #include <stdlib.h>
34 #include <sys/time.h>
35 #include <string.h>
36 #include <unistd.h>
37 #if HAVE_TCDRAIN
38 # include <termios.h>
39 #endif
40 #if defined _WIN32 || defined __CYGWIN__ /* Windows */
41 # define HAVE_WINDOWS_CONSOLES 1
42 # include <windows.h>
43 #endif
44 
45 #include "error.h"
46 #include "full-write.h"
47 #include "get_ppid_of.h"
48 #include "get_progname_of.h"
49 #include "terminfo.h"
50 #include "xalloc.h"
51 #include "xgethostname.h"
52 #include "xsize.h"
53 #if HAVE_WINDOWS_CONSOLES
54 /* Get _get_osfhandle().  */
55 # if defined _WIN32 && ! defined __CYGWIN__
56 #  include "msvc-nothrow.h"
57 # else
58 #  include <io.h>
59 # endif
60 #endif
61 #include "gettext.h"
62 
63 #define _(str) gettext (str)
64 
65 #if HAVE_TPARAM
66 /* GNU termcap's tparam() function requires a buffer argument.  Make it so
67    large that there is no risk that tparam() needs to call malloc().  */
68 static char tparambuf[100];
69 /* Define tparm in terms of tparam.  In the scope of this file, it is called
70    with at most one argument after the string.  */
71 # define tparm(str, arg1) \
72   tparam (str, tparambuf, sizeof (tparambuf), arg1)
73 #endif
74 
75 
76 /* =========================== Color primitives =========================== */
77 
78 /* A color in RGB format.  */
79 typedef struct
80 {
81   unsigned int red   : 8; /* range 0..255 */
82   unsigned int green : 8; /* range 0..255 */
83   unsigned int blue  : 8; /* range 0..255 */
84 } rgb_t;
85 
86 /* A color in HSV (a.k.a. HSB) format.  */
87 typedef struct
88 {
89   float hue;        /* normalized to interval [0,6) */
90   float saturation; /* normalized to interval [0,1] */
91   float brightness; /* a.k.a. value, normalized to interval [0,1] */
92 } hsv_t;
93 
94 /* Conversion of a color in RGB to HSV format.  */
95 static void
rgb_to_hsv(rgb_t c,hsv_t * result)96 rgb_to_hsv (rgb_t c, hsv_t *result)
97 {
98   unsigned int r = c.red;
99   unsigned int g = c.green;
100   unsigned int b = c.blue;
101 
102   if (r > g)
103     {
104       if (b > r)
105         {
106           /* b > r > g, so max = b, min = g */
107           result->hue = 4.0f + (float) (r - g) / (float) (b - g);
108           result->saturation = 1.0f - (float) g / (float) b;
109           result->brightness = (float) b / 255.0f;
110         }
111       else if (b <= g)
112         {
113           /* r > g >= b, so max = r, min = b */
114           result->hue = 0.0f + (float) (g - b) / (float) (r - b);
115           result->saturation = 1.0f - (float) b / (float) r;
116           result->brightness = (float) r / 255.0f;
117         }
118       else
119         {
120           /* r >= b > g, so max = r, min = g */
121           result->hue = 6.0f - (float) (b - g) / (float) (r - g);
122           result->saturation = 1.0f - (float) g / (float) r;
123           result->brightness = (float) r / 255.0f;
124         }
125     }
126   else
127     {
128       if (b > g)
129         {
130           /* b > g >= r, so max = b, min = r */
131           result->hue = 4.0f - (float) (g - r) / (float) (b - r);
132           result->saturation = 1.0f - (float) r / (float) b;
133           result->brightness = (float) b / 255.0f;
134         }
135       else if (b < r)
136         {
137           /* g >= r > b, so max = g, min = b */
138           result->hue = 2.0f - (float) (r - b) / (float) (g - b);
139           result->saturation = 1.0f - (float) b / (float) g;
140           result->brightness = (float) g / 255.0f;
141         }
142       else if (g > r)
143         {
144           /* g >= b >= r, g > r, so max = g, min = r */
145           result->hue = 2.0f + (float) (b - r) / (float) (g - r);
146           result->saturation = 1.0f - (float) r / (float) g;
147           result->brightness = (float) g / 255.0f;
148         }
149       else
150         {
151           /* r = g = b.  A grey color.  */
152           result->hue = 0; /* arbitrary */
153           result->saturation = 0;
154           result->brightness = (float) r / 255.0f;
155         }
156     }
157 }
158 
159 /* Square of distance of two colors.  */
160 static float
color_distance(const hsv_t * color1,const hsv_t * color2)161 color_distance (const hsv_t *color1, const hsv_t *color2)
162 {
163 #if 0
164   /* Formula taken from "John Smith: Color Similarity",
165        http://www.ctr.columbia.edu/~jrsmith/html/pubs/acmmm96/node8.html.  */
166   float angle1 = color1->hue * 1.04719755f; /* normalize to [0,2π] */
167   float angle2 = color2->hue * 1.04719755f; /* normalize to [0,2π] */
168   float delta_x = color1->saturation * cosf (angle1)
169                   - color2->saturation * cosf (angle2);
170   float delta_y = color1->saturation * sinf (angle1)
171                   - color2->saturation * sinf (angle2);
172   float delta_v = color1->brightness
173                   - color2->brightness;
174 
175   return delta_x * delta_x + delta_y * delta_y + delta_v * delta_v;
176 #else
177   /* Formula that considers hue differences with more weight than saturation
178      or brightness differences, like the human eye does.  */
179   float delta_hue =
180     (color1->hue >= color2->hue
181      ? (color1->hue - color2->hue >= 3.0f
182         ? 6.0f + color2->hue - color1->hue
183         : color1->hue - color2->hue)
184      : (color2->hue - color1->hue >= 3.0f
185         ? 6.0f + color1->hue - color2->hue
186         : color2->hue - color1->hue));
187   float min_saturation =
188     (color1->saturation < color2->saturation
189      ? color1->saturation
190      : color2->saturation);
191   float delta_saturation = color1->saturation - color2->saturation;
192   float delta_brightness = color1->brightness - color2->brightness;
193 
194   return delta_hue * delta_hue * min_saturation
195          + delta_saturation * delta_saturation * 0.2f
196          + delta_brightness * delta_brightness * 0.8f;
197 #endif
198 }
199 
200 /* Return the index of the color in a color table that is nearest to a given
201    color.  */
202 static unsigned int
nearest_color(rgb_t given,const rgb_t * table,unsigned int table_size)203 nearest_color (rgb_t given, const rgb_t *table, unsigned int table_size)
204 {
205   hsv_t given_hsv;
206   unsigned int best_index;
207   float best_distance;
208   unsigned int i;
209 
210   assert (table_size > 0);
211 
212   rgb_to_hsv (given, &given_hsv);
213 
214   best_index = 0;
215   best_distance = 1000000.0f;
216   for (i = 0; i < table_size; i++)
217     {
218       hsv_t i_hsv;
219 
220       rgb_to_hsv (table[i], &i_hsv);
221 
222       /* Avoid converting a color to grey, or fading out a color too much.  */
223       if (i_hsv.saturation > given_hsv.saturation * 0.5f)
224         {
225           float distance = color_distance (&given_hsv, &i_hsv);
226           if (distance < best_distance)
227             {
228               best_index = i;
229               best_distance = distance;
230             }
231         }
232     }
233 
234 #if 0 /* Debugging code */
235   hsv_t best_hsv;
236   rgb_to_hsv (table[best_index], &best_hsv);
237   fprintf (stderr, "nearest: (%d,%d,%d) = (%f,%f,%f)\n    -> (%f,%f,%f) = (%d,%d,%d)\n",
238                    given.red, given.green, given.blue,
239                    (double)given_hsv.hue, (double)given_hsv.saturation, (double)given_hsv.brightness,
240                    (double)best_hsv.hue, (double)best_hsv.saturation, (double)best_hsv.brightness,
241                    table[best_index].red, table[best_index].green, table[best_index].blue);
242 #endif
243 
244   return best_index;
245 }
246 
247 /* The luminance of a color.  This is the brightness of the color, as it
248    appears to the human eye.  This must be used in color to grey conversion.  */
249 static float
color_luminance(int r,int g,int b)250 color_luminance (int r, int g, int b)
251 {
252   /* Use the luminance model used by NTSC and JPEG.
253      Taken from http://www.fho-emden.de/~hoffmann/gray10012001.pdf .
254      No need to care about rounding errors leading to luminance > 1;
255      this cannot happen.  */
256   return (0.299f * r + 0.587f * g + 0.114f * b) / 255.0f;
257 }
258 
259 
260 /* ============================= Color models ============================= */
261 
262 /* The color model used by the terminal.  */
263 typedef enum
264 {
265   cm_monochrome,        /* No colors.  */
266   cm_common8,           /* Usual terminal with at least 8 colors.  */
267   cm_xterm8,            /* TERM=xterm, with 8 colors.  */
268   cm_xterm16,           /* TERM=xterm-16color, with 16 colors.  */
269   cm_xterm88,           /* TERM=xterm-88color, with 88 colors.  */
270   cm_xterm256,          /* TERM=xterm-256color, with 256 colors.  */
271   cm_xtermrgb           /* TERM=xterm-direct, with 256*256*256 colors.  */
272 } colormodel_t;
273 
274 /* ----------------------- cm_monochrome color model ----------------------- */
275 
276 /* A non-default color index doesn't exist in this color model.  */
277 static inline term_color_t
rgb_to_color_monochrome(void)278 rgb_to_color_monochrome (void)
279 {
280   return COLOR_DEFAULT;
281 }
282 
283 /* ------------------------ cm_common8 color model ------------------------ */
284 
285 /* A non-default color index is in the range 0..7.
286                        RGB components
287    COLOR_BLACK         000
288    COLOR_BLUE          001
289    COLOR_GREEN         010
290    COLOR_CYAN          011
291    COLOR_RED           100
292    COLOR_MAGENTA       101
293    COLOR_YELLOW        110
294    COLOR_WHITE         111 */
295 static const rgb_t colors_of_common8[8] =
296 {
297   /* R    G    B        grey  index */
298   {   0,   0,   0 }, /* 0.000   0 */
299   {   0,   0, 255 },
300   {   0, 255,   0 },
301   {   0, 255, 255 },
302   { 255,   0,   0 },
303   { 255,   0, 255 },
304   { 255, 255,   0 },
305   { 255, 255, 255 }  /* 1.000   7 */
306 };
307 
308 static inline term_color_t
rgb_to_color_common8(int r,int g,int b)309 rgb_to_color_common8 (int r, int g, int b)
310 {
311   rgb_t color;
312   hsv_t hsv;
313 
314   color.red = r; color.green = g; color.blue = b;
315   rgb_to_hsv (color, &hsv);
316 
317   if (hsv.saturation < 0.065f)
318     {
319       /* Greyscale approximation.  */
320       float luminance = color_luminance (r, g, b);
321       if (luminance < 0.500f)
322         return 0;
323       else
324         return 7;
325     }
326   else
327     /* Color approximation.  */
328     return nearest_color (color, colors_of_common8, 8);
329 }
330 
331 /* Convert a cm_common8 color in RGB encoding to BGR encoding.
332    See the ncurses terminfo(5) manual page, section "Color Handling", for an
333    explanation why this is needed.  */
334 static _GL_ASYNC_SAFE inline int
color_bgr(term_color_t color)335 color_bgr (term_color_t color)
336 {
337   return ((color & 4) >> 2) | (color & 2) | ((color & 1) << 2);
338 }
339 
340 /* ------------------------- cm_xterm8 color model ------------------------- */
341 
342 /* A non-default color index is in the range 0..7.
343                        BGR components
344    COLOR_BLACK         000
345    COLOR_RED           001
346    COLOR_GREEN         010
347    COLOR_YELLOW        011
348    COLOR_BLUE          100
349    COLOR_MAGENTA       101
350    COLOR_CYAN          110
351    COLOR_WHITE         111 */
352 static const rgb_t colors_of_xterm8[8] =
353 {
354   /* The real xterm's colors are dimmed; assume full-brightness instead.  */
355   /* R    G    B        grey  index */
356   {   0,   0,   0 }, /* 0.000   0 */
357   { 255,   0,   0 },
358   {   0, 255,   0 },
359   { 255, 255,   0 },
360   {   0,   0, 255 },
361   { 255,   0, 255 },
362   {   0, 255, 255 },
363   { 255, 255, 255 }  /* 1.000   7 */
364 };
365 
366 static inline term_color_t
rgb_to_color_xterm8(int r,int g,int b)367 rgb_to_color_xterm8 (int r, int g, int b)
368 {
369   rgb_t color;
370   hsv_t hsv;
371 
372   color.red = r; color.green = g; color.blue = b;
373   rgb_to_hsv (color, &hsv);
374 
375   if (hsv.saturation < 0.065f)
376     {
377       /* Greyscale approximation.  */
378       float luminance = color_luminance (r, g, b);
379       if (luminance < 0.500f)
380         return 0;
381       else
382         return 7;
383     }
384   else
385     /* Color approximation.  */
386     return nearest_color (color, colors_of_xterm8, 8);
387 }
388 
389 /* ------------------------ cm_xterm16 color model ------------------------ */
390 
391 /* A non-default color index is in the range 0..15.
392    The RGB values come from xterm's XTerm-col.ad.  */
393 static const rgb_t colors_of_xterm16[16] =
394 {
395   /* R    G    B        grey  index */
396   {   0,   0,   0 }, /* 0.000   0 */
397   { 205,   0,   0 },
398   {   0, 205,   0 },
399   { 205, 205,   0 },
400   {   0,   0, 205 },
401   { 205,   0, 205 },
402   {   0, 205, 205 },
403   { 229, 229, 229 }, /* 0.898   7 */
404   {  77,  77,  77 }, /* 0.302   8 */
405   { 255,   0,   0 },
406   {   0, 255,   0 },
407   { 255, 255,   0 },
408   {   0,   0, 255 },
409   { 255,   0, 255 },
410   {   0, 255, 255 },
411   { 255, 255, 255 }  /* 1.000  15 */
412 };
413 
414 static inline term_color_t
rgb_to_color_xterm16(int r,int g,int b)415 rgb_to_color_xterm16 (int r, int g, int b)
416 {
417   rgb_t color;
418   hsv_t hsv;
419 
420   color.red = r; color.green = g; color.blue = b;
421   rgb_to_hsv (color, &hsv);
422 
423   if (hsv.saturation < 0.065f)
424     {
425       /* Greyscale approximation.  */
426       float luminance = color_luminance (r, g, b);
427       if (luminance < 0.151f)
428         return 0;
429       else if (luminance < 0.600f)
430         return 8;
431       else if (luminance < 0.949f)
432         return 7;
433       else
434         return 15;
435     }
436   else
437     /* Color approximation.  */
438     return nearest_color (color, colors_of_xterm16, 16);
439 }
440 
441 /* ------------------------ cm_xterm88 color model ------------------------ */
442 
443 /* A non-default color index is in the range 0..87.
444    Colors 0..15 are the same as in the cm_xterm16 color model.
445    Colors 16..87 are defined in xterm's 88colres.h.  */
446 
447 static const rgb_t colors_of_xterm88[88] =
448 {
449   /* R    G    B        grey  index */
450   {   0,   0,   0 }, /* 0.000   0 */
451   { 205,   0,   0 },
452   {   0, 205,   0 },
453   { 205, 205,   0 },
454   {   0,   0, 205 },
455   { 205,   0, 205 },
456   {   0, 205, 205 },
457   { 229, 229, 229 }, /* 0.898   7 */
458   {  77,  77,  77 }, /* 0.302   8 */
459   { 255,   0,   0 },
460   {   0, 255,   0 },
461   { 255, 255,   0 },
462   {   0,   0, 255 },
463   { 255,   0, 255 },
464   {   0, 255, 255 },
465   { 255, 255, 255 }, /* 1.000  15 */
466   {   0,   0,   0 }, /* 0.000  16 */
467   {   0,   0, 139 },
468   {   0,   0, 205 },
469   {   0,   0, 255 },
470   {   0, 139,   0 },
471   {   0, 139, 139 },
472   {   0, 139, 205 },
473   {   0, 139, 255 },
474   {   0, 205,   0 },
475   {   0, 205, 139 },
476   {   0, 205, 205 },
477   {   0, 205, 255 },
478   {   0, 255,   0 },
479   {   0, 255, 139 },
480   {   0, 255, 205 },
481   {   0, 255, 255 },
482   { 139,   0,   0 },
483   { 139,   0, 139 },
484   { 139,   0, 205 },
485   { 139,   0, 255 },
486   { 139, 139,   0 },
487   { 139, 139, 139 }, /* 0.545  37 */
488   { 139, 139, 205 },
489   { 139, 139, 255 },
490   { 139, 205,   0 },
491   { 139, 205, 139 },
492   { 139, 205, 205 },
493   { 139, 205, 255 },
494   { 139, 255,   0 },
495   { 139, 255, 139 },
496   { 139, 255, 205 },
497   { 139, 255, 255 },
498   { 205,   0,   0 },
499   { 205,   0, 139 },
500   { 205,   0, 205 },
501   { 205,   0, 255 },
502   { 205, 139,   0 },
503   { 205, 139, 139 },
504   { 205, 139, 205 },
505   { 205, 139, 255 },
506   { 205, 205,   0 },
507   { 205, 205, 139 },
508   { 205, 205, 205 }, /* 0.804  58 */
509   { 205, 205, 255 },
510   { 205, 255,   0 },
511   { 205, 255, 139 },
512   { 205, 255, 205 },
513   { 205, 255, 255 },
514   { 255,   0,   0 },
515   { 255,   0, 139 },
516   { 255,   0, 205 },
517   { 255,   0, 255 },
518   { 255, 139,   0 },
519   { 255, 139, 139 },
520   { 255, 139, 205 },
521   { 255, 139, 255 },
522   { 255, 205,   0 },
523   { 255, 205, 139 },
524   { 255, 205, 205 },
525   { 255, 205, 255 },
526   { 255, 255,   0 },
527   { 255, 255, 139 },
528   { 255, 255, 205 },
529   { 255, 255, 255 }, /* 1.000  79 */
530   {  46,  46,  46 }, /* 0.180  80 */
531   {  92,  92,  92 }, /* 0.361  81 */
532   { 115, 115, 115 }, /* 0.451  82 */
533   { 139, 139, 139 }, /* 0.545  83 */
534   { 162, 162, 162 }, /* 0.635  84 */
535   { 185, 185, 185 }, /* 0.725  85 */
536   { 208, 208, 208 }, /* 0.816  86 */
537   { 231, 231, 231 }  /* 0.906  87 */
538 };
539 
540 static inline term_color_t
rgb_to_color_xterm88(int r,int g,int b)541 rgb_to_color_xterm88 (int r, int g, int b)
542 {
543   rgb_t color;
544   hsv_t hsv;
545 
546   color.red = r; color.green = g; color.blue = b;
547   rgb_to_hsv (color, &hsv);
548 
549   if (hsv.saturation < 0.065f)
550     {
551       /* Greyscale approximation.  */
552       float luminance = color_luminance (r, g, b);
553       if (luminance < 0.090f)
554         return 0;
555       else if (luminance < 0.241f)
556         return 80;
557       else if (luminance < 0.331f)
558         return 8;
559       else if (luminance < 0.406f)
560         return 81;
561       else if (luminance < 0.498f)
562         return 82;
563       else if (luminance < 0.585f)
564         return 37;
565       else if (luminance < 0.680f)
566         return 84;
567       else if (luminance < 0.764f)
568         return 85;
569       else if (luminance < 0.810f)
570         return 58;
571       else if (luminance < 0.857f)
572         return 86;
573       else if (luminance < 0.902f)
574         return 7;
575       else if (luminance < 0.953f)
576         return 87;
577       else
578         return 15;
579     }
580   else
581     /* Color approximation.  */
582     return nearest_color (color, colors_of_xterm88, 88);
583 }
584 
585 /* ------------------------ cm_xterm256 color model ------------------------ */
586 
587 /* A non-default color index is in the range 0..255.
588    Colors 0..15 are the same as in the cm_xterm16 color model.
589    Colors 16..255 are defined in xterm's 256colres.h.  */
590 
591 static const rgb_t colors_of_xterm256[256] =
592 {
593   /* R    G    B        grey  index */
594   {   0,   0,   0 }, /* 0.000   0 */
595   { 205,   0,   0 },
596   {   0, 205,   0 },
597   { 205, 205,   0 },
598   {   0,   0, 205 },
599   { 205,   0, 205 },
600   {   0, 205, 205 },
601   { 229, 229, 229 }, /* 0.898   7 */
602   {  77,  77,  77 }, /* 0.302   8 */
603   { 255,   0,   0 },
604   {   0, 255,   0 },
605   { 255, 255,   0 },
606   {   0,   0, 255 },
607   { 255,   0, 255 },
608   {   0, 255, 255 },
609   { 255, 255, 255 }, /* 1.000  15 */
610   {   0,   0,   0 }, /* 0.000  16 */
611   {   0,   0,  42 },
612   {   0,   0,  85 },
613   {   0,   0, 127 },
614   {   0,   0, 170 },
615   {   0,   0, 212 },
616   {   0,  42,   0 },
617   {   0,  42,  42 },
618   {   0,  42,  85 },
619   {   0,  42, 127 },
620   {   0,  42, 170 },
621   {   0,  42, 212 },
622   {   0,  85,   0 },
623   {   0,  85,  42 },
624   {   0,  85,  85 },
625   {   0,  85, 127 },
626   {   0,  85, 170 },
627   {   0,  85, 212 },
628   {   0, 127,   0 },
629   {   0, 127,  42 },
630   {   0, 127,  85 },
631   {   0, 127, 127 },
632   {   0, 127, 170 },
633   {   0, 127, 212 },
634   {   0, 170,   0 },
635   {   0, 170,  42 },
636   {   0, 170,  85 },
637   {   0, 170, 127 },
638   {   0, 170, 170 },
639   {   0, 170, 212 },
640   {   0, 212,   0 },
641   {   0, 212,  42 },
642   {   0, 212,  85 },
643   {   0, 212, 127 },
644   {   0, 212, 170 },
645   {   0, 212, 212 },
646   {  42,   0,   0 },
647   {  42,   0,  42 },
648   {  42,   0,  85 },
649   {  42,   0, 127 },
650   {  42,   0, 170 },
651   {  42,   0, 212 },
652   {  42,  42,   0 },
653   {  42,  42,  42 }, /* 0.165  59 */
654   {  42,  42,  85 },
655   {  42,  42, 127 },
656   {  42,  42, 170 },
657   {  42,  42, 212 },
658   {  42,  85,   0 },
659   {  42,  85,  42 },
660   {  42,  85,  85 },
661   {  42,  85, 127 },
662   {  42,  85, 170 },
663   {  42,  85, 212 },
664   {  42, 127,   0 },
665   {  42, 127,  42 },
666   {  42, 127,  85 },
667   {  42, 127, 127 },
668   {  42, 127, 170 },
669   {  42, 127, 212 },
670   {  42, 170,   0 },
671   {  42, 170,  42 },
672   {  42, 170,  85 },
673   {  42, 170, 127 },
674   {  42, 170, 170 },
675   {  42, 170, 212 },
676   {  42, 212,   0 },
677   {  42, 212,  42 },
678   {  42, 212,  85 },
679   {  42, 212, 127 },
680   {  42, 212, 170 },
681   {  42, 212, 212 },
682   {  85,   0,   0 },
683   {  85,   0,  42 },
684   {  85,   0,  85 },
685   {  85,   0, 127 },
686   {  85,   0, 170 },
687   {  85,   0, 212 },
688   {  85,  42,   0 },
689   {  85,  42,  42 },
690   {  85,  42,  85 },
691   {  85,  42, 127 },
692   {  85,  42, 170 },
693   {  85,  42, 212 },
694   {  85,  85,   0 },
695   {  85,  85,  42 },
696   {  85,  85,  85 }, /* 0.333 102 */
697   {  85,  85, 127 },
698   {  85,  85, 170 },
699   {  85,  85, 212 },
700   {  85, 127,   0 },
701   {  85, 127,  42 },
702   {  85, 127,  85 },
703   {  85, 127, 127 },
704   {  85, 127, 170 },
705   {  85, 127, 212 },
706   {  85, 170,   0 },
707   {  85, 170,  42 },
708   {  85, 170,  85 },
709   {  85, 170, 127 },
710   {  85, 170, 170 },
711   {  85, 170, 212 },
712   {  85, 212,   0 },
713   {  85, 212,  42 },
714   {  85, 212,  85 },
715   {  85, 212, 127 },
716   {  85, 212, 170 },
717   {  85, 212, 212 },
718   { 127,   0,   0 },
719   { 127,   0,  42 },
720   { 127,   0,  85 },
721   { 127,   0, 127 },
722   { 127,   0, 170 },
723   { 127,   0, 212 },
724   { 127,  42,   0 },
725   { 127,  42,  42 },
726   { 127,  42,  85 },
727   { 127,  42, 127 },
728   { 127,  42, 170 },
729   { 127,  42, 212 },
730   { 127,  85,   0 },
731   { 127,  85,  42 },
732   { 127,  85,  85 },
733   { 127,  85, 127 },
734   { 127,  85, 170 },
735   { 127,  85, 212 },
736   { 127, 127,   0 },
737   { 127, 127,  42 },
738   { 127, 127,  85 },
739   { 127, 127, 127 }, /* 0.498 145 */
740   { 127, 127, 170 },
741   { 127, 127, 212 },
742   { 127, 170,   0 },
743   { 127, 170,  42 },
744   { 127, 170,  85 },
745   { 127, 170, 127 },
746   { 127, 170, 170 },
747   { 127, 170, 212 },
748   { 127, 212,   0 },
749   { 127, 212,  42 },
750   { 127, 212,  85 },
751   { 127, 212, 127 },
752   { 127, 212, 170 },
753   { 127, 212, 212 },
754   { 170,   0,   0 },
755   { 170,   0,  42 },
756   { 170,   0,  85 },
757   { 170,   0, 127 },
758   { 170,   0, 170 },
759   { 170,   0, 212 },
760   { 170,  42,   0 },
761   { 170,  42,  42 },
762   { 170,  42,  85 },
763   { 170,  42, 127 },
764   { 170,  42, 170 },
765   { 170,  42, 212 },
766   { 170,  85,   0 },
767   { 170,  85,  42 },
768   { 170,  85,  85 },
769   { 170,  85, 127 },
770   { 170,  85, 170 },
771   { 170,  85, 212 },
772   { 170, 127,   0 },
773   { 170, 127,  42 },
774   { 170, 127,  85 },
775   { 170, 127, 127 },
776   { 170, 127, 170 },
777   { 170, 127, 212 },
778   { 170, 170,   0 },
779   { 170, 170,  42 },
780   { 170, 170,  85 },
781   { 170, 170, 127 },
782   { 170, 170, 170 }, /* 0.667 188 */
783   { 170, 170, 212 },
784   { 170, 212,   0 },
785   { 170, 212,  42 },
786   { 170, 212,  85 },
787   { 170, 212, 127 },
788   { 170, 212, 170 },
789   { 170, 212, 212 },
790   { 212,   0,   0 },
791   { 212,   0,  42 },
792   { 212,   0,  85 },
793   { 212,   0, 127 },
794   { 212,   0, 170 },
795   { 212,   0, 212 },
796   { 212,  42,   0 },
797   { 212,  42,  42 },
798   { 212,  42,  85 },
799   { 212,  42, 127 },
800   { 212,  42, 170 },
801   { 212,  42, 212 },
802   { 212,  85,   0 },
803   { 212,  85,  42 },
804   { 212,  85,  85 },
805   { 212,  85, 127 },
806   { 212,  85, 170 },
807   { 212,  85, 212 },
808   { 212, 127,   0 },
809   { 212, 127,  42 },
810   { 212, 127,  85 },
811   { 212, 127, 127 },
812   { 212, 127, 170 },
813   { 212, 127, 212 },
814   { 212, 170,   0 },
815   { 212, 170,  42 },
816   { 212, 170,  85 },
817   { 212, 170, 127 },
818   { 212, 170, 170 },
819   { 212, 170, 212 },
820   { 212, 212,   0 },
821   { 212, 212,  42 },
822   { 212, 212,  85 },
823   { 212, 212, 127 },
824   { 212, 212, 170 },
825   { 212, 212, 212 }, /* 0.831 231 */
826   {   8,   8,   8 }, /* 0.031 232 */
827   {  18,  18,  18 }, /* 0.071 233 */
828   {  28,  28,  28 }, /* 0.110 234 */
829   {  38,  38,  38 }, /* 0.149 235 */
830   {  48,  48,  48 }, /* 0.188 236 */
831   {  58,  58,  58 }, /* 0.227 237 */
832   {  68,  68,  68 }, /* 0.267 238 */
833   {  78,  78,  78 }, /* 0.306 239 */
834   {  88,  88,  88 }, /* 0.345 240 */
835   {  98,  98,  98 }, /* 0.384 241 */
836   { 108, 108, 108 }, /* 0.424 242 */
837   { 118, 118, 118 }, /* 0.463 243 */
838   { 128, 128, 128 }, /* 0.502 244 */
839   { 138, 138, 138 }, /* 0.541 245 */
840   { 148, 148, 148 }, /* 0.580 246 */
841   { 158, 158, 158 }, /* 0.620 247 */
842   { 168, 168, 168 }, /* 0.659 248 */
843   { 178, 178, 178 }, /* 0.698 249 */
844   { 188, 188, 188 }, /* 0.737 250 */
845   { 198, 198, 198 }, /* 0.776 251 */
846   { 208, 208, 208 }, /* 0.816 252 */
847   { 218, 218, 218 }, /* 0.855 253 */
848   { 228, 228, 228 }, /* 0.894 254 */
849   { 238, 238, 238 }  /* 0.933 255 */
850 };
851 
852 static inline term_color_t
rgb_to_color_xterm256(int r,int g,int b)853 rgb_to_color_xterm256 (int r, int g, int b)
854 {
855   rgb_t color;
856   hsv_t hsv;
857 
858   color.red = r; color.green = g; color.blue = b;
859   rgb_to_hsv (color, &hsv);
860 
861   if (hsv.saturation < 0.065f)
862     {
863       /* Greyscale approximation.  */
864       float luminance = color_luminance (r, g, b);
865       if (luminance < 0.015f)
866         return 0;
867       else if (luminance < 0.051f)
868         return 232;
869       else if (luminance < 0.090f)
870         return 233;
871       else if (luminance < 0.129f)
872         return 234;
873       else if (luminance < 0.157f)
874         return 235;
875       else if (luminance < 0.177f)
876         return 59;
877       else if (luminance < 0.207f)
878         return 236;
879       else if (luminance < 0.247f)
880         return 237;
881       else if (luminance < 0.284f)
882         return 238;
883       else if (luminance < 0.304f)
884         return 8;
885       else if (luminance < 0.319f)
886         return 239;
887       else if (luminance < 0.339f)
888         return 102;
889       else if (luminance < 0.364f)
890         return 240;
891       else if (luminance < 0.404f)
892         return 241;
893       else if (luminance < 0.443f)
894         return 242;
895       else if (luminance < 0.480f)
896         return 243;
897       else if (luminance < 0.500f)
898         return 145;
899       else if (luminance < 0.521f)
900         return 244;
901       else if (luminance < 0.560f)
902         return 245;
903       else if (luminance < 0.600f)
904         return 246;
905       else if (luminance < 0.639f)
906         return 247;
907       else if (luminance < 0.663f)
908         return 248;
909       else if (luminance < 0.682f)
910         return 188;
911       else if (luminance < 0.717f)
912         return 249;
913       else if (luminance < 0.756f)
914         return 250;
915       else if (luminance < 0.796f)
916         return 251;
917       else if (luminance < 0.823f)
918         return 252;
919       else if (luminance < 0.843f)
920         return 231;
921       else if (luminance < 0.874f)
922         return 253;
923       else if (luminance < 0.896f)
924         return 254;
925       else if (luminance < 0.915f)
926         return 7;
927       else if (luminance < 0.966f)
928         return 255;
929       else
930         return 15;
931     }
932   else
933     /* Color approximation.  */
934     return nearest_color (color, colors_of_xterm256, 256);
935 }
936 
937 /* ------------------------ cm_xtermrgb color model ------------------------ */
938 
939 /* We represent a color as an RGB triplet: (r << 16) | (g << 8) | (b << 0),
940    where r, g, b are in the range [0..255].  */
941 
942 static inline term_color_t
rgb_to_color_xtermrgb(int r,int g,int b)943 rgb_to_color_xtermrgb (int r, int g, int b)
944 {
945   return (r << 16) | (g << 8) | (b << 0);
946 }
947 
948 
949 /* ============================== hyperlink_t ============================== */
950 
951 /* A hyperlink is a heap-allocated structure that can be assigned to a run
952    of characters.  */
953 typedef struct
954 {
955   /* URL.
956      Should better be <= 2083 bytes long (because of Microsoft Internet
957      Explorer).  */
958   char *ref;
959   /* Id.
960      Used when the same hyperlink persists across newlines.
961      Should better be <= 256 bytes long (because of VTE and iTerm2).  */
962   char *id;
963   /* Same as id, if non-NULL.  Or some generated id.  */
964   char *real_id;
965 } hyperlink_t;
966 
967 static inline void
free_hyperlink(hyperlink_t * hyperlink)968 free_hyperlink (hyperlink_t *hyperlink)
969 {
970   free (hyperlink->ref);
971   free (hyperlink->real_id);
972   free (hyperlink);
973 }
974 
975 
976 /* ============================= attributes_t ============================= */
977 
978 /* ANSI C and ISO C99 6.7.2.1.(4) forbid use of bit fields for types other
979    than 'int' or 'unsigned int'.
980    On the other hand, C++ forbids conversion between enum types and integer
981    types without an explicit cast.  */
982 #ifdef __cplusplus
983 # define BITFIELD_TYPE(orig_type,integer_type) orig_type
984 #else
985 # define BITFIELD_TYPE(orig_type,integer_type) integer_type
986 #endif
987 
988 /* Attributes that can be set on a character.  */
989 typedef struct
990 {
991   BITFIELD_TYPE(term_color_t,     signed int)   color     : 25;
992   BITFIELD_TYPE(term_color_t,     signed int)   bgcolor   : 25;
993   BITFIELD_TYPE(term_weight_t,    unsigned int) weight    : 1;
994   BITFIELD_TYPE(term_posture_t,   unsigned int) posture   : 1;
995   BITFIELD_TYPE(term_underline_t, unsigned int) underline : 1;
996   /* Hyperlink, or NULL for none.  */
997   hyperlink_t *hyperlink;
998 } attributes_t;
999 
1000 /* Compare two sets of attributes for equality.  */
1001 static inline bool
equal_attributes(attributes_t attr1,attributes_t attr2)1002 equal_attributes (attributes_t attr1, attributes_t attr2)
1003 {
1004   return (attr1.color == attr2.color
1005           && attr1.bgcolor == attr2.bgcolor
1006           && attr1.weight == attr2.weight
1007           && attr1.posture == attr2.posture
1008           && attr1.underline == attr2.underline
1009           && attr1.hyperlink == attr2.hyperlink);
1010 }
1011 
1012 
1013 /* ============================ EINTR handling ============================ */
1014 
1015 /* EINTR handling for tcdrain().
1016    This function can return -1/EINTR even when we don't have any
1017    signal handlers set up, namely when we get interrupted via SIGSTOP.  */
1018 
1019 #if HAVE_TCDRAIN
1020 
1021 static inline int
nonintr_tcdrain(int fd)1022 nonintr_tcdrain (int fd)
1023 {
1024   int retval;
1025 
1026   do
1027     retval = tcdrain (fd);
1028   while (retval < 0 && errno == EINTR);
1029 
1030   return retval;
1031 }
1032 
1033 #endif
1034 
1035 
1036 /* ============================ term_ostream_t ============================ */
1037 
1038 #line 1039 "term-ostream.c"
1039 #include "term_ostream.priv.h"
1040 
1041 const typeinfo_t term_ostream_typeinfo = { "term_ostream" };
1042 
1043 static const typeinfo_t * const term_ostream_superclasses[] =
1044   { term_ostream_SUPERCLASSES };
1045 
1046 #define super ostream_vtable
1047 
1048 #line 1108 "term-ostream.oo.c"
1049 
1050 static struct term_style_control_data *
get_control_data(term_ostream_t stream)1051 get_control_data (term_ostream_t stream)
1052 {
1053   return &stream->control_data;
1054 }
1055 
1056 /* Simplify attributes, according to the terminal's capabilities.  */
1057 static attributes_t
simplify_attributes(term_ostream_t stream,attributes_t attr)1058 simplify_attributes (term_ostream_t stream, attributes_t attr)
1059 {
1060   if ((attr.color != COLOR_DEFAULT || attr.bgcolor != COLOR_DEFAULT)
1061       && stream->no_color_video > 0)
1062     {
1063       /* When colors and attributes can not be represented simultaneously,
1064          we give preference to the color.  */
1065       if (stream->no_color_video & 2)
1066         /* Colors conflict with underlining.  */
1067         attr.underline = UNDERLINE_OFF;
1068       if (stream->no_color_video & 32)
1069         /* Colors conflict with bold weight.  */
1070         attr.weight = WEIGHT_NORMAL;
1071     }
1072   if (!stream->supports_foreground)
1073     attr.color = COLOR_DEFAULT;
1074   if (!stream->supports_background)
1075     attr.bgcolor = COLOR_DEFAULT;
1076   if (!stream->supports_weight)
1077     attr.weight = WEIGHT_DEFAULT;
1078   if (!stream->supports_posture)
1079     attr.posture = POSTURE_DEFAULT;
1080   if (!stream->supports_underline)
1081     attr.underline = UNDERLINE_DEFAULT;
1082   if (!stream->supports_hyperlink)
1083     attr.hyperlink = NULL;
1084   return attr;
1085 }
1086 
1087 /* Generate an id for a hyperlink.  */
1088 static char *
generate_hyperlink_id(term_ostream_t stream)1089 generate_hyperlink_id (term_ostream_t stream)
1090 {
1091   /* A UUID would be optimal, but is overkill here.  An id of 128 bits
1092      (32 hexadecimal digits) should be sufficient.  */
1093   static const char hexdigits[16] =
1094     {
1095       '0', '1', '2', '3', '4', '5', '6', '7',
1096       '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
1097     };
1098   char *id = (char *) xmalloc (128 / 4 + 1);
1099   uint32_t words[4] =
1100     {
1101       stream->hostname_hash,
1102       (uint32_t) (stream->start_time >> 32),
1103       (uint32_t) stream->start_time,
1104       stream->id_serial
1105     };
1106   char *p = id;
1107   unsigned int i;
1108   for (i = 0; i < 4; i++)
1109     {
1110       uint32_t word = words[i];
1111       unsigned int j;
1112       for (j = 0; j < 32 / 4; j++)
1113         *p++ = hexdigits[(word >> (32 - 4 * (j + 1))) & 0x0f];
1114     }
1115   *p = '\0';
1116   stream->id_serial++;
1117   return id;
1118 }
1119 
1120 /* Stream that contains information about how the various out_* functions shall
1121    do output.  */
1122 static term_ostream_t volatile out_stream;
1123 
1124 /* File descriptor to which out_char and out_char_unchecked shall output escape
1125    sequences.
1126    Same as (out_stream != NULL ? out_stream->fd : -1).  */
1127 static int volatile out_fd = -1;
1128 
1129 /* Signal error after full_write failed.  */
1130 static void
out_error(void)1131 out_error (void)
1132 {
1133   error (EXIT_FAILURE, errno, _("error writing to %s"), out_stream->filename);
1134 }
1135 
1136 /* Output a single char to out_fd.  */
1137 static int
out_char(int c)1138 out_char (int c)
1139 {
1140   char bytes[1];
1141 
1142   bytes[0] = (char)c;
1143   /* We have to write directly to the file descriptor, not to a buffer with
1144      the same destination, because of the padding and sleeping that tputs()
1145      does.  */
1146   if (full_write (out_fd, bytes, 1) < 1)
1147     out_error ();
1148   return 0;
1149 }
1150 
1151 /* Output a single char to out_fd.  Ignore errors.  */
1152 static _GL_ASYNC_SAFE int
out_char_unchecked(int c)1153 out_char_unchecked (int c)
1154 {
1155   char bytes[1];
1156 
1157   bytes[0] = (char)c;
1158   full_write (out_fd, bytes, 1);
1159   return 0;
1160 }
1161 
1162 /* Output escape sequences to switch the foreground color to NEW_COLOR.  */
1163 static _GL_ASYNC_SAFE void
out_color_change(term_ostream_t stream,term_color_t new_color,bool async_safe)1164 out_color_change (term_ostream_t stream, term_color_t new_color,
1165                   bool async_safe)
1166 {
1167   assert (stream->supports_foreground);
1168   assert (new_color != COLOR_DEFAULT);
1169   switch (stream->colormodel)
1170     {
1171     case cm_common8:
1172       assert (new_color >= 0 && new_color < 8);
1173       #if HAVE_WINDOWS_CONSOLES
1174       if (stream->is_windows_console)
1175         {
1176           /* SetConsoleTextAttribute
1177              <https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute>
1178              <https://docs.microsoft.com/en-us/windows/console/console-screen-buffers>  */
1179           /* Assign to stream->current_console_attributes *before* calling
1180              SetConsoleTextAttribute, otherwise async_set_attributes_from_default
1181              will not do its job correctly.  */
1182           stream->current_console_attributes =
1183             (stream->current_console_attributes & ~(7 << 0))
1184             | (new_color << 0);
1185           SetConsoleTextAttribute (stream->handle, stream->current_console_attributes);
1186         }
1187       else
1188       #endif
1189         {
1190           if (stream->set_a_foreground != NULL)
1191             tputs (tparm (stream->set_a_foreground, color_bgr (new_color)),
1192                    1, async_safe ? out_char_unchecked : out_char);
1193           else
1194             tputs (tparm (stream->set_foreground, new_color),
1195                    1, async_safe ? out_char_unchecked : out_char);
1196         }
1197       break;
1198     /* When we are dealing with an xterm, there is no need to go through
1199        tputs() because we know there is no padding and sleeping.  */
1200     case cm_xterm8:
1201       assert (new_color >= 0 && new_color < 8);
1202       {
1203         char bytes[5];
1204         bytes[0] = 0x1B; bytes[1] = '[';
1205         bytes[2] = '3'; bytes[3] = '0' + new_color;
1206         bytes[4] = 'm';
1207         if (full_write (out_fd, bytes, 5) < 5)
1208           if (!async_safe)
1209             out_error ();
1210       }
1211       break;
1212     case cm_xterm16:
1213       assert (new_color >= 0 && new_color < 16);
1214       {
1215         char bytes[5];
1216         bytes[0] = 0x1B; bytes[1] = '[';
1217         if (new_color < 8)
1218           {
1219             bytes[2] = '3'; bytes[3] = '0' + new_color;
1220           }
1221         else
1222           {
1223             bytes[2] = '9'; bytes[3] = '0' + (new_color - 8);
1224           }
1225         bytes[4] = 'm';
1226         if (full_write (out_fd, bytes, 5) < 5)
1227           if (!async_safe)
1228             out_error ();
1229       }
1230       break;
1231     case cm_xterm88:
1232       assert (new_color >= 0 && new_color < 88);
1233       {
1234         char bytes[10];
1235         char *p;
1236         bytes[0] = 0x1B; bytes[1] = '[';
1237         bytes[2] = '3'; bytes[3] = '8'; bytes[4] = ';';
1238         bytes[5] = '5'; bytes[6] = ';';
1239         p = bytes + 7;
1240         if (new_color >= 10)
1241           *p++ = '0' + (new_color / 10);
1242         *p++ = '0' + (new_color % 10);
1243         *p++ = 'm';
1244         if (full_write (out_fd, bytes, p - bytes) < p - bytes)
1245           if (!async_safe)
1246             out_error ();
1247       }
1248       break;
1249     case cm_xterm256:
1250       assert (new_color >= 0 && new_color < 256);
1251       {
1252         char bytes[11];
1253         char *p;
1254         bytes[0] = 0x1B; bytes[1] = '[';
1255         bytes[2] = '3'; bytes[3] = '8'; bytes[4] = ';';
1256         bytes[5] = '5'; bytes[6] = ';';
1257         p = bytes + 7;
1258         if (new_color >= 100)
1259           *p++ = '0' + (new_color / 100);
1260         if (new_color >= 10)
1261           *p++ = '0' + ((new_color % 100) / 10);
1262         *p++ = '0' + (new_color % 10);
1263         *p++ = 'm';
1264         if (full_write (out_fd, bytes, p - bytes) < p - bytes)
1265           if (!async_safe)
1266             out_error ();
1267       }
1268       break;
1269     case cm_xtermrgb:
1270       assert (new_color >= 0 && new_color < 0x1000000);
1271       {
1272         char bytes[19];
1273         char *p;
1274         unsigned int r = (new_color >> 16) & 0xff;
1275         unsigned int g = (new_color >> 8) & 0xff;
1276         unsigned int b = new_color & 0xff;
1277         bytes[0] = 0x1B; bytes[1] = '[';
1278         bytes[2] = '3'; bytes[3] = '8'; bytes[4] = ';';
1279         bytes[5] = '2'; bytes[6] = ';';
1280         p = bytes + 7;
1281         if (r >= 100)
1282           *p++ = '0' + (r / 100);
1283         if (r >= 10)
1284           *p++ = '0' + ((r % 100) / 10);
1285         *p++ = '0' + (r % 10);
1286         *p++ = ';';
1287         if (g >= 100)
1288           *p++ = '0' + (g / 100);
1289         if (g >= 10)
1290           *p++ = '0' + ((g % 100) / 10);
1291         *p++ = '0' + (g % 10);
1292         *p++ = ';';
1293         if (b >= 100)
1294           *p++ = '0' + (b / 100);
1295         if (b >= 10)
1296           *p++ = '0' + ((b % 100) / 10);
1297         *p++ = '0' + (b % 10);
1298         *p++ = 'm';
1299         if (full_write (out_fd, bytes, p - bytes) < p - bytes)
1300           if (!async_safe)
1301             out_error ();
1302       }
1303       break;
1304     default:
1305       abort ();
1306     }
1307 }
1308 
1309 /* Output escape sequences to switch the background color to NEW_BGCOLOR.  */
1310 static _GL_ASYNC_SAFE void
out_bgcolor_change(term_ostream_t stream,term_color_t new_bgcolor,bool async_safe)1311 out_bgcolor_change (term_ostream_t stream, term_color_t new_bgcolor,
1312                     bool async_safe)
1313 {
1314   assert (stream->supports_background);
1315   assert (new_bgcolor != COLOR_DEFAULT);
1316   switch (stream->colormodel)
1317     {
1318     case cm_common8:
1319       assert (new_bgcolor >= 0 && new_bgcolor < 8);
1320       #if HAVE_WINDOWS_CONSOLES
1321       if (stream->is_windows_console)
1322         {
1323           /* SetConsoleTextAttribute
1324              <https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute>
1325              <https://docs.microsoft.com/en-us/windows/console/console-screen-buffers>  */
1326           /* Assign to stream->current_console_attributes *before* calling
1327              SetConsoleTextAttribute, otherwise async_set_attributes_from_default
1328              will not do its job correctly.  */
1329           stream->current_console_attributes =
1330             (stream->current_console_attributes & ~(7 << 4))
1331             | (new_bgcolor << 4);
1332           SetConsoleTextAttribute (stream->handle, stream->current_console_attributes);
1333         }
1334       else
1335       #endif
1336         {
1337           if (stream->set_a_background != NULL)
1338             tputs (tparm (stream->set_a_background, color_bgr (new_bgcolor)),
1339                    1, async_safe ? out_char_unchecked : out_char);
1340           else
1341             tputs (tparm (stream->set_background, new_bgcolor),
1342                    1, async_safe ? out_char_unchecked : out_char);
1343         }
1344       break;
1345     /* When we are dealing with an xterm, there is no need to go through
1346        tputs() because we know there is no padding and sleeping.  */
1347     case cm_xterm8:
1348       assert (new_bgcolor >= 0 && new_bgcolor < 8);
1349       {
1350         char bytes[5];
1351         bytes[0] = 0x1B; bytes[1] = '[';
1352         bytes[2] = '4'; bytes[3] = '0' + new_bgcolor;
1353         bytes[4] = 'm';
1354         if (full_write (out_fd, bytes, 5) < 5)
1355           if (!async_safe)
1356             out_error ();
1357       }
1358       break;
1359     case cm_xterm16:
1360       assert (new_bgcolor >= 0 && new_bgcolor < 16);
1361       {
1362         char bytes[6];
1363         bytes[0] = 0x1B; bytes[1] = '[';
1364         if (new_bgcolor < 8)
1365           {
1366             bytes[2] = '4'; bytes[3] = '0' + new_bgcolor;
1367             bytes[4] = 'm';
1368             if (full_write (out_fd, bytes, 5) < 5)
1369               if (!async_safe)
1370                 out_error ();
1371           }
1372         else
1373           {
1374             bytes[2] = '1'; bytes[3] = '0';
1375             bytes[4] = '0' + (new_bgcolor - 8); bytes[5] = 'm';
1376             if (full_write (out_fd, bytes, 6) < 6)
1377               if (!async_safe)
1378                 out_error ();
1379           }
1380       }
1381       break;
1382     case cm_xterm88:
1383       assert (new_bgcolor >= 0 && new_bgcolor < 88);
1384       {
1385         char bytes[10];
1386         char *p;
1387         bytes[0] = 0x1B; bytes[1] = '[';
1388         bytes[2] = '4'; bytes[3] = '8'; bytes[4] = ';';
1389         bytes[5] = '5'; bytes[6] = ';';
1390         p = bytes + 7;
1391         if (new_bgcolor >= 10)
1392           *p++ = '0' + (new_bgcolor / 10);
1393         *p++ = '0' + (new_bgcolor % 10);
1394         *p++ = 'm';
1395         if (full_write (out_fd, bytes, p - bytes) < p - bytes)
1396           if (!async_safe)
1397             out_error ();
1398       }
1399       break;
1400     case cm_xterm256:
1401       assert (new_bgcolor >= 0 && new_bgcolor < 256);
1402       {
1403         char bytes[11];
1404         char *p;
1405         bytes[0] = 0x1B; bytes[1] = '[';
1406         bytes[2] = '4'; bytes[3] = '8'; bytes[4] = ';';
1407         bytes[5] = '5'; bytes[6] = ';';
1408         p = bytes + 7;
1409         if (new_bgcolor >= 100)
1410           *p++ = '0' + (new_bgcolor / 100);
1411         if (new_bgcolor >= 10)
1412           *p++ = '0' + ((new_bgcolor % 100) / 10);
1413         *p++ = '0' + (new_bgcolor % 10);
1414         *p++ = 'm';
1415         if (full_write (out_fd, bytes, p - bytes) < p - bytes)
1416           if (!async_safe)
1417             out_error ();
1418       }
1419       break;
1420     case cm_xtermrgb:
1421       assert (new_bgcolor >= 0 && new_bgcolor < 0x1000000);
1422       {
1423         char bytes[19];
1424         char *p;
1425         unsigned int r = (new_bgcolor >> 16) & 0xff;
1426         unsigned int g = (new_bgcolor >> 8) & 0xff;
1427         unsigned int b = new_bgcolor & 0xff;
1428         bytes[0] = 0x1B; bytes[1] = '[';
1429         bytes[2] = '4'; bytes[3] = '8'; bytes[4] = ';';
1430         bytes[5] = '2'; bytes[6] = ';';
1431         p = bytes + 7;
1432         if (r >= 100)
1433           *p++ = '0' + (r / 100);
1434         if (r >= 10)
1435           *p++ = '0' + ((r % 100) / 10);
1436         *p++ = '0' + (r % 10);
1437         *p++ = ';';
1438         if (g >= 100)
1439           *p++ = '0' + (g / 100);
1440         if (g >= 10)
1441           *p++ = '0' + ((g % 100) / 10);
1442         *p++ = '0' + (g % 10);
1443         *p++ = ';';
1444         if (b >= 100)
1445           *p++ = '0' + (b / 100);
1446         if (b >= 10)
1447           *p++ = '0' + ((b % 100) / 10);
1448         *p++ = '0' + (b % 10);
1449         *p++ = 'm';
1450         if (full_write (out_fd, bytes, p - bytes) < p - bytes)
1451           if (!async_safe)
1452             out_error ();
1453       }
1454       break;
1455     default:
1456       abort ();
1457     }
1458 }
1459 
1460 /* Output escape sequences to switch the weight to NEW_WEIGHT.  */
1461 static _GL_ASYNC_SAFE void
out_weight_change(term_ostream_t stream,term_weight_t new_weight,bool async_safe)1462 out_weight_change (term_ostream_t stream, term_weight_t new_weight,
1463                    bool async_safe)
1464 {
1465   assert (stream->supports_weight);
1466   assert (new_weight != WEIGHT_DEFAULT);
1467   /* This implies:  */
1468   assert (new_weight == WEIGHT_BOLD);
1469   tputs (stream->enter_bold_mode,
1470          1, async_safe ? out_char_unchecked : out_char);
1471 }
1472 
1473 /* Output escape sequences to switch the posture to NEW_POSTURE.  */
1474 static _GL_ASYNC_SAFE void
out_posture_change(term_ostream_t stream,term_posture_t new_posture,bool async_safe)1475 out_posture_change (term_ostream_t stream, term_posture_t new_posture,
1476                     bool async_safe)
1477 {
1478   assert (stream->supports_posture);
1479   assert (new_posture != POSTURE_DEFAULT);
1480   /* This implies:  */
1481   assert (new_posture == POSTURE_ITALIC);
1482   tputs (stream->enter_italics_mode,
1483          1, async_safe ? out_char_unchecked : out_char);
1484 }
1485 
1486 /* Output escape sequences to switch the underline to NEW_UNDERLINE.  */
1487 static _GL_ASYNC_SAFE void
out_underline_change(term_ostream_t stream,term_underline_t new_underline,bool async_safe)1488 out_underline_change (term_ostream_t stream, term_underline_t new_underline,
1489                       bool async_safe)
1490 {
1491   assert (stream->supports_underline);
1492   assert (new_underline != UNDERLINE_DEFAULT);
1493   /* This implies:  */
1494   assert (new_underline == UNDERLINE_ON);
1495   #if HAVE_WINDOWS_CONSOLES
1496   if (stream->is_windows_console)
1497     {
1498       /* SetConsoleTextAttribute
1499          <https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute>
1500          <https://docs.microsoft.com/en-us/windows/console/console-screen-buffers>  */
1501       /* Assign to stream->current_console_attributes *before* calling
1502          SetConsoleTextAttribute, otherwise async_set_attributes_from_default
1503          will not do its job correctly.  */
1504       stream->current_console_attributes =
1505         stream->current_console_attributes | COMMON_LVB_UNDERSCORE;
1506       SetConsoleTextAttribute (stream->handle, stream->current_console_attributes);
1507     }
1508   else
1509   #endif
1510     {
1511       tputs (stream->enter_underline_mode,
1512              1, async_safe ? out_char_unchecked : out_char);
1513     }
1514 }
1515 
1516 /* Output escape seqeuences to switch the hyperlink to NEW_HYPERLINK.  */
1517 static _GL_ASYNC_SAFE void
out_hyperlink_change(term_ostream_t stream,hyperlink_t * new_hyperlink,bool async_safe)1518 out_hyperlink_change (term_ostream_t stream, hyperlink_t *new_hyperlink,
1519                       bool async_safe)
1520 {
1521   int (*out_ch) (int) = (async_safe ? out_char_unchecked : out_char);
1522   assert (stream->supports_hyperlink);
1523   if (new_hyperlink != NULL)
1524     {
1525       assert (new_hyperlink->real_id != NULL);
1526       tputs ("\033]8;id=",           1, out_ch);
1527       tputs (new_hyperlink->real_id, 1, out_ch);
1528       tputs (";",                    1, out_ch);
1529       tputs (new_hyperlink->ref,     1, out_ch);
1530       tputs ("\033\\",               1, out_ch);
1531     }
1532   else
1533     tputs ("\033]8;;\033\\", 1, out_ch);
1534 }
1535 
1536 /* Output escape sequences to switch from STREAM->ACTIVE_ATTR to NEW_ATTR,
1537    and update STREAM->ACTIVE_ATTR.  */
1538 static void
out_attr_change(term_ostream_t stream,attributes_t new_attr)1539 out_attr_change (term_ostream_t stream, attributes_t new_attr)
1540 {
1541   attributes_t old_attr = stream->active_attr;
1542 
1543   /* Keep track of the active attributes.  Do this *before* emitting the
1544      escape sequences, otherwise async_set_attributes_from_default will not
1545      do its job correctly.  */
1546   stream->active_attr = new_attr;
1547   stream->active_attr_color = new_attr.color;
1548   stream->active_attr_bgcolor = new_attr.bgcolor;
1549   stream->active_attr_hyperlink = new_attr.hyperlink;
1550 
1551   #if HAVE_WINDOWS_CONSOLES
1552   if (stream->is_windows_console)
1553     {
1554       /* SetConsoleTextAttribute
1555          <https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute>
1556          <https://docs.microsoft.com/en-us/windows/console/console-screen-buffers>  */
1557       /* Assign to stream->current_console_attributes *before* calling
1558          SetConsoleTextAttribute, otherwise async_set_attributes_from_default
1559          will not do its job correctly.  */
1560       stream->current_console_attributes =
1561         (stream->current_console_attributes
1562          & ~((7 << 0) | (7 << 4) | COMMON_LVB_UNDERSCORE))
1563         | (new_attr.color == COLOR_DEFAULT
1564            ? stream->default_console_attributes & (7 << 0)
1565            : (new_attr.color << 0))
1566         | (new_attr.bgcolor == COLOR_DEFAULT
1567            ? stream->default_console_attributes & (7 << 4)
1568            : (new_attr.bgcolor << 4))
1569         | (new_attr.underline ? COMMON_LVB_UNDERSCORE : 0);
1570       SetConsoleTextAttribute (stream->handle, stream->current_console_attributes);
1571     }
1572   else
1573   #endif
1574     {
1575       bool cleared_attributes;
1576 
1577       /* For out_char to work.  */
1578       out_stream = stream;
1579       out_fd = stream->fd;
1580 
1581       /* We don't know the default colors of the terminal.  The only way to
1582          switch back to a default color is to use stream->orig_pair.  */
1583       if ((new_attr.color == COLOR_DEFAULT && old_attr.color != COLOR_DEFAULT)
1584           || (new_attr.bgcolor == COLOR_DEFAULT && old_attr.bgcolor != COLOR_DEFAULT))
1585         {
1586           assert (stream->supports_foreground || stream->supports_background);
1587           tputs (stream->orig_pair, 1, out_char);
1588           old_attr.color = COLOR_DEFAULT;
1589           old_attr.bgcolor = COLOR_DEFAULT;
1590         }
1591 
1592       /* To turn off WEIGHT_BOLD, the only way is to output the
1593          exit_attribute_mode sequence.  (With xterm, you can also do it with
1594          "Esc [ 0 m", but this escape sequence is not contained in the terminfo
1595          description.)  It may also clear the colors; this is the case e.g. when
1596          TERM="xterm" or TERM="ansi".
1597          To turn off UNDERLINE_ON, we can use the exit_underline_mode or the
1598          exit_attribute_mode sequence.  In the latter case, it will not only
1599          turn off UNDERLINE_ON, but also the other attributes, and possibly also
1600          the colors.
1601          To turn off POSTURE_ITALIC, we can use the exit_italics_mode or the
1602          exit_attribute_mode sequence.  Again, in the latter case, it will not
1603          only turn off POSTURE_ITALIC, but also the other attributes, and
1604          possibly also the colors.
1605          There is no point in setting an attribute just before emitting an
1606          escape sequence that may again turn off the attribute.  Therefore we
1607          proceed in two steps: First, clear the attributes that need to be
1608          cleared; then - taking into account that this may have cleared all
1609          attributes and all colors - set the colors and the attributes.
1610          The variable 'cleared_attributes' tells whether an escape sequence
1611          has been output that may have cleared all attributes and all color
1612          settings.  */
1613       cleared_attributes = false;
1614       if (old_attr.posture != POSTURE_NORMAL
1615           && new_attr.posture == POSTURE_NORMAL
1616           && stream->exit_italics_mode != NULL)
1617         {
1618           tputs (stream->exit_italics_mode, 1, out_char);
1619           old_attr.posture = POSTURE_NORMAL;
1620           cleared_attributes = true;
1621         }
1622       if (old_attr.underline != UNDERLINE_OFF
1623           && new_attr.underline == UNDERLINE_OFF
1624           && stream->exit_underline_mode != NULL)
1625         {
1626           tputs (stream->exit_underline_mode, 1, out_char);
1627           old_attr.underline = UNDERLINE_OFF;
1628           cleared_attributes = true;
1629         }
1630       if ((old_attr.weight != WEIGHT_NORMAL
1631            && new_attr.weight == WEIGHT_NORMAL)
1632           || (old_attr.posture != POSTURE_NORMAL
1633               && new_attr.posture == POSTURE_NORMAL
1634               /* implies stream->exit_italics_mode == NULL */)
1635           || (old_attr.underline != UNDERLINE_OFF
1636               && new_attr.underline == UNDERLINE_OFF
1637               /* implies stream->exit_underline_mode == NULL */))
1638         {
1639           tputs (stream->exit_attribute_mode, 1, out_char);
1640           /* We don't know exactly what effects exit_attribute_mode has, but
1641              this is the minimum effect:  */
1642           old_attr.weight = WEIGHT_NORMAL;
1643           if (stream->exit_italics_mode == NULL)
1644             old_attr.posture = POSTURE_NORMAL;
1645           if (stream->exit_underline_mode == NULL)
1646             old_attr.underline = UNDERLINE_OFF;
1647           cleared_attributes = true;
1648         }
1649 
1650       /* Turn on the colors.  */
1651       if (new_attr.color != old_attr.color
1652           || (cleared_attributes && new_attr.color != COLOR_DEFAULT))
1653         {
1654           out_color_change (stream, new_attr.color, false);
1655         }
1656       if (new_attr.bgcolor != old_attr.bgcolor
1657           || (cleared_attributes && new_attr.bgcolor != COLOR_DEFAULT))
1658         {
1659           out_bgcolor_change (stream, new_attr.bgcolor, false);
1660         }
1661       if (new_attr.weight != old_attr.weight
1662           || (cleared_attributes && new_attr.weight != WEIGHT_DEFAULT))
1663         {
1664           out_weight_change (stream, new_attr.weight, false);
1665         }
1666       if (new_attr.posture != old_attr.posture
1667           || (cleared_attributes && new_attr.posture != POSTURE_DEFAULT))
1668         {
1669           out_posture_change (stream, new_attr.posture, false);
1670         }
1671       if (new_attr.underline != old_attr.underline
1672           || (cleared_attributes && new_attr.underline != UNDERLINE_DEFAULT))
1673         {
1674           out_underline_change (stream, new_attr.underline, false);
1675         }
1676       if (new_attr.hyperlink != old_attr.hyperlink)
1677         {
1678           out_hyperlink_change (stream, new_attr.hyperlink, false);
1679         }
1680     }
1681 }
1682 
1683 static void
restore(term_ostream_t stream)1684 restore (term_ostream_t stream)
1685 {
1686   #if HAVE_WINDOWS_CONSOLES
1687   if (stream->is_windows_console)
1688     {
1689       /* SetConsoleTextAttribute
1690          <https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute>
1691          <https://docs.microsoft.com/en-us/windows/console/console-screen-buffers>  */
1692       SetConsoleTextAttribute (stream->handle, stream->default_console_attributes);
1693     }
1694   else
1695   #endif
1696     {
1697       /* For out_char_unchecked to work.  */
1698       out_stream = stream;
1699       out_fd = stream->fd;
1700 
1701       if (stream->restore_colors != NULL)
1702         tputs (stream->restore_colors, 1, out_char_unchecked);
1703       if (stream->restore_weight != NULL)
1704         tputs (stream->restore_weight, 1, out_char_unchecked);
1705       if (stream->restore_posture != NULL)
1706         tputs (stream->restore_posture, 1, out_char_unchecked);
1707       if (stream->restore_underline != NULL)
1708         tputs (stream->restore_underline, 1, out_char_unchecked);
1709       if (stream->restore_hyperlink != NULL)
1710         tputs (stream->restore_hyperlink, 1, out_char_unchecked);
1711     }
1712 }
1713 
1714 static _GL_ASYNC_SAFE void
async_restore(term_ostream_t stream)1715 async_restore (term_ostream_t stream)
1716 {
1717   #if HAVE_WINDOWS_CONSOLES
1718   if (stream->is_windows_console)
1719     {
1720       /* SetConsoleTextAttribute
1721          <https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute>
1722          <https://docs.microsoft.com/en-us/windows/console/console-screen-buffers>  */
1723       SetConsoleTextAttribute (stream->handle, stream->default_console_attributes);
1724     }
1725   else
1726   #endif
1727     {
1728       /* For out_char_unchecked to work.  */
1729       out_stream = stream;
1730       out_fd = stream->fd;
1731 
1732       if (stream->restore_colors != NULL)
1733         tputs (stream->restore_colors, 1, out_char_unchecked);
1734       if (stream->restore_weight != NULL)
1735         tputs (stream->restore_weight, 1, out_char_unchecked);
1736       if (stream->restore_posture != NULL)
1737         tputs (stream->restore_posture, 1, out_char_unchecked);
1738       if (stream->restore_underline != NULL)
1739         tputs (stream->restore_underline, 1, out_char_unchecked);
1740       if (stream->restore_hyperlink != NULL)
1741         tputs (stream->restore_hyperlink, 1, out_char_unchecked);
1742     }
1743 }
1744 
1745 static _GL_ASYNC_SAFE void
async_set_attributes_from_default(term_ostream_t stream)1746 async_set_attributes_from_default (term_ostream_t stream)
1747 {
1748   #if HAVE_WINDOWS_CONSOLES
1749   if (stream->is_windows_console)
1750     {
1751       /* SetConsoleTextAttribute
1752          <https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute>
1753          <https://docs.microsoft.com/en-us/windows/console/console-screen-buffers>  */
1754       SetConsoleTextAttribute (stream->handle, stream->current_console_attributes);
1755     }
1756   else
1757   #endif
1758     {
1759       attributes_t new_attr = stream->active_attr;
1760       /* Since stream->active_attr is not guaranteed to be loaded atomically,
1761          new_attr.color and new_attr.bgcolor may have invalid values.
1762          Use the atomically loadable values instead.  */
1763       new_attr.color = stream->active_attr_color;
1764       new_attr.bgcolor = stream->active_attr_bgcolor;
1765       new_attr.hyperlink = stream->active_attr_hyperlink;
1766 
1767       /* For out_char_unchecked to work.  */
1768       out_stream = stream;
1769       out_fd = stream->fd;
1770 
1771       if (new_attr.color != COLOR_DEFAULT)
1772         out_color_change (stream, new_attr.color, true);
1773       if (new_attr.bgcolor != COLOR_DEFAULT)
1774         out_bgcolor_change (stream, new_attr.bgcolor, true);
1775       if (new_attr.weight != WEIGHT_DEFAULT)
1776         out_weight_change (stream, new_attr.weight, true);
1777       if (new_attr.posture != POSTURE_DEFAULT)
1778         out_posture_change (stream, new_attr.posture, true);
1779       if (new_attr.underline != UNDERLINE_DEFAULT)
1780         out_underline_change (stream, new_attr.underline, true);
1781       if (new_attr.hyperlink != NULL)
1782         out_hyperlink_change (stream, new_attr.hyperlink, true);
1783     }
1784 }
1785 
1786 static const struct term_style_controller controller =
1787 {
1788   get_control_data,
1789   restore,
1790   async_restore,
1791   async_set_attributes_from_default
1792 };
1793 
1794 /* Activate the default attributes.  */
1795 static void
activate_default_attr(term_ostream_t stream)1796 activate_default_attr (term_ostream_t stream)
1797 {
1798   /* Switch back to the default attributes.  */
1799   out_attr_change (stream, stream->default_attr);
1800 
1801   deactivate_term_non_default_mode (&controller, stream);
1802 }
1803 
1804 /* Output the buffered line atomically.
1805    The terminal is left in the the state (regarding colors and attributes)
1806    represented by the simplified attributes goal_attr.  */
1807 static void
output_buffer(term_ostream_t stream,attributes_t goal_attr)1808 output_buffer (term_ostream_t stream, attributes_t goal_attr)
1809 {
1810   const char *cp;
1811   const attributes_t *ap;
1812   size_t len;
1813   size_t n;
1814 
1815   cp = stream->buffer;
1816   ap = stream->attrbuffer;
1817   len = stream->buflen;
1818 
1819   /* See how much we can output without blocking signals.  */
1820   for (n = 0; n < len && equal_attributes (ap[n], stream->active_attr); n++)
1821     ;
1822   if (n > 0)
1823     {
1824       if (full_write (stream->fd, cp, n) < n)
1825         {
1826           int error_code = errno;
1827           /* Do output to stderr only after we have switched back to the
1828              default attributes.  Otherwise this output may come out with
1829              the wrong text attributes.  */
1830           if (!equal_attributes (stream->active_attr, stream->default_attr))
1831             activate_default_attr (stream);
1832           error (EXIT_FAILURE, error_code, _("error writing to %s"),
1833                  stream->filename);
1834         }
1835       cp += n;
1836       ap += n;
1837       len -= n;
1838     }
1839   if (len > 0)
1840     {
1841       if (!equal_attributes (*ap, stream->default_attr))
1842         activate_term_non_default_mode (&controller, stream);
1843 
1844       do
1845         {
1846           /* Activate the attributes in *ap.  */
1847           out_attr_change (stream, *ap);
1848           /* See how many characters we can output without further attribute
1849              changes.  */
1850           for (n = 1; n < len && equal_attributes (ap[n], stream->active_attr); n++)
1851             ;
1852           if (full_write (stream->fd, cp, n) < n)
1853             {
1854               int error_code = errno;
1855               /* Do output to stderr only after we have switched back to the
1856                  default attributes.  Otherwise this output may come out with
1857                  the wrong text attributes.  */
1858               if (!equal_attributes (stream->active_attr, stream->default_attr))
1859                 activate_default_attr (stream);
1860               error (EXIT_FAILURE, error_code, _("error writing to %s"),
1861                      stream->filename);
1862             }
1863           cp += n;
1864           ap += n;
1865           len -= n;
1866         }
1867       while (len > 0);
1868     }
1869   stream->buflen = 0;
1870 
1871   /* Before changing to goal_attr, we may need to enable the non-default
1872      attributes mode.  */
1873   if (!equal_attributes (goal_attr, stream->default_attr))
1874     activate_term_non_default_mode (&controller, stream);
1875   /* Change to goal_attr.  */
1876   if (!equal_attributes (goal_attr, stream->active_attr))
1877     out_attr_change (stream, goal_attr);
1878   /* When we can deactivate the non-default attributes mode, do so.  */
1879   if (equal_attributes (goal_attr, stream->default_attr))
1880     deactivate_term_non_default_mode (&controller, stream);
1881 
1882   /* Free the hyperlink_t objects that are no longer referenced by the
1883      stream->attrbuffer.  */
1884   {
1885     size_t count = stream->hyperlinks_count;
1886     size_t j = 0;
1887     size_t i;
1888     for (i = 0; i < count; i++)
1889       {
1890         /* Here 0 <= j <= i.  */
1891         hyperlink_t *hyperlink = stream->hyperlinks_array[i];
1892         /* stream->default_attr.hyperlink is always == NULL.
1893            stream->simp_attr.hyperlink is either == NULL
1894                                               or == stream->curr_attr.hyperlink.
1895            We can therefore ignore both.  */
1896         if (hyperlink == stream->curr_attr.hyperlink
1897             || hyperlink == stream->active_attr.hyperlink)
1898           {
1899             /* The hyperlink is still in use.  */
1900             stream->hyperlinks_array[j] = hyperlink;
1901             j++;
1902           }
1903         else
1904           {
1905             /* The hyperlink is not in use any more.  */
1906             free_hyperlink (hyperlink);
1907           }
1908       }
1909     stream->hyperlinks_count = j;
1910   }
1911 }
1912 
1913 /* Implementation of ostream_t methods.  */
1914 
1915 static term_color_t
term_ostream__rgb_to_color(term_ostream_t stream,int red,int green,int blue)1916 term_ostream__rgb_to_color (term_ostream_t stream, int red, int green, int blue)
1917 {
1918   switch (stream->colormodel)
1919     {
1920     case cm_monochrome:
1921       return rgb_to_color_monochrome ();
1922     case cm_common8:
1923       return rgb_to_color_common8 (red, green, blue);
1924     case cm_xterm8:
1925       return rgb_to_color_xterm8 (red, green, blue);
1926     case cm_xterm16:
1927       return rgb_to_color_xterm16 (red, green, blue);
1928     case cm_xterm88:
1929       return rgb_to_color_xterm88 (red, green, blue);
1930     case cm_xterm256:
1931       return rgb_to_color_xterm256 (red, green, blue);
1932     case cm_xtermrgb:
1933       return rgb_to_color_xtermrgb (red, green, blue);
1934     default:
1935       abort ();
1936     }
1937 }
1938 
1939 static void
term_ostream__write_mem(term_ostream_t stream,const void * data,size_t len)1940 term_ostream__write_mem (term_ostream_t stream, const void *data, size_t len)
1941 {
1942   const char *cp = (const char *) data;
1943   while (len > 0)
1944     {
1945       /* Look for the next newline.  */
1946       const char *newline = (const char *) memchr (cp, '\n', len);
1947       size_t n = (newline != NULL ? newline - cp : len);
1948 
1949       /* Copy n bytes into the buffer.  */
1950       if (n > stream->allocated - stream->buflen)
1951         {
1952           size_t new_allocated =
1953             xmax (xsum (stream->buflen, n),
1954                   xsum (stream->allocated, stream->allocated));
1955           if (size_overflow_p (new_allocated))
1956             error (EXIT_FAILURE, 0,
1957                    _("%s: too much output, buffer size overflow"),
1958                    "term_ostream");
1959           stream->buffer = (char *) xrealloc (stream->buffer, new_allocated);
1960           stream->attrbuffer =
1961             (attributes_t *)
1962             xrealloc (stream->attrbuffer,
1963                       new_allocated * sizeof (attributes_t));
1964           stream->allocated = new_allocated;
1965         }
1966       memcpy (stream->buffer + stream->buflen, cp, n);
1967       {
1968         attributes_t attr = stream->simp_attr;
1969         attributes_t *ap = stream->attrbuffer + stream->buflen;
1970         attributes_t *ap_end = ap + n;
1971         for (; ap < ap_end; ap++)
1972           *ap = attr;
1973       }
1974       stream->buflen += n;
1975 
1976       if (newline != NULL)
1977         {
1978           output_buffer (stream, stream->default_attr);
1979           if (full_write (stream->fd, "\n", 1) < 1)
1980             error (EXIT_FAILURE, errno, _("error writing to %s"),
1981                    stream->filename);
1982           cp += n + 1; /* cp = newline + 1; */
1983           len -= n + 1;
1984         }
1985       else
1986         break;
1987     }
1988 }
1989 
1990 static void
term_ostream__flush(term_ostream_t stream,ostream_flush_scope_t scope)1991 term_ostream__flush (term_ostream_t stream, ostream_flush_scope_t scope)
1992 {
1993   output_buffer (stream, stream->default_attr);
1994   if (scope == FLUSH_ALL)
1995     {
1996       #if HAVE_WINDOWS_CONSOLES
1997       if (!stream->is_windows_console)
1998       #endif
1999         {
2000           /* For streams connected to a disk file:  */
2001           fsync (stream->fd);
2002           #if HAVE_TCDRAIN
2003           /* For streams connected to a terminal:  */
2004           nonintr_tcdrain (stream->fd);
2005           #endif
2006         }
2007     }
2008 }
2009 
2010 static void
term_ostream__free(term_ostream_t stream)2011 term_ostream__free (term_ostream_t stream)
2012 {
2013   term_ostream_flush (stream, FLUSH_THIS_STREAM);
2014 
2015   deactivate_term_style_controller (&controller, stream);
2016 
2017   free (stream->filename);
2018   if (stream->set_a_foreground != NULL)
2019     free (stream->set_a_foreground);
2020   if (stream->set_foreground != NULL)
2021     free (stream->set_foreground);
2022   if (stream->set_a_background != NULL)
2023     free (stream->set_a_background);
2024   if (stream->set_background != NULL)
2025     free (stream->set_background);
2026   if (stream->orig_pair != NULL)
2027     free (stream->orig_pair);
2028   if (stream->enter_bold_mode != NULL)
2029     free (stream->enter_bold_mode);
2030   if (stream->enter_italics_mode != NULL)
2031     free (stream->enter_italics_mode);
2032   if (stream->exit_italics_mode != NULL)
2033     free (stream->exit_italics_mode);
2034   if (stream->enter_underline_mode != NULL)
2035     free (stream->enter_underline_mode);
2036   if (stream->exit_underline_mode != NULL)
2037     free (stream->exit_underline_mode);
2038   if (stream->exit_attribute_mode != NULL)
2039     free (stream->exit_attribute_mode);
2040   if (stream->hyperlinks_array != NULL)
2041     {
2042       size_t count = stream->hyperlinks_count;
2043       size_t i;
2044       for (i = 0; i < count; i++)
2045         free_hyperlink (stream->hyperlinks_array[i]);
2046       free (stream->hyperlinks_array);
2047     }
2048   free (stream->buffer);
2049   free (stream->attrbuffer);
2050   free (stream);
2051 }
2052 
2053 /* Implementation of term_ostream_t methods.  */
2054 
2055 static term_color_t
term_ostream__get_color(term_ostream_t stream)2056 term_ostream__get_color (term_ostream_t stream)
2057 {
2058   return stream->curr_attr.color;
2059 }
2060 
2061 static void
term_ostream__set_color(term_ostream_t stream,term_color_t color)2062 term_ostream__set_color (term_ostream_t stream, term_color_t color)
2063 {
2064   stream->curr_attr.color = color;
2065   stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
2066 }
2067 
2068 static term_color_t
term_ostream__get_bgcolor(term_ostream_t stream)2069 term_ostream__get_bgcolor (term_ostream_t stream)
2070 {
2071   return stream->curr_attr.bgcolor;
2072 }
2073 
2074 static void
term_ostream__set_bgcolor(term_ostream_t stream,term_color_t color)2075 term_ostream__set_bgcolor (term_ostream_t stream, term_color_t color)
2076 {
2077   stream->curr_attr.bgcolor = color;
2078   stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
2079 }
2080 
2081 static term_weight_t
term_ostream__get_weight(term_ostream_t stream)2082 term_ostream__get_weight (term_ostream_t stream)
2083 {
2084   return stream->curr_attr.weight;
2085 }
2086 
2087 static void
term_ostream__set_weight(term_ostream_t stream,term_weight_t weight)2088 term_ostream__set_weight (term_ostream_t stream, term_weight_t weight)
2089 {
2090   stream->curr_attr.weight = weight;
2091   stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
2092 }
2093 
2094 static term_posture_t
term_ostream__get_posture(term_ostream_t stream)2095 term_ostream__get_posture (term_ostream_t stream)
2096 {
2097   return stream->curr_attr.posture;
2098 }
2099 
2100 static void
term_ostream__set_posture(term_ostream_t stream,term_posture_t posture)2101 term_ostream__set_posture (term_ostream_t stream, term_posture_t posture)
2102 {
2103   stream->curr_attr.posture = posture;
2104   stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
2105 }
2106 
2107 static term_underline_t
term_ostream__get_underline(term_ostream_t stream)2108 term_ostream__get_underline (term_ostream_t stream)
2109 {
2110   return stream->curr_attr.underline;
2111 }
2112 
2113 static void
term_ostream__set_underline(term_ostream_t stream,term_underline_t underline)2114 term_ostream__set_underline (term_ostream_t stream, term_underline_t underline)
2115 {
2116   stream->curr_attr.underline = underline;
2117   stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
2118 }
2119 
2120 static const char *
term_ostream__get_hyperlink_ref(term_ostream_t stream)2121 term_ostream__get_hyperlink_ref (term_ostream_t stream)
2122 {
2123   hyperlink_t *hyperlink = stream->curr_attr.hyperlink;
2124   return (hyperlink != NULL ? hyperlink->ref : NULL);
2125 }
2126 
2127 static const char *
term_ostream__get_hyperlink_id(term_ostream_t stream)2128 term_ostream__get_hyperlink_id (term_ostream_t stream)
2129 {
2130   hyperlink_t *hyperlink = stream->curr_attr.hyperlink;
2131   return (hyperlink != NULL ? hyperlink->id : NULL);
2132 }
2133 
2134 static void
term_ostream__set_hyperlink(term_ostream_t stream,const char * ref,const char * id)2135 term_ostream__set_hyperlink (term_ostream_t stream,
2136                              const char *ref, const char *id)
2137 {
2138   if (ref == NULL)
2139     stream->curr_attr.hyperlink = NULL;
2140   else
2141     {
2142       /* Create a new hyperlink_t object.  */
2143       hyperlink_t *hyperlink = XMALLOC (hyperlink_t);
2144 
2145       hyperlink->ref = xstrdup (ref);
2146       if (id != NULL)
2147         {
2148           hyperlink->id = xstrdup (id);
2149           hyperlink->real_id = hyperlink->id;
2150         }
2151       else
2152         {
2153           hyperlink->id = NULL;
2154           if (stream->supports_hyperlink)
2155             {
2156               /* Generate an id always, since we don't know at this point
2157                  whether the hyperlink will span multiple lines.  */
2158               hyperlink->real_id = generate_hyperlink_id (stream);
2159             }
2160           else
2161             hyperlink->real_id = NULL;
2162         }
2163 
2164       /* Store it.  */
2165       if (stream->hyperlinks_count == stream->hyperlinks_allocated)
2166         {
2167           stream->hyperlinks_allocated = 2 * stream->hyperlinks_allocated + 10;
2168           stream->hyperlinks_array =
2169             (hyperlink_t **)
2170             xrealloc (stream->hyperlinks_array,
2171                       stream->hyperlinks_allocated * sizeof (hyperlink_t *));
2172         }
2173       stream->hyperlinks_array[stream->hyperlinks_count++] = hyperlink;
2174 
2175       /* Install it.  */
2176       stream->curr_attr.hyperlink = hyperlink;
2177     }
2178   stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
2179 }
2180 
2181 static void
term_ostream__flush_to_current_style(term_ostream_t stream)2182 term_ostream__flush_to_current_style (term_ostream_t stream)
2183 {
2184   output_buffer (stream, stream->simp_attr);
2185 }
2186 
2187 /* Constructor.  */
2188 
2189 static inline char *
xstrdup0(const char * str)2190 xstrdup0 (const char *str)
2191 {
2192   if (str == NULL)
2193     return NULL;
2194 #if HAVE_TERMINFO
2195   if (str == (const char *)(-1))
2196     return NULL;
2197 #endif
2198   return xstrdup (str);
2199 }
2200 
2201 /* Returns the base name of the terminal emulator program, possibly truncated,
2202    as a freshly allocated string, or NULL if it cannot be determined.
2203    Note: This function is a hack.  It does not work across ssh, and it may fail
2204    in some local situations as well.  */
2205 static inline char *
get_terminal_emulator_progname(void)2206 get_terminal_emulator_progname (void)
2207 {
2208   #if HAVE_GETSID
2209   /* Get the process id of the session leader.
2210      When running in a terminal emulator, it's the shell process that was
2211      spawned by the terminal emulator.  When running in a console, it's the
2212      'login' process.
2213      On some operating systems (Linux, *BSD, AIX), the same result could also
2214      be obtained through
2215        pid_t p;
2216        if (ioctl (1, TIOCGSID, &p) >= 0) ...
2217    */
2218   pid_t session_leader_pid = getsid (0);
2219   if (session_leader_pid != (pid_t)(-1))
2220     {
2221       /* Get the process id of the terminal emulator.
2222          When running in a console, it's the process id of the 'init'
2223          process.  */
2224       pid_t terminal_emulator_pid = get_ppid_of (session_leader_pid);
2225       if (terminal_emulator_pid != 0)
2226         {
2227           /* Retrieve the base name of the program name of this process.  */
2228           return get_progname_of (terminal_emulator_pid);
2229         }
2230     }
2231   #endif
2232   return NULL;
2233 }
2234 
2235 /* Returns true if we should enable hyperlinks.
2236    term is the value of the TERM environment variable.  */
2237 static inline bool
should_enable_hyperlinks(const char * term)2238 should_enable_hyperlinks (const char *term)
2239 {
2240   if (getenv ("NO_TERM_HYPERLINKS") != NULL)
2241     /* The user has disabled hyperlinks.  */
2242     return false;
2243 
2244   /* Dispatch based on $TERM.  */
2245   if (term != NULL)
2246     {
2247       /* rxvt-based terminal emulators:
2248            Program           | TERM         | Supports hyperlinks?
2249            ------------------+--------------+-------------------------------------
2250            rxvt 2.7.10       | rxvt         | hangs after "cat hyperlink-demo.txt"
2251            mrxvt 0.5.3       | rxvt         | no
2252            rxvt-unicode 9.22 | rxvt-unicode | no
2253        */
2254       if (strcmp (term, "rxvt") == 0)
2255         return false;
2256 
2257       /* Emacs-based terminal emulators:
2258            Program             | TERM        | Supports hyperlinks?
2259            --------------------+-------------+---------------------
2260            emacs-terminal 26.1 | eterm-color | produces garbage
2261        */
2262       if (strncmp (term, "eterm", 5) == 0)
2263         return false;
2264 
2265       /* xterm-compatible terminal emulators:
2266            Program          | TERM           | Supports hyperlinks?
2267            -----------------+----------------+---------------------------
2268            guake 0.8.8      | xterm          | produces garbage
2269            lilyterm 0.9.9.2 | xterm          | produces garbage
2270            lterm 1.5.1      | xterm          | produces garbage
2271            lxterminal 0.3.2 | xterm          | produces garbage
2272            termit 2.9.6     | xterm          | produces garbage
2273            konsole 18.12.3  | xterm-256color | produces extra backslashes
2274            yakuake 3.0.5    | xterm-256color | produces extra backslashes
2275            other            |                | yes or no, no severe bugs
2276 
2277          TODO: Revisit this table periodically.
2278        */
2279       if (strncmp (term, "xterm", 5) == 0)
2280         {
2281           char *progname = get_terminal_emulator_progname ();
2282           if (progname != NULL)
2283             {
2284               bool known_buggy =
2285                 strncmp (progname, "python", 6) == 0 /* guake */
2286                 || strcmp (progname, "lilyterm") == 0
2287                 || strcmp (progname, "lterm") == 0
2288                 || strcmp (progname, "lxterminal") == 0
2289                 || strcmp (progname, "termit") == 0
2290                 || strcmp (progname, "konsole") == 0
2291                 || strcmp (progname, "yakuake") == 0;
2292               free (progname);
2293               /* Enable hyperlinks except for programs that are known buggy.  */
2294               return !known_buggy;
2295             }
2296         }
2297     }
2298 
2299   /* In case of doubt, enable hyperlinks.  So this code does not need to change
2300      as more and more terminal emulators support hyperlinks.
2301      If there are adverse effects, the user can disable hyperlinks by setting
2302      NO_TERM_HYPERLINKS.  */
2303   return true;
2304 }
2305 
2306 term_ostream_t
term_ostream_create(int fd,const char * filename,ttyctl_t tty_control)2307 term_ostream_create (int fd, const char *filename, ttyctl_t tty_control)
2308 {
2309   term_ostream_t stream = XMALLOC (struct term_ostream_representation);
2310 
2311   stream->base.vtable = &term_ostream_vtable;
2312   stream->fd = fd;
2313   #if HAVE_WINDOWS_CONSOLES
2314   stream->handle = (HANDLE) _get_osfhandle (fd);
2315   {
2316     DWORD mode;
2317 
2318     if (stream->handle != INVALID_HANDLE_VALUE
2319         /* GetConsoleMode
2320            <https://docs.microsoft.com/en-us/windows/console/getconsolemode>  */
2321         && GetConsoleMode (stream->handle, &mode) != 0)
2322       {
2323         CONSOLE_SCREEN_BUFFER_INFO info;
2324         BOOL ok;
2325 
2326         /* GetConsoleScreenBufferInfo
2327            <https://docs.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo>
2328            <https://docs.microsoft.com/en-us/windows/console/console-screen-buffer-info-str>  */
2329         ok = GetConsoleScreenBufferInfo (stream->handle, &info);
2330         if (!ok)
2331           {
2332             /* GetConsoleScreenBufferInfo
2333                  - fails when the handle is == GetStdHandle (STD_INPUT_HANDLE)
2334                  - but succeeds when it is == GetStdHandle (STD_OUTPUT_HANDLE)
2335                    or == GetStdHandle (STD_ERROR_HANDLE).
2336                Native Windows programs use GetStdHandle (STD_OUTPUT_HANDLE) for
2337                fd 1, as expected.
2338                But Cygwin uses GetStdHandle (STD_INPUT_HANDLE) for all of fd 0,
2339                1, 2.  So, we have to use either GetStdHandle (STD_OUTPUT_HANDLE)
2340                or GetStdHandle (STD_ERROR_HANDLE) in order to be able to use
2341                GetConsoleScreenBufferInfo.  */
2342             if (fd == 1 || fd == 2)
2343               {
2344                 HANDLE handle =
2345                   GetStdHandle (fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
2346                 ok = GetConsoleScreenBufferInfo (handle, &info);
2347                 if (ok)
2348                   stream->handle = handle;
2349               }
2350           }
2351         if (ok)
2352           {
2353             stream->is_windows_console = true;
2354             stream->default_console_attributes = info.wAttributes;
2355             stream->current_console_attributes = stream->default_console_attributes;
2356           }
2357         else
2358           /* It's a console, but we cannot use GetConsoleScreenBufferInfo.  */
2359           stream->is_windows_console = false;
2360       }
2361     else
2362       stream->is_windows_console = false;
2363   }
2364   #endif
2365   stream->filename = xstrdup (filename);
2366 
2367   /* Defaults.  */
2368   stream->max_colors = -1;
2369   stream->no_color_video = -1;
2370   stream->set_a_foreground = NULL;
2371   stream->set_foreground = NULL;
2372   stream->set_a_background = NULL;
2373   stream->set_background = NULL;
2374   stream->orig_pair = NULL;
2375   stream->enter_bold_mode = NULL;
2376   stream->enter_italics_mode = NULL;
2377   stream->exit_italics_mode = NULL;
2378   stream->enter_underline_mode = NULL;
2379   stream->exit_underline_mode = NULL;
2380   stream->exit_attribute_mode = NULL;
2381 
2382   #if HAVE_WINDOWS_CONSOLES
2383   if (stream->is_windows_console)
2384     {
2385       /* For Windows consoles, two approaches are possible:
2386          (A) Use SetConsoleMode
2387              <https://docs.microsoft.com/en-us/windows/console/setconsolemode>
2388              to enable the ENABLE_VIRTUAL_TERMINAL_PROCESSING flag, and then
2389              emit escape sequences, as documented in
2390              <https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences>.
2391          (B) Use SetConsoleTextAttribute
2392              <https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute>
2393              to change the text attributes.
2394          Approach (A) has two drawbacks:
2395            * It produces colors that ignore the console's configuration: it
2396              assumes the default configuration (light grey foreground).  Thus
2397              when you ask for cyan, you will always get some blue color, never
2398              real cyan.  Whereas approach (B) produces colors that respect the
2399              "Screen Text" and "Screen Background" settings in the console's
2400              configuration.
2401            * When the program terminates abnormally, we would leave the console
2402              with ENABLE_VIRTUAL_TERMINAL_PROCESSING enabled, which can be
2403              dangerous.
2404          Therefore we use approach (B).  */
2405       stream->max_colors = 8;
2406       stream->no_color_video = 1 | 4;
2407       stream->supports_foreground = true;
2408       stream->supports_background = true;
2409       stream->colormodel = cm_common8;
2410       /* The Windows consoles have high and low intensity, but the default is
2411          high intensity.  If we wanted to support WEIGHT_BOLD, we would have to
2412          use low-intensity rendering for normal output, which would look ugly
2413          compared to the output by other programs.  We could support WEIGHT_DIM,
2414          but this is not part of our enum term_weight_t.  */
2415       stream->supports_weight = false;
2416       stream->supports_posture = false;
2417       stream->supports_underline = true;
2418       stream->supports_hyperlink = false;
2419       stream->restore_colors = NULL;
2420       stream->restore_weight = NULL;
2421       stream->restore_posture = NULL;
2422       stream->restore_underline = NULL;
2423       stream->restore_hyperlink = NULL;
2424     }
2425   else
2426   #endif
2427     {
2428       const char *term;
2429 
2430       /* Retrieve the terminal type.  */
2431       term = getenv ("TERM");
2432       if (term != NULL && term[0] != '\0')
2433         {
2434           /* When the terminfo function are available, we prefer them over the
2435              termcap functions because
2436                1. they don't risk a buffer overflow,
2437                2. on OSF/1, for TERM=xterm, the tiget* functions provide access
2438                   to the number of colors and the color escape sequences,
2439                   whereas the tget* functions don't provide them.  */
2440           #if HAVE_TERMINFO
2441           int err = 1;
2442 
2443           if (setupterm (term, fd, &err) || err == 1)
2444             {
2445               /* Retrieve particular values depending on the terminal type.  */
2446               stream->max_colors = tigetnum ("colors");
2447               stream->no_color_video = tigetnum ("ncv");
2448               stream->set_a_foreground = xstrdup0 (tigetstr ("setaf"));
2449               stream->set_foreground = xstrdup0 (tigetstr ("setf"));
2450               stream->set_a_background = xstrdup0 (tigetstr ("setab"));
2451               stream->set_background = xstrdup0 (tigetstr ("setb"));
2452               stream->orig_pair = xstrdup0 (tigetstr ("op"));
2453               stream->enter_bold_mode = xstrdup0 (tigetstr ("bold"));
2454               stream->enter_italics_mode = xstrdup0 (tigetstr ("sitm"));
2455               stream->exit_italics_mode = xstrdup0 (tigetstr ("ritm"));
2456               stream->enter_underline_mode = xstrdup0 (tigetstr ("smul"));
2457               stream->exit_underline_mode = xstrdup0 (tigetstr ("rmul"));
2458               stream->exit_attribute_mode = xstrdup0 (tigetstr ("sgr0"));
2459             }
2460           #elif HAVE_TERMCAP
2461           struct { char buf[1024]; char canary[4]; } termcapbuf;
2462           int retval;
2463 
2464           /* Call tgetent, being defensive against buffer overflow.  */
2465           memcpy (termcapbuf.canary, "CnRy", 4);
2466           retval = tgetent (termcapbuf.buf, term);
2467           if (memcmp (termcapbuf.canary, "CnRy", 4) != 0)
2468             /* Buffer overflow!  */
2469             abort ();
2470 
2471           if (retval > 0)
2472             {
2473               struct { char buf[1024]; char canary[4]; } termentrybuf;
2474               char *termentryptr;
2475 
2476               /* Prepare for calling tgetstr, being defensive against buffer
2477                  overflow.  ncurses' tgetstr() supports a second argument NULL,
2478                  but NetBSD's tgetstr() doesn't.  */
2479               memcpy (termentrybuf.canary, "CnRz", 4);
2480               #define TEBP ((termentryptr = termentrybuf.buf), &termentryptr)
2481 
2482               /* Retrieve particular values depending on the terminal type.  */
2483               stream->max_colors = tgetnum ("Co");
2484               stream->no_color_video = tgetnum ("NC");
2485               stream->set_a_foreground = xstrdup0 (tgetstr ("AF", TEBP));
2486               stream->set_foreground = xstrdup0 (tgetstr ("Sf", TEBP));
2487               stream->set_a_background = xstrdup0 (tgetstr ("AB", TEBP));
2488               stream->set_background = xstrdup0 (tgetstr ("Sb", TEBP));
2489               stream->orig_pair = xstrdup0 (tgetstr ("op", TEBP));
2490               stream->enter_bold_mode = xstrdup0 (tgetstr ("md", TEBP));
2491               stream->enter_italics_mode = xstrdup0 (tgetstr ("ZH", TEBP));
2492               stream->exit_italics_mode = xstrdup0 (tgetstr ("ZR", TEBP));
2493               stream->enter_underline_mode = xstrdup0 (tgetstr ("us", TEBP));
2494               stream->exit_underline_mode = xstrdup0 (tgetstr ("ue", TEBP));
2495               stream->exit_attribute_mode = xstrdup0 (tgetstr ("me", TEBP));
2496 
2497               #ifdef __BEOS__
2498               /* The BeOS termcap entry for "beterm" is broken: For "AF" and
2499                  "AB" it contains balues in terminfo syntax but the system's
2500                  tparam() function understands only the termcap syntax.  */
2501               if (stream->set_a_foreground != NULL
2502                   && strcmp (stream->set_a_foreground, "\033[3%p1%dm") == 0)
2503                 {
2504                   free (stream->set_a_foreground);
2505                   stream->set_a_foreground = xstrdup ("\033[3%dm");
2506                 }
2507               if (stream->set_a_background != NULL
2508                   && strcmp (stream->set_a_background, "\033[4%p1%dm") == 0)
2509                 {
2510                   free (stream->set_a_background);
2511                   stream->set_a_background = xstrdup ("\033[4%dm");
2512                 }
2513               #endif
2514 
2515               /* The termcap entry for cygwin is broken: It has no "ncv" value,
2516                  but bold and underline are actually rendered through colors.  */
2517               if (strcmp (term, "cygwin") == 0)
2518                 stream->no_color_video |= 2 | 32;
2519 
2520               /* Done with tgetstr.  Detect possible buffer overflow.  */
2521               #undef TEBP
2522               if (memcmp (termentrybuf.canary, "CnRz", 4) != 0)
2523                 /* Buffer overflow!  */
2524                 abort ();
2525             }
2526           #else
2527           /* Fallback code for platforms with neither the terminfo nor the
2528              termcap functions, such as mingw.
2529              Assume the ANSI escape sequences.  Extracted through
2530              "TERM=ansi infocmp", replacing \E with \033.  */
2531           stream->max_colors = 8;
2532           stream->no_color_video = 3;
2533           stream->set_a_foreground = xstrdup ("\033[3%p1%dm");
2534           stream->set_a_background = xstrdup ("\033[4%p1%dm");
2535           stream->orig_pair = xstrdup ("\033[39;49m");
2536           stream->enter_bold_mode = xstrdup ("\033[1m");
2537           stream->enter_underline_mode = xstrdup ("\033[4m");
2538           stream->exit_underline_mode = xstrdup ("\033[m");
2539           stream->exit_attribute_mode = xstrdup ("\033[0;10m");
2540           #endif
2541 
2542           /* AIX 4.3.2, IRIX 6.5, HP-UX 11, Solaris 7..10 all lack the
2543              description of color capabilities of "xterm" and "xterms"
2544              in their terminfo database.  But it is important to have
2545              color in xterm.  So we provide the color capabilities here.  */
2546           if (stream->max_colors <= 1
2547               && (strcmp (term, "xterm") == 0 || strcmp (term, "xterms") == 0))
2548             {
2549               stream->max_colors = 8;
2550               stream->set_a_foreground = xstrdup ("\033[3%p1%dm");
2551               stream->set_a_background = xstrdup ("\033[4%p1%dm");
2552               stream->orig_pair = xstrdup ("\033[39;49m");
2553             }
2554         }
2555 
2556       /* Infer the capabilities.  */
2557       stream->supports_foreground =
2558         (stream->max_colors >= 8
2559          && (stream->set_a_foreground != NULL || stream->set_foreground != NULL)
2560          && stream->orig_pair != NULL);
2561       stream->supports_background =
2562         (stream->max_colors >= 8
2563          && (stream->set_a_background != NULL || stream->set_background != NULL)
2564          && stream->orig_pair != NULL);
2565       stream->colormodel =
2566         (stream->supports_foreground || stream->supports_background
2567          ? (term != NULL
2568             && (/* Recognize xterm-16color, xterm-88color, xterm-256color.  */
2569                 (strlen (term) >= 5 && memcmp (term, "xterm", 5) == 0)
2570                 || /* Recognize *-16color.  */
2571                    (strlen (term) > 8
2572                     && strcmp (term + strlen (term) - 8, "-16color") == 0)
2573                 || /* Recognize *-256color.  */
2574                    (strlen (term) > 9
2575                     && strcmp (term + strlen (term) - 9, "-256color") == 0)
2576                 || /* Recognize *-direct.  */
2577                    (strlen (term) > 8
2578                     && strcmp (term + strlen (term) - 8, "-direct") == 0))
2579             ? (stream->max_colors >= 0x7fff ? cm_xtermrgb :
2580                stream->max_colors == 256 ? cm_xterm256 :
2581                stream->max_colors == 88 ? cm_xterm88 :
2582                stream->max_colors == 16 ? cm_xterm16 :
2583                cm_xterm8)
2584             : cm_common8)
2585          : cm_monochrome);
2586       stream->supports_weight =
2587         (stream->enter_bold_mode != NULL
2588          && stream->exit_attribute_mode != NULL);
2589       stream->supports_posture =
2590         (stream->enter_italics_mode != NULL
2591          && (stream->exit_italics_mode != NULL
2592              || stream->exit_attribute_mode != NULL));
2593       stream->supports_underline =
2594         (stream->enter_underline_mode != NULL
2595          && (stream->exit_underline_mode != NULL
2596              || stream->exit_attribute_mode != NULL));
2597       /* TODO: Use a terminfo capability, once ncurses implements it.  */
2598       stream->supports_hyperlink = should_enable_hyperlinks (term);
2599 
2600       /* Infer the restore strings.  */
2601       stream->restore_colors =
2602         (stream->supports_foreground || stream->supports_background
2603          ? stream->orig_pair
2604          : NULL);
2605       stream->restore_weight =
2606         (stream->supports_weight ? stream->exit_attribute_mode : NULL);
2607       stream->restore_posture =
2608         (stream->supports_posture
2609          ? (stream->exit_italics_mode != NULL
2610             ? stream->exit_italics_mode
2611             : stream->exit_attribute_mode)
2612          : NULL);
2613       stream->restore_underline =
2614         (stream->supports_underline
2615          ? (stream->exit_underline_mode != NULL
2616             ? stream->exit_underline_mode
2617             : stream->exit_attribute_mode)
2618          : NULL);
2619       stream->restore_hyperlink =
2620         (stream->supports_hyperlink
2621          ? "\033]8;;\033\\"
2622          : NULL);
2623     }
2624 
2625   /* Initialize the hyperlink id generator.  */
2626   if (stream->supports_hyperlink)
2627     {
2628       char *hostname = xgethostname ();
2629       { /* Compute a hash code, like in gnulib/lib/hash-pjw.c.  */
2630         uint32_t h = 0;
2631         const char *p;
2632         for (p = hostname; *p; p++)
2633           h = (unsigned char) *p + ((h << 9) | (h >> (32 - 9)));
2634         stream->hostname_hash = h;
2635       }
2636       free (hostname);
2637 
2638       {
2639         struct timeval tv;
2640         gettimeofday (&tv, NULL);
2641         stream->start_time =
2642           (uint64_t) tv.tv_sec * (uint64_t) 1000000 + (uint64_t) tv.tv_usec;
2643       }
2644 
2645       stream->id_serial = 0;
2646     }
2647 
2648   /* Initialize the set of hyperlink_t.  */
2649   stream->hyperlinks_array = NULL;
2650   stream->hyperlinks_count = 0;
2651   stream->hyperlinks_allocated = 0;
2652 
2653   /* Initialize the buffer.  */
2654   stream->allocated = 120;
2655   stream->buffer = XNMALLOC (stream->allocated, char);
2656   stream->attrbuffer = XNMALLOC (stream->allocated, attributes_t);
2657   stream->buflen = 0;
2658 
2659   /* Initialize the current attributes.  */
2660   {
2661     attributes_t assumed_default;
2662     attributes_t simplified_default;
2663 
2664     assumed_default.color = COLOR_DEFAULT;
2665     assumed_default.bgcolor = COLOR_DEFAULT;
2666     assumed_default.weight = WEIGHT_DEFAULT;
2667     assumed_default.posture = POSTURE_DEFAULT;
2668     assumed_default.underline = UNDERLINE_DEFAULT;
2669     assumed_default.hyperlink = NULL;
2670 
2671     simplified_default = simplify_attributes (stream, assumed_default);
2672 
2673     stream->default_attr = simplified_default;
2674     stream->active_attr = simplified_default;
2675     stream->curr_attr = assumed_default;
2676     stream->simp_attr = simplified_default;
2677   }
2678 
2679   /* Prepare tty control.  */
2680   activate_term_style_controller (&controller, stream, fd, tty_control);
2681 
2682   return stream;
2683 }
2684 
2685 #line 2686 "term-ostream.c"
2686 
2687 const struct term_ostream_implementation term_ostream_vtable =
2688 {
2689   term_ostream_superclasses,
2690   sizeof (term_ostream_superclasses) / sizeof (term_ostream_superclasses[0]),
2691   sizeof (struct term_ostream_representation),
2692   term_ostream__write_mem,
2693   term_ostream__flush,
2694   term_ostream__free,
2695   term_ostream__rgb_to_color,
2696   term_ostream__get_color,
2697   term_ostream__set_color,
2698   term_ostream__get_bgcolor,
2699   term_ostream__set_bgcolor,
2700   term_ostream__get_weight,
2701   term_ostream__set_weight,
2702   term_ostream__get_posture,
2703   term_ostream__set_posture,
2704   term_ostream__get_underline,
2705   term_ostream__set_underline,
2706   term_ostream__get_hyperlink_ref,
2707   term_ostream__get_hyperlink_id,
2708   term_ostream__set_hyperlink,
2709   term_ostream__flush_to_current_style,
2710 };
2711 
2712 #if !HAVE_INLINE
2713 
2714 /* Define the functions that invoke the methods.  */
2715 
2716 void
term_ostream_write_mem(term_ostream_t first_arg,const void * data,size_t len)2717 term_ostream_write_mem (term_ostream_t first_arg, const void *data, size_t len)
2718 {
2719   const struct term_ostream_implementation *vtable =
2720     ((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
2721   vtable->write_mem (first_arg,data,len);
2722 }
2723 
2724 void
term_ostream_flush(term_ostream_t first_arg,ostream_flush_scope_t scope)2725 term_ostream_flush (term_ostream_t first_arg, ostream_flush_scope_t scope)
2726 {
2727   const struct term_ostream_implementation *vtable =
2728     ((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
2729   vtable->flush (first_arg,scope);
2730 }
2731 
2732 void
term_ostream_free(term_ostream_t first_arg)2733 term_ostream_free (term_ostream_t first_arg)
2734 {
2735   const struct term_ostream_implementation *vtable =
2736     ((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
2737   vtable->free (first_arg);
2738 }
2739 
2740 term_color_t
term_ostream_rgb_to_color(term_ostream_t first_arg,int red,int green,int blue)2741 term_ostream_rgb_to_color (term_ostream_t first_arg,                              int red, int green, int blue)
2742 {
2743   const struct term_ostream_implementation *vtable =
2744     ((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
2745   return vtable->rgb_to_color (first_arg,red,green,blue);
2746 }
2747 
2748 term_color_t
term_ostream_get_color(term_ostream_t first_arg)2749 term_ostream_get_color (term_ostream_t first_arg)
2750 {
2751   const struct term_ostream_implementation *vtable =
2752     ((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
2753   return vtable->get_color (first_arg);
2754 }
2755 
2756 void
term_ostream_set_color(term_ostream_t first_arg,term_color_t color)2757 term_ostream_set_color (term_ostream_t first_arg, term_color_t color)
2758 {
2759   const struct term_ostream_implementation *vtable =
2760     ((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
2761   vtable->set_color (first_arg,color);
2762 }
2763 
2764 term_color_t
term_ostream_get_bgcolor(term_ostream_t first_arg)2765 term_ostream_get_bgcolor (term_ostream_t first_arg)
2766 {
2767   const struct term_ostream_implementation *vtable =
2768     ((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
2769   return vtable->get_bgcolor (first_arg);
2770 }
2771 
2772 void
term_ostream_set_bgcolor(term_ostream_t first_arg,term_color_t color)2773 term_ostream_set_bgcolor (term_ostream_t first_arg, term_color_t color)
2774 {
2775   const struct term_ostream_implementation *vtable =
2776     ((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
2777   vtable->set_bgcolor (first_arg,color);
2778 }
2779 
2780 term_weight_t
term_ostream_get_weight(term_ostream_t first_arg)2781 term_ostream_get_weight (term_ostream_t first_arg)
2782 {
2783   const struct term_ostream_implementation *vtable =
2784     ((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
2785   return vtable->get_weight (first_arg);
2786 }
2787 
2788 void
term_ostream_set_weight(term_ostream_t first_arg,term_weight_t weight)2789 term_ostream_set_weight (term_ostream_t first_arg, term_weight_t weight)
2790 {
2791   const struct term_ostream_implementation *vtable =
2792     ((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
2793   vtable->set_weight (first_arg,weight);
2794 }
2795 
2796 term_posture_t
term_ostream_get_posture(term_ostream_t first_arg)2797 term_ostream_get_posture (term_ostream_t first_arg)
2798 {
2799   const struct term_ostream_implementation *vtable =
2800     ((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
2801   return vtable->get_posture (first_arg);
2802 }
2803 
2804 void
term_ostream_set_posture(term_ostream_t first_arg,term_posture_t posture)2805 term_ostream_set_posture (term_ostream_t first_arg, term_posture_t posture)
2806 {
2807   const struct term_ostream_implementation *vtable =
2808     ((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
2809   vtable->set_posture (first_arg,posture);
2810 }
2811 
2812 term_underline_t
term_ostream_get_underline(term_ostream_t first_arg)2813 term_ostream_get_underline (term_ostream_t first_arg)
2814 {
2815   const struct term_ostream_implementation *vtable =
2816     ((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
2817   return vtable->get_underline (first_arg);
2818 }
2819 
2820 void
term_ostream_set_underline(term_ostream_t first_arg,term_underline_t underline)2821 term_ostream_set_underline (term_ostream_t first_arg,                                   term_underline_t underline)
2822 {
2823   const struct term_ostream_implementation *vtable =
2824     ((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
2825   vtable->set_underline (first_arg,underline);
2826 }
2827 
2828 const char *
term_ostream_get_hyperlink_ref(term_ostream_t first_arg)2829 term_ostream_get_hyperlink_ref (term_ostream_t first_arg)
2830 {
2831   const struct term_ostream_implementation *vtable =
2832     ((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
2833   return vtable->get_hyperlink_ref (first_arg);
2834 }
2835 
2836 const char *
term_ostream_get_hyperlink_id(term_ostream_t first_arg)2837 term_ostream_get_hyperlink_id (term_ostream_t first_arg)
2838 {
2839   const struct term_ostream_implementation *vtable =
2840     ((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
2841   return vtable->get_hyperlink_id (first_arg);
2842 }
2843 
2844 void
term_ostream_set_hyperlink(term_ostream_t first_arg,const char * ref,const char * id)2845 term_ostream_set_hyperlink (term_ostream_t first_arg,                               const char *ref, const char *id)
2846 {
2847   const struct term_ostream_implementation *vtable =
2848     ((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
2849   vtable->set_hyperlink (first_arg,ref,id);
2850 }
2851 
2852 void
term_ostream_flush_to_current_style(term_ostream_t first_arg)2853 term_ostream_flush_to_current_style (term_ostream_t first_arg)
2854 {
2855   const struct term_ostream_implementation *vtable =
2856     ((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
2857   vtable->flush_to_current_style (first_arg);
2858 }
2859 
2860 #endif
2861