1/* Output stream for attributed text, producing ANSI escape sequences. 2 Copyright (C) 2006-2007 Free Software Foundation, Inc. 3 Written by Bruno Haible <bruno@clisp.org>, 2006. 4 5 This program is free software: you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 17 18#include <config.h> 19 20/* Specification. */ 21#include "term-ostream.h" 22 23#include <assert.h> 24#include <errno.h> 25#include <signal.h> 26#include <stdbool.h> 27#include <stdlib.h> 28#include <string.h> 29 30#include "error.h" 31#include "fatal-signal.h" 32#include "full-write.h" 33#include "terminfo.h" 34#include "xalloc.h" 35#include "xsize.h" 36#include "gettext.h" 37 38#define _(str) gettext (str) 39 40#if HAVE_TPARAM 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(). */ 43static 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) 48#endif 49 50#define SIZEOF(a) (sizeof(a) / sizeof(a[0])) 51 52 53/* =========================== Color primitives =========================== */ 54 55/* A color in RGB format. */ 56typedef struct 57{ 58 unsigned int red : 8; /* range 0..255 */ 59 unsigned int green : 8; /* range 0..255 */ 60 unsigned int blue : 8; /* range 0..255 */ 61} rgb_t; 62 63/* A color in HSV (a.k.a. HSB) format. */ 64typedef struct 65{ 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] */ 69} hsv_t; 70 71/* Conversion of a color in RGB to HSV format. */ 72static void 73rgb_to_hsv (rgb_t c, hsv_t *result) 74{ 75 unsigned int r = c.red; 76 unsigned int g = c.green; 77 unsigned int b = c.blue; 78 79 if (r > g) 80 { 81 if (b > r) 82 { 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; 87 } 88 else if (b <= g) 89 { 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; 94 } 95 else 96 { 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; 101 } 102 } 103 else 104 { 105 if (b > g) 106 { 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; 111 } 112 else if (b < r) 113 { 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; 118 } 119 else if (g > r) 120 { 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; 125 } 126 else 127 { 128 /* r = g = b. A grey color. */ 129 result->hue = 0; /* arbitrary */ 130 result->saturation = 0; 131 result->brightness = (float) r / 255.0f; 132 } 133 } 134} 135 136/* Square of distance of two colors. */ 137static float 138color_distance (const hsv_t *color1, const hsv_t *color2) 139{ 140#if 0 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; 151 152 return delta_x * delta_x + delta_y * delta_y + delta_v * delta_v; 153#else 154 /* Formula that considers hue differences with more weight than saturation 155 or brightness differences, like the human eye does. */ 156 float delta_hue = 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 166 ? color1->saturation 167 : color2->saturation); 168 float delta_saturation = color1->saturation - color2->saturation; 169 float delta_brightness = color1->brightness - color2->brightness; 170 171 return delta_hue * delta_hue * min_saturation 172 + delta_saturation * delta_saturation * 0.2f 173 + delta_brightness * delta_brightness * 0.8f; 174#endif 175} 176 177/* Return the index of the color in a color table that is nearest to a given 178 color. */ 179static unsigned int 180nearest_color (rgb_t given, const rgb_t *table, unsigned int table_size) 181{ 182 hsv_t given_hsv; 183 unsigned int best_index; 184 float best_distance; 185 unsigned int i; 186 187 assert (table_size > 0); 188 189 rgb_to_hsv (given, &given_hsv); 190 191 best_index = 0; 192 best_distance = 1000000.0f; 193 for (i = 0; i < table_size; i++) 194 { 195 hsv_t i_hsv; 196 197 rgb_to_hsv (table[i], &i_hsv); 198 199 /* Avoid converting a color to grey, or fading out a color too much. */ 200 if (i_hsv.saturation > given_hsv.saturation * 0.5f) 201 { 202 float distance = color_distance (&given_hsv, &i_hsv); 203 if (distance < best_distance) 204 { 205 best_index = i; 206 best_distance = distance; 207 } 208 } 209 } 210 211#if 0 /* Debugging code */ 212 hsv_t best_hsv; 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); 219#endif 220 221 return best_index; 222} 223 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. */ 226static float 227color_luminance (int r, int g, int b) 228{ 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; 234} 235 236 237/* ============================= Color models ============================= */ 238 239/* The color model used by the terminal. */ 240typedef enum 241{ 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. */ 248} colormodel_t; 249 250/* ----------------------- cm_monochrome color model ----------------------- */ 251 252/* A non-default color index doesn't exist in this color model. */ 253static inline term_color_t 254rgb_to_color_monochrome () 255{ 256 return COLOR_DEFAULT; 257} 258 259/* ------------------------ cm_common8 color model ------------------------ */ 260 261/* A non-default color index is in the range 0..7. 262 RGB components 263 COLOR_BLACK 000 264 COLOR_BLUE 001 265 COLOR_GREEN 010 266 COLOR_CYAN 011 267 COLOR_RED 100 268 COLOR_MAGENTA 101 269 COLOR_YELLOW 110 270 COLOR_WHITE 111 */ 271static const rgb_t colors_of_common8[8] = 272{ 273 /* R G B grey index */ 274 { 0, 0, 0 }, /* 0.000 0 */ 275 { 0, 0, 255 }, 276 { 0, 255, 0 }, 277 { 0, 255, 255 }, 278 { 255, 0, 0 }, 279 { 255, 0, 255 }, 280 { 255, 255, 0 }, 281 { 255, 255, 255 } /* 1.000 7 */ 282}; 283 284static inline term_color_t 285rgb_to_color_common8 (int r, int g, int b) 286{ 287 rgb_t color; 288 hsv_t hsv; 289 290 color.red = r; color.green = g; color.blue = b; 291 rgb_to_hsv (color, &hsv); 292 293 if (hsv.saturation < 0.065f) 294 { 295 /* Greyscale approximation. */ 296 float luminance = color_luminance (r, g, b); 297 if (luminance < 0.500f) 298 return 0; 299 else 300 return 7; 301 } 302 else 303 /* Color approximation. */ 304 return nearest_color (color, colors_of_common8, 8); 305} 306 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. */ 310static inline int 311color_bgr (term_color_t color) 312{ 313 return ((color & 4) >> 2) | (color & 2) | ((color & 1) << 2); 314} 315 316/* ------------------------- cm_xterm8 color model ------------------------- */ 317 318/* A non-default color index is in the range 0..7. 319 BGR components 320 COLOR_BLACK 000 321 COLOR_RED 001 322 COLOR_GREEN 010 323 COLOR_YELLOW 011 324 COLOR_BLUE 100 325 COLOR_MAGENTA 101 326 COLOR_CYAN 110 327 COLOR_WHITE 111 */ 328static const rgb_t colors_of_xterm8[8] = 329{ 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 */ 333 { 255, 0, 0 }, 334 { 0, 255, 0 }, 335 { 255, 255, 0 }, 336 { 0, 0, 255 }, 337 { 255, 0, 255 }, 338 { 0, 255, 255 }, 339 { 255, 255, 255 } /* 1.000 7 */ 340}; 341 342static inline term_color_t 343rgb_to_color_xterm8 (int r, int g, int b) 344{ 345 rgb_t color; 346 hsv_t hsv; 347 348 color.red = r; color.green = g; color.blue = b; 349 rgb_to_hsv (color, &hsv); 350 351 if (hsv.saturation < 0.065f) 352 { 353 /* Greyscale approximation. */ 354 float luminance = color_luminance (r, g, b); 355 if (luminance < 0.500f) 356 return 0; 357 else 358 return 7; 359 } 360 else 361 /* Color approximation. */ 362 return nearest_color (color, colors_of_xterm8, 8); 363} 364 365/* ------------------------ cm_xterm16 color model ------------------------ */ 366 367/* A non-default color index is in the range 0..15. 368 The RGB values come from xterm's XTerm-col.ad. */ 369static const rgb_t colors_of_xterm16[16] = 370{ 371 /* R G B grey index */ 372 { 0, 0, 0 }, /* 0.000 0 */ 373 { 205, 0, 0 }, 374 { 0, 205, 0 }, 375 { 205, 205, 0 }, 376 { 0, 0, 205 }, 377 { 205, 0, 205 }, 378 { 0, 205, 205 }, 379 { 229, 229, 229 }, /* 0.898 7 */ 380 { 77, 77, 77 }, /* 0.302 8 */ 381 { 255, 0, 0 }, 382 { 0, 255, 0 }, 383 { 255, 255, 0 }, 384 { 0, 0, 255 }, 385 { 255, 0, 255 }, 386 { 0, 255, 255 }, 387 { 255, 255, 255 } /* 1.000 15 */ 388}; 389 390static inline term_color_t 391rgb_to_color_xterm16 (int r, int g, int b) 392{ 393 rgb_t color; 394 hsv_t hsv; 395 396 color.red = r; color.green = g; color.blue = b; 397 rgb_to_hsv (color, &hsv); 398 399 if (hsv.saturation < 0.065f) 400 { 401 /* Greyscale approximation. */ 402 float luminance = color_luminance (r, g, b); 403 if (luminance < 0.151f) 404 return 0; 405 else if (luminance < 0.600f) 406 return 8; 407 else if (luminance < 0.949f) 408 return 7; 409 else 410 return 15; 411 } 412 else 413 /* Color approximation. */ 414 return nearest_color (color, colors_of_xterm16, 16); 415} 416 417/* ------------------------ cm_xterm88 color model ------------------------ */ 418 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. */ 422 423static const rgb_t colors_of_xterm88[88] = 424{ 425 /* R G B grey index */ 426 { 0, 0, 0 }, /* 0.000 0 */ 427 { 205, 0, 0 }, 428 { 0, 205, 0 }, 429 { 205, 205, 0 }, 430 { 0, 0, 205 }, 431 { 205, 0, 205 }, 432 { 0, 205, 205 }, 433 { 229, 229, 229 }, /* 0.898 7 */ 434 { 77, 77, 77 }, /* 0.302 8 */ 435 { 255, 0, 0 }, 436 { 0, 255, 0 }, 437 { 255, 255, 0 }, 438 { 0, 0, 255 }, 439 { 255, 0, 255 }, 440 { 0, 255, 255 }, 441 { 255, 255, 255 }, /* 1.000 15 */ 442 { 0, 0, 0 }, /* 0.000 16 */ 443 { 0, 0, 139 }, 444 { 0, 0, 205 }, 445 { 0, 0, 255 }, 446 { 0, 139, 0 }, 447 { 0, 139, 139 }, 448 { 0, 139, 205 }, 449 { 0, 139, 255 }, 450 { 0, 205, 0 }, 451 { 0, 205, 139 }, 452 { 0, 205, 205 }, 453 { 0, 205, 255 }, 454 { 0, 255, 0 }, 455 { 0, 255, 139 }, 456 { 0, 255, 205 }, 457 { 0, 255, 255 }, 458 { 139, 0, 0 }, 459 { 139, 0, 139 }, 460 { 139, 0, 205 }, 461 { 139, 0, 255 }, 462 { 139, 139, 0 }, 463 { 139, 139, 139 }, /* 0.545 37 */ 464 { 139, 139, 205 }, 465 { 139, 139, 255 }, 466 { 139, 205, 0 }, 467 { 139, 205, 139 }, 468 { 139, 205, 205 }, 469 { 139, 205, 255 }, 470 { 139, 255, 0 }, 471 { 139, 255, 139 }, 472 { 139, 255, 205 }, 473 { 139, 255, 255 }, 474 { 205, 0, 0 }, 475 { 205, 0, 139 }, 476 { 205, 0, 205 }, 477 { 205, 0, 255 }, 478 { 205, 139, 0 }, 479 { 205, 139, 139 }, 480 { 205, 139, 205 }, 481 { 205, 139, 255 }, 482 { 205, 205, 0 }, 483 { 205, 205, 139 }, 484 { 205, 205, 205 }, /* 0.804 58 */ 485 { 205, 205, 255 }, 486 { 205, 255, 0 }, 487 { 205, 255, 139 }, 488 { 205, 255, 205 }, 489 { 205, 255, 255 }, 490 { 255, 0, 0 }, 491 { 255, 0, 139 }, 492 { 255, 0, 205 }, 493 { 255, 0, 255 }, 494 { 255, 139, 0 }, 495 { 255, 139, 139 }, 496 { 255, 139, 205 }, 497 { 255, 139, 255 }, 498 { 255, 205, 0 }, 499 { 255, 205, 139 }, 500 { 255, 205, 205 }, 501 { 255, 205, 255 }, 502 { 255, 255, 0 }, 503 { 255, 255, 139 }, 504 { 255, 255, 205 }, 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 */ 514}; 515 516static inline term_color_t 517rgb_to_color_xterm88 (int r, int g, int b) 518{ 519 rgb_t color; 520 hsv_t hsv; 521 522 color.red = r; color.green = g; color.blue = b; 523 rgb_to_hsv (color, &hsv); 524 525 if (hsv.saturation < 0.065f) 526 { 527 /* Greyscale approximation. */ 528 float luminance = color_luminance (r, g, b); 529 if (luminance < 0.090f) 530 return 0; 531 else if (luminance < 0.241f) 532 return 80; 533 else if (luminance < 0.331f) 534 return 8; 535 else if (luminance < 0.406f) 536 return 81; 537 else if (luminance < 0.498f) 538 return 82; 539 else if (luminance < 0.585f) 540 return 37; 541 else if (luminance < 0.680f) 542 return 84; 543 else if (luminance < 0.764f) 544 return 85; 545 else if (luminance < 0.810f) 546 return 58; 547 else if (luminance < 0.857f) 548 return 86; 549 else if (luminance < 0.902f) 550 return 7; 551 else if (luminance < 0.953f) 552 return 87; 553 else 554 return 15; 555 } 556 else 557 /* Color approximation. */ 558 return nearest_color (color, colors_of_xterm88, 88); 559} 560 561/* ------------------------ cm_xterm256 color model ------------------------ */ 562 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. */ 566 567static const rgb_t colors_of_xterm256[256] = 568{ 569 /* R G B grey index */ 570 { 0, 0, 0 }, /* 0.000 0 */ 571 { 205, 0, 0 }, 572 { 0, 205, 0 }, 573 { 205, 205, 0 }, 574 { 0, 0, 205 }, 575 { 205, 0, 205 }, 576 { 0, 205, 205 }, 577 { 229, 229, 229 }, /* 0.898 7 */ 578 { 77, 77, 77 }, /* 0.302 8 */ 579 { 255, 0, 0 }, 580 { 0, 255, 0 }, 581 { 255, 255, 0 }, 582 { 0, 0, 255 }, 583 { 255, 0, 255 }, 584 { 0, 255, 255 }, 585 { 255, 255, 255 }, /* 1.000 15 */ 586 { 0, 0, 0 }, /* 0.000 16 */ 587 { 0, 0, 42 }, 588 { 0, 0, 85 }, 589 { 0, 0, 127 }, 590 { 0, 0, 170 }, 591 { 0, 0, 212 }, 592 { 0, 42, 0 }, 593 { 0, 42, 42 }, 594 { 0, 42, 85 }, 595 { 0, 42, 127 }, 596 { 0, 42, 170 }, 597 { 0, 42, 212 }, 598 { 0, 85, 0 }, 599 { 0, 85, 42 }, 600 { 0, 85, 85 }, 601 { 0, 85, 127 }, 602 { 0, 85, 170 }, 603 { 0, 85, 212 }, 604 { 0, 127, 0 }, 605 { 0, 127, 42 }, 606 { 0, 127, 85 }, 607 { 0, 127, 127 }, 608 { 0, 127, 170 }, 609 { 0, 127, 212 }, 610 { 0, 170, 0 }, 611 { 0, 170, 42 }, 612 { 0, 170, 85 }, 613 { 0, 170, 127 }, 614 { 0, 170, 170 }, 615 { 0, 170, 212 }, 616 { 0, 212, 0 }, 617 { 0, 212, 42 }, 618 { 0, 212, 85 }, 619 { 0, 212, 127 }, 620 { 0, 212, 170 }, 621 { 0, 212, 212 }, 622 { 42, 0, 0 }, 623 { 42, 0, 42 }, 624 { 42, 0, 85 }, 625 { 42, 0, 127 }, 626 { 42, 0, 170 }, 627 { 42, 0, 212 }, 628 { 42, 42, 0 }, 629 { 42, 42, 42 }, /* 0.165 59 */ 630 { 42, 42, 85 }, 631 { 42, 42, 127 }, 632 { 42, 42, 170 }, 633 { 42, 42, 212 }, 634 { 42, 85, 0 }, 635 { 42, 85, 42 }, 636 { 42, 85, 85 }, 637 { 42, 85, 127 }, 638 { 42, 85, 170 }, 639 { 42, 85, 212 }, 640 { 42, 127, 0 }, 641 { 42, 127, 42 }, 642 { 42, 127, 85 }, 643 { 42, 127, 127 }, 644 { 42, 127, 170 }, 645 { 42, 127, 212 }, 646 { 42, 170, 0 }, 647 { 42, 170, 42 }, 648 { 42, 170, 85 }, 649 { 42, 170, 127 }, 650 { 42, 170, 170 }, 651 { 42, 170, 212 }, 652 { 42, 212, 0 }, 653 { 42, 212, 42 }, 654 { 42, 212, 85 }, 655 { 42, 212, 127 }, 656 { 42, 212, 170 }, 657 { 42, 212, 212 }, 658 { 85, 0, 0 }, 659 { 85, 0, 42 }, 660 { 85, 0, 85 }, 661 { 85, 0, 127 }, 662 { 85, 0, 170 }, 663 { 85, 0, 212 }, 664 { 85, 42, 0 }, 665 { 85, 42, 42 }, 666 { 85, 42, 85 }, 667 { 85, 42, 127 }, 668 { 85, 42, 170 }, 669 { 85, 42, 212 }, 670 { 85, 85, 0 }, 671 { 85, 85, 42 }, 672 { 85, 85, 85 }, /* 0.333 102 */ 673 { 85, 85, 127 }, 674 { 85, 85, 170 }, 675 { 85, 85, 212 }, 676 { 85, 127, 0 }, 677 { 85, 127, 42 }, 678 { 85, 127, 85 }, 679 { 85, 127, 127 }, 680 { 85, 127, 170 }, 681 { 85, 127, 212 }, 682 { 85, 170, 0 }, 683 { 85, 170, 42 }, 684 { 85, 170, 85 }, 685 { 85, 170, 127 }, 686 { 85, 170, 170 }, 687 { 85, 170, 212 }, 688 { 85, 212, 0 }, 689 { 85, 212, 42 }, 690 { 85, 212, 85 }, 691 { 85, 212, 127 }, 692 { 85, 212, 170 }, 693 { 85, 212, 212 }, 694 { 127, 0, 0 }, 695 { 127, 0, 42 }, 696 { 127, 0, 85 }, 697 { 127, 0, 127 }, 698 { 127, 0, 170 }, 699 { 127, 0, 212 }, 700 { 127, 42, 0 }, 701 { 127, 42, 42 }, 702 { 127, 42, 85 }, 703 { 127, 42, 127 }, 704 { 127, 42, 170 }, 705 { 127, 42, 212 }, 706 { 127, 85, 0 }, 707 { 127, 85, 42 }, 708 { 127, 85, 85 }, 709 { 127, 85, 127 }, 710 { 127, 85, 170 }, 711 { 127, 85, 212 }, 712 { 127, 127, 0 }, 713 { 127, 127, 42 }, 714 { 127, 127, 85 }, 715 { 127, 127, 127 }, /* 0.498 145 */ 716 { 127, 127, 170 }, 717 { 127, 127, 212 }, 718 { 127, 170, 0 }, 719 { 127, 170, 42 }, 720 { 127, 170, 85 }, 721 { 127, 170, 127 }, 722 { 127, 170, 170 }, 723 { 127, 170, 212 }, 724 { 127, 212, 0 }, 725 { 127, 212, 42 }, 726 { 127, 212, 85 }, 727 { 127, 212, 127 }, 728 { 127, 212, 170 }, 729 { 127, 212, 212 }, 730 { 170, 0, 0 }, 731 { 170, 0, 42 }, 732 { 170, 0, 85 }, 733 { 170, 0, 127 }, 734 { 170, 0, 170 }, 735 { 170, 0, 212 }, 736 { 170, 42, 0 }, 737 { 170, 42, 42 }, 738 { 170, 42, 85 }, 739 { 170, 42, 127 }, 740 { 170, 42, 170 }, 741 { 170, 42, 212 }, 742 { 170, 85, 0 }, 743 { 170, 85, 42 }, 744 { 170, 85, 85 }, 745 { 170, 85, 127 }, 746 { 170, 85, 170 }, 747 { 170, 85, 212 }, 748 { 170, 127, 0 }, 749 { 170, 127, 42 }, 750 { 170, 127, 85 }, 751 { 170, 127, 127 }, 752 { 170, 127, 170 }, 753 { 170, 127, 212 }, 754 { 170, 170, 0 }, 755 { 170, 170, 42 }, 756 { 170, 170, 85 }, 757 { 170, 170, 127 }, 758 { 170, 170, 170 }, /* 0.667 188 */ 759 { 170, 170, 212 }, 760 { 170, 212, 0 }, 761 { 170, 212, 42 }, 762 { 170, 212, 85 }, 763 { 170, 212, 127 }, 764 { 170, 212, 170 }, 765 { 170, 212, 212 }, 766 { 212, 0, 0 }, 767 { 212, 0, 42 }, 768 { 212, 0, 85 }, 769 { 212, 0, 127 }, 770 { 212, 0, 170 }, 771 { 212, 0, 212 }, 772 { 212, 42, 0 }, 773 { 212, 42, 42 }, 774 { 212, 42, 85 }, 775 { 212, 42, 127 }, 776 { 212, 42, 170 }, 777 { 212, 42, 212 }, 778 { 212, 85, 0 }, 779 { 212, 85, 42 }, 780 { 212, 85, 85 }, 781 { 212, 85, 127 }, 782 { 212, 85, 170 }, 783 { 212, 85, 212 }, 784 { 212, 127, 0 }, 785 { 212, 127, 42 }, 786 { 212, 127, 85 }, 787 { 212, 127, 127 }, 788 { 212, 127, 170 }, 789 { 212, 127, 212 }, 790 { 212, 170, 0 }, 791 { 212, 170, 42 }, 792 { 212, 170, 85 }, 793 { 212, 170, 127 }, 794 { 212, 170, 170 }, 795 { 212, 170, 212 }, 796 { 212, 212, 0 }, 797 { 212, 212, 42 }, 798 { 212, 212, 85 }, 799 { 212, 212, 127 }, 800 { 212, 212, 170 }, 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 */ 826}; 827 828static inline term_color_t 829rgb_to_color_xterm256 (int r, int g, int b) 830{ 831 rgb_t color; 832 hsv_t hsv; 833 834 color.red = r; color.green = g; color.blue = b; 835 rgb_to_hsv (color, &hsv); 836 837 if (hsv.saturation < 0.065f) 838 { 839 /* Greyscale approximation. */ 840 float luminance = color_luminance (r, g, b); 841 if (luminance < 0.015f) 842 return 0; 843 else if (luminance < 0.051f) 844 return 232; 845 else if (luminance < 0.090f) 846 return 233; 847 else if (luminance < 0.129f) 848 return 234; 849 else if (luminance < 0.157f) 850 return 235; 851 else if (luminance < 0.177f) 852 return 59; 853 else if (luminance < 0.207f) 854 return 236; 855 else if (luminance < 0.247f) 856 return 237; 857 else if (luminance < 0.284f) 858 return 238; 859 else if (luminance < 0.304f) 860 return 8; 861 else if (luminance < 0.319f) 862 return 239; 863 else if (luminance < 0.339f) 864 return 102; 865 else if (luminance < 0.364f) 866 return 240; 867 else if (luminance < 0.404f) 868 return 241; 869 else if (luminance < 0.443f) 870 return 242; 871 else if (luminance < 0.480f) 872 return 243; 873 else if (luminance < 0.500f) 874 return 145; 875 else if (luminance < 0.521f) 876 return 244; 877 else if (luminance < 0.560f) 878 return 245; 879 else if (luminance < 0.600f) 880 return 246; 881 else if (luminance < 0.639f) 882 return 247; 883 else if (luminance < 0.663f) 884 return 248; 885 else if (luminance < 0.682f) 886 return 188; 887 else if (luminance < 0.717f) 888 return 249; 889 else if (luminance < 0.756f) 890 return 250; 891 else if (luminance < 0.796f) 892 return 251; 893 else if (luminance < 0.823f) 894 return 252; 895 else if (luminance < 0.843f) 896 return 231; 897 else if (luminance < 0.874f) 898 return 253; 899 else if (luminance < 0.896f) 900 return 254; 901 else if (luminance < 0.915f) 902 return 7; 903 else if (luminance < 0.966f) 904 return 255; 905 else 906 return 15; 907 } 908 else 909 /* Color approximation. */ 910 return nearest_color (color, colors_of_xterm256, 256); 911} 912 913 914/* ============================= attributes_t ============================= */ 915 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. */ 920#ifdef __cplusplus 921# define BITFIELD_TYPE(orig_type,integer_type) orig_type 922#else 923# define BITFIELD_TYPE(orig_type,integer_type) integer_type 924#endif 925 926/* Attributes that can be set on a character. */ 927typedef struct 928{ 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; 934} attributes_t; 935 936 937/* ============================ term_ostream_t ============================ */ 938 939struct term_ostream : struct ostream 940{ 941fields: 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. */ 945 int fd; 946 char *filename; 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 973 length as buffer. */ 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. */ 978}; 979 980/* Simplify attributes, according to the terminal's capabilities. */ 981static attributes_t 982simplify_attributes (term_ostream_t stream, attributes_t attr) 983{ 984 if ((attr.color != COLOR_DEFAULT || attr.bgcolor != COLOR_DEFAULT) 985 && stream->no_color_video > 0) 986 { 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; 995 } 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; 1006 return attr; 1007} 1008 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. */ 1011 1012/* File descriptor to which out_char shall output escape sequences. */ 1013static int out_fd = -1; 1014 1015/* Filename of out_fd. */ 1016static const char *out_filename; 1017 1018/* Output a single char to out_fd. Ignore errors. */ 1019static int 1020out_char_unchecked (int c) 1021{ 1022 char bytes[1]; 1023 1024 bytes[0] = (char)c; 1025 full_write (out_fd, bytes, 1); 1026 return 0; 1027} 1028 1029/* State that informs the exit handler what to do. */ 1030static const char *restore_colors; 1031static const char *restore_weight; 1032static const char *restore_posture; 1033static const char *restore_underline; 1034 1035/* The exit handler. */ 1036static void 1037restore (void) 1038{ 1039 /* Only do something while some output was interrupted. */ 1040 if (out_fd >= 0) 1041 { 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); 1050 } 1051} 1052 1053/* The list of signals whose default behaviour is to stop the program. */ 1054static int stopping_signals[] = 1055 { 1056#ifdef SIGTSTP 1057 SIGTSTP, 1058#endif 1059#ifdef SIGTTIN 1060 SIGTTIN, 1061#endif 1062#ifdef SIGTTOU 1063 SIGTTOU, 1064#endif 1065 0 1066 }; 1067 1068#define num_stopping_signals (SIZEOF (stopping_signals) - 1) 1069 1070static sigset_t stopping_signal_set; 1071 1072static void 1073init_stopping_signal_set () 1074{ 1075 static bool stopping_signal_set_initialized = false; 1076 if (!stopping_signal_set_initialized) 1077 { 1078 size_t i; 1079 1080 sigemptyset (&stopping_signal_set); 1081 for (i = 0; i < num_stopping_signals; i++) 1082 sigaddset (&stopping_signal_set, stopping_signals[i]); 1083 1084 stopping_signal_set_initialized = true; 1085 } 1086} 1087 1088/* Temporarily delay the stopping signals. */ 1089static inline void 1090block_stopping_signals () 1091{ 1092 init_stopping_signal_set (); 1093 sigprocmask (SIG_BLOCK, &stopping_signal_set, NULL); 1094} 1095 1096/* Stop delaying the stopping signals. */ 1097static inline void 1098unblock_stopping_signals () 1099{ 1100 init_stopping_signal_set (); 1101 sigprocmask (SIG_UNBLOCK, &stopping_signal_set, NULL); 1102} 1103 1104/* Compare two sets of attributes for equality. */ 1105static inline bool 1106equal_attributes (attributes_t attr1, attributes_t attr2) 1107{ 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); 1113} 1114 1115/* Signal error after full_write failed. */ 1116static void 1117out_error () 1118{ 1119 error (EXIT_FAILURE, errno, _("error writing to %s"), out_filename); 1120} 1121 1122/* Output a single char to out_fd. */ 1123static int 1124out_char (int c) 1125{ 1126 char bytes[1]; 1127 1128 bytes[0] = (char)c; 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() 1131 does. */ 1132 if (full_write (out_fd, bytes, 1) < 1) 1133 out_error (); 1134 return 0; 1135} 1136 1137/* Output escape sequences to switch from OLD_ATTR to NEW_ATTR. */ 1138static void 1139out_attr_change (term_ostream_t stream, 1140 attributes_t old_attr, attributes_t new_attr) 1141{ 1142 bool cleared_attributes; 1143 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)) 1148 { 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; 1153 } 1154 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 1159 TERM="ansi". 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 1163 the colors. 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 1167 also the colors. 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 1175 settings. */ 1176 cleared_attributes = false; 1177 if (old_attr.posture != POSTURE_NORMAL 1178 && new_attr.posture == POSTURE_NORMAL 1179 && stream->exit_italics_mode != NULL) 1180 { 1181 tputs (stream->exit_italics_mode, 1, out_char); 1182 old_attr.posture = POSTURE_NORMAL; 1183 cleared_attributes = true; 1184 } 1185 if (old_attr.underline != UNDERLINE_OFF 1186 && new_attr.underline == UNDERLINE_OFF 1187 && stream->exit_underline_mode != NULL) 1188 { 1189 tputs (stream->exit_underline_mode, 1, out_char); 1190 old_attr.underline = UNDERLINE_OFF; 1191 cleared_attributes = true; 1192 } 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 */)) 1201 { 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; 1211 } 1212 1213 /* Turn on the colors. */ 1214 if (new_attr.color != old_attr.color 1215 || (cleared_attributes && new_attr.color != COLOR_DEFAULT)) 1216 { 1217 assert (stream->supports_foreground); 1218 assert (new_attr.color != COLOR_DEFAULT); 1219 switch (stream->colormodel) 1220 { 1221 case cm_common8: 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)), 1226 1, out_char); 1227 else 1228 tputs (tparm (stream->set_foreground, new_attr.color), 1229 1, out_char); 1230 break; 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. */ 1233 case cm_xterm8: 1234 assert (new_attr.color >= 0 && new_attr.color < 8); 1235 { 1236 char bytes[5]; 1237 bytes[0] = 0x1B; bytes[1] = '['; 1238 bytes[2] = '3'; bytes[3] = '0' + new_attr.color; 1239 bytes[4] = 'm'; 1240 if (full_write (out_fd, bytes, 5) < 5) 1241 out_error (); 1242 } 1243 break; 1244 case cm_xterm16: 1245 assert (new_attr.color >= 0 && new_attr.color < 16); 1246 { 1247 char bytes[5]; 1248 bytes[0] = 0x1B; bytes[1] = '['; 1249 if (new_attr.color < 8) 1250 { 1251 bytes[2] = '3'; bytes[3] = '0' + new_attr.color; 1252 } 1253 else 1254 { 1255 bytes[2] = '9'; bytes[3] = '0' + (new_attr.color - 8); 1256 } 1257 bytes[4] = 'm'; 1258 if (full_write (out_fd, bytes, 5) < 5) 1259 out_error (); 1260 } 1261 break; 1262 case cm_xterm88: 1263 assert (new_attr.color >= 0 && new_attr.color < 88); 1264 { 1265 char bytes[10]; 1266 char *p; 1267 bytes[0] = 0x1B; bytes[1] = '['; 1268 bytes[2] = '3'; bytes[3] = '8'; bytes[4] = ';'; 1269 bytes[5] = '5'; bytes[6] = ';'; 1270 p = bytes + 7; 1271 if (new_attr.color >= 10) 1272 *p++ = '0' + (new_attr.color / 10); 1273 *p++ = '0' + (new_attr.color % 10); 1274 *p++ = 'm'; 1275 if (full_write (out_fd, bytes, p - bytes) < p - bytes) 1276 out_error (); 1277 } 1278 break; 1279 case cm_xterm256: 1280 assert (new_attr.color >= 0 && new_attr.color < 256); 1281 { 1282 char bytes[11]; 1283 char *p; 1284 bytes[0] = 0x1B; bytes[1] = '['; 1285 bytes[2] = '3'; bytes[3] = '8'; bytes[4] = ';'; 1286 bytes[5] = '5'; bytes[6] = ';'; 1287 p = bytes + 7; 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); 1293 *p++ = 'm'; 1294 if (full_write (out_fd, bytes, p - bytes) < p - bytes) 1295 out_error (); 1296 } 1297 break; 1298 default: 1299 abort (); 1300 } 1301 } 1302 if (new_attr.bgcolor != old_attr.bgcolor 1303 || (cleared_attributes && new_attr.bgcolor != COLOR_DEFAULT)) 1304 { 1305 assert (stream->supports_background); 1306 assert (new_attr.bgcolor != COLOR_DEFAULT); 1307 switch (stream->colormodel) 1308 { 1309 case cm_common8: 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)), 1314 1, out_char); 1315 else 1316 tputs (tparm (stream->set_background, new_attr.bgcolor), 1317 1, out_char); 1318 break; 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. */ 1321 case cm_xterm8: 1322 assert (new_attr.bgcolor >= 0 && new_attr.bgcolor < 8); 1323 { 1324 char bytes[5]; 1325 bytes[0] = 0x1B; bytes[1] = '['; 1326 bytes[2] = '4'; bytes[3] = '0' + new_attr.bgcolor; 1327 bytes[4] = 'm'; 1328 if (full_write (out_fd, bytes, 5) < 5) 1329 out_error (); 1330 } 1331 break; 1332 case cm_xterm16: 1333 assert (new_attr.bgcolor >= 0 && new_attr.bgcolor < 16); 1334 { 1335 char bytes[6]; 1336 bytes[0] = 0x1B; bytes[1] = '['; 1337 if (new_attr.bgcolor < 8) 1338 { 1339 bytes[2] = '4'; bytes[3] = '0' + new_attr.bgcolor; 1340 bytes[4] = 'm'; 1341 if (full_write (out_fd, bytes, 5) < 5) 1342 out_error (); 1343 } 1344 else 1345 { 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) 1349 out_error (); 1350 } 1351 } 1352 break; 1353 case cm_xterm88: 1354 assert (new_attr.bgcolor >= 0 && new_attr.bgcolor < 88); 1355 { 1356 char bytes[10]; 1357 char *p; 1358 bytes[0] = 0x1B; bytes[1] = '['; 1359 bytes[2] = '4'; bytes[3] = '8'; bytes[4] = ';'; 1360 bytes[5] = '5'; bytes[6] = ';'; 1361 p = bytes + 7; 1362 if (new_attr.bgcolor >= 10) 1363 *p++ = '0' + (new_attr.bgcolor / 10); 1364 *p++ = '0' + (new_attr.bgcolor % 10); 1365 *p++ = 'm'; 1366 if (full_write (out_fd, bytes, p - bytes) < p - bytes) 1367 out_error (); 1368 } 1369 break; 1370 case cm_xterm256: 1371 assert (new_attr.bgcolor >= 0 && new_attr.bgcolor < 256); 1372 { 1373 char bytes[11]; 1374 char *p; 1375 bytes[0] = 0x1B; bytes[1] = '['; 1376 bytes[2] = '4'; bytes[3] = '8'; bytes[4] = ';'; 1377 bytes[5] = '5'; bytes[6] = ';'; 1378 p = bytes + 7; 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); 1384 *p++ = 'm'; 1385 if (full_write (out_fd, bytes, p - bytes) < p - bytes) 1386 out_error (); 1387 } 1388 break; 1389 default: 1390 abort (); 1391 } 1392 } 1393 1394 if (new_attr.weight != old_attr.weight 1395 || (cleared_attributes && new_attr.weight != WEIGHT_DEFAULT)) 1396 { 1397 assert (stream->supports_weight); 1398 assert (new_attr.weight != WEIGHT_DEFAULT); 1399 /* This implies: */ 1400 assert (new_attr.weight == WEIGHT_BOLD); 1401 tputs (stream->enter_bold_mode, 1, out_char); 1402 } 1403 if (new_attr.posture != old_attr.posture 1404 || (cleared_attributes && new_attr.posture != POSTURE_DEFAULT)) 1405 { 1406 assert (stream->supports_posture); 1407 assert (new_attr.posture != POSTURE_DEFAULT); 1408 /* This implies: */ 1409 assert (new_attr.posture == POSTURE_ITALIC); 1410 tputs (stream->enter_italics_mode, 1, out_char); 1411 } 1412 if (new_attr.underline != old_attr.underline 1413 || (cleared_attributes && new_attr.underline != UNDERLINE_DEFAULT)) 1414 { 1415 assert (stream->supports_underline); 1416 assert (new_attr.underline != UNDERLINE_DEFAULT); 1417 /* This implies: */ 1418 assert (new_attr.underline == UNDERLINE_ON); 1419 tputs (stream->enter_underline_mode, 1, out_char); 1420 } 1421} 1422 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). */ 1427static void 1428output_buffer (term_ostream_t stream) 1429{ 1430 attributes_t default_attr; 1431 attributes_t attr; 1432 const char *cp; 1433 const attributes_t *ap; 1434 size_t len; 1435 size_t n; 1436 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; 1442 1443 attr = default_attr; 1444 1445 cp = stream->buffer; 1446 ap = stream->attrbuffer; 1447 len = stream->buflen; 1448 1449 /* See how much we can output without blocking signals. */ 1450 for (n = 0; n < len && equal_attributes (ap[n], attr); n++) 1451 ; 1452 if (n > 0) 1453 { 1454 if (full_write (stream->fd, cp, n) < n) 1455 error (EXIT_FAILURE, errno, _("error writing to %s"), stream->filename); 1456 cp += n; 1457 ap += n; 1458 len -= n; 1459 } 1460 if (len > 0) 1461 { 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 (); 1467 1468 /* Enable the exit handler for restoring the terminal's state. */ 1469 restore_colors = 1470 (stream->supports_foreground || stream->supports_background 1471 ? stream->orig_pair 1472 : NULL); 1473 restore_weight = 1474 (stream->supports_weight ? stream->exit_attribute_mode : NULL); 1475 restore_posture = 1476 (stream->supports_posture 1477 ? (stream->exit_italics_mode != NULL 1478 ? stream->exit_italics_mode 1479 : stream->exit_attribute_mode) 1480 : NULL); 1481 restore_underline = 1482 (stream->supports_underline 1483 ? (stream->exit_underline_mode != NULL 1484 ? stream->exit_underline_mode 1485 : stream->exit_attribute_mode) 1486 : NULL); 1487 out_fd = stream->fd; 1488 out_filename = stream->filename; 1489 1490 while (len > 0) 1491 { 1492 /* Activate the attributes in *ap. */ 1493 out_attr_change (stream, attr, *ap); 1494 attr = *ap; 1495 /* See how many characters we can output without further attribute 1496 changes. */ 1497 for (n = 1; n < len && equal_attributes (ap[n], attr); n++) 1498 ; 1499 if (full_write (stream->fd, cp, n) < n) 1500 error (EXIT_FAILURE, errno, _("error writing to %s"), 1501 stream->filename); 1502 cp += n; 1503 ap += n; 1504 len -= n; 1505 } 1506 1507 /* Switch back to the default attributes. */ 1508 out_attr_change (stream, attr, default_attr); 1509 1510 /* Disable the exit handler. */ 1511 out_fd = -1; 1512 out_filename = NULL; 1513 1514 /* Unblock fatal and stopping signals. */ 1515 unblock_stopping_signals (); 1516 unblock_fatal_signals (); 1517 } 1518 stream->buflen = 0; 1519} 1520 1521/* Implementation of ostream_t methods. */ 1522 1523static term_color_t 1524term_ostream::rgb_to_color (term_ostream_t stream, int red, int green, int blue) 1525{ 1526 switch (stream->colormodel) 1527 { 1528 case cm_monochrome: 1529 return rgb_to_color_monochrome (); 1530 case cm_common8: 1531 return rgb_to_color_common8 (red, green, blue); 1532 case cm_xterm8: 1533 return rgb_to_color_xterm8 (red, green, blue); 1534 case cm_xterm16: 1535 return rgb_to_color_xterm16 (red, green, blue); 1536 case cm_xterm88: 1537 return rgb_to_color_xterm88 (red, green, blue); 1538 case cm_xterm256: 1539 return rgb_to_color_xterm256 (red, green, blue); 1540 default: 1541 abort (); 1542 } 1543} 1544 1545static void 1546term_ostream::write_mem (term_ostream_t stream, const void *data, size_t len) 1547{ 1548 const char *cp = (const char *) data; 1549 while (len > 0) 1550 { 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); 1554 1555 /* Copy n bytes into the buffer. */ 1556 if (n > stream->allocated - stream->buflen) 1557 { 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"), 1564 "term_ostream"); 1565 stream->buffer = (char *) xrealloc (stream->buffer, new_allocated); 1566 stream->attrbuffer = 1567 (attributes_t *) 1568 xrealloc (stream->attrbuffer, 1569 new_allocated * sizeof (attributes_t)); 1570 stream->allocated = new_allocated; 1571 } 1572 memcpy (stream->buffer + stream->buflen, cp, n); 1573 { 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++) 1578 *ap = attr; 1579 } 1580 stream->buflen += n; 1581 1582 if (newline != NULL) 1583 { 1584 output_buffer (stream); 1585 if (full_write (stream->fd, "\n", 1) < 1) 1586 error (EXIT_FAILURE, errno, _("error writing to %s"), 1587 stream->filename); 1588 cp += n + 1; /* cp = newline + 1; */ 1589 len -= n + 1; 1590 } 1591 else 1592 break; 1593 } 1594} 1595 1596static void 1597term_ostream::flush (term_ostream_t stream) 1598{ 1599 output_buffer (stream); 1600} 1601 1602static void 1603term_ostream::free (term_ostream_t stream) 1604{ 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); 1630 free (stream); 1631} 1632 1633/* Implementation of term_ostream_t methods. */ 1634 1635term_color_t 1636term_ostream::get_color (term_ostream_t stream) 1637{ 1638 return stream->curr_attr.color; 1639} 1640 1641void 1642term_ostream::set_color (term_ostream_t stream, term_color_t color) 1643{ 1644 stream->curr_attr.color = color; 1645 stream->simp_attr = simplify_attributes (stream, stream->curr_attr); 1646} 1647 1648term_color_t 1649term_ostream::get_bgcolor (term_ostream_t stream) 1650{ 1651 return stream->curr_attr.bgcolor; 1652} 1653 1654void 1655term_ostream::set_bgcolor (term_ostream_t stream, term_color_t color) 1656{ 1657 stream->curr_attr.bgcolor = color; 1658 stream->simp_attr = simplify_attributes (stream, stream->curr_attr); 1659} 1660 1661term_weight_t 1662term_ostream::get_weight (term_ostream_t stream) 1663{ 1664 return stream->curr_attr.weight; 1665} 1666 1667void 1668term_ostream::set_weight (term_ostream_t stream, term_weight_t weight) 1669{ 1670 stream->curr_attr.weight = weight; 1671 stream->simp_attr = simplify_attributes (stream, stream->curr_attr); 1672} 1673 1674term_posture_t 1675term_ostream::get_posture (term_ostream_t stream) 1676{ 1677 return stream->curr_attr.posture; 1678} 1679 1680void 1681term_ostream::set_posture (term_ostream_t stream, term_posture_t posture) 1682{ 1683 stream->curr_attr.posture = posture; 1684 stream->simp_attr = simplify_attributes (stream, stream->curr_attr); 1685} 1686 1687term_underline_t 1688term_ostream::get_underline (term_ostream_t stream) 1689{ 1690 return stream->curr_attr.underline; 1691} 1692 1693void 1694term_ostream::set_underline (term_ostream_t stream, term_underline_t underline) 1695{ 1696 stream->curr_attr.underline = underline; 1697 stream->simp_attr = simplify_attributes (stream, stream->curr_attr); 1698} 1699 1700/* Constructor. */ 1701 1702static inline char * 1703xstrdup0 (const char *str) 1704{ 1705 if (str == NULL) 1706 return NULL; 1707#if HAVE_TERMINFO 1708 if (str == (const char *)(-1)) 1709 return NULL; 1710#endif 1711 return xstrdup (str); 1712} 1713 1714term_ostream_t 1715term_ostream_create (int fd, const char *filename) 1716{ 1717 term_ostream_t stream = XMALLOC (struct term_ostream_representation); 1718 const char *term; 1719 1720 stream->base.vtable = &term_ostream_vtable; 1721 stream->fd = fd; 1722 stream->filename = xstrdup (filename); 1723 1724 /* Defaults. */ 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; 1738 1739 /* Retrieve the terminal type. */ 1740 term = getenv ("TERM"); 1741 if (term != NULL && term[0] != '\0') 1742 { 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. */ 1749#if HAVE_TERMINFO 1750 int err = 1; 1751 1752 if (setupterm (term, fd, &err) || err == 1) 1753 { 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")); 1768 } 1769#elif HAVE_TERMCAP 1770 struct { char buf[1024]; char canary[4]; } termcapbuf; 1771 int retval; 1772 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! */ 1778 abort (); 1779 1780 if (retval > 0) 1781 { 1782 struct { char buf[1024]; char canary[4]; } termentrybuf; 1783 char *termentryptr; 1784 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) 1790 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)); 1805 1806# ifdef __BEOS__ 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) 1812 { 1813 free (stream->set_a_foreground); 1814 stream->set_a_foreground = xstrdup ("\033[3%dm"); 1815 } 1816 if (stream->set_a_background != NULL 1817 && strcmp (stream->set_a_background, "\033[4%p1%dm") == 0) 1818 { 1819 free (stream->set_a_background); 1820 stream->set_a_background = xstrdup ("\033[4%dm"); 1821 } 1822# endif 1823 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; 1828 1829 /* Done with tgetstr. Detect possible buffer overflow. */ 1830 #undef TEBP 1831 if (memcmp (termentrybuf.canary, "CnRz", 4) != 0) 1832 /* Buffer overflow! */ 1833 abort (); 1834 } 1835#else 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"); 1849#endif 1850 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)) 1857 { 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"); 1862 } 1863 } 1864 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 1876 ? (term != NULL 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 : 1886 cm_xterm8) 1887 : cm_common8) 1888 : cm_monochrome); 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)); 1899 1900 /* Initialize the buffer. */ 1901 stream->allocated = 120; 1902 stream->buffer = XNMALLOC (stream->allocated, char); 1903 stream->attrbuffer = XNMALLOC (stream->allocated, attributes_t); 1904 stream->buflen = 0; 1905 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); 1913 1914 /* Register an exit handler. */ 1915 { 1916 static bool registered = false; 1917 if (!registered) 1918 { 1919 atexit (restore); 1920 registered = true; 1921 } 1922 } 1923 1924 return stream; 1925} 1926