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