facf0c92e0
Red Bear OS is a full fork. All sources must be available from git clone with zero network access. Removed gitignore rules that excluded fetched source trees under recipes/*/source/, local/recipes/kde/*/source/, local/recipes/qt/*/source/, and vendor source trees. Build artifacts (target/, build/, source.tar, *.o, *.so) remain excluded. 127291 files added — kernel, relibc, base, bootloader, pkgar, all KDE/Qt frameworks, mesa, wayland, DRM drivers, and every other recipe source.
2951 lines
94 KiB
C
2951 lines
94 KiB
C
/* DO NOT EDIT! GENERATED AUTOMATICALLY! */
|
|
|
|
#if !IS_CPLUSPLUS
|
|
#define term_ostream_representation any_ostream_representation
|
|
#endif
|
|
#line 1 "term-ostream.oo.c"
|
|
/* Output stream for attributed text, producing ANSI escape sequences.
|
|
Copyright (C) 2006-2008, 2017, 2019-2020, 2022-2023 Free Software Foundation, Inc.
|
|
Written by Bruno Haible <bruno@clisp.org>, 2006.
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
|
|
|
#include <config.h>
|
|
|
|
/* Specification. */
|
|
#include "term-ostream.h"
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <sys/time.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#if HAVE_TCDRAIN
|
|
# include <termios.h>
|
|
#endif
|
|
#if defined _WIN32 || defined __CYGWIN__ /* Windows */
|
|
# define HAVE_WINDOWS_CONSOLES 1
|
|
# include <windows.h>
|
|
#endif
|
|
|
|
#include "error.h"
|
|
#include "full-write.h"
|
|
#include "get_ppid_of.h"
|
|
#include "get_progname_of.h"
|
|
#include "terminfo.h"
|
|
#include "xalloc.h"
|
|
#include "xgethostname.h"
|
|
#include "xsize.h"
|
|
#if HAVE_WINDOWS_CONSOLES
|
|
/* Get _get_osfhandle(). */
|
|
# if defined _WIN32 && ! defined __CYGWIN__
|
|
# include "msvc-nothrow.h"
|
|
# else
|
|
# include <io.h>
|
|
# endif
|
|
#endif
|
|
#include "gettext.h"
|
|
|
|
#define _(str) gettext (str)
|
|
|
|
#if HAVE_TPARAM
|
|
/* GNU termcap's tparam() function requires a buffer argument. Make it so
|
|
large that there is no risk that tparam() needs to call malloc(). */
|
|
static char tparambuf[100];
|
|
/* Define tparm in terms of tparam. In the scope of this file, it is called
|
|
with at most one argument after the string. */
|
|
# define tparm(str, arg1) \
|
|
tparam (str, tparambuf, sizeof (tparambuf), arg1)
|
|
#endif
|
|
|
|
|
|
/* =========================== Color primitives =========================== */
|
|
|
|
/* A color in RGB format. */
|
|
typedef struct
|
|
{
|
|
unsigned int red : 8; /* range 0..255 */
|
|
unsigned int green : 8; /* range 0..255 */
|
|
unsigned int blue : 8; /* range 0..255 */
|
|
} rgb_t;
|
|
|
|
/* A color in HSV (a.k.a. HSB) format. */
|
|
typedef struct
|
|
{
|
|
float hue; /* normalized to interval [0,6) */
|
|
float saturation; /* normalized to interval [0,1] */
|
|
float brightness; /* a.k.a. value, normalized to interval [0,1] */
|
|
} hsv_t;
|
|
|
|
/* Conversion of a color in RGB to HSV format. */
|
|
static void
|
|
rgb_to_hsv (rgb_t c, hsv_t *result)
|
|
{
|
|
unsigned int r = c.red;
|
|
unsigned int g = c.green;
|
|
unsigned int b = c.blue;
|
|
|
|
if (r > g)
|
|
{
|
|
if (b > r)
|
|
{
|
|
/* b > r > g, so max = b, min = g */
|
|
result->hue = 4.0f + (float) (r - g) / (float) (b - g);
|
|
result->saturation = 1.0f - (float) g / (float) b;
|
|
result->brightness = (float) b / 255.0f;
|
|
}
|
|
else if (b <= g)
|
|
{
|
|
/* r > g >= b, so max = r, min = b */
|
|
result->hue = 0.0f + (float) (g - b) / (float) (r - b);
|
|
result->saturation = 1.0f - (float) b / (float) r;
|
|
result->brightness = (float) r / 255.0f;
|
|
}
|
|
else
|
|
{
|
|
/* r >= b > g, so max = r, min = g */
|
|
result->hue = 6.0f - (float) (b - g) / (float) (r - g);
|
|
result->saturation = 1.0f - (float) g / (float) r;
|
|
result->brightness = (float) r / 255.0f;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (b > g)
|
|
{
|
|
/* b > g >= r, so max = b, min = r */
|
|
result->hue = 4.0f - (float) (g - r) / (float) (b - r);
|
|
result->saturation = 1.0f - (float) r / (float) b;
|
|
result->brightness = (float) b / 255.0f;
|
|
}
|
|
else if (b < r)
|
|
{
|
|
/* g >= r > b, so max = g, min = b */
|
|
result->hue = 2.0f - (float) (r - b) / (float) (g - b);
|
|
result->saturation = 1.0f - (float) b / (float) g;
|
|
result->brightness = (float) g / 255.0f;
|
|
}
|
|
else if (g > r)
|
|
{
|
|
/* g >= b >= r, g > r, so max = g, min = r */
|
|
result->hue = 2.0f + (float) (b - r) / (float) (g - r);
|
|
result->saturation = 1.0f - (float) r / (float) g;
|
|
result->brightness = (float) g / 255.0f;
|
|
}
|
|
else
|
|
{
|
|
/* r = g = b. A grey color. */
|
|
result->hue = 0; /* arbitrary */
|
|
result->saturation = 0;
|
|
result->brightness = (float) r / 255.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Square of distance of two colors. */
|
|
static float
|
|
color_distance (const hsv_t *color1, const hsv_t *color2)
|
|
{
|
|
#if 0
|
|
/* Formula taken from "John Smith: Color Similarity",
|
|
http://www.ctr.columbia.edu/~jrsmith/html/pubs/acmmm96/node8.html. */
|
|
float angle1 = color1->hue * 1.04719755f; /* normalize to [0,2π] */
|
|
float angle2 = color2->hue * 1.04719755f; /* normalize to [0,2π] */
|
|
float delta_x = color1->saturation * cosf (angle1)
|
|
- color2->saturation * cosf (angle2);
|
|
float delta_y = color1->saturation * sinf (angle1)
|
|
- color2->saturation * sinf (angle2);
|
|
float delta_v = color1->brightness
|
|
- color2->brightness;
|
|
|
|
return delta_x * delta_x + delta_y * delta_y + delta_v * delta_v;
|
|
#else
|
|
/* Formula that considers hue differences with more weight than saturation
|
|
or brightness differences, like the human eye does. */
|
|
float delta_hue =
|
|
(color1->hue >= color2->hue
|
|
? (color1->hue - color2->hue >= 3.0f
|
|
? 6.0f + color2->hue - color1->hue
|
|
: color1->hue - color2->hue)
|
|
: (color2->hue - color1->hue >= 3.0f
|
|
? 6.0f + color1->hue - color2->hue
|
|
: color2->hue - color1->hue));
|
|
float min_saturation =
|
|
(color1->saturation < color2->saturation
|
|
? color1->saturation
|
|
: color2->saturation);
|
|
float delta_saturation = color1->saturation - color2->saturation;
|
|
float delta_brightness = color1->brightness - color2->brightness;
|
|
|
|
return delta_hue * delta_hue * min_saturation
|
|
+ delta_saturation * delta_saturation * 0.2f
|
|
+ delta_brightness * delta_brightness * 0.8f;
|
|
#endif
|
|
}
|
|
|
|
/* Return the index of the color in a color table that is nearest to a given
|
|
color. */
|
|
static unsigned int
|
|
nearest_color (rgb_t given, const rgb_t *table, unsigned int table_size)
|
|
{
|
|
hsv_t given_hsv;
|
|
unsigned int best_index;
|
|
float best_distance;
|
|
unsigned int i;
|
|
|
|
assert (table_size > 0);
|
|
|
|
rgb_to_hsv (given, &given_hsv);
|
|
|
|
best_index = 0;
|
|
best_distance = 1000000.0f;
|
|
for (i = 0; i < table_size; i++)
|
|
{
|
|
hsv_t i_hsv;
|
|
|
|
rgb_to_hsv (table[i], &i_hsv);
|
|
|
|
/* Avoid converting a color to grey, or fading out a color too much. */
|
|
if (i_hsv.saturation > given_hsv.saturation * 0.5f)
|
|
{
|
|
float distance = color_distance (&given_hsv, &i_hsv);
|
|
if (distance < best_distance)
|
|
{
|
|
best_index = i;
|
|
best_distance = distance;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0 /* Debugging code */
|
|
hsv_t best_hsv;
|
|
rgb_to_hsv (table[best_index], &best_hsv);
|
|
fprintf (stderr, "nearest: (%d,%d,%d) = (%f,%f,%f)\n -> (%f,%f,%f) = (%d,%d,%d)\n",
|
|
given.red, given.green, given.blue,
|
|
(double)given_hsv.hue, (double)given_hsv.saturation, (double)given_hsv.brightness,
|
|
(double)best_hsv.hue, (double)best_hsv.saturation, (double)best_hsv.brightness,
|
|
table[best_index].red, table[best_index].green, table[best_index].blue);
|
|
#endif
|
|
|
|
return best_index;
|
|
}
|
|
|
|
/* The luminance of a color. This is the brightness of the color, as it
|
|
appears to the human eye. This must be used in color to grey conversion. */
|
|
static float
|
|
color_luminance (int r, int g, int b)
|
|
{
|
|
/* Use the luminance model used by NTSC and JPEG.
|
|
Taken from http://www.fho-emden.de/~hoffmann/gray10012001.pdf .
|
|
No need to care about rounding errors leading to luminance > 1;
|
|
this cannot happen. */
|
|
return (0.299f * r + 0.587f * g + 0.114f * b) / 255.0f;
|
|
}
|
|
|
|
|
|
/* ============================= Color models ============================= */
|
|
|
|
/* The color model used by the terminal. */
|
|
typedef enum
|
|
{
|
|
cm_monochrome, /* No colors. */
|
|
cm_common8, /* Usual terminal with at least 8 colors. */
|
|
cm_xterm8, /* TERM=xterm, with 8 colors. */
|
|
cm_xterm16, /* TERM=xterm-16color, with 16 colors. */
|
|
cm_xterm88, /* TERM=xterm-88color, with 88 colors. */
|
|
cm_xterm256, /* TERM=xterm-256color, with 256 colors. */
|
|
cm_xtermrgb /* TERM=xterm-direct, with 256*256*256 colors. */
|
|
} colormodel_t;
|
|
|
|
/* ----------------------- cm_monochrome color model ----------------------- */
|
|
|
|
/* A non-default color index doesn't exist in this color model. */
|
|
static inline term_color_t
|
|
rgb_to_color_monochrome (void)
|
|
{
|
|
return COLOR_DEFAULT;
|
|
}
|
|
|
|
/* ------------------------ cm_common8 color model ------------------------ */
|
|
|
|
/* A non-default color index is in the range 0..7.
|
|
RGB components
|
|
COLOR_BLACK 000
|
|
COLOR_BLUE 001
|
|
COLOR_GREEN 010
|
|
COLOR_CYAN 011
|
|
COLOR_RED 100
|
|
COLOR_MAGENTA 101
|
|
COLOR_YELLOW 110
|
|
COLOR_WHITE 111 */
|
|
static const rgb_t colors_of_common8[8] =
|
|
{
|
|
/* R G B grey index */
|
|
{ 0, 0, 0 }, /* 0.000 0 */
|
|
{ 0, 0, 255 },
|
|
{ 0, 255, 0 },
|
|
{ 0, 255, 255 },
|
|
{ 255, 0, 0 },
|
|
{ 255, 0, 255 },
|
|
{ 255, 255, 0 },
|
|
{ 255, 255, 255 } /* 1.000 7 */
|
|
};
|
|
|
|
static inline term_color_t
|
|
rgb_to_color_common8 (int r, int g, int b)
|
|
{
|
|
rgb_t color;
|
|
hsv_t hsv;
|
|
|
|
color.red = r; color.green = g; color.blue = b;
|
|
rgb_to_hsv (color, &hsv);
|
|
|
|
if (hsv.saturation < 0.065f)
|
|
{
|
|
/* Greyscale approximation. */
|
|
float luminance = color_luminance (r, g, b);
|
|
if (luminance < 0.500f)
|
|
return 0;
|
|
else
|
|
return 7;
|
|
}
|
|
else
|
|
/* Color approximation. */
|
|
return nearest_color (color, colors_of_common8, 8);
|
|
}
|
|
|
|
/* Convert a cm_common8 color in RGB encoding to BGR encoding.
|
|
See the ncurses terminfo(5) manual page, section "Color Handling", for an
|
|
explanation why this is needed. */
|
|
static _GL_ASYNC_SAFE inline int
|
|
color_bgr (term_color_t color)
|
|
{
|
|
return ((color & 4) >> 2) | (color & 2) | ((color & 1) << 2);
|
|
}
|
|
|
|
/* ------------------------- cm_xterm8 color model ------------------------- */
|
|
|
|
/* A non-default color index is in the range 0..7.
|
|
BGR components
|
|
COLOR_BLACK 000
|
|
COLOR_RED 001
|
|
COLOR_GREEN 010
|
|
COLOR_YELLOW 011
|
|
COLOR_BLUE 100
|
|
COLOR_MAGENTA 101
|
|
COLOR_CYAN 110
|
|
COLOR_WHITE 111 */
|
|
static const rgb_t colors_of_xterm8[8] =
|
|
{
|
|
/* The real xterm's colors are dimmed; assume full-brightness instead. */
|
|
/* R G B grey index */
|
|
{ 0, 0, 0 }, /* 0.000 0 */
|
|
{ 255, 0, 0 },
|
|
{ 0, 255, 0 },
|
|
{ 255, 255, 0 },
|
|
{ 0, 0, 255 },
|
|
{ 255, 0, 255 },
|
|
{ 0, 255, 255 },
|
|
{ 255, 255, 255 } /* 1.000 7 */
|
|
};
|
|
|
|
static inline term_color_t
|
|
rgb_to_color_xterm8 (int r, int g, int b)
|
|
{
|
|
rgb_t color;
|
|
hsv_t hsv;
|
|
|
|
color.red = r; color.green = g; color.blue = b;
|
|
rgb_to_hsv (color, &hsv);
|
|
|
|
if (hsv.saturation < 0.065f)
|
|
{
|
|
/* Greyscale approximation. */
|
|
float luminance = color_luminance (r, g, b);
|
|
if (luminance < 0.500f)
|
|
return 0;
|
|
else
|
|
return 7;
|
|
}
|
|
else
|
|
/* Color approximation. */
|
|
return nearest_color (color, colors_of_xterm8, 8);
|
|
}
|
|
|
|
/* ------------------------ cm_xterm16 color model ------------------------ */
|
|
|
|
/* A non-default color index is in the range 0..15.
|
|
The RGB values come from xterm's XTerm-col.ad. */
|
|
static const rgb_t colors_of_xterm16[16] =
|
|
{
|
|
/* R G B grey index */
|
|
{ 0, 0, 0 }, /* 0.000 0 */
|
|
{ 205, 0, 0 },
|
|
{ 0, 205, 0 },
|
|
{ 205, 205, 0 },
|
|
{ 0, 0, 205 },
|
|
{ 205, 0, 205 },
|
|
{ 0, 205, 205 },
|
|
{ 229, 229, 229 }, /* 0.898 7 */
|
|
{ 77, 77, 77 }, /* 0.302 8 */
|
|
{ 255, 0, 0 },
|
|
{ 0, 255, 0 },
|
|
{ 255, 255, 0 },
|
|
{ 0, 0, 255 },
|
|
{ 255, 0, 255 },
|
|
{ 0, 255, 255 },
|
|
{ 255, 255, 255 } /* 1.000 15 */
|
|
};
|
|
|
|
static inline term_color_t
|
|
rgb_to_color_xterm16 (int r, int g, int b)
|
|
{
|
|
rgb_t color;
|
|
hsv_t hsv;
|
|
|
|
color.red = r; color.green = g; color.blue = b;
|
|
rgb_to_hsv (color, &hsv);
|
|
|
|
if (hsv.saturation < 0.065f)
|
|
{
|
|
/* Greyscale approximation. */
|
|
float luminance = color_luminance (r, g, b);
|
|
if (luminance < 0.151f)
|
|
return 0;
|
|
else if (luminance < 0.600f)
|
|
return 8;
|
|
else if (luminance < 0.949f)
|
|
return 7;
|
|
else
|
|
return 15;
|
|
}
|
|
else
|
|
/* Color approximation. */
|
|
return nearest_color (color, colors_of_xterm16, 16);
|
|
}
|
|
|
|
/* ------------------------ cm_xterm88 color model ------------------------ */
|
|
|
|
/* A non-default color index is in the range 0..87.
|
|
Colors 0..15 are the same as in the cm_xterm16 color model.
|
|
Colors 16..87 are defined in xterm's 88colres.h. */
|
|
|
|
static const rgb_t colors_of_xterm88[88] =
|
|
{
|
|
/* R G B grey index */
|
|
{ 0, 0, 0 }, /* 0.000 0 */
|
|
{ 205, 0, 0 },
|
|
{ 0, 205, 0 },
|
|
{ 205, 205, 0 },
|
|
{ 0, 0, 205 },
|
|
{ 205, 0, 205 },
|
|
{ 0, 205, 205 },
|
|
{ 229, 229, 229 }, /* 0.898 7 */
|
|
{ 77, 77, 77 }, /* 0.302 8 */
|
|
{ 255, 0, 0 },
|
|
{ 0, 255, 0 },
|
|
{ 255, 255, 0 },
|
|
{ 0, 0, 255 },
|
|
{ 255, 0, 255 },
|
|
{ 0, 255, 255 },
|
|
{ 255, 255, 255 }, /* 1.000 15 */
|
|
{ 0, 0, 0 }, /* 0.000 16 */
|
|
{ 0, 0, 139 },
|
|
{ 0, 0, 205 },
|
|
{ 0, 0, 255 },
|
|
{ 0, 139, 0 },
|
|
{ 0, 139, 139 },
|
|
{ 0, 139, 205 },
|
|
{ 0, 139, 255 },
|
|
{ 0, 205, 0 },
|
|
{ 0, 205, 139 },
|
|
{ 0, 205, 205 },
|
|
{ 0, 205, 255 },
|
|
{ 0, 255, 0 },
|
|
{ 0, 255, 139 },
|
|
{ 0, 255, 205 },
|
|
{ 0, 255, 255 },
|
|
{ 139, 0, 0 },
|
|
{ 139, 0, 139 },
|
|
{ 139, 0, 205 },
|
|
{ 139, 0, 255 },
|
|
{ 139, 139, 0 },
|
|
{ 139, 139, 139 }, /* 0.545 37 */
|
|
{ 139, 139, 205 },
|
|
{ 139, 139, 255 },
|
|
{ 139, 205, 0 },
|
|
{ 139, 205, 139 },
|
|
{ 139, 205, 205 },
|
|
{ 139, 205, 255 },
|
|
{ 139, 255, 0 },
|
|
{ 139, 255, 139 },
|
|
{ 139, 255, 205 },
|
|
{ 139, 255, 255 },
|
|
{ 205, 0, 0 },
|
|
{ 205, 0, 139 },
|
|
{ 205, 0, 205 },
|
|
{ 205, 0, 255 },
|
|
{ 205, 139, 0 },
|
|
{ 205, 139, 139 },
|
|
{ 205, 139, 205 },
|
|
{ 205, 139, 255 },
|
|
{ 205, 205, 0 },
|
|
{ 205, 205, 139 },
|
|
{ 205, 205, 205 }, /* 0.804 58 */
|
|
{ 205, 205, 255 },
|
|
{ 205, 255, 0 },
|
|
{ 205, 255, 139 },
|
|
{ 205, 255, 205 },
|
|
{ 205, 255, 255 },
|
|
{ 255, 0, 0 },
|
|
{ 255, 0, 139 },
|
|
{ 255, 0, 205 },
|
|
{ 255, 0, 255 },
|
|
{ 255, 139, 0 },
|
|
{ 255, 139, 139 },
|
|
{ 255, 139, 205 },
|
|
{ 255, 139, 255 },
|
|
{ 255, 205, 0 },
|
|
{ 255, 205, 139 },
|
|
{ 255, 205, 205 },
|
|
{ 255, 205, 255 },
|
|
{ 255, 255, 0 },
|
|
{ 255, 255, 139 },
|
|
{ 255, 255, 205 },
|
|
{ 255, 255, 255 }, /* 1.000 79 */
|
|
{ 46, 46, 46 }, /* 0.180 80 */
|
|
{ 92, 92, 92 }, /* 0.361 81 */
|
|
{ 115, 115, 115 }, /* 0.451 82 */
|
|
{ 139, 139, 139 }, /* 0.545 83 */
|
|
{ 162, 162, 162 }, /* 0.635 84 */
|
|
{ 185, 185, 185 }, /* 0.725 85 */
|
|
{ 208, 208, 208 }, /* 0.816 86 */
|
|
{ 231, 231, 231 } /* 0.906 87 */
|
|
};
|
|
|
|
static inline term_color_t
|
|
rgb_to_color_xterm88 (int r, int g, int b)
|
|
{
|
|
rgb_t color;
|
|
hsv_t hsv;
|
|
|
|
color.red = r; color.green = g; color.blue = b;
|
|
rgb_to_hsv (color, &hsv);
|
|
|
|
if (hsv.saturation < 0.065f)
|
|
{
|
|
/* Greyscale approximation. */
|
|
float luminance = color_luminance (r, g, b);
|
|
if (luminance < 0.090f)
|
|
return 0;
|
|
else if (luminance < 0.241f)
|
|
return 80;
|
|
else if (luminance < 0.331f)
|
|
return 8;
|
|
else if (luminance < 0.406f)
|
|
return 81;
|
|
else if (luminance < 0.498f)
|
|
return 82;
|
|
else if (luminance < 0.585f)
|
|
return 37;
|
|
else if (luminance < 0.680f)
|
|
return 84;
|
|
else if (luminance < 0.764f)
|
|
return 85;
|
|
else if (luminance < 0.810f)
|
|
return 58;
|
|
else if (luminance < 0.857f)
|
|
return 86;
|
|
else if (luminance < 0.902f)
|
|
return 7;
|
|
else if (luminance < 0.953f)
|
|
return 87;
|
|
else
|
|
return 15;
|
|
}
|
|
else
|
|
/* Color approximation. */
|
|
return nearest_color (color, colors_of_xterm88, 88);
|
|
}
|
|
|
|
/* ------------------------ cm_xterm256 color model ------------------------ */
|
|
|
|
/* A non-default color index is in the range 0..255.
|
|
Colors 0..15 are the same as in the cm_xterm16 color model.
|
|
Colors 16..255 are defined in xterm's 256colres.h. */
|
|
|
|
static const rgb_t colors_of_xterm256[256] =
|
|
{
|
|
/* R G B grey index */
|
|
{ 0, 0, 0 }, /* 0.000 0 */
|
|
{ 205, 0, 0 },
|
|
{ 0, 205, 0 },
|
|
{ 205, 205, 0 },
|
|
{ 0, 0, 205 },
|
|
{ 205, 0, 205 },
|
|
{ 0, 205, 205 },
|
|
{ 229, 229, 229 }, /* 0.898 7 */
|
|
{ 77, 77, 77 }, /* 0.302 8 */
|
|
{ 255, 0, 0 },
|
|
{ 0, 255, 0 },
|
|
{ 255, 255, 0 },
|
|
{ 0, 0, 255 },
|
|
{ 255, 0, 255 },
|
|
{ 0, 255, 255 },
|
|
{ 255, 255, 255 }, /* 1.000 15 */
|
|
{ 0, 0, 0 }, /* 0.000 16 */
|
|
{ 0, 0, 42 },
|
|
{ 0, 0, 85 },
|
|
{ 0, 0, 127 },
|
|
{ 0, 0, 170 },
|
|
{ 0, 0, 212 },
|
|
{ 0, 42, 0 },
|
|
{ 0, 42, 42 },
|
|
{ 0, 42, 85 },
|
|
{ 0, 42, 127 },
|
|
{ 0, 42, 170 },
|
|
{ 0, 42, 212 },
|
|
{ 0, 85, 0 },
|
|
{ 0, 85, 42 },
|
|
{ 0, 85, 85 },
|
|
{ 0, 85, 127 },
|
|
{ 0, 85, 170 },
|
|
{ 0, 85, 212 },
|
|
{ 0, 127, 0 },
|
|
{ 0, 127, 42 },
|
|
{ 0, 127, 85 },
|
|
{ 0, 127, 127 },
|
|
{ 0, 127, 170 },
|
|
{ 0, 127, 212 },
|
|
{ 0, 170, 0 },
|
|
{ 0, 170, 42 },
|
|
{ 0, 170, 85 },
|
|
{ 0, 170, 127 },
|
|
{ 0, 170, 170 },
|
|
{ 0, 170, 212 },
|
|
{ 0, 212, 0 },
|
|
{ 0, 212, 42 },
|
|
{ 0, 212, 85 },
|
|
{ 0, 212, 127 },
|
|
{ 0, 212, 170 },
|
|
{ 0, 212, 212 },
|
|
{ 42, 0, 0 },
|
|
{ 42, 0, 42 },
|
|
{ 42, 0, 85 },
|
|
{ 42, 0, 127 },
|
|
{ 42, 0, 170 },
|
|
{ 42, 0, 212 },
|
|
{ 42, 42, 0 },
|
|
{ 42, 42, 42 }, /* 0.165 59 */
|
|
{ 42, 42, 85 },
|
|
{ 42, 42, 127 },
|
|
{ 42, 42, 170 },
|
|
{ 42, 42, 212 },
|
|
{ 42, 85, 0 },
|
|
{ 42, 85, 42 },
|
|
{ 42, 85, 85 },
|
|
{ 42, 85, 127 },
|
|
{ 42, 85, 170 },
|
|
{ 42, 85, 212 },
|
|
{ 42, 127, 0 },
|
|
{ 42, 127, 42 },
|
|
{ 42, 127, 85 },
|
|
{ 42, 127, 127 },
|
|
{ 42, 127, 170 },
|
|
{ 42, 127, 212 },
|
|
{ 42, 170, 0 },
|
|
{ 42, 170, 42 },
|
|
{ 42, 170, 85 },
|
|
{ 42, 170, 127 },
|
|
{ 42, 170, 170 },
|
|
{ 42, 170, 212 },
|
|
{ 42, 212, 0 },
|
|
{ 42, 212, 42 },
|
|
{ 42, 212, 85 },
|
|
{ 42, 212, 127 },
|
|
{ 42, 212, 170 },
|
|
{ 42, 212, 212 },
|
|
{ 85, 0, 0 },
|
|
{ 85, 0, 42 },
|
|
{ 85, 0, 85 },
|
|
{ 85, 0, 127 },
|
|
{ 85, 0, 170 },
|
|
{ 85, 0, 212 },
|
|
{ 85, 42, 0 },
|
|
{ 85, 42, 42 },
|
|
{ 85, 42, 85 },
|
|
{ 85, 42, 127 },
|
|
{ 85, 42, 170 },
|
|
{ 85, 42, 212 },
|
|
{ 85, 85, 0 },
|
|
{ 85, 85, 42 },
|
|
{ 85, 85, 85 }, /* 0.333 102 */
|
|
{ 85, 85, 127 },
|
|
{ 85, 85, 170 },
|
|
{ 85, 85, 212 },
|
|
{ 85, 127, 0 },
|
|
{ 85, 127, 42 },
|
|
{ 85, 127, 85 },
|
|
{ 85, 127, 127 },
|
|
{ 85, 127, 170 },
|
|
{ 85, 127, 212 },
|
|
{ 85, 170, 0 },
|
|
{ 85, 170, 42 },
|
|
{ 85, 170, 85 },
|
|
{ 85, 170, 127 },
|
|
{ 85, 170, 170 },
|
|
{ 85, 170, 212 },
|
|
{ 85, 212, 0 },
|
|
{ 85, 212, 42 },
|
|
{ 85, 212, 85 },
|
|
{ 85, 212, 127 },
|
|
{ 85, 212, 170 },
|
|
{ 85, 212, 212 },
|
|
{ 127, 0, 0 },
|
|
{ 127, 0, 42 },
|
|
{ 127, 0, 85 },
|
|
{ 127, 0, 127 },
|
|
{ 127, 0, 170 },
|
|
{ 127, 0, 212 },
|
|
{ 127, 42, 0 },
|
|
{ 127, 42, 42 },
|
|
{ 127, 42, 85 },
|
|
{ 127, 42, 127 },
|
|
{ 127, 42, 170 },
|
|
{ 127, 42, 212 },
|
|
{ 127, 85, 0 },
|
|
{ 127, 85, 42 },
|
|
{ 127, 85, 85 },
|
|
{ 127, 85, 127 },
|
|
{ 127, 85, 170 },
|
|
{ 127, 85, 212 },
|
|
{ 127, 127, 0 },
|
|
{ 127, 127, 42 },
|
|
{ 127, 127, 85 },
|
|
{ 127, 127, 127 }, /* 0.498 145 */
|
|
{ 127, 127, 170 },
|
|
{ 127, 127, 212 },
|
|
{ 127, 170, 0 },
|
|
{ 127, 170, 42 },
|
|
{ 127, 170, 85 },
|
|
{ 127, 170, 127 },
|
|
{ 127, 170, 170 },
|
|
{ 127, 170, 212 },
|
|
{ 127, 212, 0 },
|
|
{ 127, 212, 42 },
|
|
{ 127, 212, 85 },
|
|
{ 127, 212, 127 },
|
|
{ 127, 212, 170 },
|
|
{ 127, 212, 212 },
|
|
{ 170, 0, 0 },
|
|
{ 170, 0, 42 },
|
|
{ 170, 0, 85 },
|
|
{ 170, 0, 127 },
|
|
{ 170, 0, 170 },
|
|
{ 170, 0, 212 },
|
|
{ 170, 42, 0 },
|
|
{ 170, 42, 42 },
|
|
{ 170, 42, 85 },
|
|
{ 170, 42, 127 },
|
|
{ 170, 42, 170 },
|
|
{ 170, 42, 212 },
|
|
{ 170, 85, 0 },
|
|
{ 170, 85, 42 },
|
|
{ 170, 85, 85 },
|
|
{ 170, 85, 127 },
|
|
{ 170, 85, 170 },
|
|
{ 170, 85, 212 },
|
|
{ 170, 127, 0 },
|
|
{ 170, 127, 42 },
|
|
{ 170, 127, 85 },
|
|
{ 170, 127, 127 },
|
|
{ 170, 127, 170 },
|
|
{ 170, 127, 212 },
|
|
{ 170, 170, 0 },
|
|
{ 170, 170, 42 },
|
|
{ 170, 170, 85 },
|
|
{ 170, 170, 127 },
|
|
{ 170, 170, 170 }, /* 0.667 188 */
|
|
{ 170, 170, 212 },
|
|
{ 170, 212, 0 },
|
|
{ 170, 212, 42 },
|
|
{ 170, 212, 85 },
|
|
{ 170, 212, 127 },
|
|
{ 170, 212, 170 },
|
|
{ 170, 212, 212 },
|
|
{ 212, 0, 0 },
|
|
{ 212, 0, 42 },
|
|
{ 212, 0, 85 },
|
|
{ 212, 0, 127 },
|
|
{ 212, 0, 170 },
|
|
{ 212, 0, 212 },
|
|
{ 212, 42, 0 },
|
|
{ 212, 42, 42 },
|
|
{ 212, 42, 85 },
|
|
{ 212, 42, 127 },
|
|
{ 212, 42, 170 },
|
|
{ 212, 42, 212 },
|
|
{ 212, 85, 0 },
|
|
{ 212, 85, 42 },
|
|
{ 212, 85, 85 },
|
|
{ 212, 85, 127 },
|
|
{ 212, 85, 170 },
|
|
{ 212, 85, 212 },
|
|
{ 212, 127, 0 },
|
|
{ 212, 127, 42 },
|
|
{ 212, 127, 85 },
|
|
{ 212, 127, 127 },
|
|
{ 212, 127, 170 },
|
|
{ 212, 127, 212 },
|
|
{ 212, 170, 0 },
|
|
{ 212, 170, 42 },
|
|
{ 212, 170, 85 },
|
|
{ 212, 170, 127 },
|
|
{ 212, 170, 170 },
|
|
{ 212, 170, 212 },
|
|
{ 212, 212, 0 },
|
|
{ 212, 212, 42 },
|
|
{ 212, 212, 85 },
|
|
{ 212, 212, 127 },
|
|
{ 212, 212, 170 },
|
|
{ 212, 212, 212 }, /* 0.831 231 */
|
|
{ 8, 8, 8 }, /* 0.031 232 */
|
|
{ 18, 18, 18 }, /* 0.071 233 */
|
|
{ 28, 28, 28 }, /* 0.110 234 */
|
|
{ 38, 38, 38 }, /* 0.149 235 */
|
|
{ 48, 48, 48 }, /* 0.188 236 */
|
|
{ 58, 58, 58 }, /* 0.227 237 */
|
|
{ 68, 68, 68 }, /* 0.267 238 */
|
|
{ 78, 78, 78 }, /* 0.306 239 */
|
|
{ 88, 88, 88 }, /* 0.345 240 */
|
|
{ 98, 98, 98 }, /* 0.384 241 */
|
|
{ 108, 108, 108 }, /* 0.424 242 */
|
|
{ 118, 118, 118 }, /* 0.463 243 */
|
|
{ 128, 128, 128 }, /* 0.502 244 */
|
|
{ 138, 138, 138 }, /* 0.541 245 */
|
|
{ 148, 148, 148 }, /* 0.580 246 */
|
|
{ 158, 158, 158 }, /* 0.620 247 */
|
|
{ 168, 168, 168 }, /* 0.659 248 */
|
|
{ 178, 178, 178 }, /* 0.698 249 */
|
|
{ 188, 188, 188 }, /* 0.737 250 */
|
|
{ 198, 198, 198 }, /* 0.776 251 */
|
|
{ 208, 208, 208 }, /* 0.816 252 */
|
|
{ 218, 218, 218 }, /* 0.855 253 */
|
|
{ 228, 228, 228 }, /* 0.894 254 */
|
|
{ 238, 238, 238 } /* 0.933 255 */
|
|
};
|
|
|
|
static inline term_color_t
|
|
rgb_to_color_xterm256 (int r, int g, int b)
|
|
{
|
|
rgb_t color;
|
|
hsv_t hsv;
|
|
|
|
color.red = r; color.green = g; color.blue = b;
|
|
rgb_to_hsv (color, &hsv);
|
|
|
|
if (hsv.saturation < 0.065f)
|
|
{
|
|
/* Greyscale approximation. */
|
|
float luminance = color_luminance (r, g, b);
|
|
if (luminance < 0.015f)
|
|
return 0;
|
|
else if (luminance < 0.051f)
|
|
return 232;
|
|
else if (luminance < 0.090f)
|
|
return 233;
|
|
else if (luminance < 0.129f)
|
|
return 234;
|
|
else if (luminance < 0.157f)
|
|
return 235;
|
|
else if (luminance < 0.177f)
|
|
return 59;
|
|
else if (luminance < 0.207f)
|
|
return 236;
|
|
else if (luminance < 0.247f)
|
|
return 237;
|
|
else if (luminance < 0.284f)
|
|
return 238;
|
|
else if (luminance < 0.304f)
|
|
return 8;
|
|
else if (luminance < 0.319f)
|
|
return 239;
|
|
else if (luminance < 0.339f)
|
|
return 102;
|
|
else if (luminance < 0.364f)
|
|
return 240;
|
|
else if (luminance < 0.404f)
|
|
return 241;
|
|
else if (luminance < 0.443f)
|
|
return 242;
|
|
else if (luminance < 0.480f)
|
|
return 243;
|
|
else if (luminance < 0.500f)
|
|
return 145;
|
|
else if (luminance < 0.521f)
|
|
return 244;
|
|
else if (luminance < 0.560f)
|
|
return 245;
|
|
else if (luminance < 0.600f)
|
|
return 246;
|
|
else if (luminance < 0.639f)
|
|
return 247;
|
|
else if (luminance < 0.663f)
|
|
return 248;
|
|
else if (luminance < 0.682f)
|
|
return 188;
|
|
else if (luminance < 0.717f)
|
|
return 249;
|
|
else if (luminance < 0.756f)
|
|
return 250;
|
|
else if (luminance < 0.796f)
|
|
return 251;
|
|
else if (luminance < 0.823f)
|
|
return 252;
|
|
else if (luminance < 0.843f)
|
|
return 231;
|
|
else if (luminance < 0.874f)
|
|
return 253;
|
|
else if (luminance < 0.896f)
|
|
return 254;
|
|
else if (luminance < 0.915f)
|
|
return 7;
|
|
else if (luminance < 0.966f)
|
|
return 255;
|
|
else
|
|
return 15;
|
|
}
|
|
else
|
|
/* Color approximation. */
|
|
return nearest_color (color, colors_of_xterm256, 256);
|
|
}
|
|
|
|
/* ------------------------ cm_xtermrgb color model ------------------------ */
|
|
|
|
/* We represent a color as an RGB triplet: (r << 16) | (g << 8) | (b << 0),
|
|
where r, g, b are in the range [0..255]. */
|
|
|
|
static inline term_color_t
|
|
rgb_to_color_xtermrgb (int r, int g, int b)
|
|
{
|
|
return (r << 16) | (g << 8) | (b << 0);
|
|
}
|
|
|
|
|
|
/* ============================== hyperlink_t ============================== */
|
|
|
|
/* A hyperlink is a heap-allocated structure that can be assigned to a run
|
|
of characters. */
|
|
typedef struct
|
|
{
|
|
/* URL.
|
|
Should better be <= 2083 bytes long (because of Microsoft Internet
|
|
Explorer). */
|
|
char *ref;
|
|
/* Id.
|
|
Used when the same hyperlink persists across newlines.
|
|
Should better be <= 256 bytes long (because of VTE and iTerm2). */
|
|
char *id;
|
|
/* Same as id, if non-NULL. Or some generated id. */
|
|
char *real_id;
|
|
} hyperlink_t;
|
|
|
|
static inline void
|
|
free_hyperlink (hyperlink_t *hyperlink)
|
|
{
|
|
free (hyperlink->ref);
|
|
free (hyperlink->real_id);
|
|
free (hyperlink);
|
|
}
|
|
|
|
|
|
/* ============================= attributes_t ============================= */
|
|
|
|
/* ANSI C and ISO C99 6.7.2.1.(4) forbid use of bit fields for types other
|
|
than 'int' or 'unsigned int'.
|
|
On the other hand, C++ forbids conversion between enum types and integer
|
|
types without an explicit cast. */
|
|
#ifdef __cplusplus
|
|
# define BITFIELD_TYPE(orig_type,integer_type) orig_type
|
|
#else
|
|
# define BITFIELD_TYPE(orig_type,integer_type) integer_type
|
|
#endif
|
|
|
|
/* Attributes that can be set on a character. */
|
|
typedef struct
|
|
{
|
|
BITFIELD_TYPE(term_color_t, signed int) color : 25;
|
|
BITFIELD_TYPE(term_color_t, signed int) bgcolor : 25;
|
|
BITFIELD_TYPE(term_weight_t, unsigned int) weight : 1;
|
|
BITFIELD_TYPE(term_posture_t, unsigned int) posture : 1;
|
|
BITFIELD_TYPE(term_underline_t, unsigned int) underline : 1;
|
|
/* Hyperlink, or NULL for none. */
|
|
hyperlink_t *hyperlink;
|
|
} attributes_t;
|
|
|
|
/* Compare two sets of attributes for equality. */
|
|
static inline bool
|
|
equal_attributes (attributes_t attr1, attributes_t attr2)
|
|
{
|
|
return (attr1.color == attr2.color
|
|
&& attr1.bgcolor == attr2.bgcolor
|
|
&& attr1.weight == attr2.weight
|
|
&& attr1.posture == attr2.posture
|
|
&& attr1.underline == attr2.underline
|
|
&& attr1.hyperlink == attr2.hyperlink);
|
|
}
|
|
|
|
|
|
/* ============================ EINTR handling ============================ */
|
|
|
|
/* EINTR handling for tcdrain().
|
|
This function can return -1/EINTR even when we don't have any
|
|
signal handlers set up, namely when we get interrupted via SIGSTOP. */
|
|
|
|
#if HAVE_TCDRAIN
|
|
|
|
static inline int
|
|
nonintr_tcdrain (int fd)
|
|
{
|
|
int retval;
|
|
|
|
do
|
|
retval = tcdrain (fd);
|
|
while (retval < 0 && errno == EINTR);
|
|
|
|
return retval;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/* ============================ term_ostream_t ============================ */
|
|
|
|
#line 1039 "term-ostream.c"
|
|
#include "term_ostream.priv.h"
|
|
|
|
const typeinfo_t term_ostream_typeinfo = { "term_ostream" };
|
|
|
|
static const typeinfo_t * const term_ostream_superclasses[] =
|
|
{ term_ostream_SUPERCLASSES };
|
|
|
|
#define super ostream_vtable
|
|
|
|
#line 1109 "term-ostream.oo.c"
|
|
|
|
static struct term_style_control_data *
|
|
get_control_data (term_ostream_t stream)
|
|
{
|
|
return &stream->control_data;
|
|
}
|
|
|
|
/* Simplify attributes, according to the terminal's capabilities. */
|
|
static attributes_t
|
|
simplify_attributes (term_ostream_t stream, attributes_t attr)
|
|
{
|
|
if ((attr.color != COLOR_DEFAULT || attr.bgcolor != COLOR_DEFAULT)
|
|
&& stream->no_color_video > 0)
|
|
{
|
|
/* When colors and attributes can not be represented simultaneously,
|
|
we give preference to the color. */
|
|
if (stream->no_color_video & 2)
|
|
/* Colors conflict with underlining. */
|
|
attr.underline = UNDERLINE_OFF;
|
|
if (stream->no_color_video & 32)
|
|
/* Colors conflict with bold weight. */
|
|
attr.weight = WEIGHT_NORMAL;
|
|
}
|
|
if (!stream->supports_foreground)
|
|
attr.color = COLOR_DEFAULT;
|
|
if (!stream->supports_background)
|
|
attr.bgcolor = COLOR_DEFAULT;
|
|
if (!stream->supports_weight)
|
|
attr.weight = WEIGHT_DEFAULT;
|
|
if (!stream->supports_posture)
|
|
attr.posture = POSTURE_DEFAULT;
|
|
if (!stream->supports_underline)
|
|
attr.underline = UNDERLINE_DEFAULT;
|
|
if (!stream->supports_hyperlink)
|
|
attr.hyperlink = NULL;
|
|
return attr;
|
|
}
|
|
|
|
/* Generate an id for a hyperlink. */
|
|
static char *
|
|
generate_hyperlink_id (term_ostream_t stream)
|
|
{
|
|
/* A UUID would be optimal, but is overkill here. An id of 128 bits
|
|
(32 hexadecimal digits) should be sufficient. */
|
|
static const char hexdigits[16] =
|
|
{
|
|
'0', '1', '2', '3', '4', '5', '6', '7',
|
|
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
|
|
};
|
|
char *id = (char *) xmalloc (128 / 4 + 1);
|
|
uint32_t words[4] =
|
|
{
|
|
stream->hostname_hash,
|
|
(uint32_t) (stream->start_time >> 32),
|
|
(uint32_t) stream->start_time,
|
|
stream->id_serial
|
|
};
|
|
char *p = id;
|
|
unsigned int i;
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
uint32_t word = words[i];
|
|
unsigned int j;
|
|
for (j = 0; j < 32 / 4; j++)
|
|
*p++ = hexdigits[(word >> (32 - 4 * (j + 1))) & 0x0f];
|
|
}
|
|
*p = '\0';
|
|
stream->id_serial++;
|
|
return id;
|
|
}
|
|
|
|
/* Stream that contains information about how the various out_* functions shall
|
|
do output. */
|
|
static term_ostream_t volatile out_stream;
|
|
|
|
/* File descriptor to which out_char and out_char_unchecked shall output escape
|
|
sequences.
|
|
Same as (out_stream != NULL ? out_stream->fd : -1). */
|
|
static int volatile out_fd = -1;
|
|
|
|
/* Signal error after full_write failed. */
|
|
static void
|
|
out_error (void)
|
|
{
|
|
error (EXIT_FAILURE, errno, _("error writing to %s"), out_stream->filename);
|
|
}
|
|
|
|
/* Output a single char to out_fd. */
|
|
static int
|
|
out_char (int c)
|
|
{
|
|
char bytes[1];
|
|
|
|
bytes[0] = (char)c;
|
|
/* We have to write directly to the file descriptor, not to a buffer with
|
|
the same destination, because of the padding and sleeping that tputs()
|
|
does. */
|
|
if (full_write (out_fd, bytes, 1) < 1)
|
|
out_error ();
|
|
return 0;
|
|
}
|
|
|
|
/* Output a single char to out_fd. Ignore errors. */
|
|
static _GL_ASYNC_SAFE int
|
|
out_char_unchecked (int c)
|
|
{
|
|
char bytes[1];
|
|
|
|
bytes[0] = (char)c;
|
|
full_write (out_fd, bytes, 1);
|
|
return 0;
|
|
}
|
|
|
|
/* Output escape sequences to switch the foreground color to NEW_COLOR. */
|
|
static _GL_ASYNC_SAFE void
|
|
out_color_change (term_ostream_t stream, term_color_t new_color,
|
|
bool async_safe)
|
|
{
|
|
assert (stream->supports_foreground);
|
|
assert (new_color != COLOR_DEFAULT);
|
|
switch (stream->colormodel)
|
|
{
|
|
case cm_common8:
|
|
assert (new_color >= 0 && new_color < 8);
|
|
#if HAVE_WINDOWS_CONSOLES
|
|
if (stream->is_windows_console)
|
|
{
|
|
/* SetConsoleTextAttribute
|
|
<https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute>
|
|
<https://docs.microsoft.com/en-us/windows/console/console-screen-buffers> */
|
|
/* Assign to stream->current_console_attributes *before* calling
|
|
SetConsoleTextAttribute, otherwise async_set_attributes_from_default
|
|
will not do its job correctly. */
|
|
stream->current_console_attributes =
|
|
(stream->current_console_attributes & ~(7 << 0))
|
|
| (new_color << 0);
|
|
SetConsoleTextAttribute (stream->handle, stream->current_console_attributes);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (stream->set_a_foreground != NULL)
|
|
tputs (tparm (stream->set_a_foreground, color_bgr (new_color)),
|
|
1, async_safe ? out_char_unchecked : out_char);
|
|
else
|
|
tputs (tparm (stream->set_foreground, new_color),
|
|
1, async_safe ? out_char_unchecked : out_char);
|
|
}
|
|
break;
|
|
/* When we are dealing with an xterm, there is no need to go through
|
|
tputs() because we know there is no padding and sleeping. */
|
|
case cm_xterm8:
|
|
assert (new_color >= 0 && new_color < 8);
|
|
{
|
|
char bytes[5];
|
|
bytes[0] = 0x1B; bytes[1] = '[';
|
|
bytes[2] = '3'; bytes[3] = '0' + new_color;
|
|
bytes[4] = 'm';
|
|
if (full_write (out_fd, bytes, 5) < 5)
|
|
if (!async_safe)
|
|
out_error ();
|
|
}
|
|
break;
|
|
case cm_xterm16:
|
|
assert (new_color >= 0 && new_color < 16);
|
|
{
|
|
char bytes[5];
|
|
bytes[0] = 0x1B; bytes[1] = '[';
|
|
if (new_color < 8)
|
|
{
|
|
bytes[2] = '3'; bytes[3] = '0' + new_color;
|
|
}
|
|
else
|
|
{
|
|
bytes[2] = '9'; bytes[3] = '0' + (new_color - 8);
|
|
}
|
|
bytes[4] = 'm';
|
|
if (full_write (out_fd, bytes, 5) < 5)
|
|
if (!async_safe)
|
|
out_error ();
|
|
}
|
|
break;
|
|
case cm_xterm88:
|
|
assert (new_color >= 0 && new_color < 88);
|
|
{
|
|
char bytes[10];
|
|
char *p;
|
|
bytes[0] = 0x1B; bytes[1] = '[';
|
|
bytes[2] = '3'; bytes[3] = '8'; bytes[4] = ';';
|
|
bytes[5] = '5'; bytes[6] = ';';
|
|
p = bytes + 7;
|
|
if (new_color >= 10)
|
|
*p++ = '0' + (new_color / 10);
|
|
*p++ = '0' + (new_color % 10);
|
|
*p++ = 'm';
|
|
if (full_write (out_fd, bytes, p - bytes) < p - bytes)
|
|
if (!async_safe)
|
|
out_error ();
|
|
}
|
|
break;
|
|
case cm_xterm256:
|
|
assert (new_color >= 0 && new_color < 256);
|
|
{
|
|
char bytes[11];
|
|
char *p;
|
|
bytes[0] = 0x1B; bytes[1] = '[';
|
|
bytes[2] = '3'; bytes[3] = '8'; bytes[4] = ';';
|
|
bytes[5] = '5'; bytes[6] = ';';
|
|
p = bytes + 7;
|
|
if (new_color >= 100)
|
|
*p++ = '0' + (new_color / 100);
|
|
if (new_color >= 10)
|
|
*p++ = '0' + ((new_color % 100) / 10);
|
|
*p++ = '0' + (new_color % 10);
|
|
*p++ = 'm';
|
|
if (full_write (out_fd, bytes, p - bytes) < p - bytes)
|
|
if (!async_safe)
|
|
out_error ();
|
|
}
|
|
break;
|
|
case cm_xtermrgb:
|
|
assert (new_color >= 0 && new_color < 0x1000000);
|
|
{
|
|
char bytes[19];
|
|
char *p;
|
|
unsigned int r = (new_color >> 16) & 0xff;
|
|
unsigned int g = (new_color >> 8) & 0xff;
|
|
unsigned int b = new_color & 0xff;
|
|
bytes[0] = 0x1B; bytes[1] = '[';
|
|
bytes[2] = '3'; bytes[3] = '8'; bytes[4] = ';';
|
|
bytes[5] = '2'; bytes[6] = ';';
|
|
p = bytes + 7;
|
|
if (r >= 100)
|
|
*p++ = '0' + (r / 100);
|
|
if (r >= 10)
|
|
*p++ = '0' + ((r % 100) / 10);
|
|
*p++ = '0' + (r % 10);
|
|
*p++ = ';';
|
|
if (g >= 100)
|
|
*p++ = '0' + (g / 100);
|
|
if (g >= 10)
|
|
*p++ = '0' + ((g % 100) / 10);
|
|
*p++ = '0' + (g % 10);
|
|
*p++ = ';';
|
|
if (b >= 100)
|
|
*p++ = '0' + (b / 100);
|
|
if (b >= 10)
|
|
*p++ = '0' + ((b % 100) / 10);
|
|
*p++ = '0' + (b % 10);
|
|
*p++ = 'm';
|
|
if (full_write (out_fd, bytes, p - bytes) < p - bytes)
|
|
if (!async_safe)
|
|
out_error ();
|
|
}
|
|
break;
|
|
default:
|
|
abort ();
|
|
}
|
|
}
|
|
|
|
/* Output escape sequences to switch the background color to NEW_BGCOLOR. */
|
|
static _GL_ASYNC_SAFE void
|
|
out_bgcolor_change (term_ostream_t stream, term_color_t new_bgcolor,
|
|
bool async_safe)
|
|
{
|
|
assert (stream->supports_background);
|
|
assert (new_bgcolor != COLOR_DEFAULT);
|
|
switch (stream->colormodel)
|
|
{
|
|
case cm_common8:
|
|
assert (new_bgcolor >= 0 && new_bgcolor < 8);
|
|
#if HAVE_WINDOWS_CONSOLES
|
|
if (stream->is_windows_console)
|
|
{
|
|
/* SetConsoleTextAttribute
|
|
<https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute>
|
|
<https://docs.microsoft.com/en-us/windows/console/console-screen-buffers> */
|
|
/* Assign to stream->current_console_attributes *before* calling
|
|
SetConsoleTextAttribute, otherwise async_set_attributes_from_default
|
|
will not do its job correctly. */
|
|
stream->current_console_attributes =
|
|
(stream->current_console_attributes & ~(7 << 4))
|
|
| (new_bgcolor << 4);
|
|
SetConsoleTextAttribute (stream->handle, stream->current_console_attributes);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (stream->set_a_background != NULL)
|
|
tputs (tparm (stream->set_a_background, color_bgr (new_bgcolor)),
|
|
1, async_safe ? out_char_unchecked : out_char);
|
|
else
|
|
tputs (tparm (stream->set_background, new_bgcolor),
|
|
1, async_safe ? out_char_unchecked : out_char);
|
|
}
|
|
break;
|
|
/* When we are dealing with an xterm, there is no need to go through
|
|
tputs() because we know there is no padding and sleeping. */
|
|
case cm_xterm8:
|
|
assert (new_bgcolor >= 0 && new_bgcolor < 8);
|
|
{
|
|
char bytes[5];
|
|
bytes[0] = 0x1B; bytes[1] = '[';
|
|
bytes[2] = '4'; bytes[3] = '0' + new_bgcolor;
|
|
bytes[4] = 'm';
|
|
if (full_write (out_fd, bytes, 5) < 5)
|
|
if (!async_safe)
|
|
out_error ();
|
|
}
|
|
break;
|
|
case cm_xterm16:
|
|
assert (new_bgcolor >= 0 && new_bgcolor < 16);
|
|
{
|
|
char bytes[6];
|
|
bytes[0] = 0x1B; bytes[1] = '[';
|
|
if (new_bgcolor < 8)
|
|
{
|
|
bytes[2] = '4'; bytes[3] = '0' + new_bgcolor;
|
|
bytes[4] = 'm';
|
|
if (full_write (out_fd, bytes, 5) < 5)
|
|
if (!async_safe)
|
|
out_error ();
|
|
}
|
|
else
|
|
{
|
|
bytes[2] = '1'; bytes[3] = '0';
|
|
bytes[4] = '0' + (new_bgcolor - 8); bytes[5] = 'm';
|
|
if (full_write (out_fd, bytes, 6) < 6)
|
|
if (!async_safe)
|
|
out_error ();
|
|
}
|
|
}
|
|
break;
|
|
case cm_xterm88:
|
|
assert (new_bgcolor >= 0 && new_bgcolor < 88);
|
|
{
|
|
char bytes[10];
|
|
char *p;
|
|
bytes[0] = 0x1B; bytes[1] = '[';
|
|
bytes[2] = '4'; bytes[3] = '8'; bytes[4] = ';';
|
|
bytes[5] = '5'; bytes[6] = ';';
|
|
p = bytes + 7;
|
|
if (new_bgcolor >= 10)
|
|
*p++ = '0' + (new_bgcolor / 10);
|
|
*p++ = '0' + (new_bgcolor % 10);
|
|
*p++ = 'm';
|
|
if (full_write (out_fd, bytes, p - bytes) < p - bytes)
|
|
if (!async_safe)
|
|
out_error ();
|
|
}
|
|
break;
|
|
case cm_xterm256:
|
|
assert (new_bgcolor >= 0 && new_bgcolor < 256);
|
|
{
|
|
char bytes[11];
|
|
char *p;
|
|
bytes[0] = 0x1B; bytes[1] = '[';
|
|
bytes[2] = '4'; bytes[3] = '8'; bytes[4] = ';';
|
|
bytes[5] = '5'; bytes[6] = ';';
|
|
p = bytes + 7;
|
|
if (new_bgcolor >= 100)
|
|
*p++ = '0' + (new_bgcolor / 100);
|
|
if (new_bgcolor >= 10)
|
|
*p++ = '0' + ((new_bgcolor % 100) / 10);
|
|
*p++ = '0' + (new_bgcolor % 10);
|
|
*p++ = 'm';
|
|
if (full_write (out_fd, bytes, p - bytes) < p - bytes)
|
|
if (!async_safe)
|
|
out_error ();
|
|
}
|
|
break;
|
|
case cm_xtermrgb:
|
|
assert (new_bgcolor >= 0 && new_bgcolor < 0x1000000);
|
|
{
|
|
char bytes[19];
|
|
char *p;
|
|
unsigned int r = (new_bgcolor >> 16) & 0xff;
|
|
unsigned int g = (new_bgcolor >> 8) & 0xff;
|
|
unsigned int b = new_bgcolor & 0xff;
|
|
bytes[0] = 0x1B; bytes[1] = '[';
|
|
bytes[2] = '4'; bytes[3] = '8'; bytes[4] = ';';
|
|
bytes[5] = '2'; bytes[6] = ';';
|
|
p = bytes + 7;
|
|
if (r >= 100)
|
|
*p++ = '0' + (r / 100);
|
|
if (r >= 10)
|
|
*p++ = '0' + ((r % 100) / 10);
|
|
*p++ = '0' + (r % 10);
|
|
*p++ = ';';
|
|
if (g >= 100)
|
|
*p++ = '0' + (g / 100);
|
|
if (g >= 10)
|
|
*p++ = '0' + ((g % 100) / 10);
|
|
*p++ = '0' + (g % 10);
|
|
*p++ = ';';
|
|
if (b >= 100)
|
|
*p++ = '0' + (b / 100);
|
|
if (b >= 10)
|
|
*p++ = '0' + ((b % 100) / 10);
|
|
*p++ = '0' + (b % 10);
|
|
*p++ = 'm';
|
|
if (full_write (out_fd, bytes, p - bytes) < p - bytes)
|
|
if (!async_safe)
|
|
out_error ();
|
|
}
|
|
break;
|
|
default:
|
|
abort ();
|
|
}
|
|
}
|
|
|
|
/* Output escape sequences to switch the weight to NEW_WEIGHT. */
|
|
static _GL_ASYNC_SAFE void
|
|
out_weight_change (term_ostream_t stream, term_weight_t new_weight,
|
|
bool async_safe)
|
|
{
|
|
assert (stream->supports_weight);
|
|
assert (new_weight != WEIGHT_DEFAULT);
|
|
/* This implies: */
|
|
assert (new_weight == WEIGHT_BOLD);
|
|
tputs (stream->enter_bold_mode,
|
|
1, async_safe ? out_char_unchecked : out_char);
|
|
}
|
|
|
|
/* Output escape sequences to switch the posture to NEW_POSTURE. */
|
|
static _GL_ASYNC_SAFE void
|
|
out_posture_change (term_ostream_t stream, term_posture_t new_posture,
|
|
bool async_safe)
|
|
{
|
|
assert (stream->supports_posture);
|
|
assert (new_posture != POSTURE_DEFAULT);
|
|
/* This implies: */
|
|
assert (new_posture == POSTURE_ITALIC);
|
|
tputs (stream->enter_italics_mode,
|
|
1, async_safe ? out_char_unchecked : out_char);
|
|
}
|
|
|
|
/* Output escape sequences to switch the underline to NEW_UNDERLINE. */
|
|
static _GL_ASYNC_SAFE void
|
|
out_underline_change (term_ostream_t stream, term_underline_t new_underline,
|
|
bool async_safe)
|
|
{
|
|
assert (stream->supports_underline);
|
|
assert (new_underline != UNDERLINE_DEFAULT);
|
|
/* This implies: */
|
|
assert (new_underline == UNDERLINE_ON);
|
|
#if HAVE_WINDOWS_CONSOLES
|
|
if (stream->is_windows_console)
|
|
{
|
|
/* SetConsoleTextAttribute
|
|
<https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute>
|
|
<https://docs.microsoft.com/en-us/windows/console/console-screen-buffers> */
|
|
/* Assign to stream->current_console_attributes *before* calling
|
|
SetConsoleTextAttribute, otherwise async_set_attributes_from_default
|
|
will not do its job correctly. */
|
|
stream->current_console_attributes =
|
|
stream->current_console_attributes | COMMON_LVB_UNDERSCORE;
|
|
SetConsoleTextAttribute (stream->handle, stream->current_console_attributes);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
tputs (stream->enter_underline_mode,
|
|
1, async_safe ? out_char_unchecked : out_char);
|
|
}
|
|
}
|
|
|
|
/* Output escape seqeuences to switch the hyperlink to NEW_HYPERLINK. */
|
|
static _GL_ASYNC_SAFE void
|
|
out_hyperlink_change (term_ostream_t stream, hyperlink_t *new_hyperlink,
|
|
bool async_safe)
|
|
{
|
|
int (*out_ch) (int) = (async_safe ? out_char_unchecked : out_char);
|
|
assert (stream->supports_hyperlink);
|
|
if (new_hyperlink != NULL)
|
|
{
|
|
assert (new_hyperlink->real_id != NULL);
|
|
tputs ("\033]8;id=", 1, out_ch);
|
|
tputs (new_hyperlink->real_id, 1, out_ch);
|
|
tputs (";", 1, out_ch);
|
|
tputs (new_hyperlink->ref, 1, out_ch);
|
|
tputs ("\033\\", 1, out_ch);
|
|
}
|
|
else
|
|
tputs ("\033]8;;\033\\", 1, out_ch);
|
|
}
|
|
|
|
/* Output escape sequences to switch from STREAM->ACTIVE_ATTR to NEW_ATTR,
|
|
and update STREAM->ACTIVE_ATTR. */
|
|
static void
|
|
out_attr_change (term_ostream_t stream, attributes_t new_attr)
|
|
{
|
|
attributes_t old_attr = stream->active_attr;
|
|
|
|
/* Keep track of the active attributes. Do this *before* emitting the
|
|
escape sequences, otherwise async_set_attributes_from_default will not
|
|
do its job correctly. */
|
|
stream->active_attr = new_attr;
|
|
stream->active_attr_color = new_attr.color;
|
|
stream->active_attr_bgcolor = new_attr.bgcolor;
|
|
stream->active_attr_hyperlink = new_attr.hyperlink;
|
|
|
|
#if HAVE_WINDOWS_CONSOLES
|
|
if (stream->is_windows_console)
|
|
{
|
|
/* SetConsoleTextAttribute
|
|
<https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute>
|
|
<https://docs.microsoft.com/en-us/windows/console/console-screen-buffers> */
|
|
/* Assign to stream->current_console_attributes *before* calling
|
|
SetConsoleTextAttribute, otherwise async_set_attributes_from_default
|
|
will not do its job correctly. */
|
|
stream->current_console_attributes =
|
|
(stream->current_console_attributes
|
|
& ~((7 << 0) | (7 << 4) | COMMON_LVB_UNDERSCORE))
|
|
| (new_attr.color == COLOR_DEFAULT
|
|
? stream->default_console_attributes & (7 << 0)
|
|
: (new_attr.color << 0))
|
|
| (new_attr.bgcolor == COLOR_DEFAULT
|
|
? stream->default_console_attributes & (7 << 4)
|
|
: (new_attr.bgcolor << 4))
|
|
| (new_attr.underline ? COMMON_LVB_UNDERSCORE : 0);
|
|
SetConsoleTextAttribute (stream->handle, stream->current_console_attributes);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
bool cleared_attributes;
|
|
|
|
/* For out_char to work. */
|
|
out_stream = stream;
|
|
out_fd = stream->fd;
|
|
|
|
/* We don't know the default colors of the terminal. The only way to
|
|
switch back to a default color is to use stream->orig_pair. */
|
|
if ((new_attr.color == COLOR_DEFAULT && old_attr.color != COLOR_DEFAULT)
|
|
|| (new_attr.bgcolor == COLOR_DEFAULT && old_attr.bgcolor != COLOR_DEFAULT))
|
|
{
|
|
assert (stream->supports_foreground || stream->supports_background);
|
|
tputs (stream->orig_pair, 1, out_char);
|
|
old_attr.color = COLOR_DEFAULT;
|
|
old_attr.bgcolor = COLOR_DEFAULT;
|
|
}
|
|
|
|
/* To turn off WEIGHT_BOLD, the only way is to output the
|
|
exit_attribute_mode sequence. (With xterm, you can also do it with
|
|
"Esc [ 0 m", but this escape sequence is not contained in the terminfo
|
|
description.) It may also clear the colors; this is the case e.g. when
|
|
TERM="xterm" or TERM="ansi".
|
|
To turn off UNDERLINE_ON, we can use the exit_underline_mode or the
|
|
exit_attribute_mode sequence. In the latter case, it will not only
|
|
turn off UNDERLINE_ON, but also the other attributes, and possibly also
|
|
the colors.
|
|
To turn off POSTURE_ITALIC, we can use the exit_italics_mode or the
|
|
exit_attribute_mode sequence. Again, in the latter case, it will not
|
|
only turn off POSTURE_ITALIC, but also the other attributes, and
|
|
possibly also the colors.
|
|
There is no point in setting an attribute just before emitting an
|
|
escape sequence that may again turn off the attribute. Therefore we
|
|
proceed in two steps: First, clear the attributes that need to be
|
|
cleared; then - taking into account that this may have cleared all
|
|
attributes and all colors - set the colors and the attributes.
|
|
The variable 'cleared_attributes' tells whether an escape sequence
|
|
has been output that may have cleared all attributes and all color
|
|
settings. */
|
|
cleared_attributes = false;
|
|
if (old_attr.posture != POSTURE_NORMAL
|
|
&& new_attr.posture == POSTURE_NORMAL
|
|
&& stream->exit_italics_mode != NULL)
|
|
{
|
|
tputs (stream->exit_italics_mode, 1, out_char);
|
|
old_attr.posture = POSTURE_NORMAL;
|
|
cleared_attributes = true;
|
|
}
|
|
if (old_attr.underline != UNDERLINE_OFF
|
|
&& new_attr.underline == UNDERLINE_OFF
|
|
&& stream->exit_underline_mode != NULL)
|
|
{
|
|
tputs (stream->exit_underline_mode, 1, out_char);
|
|
old_attr.underline = UNDERLINE_OFF;
|
|
cleared_attributes = true;
|
|
}
|
|
if ((old_attr.weight != WEIGHT_NORMAL
|
|
&& new_attr.weight == WEIGHT_NORMAL)
|
|
|| (old_attr.posture != POSTURE_NORMAL
|
|
&& new_attr.posture == POSTURE_NORMAL
|
|
/* implies stream->exit_italics_mode == NULL */)
|
|
|| (old_attr.underline != UNDERLINE_OFF
|
|
&& new_attr.underline == UNDERLINE_OFF
|
|
/* implies stream->exit_underline_mode == NULL */))
|
|
{
|
|
tputs (stream->exit_attribute_mode, 1, out_char);
|
|
/* We don't know exactly what effects exit_attribute_mode has, but
|
|
this is the minimum effect: */
|
|
old_attr.weight = WEIGHT_NORMAL;
|
|
if (stream->exit_italics_mode == NULL)
|
|
old_attr.posture = POSTURE_NORMAL;
|
|
if (stream->exit_underline_mode == NULL)
|
|
old_attr.underline = UNDERLINE_OFF;
|
|
cleared_attributes = true;
|
|
}
|
|
|
|
/* Turn on the colors. */
|
|
if (new_attr.color != old_attr.color
|
|
|| (cleared_attributes && new_attr.color != COLOR_DEFAULT))
|
|
{
|
|
out_color_change (stream, new_attr.color, false);
|
|
}
|
|
if (new_attr.bgcolor != old_attr.bgcolor
|
|
|| (cleared_attributes && new_attr.bgcolor != COLOR_DEFAULT))
|
|
{
|
|
out_bgcolor_change (stream, new_attr.bgcolor, false);
|
|
}
|
|
if (new_attr.weight != old_attr.weight
|
|
|| (cleared_attributes && new_attr.weight != WEIGHT_DEFAULT))
|
|
{
|
|
out_weight_change (stream, new_attr.weight, false);
|
|
}
|
|
if (new_attr.posture != old_attr.posture
|
|
|| (cleared_attributes && new_attr.posture != POSTURE_DEFAULT))
|
|
{
|
|
out_posture_change (stream, new_attr.posture, false);
|
|
}
|
|
if (new_attr.underline != old_attr.underline
|
|
|| (cleared_attributes && new_attr.underline != UNDERLINE_DEFAULT))
|
|
{
|
|
out_underline_change (stream, new_attr.underline, false);
|
|
}
|
|
if (new_attr.hyperlink != old_attr.hyperlink)
|
|
{
|
|
out_hyperlink_change (stream, new_attr.hyperlink, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
restore (term_ostream_t stream)
|
|
{
|
|
#if HAVE_WINDOWS_CONSOLES
|
|
if (stream->is_windows_console)
|
|
{
|
|
/* SetConsoleTextAttribute
|
|
<https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute>
|
|
<https://docs.microsoft.com/en-us/windows/console/console-screen-buffers> */
|
|
SetConsoleTextAttribute (stream->handle, stream->default_console_attributes);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* For out_char_unchecked to work. */
|
|
out_stream = stream;
|
|
out_fd = stream->fd;
|
|
|
|
if (stream->restore_colors != NULL)
|
|
tputs (stream->restore_colors, 1, out_char_unchecked);
|
|
if (stream->restore_weight != NULL)
|
|
tputs (stream->restore_weight, 1, out_char_unchecked);
|
|
if (stream->restore_posture != NULL)
|
|
tputs (stream->restore_posture, 1, out_char_unchecked);
|
|
if (stream->restore_underline != NULL)
|
|
tputs (stream->restore_underline, 1, out_char_unchecked);
|
|
if (stream->restore_hyperlink != NULL)
|
|
tputs (stream->restore_hyperlink, 1, out_char_unchecked);
|
|
}
|
|
}
|
|
|
|
static _GL_ASYNC_SAFE void
|
|
async_restore (term_ostream_t stream)
|
|
{
|
|
#if HAVE_WINDOWS_CONSOLES
|
|
if (stream->is_windows_console)
|
|
{
|
|
/* SetConsoleTextAttribute
|
|
<https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute>
|
|
<https://docs.microsoft.com/en-us/windows/console/console-screen-buffers> */
|
|
SetConsoleTextAttribute (stream->handle, stream->default_console_attributes);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* For out_char_unchecked to work. */
|
|
out_stream = stream;
|
|
out_fd = stream->fd;
|
|
|
|
if (stream->restore_colors != NULL)
|
|
tputs (stream->restore_colors, 1, out_char_unchecked);
|
|
if (stream->restore_weight != NULL)
|
|
tputs (stream->restore_weight, 1, out_char_unchecked);
|
|
if (stream->restore_posture != NULL)
|
|
tputs (stream->restore_posture, 1, out_char_unchecked);
|
|
if (stream->restore_underline != NULL)
|
|
tputs (stream->restore_underline, 1, out_char_unchecked);
|
|
if (stream->restore_hyperlink != NULL)
|
|
tputs (stream->restore_hyperlink, 1, out_char_unchecked);
|
|
}
|
|
}
|
|
|
|
static _GL_ASYNC_SAFE void
|
|
async_set_attributes_from_default (term_ostream_t stream)
|
|
{
|
|
#if HAVE_WINDOWS_CONSOLES
|
|
if (stream->is_windows_console)
|
|
{
|
|
/* SetConsoleTextAttribute
|
|
<https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute>
|
|
<https://docs.microsoft.com/en-us/windows/console/console-screen-buffers> */
|
|
SetConsoleTextAttribute (stream->handle, stream->current_console_attributes);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
attributes_t new_attr = stream->active_attr;
|
|
/* Since stream->active_attr is not guaranteed to be loaded atomically,
|
|
new_attr.color and new_attr.bgcolor may have invalid values.
|
|
Use the atomically loadable values instead. */
|
|
new_attr.color = stream->active_attr_color;
|
|
new_attr.bgcolor = stream->active_attr_bgcolor;
|
|
new_attr.hyperlink = stream->active_attr_hyperlink;
|
|
|
|
/* For out_char_unchecked to work. */
|
|
out_stream = stream;
|
|
out_fd = stream->fd;
|
|
|
|
if (new_attr.color != COLOR_DEFAULT)
|
|
out_color_change (stream, new_attr.color, true);
|
|
if (new_attr.bgcolor != COLOR_DEFAULT)
|
|
out_bgcolor_change (stream, new_attr.bgcolor, true);
|
|
if (new_attr.weight != WEIGHT_DEFAULT)
|
|
out_weight_change (stream, new_attr.weight, true);
|
|
if (new_attr.posture != POSTURE_DEFAULT)
|
|
out_posture_change (stream, new_attr.posture, true);
|
|
if (new_attr.underline != UNDERLINE_DEFAULT)
|
|
out_underline_change (stream, new_attr.underline, true);
|
|
if (new_attr.hyperlink != NULL)
|
|
out_hyperlink_change (stream, new_attr.hyperlink, true);
|
|
}
|
|
}
|
|
|
|
static const struct term_style_controller controller =
|
|
{
|
|
get_control_data,
|
|
restore,
|
|
async_restore,
|
|
async_set_attributes_from_default
|
|
};
|
|
|
|
/* Activate the default attributes. */
|
|
static void
|
|
activate_default_attr (term_ostream_t stream)
|
|
{
|
|
/* Switch back to the default attributes. */
|
|
out_attr_change (stream, stream->default_attr);
|
|
|
|
deactivate_term_non_default_mode (&controller, stream);
|
|
}
|
|
|
|
/* Output the buffered line atomically.
|
|
The terminal is left in the the state (regarding colors and attributes)
|
|
represented by the simplified attributes goal_attr. */
|
|
static void
|
|
output_buffer (term_ostream_t stream, attributes_t goal_attr)
|
|
{
|
|
const char *cp;
|
|
const attributes_t *ap;
|
|
size_t len;
|
|
size_t n;
|
|
|
|
cp = stream->buffer;
|
|
ap = stream->attrbuffer;
|
|
len = stream->buflen;
|
|
|
|
/* See how much we can output without blocking signals. */
|
|
for (n = 0; n < len && equal_attributes (ap[n], stream->active_attr); n++)
|
|
;
|
|
if (n > 0)
|
|
{
|
|
if (full_write (stream->fd, cp, n) < n)
|
|
{
|
|
int error_code = errno;
|
|
/* Do output to stderr only after we have switched back to the
|
|
default attributes. Otherwise this output may come out with
|
|
the wrong text attributes. */
|
|
if (!equal_attributes (stream->active_attr, stream->default_attr))
|
|
activate_default_attr (stream);
|
|
error (EXIT_FAILURE, error_code, _("error writing to %s"),
|
|
stream->filename);
|
|
}
|
|
cp += n;
|
|
ap += n;
|
|
len -= n;
|
|
}
|
|
if (len > 0)
|
|
{
|
|
if (!equal_attributes (*ap, stream->default_attr))
|
|
activate_term_non_default_mode (&controller, stream);
|
|
|
|
do
|
|
{
|
|
/* Activate the attributes in *ap. */
|
|
out_attr_change (stream, *ap);
|
|
/* See how many characters we can output without further attribute
|
|
changes. */
|
|
for (n = 1; n < len && equal_attributes (ap[n], stream->active_attr); n++)
|
|
;
|
|
if (full_write (stream->fd, cp, n) < n)
|
|
{
|
|
int error_code = errno;
|
|
/* Do output to stderr only after we have switched back to the
|
|
default attributes. Otherwise this output may come out with
|
|
the wrong text attributes. */
|
|
if (!equal_attributes (stream->active_attr, stream->default_attr))
|
|
activate_default_attr (stream);
|
|
error (EXIT_FAILURE, error_code, _("error writing to %s"),
|
|
stream->filename);
|
|
}
|
|
cp += n;
|
|
ap += n;
|
|
len -= n;
|
|
}
|
|
while (len > 0);
|
|
}
|
|
stream->buflen = 0;
|
|
|
|
/* Before changing to goal_attr, we may need to enable the non-default
|
|
attributes mode. */
|
|
if (!equal_attributes (goal_attr, stream->default_attr))
|
|
activate_term_non_default_mode (&controller, stream);
|
|
/* Change to goal_attr. */
|
|
if (!equal_attributes (goal_attr, stream->active_attr))
|
|
out_attr_change (stream, goal_attr);
|
|
/* When we can deactivate the non-default attributes mode, do so. */
|
|
if (equal_attributes (goal_attr, stream->default_attr))
|
|
deactivate_term_non_default_mode (&controller, stream);
|
|
|
|
/* Free the hyperlink_t objects that are no longer referenced by the
|
|
stream->attrbuffer. */
|
|
{
|
|
size_t count = stream->hyperlinks_count;
|
|
size_t j = 0;
|
|
size_t i;
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
/* Here 0 <= j <= i. */
|
|
hyperlink_t *hyperlink = stream->hyperlinks_array[i];
|
|
/* stream->default_attr.hyperlink is always == NULL.
|
|
stream->simp_attr.hyperlink is either == NULL
|
|
or == stream->curr_attr.hyperlink.
|
|
We can therefore ignore both. */
|
|
if (hyperlink == stream->curr_attr.hyperlink
|
|
|| hyperlink == stream->active_attr.hyperlink)
|
|
{
|
|
/* The hyperlink is still in use. */
|
|
stream->hyperlinks_array[j] = hyperlink;
|
|
j++;
|
|
}
|
|
else
|
|
{
|
|
/* The hyperlink is not in use any more. */
|
|
free_hyperlink (hyperlink);
|
|
}
|
|
}
|
|
stream->hyperlinks_count = j;
|
|
}
|
|
}
|
|
|
|
/* Implementation of ostream_t methods. */
|
|
|
|
static term_color_t
|
|
term_ostream__rgb_to_color (term_ostream_t stream, int red, int green, int blue)
|
|
{
|
|
switch (stream->colormodel)
|
|
{
|
|
case cm_monochrome:
|
|
return rgb_to_color_monochrome ();
|
|
case cm_common8:
|
|
return rgb_to_color_common8 (red, green, blue);
|
|
case cm_xterm8:
|
|
return rgb_to_color_xterm8 (red, green, blue);
|
|
case cm_xterm16:
|
|
return rgb_to_color_xterm16 (red, green, blue);
|
|
case cm_xterm88:
|
|
return rgb_to_color_xterm88 (red, green, blue);
|
|
case cm_xterm256:
|
|
return rgb_to_color_xterm256 (red, green, blue);
|
|
case cm_xtermrgb:
|
|
return rgb_to_color_xtermrgb (red, green, blue);
|
|
default:
|
|
abort ();
|
|
}
|
|
}
|
|
|
|
static void
|
|
term_ostream__write_mem (term_ostream_t stream, const void *data, size_t len)
|
|
{
|
|
const char *cp = (const char *) data;
|
|
while (len > 0)
|
|
{
|
|
/* Look for the next newline. */
|
|
const char *newline = (const char *) memchr (cp, '\n', len);
|
|
size_t n = (newline != NULL ? newline - cp : len);
|
|
|
|
/* Copy n bytes into the buffer. */
|
|
if (n > stream->allocated - stream->buflen)
|
|
{
|
|
size_t new_allocated =
|
|
xmax (xsum (stream->buflen, n),
|
|
xsum (stream->allocated, stream->allocated));
|
|
if (size_overflow_p (new_allocated))
|
|
error (EXIT_FAILURE, 0,
|
|
_("%s: too much output, buffer size overflow"),
|
|
"term_ostream");
|
|
stream->buffer = (char *) xrealloc (stream->buffer, new_allocated);
|
|
stream->attrbuffer =
|
|
(attributes_t *)
|
|
xrealloc (stream->attrbuffer,
|
|
new_allocated * sizeof (attributes_t));
|
|
stream->allocated = new_allocated;
|
|
}
|
|
memcpy (stream->buffer + stream->buflen, cp, n);
|
|
{
|
|
attributes_t attr = stream->simp_attr;
|
|
attributes_t *ap = stream->attrbuffer + stream->buflen;
|
|
attributes_t *ap_end = ap + n;
|
|
for (; ap < ap_end; ap++)
|
|
*ap = attr;
|
|
}
|
|
stream->buflen += n;
|
|
|
|
if (newline != NULL)
|
|
{
|
|
output_buffer (stream, stream->default_attr);
|
|
if (full_write (stream->fd, "\n", 1) < 1)
|
|
error (EXIT_FAILURE, errno, _("error writing to %s"),
|
|
stream->filename);
|
|
cp += n + 1; /* cp = newline + 1; */
|
|
len -= n + 1;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
term_ostream__flush (term_ostream_t stream, ostream_flush_scope_t scope)
|
|
{
|
|
output_buffer (stream, stream->default_attr);
|
|
if (scope == FLUSH_ALL)
|
|
{
|
|
#if HAVE_WINDOWS_CONSOLES
|
|
if (!stream->is_windows_console)
|
|
#endif
|
|
{
|
|
/* For streams connected to a disk file: */
|
|
fsync (stream->fd);
|
|
#if HAVE_TCDRAIN
|
|
/* For streams connected to a terminal: */
|
|
nonintr_tcdrain (stream->fd);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
term_ostream__free (term_ostream_t stream)
|
|
{
|
|
term_ostream_flush (stream, FLUSH_THIS_STREAM);
|
|
|
|
deactivate_term_style_controller (&controller, stream);
|
|
|
|
free (stream->filename);
|
|
if (stream->set_a_foreground != NULL)
|
|
free (stream->set_a_foreground);
|
|
if (stream->set_foreground != NULL)
|
|
free (stream->set_foreground);
|
|
if (stream->set_a_background != NULL)
|
|
free (stream->set_a_background);
|
|
if (stream->set_background != NULL)
|
|
free (stream->set_background);
|
|
if (stream->orig_pair != NULL)
|
|
free (stream->orig_pair);
|
|
if (stream->enter_bold_mode != NULL)
|
|
free (stream->enter_bold_mode);
|
|
if (stream->enter_italics_mode != NULL)
|
|
free (stream->enter_italics_mode);
|
|
if (stream->exit_italics_mode != NULL)
|
|
free (stream->exit_italics_mode);
|
|
if (stream->enter_underline_mode != NULL)
|
|
free (stream->enter_underline_mode);
|
|
if (stream->exit_underline_mode != NULL)
|
|
free (stream->exit_underline_mode);
|
|
if (stream->exit_attribute_mode != NULL)
|
|
free (stream->exit_attribute_mode);
|
|
if (stream->hyperlinks_array != NULL)
|
|
{
|
|
size_t count = stream->hyperlinks_count;
|
|
size_t i;
|
|
for (i = 0; i < count; i++)
|
|
free_hyperlink (stream->hyperlinks_array[i]);
|
|
free (stream->hyperlinks_array);
|
|
}
|
|
free (stream->buffer);
|
|
free (stream->attrbuffer);
|
|
free (stream);
|
|
}
|
|
|
|
/* Implementation of term_ostream_t methods. */
|
|
|
|
static term_color_t
|
|
term_ostream__get_color (term_ostream_t stream)
|
|
{
|
|
return stream->curr_attr.color;
|
|
}
|
|
|
|
static void
|
|
term_ostream__set_color (term_ostream_t stream, term_color_t color)
|
|
{
|
|
stream->curr_attr.color = color;
|
|
stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
|
|
}
|
|
|
|
static term_color_t
|
|
term_ostream__get_bgcolor (term_ostream_t stream)
|
|
{
|
|
return stream->curr_attr.bgcolor;
|
|
}
|
|
|
|
static void
|
|
term_ostream__set_bgcolor (term_ostream_t stream, term_color_t color)
|
|
{
|
|
stream->curr_attr.bgcolor = color;
|
|
stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
|
|
}
|
|
|
|
static term_weight_t
|
|
term_ostream__get_weight (term_ostream_t stream)
|
|
{
|
|
return stream->curr_attr.weight;
|
|
}
|
|
|
|
static void
|
|
term_ostream__set_weight (term_ostream_t stream, term_weight_t weight)
|
|
{
|
|
stream->curr_attr.weight = weight;
|
|
stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
|
|
}
|
|
|
|
static term_posture_t
|
|
term_ostream__get_posture (term_ostream_t stream)
|
|
{
|
|
return stream->curr_attr.posture;
|
|
}
|
|
|
|
static void
|
|
term_ostream__set_posture (term_ostream_t stream, term_posture_t posture)
|
|
{
|
|
stream->curr_attr.posture = posture;
|
|
stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
|
|
}
|
|
|
|
static term_underline_t
|
|
term_ostream__get_underline (term_ostream_t stream)
|
|
{
|
|
return stream->curr_attr.underline;
|
|
}
|
|
|
|
static void
|
|
term_ostream__set_underline (term_ostream_t stream, term_underline_t underline)
|
|
{
|
|
stream->curr_attr.underline = underline;
|
|
stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
|
|
}
|
|
|
|
static const char *
|
|
term_ostream__get_hyperlink_ref (term_ostream_t stream)
|
|
{
|
|
hyperlink_t *hyperlink = stream->curr_attr.hyperlink;
|
|
return (hyperlink != NULL ? hyperlink->ref : NULL);
|
|
}
|
|
|
|
static const char *
|
|
term_ostream__get_hyperlink_id (term_ostream_t stream)
|
|
{
|
|
hyperlink_t *hyperlink = stream->curr_attr.hyperlink;
|
|
return (hyperlink != NULL ? hyperlink->id : NULL);
|
|
}
|
|
|
|
static void
|
|
term_ostream__set_hyperlink (term_ostream_t stream,
|
|
const char *ref, const char *id)
|
|
{
|
|
if (ref == NULL)
|
|
stream->curr_attr.hyperlink = NULL;
|
|
else
|
|
{
|
|
/* Create a new hyperlink_t object. */
|
|
hyperlink_t *hyperlink = XMALLOC (hyperlink_t);
|
|
|
|
hyperlink->ref = xstrdup (ref);
|
|
if (id != NULL)
|
|
{
|
|
hyperlink->id = xstrdup (id);
|
|
hyperlink->real_id = hyperlink->id;
|
|
}
|
|
else
|
|
{
|
|
hyperlink->id = NULL;
|
|
if (stream->supports_hyperlink)
|
|
{
|
|
/* Generate an id always, since we don't know at this point
|
|
whether the hyperlink will span multiple lines. */
|
|
hyperlink->real_id = generate_hyperlink_id (stream);
|
|
}
|
|
else
|
|
hyperlink->real_id = NULL;
|
|
}
|
|
|
|
/* Store it. */
|
|
if (stream->hyperlinks_count == stream->hyperlinks_allocated)
|
|
{
|
|
stream->hyperlinks_allocated = 2 * stream->hyperlinks_allocated + 10;
|
|
stream->hyperlinks_array =
|
|
(hyperlink_t **)
|
|
xrealloc (stream->hyperlinks_array,
|
|
stream->hyperlinks_allocated * sizeof (hyperlink_t *));
|
|
}
|
|
stream->hyperlinks_array[stream->hyperlinks_count++] = hyperlink;
|
|
|
|
/* Install it. */
|
|
stream->curr_attr.hyperlink = hyperlink;
|
|
}
|
|
stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
|
|
}
|
|
|
|
static void
|
|
term_ostream__flush_to_current_style (term_ostream_t stream)
|
|
{
|
|
output_buffer (stream, stream->simp_attr);
|
|
}
|
|
|
|
/* Constructor. */
|
|
|
|
static inline char *
|
|
xstrdup0 (const char *str)
|
|
{
|
|
if (str == NULL)
|
|
return NULL;
|
|
#if HAVE_TERMINFO
|
|
if (str == (const char *)(-1))
|
|
return NULL;
|
|
#endif
|
|
return xstrdup (str);
|
|
}
|
|
|
|
/* Returns the base name of the terminal emulator program, possibly truncated,
|
|
as a freshly allocated string, or NULL if it cannot be determined.
|
|
Note: This function is a hack. It does not work across ssh, and it may fail
|
|
in some local situations as well. */
|
|
static inline char *
|
|
get_terminal_emulator_progname (void)
|
|
{
|
|
#if HAVE_GETSID
|
|
/* Get the process id of the session leader.
|
|
When running in a terminal emulator, it's the shell process that was
|
|
spawned by the terminal emulator. When running in a console, it's the
|
|
'login' process.
|
|
On some operating systems (Linux, *BSD, AIX), the same result could also
|
|
be obtained through
|
|
pid_t p;
|
|
if (ioctl (1, TIOCGSID, &p) >= 0) ...
|
|
*/
|
|
pid_t session_leader_pid = getsid (0);
|
|
if (session_leader_pid != (pid_t)(-1))
|
|
{
|
|
/* Get the process id of the terminal emulator.
|
|
When running in a console, it's the process id of the 'init'
|
|
process. */
|
|
pid_t terminal_emulator_pid = get_ppid_of (session_leader_pid);
|
|
if (terminal_emulator_pid != 0)
|
|
{
|
|
/* Retrieve the base name of the program name of this process. */
|
|
return get_progname_of (terminal_emulator_pid);
|
|
}
|
|
}
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
/* Returns true if we should enable hyperlinks.
|
|
term is the value of the TERM environment variable. */
|
|
static inline bool
|
|
should_enable_hyperlinks (const char *term)
|
|
{
|
|
if (getenv ("NO_TERM_HYPERLINKS") != NULL)
|
|
/* The user has disabled hyperlinks. */
|
|
return false;
|
|
|
|
/* Dispatch based on $TERM. */
|
|
if (term != NULL)
|
|
{
|
|
/* rxvt-based terminal emulators:
|
|
Program | TERM | Supports hyperlinks?
|
|
------------------+--------------+-------------------------------------
|
|
rxvt 2.7.10 | rxvt | hangs after "cat hyperlink-demo.txt"
|
|
mrxvt 0.5.3 | rxvt | no
|
|
rxvt-unicode 9.22 | rxvt-unicode | no
|
|
*/
|
|
if (strcmp (term, "rxvt") == 0)
|
|
return false;
|
|
|
|
/* Emacs-based terminal emulators:
|
|
Program | TERM | Supports hyperlinks?
|
|
--------------------+-------------+---------------------
|
|
emacs-terminal 26.1 | eterm-color | produces garbage
|
|
*/
|
|
if (strncmp (term, "eterm", 5) == 0)
|
|
return false;
|
|
|
|
/* xterm-compatible terminal emulators:
|
|
Program | TERM | Supports hyperlinks?
|
|
-----------------+----------------+---------------------------
|
|
guake 0.8.8 | xterm | produces garbage
|
|
lilyterm 0.9.9.2 | xterm | produces garbage
|
|
lterm 1.5.1 | xterm | produces garbage
|
|
lxterminal 0.3.2 | xterm | produces garbage
|
|
termit 2.9.6 | xterm | produces garbage
|
|
konsole 18.12.3 | xterm-256color | produces extra backslashes
|
|
yakuake 3.0.5 | xterm-256color | produces extra backslashes
|
|
other | | yes or no, no severe bugs
|
|
|
|
TODO: Revisit this table periodically.
|
|
*/
|
|
if (strncmp (term, "xterm", 5) == 0)
|
|
{
|
|
char *progname = get_terminal_emulator_progname ();
|
|
if (progname != NULL)
|
|
{
|
|
bool known_buggy =
|
|
strncmp (progname, "python", 6) == 0 /* guake */
|
|
|| strcmp (progname, "lilyterm") == 0
|
|
|| strcmp (progname, "lterm") == 0
|
|
|| strcmp (progname, "lxterminal") == 0
|
|
|| strcmp (progname, "termit") == 0
|
|
|| strcmp (progname, "konsole") == 0
|
|
|| strcmp (progname, "yakuake") == 0;
|
|
free (progname);
|
|
/* Enable hyperlinks except for programs that are known buggy. */
|
|
return !known_buggy;
|
|
}
|
|
}
|
|
|
|
/* Solaris console.
|
|
Program | TERM | Supports hyperlinks?
|
|
-----------------------------------+-----------+---------------------------
|
|
Solaris kernel's terminal emulator | sun-color | produces garbage
|
|
SPARC PROM's terminal emulator | sun | ?
|
|
*/
|
|
if (strcmp (term, "sun") == 0 || strcmp (term, "sun-color") == 0)
|
|
return false;
|
|
}
|
|
|
|
/* In case of doubt, enable hyperlinks. So this code does not need to change
|
|
as more and more terminal emulators support hyperlinks.
|
|
If there are adverse effects, the user can disable hyperlinks by setting
|
|
NO_TERM_HYPERLINKS. */
|
|
return true;
|
|
}
|
|
|
|
term_ostream_t
|
|
term_ostream_create (int fd, const char *filename, ttyctl_t tty_control)
|
|
{
|
|
term_ostream_t stream = XMALLOC (struct term_ostream_representation);
|
|
|
|
stream->base.vtable = &term_ostream_vtable;
|
|
stream->fd = fd;
|
|
#if HAVE_WINDOWS_CONSOLES
|
|
stream->handle = (HANDLE) _get_osfhandle (fd);
|
|
{
|
|
DWORD mode;
|
|
|
|
if (stream->handle != INVALID_HANDLE_VALUE
|
|
/* GetConsoleMode
|
|
<https://docs.microsoft.com/en-us/windows/console/getconsolemode> */
|
|
&& GetConsoleMode (stream->handle, &mode) != 0)
|
|
{
|
|
CONSOLE_SCREEN_BUFFER_INFO info;
|
|
BOOL ok;
|
|
|
|
/* GetConsoleScreenBufferInfo
|
|
<https://docs.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo>
|
|
<https://docs.microsoft.com/en-us/windows/console/console-screen-buffer-info-str> */
|
|
ok = GetConsoleScreenBufferInfo (stream->handle, &info);
|
|
if (!ok)
|
|
{
|
|
/* GetConsoleScreenBufferInfo
|
|
- fails when the handle is == GetStdHandle (STD_INPUT_HANDLE)
|
|
- but succeeds when it is == GetStdHandle (STD_OUTPUT_HANDLE)
|
|
or == GetStdHandle (STD_ERROR_HANDLE).
|
|
Native Windows programs use GetStdHandle (STD_OUTPUT_HANDLE) for
|
|
fd 1, as expected.
|
|
But Cygwin uses GetStdHandle (STD_INPUT_HANDLE) for all of fd 0,
|
|
1, 2. So, we have to use either GetStdHandle (STD_OUTPUT_HANDLE)
|
|
or GetStdHandle (STD_ERROR_HANDLE) in order to be able to use
|
|
GetConsoleScreenBufferInfo. */
|
|
if (fd == 1 || fd == 2)
|
|
{
|
|
HANDLE handle =
|
|
GetStdHandle (fd == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
|
|
ok = GetConsoleScreenBufferInfo (handle, &info);
|
|
if (ok)
|
|
stream->handle = handle;
|
|
}
|
|
}
|
|
if (ok)
|
|
{
|
|
stream->is_windows_console = true;
|
|
stream->default_console_attributes = info.wAttributes;
|
|
stream->current_console_attributes = stream->default_console_attributes;
|
|
}
|
|
else
|
|
/* It's a console, but we cannot use GetConsoleScreenBufferInfo. */
|
|
stream->is_windows_console = false;
|
|
}
|
|
else
|
|
stream->is_windows_console = false;
|
|
}
|
|
#endif
|
|
stream->filename = xstrdup (filename);
|
|
stream->tty_control = tty_control;
|
|
|
|
/* Defaults. */
|
|
stream->max_colors = -1;
|
|
stream->no_color_video = -1;
|
|
stream->set_a_foreground = NULL;
|
|
stream->set_foreground = NULL;
|
|
stream->set_a_background = NULL;
|
|
stream->set_background = NULL;
|
|
stream->orig_pair = NULL;
|
|
stream->enter_bold_mode = NULL;
|
|
stream->enter_italics_mode = NULL;
|
|
stream->exit_italics_mode = NULL;
|
|
stream->enter_underline_mode = NULL;
|
|
stream->exit_underline_mode = NULL;
|
|
stream->exit_attribute_mode = NULL;
|
|
|
|
#if HAVE_WINDOWS_CONSOLES
|
|
if (stream->is_windows_console)
|
|
{
|
|
/* For Windows consoles, two approaches are possible:
|
|
(A) Use SetConsoleMode
|
|
<https://docs.microsoft.com/en-us/windows/console/setconsolemode>
|
|
to enable the ENABLE_VIRTUAL_TERMINAL_PROCESSING flag, and then
|
|
emit escape sequences, as documented in
|
|
<https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences>.
|
|
(B) Use SetConsoleTextAttribute
|
|
<https://docs.microsoft.com/en-us/windows/console/setconsoletextattribute>
|
|
to change the text attributes.
|
|
Approach (A) has two drawbacks:
|
|
* It produces colors that ignore the console's configuration: it
|
|
assumes the default configuration (light grey foreground). Thus
|
|
when you ask for cyan, you will always get some blue color, never
|
|
real cyan. Whereas approach (B) produces colors that respect the
|
|
"Screen Text" and "Screen Background" settings in the console's
|
|
configuration.
|
|
* When the program terminates abnormally, we would leave the console
|
|
with ENABLE_VIRTUAL_TERMINAL_PROCESSING enabled, which can be
|
|
dangerous.
|
|
Therefore we use approach (B). */
|
|
stream->max_colors = 8;
|
|
stream->no_color_video = 1 | 4;
|
|
stream->supports_foreground = true;
|
|
stream->supports_background = true;
|
|
stream->colormodel = cm_common8;
|
|
/* The Windows consoles have high and low intensity, but the default is
|
|
high intensity. If we wanted to support WEIGHT_BOLD, we would have to
|
|
use low-intensity rendering for normal output, which would look ugly
|
|
compared to the output by other programs. We could support WEIGHT_DIM,
|
|
but this is not part of our enum term_weight_t. */
|
|
stream->supports_weight = false;
|
|
stream->supports_posture = false;
|
|
stream->supports_underline = true;
|
|
stream->supports_hyperlink = false;
|
|
stream->restore_colors = NULL;
|
|
stream->restore_weight = NULL;
|
|
stream->restore_posture = NULL;
|
|
stream->restore_underline = NULL;
|
|
stream->restore_hyperlink = NULL;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
const char *term;
|
|
|
|
/* Retrieve the terminal type. */
|
|
term = getenv ("TERM");
|
|
if (term != NULL && term[0] != '\0')
|
|
{
|
|
/* When the terminfo function are available, we prefer them over the
|
|
termcap functions because
|
|
1. they don't risk a buffer overflow,
|
|
2. on OSF/1, for TERM=xterm, the tiget* functions provide access
|
|
to the number of colors and the color escape sequences,
|
|
whereas the tget* functions don't provide them. */
|
|
#if HAVE_TERMINFO
|
|
int err = 1;
|
|
|
|
if (setupterm (term, fd, &err) == 0 || err == 1)
|
|
{
|
|
/* Retrieve particular values depending on the terminal type. */
|
|
stream->max_colors = tigetnum ("colors");
|
|
stream->no_color_video = tigetnum ("ncv");
|
|
stream->set_a_foreground = xstrdup0 (tigetstr ("setaf"));
|
|
stream->set_foreground = xstrdup0 (tigetstr ("setf"));
|
|
stream->set_a_background = xstrdup0 (tigetstr ("setab"));
|
|
stream->set_background = xstrdup0 (tigetstr ("setb"));
|
|
stream->orig_pair = xstrdup0 (tigetstr ("op"));
|
|
stream->enter_bold_mode = xstrdup0 (tigetstr ("bold"));
|
|
stream->enter_italics_mode = xstrdup0 (tigetstr ("sitm"));
|
|
stream->exit_italics_mode = xstrdup0 (tigetstr ("ritm"));
|
|
stream->enter_underline_mode = xstrdup0 (tigetstr ("smul"));
|
|
stream->exit_underline_mode = xstrdup0 (tigetstr ("rmul"));
|
|
stream->exit_attribute_mode = xstrdup0 (tigetstr ("sgr0"));
|
|
}
|
|
#elif HAVE_TERMCAP
|
|
/* The buffer size needed for termcap was 1024 bytes in the past, but
|
|
nowadays the largest termcap description (bq300-8-pc-w-rv) is 1507
|
|
bytes long. <https://tldp.org/LDP/lpg/node91.html> suggests a
|
|
buffer size of 2048 bytes. */
|
|
struct { char buf[2048]; char canary[4]; } termcapbuf;
|
|
int retval;
|
|
|
|
/* Call tgetent, being defensive against buffer overflow. */
|
|
memcpy (termcapbuf.canary, "CnRy", 4);
|
|
retval = tgetent (termcapbuf.buf, term);
|
|
if (memcmp (termcapbuf.canary, "CnRy", 4) != 0)
|
|
/* Buffer overflow! */
|
|
abort ();
|
|
|
|
if (retval > 0)
|
|
{
|
|
/* The buffer size needed for a termcap entry was 1024 bytes in
|
|
the past, but nowadays the largest one (in bq300-8-pc-w-rv)
|
|
is 1034 bytes long. */
|
|
struct { char buf[2048]; char canary[4]; } termentrybuf;
|
|
char *termentryptr;
|
|
|
|
/* Prepare for calling tgetstr, being defensive against buffer
|
|
overflow. ncurses' tgetstr() supports a second argument NULL,
|
|
but NetBSD's tgetstr() doesn't. */
|
|
memcpy (termentrybuf.canary, "CnRz", 4);
|
|
#define TEBP ((termentryptr = termentrybuf.buf), &termentryptr)
|
|
|
|
/* Retrieve particular values depending on the terminal type. */
|
|
stream->max_colors = tgetnum ("Co");
|
|
stream->no_color_video = tgetnum ("NC");
|
|
stream->set_a_foreground = xstrdup0 (tgetstr ("AF", TEBP));
|
|
stream->set_foreground = xstrdup0 (tgetstr ("Sf", TEBP));
|
|
stream->set_a_background = xstrdup0 (tgetstr ("AB", TEBP));
|
|
stream->set_background = xstrdup0 (tgetstr ("Sb", TEBP));
|
|
stream->orig_pair = xstrdup0 (tgetstr ("op", TEBP));
|
|
stream->enter_bold_mode = xstrdup0 (tgetstr ("md", TEBP));
|
|
stream->enter_italics_mode = xstrdup0 (tgetstr ("ZH", TEBP));
|
|
stream->exit_italics_mode = xstrdup0 (tgetstr ("ZR", TEBP));
|
|
stream->enter_underline_mode = xstrdup0 (tgetstr ("us", TEBP));
|
|
stream->exit_underline_mode = xstrdup0 (tgetstr ("ue", TEBP));
|
|
stream->exit_attribute_mode = xstrdup0 (tgetstr ("me", TEBP));
|
|
|
|
#ifdef __BEOS__
|
|
/* The BeOS termcap entry for "beterm" is broken: For "AF" and
|
|
"AB" it contains values in terminfo syntax but the system's
|
|
tparam() function understands only the termcap syntax. */
|
|
if (stream->set_a_foreground != NULL
|
|
&& strcmp (stream->set_a_foreground, "\033[3%p1%dm") == 0)
|
|
{
|
|
free (stream->set_a_foreground);
|
|
stream->set_a_foreground = xstrdup ("\033[3%dm");
|
|
}
|
|
if (stream->set_a_background != NULL
|
|
&& strcmp (stream->set_a_background, "\033[4%p1%dm") == 0)
|
|
{
|
|
free (stream->set_a_background);
|
|
stream->set_a_background = xstrdup ("\033[4%dm");
|
|
}
|
|
#endif
|
|
|
|
/* The termcap entry for cygwin is broken: It has no "ncv" value,
|
|
but bold and underline are actually rendered through colors. */
|
|
if (strcmp (term, "cygwin") == 0)
|
|
stream->no_color_video |= 2 | 32;
|
|
|
|
/* Done with tgetstr. Detect possible buffer overflow. */
|
|
#undef TEBP
|
|
if (memcmp (termentrybuf.canary, "CnRz", 4) != 0)
|
|
/* Buffer overflow! */
|
|
abort ();
|
|
}
|
|
#else
|
|
/* Fallback code for platforms with neither the terminfo nor the
|
|
termcap functions, such as mingw.
|
|
Assume the ANSI escape sequences. Extracted through
|
|
"TERM=ansi infocmp", replacing \E with \033. */
|
|
stream->max_colors = 8;
|
|
stream->no_color_video = 3;
|
|
stream->set_a_foreground = xstrdup ("\033[3%p1%dm");
|
|
stream->set_a_background = xstrdup ("\033[4%p1%dm");
|
|
stream->orig_pair = xstrdup ("\033[39;49m");
|
|
stream->enter_bold_mode = xstrdup ("\033[1m");
|
|
stream->enter_underline_mode = xstrdup ("\033[4m");
|
|
stream->exit_underline_mode = xstrdup ("\033[m");
|
|
stream->exit_attribute_mode = xstrdup ("\033[0;10m");
|
|
#endif
|
|
|
|
/* AIX 4.3.2, IRIX 6.5, HP-UX 11, Solaris 7..10 all lack the
|
|
description of color capabilities of "xterm" and "xterms"
|
|
in their terminfo database. But it is important to have
|
|
color in xterm. So we provide the color capabilities here. */
|
|
if (stream->max_colors <= 1
|
|
&& (strcmp (term, "xterm") == 0 || strcmp (term, "xterms") == 0))
|
|
{
|
|
stream->max_colors = 8;
|
|
stream->set_a_foreground = xstrdup ("\033[3%p1%dm");
|
|
stream->set_a_background = xstrdup ("\033[4%p1%dm");
|
|
stream->orig_pair = xstrdup ("\033[39;49m");
|
|
}
|
|
}
|
|
|
|
/* Infer the capabilities. */
|
|
stream->supports_foreground =
|
|
(stream->max_colors >= 8
|
|
&& (stream->set_a_foreground != NULL || stream->set_foreground != NULL)
|
|
&& stream->orig_pair != NULL);
|
|
stream->supports_background =
|
|
(stream->max_colors >= 8
|
|
&& (stream->set_a_background != NULL || stream->set_background != NULL)
|
|
&& stream->orig_pair != NULL);
|
|
stream->colormodel =
|
|
(stream->supports_foreground || stream->supports_background
|
|
? (term != NULL
|
|
&& (/* Recognize xterm-16color, xterm-88color, xterm-256color. */
|
|
(strlen (term) >= 5 && memcmp (term, "xterm", 5) == 0)
|
|
|| /* Recognize *-16color. */
|
|
(strlen (term) > 8
|
|
&& strcmp (term + strlen (term) - 8, "-16color") == 0)
|
|
|| /* Recognize *-256color. */
|
|
(strlen (term) > 9
|
|
&& strcmp (term + strlen (term) - 9, "-256color") == 0)
|
|
|| /* Recognize *-direct. */
|
|
(strlen (term) > 8
|
|
&& strcmp (term + strlen (term) - 8, "-direct") == 0))
|
|
? (stream->max_colors >= 0x7fff ? cm_xtermrgb :
|
|
stream->max_colors == 256 ? cm_xterm256 :
|
|
stream->max_colors == 88 ? cm_xterm88 :
|
|
stream->max_colors == 16 ? cm_xterm16 :
|
|
cm_xterm8)
|
|
: cm_common8)
|
|
: cm_monochrome);
|
|
stream->supports_weight =
|
|
(stream->enter_bold_mode != NULL
|
|
&& stream->exit_attribute_mode != NULL);
|
|
stream->supports_posture =
|
|
(stream->enter_italics_mode != NULL
|
|
&& (stream->exit_italics_mode != NULL
|
|
|| stream->exit_attribute_mode != NULL));
|
|
stream->supports_underline =
|
|
(stream->enter_underline_mode != NULL
|
|
&& (stream->exit_underline_mode != NULL
|
|
|| stream->exit_attribute_mode != NULL));
|
|
/* TODO: Use a terminfo capability, once ncurses implements it. */
|
|
stream->supports_hyperlink = should_enable_hyperlinks (term);
|
|
|
|
/* Infer the restore strings. */
|
|
stream->restore_colors =
|
|
(stream->supports_foreground || stream->supports_background
|
|
? stream->orig_pair
|
|
: NULL);
|
|
stream->restore_weight =
|
|
(stream->supports_weight ? stream->exit_attribute_mode : NULL);
|
|
stream->restore_posture =
|
|
(stream->supports_posture
|
|
? (stream->exit_italics_mode != NULL
|
|
? stream->exit_italics_mode
|
|
: stream->exit_attribute_mode)
|
|
: NULL);
|
|
stream->restore_underline =
|
|
(stream->supports_underline
|
|
? (stream->exit_underline_mode != NULL
|
|
? stream->exit_underline_mode
|
|
: stream->exit_attribute_mode)
|
|
: NULL);
|
|
stream->restore_hyperlink =
|
|
(stream->supports_hyperlink
|
|
? "\033]8;;\033\\"
|
|
: NULL);
|
|
}
|
|
|
|
/* Initialize the hyperlink id generator. */
|
|
if (stream->supports_hyperlink)
|
|
{
|
|
char *hostname = xgethostname ();
|
|
{ /* Compute a hash code, like in gnulib/lib/hash-pjw.c. */
|
|
uint32_t h = 0;
|
|
if (hostname != NULL)
|
|
{
|
|
const char *p;
|
|
for (p = hostname; *p; p++)
|
|
h = (unsigned char) *p + ((h << 9) | (h >> (32 - 9)));
|
|
}
|
|
stream->hostname_hash = h;
|
|
}
|
|
free (hostname);
|
|
|
|
{
|
|
struct timeval tv;
|
|
gettimeofday (&tv, NULL);
|
|
stream->start_time =
|
|
(uint64_t) tv.tv_sec * (uint64_t) 1000000 + (uint64_t) tv.tv_usec;
|
|
}
|
|
|
|
stream->id_serial = 0;
|
|
}
|
|
|
|
/* Initialize the set of hyperlink_t. */
|
|
stream->hyperlinks_array = NULL;
|
|
stream->hyperlinks_count = 0;
|
|
stream->hyperlinks_allocated = 0;
|
|
|
|
/* Initialize the buffer. */
|
|
stream->allocated = 120;
|
|
stream->buffer = XNMALLOC (stream->allocated, char);
|
|
stream->attrbuffer = XNMALLOC (stream->allocated, attributes_t);
|
|
stream->buflen = 0;
|
|
|
|
/* Initialize the current attributes. */
|
|
{
|
|
attributes_t assumed_default;
|
|
attributes_t simplified_default;
|
|
|
|
assumed_default.color = COLOR_DEFAULT;
|
|
assumed_default.bgcolor = COLOR_DEFAULT;
|
|
assumed_default.weight = WEIGHT_DEFAULT;
|
|
assumed_default.posture = POSTURE_DEFAULT;
|
|
assumed_default.underline = UNDERLINE_DEFAULT;
|
|
assumed_default.hyperlink = NULL;
|
|
|
|
simplified_default = simplify_attributes (stream, assumed_default);
|
|
|
|
stream->default_attr = simplified_default;
|
|
stream->active_attr = simplified_default;
|
|
stream->curr_attr = assumed_default;
|
|
stream->simp_attr = simplified_default;
|
|
}
|
|
|
|
/* Prepare tty control. */
|
|
activate_term_style_controller (&controller, stream, fd, tty_control);
|
|
|
|
return stream;
|
|
}
|
|
|
|
/* Accessors. */
|
|
|
|
static int
|
|
term_ostream__get_descriptor (term_ostream_t stream)
|
|
{
|
|
return stream->fd;
|
|
}
|
|
|
|
static const char *
|
|
term_ostream__get_filename (term_ostream_t stream)
|
|
{
|
|
return stream->filename;
|
|
}
|
|
|
|
static ttyctl_t
|
|
term_ostream__get_tty_control (term_ostream_t stream)
|
|
{
|
|
return stream->tty_control;
|
|
}
|
|
|
|
static ttyctl_t
|
|
term_ostream__get_effective_tty_control (term_ostream_t stream)
|
|
{
|
|
return stream->control_data.tty_control;
|
|
}
|
|
|
|
/* Instanceof test. */
|
|
|
|
bool
|
|
is_instance_of_term_ostream (ostream_t stream)
|
|
{
|
|
return IS_INSTANCE (stream, ostream, term_ostream);
|
|
}
|
|
|
|
#line 2740 "term-ostream.c"
|
|
|
|
const struct term_ostream_implementation term_ostream_vtable =
|
|
{
|
|
term_ostream_superclasses,
|
|
sizeof (term_ostream_superclasses) / sizeof (term_ostream_superclasses[0]),
|
|
sizeof (struct term_ostream_representation),
|
|
term_ostream__write_mem,
|
|
term_ostream__flush,
|
|
term_ostream__free,
|
|
term_ostream__rgb_to_color,
|
|
term_ostream__get_color,
|
|
term_ostream__set_color,
|
|
term_ostream__get_bgcolor,
|
|
term_ostream__set_bgcolor,
|
|
term_ostream__get_weight,
|
|
term_ostream__set_weight,
|
|
term_ostream__get_posture,
|
|
term_ostream__set_posture,
|
|
term_ostream__get_underline,
|
|
term_ostream__set_underline,
|
|
term_ostream__get_hyperlink_ref,
|
|
term_ostream__get_hyperlink_id,
|
|
term_ostream__set_hyperlink,
|
|
term_ostream__flush_to_current_style,
|
|
term_ostream__get_descriptor,
|
|
term_ostream__get_filename,
|
|
term_ostream__get_tty_control,
|
|
term_ostream__get_effective_tty_control,
|
|
};
|
|
|
|
#if !HAVE_INLINE
|
|
|
|
/* Define the functions that invoke the methods. */
|
|
|
|
void
|
|
term_ostream_write_mem (term_ostream_t first_arg, const void *data, size_t len)
|
|
{
|
|
const struct term_ostream_implementation *vtable =
|
|
((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
|
|
vtable->write_mem (first_arg,data,len);
|
|
}
|
|
|
|
void
|
|
term_ostream_flush (term_ostream_t first_arg, ostream_flush_scope_t scope)
|
|
{
|
|
const struct term_ostream_implementation *vtable =
|
|
((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
|
|
vtable->flush (first_arg,scope);
|
|
}
|
|
|
|
void
|
|
term_ostream_free (term_ostream_t first_arg)
|
|
{
|
|
const struct term_ostream_implementation *vtable =
|
|
((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
|
|
vtable->free (first_arg);
|
|
}
|
|
|
|
term_color_t
|
|
term_ostream_rgb_to_color (term_ostream_t first_arg, int red, int green, int blue)
|
|
{
|
|
const struct term_ostream_implementation *vtable =
|
|
((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
|
|
return vtable->rgb_to_color (first_arg,red,green,blue);
|
|
}
|
|
|
|
term_color_t
|
|
term_ostream_get_color (term_ostream_t first_arg)
|
|
{
|
|
const struct term_ostream_implementation *vtable =
|
|
((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
|
|
return vtable->get_color (first_arg);
|
|
}
|
|
|
|
void
|
|
term_ostream_set_color (term_ostream_t first_arg, term_color_t color)
|
|
{
|
|
const struct term_ostream_implementation *vtable =
|
|
((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
|
|
vtable->set_color (first_arg,color);
|
|
}
|
|
|
|
term_color_t
|
|
term_ostream_get_bgcolor (term_ostream_t first_arg)
|
|
{
|
|
const struct term_ostream_implementation *vtable =
|
|
((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
|
|
return vtable->get_bgcolor (first_arg);
|
|
}
|
|
|
|
void
|
|
term_ostream_set_bgcolor (term_ostream_t first_arg, term_color_t color)
|
|
{
|
|
const struct term_ostream_implementation *vtable =
|
|
((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
|
|
vtable->set_bgcolor (first_arg,color);
|
|
}
|
|
|
|
term_weight_t
|
|
term_ostream_get_weight (term_ostream_t first_arg)
|
|
{
|
|
const struct term_ostream_implementation *vtable =
|
|
((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
|
|
return vtable->get_weight (first_arg);
|
|
}
|
|
|
|
void
|
|
term_ostream_set_weight (term_ostream_t first_arg, term_weight_t weight)
|
|
{
|
|
const struct term_ostream_implementation *vtable =
|
|
((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
|
|
vtable->set_weight (first_arg,weight);
|
|
}
|
|
|
|
term_posture_t
|
|
term_ostream_get_posture (term_ostream_t first_arg)
|
|
{
|
|
const struct term_ostream_implementation *vtable =
|
|
((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
|
|
return vtable->get_posture (first_arg);
|
|
}
|
|
|
|
void
|
|
term_ostream_set_posture (term_ostream_t first_arg, term_posture_t posture)
|
|
{
|
|
const struct term_ostream_implementation *vtable =
|
|
((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
|
|
vtable->set_posture (first_arg,posture);
|
|
}
|
|
|
|
term_underline_t
|
|
term_ostream_get_underline (term_ostream_t first_arg)
|
|
{
|
|
const struct term_ostream_implementation *vtable =
|
|
((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
|
|
return vtable->get_underline (first_arg);
|
|
}
|
|
|
|
void
|
|
term_ostream_set_underline (term_ostream_t first_arg, term_underline_t underline)
|
|
{
|
|
const struct term_ostream_implementation *vtable =
|
|
((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
|
|
vtable->set_underline (first_arg,underline);
|
|
}
|
|
|
|
const char *
|
|
term_ostream_get_hyperlink_ref (term_ostream_t first_arg)
|
|
{
|
|
const struct term_ostream_implementation *vtable =
|
|
((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
|
|
return vtable->get_hyperlink_ref (first_arg);
|
|
}
|
|
|
|
const char *
|
|
term_ostream_get_hyperlink_id (term_ostream_t first_arg)
|
|
{
|
|
const struct term_ostream_implementation *vtable =
|
|
((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
|
|
return vtable->get_hyperlink_id (first_arg);
|
|
}
|
|
|
|
void
|
|
term_ostream_set_hyperlink (term_ostream_t first_arg, const char *ref, const char *id)
|
|
{
|
|
const struct term_ostream_implementation *vtable =
|
|
((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
|
|
vtable->set_hyperlink (first_arg,ref,id);
|
|
}
|
|
|
|
void
|
|
term_ostream_flush_to_current_style (term_ostream_t first_arg)
|
|
{
|
|
const struct term_ostream_implementation *vtable =
|
|
((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
|
|
vtable->flush_to_current_style (first_arg);
|
|
}
|
|
|
|
int
|
|
term_ostream_get_descriptor (term_ostream_t first_arg)
|
|
{
|
|
const struct term_ostream_implementation *vtable =
|
|
((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
|
|
return vtable->get_descriptor (first_arg);
|
|
}
|
|
|
|
const char *
|
|
term_ostream_get_filename (term_ostream_t first_arg)
|
|
{
|
|
const struct term_ostream_implementation *vtable =
|
|
((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
|
|
return vtable->get_filename (first_arg);
|
|
}
|
|
|
|
ttyctl_t
|
|
term_ostream_get_tty_control (term_ostream_t first_arg)
|
|
{
|
|
const struct term_ostream_implementation *vtable =
|
|
((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
|
|
return vtable->get_tty_control (first_arg);
|
|
}
|
|
|
|
ttyctl_t
|
|
term_ostream_get_effective_tty_control (term_ostream_t first_arg)
|
|
{
|
|
const struct term_ostream_implementation *vtable =
|
|
((struct term_ostream_representation_header *) (struct term_ostream_representation *) first_arg)->vtable;
|
|
return vtable->get_effective_tty_control (first_arg);
|
|
}
|
|
|
|
#endif
|