summaryrefslogtreecommitdiff
path: root/gnulib-local/lib/term-ostream.oo.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnulib-local/lib/term-ostream.oo.c')
-rw-r--r--gnulib-local/lib/term-ostream.oo.c1925
1 files changed, 1925 insertions, 0 deletions
diff --git a/gnulib-local/lib/term-ostream.oo.c b/gnulib-local/lib/term-ostream.oo.c
new file mode 100644
index 0000000..6556e15
--- /dev/null
+++ b/gnulib-local/lib/term-ostream.oo.c
@@ -0,0 +1,1925 @@
+/* Output stream for attributed text, producing ANSI escape sequences.
+ Copyright (C) 2006-2008 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 <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+/* Specification. */
+#include "term-ostream.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "error.h"
+#include "fatal-signal.h"
+#include "full-write.h"
+#include "terminfo.h"
+#include "xalloc.h"
+#include "xsize.h"
+#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
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+
+/* =========================== 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. */
+} 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 ()
+{
+ 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 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);
+}
+
+
+/* ============================= 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 : 9;
+ BITFIELD_TYPE(term_color_t, signed int) bgcolor : 9;
+ 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;
+} attributes_t;
+
+
+/* ============================ term_ostream_t ============================ */
+
+struct term_ostream : struct ostream
+{
+fields:
+ /* The file descriptor used for output. Note that ncurses termcap emulation
+ uses the baud rate information from file descriptor 1 (stdout) if it is
+ a tty, or from file descriptor 2 (stderr) otherwise. */
+ int fd;
+ char *filename;
+ /* Values from the terminal type's terminfo/termcap description.
+ See terminfo(5) for details. */
+ /* terminfo termcap */
+ int max_colors; /* colors Co */
+ int no_color_video; /* ncv NC */
+ char *set_a_foreground; /* setaf AF */
+ char *set_foreground; /* setf Sf */
+ char *set_a_background; /* setab AB */
+ char *set_background; /* setb Sb */
+ char *orig_pair; /* op op */
+ char *enter_bold_mode; /* bold md */
+ char *enter_italics_mode; /* sitm ZH */
+ char *exit_italics_mode; /* ritm ZR */
+ char *enter_underline_mode; /* smul us */
+ char *exit_underline_mode; /* rmul ue */
+ char *exit_attribute_mode; /* sgr0 me */
+ /* Inferred values. */
+ bool supports_foreground;
+ bool supports_background;
+ colormodel_t colormodel;
+ bool supports_weight;
+ bool supports_posture;
+ bool supports_underline;
+ /* Variable state. */
+ char *buffer; /* Buffer for the current line. */
+ attributes_t *attrbuffer; /* Buffer for the simplified attributes; same
+ length as buffer. */
+ size_t buflen; /* Number of bytes stored so far. */
+ size_t allocated; /* Allocated size of the buffer. */
+ attributes_t curr_attr; /* Current attributes. */
+ attributes_t simp_attr; /* Simplified current attributes. */
+};
+
+/* 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;
+ return attr;
+}
+
+/* While a line is being output, we need to be careful to restore the
+ terminal's settings in case of a fatal signal or an exit() call. */
+
+/* File descriptor to which out_char shall output escape sequences. */
+static int out_fd = -1;
+
+/* Filename of out_fd. */
+static const char *out_filename;
+
+/* Output a single char to out_fd. Ignore errors. */
+static int
+out_char_unchecked (int c)
+{
+ char bytes[1];
+
+ bytes[0] = (char)c;
+ full_write (out_fd, bytes, 1);
+ return 0;
+}
+
+/* State that informs the exit handler what to do. */
+static const char *restore_colors;
+static const char *restore_weight;
+static const char *restore_posture;
+static const char *restore_underline;
+
+/* The exit handler. */
+static void
+restore (void)
+{
+ /* Only do something while some output was interrupted. */
+ if (out_fd >= 0)
+ {
+ if (restore_colors != NULL)
+ tputs (restore_colors, 1, out_char_unchecked);
+ if (restore_weight != NULL)
+ tputs (restore_weight, 1, out_char_unchecked);
+ if (restore_posture != NULL)
+ tputs (restore_posture, 1, out_char_unchecked);
+ if (restore_underline != NULL)
+ tputs (restore_underline, 1, out_char_unchecked);
+ }
+}
+
+/* The list of signals whose default behaviour is to stop the program. */
+static int stopping_signals[] =
+ {
+#ifdef SIGTSTP
+ SIGTSTP,
+#endif
+#ifdef SIGTTIN
+ SIGTTIN,
+#endif
+#ifdef SIGTTOU
+ SIGTTOU,
+#endif
+ 0
+ };
+
+#define num_stopping_signals (SIZEOF (stopping_signals) - 1)
+
+static sigset_t stopping_signal_set;
+
+static void
+init_stopping_signal_set ()
+{
+ static bool stopping_signal_set_initialized = false;
+ if (!stopping_signal_set_initialized)
+ {
+ size_t i;
+
+ sigemptyset (&stopping_signal_set);
+ for (i = 0; i < num_stopping_signals; i++)
+ sigaddset (&stopping_signal_set, stopping_signals[i]);
+
+ stopping_signal_set_initialized = true;
+ }
+}
+
+/* Temporarily delay the stopping signals. */
+static inline void
+block_stopping_signals ()
+{
+ init_stopping_signal_set ();
+ sigprocmask (SIG_BLOCK, &stopping_signal_set, NULL);
+}
+
+/* Stop delaying the stopping signals. */
+static inline void
+unblock_stopping_signals ()
+{
+ init_stopping_signal_set ();
+ sigprocmask (SIG_UNBLOCK, &stopping_signal_set, NULL);
+}
+
+/* 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);
+}
+
+/* Signal error after full_write failed. */
+static void
+out_error ()
+{
+ error (EXIT_FAILURE, errno, _("error writing to %s"), out_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 escape sequences to switch from OLD_ATTR to NEW_ATTR. */
+static void
+out_attr_change (term_ostream_t stream,
+ attributes_t old_attr, attributes_t new_attr)
+{
+ bool cleared_attributes;
+
+ /* 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))
+ {
+ assert (stream->supports_foreground);
+ assert (new_attr.color != COLOR_DEFAULT);
+ switch (stream->colormodel)
+ {
+ case cm_common8:
+ assert (new_attr.color >= 0 && new_attr.color < 8);
+ if (stream->set_a_foreground != NULL)
+ tputs (tparm (stream->set_a_foreground,
+ color_bgr (new_attr.color)),
+ 1, out_char);
+ else
+ tputs (tparm (stream->set_foreground, new_attr.color),
+ 1, 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_attr.color >= 0 && new_attr.color < 8);
+ {
+ char bytes[5];
+ bytes[0] = 0x1B; bytes[1] = '[';
+ bytes[2] = '3'; bytes[3] = '0' + new_attr.color;
+ bytes[4] = 'm';
+ if (full_write (out_fd, bytes, 5) < 5)
+ out_error ();
+ }
+ break;
+ case cm_xterm16:
+ assert (new_attr.color >= 0 && new_attr.color < 16);
+ {
+ char bytes[5];
+ bytes[0] = 0x1B; bytes[1] = '[';
+ if (new_attr.color < 8)
+ {
+ bytes[2] = '3'; bytes[3] = '0' + new_attr.color;
+ }
+ else
+ {
+ bytes[2] = '9'; bytes[3] = '0' + (new_attr.color - 8);
+ }
+ bytes[4] = 'm';
+ if (full_write (out_fd, bytes, 5) < 5)
+ out_error ();
+ }
+ break;
+ case cm_xterm88:
+ assert (new_attr.color >= 0 && new_attr.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_attr.color >= 10)
+ *p++ = '0' + (new_attr.color / 10);
+ *p++ = '0' + (new_attr.color % 10);
+ *p++ = 'm';
+ if (full_write (out_fd, bytes, p - bytes) < p - bytes)
+ out_error ();
+ }
+ break;
+ case cm_xterm256:
+ assert (new_attr.color >= 0 && new_attr.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_attr.color >= 100)
+ *p++ = '0' + (new_attr.color / 100);
+ if (new_attr.color >= 10)
+ *p++ = '0' + ((new_attr.color % 100) / 10);
+ *p++ = '0' + (new_attr.color % 10);
+ *p++ = 'm';
+ if (full_write (out_fd, bytes, p - bytes) < p - bytes)
+ out_error ();
+ }
+ break;
+ default:
+ abort ();
+ }
+ }
+ if (new_attr.bgcolor != old_attr.bgcolor
+ || (cleared_attributes && new_attr.bgcolor != COLOR_DEFAULT))
+ {
+ assert (stream->supports_background);
+ assert (new_attr.bgcolor != COLOR_DEFAULT);
+ switch (stream->colormodel)
+ {
+ case cm_common8:
+ assert (new_attr.bgcolor >= 0 && new_attr.bgcolor < 8);
+ if (stream->set_a_background != NULL)
+ tputs (tparm (stream->set_a_background,
+ color_bgr (new_attr.bgcolor)),
+ 1, out_char);
+ else
+ tputs (tparm (stream->set_background, new_attr.bgcolor),
+ 1, 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_attr.bgcolor >= 0 && new_attr.bgcolor < 8);
+ {
+ char bytes[5];
+ bytes[0] = 0x1B; bytes[1] = '[';
+ bytes[2] = '4'; bytes[3] = '0' + new_attr.bgcolor;
+ bytes[4] = 'm';
+ if (full_write (out_fd, bytes, 5) < 5)
+ out_error ();
+ }
+ break;
+ case cm_xterm16:
+ assert (new_attr.bgcolor >= 0 && new_attr.bgcolor < 16);
+ {
+ char bytes[6];
+ bytes[0] = 0x1B; bytes[1] = '[';
+ if (new_attr.bgcolor < 8)
+ {
+ bytes[2] = '4'; bytes[3] = '0' + new_attr.bgcolor;
+ bytes[4] = 'm';
+ if (full_write (out_fd, bytes, 5) < 5)
+ out_error ();
+ }
+ else
+ {
+ bytes[2] = '1'; bytes[3] = '0';
+ bytes[4] = '0' + (new_attr.bgcolor - 8); bytes[5] = 'm';
+ if (full_write (out_fd, bytes, 6) < 6)
+ out_error ();
+ }
+ }
+ break;
+ case cm_xterm88:
+ assert (new_attr.bgcolor >= 0 && new_attr.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_attr.bgcolor >= 10)
+ *p++ = '0' + (new_attr.bgcolor / 10);
+ *p++ = '0' + (new_attr.bgcolor % 10);
+ *p++ = 'm';
+ if (full_write (out_fd, bytes, p - bytes) < p - bytes)
+ out_error ();
+ }
+ break;
+ case cm_xterm256:
+ assert (new_attr.bgcolor >= 0 && new_attr.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_attr.bgcolor >= 100)
+ *p++ = '0' + (new_attr.bgcolor / 100);
+ if (new_attr.bgcolor >= 10)
+ *p++ = '0' + ((new_attr.bgcolor % 100) / 10);
+ *p++ = '0' + (new_attr.bgcolor % 10);
+ *p++ = 'm';
+ if (full_write (out_fd, bytes, p - bytes) < p - bytes)
+ out_error ();
+ }
+ break;
+ default:
+ abort ();
+ }
+ }
+
+ if (new_attr.weight != old_attr.weight
+ || (cleared_attributes && new_attr.weight != WEIGHT_DEFAULT))
+ {
+ assert (stream->supports_weight);
+ assert (new_attr.weight != WEIGHT_DEFAULT);
+ /* This implies: */
+ assert (new_attr.weight == WEIGHT_BOLD);
+ tputs (stream->enter_bold_mode, 1, out_char);
+ }
+ if (new_attr.posture != old_attr.posture
+ || (cleared_attributes && new_attr.posture != POSTURE_DEFAULT))
+ {
+ assert (stream->supports_posture);
+ assert (new_attr.posture != POSTURE_DEFAULT);
+ /* This implies: */
+ assert (new_attr.posture == POSTURE_ITALIC);
+ tputs (stream->enter_italics_mode, 1, out_char);
+ }
+ if (new_attr.underline != old_attr.underline
+ || (cleared_attributes && new_attr.underline != UNDERLINE_DEFAULT))
+ {
+ assert (stream->supports_underline);
+ assert (new_attr.underline != UNDERLINE_DEFAULT);
+ /* This implies: */
+ assert (new_attr.underline == UNDERLINE_ON);
+ tputs (stream->enter_underline_mode, 1, out_char);
+ }
+}
+
+/* Output the buffered line atomically.
+ The terminal is assumed to have the default state (regarding colors and
+ attributes) before this call. It is left in default state after this
+ call (regardless of stream->curr_attr). */
+static void
+output_buffer (term_ostream_t stream)
+{
+ attributes_t default_attr;
+ attributes_t attr;
+ const char *cp;
+ const attributes_t *ap;
+ size_t len;
+ size_t n;
+
+ default_attr.color = COLOR_DEFAULT;
+ default_attr.bgcolor = COLOR_DEFAULT;
+ default_attr.weight = WEIGHT_DEFAULT;
+ default_attr.posture = POSTURE_DEFAULT;
+ default_attr.underline = UNDERLINE_DEFAULT;
+
+ attr = default_attr;
+
+ 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], attr); n++)
+ ;
+ if (n > 0)
+ {
+ if (full_write (stream->fd, cp, n) < n)
+ error (EXIT_FAILURE, errno, _("error writing to %s"), stream->filename);
+ cp += n;
+ ap += n;
+ len -= n;
+ }
+ if (len > 0)
+ {
+ /* Block fatal signals, so that a SIGINT or similar doesn't interrupt
+ us without the possibility of restoring the terminal's state. */
+ block_fatal_signals ();
+ /* Likewise for SIGTSTP etc. */
+ block_stopping_signals ();
+
+ /* Enable the exit handler for restoring the terminal's state. */
+ restore_colors =
+ (stream->supports_foreground || stream->supports_background
+ ? stream->orig_pair
+ : NULL);
+ restore_weight =
+ (stream->supports_weight ? stream->exit_attribute_mode : NULL);
+ restore_posture =
+ (stream->supports_posture
+ ? (stream->exit_italics_mode != NULL
+ ? stream->exit_italics_mode
+ : stream->exit_attribute_mode)
+ : NULL);
+ restore_underline =
+ (stream->supports_underline
+ ? (stream->exit_underline_mode != NULL
+ ? stream->exit_underline_mode
+ : stream->exit_attribute_mode)
+ : NULL);
+ out_fd = stream->fd;
+ out_filename = stream->filename;
+
+ while (len > 0)
+ {
+ /* Activate the attributes in *ap. */
+ out_attr_change (stream, attr, *ap);
+ attr = *ap;
+ /* See how many characters we can output without further attribute
+ changes. */
+ for (n = 1; n < len && equal_attributes (ap[n], attr); n++)
+ ;
+ if (full_write (stream->fd, cp, n) < n)
+ error (EXIT_FAILURE, errno, _("error writing to %s"),
+ stream->filename);
+ cp += n;
+ ap += n;
+ len -= n;
+ }
+
+ /* Switch back to the default attributes. */
+ out_attr_change (stream, attr, default_attr);
+
+ /* Disable the exit handler. */
+ out_fd = -1;
+ out_filename = NULL;
+
+ /* Unblock fatal and stopping signals. */
+ unblock_stopping_signals ();
+ unblock_fatal_signals ();
+ }
+ stream->buflen = 0;
+}
+
+/* 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);
+ 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);
+ 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)
+{
+ output_buffer (stream);
+}
+
+static void
+term_ostream::free (term_ostream_t stream)
+{
+ term_ostream_flush (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);
+ free (stream->buffer);
+ 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);
+}
+
+/* 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);
+}
+
+term_ostream_t
+term_ostream_create (int fd, const char *filename)
+{
+ term_ostream_t stream = XMALLOC (struct term_ostream_representation);
+ const char *term;
+
+ stream->base.vtable = &term_ostream_vtable;
+ stream->fd = fd;
+ stream->filename = xstrdup (filename);
+
+ /* 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;
+
+ /* 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) || 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
+ struct { char buf[1024]; 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)
+ {
+ struct { char buf[1024]; 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 balues 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 rxvt-16color. */
+ (strlen (term) >= 4 && memcmp (term, "rxvt", 7) == 0)
+ || /* Recognize konsole-16color. */
+ (strlen (term) >= 7 && memcmp (term, "konsole", 7) == 0))
+ ? (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));
+
+ /* 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. */
+ stream->curr_attr.color = COLOR_DEFAULT;
+ stream->curr_attr.bgcolor = COLOR_DEFAULT;
+ stream->curr_attr.weight = WEIGHT_DEFAULT;
+ stream->curr_attr.posture = POSTURE_DEFAULT;
+ stream->curr_attr.underline = UNDERLINE_DEFAULT;
+ stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
+
+ /* Register an exit handler. */
+ {
+ static bool registered = false;
+ if (!registered)
+ {
+ atexit (restore);
+ registered = true;
+ }
+ }
+
+ return stream;
+}