1 /* Output stream for attributed text, producing ANSI escape sequences.
2 Copyright (C) 2006-2008 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2006.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21 #include "term-ostream.h"
31 #include "fatal-signal.h"
32 #include "full-write.h"
38 #define _(str) gettext (str)
41 /* GNU termcap's tparam() function requires a buffer argument. Make it so
42 large that there is no risk that tparam() needs to call malloc(). */
43 static char tparambuf[100];
44 /* Define tparm in terms of tparam. In the scope of this file, it is called
45 with at most one argument after the string. */
46 # define tparm(str, arg1) \
47 tparam (str, tparambuf, sizeof (tparambuf), arg1)
50 #define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
53 /* =========================== Color primitives =========================== */
55 /* A color in RGB format. */
58 unsigned int red : 8; /* range 0..255 */
59 unsigned int green : 8; /* range 0..255 */
60 unsigned int blue : 8; /* range 0..255 */
63 /* A color in HSV (a.k.a. HSB) format. */
66 float hue; /* normalized to interval [0,6) */
67 float saturation; /* normalized to interval [0,1] */
68 float brightness; /* a.k.a. value, normalized to interval [0,1] */
71 /* Conversion of a color in RGB to HSV format. */
73 rgb_to_hsv (rgb_t c, hsv_t *result)
75 unsigned int r = c.red;
76 unsigned int g = c.green;
77 unsigned int b = c.blue;
83 /* b > r > g, so max = b, min = g */
84 result->hue = 4.0f + (float) (r - g) / (float) (b - g);
85 result->saturation = 1.0f - (float) g / (float) b;
86 result->brightness = (float) b / 255.0f;
90 /* r > g >= b, so max = r, min = b */
91 result->hue = 0.0f + (float) (g - b) / (float) (r - b);
92 result->saturation = 1.0f - (float) b / (float) r;
93 result->brightness = (float) r / 255.0f;
97 /* r >= b > g, so max = r, min = g */
98 result->hue = 6.0f - (float) (b - g) / (float) (r - g);
99 result->saturation = 1.0f - (float) g / (float) r;
100 result->brightness = (float) r / 255.0f;
107 /* b > g >= r, so max = b, min = r */
108 result->hue = 4.0f - (float) (g - r) / (float) (b - r);
109 result->saturation = 1.0f - (float) r / (float) b;
110 result->brightness = (float) b / 255.0f;
114 /* g >= r > b, so max = g, min = b */
115 result->hue = 2.0f - (float) (r - b) / (float) (g - b);
116 result->saturation = 1.0f - (float) b / (float) g;
117 result->brightness = (float) g / 255.0f;
121 /* g >= b >= r, g > r, so max = g, min = r */
122 result->hue = 2.0f + (float) (b - r) / (float) (g - r);
123 result->saturation = 1.0f - (float) r / (float) g;
124 result->brightness = (float) g / 255.0f;
128 /* r = g = b. A grey color. */
129 result->hue = 0; /* arbitrary */
130 result->saturation = 0;
131 result->brightness = (float) r / 255.0f;
136 /* Square of distance of two colors. */
138 color_distance (const hsv_t *color1, const hsv_t *color2)
141 /* Formula taken from "John Smith: Color Similarity",
142 http://www.ctr.columbia.edu/~jrsmith/html/pubs/acmmm96/node8.html. */
143 float angle1 = color1->hue * 1.04719755f; /* normalize to [0,2π] */
144 float angle2 = color2->hue * 1.04719755f; /* normalize to [0,2π] */
145 float delta_x = color1->saturation * cosf (angle1)
146 - color2->saturation * cosf (angle2);
147 float delta_y = color1->saturation * sinf (angle1)
148 - color2->saturation * sinf (angle2);
149 float delta_v = color1->brightness
150 - color2->brightness;
152 return delta_x * delta_x + delta_y * delta_y + delta_v * delta_v;
154 /* Formula that considers hue differences with more weight than saturation
155 or brightness differences, like the human eye does. */
157 (color1->hue >= color2->hue
158 ? (color1->hue - color2->hue >= 3.0f
159 ? 6.0f + color2->hue - color1->hue
160 : color1->hue - color2->hue)
161 : (color2->hue - color1->hue >= 3.0f
162 ? 6.0f + color1->hue - color2->hue
163 : color2->hue - color1->hue));
164 float min_saturation =
165 (color1->saturation < color2->saturation
167 : color2->saturation);
168 float delta_saturation = color1->saturation - color2->saturation;
169 float delta_brightness = color1->brightness - color2->brightness;
171 return delta_hue * delta_hue * min_saturation
172 + delta_saturation * delta_saturation * 0.2f
173 + delta_brightness * delta_brightness * 0.8f;
177 /* Return the index of the color in a color table that is nearest to a given
180 nearest_color (rgb_t given, const rgb_t *table, unsigned int table_size)
183 unsigned int best_index;
187 assert (table_size > 0);
189 rgb_to_hsv (given, &given_hsv);
192 best_distance = 1000000.0f;
193 for (i = 0; i < table_size; i++)
197 rgb_to_hsv (table[i], &i_hsv);
199 /* Avoid converting a color to grey, or fading out a color too much. */
200 if (i_hsv.saturation > given_hsv.saturation * 0.5f)
202 float distance = color_distance (&given_hsv, &i_hsv);
203 if (distance < best_distance)
206 best_distance = distance;
211 #if 0 /* Debugging code */
213 rgb_to_hsv (table[best_index], &best_hsv);
214 fprintf (stderr, "nearest: (%d,%d,%d) = (%f,%f,%f)\n -> (%f,%f,%f) = (%d,%d,%d)\n",
215 given.red, given.green, given.blue,
216 (double)given_hsv.hue, (double)given_hsv.saturation, (double)given_hsv.brightness,
217 (double)best_hsv.hue, (double)best_hsv.saturation, (double)best_hsv.brightness,
218 table[best_index].red, table[best_index].green, table[best_index].blue);
224 /* The luminance of a color. This is the brightness of the color, as it
225 appears to the human eye. This must be used in color to grey conversion. */
227 color_luminance (int r, int g, int b)
229 /* Use the luminance model used by NTSC and JPEG.
230 Taken from http://www.fho-emden.de/~hoffmann/gray10012001.pdf .
231 No need to care about rounding errors leading to luminance > 1;
232 this cannot happen. */
233 return (0.299f * r + 0.587f * g + 0.114f * b) / 255.0f;
237 /* ============================= Color models ============================= */
239 /* The color model used by the terminal. */
242 cm_monochrome, /* No colors. */
243 cm_common8, /* Usual terminal with at least 8 colors. */
244 cm_xterm8, /* TERM=xterm, with 8 colors. */
245 cm_xterm16, /* TERM=xterm-16color, with 16 colors. */
246 cm_xterm88, /* TERM=xterm-88color, with 88 colors. */
247 cm_xterm256 /* TERM=xterm-256color, with 256 colors. */
250 /* ----------------------- cm_monochrome color model ----------------------- */
252 /* A non-default color index doesn't exist in this color model. */
253 static inline term_color_t
254 rgb_to_color_monochrome ()
256 return COLOR_DEFAULT;
259 /* ------------------------ cm_common8 color model ------------------------ */
261 /* A non-default color index is in the range 0..7.
271 static const rgb_t colors_of_common8[8] =
273 /* R G B grey index */
274 { 0, 0, 0 }, /* 0.000 0 */
281 { 255, 255, 255 } /* 1.000 7 */
284 static inline term_color_t
285 rgb_to_color_common8 (int r, int g, int b)
290 color.red = r; color.green = g; color.blue = b;
291 rgb_to_hsv (color, &hsv);
293 if (hsv.saturation < 0.065f)
295 /* Greyscale approximation. */
296 float luminance = color_luminance (r, g, b);
297 if (luminance < 0.500f)
303 /* Color approximation. */
304 return nearest_color (color, colors_of_common8, 8);
307 /* Convert a cm_common8 color in RGB encoding to BGR encoding.
308 See the ncurses terminfo(5) manual page, section "Color Handling", for an
309 explanation why this is needed. */
311 color_bgr (term_color_t color)
313 return ((color & 4) >> 2) | (color & 2) | ((color & 1) << 2);
316 /* ------------------------- cm_xterm8 color model ------------------------- */
318 /* A non-default color index is in the range 0..7.
328 static const rgb_t colors_of_xterm8[8] =
330 /* The real xterm's colors are dimmed; assume full-brightness instead. */
331 /* R G B grey index */
332 { 0, 0, 0 }, /* 0.000 0 */
339 { 255, 255, 255 } /* 1.000 7 */
342 static inline term_color_t
343 rgb_to_color_xterm8 (int r, int g, int b)
348 color.red = r; color.green = g; color.blue = b;
349 rgb_to_hsv (color, &hsv);
351 if (hsv.saturation < 0.065f)
353 /* Greyscale approximation. */
354 float luminance = color_luminance (r, g, b);
355 if (luminance < 0.500f)
361 /* Color approximation. */
362 return nearest_color (color, colors_of_xterm8, 8);
365 /* ------------------------ cm_xterm16 color model ------------------------ */
367 /* A non-default color index is in the range 0..15.
368 The RGB values come from xterm's XTerm-col.ad. */
369 static const rgb_t colors_of_xterm16[16] =
371 /* R G B grey index */
372 { 0, 0, 0 }, /* 0.000 0 */
379 { 229, 229, 229 }, /* 0.898 7 */
380 { 77, 77, 77 }, /* 0.302 8 */
387 { 255, 255, 255 } /* 1.000 15 */
390 static inline term_color_t
391 rgb_to_color_xterm16 (int r, int g, int b)
396 color.red = r; color.green = g; color.blue = b;
397 rgb_to_hsv (color, &hsv);
399 if (hsv.saturation < 0.065f)
401 /* Greyscale approximation. */
402 float luminance = color_luminance (r, g, b);
403 if (luminance < 0.151f)
405 else if (luminance < 0.600f)
407 else if (luminance < 0.949f)
413 /* Color approximation. */
414 return nearest_color (color, colors_of_xterm16, 16);
417 /* ------------------------ cm_xterm88 color model ------------------------ */
419 /* A non-default color index is in the range 0..87.
420 Colors 0..15 are the same as in the cm_xterm16 color model.
421 Colors 16..87 are defined in xterm's 88colres.h. */
423 static const rgb_t colors_of_xterm88[88] =
425 /* R G B grey index */
426 { 0, 0, 0 }, /* 0.000 0 */
433 { 229, 229, 229 }, /* 0.898 7 */
434 { 77, 77, 77 }, /* 0.302 8 */
441 { 255, 255, 255 }, /* 1.000 15 */
442 { 0, 0, 0 }, /* 0.000 16 */
463 { 139, 139, 139 }, /* 0.545 37 */
484 { 205, 205, 205 }, /* 0.804 58 */
505 { 255, 255, 255 }, /* 1.000 79 */
506 { 46, 46, 46 }, /* 0.180 80 */
507 { 92, 92, 92 }, /* 0.361 81 */
508 { 115, 115, 115 }, /* 0.451 82 */
509 { 139, 139, 139 }, /* 0.545 83 */
510 { 162, 162, 162 }, /* 0.635 84 */
511 { 185, 185, 185 }, /* 0.725 85 */
512 { 208, 208, 208 }, /* 0.816 86 */
513 { 231, 231, 231 } /* 0.906 87 */
516 static inline term_color_t
517 rgb_to_color_xterm88 (int r, int g, int b)
522 color.red = r; color.green = g; color.blue = b;
523 rgb_to_hsv (color, &hsv);
525 if (hsv.saturation < 0.065f)
527 /* Greyscale approximation. */
528 float luminance = color_luminance (r, g, b);
529 if (luminance < 0.090f)
531 else if (luminance < 0.241f)
533 else if (luminance < 0.331f)
535 else if (luminance < 0.406f)
537 else if (luminance < 0.498f)
539 else if (luminance < 0.585f)
541 else if (luminance < 0.680f)
543 else if (luminance < 0.764f)
545 else if (luminance < 0.810f)
547 else if (luminance < 0.857f)
549 else if (luminance < 0.902f)
551 else if (luminance < 0.953f)
557 /* Color approximation. */
558 return nearest_color (color, colors_of_xterm88, 88);
561 /* ------------------------ cm_xterm256 color model ------------------------ */
563 /* A non-default color index is in the range 0..255.
564 Colors 0..15 are the same as in the cm_xterm16 color model.
565 Colors 16..255 are defined in xterm's 256colres.h. */
567 static const rgb_t colors_of_xterm256[256] =
569 /* R G B grey index */
570 { 0, 0, 0 }, /* 0.000 0 */
577 { 229, 229, 229 }, /* 0.898 7 */
578 { 77, 77, 77 }, /* 0.302 8 */
585 { 255, 255, 255 }, /* 1.000 15 */
586 { 0, 0, 0 }, /* 0.000 16 */
629 { 42, 42, 42 }, /* 0.165 59 */
672 { 85, 85, 85 }, /* 0.333 102 */
715 { 127, 127, 127 }, /* 0.498 145 */
758 { 170, 170, 170 }, /* 0.667 188 */
801 { 212, 212, 212 }, /* 0.831 231 */
802 { 8, 8, 8 }, /* 0.031 232 */
803 { 18, 18, 18 }, /* 0.071 233 */
804 { 28, 28, 28 }, /* 0.110 234 */
805 { 38, 38, 38 }, /* 0.149 235 */
806 { 48, 48, 48 }, /* 0.188 236 */
807 { 58, 58, 58 }, /* 0.227 237 */
808 { 68, 68, 68 }, /* 0.267 238 */
809 { 78, 78, 78 }, /* 0.306 239 */
810 { 88, 88, 88 }, /* 0.345 240 */
811 { 98, 98, 98 }, /* 0.384 241 */
812 { 108, 108, 108 }, /* 0.424 242 */
813 { 118, 118, 118 }, /* 0.463 243 */
814 { 128, 128, 128 }, /* 0.502 244 */
815 { 138, 138, 138 }, /* 0.541 245 */
816 { 148, 148, 148 }, /* 0.580 246 */
817 { 158, 158, 158 }, /* 0.620 247 */
818 { 168, 168, 168 }, /* 0.659 248 */
819 { 178, 178, 178 }, /* 0.698 249 */
820 { 188, 188, 188 }, /* 0.737 250 */
821 { 198, 198, 198 }, /* 0.776 251 */
822 { 208, 208, 208 }, /* 0.816 252 */
823 { 218, 218, 218 }, /* 0.855 253 */
824 { 228, 228, 228 }, /* 0.894 254 */
825 { 238, 238, 238 } /* 0.933 255 */
828 static inline term_color_t
829 rgb_to_color_xterm256 (int r, int g, int b)
834 color.red = r; color.green = g; color.blue = b;
835 rgb_to_hsv (color, &hsv);
837 if (hsv.saturation < 0.065f)
839 /* Greyscale approximation. */
840 float luminance = color_luminance (r, g, b);
841 if (luminance < 0.015f)
843 else if (luminance < 0.051f)
845 else if (luminance < 0.090f)
847 else if (luminance < 0.129f)
849 else if (luminance < 0.157f)
851 else if (luminance < 0.177f)
853 else if (luminance < 0.207f)
855 else if (luminance < 0.247f)
857 else if (luminance < 0.284f)
859 else if (luminance < 0.304f)
861 else if (luminance < 0.319f)
863 else if (luminance < 0.339f)
865 else if (luminance < 0.364f)
867 else if (luminance < 0.404f)
869 else if (luminance < 0.443f)
871 else if (luminance < 0.480f)
873 else if (luminance < 0.500f)
875 else if (luminance < 0.521f)
877 else if (luminance < 0.560f)
879 else if (luminance < 0.600f)
881 else if (luminance < 0.639f)
883 else if (luminance < 0.663f)
885 else if (luminance < 0.682f)
887 else if (luminance < 0.717f)
889 else if (luminance < 0.756f)
891 else if (luminance < 0.796f)
893 else if (luminance < 0.823f)
895 else if (luminance < 0.843f)
897 else if (luminance < 0.874f)
899 else if (luminance < 0.896f)
901 else if (luminance < 0.915f)
903 else if (luminance < 0.966f)
909 /* Color approximation. */
910 return nearest_color (color, colors_of_xterm256, 256);
914 /* ============================= attributes_t ============================= */
916 /* ANSI C and ISO C99 6.7.2.1.(4) forbid use of bit fields for types other
917 than 'int' or 'unsigned int'.
918 On the other hand, C++ forbids conversion between enum types and integer
919 types without an explicit cast. */
921 # define BITFIELD_TYPE(orig_type,integer_type) orig_type
923 # define BITFIELD_TYPE(orig_type,integer_type) integer_type
926 /* Attributes that can be set on a character. */
929 BITFIELD_TYPE(term_color_t, signed int) color : 9;
930 BITFIELD_TYPE(term_color_t, signed int) bgcolor : 9;
931 BITFIELD_TYPE(term_weight_t, unsigned int) weight : 1;
932 BITFIELD_TYPE(term_posture_t, unsigned int) posture : 1;
933 BITFIELD_TYPE(term_underline_t, unsigned int) underline : 1;
937 /* ============================ term_ostream_t ============================ */
939 struct term_ostream : struct ostream
942 /* The file descriptor used for output. Note that ncurses termcap emulation
943 uses the baud rate information from file descriptor 1 (stdout) if it is
944 a tty, or from file descriptor 2 (stderr) otherwise. */
947 /* Values from the terminal type's terminfo/termcap description.
948 See terminfo(5) for details. */
949 /* terminfo termcap */
950 int max_colors; /* colors Co */
951 int no_color_video; /* ncv NC */
952 char *set_a_foreground; /* setaf AF */
953 char *set_foreground; /* setf Sf */
954 char *set_a_background; /* setab AB */
955 char *set_background; /* setb Sb */
956 char *orig_pair; /* op op */
957 char *enter_bold_mode; /* bold md */
958 char *enter_italics_mode; /* sitm ZH */
959 char *exit_italics_mode; /* ritm ZR */
960 char *enter_underline_mode; /* smul us */
961 char *exit_underline_mode; /* rmul ue */
962 char *exit_attribute_mode; /* sgr0 me */
963 /* Inferred values. */
964 bool supports_foreground;
965 bool supports_background;
966 colormodel_t colormodel;
967 bool supports_weight;
968 bool supports_posture;
969 bool supports_underline;
970 /* Variable state. */
971 char *buffer; /* Buffer for the current line. */
972 attributes_t *attrbuffer; /* Buffer for the simplified attributes; same
974 size_t buflen; /* Number of bytes stored so far. */
975 size_t allocated; /* Allocated size of the buffer. */
976 attributes_t curr_attr; /* Current attributes. */
977 attributes_t simp_attr; /* Simplified current attributes. */
980 /* Simplify attributes, according to the terminal's capabilities. */
982 simplify_attributes (term_ostream_t stream, attributes_t attr)
984 if ((attr.color != COLOR_DEFAULT || attr.bgcolor != COLOR_DEFAULT)
985 && stream->no_color_video > 0)
987 /* When colors and attributes can not be represented simultaneously,
988 we give preference to the color. */
989 if (stream->no_color_video & 2)
990 /* Colors conflict with underlining. */
991 attr.underline = UNDERLINE_OFF;
992 if (stream->no_color_video & 32)
993 /* Colors conflict with bold weight. */
994 attr.weight = WEIGHT_NORMAL;
996 if (!stream->supports_foreground)
997 attr.color = COLOR_DEFAULT;
998 if (!stream->supports_background)
999 attr.bgcolor = COLOR_DEFAULT;
1000 if (!stream->supports_weight)
1001 attr.weight = WEIGHT_DEFAULT;
1002 if (!stream->supports_posture)
1003 attr.posture = POSTURE_DEFAULT;
1004 if (!stream->supports_underline)
1005 attr.underline = UNDERLINE_DEFAULT;
1009 /* While a line is being output, we need to be careful to restore the
1010 terminal's settings in case of a fatal signal or an exit() call. */
1012 /* File descriptor to which out_char shall output escape sequences. */
1013 static int out_fd = -1;
1015 /* Filename of out_fd. */
1016 static const char *out_filename;
1018 /* Output a single char to out_fd. Ignore errors. */
1020 out_char_unchecked (int c)
1025 full_write (out_fd, bytes, 1);
1029 /* State that informs the exit handler what to do. */
1030 static const char *restore_colors;
1031 static const char *restore_weight;
1032 static const char *restore_posture;
1033 static const char *restore_underline;
1035 /* The exit handler. */
1039 /* Only do something while some output was interrupted. */
1042 if (restore_colors != NULL)
1043 tputs (restore_colors, 1, out_char_unchecked);
1044 if (restore_weight != NULL)
1045 tputs (restore_weight, 1, out_char_unchecked);
1046 if (restore_posture != NULL)
1047 tputs (restore_posture, 1, out_char_unchecked);
1048 if (restore_underline != NULL)
1049 tputs (restore_underline, 1, out_char_unchecked);
1053 /* The list of signals whose default behaviour is to stop the program. */
1054 static int stopping_signals[] =
1068 #define num_stopping_signals (SIZEOF (stopping_signals) - 1)
1070 static sigset_t stopping_signal_set;
1073 init_stopping_signal_set ()
1075 static bool stopping_signal_set_initialized = false;
1076 if (!stopping_signal_set_initialized)
1080 sigemptyset (&stopping_signal_set);
1081 for (i = 0; i < num_stopping_signals; i++)
1082 sigaddset (&stopping_signal_set, stopping_signals[i]);
1084 stopping_signal_set_initialized = true;
1088 /* Temporarily delay the stopping signals. */
1090 block_stopping_signals ()
1092 init_stopping_signal_set ();
1093 sigprocmask (SIG_BLOCK, &stopping_signal_set, NULL);
1096 /* Stop delaying the stopping signals. */
1098 unblock_stopping_signals ()
1100 init_stopping_signal_set ();
1101 sigprocmask (SIG_UNBLOCK, &stopping_signal_set, NULL);
1104 /* Compare two sets of attributes for equality. */
1106 equal_attributes (attributes_t attr1, attributes_t attr2)
1108 return (attr1.color == attr2.color
1109 && attr1.bgcolor == attr2.bgcolor
1110 && attr1.weight == attr2.weight
1111 && attr1.posture == attr2.posture
1112 && attr1.underline == attr2.underline);
1115 /* Signal error after full_write failed. */
1119 error (EXIT_FAILURE, errno, _("error writing to %s"), out_filename);
1122 /* Output a single char to out_fd. */
1129 /* We have to write directly to the file descriptor, not to a buffer with
1130 the same destination, because of the padding and sleeping that tputs()
1132 if (full_write (out_fd, bytes, 1) < 1)
1137 /* Output escape sequences to switch from OLD_ATTR to NEW_ATTR. */
1139 out_attr_change (term_ostream_t stream,
1140 attributes_t old_attr, attributes_t new_attr)
1142 bool cleared_attributes;
1144 /* We don't know the default colors of the terminal. The only way to switch
1145 back to a default color is to use stream->orig_pair. */
1146 if ((new_attr.color == COLOR_DEFAULT && old_attr.color != COLOR_DEFAULT)
1147 || (new_attr.bgcolor == COLOR_DEFAULT && old_attr.bgcolor != COLOR_DEFAULT))
1149 assert (stream->supports_foreground || stream->supports_background);
1150 tputs (stream->orig_pair, 1, out_char);
1151 old_attr.color = COLOR_DEFAULT;
1152 old_attr.bgcolor = COLOR_DEFAULT;
1155 /* To turn off WEIGHT_BOLD, the only way is to output the exit_attribute_mode
1156 sequence. (With xterm, you can also do it with "Esc [ 0 m", but this
1157 escape sequence is not contained in the terminfo description.) It may
1158 also clear the colors; this is the case e.g. when TERM="xterm" or
1160 To turn off UNDERLINE_ON, we can use the exit_underline_mode or the
1161 exit_attribute_mode sequence. In the latter case, it will not only
1162 turn off UNDERLINE_ON, but also the other attributes, and possibly also
1164 To turn off POSTURE_ITALIC, we can use the exit_italics_mode or the
1165 exit_attribute_mode sequence. Again, in the latter case, it will not
1166 only turn off POSTURE_ITALIC, but also the other attributes, and possibly
1168 There is no point in setting an attribute just before emitting an
1169 escape sequence that may again turn off the attribute. Therefore we
1170 proceed in two steps: First, clear the attributes that need to be
1171 cleared; then - taking into account that this may have cleared all
1172 attributes and all colors - set the colors and the attributes.
1173 The variable 'cleared_attributes' tells whether an escape sequence
1174 has been output that may have cleared all attributes and all color
1176 cleared_attributes = false;
1177 if (old_attr.posture != POSTURE_NORMAL
1178 && new_attr.posture == POSTURE_NORMAL
1179 && stream->exit_italics_mode != NULL)
1181 tputs (stream->exit_italics_mode, 1, out_char);
1182 old_attr.posture = POSTURE_NORMAL;
1183 cleared_attributes = true;
1185 if (old_attr.underline != UNDERLINE_OFF
1186 && new_attr.underline == UNDERLINE_OFF
1187 && stream->exit_underline_mode != NULL)
1189 tputs (stream->exit_underline_mode, 1, out_char);
1190 old_attr.underline = UNDERLINE_OFF;
1191 cleared_attributes = true;
1193 if ((old_attr.weight != WEIGHT_NORMAL
1194 && new_attr.weight == WEIGHT_NORMAL)
1195 || (old_attr.posture != POSTURE_NORMAL
1196 && new_attr.posture == POSTURE_NORMAL
1197 /* implies stream->exit_italics_mode == NULL */)
1198 || (old_attr.underline != UNDERLINE_OFF
1199 && new_attr.underline == UNDERLINE_OFF
1200 /* implies stream->exit_underline_mode == NULL */))
1202 tputs (stream->exit_attribute_mode, 1, out_char);
1203 /* We don't know exactly what effects exit_attribute_mode has, but
1204 this is the minimum effect: */
1205 old_attr.weight = WEIGHT_NORMAL;
1206 if (stream->exit_italics_mode == NULL)
1207 old_attr.posture = POSTURE_NORMAL;
1208 if (stream->exit_underline_mode == NULL)
1209 old_attr.underline = UNDERLINE_OFF;
1210 cleared_attributes = true;
1213 /* Turn on the colors. */
1214 if (new_attr.color != old_attr.color
1215 || (cleared_attributes && new_attr.color != COLOR_DEFAULT))
1217 assert (stream->supports_foreground);
1218 assert (new_attr.color != COLOR_DEFAULT);
1219 switch (stream->colormodel)
1222 assert (new_attr.color >= 0 && new_attr.color < 8);
1223 if (stream->set_a_foreground != NULL)
1224 tputs (tparm (stream->set_a_foreground,
1225 color_bgr (new_attr.color)),
1228 tputs (tparm (stream->set_foreground, new_attr.color),
1231 /* When we are dealing with an xterm, there is no need to go through
1232 tputs() because we know there is no padding and sleeping. */
1234 assert (new_attr.color >= 0 && new_attr.color < 8);
1237 bytes[0] = 0x1B; bytes[1] = '[';
1238 bytes[2] = '3'; bytes[3] = '0' + new_attr.color;
1240 if (full_write (out_fd, bytes, 5) < 5)
1245 assert (new_attr.color >= 0 && new_attr.color < 16);
1248 bytes[0] = 0x1B; bytes[1] = '[';
1249 if (new_attr.color < 8)
1251 bytes[2] = '3'; bytes[3] = '0' + new_attr.color;
1255 bytes[2] = '9'; bytes[3] = '0' + (new_attr.color - 8);
1258 if (full_write (out_fd, bytes, 5) < 5)
1263 assert (new_attr.color >= 0 && new_attr.color < 88);
1267 bytes[0] = 0x1B; bytes[1] = '[';
1268 bytes[2] = '3'; bytes[3] = '8'; bytes[4] = ';';
1269 bytes[5] = '5'; bytes[6] = ';';
1271 if (new_attr.color >= 10)
1272 *p++ = '0' + (new_attr.color / 10);
1273 *p++ = '0' + (new_attr.color % 10);
1275 if (full_write (out_fd, bytes, p - bytes) < p - bytes)
1280 assert (new_attr.color >= 0 && new_attr.color < 256);
1284 bytes[0] = 0x1B; bytes[1] = '[';
1285 bytes[2] = '3'; bytes[3] = '8'; bytes[4] = ';';
1286 bytes[5] = '5'; bytes[6] = ';';
1288 if (new_attr.color >= 100)
1289 *p++ = '0' + (new_attr.color / 100);
1290 if (new_attr.color >= 10)
1291 *p++ = '0' + ((new_attr.color % 100) / 10);
1292 *p++ = '0' + (new_attr.color % 10);
1294 if (full_write (out_fd, bytes, p - bytes) < p - bytes)
1302 if (new_attr.bgcolor != old_attr.bgcolor
1303 || (cleared_attributes && new_attr.bgcolor != COLOR_DEFAULT))
1305 assert (stream->supports_background);
1306 assert (new_attr.bgcolor != COLOR_DEFAULT);
1307 switch (stream->colormodel)
1310 assert (new_attr.bgcolor >= 0 && new_attr.bgcolor < 8);
1311 if (stream->set_a_background != NULL)
1312 tputs (tparm (stream->set_a_background,
1313 color_bgr (new_attr.bgcolor)),
1316 tputs (tparm (stream->set_background, new_attr.bgcolor),
1319 /* When we are dealing with an xterm, there is no need to go through
1320 tputs() because we know there is no padding and sleeping. */
1322 assert (new_attr.bgcolor >= 0 && new_attr.bgcolor < 8);
1325 bytes[0] = 0x1B; bytes[1] = '[';
1326 bytes[2] = '4'; bytes[3] = '0' + new_attr.bgcolor;
1328 if (full_write (out_fd, bytes, 5) < 5)
1333 assert (new_attr.bgcolor >= 0 && new_attr.bgcolor < 16);
1336 bytes[0] = 0x1B; bytes[1] = '[';
1337 if (new_attr.bgcolor < 8)
1339 bytes[2] = '4'; bytes[3] = '0' + new_attr.bgcolor;
1341 if (full_write (out_fd, bytes, 5) < 5)
1346 bytes[2] = '1'; bytes[3] = '0';
1347 bytes[4] = '0' + (new_attr.bgcolor - 8); bytes[5] = 'm';
1348 if (full_write (out_fd, bytes, 6) < 6)
1354 assert (new_attr.bgcolor >= 0 && new_attr.bgcolor < 88);
1358 bytes[0] = 0x1B; bytes[1] = '[';
1359 bytes[2] = '4'; bytes[3] = '8'; bytes[4] = ';';
1360 bytes[5] = '5'; bytes[6] = ';';
1362 if (new_attr.bgcolor >= 10)
1363 *p++ = '0' + (new_attr.bgcolor / 10);
1364 *p++ = '0' + (new_attr.bgcolor % 10);
1366 if (full_write (out_fd, bytes, p - bytes) < p - bytes)
1371 assert (new_attr.bgcolor >= 0 && new_attr.bgcolor < 256);
1375 bytes[0] = 0x1B; bytes[1] = '[';
1376 bytes[2] = '4'; bytes[3] = '8'; bytes[4] = ';';
1377 bytes[5] = '5'; bytes[6] = ';';
1379 if (new_attr.bgcolor >= 100)
1380 *p++ = '0' + (new_attr.bgcolor / 100);
1381 if (new_attr.bgcolor >= 10)
1382 *p++ = '0' + ((new_attr.bgcolor % 100) / 10);
1383 *p++ = '0' + (new_attr.bgcolor % 10);
1385 if (full_write (out_fd, bytes, p - bytes) < p - bytes)
1394 if (new_attr.weight != old_attr.weight
1395 || (cleared_attributes && new_attr.weight != WEIGHT_DEFAULT))
1397 assert (stream->supports_weight);
1398 assert (new_attr.weight != WEIGHT_DEFAULT);
1400 assert (new_attr.weight == WEIGHT_BOLD);
1401 tputs (stream->enter_bold_mode, 1, out_char);
1403 if (new_attr.posture != old_attr.posture
1404 || (cleared_attributes && new_attr.posture != POSTURE_DEFAULT))
1406 assert (stream->supports_posture);
1407 assert (new_attr.posture != POSTURE_DEFAULT);
1409 assert (new_attr.posture == POSTURE_ITALIC);
1410 tputs (stream->enter_italics_mode, 1, out_char);
1412 if (new_attr.underline != old_attr.underline
1413 || (cleared_attributes && new_attr.underline != UNDERLINE_DEFAULT))
1415 assert (stream->supports_underline);
1416 assert (new_attr.underline != UNDERLINE_DEFAULT);
1418 assert (new_attr.underline == UNDERLINE_ON);
1419 tputs (stream->enter_underline_mode, 1, out_char);
1423 /* Output the buffered line atomically.
1424 The terminal is assumed to have the default state (regarding colors and
1425 attributes) before this call. It is left in default state after this
1426 call (regardless of stream->curr_attr). */
1428 output_buffer (term_ostream_t stream)
1430 attributes_t default_attr;
1433 const attributes_t *ap;
1437 default_attr.color = COLOR_DEFAULT;
1438 default_attr.bgcolor = COLOR_DEFAULT;
1439 default_attr.weight = WEIGHT_DEFAULT;
1440 default_attr.posture = POSTURE_DEFAULT;
1441 default_attr.underline = UNDERLINE_DEFAULT;
1443 attr = default_attr;
1445 cp = stream->buffer;
1446 ap = stream->attrbuffer;
1447 len = stream->buflen;
1449 /* See how much we can output without blocking signals. */
1450 for (n = 0; n < len && equal_attributes (ap[n], attr); n++)
1454 if (full_write (stream->fd, cp, n) < n)
1455 error (EXIT_FAILURE, errno, _("error writing to %s"), stream->filename);
1462 /* Block fatal signals, so that a SIGINT or similar doesn't interrupt
1463 us without the possibility of restoring the terminal's state. */
1464 block_fatal_signals ();
1465 /* Likewise for SIGTSTP etc. */
1466 block_stopping_signals ();
1468 /* Enable the exit handler for restoring the terminal's state. */
1470 (stream->supports_foreground || stream->supports_background
1474 (stream->supports_weight ? stream->exit_attribute_mode : NULL);
1476 (stream->supports_posture
1477 ? (stream->exit_italics_mode != NULL
1478 ? stream->exit_italics_mode
1479 : stream->exit_attribute_mode)
1482 (stream->supports_underline
1483 ? (stream->exit_underline_mode != NULL
1484 ? stream->exit_underline_mode
1485 : stream->exit_attribute_mode)
1487 out_fd = stream->fd;
1488 out_filename = stream->filename;
1492 /* Activate the attributes in *ap. */
1493 out_attr_change (stream, attr, *ap);
1495 /* See how many characters we can output without further attribute
1497 for (n = 1; n < len && equal_attributes (ap[n], attr); n++)
1499 if (full_write (stream->fd, cp, n) < n)
1500 error (EXIT_FAILURE, errno, _("error writing to %s"),
1507 /* Switch back to the default attributes. */
1508 out_attr_change (stream, attr, default_attr);
1510 /* Disable the exit handler. */
1512 out_filename = NULL;
1514 /* Unblock fatal and stopping signals. */
1515 unblock_stopping_signals ();
1516 unblock_fatal_signals ();
1521 /* Implementation of ostream_t methods. */
1524 term_ostream::rgb_to_color (term_ostream_t stream, int red, int green, int blue)
1526 switch (stream->colormodel)
1529 return rgb_to_color_monochrome ();
1531 return rgb_to_color_common8 (red, green, blue);
1533 return rgb_to_color_xterm8 (red, green, blue);
1535 return rgb_to_color_xterm16 (red, green, blue);
1537 return rgb_to_color_xterm88 (red, green, blue);
1539 return rgb_to_color_xterm256 (red, green, blue);
1546 term_ostream::write_mem (term_ostream_t stream, const void *data, size_t len)
1548 const char *cp = (const char *) data;
1551 /* Look for the next newline. */
1552 const char *newline = (const char *) memchr (cp, '\n', len);
1553 size_t n = (newline != NULL ? newline - cp : len);
1555 /* Copy n bytes into the buffer. */
1556 if (n > stream->allocated - stream->buflen)
1558 size_t new_allocated =
1559 xmax (xsum (stream->buflen, n),
1560 xsum (stream->allocated, stream->allocated));
1561 if (size_overflow_p (new_allocated))
1562 error (EXIT_FAILURE, 0,
1563 _("%s: too much output, buffer size overflow"),
1565 stream->buffer = (char *) xrealloc (stream->buffer, new_allocated);
1566 stream->attrbuffer =
1568 xrealloc (stream->attrbuffer,
1569 new_allocated * sizeof (attributes_t));
1570 stream->allocated = new_allocated;
1572 memcpy (stream->buffer + stream->buflen, cp, n);
1574 attributes_t attr = stream->simp_attr;
1575 attributes_t *ap = stream->attrbuffer + stream->buflen;
1576 attributes_t *ap_end = ap + n;
1577 for (; ap < ap_end; ap++)
1580 stream->buflen += n;
1582 if (newline != NULL)
1584 output_buffer (stream);
1585 if (full_write (stream->fd, "\n", 1) < 1)
1586 error (EXIT_FAILURE, errno, _("error writing to %s"),
1588 cp += n + 1; /* cp = newline + 1; */
1597 term_ostream::flush (term_ostream_t stream)
1599 output_buffer (stream);
1603 term_ostream::free (term_ostream_t stream)
1605 term_ostream_flush (stream);
1606 free (stream->filename);
1607 if (stream->set_a_foreground != NULL)
1608 free (stream->set_a_foreground);
1609 if (stream->set_foreground != NULL)
1610 free (stream->set_foreground);
1611 if (stream->set_a_background != NULL)
1612 free (stream->set_a_background);
1613 if (stream->set_background != NULL)
1614 free (stream->set_background);
1615 if (stream->orig_pair != NULL)
1616 free (stream->orig_pair);
1617 if (stream->enter_bold_mode != NULL)
1618 free (stream->enter_bold_mode);
1619 if (stream->enter_italics_mode != NULL)
1620 free (stream->enter_italics_mode);
1621 if (stream->exit_italics_mode != NULL)
1622 free (stream->exit_italics_mode);
1623 if (stream->enter_underline_mode != NULL)
1624 free (stream->enter_underline_mode);
1625 if (stream->exit_underline_mode != NULL)
1626 free (stream->exit_underline_mode);
1627 if (stream->exit_attribute_mode != NULL)
1628 free (stream->exit_attribute_mode);
1629 free (stream->buffer);
1633 /* Implementation of term_ostream_t methods. */
1636 term_ostream::get_color (term_ostream_t stream)
1638 return stream->curr_attr.color;
1642 term_ostream::set_color (term_ostream_t stream, term_color_t color)
1644 stream->curr_attr.color = color;
1645 stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
1649 term_ostream::get_bgcolor (term_ostream_t stream)
1651 return stream->curr_attr.bgcolor;
1655 term_ostream::set_bgcolor (term_ostream_t stream, term_color_t color)
1657 stream->curr_attr.bgcolor = color;
1658 stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
1661 static term_weight_t
1662 term_ostream::get_weight (term_ostream_t stream)
1664 return stream->curr_attr.weight;
1668 term_ostream::set_weight (term_ostream_t stream, term_weight_t weight)
1670 stream->curr_attr.weight = weight;
1671 stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
1674 static term_posture_t
1675 term_ostream::get_posture (term_ostream_t stream)
1677 return stream->curr_attr.posture;
1681 term_ostream::set_posture (term_ostream_t stream, term_posture_t posture)
1683 stream->curr_attr.posture = posture;
1684 stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
1687 static term_underline_t
1688 term_ostream::get_underline (term_ostream_t stream)
1690 return stream->curr_attr.underline;
1694 term_ostream::set_underline (term_ostream_t stream, term_underline_t underline)
1696 stream->curr_attr.underline = underline;
1697 stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
1702 static inline char *
1703 xstrdup0 (const char *str)
1708 if (str == (const char *)(-1))
1711 return xstrdup (str);
1715 term_ostream_create (int fd, const char *filename)
1717 term_ostream_t stream = XMALLOC (struct term_ostream_representation);
1720 stream->base.vtable = &term_ostream_vtable;
1722 stream->filename = xstrdup (filename);
1725 stream->max_colors = -1;
1726 stream->no_color_video = -1;
1727 stream->set_a_foreground = NULL;
1728 stream->set_foreground = NULL;
1729 stream->set_a_background = NULL;
1730 stream->set_background = NULL;
1731 stream->orig_pair = NULL;
1732 stream->enter_bold_mode = NULL;
1733 stream->enter_italics_mode = NULL;
1734 stream->exit_italics_mode = NULL;
1735 stream->enter_underline_mode = NULL;
1736 stream->exit_underline_mode = NULL;
1737 stream->exit_attribute_mode = NULL;
1739 /* Retrieve the terminal type. */
1740 term = getenv ("TERM");
1741 if (term != NULL && term[0] != '\0')
1743 /* When the terminfo function are available, we prefer them over the
1744 termcap functions because
1745 1. they don't risk a buffer overflow,
1746 2. on OSF/1, for TERM=xterm, the tiget* functions provide access
1747 to the number of colors and the color escape sequences, whereas
1748 the tget* functions don't provide them. */
1752 if (setupterm (term, fd, &err) || err == 1)
1754 /* Retrieve particular values depending on the terminal type. */
1755 stream->max_colors = tigetnum ("colors");
1756 stream->no_color_video = tigetnum ("ncv");
1757 stream->set_a_foreground = xstrdup0 (tigetstr ("setaf"));
1758 stream->set_foreground = xstrdup0 (tigetstr ("setf"));
1759 stream->set_a_background = xstrdup0 (tigetstr ("setab"));
1760 stream->set_background = xstrdup0 (tigetstr ("setb"));
1761 stream->orig_pair = xstrdup0 (tigetstr ("op"));
1762 stream->enter_bold_mode = xstrdup0 (tigetstr ("bold"));
1763 stream->enter_italics_mode = xstrdup0 (tigetstr ("sitm"));
1764 stream->exit_italics_mode = xstrdup0 (tigetstr ("ritm"));
1765 stream->enter_underline_mode = xstrdup0 (tigetstr ("smul"));
1766 stream->exit_underline_mode = xstrdup0 (tigetstr ("rmul"));
1767 stream->exit_attribute_mode = xstrdup0 (tigetstr ("sgr0"));
1770 struct { char buf[1024]; char canary[4]; } termcapbuf;
1773 /* Call tgetent, being defensive against buffer overflow. */
1774 memcpy (termcapbuf.canary, "CnRy", 4);
1775 retval = tgetent (termcapbuf.buf, term);
1776 if (memcmp (termcapbuf.canary, "CnRy", 4) != 0)
1777 /* Buffer overflow! */
1782 struct { char buf[1024]; char canary[4]; } termentrybuf;
1785 /* Prepare for calling tgetstr, being defensive against buffer
1786 overflow. ncurses' tgetstr() supports a second argument NULL,
1787 but NetBSD's tgetstr() doesn't. */
1788 memcpy (termentrybuf.canary, "CnRz", 4);
1789 #define TEBP ((termentryptr = termentrybuf.buf), &termentryptr)
1791 /* Retrieve particular values depending on the terminal type. */
1792 stream->max_colors = tgetnum ("Co");
1793 stream->no_color_video = tgetnum ("NC");
1794 stream->set_a_foreground = xstrdup0 (tgetstr ("AF", TEBP));
1795 stream->set_foreground = xstrdup0 (tgetstr ("Sf", TEBP));
1796 stream->set_a_background = xstrdup0 (tgetstr ("AB", TEBP));
1797 stream->set_background = xstrdup0 (tgetstr ("Sb", TEBP));
1798 stream->orig_pair = xstrdup0 (tgetstr ("op", TEBP));
1799 stream->enter_bold_mode = xstrdup0 (tgetstr ("md", TEBP));
1800 stream->enter_italics_mode = xstrdup0 (tgetstr ("ZH", TEBP));
1801 stream->exit_italics_mode = xstrdup0 (tgetstr ("ZR", TEBP));
1802 stream->enter_underline_mode = xstrdup0 (tgetstr ("us", TEBP));
1803 stream->exit_underline_mode = xstrdup0 (tgetstr ("ue", TEBP));
1804 stream->exit_attribute_mode = xstrdup0 (tgetstr ("me", TEBP));
1807 /* The BeOS termcap entry for "beterm" is broken: For "AF" and "AB"
1808 it contains balues in terminfo syntax but the system's tparam()
1809 function understands only the termcap syntax. */
1810 if (stream->set_a_foreground != NULL
1811 && strcmp (stream->set_a_foreground, "\033[3%p1%dm") == 0)
1813 free (stream->set_a_foreground);
1814 stream->set_a_foreground = xstrdup ("\033[3%dm");
1816 if (stream->set_a_background != NULL
1817 && strcmp (stream->set_a_background, "\033[4%p1%dm") == 0)
1819 free (stream->set_a_background);
1820 stream->set_a_background = xstrdup ("\033[4%dm");
1824 /* The termcap entry for cygwin is broken: It has no "ncv" value,
1825 but bold and underline are actually rendered through colors. */
1826 if (strcmp (term, "cygwin") == 0)
1827 stream->no_color_video |= 2 | 32;
1829 /* Done with tgetstr. Detect possible buffer overflow. */
1831 if (memcmp (termentrybuf.canary, "CnRz", 4) != 0)
1832 /* Buffer overflow! */
1836 /* Fallback code for platforms with neither the terminfo nor the termcap
1837 functions, such as mingw.
1838 Assume the ANSI escape sequences. Extracted through
1839 "TERM=ansi infocmp", replacing \E with \033. */
1840 stream->max_colors = 8;
1841 stream->no_color_video = 3;
1842 stream->set_a_foreground = xstrdup ("\033[3%p1%dm");
1843 stream->set_a_background = xstrdup ("\033[4%p1%dm");
1844 stream->orig_pair = xstrdup ("\033[39;49m");
1845 stream->enter_bold_mode = xstrdup ("\033[1m");
1846 stream->enter_underline_mode = xstrdup ("\033[4m");
1847 stream->exit_underline_mode = xstrdup ("\033[m");
1848 stream->exit_attribute_mode = xstrdup ("\033[0;10m");
1851 /* AIX 4.3.2, IRIX 6.5, HP-UX 11, Solaris 7..10 all lack the
1852 description of color capabilities of "xterm" and "xterms"
1853 in their terminfo database. But it is important to have
1854 color in xterm. So we provide the color capabilities here. */
1855 if (stream->max_colors <= 1
1856 && (strcmp (term, "xterm") == 0 || strcmp (term, "xterms") == 0))
1858 stream->max_colors = 8;
1859 stream->set_a_foreground = xstrdup ("\033[3%p1%dm");
1860 stream->set_a_background = xstrdup ("\033[4%p1%dm");
1861 stream->orig_pair = xstrdup ("\033[39;49m");
1865 /* Infer the capabilities. */
1866 stream->supports_foreground =
1867 (stream->max_colors >= 8
1868 && (stream->set_a_foreground != NULL || stream->set_foreground != NULL)
1869 && stream->orig_pair != NULL);
1870 stream->supports_background =
1871 (stream->max_colors >= 8
1872 && (stream->set_a_background != NULL || stream->set_background != NULL)
1873 && stream->orig_pair != NULL);
1874 stream->colormodel =
1875 (stream->supports_foreground || stream->supports_background
1877 && (/* Recognize xterm-16color, xterm-88color, xterm-256color. */
1878 (strlen (term) >= 5 && memcmp (term, "xterm", 5) == 0)
1879 || /* Recognize rxvt-16color. */
1880 (strlen (term) >= 4 && memcmp (term, "rxvt", 7) == 0)
1881 || /* Recognize konsole-16color. */
1882 (strlen (term) >= 7 && memcmp (term, "konsole", 7) == 0))
1883 ? (stream->max_colors == 256 ? cm_xterm256 :
1884 stream->max_colors == 88 ? cm_xterm88 :
1885 stream->max_colors == 16 ? cm_xterm16 :
1889 stream->supports_weight =
1890 (stream->enter_bold_mode != NULL && stream->exit_attribute_mode != NULL);
1891 stream->supports_posture =
1892 (stream->enter_italics_mode != NULL
1893 && (stream->exit_italics_mode != NULL
1894 || stream->exit_attribute_mode != NULL));
1895 stream->supports_underline =
1896 (stream->enter_underline_mode != NULL
1897 && (stream->exit_underline_mode != NULL
1898 || stream->exit_attribute_mode != NULL));
1900 /* Initialize the buffer. */
1901 stream->allocated = 120;
1902 stream->buffer = XNMALLOC (stream->allocated, char);
1903 stream->attrbuffer = XNMALLOC (stream->allocated, attributes_t);
1906 /* Initialize the current attributes. */
1907 stream->curr_attr.color = COLOR_DEFAULT;
1908 stream->curr_attr.bgcolor = COLOR_DEFAULT;
1909 stream->curr_attr.weight = WEIGHT_DEFAULT;
1910 stream->curr_attr.posture = POSTURE_DEFAULT;
1911 stream->curr_attr.underline = UNDERLINE_DEFAULT;
1912 stream->simp_attr = simplify_attributes (stream, stream->curr_attr);
1914 /* Register an exit handler. */
1916 static bool registered = false;