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