1 /* 2 * Copyright (C) 1984-2022 Mark Nudelman 3 * 4 * You may distribute under the terms of either the GNU General Public 5 * License or the Less License, as specified in the README file. 6 * 7 * For more information, see the README file. 8 */ 9 10 /* 11 * Routines to manipulate the "line buffer". 12 * The line buffer holds a line of output as it is being built 13 * in preparation for output to the screen. 14 */ 15 16 #include "less.h" 17 #include "charset.h" 18 #include "position.h" 19 20 #if MSDOS_COMPILER==WIN32C 21 #define WIN32_LEAN_AND_MEAN 22 #include <windows.h> 23 #endif 24 25 #define MAX_PFX_WIDTH (MAX_LINENUM_WIDTH + MAX_STATUSCOL_WIDTH + 1) 26 static struct { 27 char *buf; /* Buffer which holds the current output line */ 28 int *attr; /* Parallel to buf, to hold attributes */ 29 int print; /* Index in buf of first printable char */ 30 int end; /* Number of chars in buf */ 31 char pfx[MAX_PFX_WIDTH]; /* Holds status column and line number */ 32 int pfx_attr[MAX_PFX_WIDTH]; 33 int pfx_end; /* Number of chars in pfx */ 34 } linebuf; 35 36 /* 37 * Buffer of ansi sequences which have been shifted off the left edge 38 * of the screen. 39 */ 40 struct xbuffer shifted_ansi; 41 42 /* 43 * Ring buffer of last ansi sequences sent. 44 * While sending a line, these will be resent at the end 45 * of any highlighted string, to restore text modes. 46 * {{ Not ideal, since we don't really know how many to resend. }} 47 */ 48 #define NUM_LAST_ANSIS 3 49 static struct xbuffer last_ansi; 50 static struct xbuffer last_ansis[NUM_LAST_ANSIS]; 51 static int curr_last_ansi; 52 53 public int size_linebuf = 0; /* Size of line buffer (and attr buffer) */ 54 static struct ansi_state *line_ansi = NULL; 55 static int ansi_in_line; 56 static int hlink_in_line; 57 static int line_mark_attr; 58 static int cshift; /* Current left-shift of output line buffer */ 59 public int hshift; /* Desired left-shift of output line buffer */ 60 public int tabstops[TABSTOP_MAX] = { 0 }; /* Custom tabstops */ 61 public int ntabstops = 1; /* Number of tabstops */ 62 public int tabdefault = 8; /* Default repeated tabstops */ 63 public POSITION highest_hilite; /* Pos of last hilite in file found so far */ 64 65 static int end_column; /* Printable length, accounting for backspaces, etc. */ 66 static int right_curr; 67 static int right_column; 68 static int overstrike; /* Next char should overstrike previous char */ 69 static int last_overstrike = AT_NORMAL; 70 static int is_null_line; /* There is no current line */ 71 static LWCHAR pendc; 72 static POSITION pendpos; 73 static char *end_ansi_chars; 74 static char *mid_ansi_chars; 75 static int in_hilite; 76 77 static int attr_swidth LESSPARAMS ((int a)); 78 static int attr_ewidth LESSPARAMS ((int a)); 79 static int do_append LESSPARAMS ((LWCHAR ch, char *rep, POSITION pos)); 80 81 extern int sigs; 82 extern int bs_mode; 83 extern int linenums; 84 extern int ctldisp; 85 extern int twiddle; 86 extern int binattr; 87 extern int status_col; 88 extern int status_col_width; 89 extern int linenum_width; 90 extern int auto_wrap, ignaw; 91 extern int bo_s_width, bo_e_width; 92 extern int ul_s_width, ul_e_width; 93 extern int bl_s_width, bl_e_width; 94 extern int so_s_width, so_e_width; 95 extern int sc_width, sc_height; 96 extern int utf_mode; 97 extern POSITION start_attnpos; 98 extern POSITION end_attnpos; 99 extern char rscroll_char; 100 extern int rscroll_attr; 101 extern int use_color; 102 extern int status_line; 103 104 static char mbc_buf[MAX_UTF_CHAR_LEN]; 105 static int mbc_buf_len = 0; 106 static int mbc_buf_index = 0; 107 static POSITION mbc_pos; 108 109 /* Configurable color map */ 110 static char color_map[AT_NUM_COLORS][12] = { 111 "Wm", /* AT_COLOR_ATTN */ 112 "kR", /* AT_COLOR_BIN */ 113 "kR", /* AT_COLOR_CTRL */ 114 "kY", /* AT_COLOR_ERROR */ 115 "c", /* AT_COLOR_LINENUM */ 116 "Wb", /* AT_COLOR_MARK */ 117 "kC", /* AT_COLOR_PROMPT */ 118 "kc", /* AT_COLOR_RSCROLL */ 119 "kG", /* AT_COLOR_SEARCH */ 120 "", /* AT_COLOR_HEADER */ 121 "", /* AT_UNDERLINE */ 122 "", /* AT_BOLD */ 123 "", /* AT_BLINK */ 124 "", /* AT_STANDOUT */ 125 }; 126 127 /* State while processing an ANSI escape sequence */ 128 struct ansi_state { 129 int hindex; /* Index into hyperlink prefix */ 130 int hlink; /* Processing hyperlink address? */ 131 int prev_esc; /* Prev char was ESC (to detect ESC-\ seq) */ 132 }; 133 134 /* 135 * Initialize from environment variables. 136 */ 137 public void 138 init_line(VOID_PARAM) 139 { 140 int ax; 141 142 end_ansi_chars = lgetenv("LESSANSIENDCHARS"); 143 if (isnullenv(end_ansi_chars)) 144 end_ansi_chars = "m"; 145 146 mid_ansi_chars = lgetenv("LESSANSIMIDCHARS"); 147 if (isnullenv(mid_ansi_chars)) 148 mid_ansi_chars = "0123456789:;[?!\"'#%()*+ "; 149 150 linebuf.buf = (char *) ecalloc(LINEBUF_SIZE, sizeof(char)); 151 linebuf.attr = (int *) ecalloc(LINEBUF_SIZE, sizeof(int)); 152 size_linebuf = LINEBUF_SIZE; 153 xbuf_init(&shifted_ansi); 154 xbuf_init(&last_ansi); 155 for (ax = 0; ax < NUM_LAST_ANSIS; ax++) 156 xbuf_init(&last_ansis[ax]); 157 curr_last_ansi = 0; 158 } 159 160 /* 161 * Expand the line buffer. 162 */ 163 static int 164 expand_linebuf(VOID_PARAM) 165 { 166 /* Double the size of the line buffer. */ 167 int new_size = size_linebuf * 2; 168 char *new_buf = (char *) calloc(new_size, sizeof(char)); 169 int *new_attr = (int *) calloc(new_size, sizeof(int)); 170 if (new_buf == NULL || new_attr == NULL) 171 { 172 if (new_attr != NULL) 173 free(new_attr); 174 if (new_buf != NULL) 175 free(new_buf); 176 return 1; 177 } 178 /* 179 * We just calloc'd the buffers; copy the old contents. 180 */ 181 memcpy(new_buf, linebuf.buf, size_linebuf * sizeof(char)); 182 memcpy(new_attr, linebuf.attr, size_linebuf * sizeof(int)); 183 free(linebuf.attr); 184 free(linebuf.buf); 185 linebuf.buf = new_buf; 186 linebuf.attr = new_attr; 187 size_linebuf = new_size; 188 return 0; 189 } 190 191 /* 192 * Is a character ASCII? 193 */ 194 public int 195 is_ascii_char(ch) 196 LWCHAR ch; 197 { 198 return (ch <= 0x7F); 199 } 200 201 /* 202 */ 203 static void 204 inc_end_column(w) 205 int w; 206 { 207 if (end_column > right_column && w > 0) 208 { 209 right_column = end_column; 210 right_curr = linebuf.end; 211 } 212 end_column += w; 213 } 214 215 /* 216 * Rewind the line buffer. 217 */ 218 public void 219 prewind(VOID_PARAM) 220 { 221 int ax; 222 223 linebuf.print = 6; /* big enough for longest UTF-8 sequence */ 224 linebuf.pfx_end = 0; 225 for (linebuf.end = 0; linebuf.end < linebuf.print; linebuf.end++) 226 { 227 linebuf.buf[linebuf.end] = '\0'; 228 linebuf.attr[linebuf.end] = 0; 229 } 230 231 end_column = 0; 232 right_curr = 0; 233 right_column = 0; 234 cshift = 0; 235 overstrike = 0; 236 last_overstrike = AT_NORMAL; 237 mbc_buf_len = 0; 238 is_null_line = 0; 239 pendc = '\0'; 240 in_hilite = 0; 241 ansi_in_line = 0; 242 hlink_in_line = 0; 243 line_mark_attr = 0; 244 xbuf_reset(&shifted_ansi); 245 xbuf_reset(&last_ansi); 246 for (ax = 0; ax < NUM_LAST_ANSIS; ax++) 247 xbuf_reset(&last_ansis[ax]); 248 curr_last_ansi = 0; 249 } 250 251 /* 252 * Set a character in the line buffer. 253 */ 254 static void 255 set_linebuf(n, ch, attr) 256 int n; 257 char ch; 258 int attr; 259 { 260 linebuf.buf[n] = ch; 261 linebuf.attr[n] = attr; 262 } 263 264 /* 265 * Append a character to the line buffer. 266 */ 267 static void 268 add_linebuf(ch, attr, w) 269 char ch; 270 int attr; 271 int w; 272 { 273 set_linebuf(linebuf.end++, ch, attr); 274 inc_end_column(w); 275 } 276 277 /* 278 * Append a string to the line buffer. 279 */ 280 static void 281 addstr_linebuf(s, attr, cw) 282 char *s; 283 int attr; 284 int cw; 285 { 286 for ( ; *s != '\0'; s++) 287 add_linebuf(*s, attr, cw); 288 } 289 290 /* 291 * Set a character in the line prefix buffer. 292 */ 293 static void 294 set_pfx(n, ch, attr) 295 int n; 296 char ch; 297 int attr; 298 { 299 linebuf.pfx[n] = ch; 300 linebuf.pfx_attr[n] = attr; 301 } 302 303 /* 304 * Append a character to the line prefix buffer. 305 */ 306 static void 307 add_pfx(ch, attr) 308 char ch; 309 int attr; 310 { 311 set_pfx(linebuf.pfx_end++, ch, attr); 312 } 313 314 /* 315 * Insert the status column and line number into the line buffer. 316 */ 317 public void 318 plinestart(pos) 319 POSITION pos; 320 { 321 LINENUM linenum = 0; 322 int i; 323 324 if (linenums == OPT_ONPLUS) 325 { 326 /* 327 * Get the line number and put it in the current line. 328 * {{ Note: since find_linenum calls forw_raw_line, 329 * it may seek in the input file, requiring the caller 330 * of plinestart to re-seek if necessary. }} 331 * {{ Since forw_raw_line modifies linebuf, we must 332 * do this first, before storing anything in linebuf. }} 333 */ 334 linenum = find_linenum(pos); 335 } 336 337 /* 338 * Display a status column if the -J option is set. 339 */ 340 if (status_col || status_line) 341 { 342 char c = posmark(pos); 343 if (c != 0) 344 line_mark_attr = AT_HILITE|AT_COLOR_MARK; 345 else if (start_attnpos != NULL_POSITION && 346 pos >= start_attnpos && pos <= end_attnpos) 347 line_mark_attr = AT_HILITE|AT_COLOR_ATTN; 348 if (status_col) 349 { 350 add_pfx(c ? c : ' ', line_mark_attr); /* column 0: status */ 351 while (linebuf.pfx_end < status_col_width) 352 add_pfx(' ', AT_NORMAL); 353 } 354 } 355 356 /* 357 * Display the line number at the start of each line 358 * if the -N option is set. 359 */ 360 if (linenums == OPT_ONPLUS) 361 { 362 char buf[INT_STRLEN_BOUND(linenum) + 2]; 363 int len; 364 365 linenum = vlinenum(linenum); 366 if (linenum == 0) 367 len = 0; 368 else 369 { 370 linenumtoa(linenum, buf); 371 len = (int) strlen(buf); 372 } 373 for (i = 0; i < linenum_width - len; i++) 374 add_pfx(' ', AT_NORMAL); 375 for (i = 0; i < len; i++) 376 add_pfx(buf[i], AT_BOLD|AT_COLOR_LINENUM); 377 add_pfx(' ', AT_NORMAL); 378 } 379 end_column = linebuf.pfx_end; 380 } 381 382 /* 383 * Return the width of the line prefix (status column and line number). 384 * {{ Actual line number can be wider than linenum_width. }} 385 */ 386 public int 387 line_pfx_width(VOID_PARAM) 388 { 389 int width = 0; 390 if (status_col) 391 width += status_col_width; 392 if (linenums == OPT_ONPLUS) 393 width += linenum_width + 1; 394 return width; 395 } 396 397 /* 398 * Shift line left so that the last char is just to the left 399 * of the first visible column. 400 */ 401 public void 402 pshift_all(VOID_PARAM) 403 { 404 int i; 405 for (i = linebuf.print; i < linebuf.end; i++) 406 if (linebuf.attr[i] == AT_ANSI) 407 xbuf_add(&shifted_ansi, linebuf.buf[i]); 408 linebuf.end = linebuf.print; 409 end_column = linebuf.pfx_end; 410 } 411 412 /* 413 * Return the printing width of the start (enter) sequence 414 * for a given character attribute. 415 */ 416 static int 417 attr_swidth(a) 418 int a; 419 { 420 int w = 0; 421 422 a = apply_at_specials(a); 423 424 if (a & AT_UNDERLINE) 425 w += ul_s_width; 426 if (a & AT_BOLD) 427 w += bo_s_width; 428 if (a & AT_BLINK) 429 w += bl_s_width; 430 if (a & AT_STANDOUT) 431 w += so_s_width; 432 433 return w; 434 } 435 436 /* 437 * Return the printing width of the end (exit) sequence 438 * for a given character attribute. 439 */ 440 static int 441 attr_ewidth(a) 442 int a; 443 { 444 int w = 0; 445 446 a = apply_at_specials(a); 447 448 if (a & AT_UNDERLINE) 449 w += ul_e_width; 450 if (a & AT_BOLD) 451 w += bo_e_width; 452 if (a & AT_BLINK) 453 w += bl_e_width; 454 if (a & AT_STANDOUT) 455 w += so_e_width; 456 457 return w; 458 } 459 460 /* 461 * Return the printing width of a given character and attribute, 462 * if the character were added after prev_ch. 463 * Adding a character with a given attribute may cause an enter or exit 464 * attribute sequence to be inserted, so this must be taken into account. 465 */ 466 public int 467 pwidth(ch, a, prev_ch, prev_a) 468 LWCHAR ch; 469 int a; 470 LWCHAR prev_ch; 471 int prev_a; 472 { 473 int w; 474 475 if (ch == '\b') 476 { 477 /* 478 * Backspace moves backwards one or two positions. 479 */ 480 if (prev_a & (AT_ANSI|AT_BINARY)) 481 return strlen(prchar('\b')); 482 return (utf_mode && is_wide_char(prev_ch)) ? -2 : -1; 483 } 484 485 if (!utf_mode || is_ascii_char(ch)) 486 { 487 if (control_char((char)ch)) 488 { 489 /* 490 * Control characters do unpredictable things, 491 * so we don't even try to guess; say it doesn't move. 492 * This can only happen if the -r flag is in effect. 493 */ 494 return (0); 495 } 496 } else 497 { 498 if (is_composing_char(ch) || is_combining_char(prev_ch, ch)) 499 { 500 /* 501 * Composing and combining chars take up no space. 502 * 503 * Some terminals, upon failure to compose a 504 * composing character with the character(s) that 505 * precede(s) it will actually take up one end_column 506 * for the composing character; there isn't much 507 * we could do short of testing the (complex) 508 * composition process ourselves and printing 509 * a binary representation when it fails. 510 */ 511 return (0); 512 } 513 } 514 515 /* 516 * Other characters take one or two columns, 517 * plus the width of any attribute enter/exit sequence. 518 */ 519 w = 1; 520 if (is_wide_char(ch)) 521 w++; 522 if (linebuf.end > 0 && !is_at_equiv(linebuf.attr[linebuf.end-1], a)) 523 w += attr_ewidth(linebuf.attr[linebuf.end-1]); 524 if (apply_at_specials(a) != AT_NORMAL && 525 (linebuf.end == 0 || !is_at_equiv(linebuf.attr[linebuf.end-1], a))) 526 w += attr_swidth(a); 527 return (w); 528 } 529 530 /* 531 * Delete to the previous base character in the line buffer. 532 */ 533 static int 534 backc(VOID_PARAM) 535 { 536 LWCHAR ch; 537 char *p; 538 539 if (linebuf.end == 0) 540 return (0); 541 p = &linebuf.buf[linebuf.end]; 542 ch = step_char(&p, -1, linebuf.buf); 543 /* Skip back to the next nonzero-width char. */ 544 while (p > linebuf.buf) 545 { 546 LWCHAR prev_ch; 547 int width; 548 linebuf.end = (int) (p - linebuf.buf); 549 prev_ch = step_char(&p, -1, linebuf.buf); 550 width = pwidth(ch, linebuf.attr[linebuf.end], prev_ch, linebuf.attr[linebuf.end-1]); 551 end_column -= width; 552 /* {{ right_column? }} */ 553 if (width > 0) 554 break; 555 ch = prev_ch; 556 } 557 return (1); 558 } 559 560 /* 561 * Is a character the end of an ANSI escape sequence? 562 */ 563 public int 564 is_ansi_end(ch) 565 LWCHAR ch; 566 { 567 if (!is_ascii_char(ch)) 568 return (0); 569 return (strchr(end_ansi_chars, (char) ch) != NULL); 570 } 571 572 /* 573 * Can a char appear in an ANSI escape sequence, before the end char? 574 */ 575 public int 576 is_ansi_middle(ch) 577 LWCHAR ch; 578 { 579 if (!is_ascii_char(ch)) 580 return (0); 581 if (is_ansi_end(ch)) 582 return (0); 583 return (strchr(mid_ansi_chars, (char) ch) != NULL); 584 } 585 586 /* 587 * Skip past an ANSI escape sequence. 588 * pp is initially positioned just after the CSI_START char. 589 */ 590 public void 591 skip_ansi(pansi, pp, limit) 592 struct ansi_state *pansi; 593 char **pp; 594 constant char *limit; 595 { 596 LWCHAR c; 597 do { 598 c = step_char(pp, +1, limit); 599 } while (*pp < limit && ansi_step(pansi, c) == ANSI_MID); 600 /* Note that we discard final char, for which is_ansi_end is true. */ 601 } 602 603 /* 604 * Determine if a character starts an ANSI escape sequence. 605 * If so, return an ansi_state struct; otherwise return NULL. 606 */ 607 public struct ansi_state * 608 ansi_start(ch) 609 LWCHAR ch; 610 { 611 struct ansi_state *pansi; 612 613 if (!IS_CSI_START(ch)) 614 return NULL; 615 pansi = ecalloc(1, sizeof(struct ansi_state)); 616 pansi->hindex = 0; 617 pansi->hlink = 0; 618 pansi->prev_esc = 0; 619 return pansi; 620 } 621 622 /* 623 * Determine whether the next char in an ANSI escape sequence 624 * ends the sequence. 625 */ 626 public int 627 ansi_step(pansi, ch) 628 struct ansi_state *pansi; 629 LWCHAR ch; 630 { 631 if (pansi->hlink) 632 { 633 /* Hyperlink ends with \7 or ESC-backslash. */ 634 if (ch == '\7') 635 return ANSI_END; 636 if (pansi->prev_esc && ch == '\\') 637 return ANSI_END; 638 pansi->prev_esc = (ch == ESC); 639 return ANSI_MID; 640 } 641 if (pansi->hindex >= 0) 642 { 643 static char hlink_prefix[] = ESCS "]8;"; 644 if (ch == hlink_prefix[pansi->hindex] || 645 (pansi->hindex == 0 && IS_CSI_START(ch))) 646 { 647 pansi->hindex++; 648 if (hlink_prefix[pansi->hindex] == '\0') 649 pansi->hlink = 1; /* now processing hyperlink addr */ 650 return ANSI_MID; 651 } 652 pansi->hindex = -1; /* not a hyperlink */ 653 } 654 /* Check for SGR sequences */ 655 if (is_ansi_middle(ch)) 656 return ANSI_MID; 657 if (is_ansi_end(ch)) 658 return ANSI_END; 659 return ANSI_ERR; 660 } 661 662 /* 663 * Free an ansi_state structure. 664 */ 665 public void 666 ansi_done(pansi) 667 struct ansi_state *pansi; 668 { 669 free(pansi); 670 } 671 672 /* 673 * Will w characters in attribute a fit on the screen? 674 */ 675 static int 676 fits_on_screen(w, a) 677 int w; 678 int a; 679 { 680 if (ctldisp == OPT_ON) 681 /* We're not counting, so say that everything fits. */ 682 return 1; 683 return (end_column - cshift + w + attr_ewidth(a) <= sc_width); 684 } 685 686 /* 687 * Append a character and attribute to the line buffer. 688 */ 689 #define STORE_CHAR(ch,a,rep,pos) \ 690 do { \ 691 if (store_char((ch),(a),(rep),(pos))) return (1); \ 692 } while (0) 693 694 static int 695 store_char(ch, a, rep, pos) 696 LWCHAR ch; 697 int a; 698 char *rep; 699 POSITION pos; 700 { 701 int w; 702 int i; 703 int replen; 704 char cs; 705 706 i = (a & (AT_UNDERLINE|AT_BOLD)); 707 if (i != AT_NORMAL) 708 last_overstrike = i; 709 710 #if HILITE_SEARCH 711 { 712 int matches; 713 int resend_last = 0; 714 int hl_attr; 715 716 if (pos == NULL_POSITION) 717 { 718 /* Color the prompt unless it has ansi sequences in it. */ 719 hl_attr = ansi_in_line ? 0 : AT_STANDOUT|AT_COLOR_PROMPT; 720 } else 721 { 722 hl_attr = is_hilited_attr(pos, pos+1, 0, &matches); 723 if (hl_attr == 0 && status_line) 724 hl_attr = line_mark_attr; 725 } 726 if (hl_attr) 727 { 728 /* 729 * This character should be highlighted. 730 * Override the attribute passed in. 731 */ 732 if (a != AT_ANSI) 733 { 734 if (highest_hilite != NULL_POSITION && pos != NULL_POSITION && pos > highest_hilite) 735 highest_hilite = pos; 736 a |= hl_attr; 737 } 738 in_hilite = 1; 739 } else 740 { 741 if (in_hilite) 742 { 743 /* 744 * This is the first non-hilited char after a hilite. 745 * Resend the last ANSI seq to restore color. 746 */ 747 resend_last = 1; 748 } 749 in_hilite = 0; 750 } 751 if (resend_last) 752 { 753 int ai; 754 for (ai = 0; ai < NUM_LAST_ANSIS; ai++) 755 { 756 int ax = (curr_last_ansi + ai) % NUM_LAST_ANSIS; 757 for (i = 0; i < last_ansis[ax].end; i++) 758 STORE_CHAR(last_ansis[ax].data[i], AT_ANSI, NULL, pos); 759 } 760 } 761 } 762 #endif 763 764 if (a == AT_ANSI) { 765 w = 0; 766 } else { 767 char *p = &linebuf.buf[linebuf.end]; 768 LWCHAR prev_ch = (linebuf.end > 0) ? step_char(&p, -1, linebuf.buf) : 0; 769 int prev_a = (linebuf.end > 0) ? linebuf.attr[linebuf.end-1] : 0; 770 w = pwidth(ch, a, prev_ch, prev_a); 771 } 772 773 if (!fits_on_screen(w, a)) 774 return (1); 775 776 if (rep == NULL) 777 { 778 cs = (char) ch; 779 rep = &cs; 780 replen = 1; 781 } else 782 { 783 replen = utf_len(rep[0]); 784 } 785 if (linebuf.end + replen >= size_linebuf-6) 786 { 787 /* 788 * Won't fit in line buffer. 789 * Try to expand it. 790 */ 791 if (expand_linebuf()) 792 return (1); 793 } 794 795 if (cshift == hshift && shifted_ansi.end > 0) 796 { 797 /* Copy shifted ANSI sequences to beginning of line. */ 798 for (i = 0; i < shifted_ansi.end; i++) 799 add_linebuf(shifted_ansi.data[i], AT_ANSI, 0); 800 xbuf_reset(&shifted_ansi); 801 } 802 /* Add the char to the buf, even if we will left-shift it next. */ 803 inc_end_column(w); 804 for (i = 0; i < replen; i++) 805 add_linebuf(*rep++, a, 0); 806 807 if (cshift < hshift) 808 { 809 /* We haven't left-shifted enough yet. */ 810 if (a == AT_ANSI) 811 xbuf_add(&shifted_ansi, ch); /* Save ANSI attributes */ 812 if (linebuf.end > linebuf.print) 813 { 814 /* Shift left enough to put last byte of this char at print-1. */ 815 int i; 816 for (i = 0; i < linebuf.print; i++) 817 { 818 linebuf.buf[i] = linebuf.buf[i+replen]; 819 linebuf.attr[i] = linebuf.attr[i+replen]; 820 } 821 linebuf.end -= replen; 822 cshift += w; 823 /* 824 * If the char we just left-shifted was double width, 825 * the 2 spaces we shifted may be too much. 826 * Represent the "half char" at start of line with a highlighted space. 827 */ 828 while (cshift > hshift) 829 { 830 add_linebuf(' ', rscroll_attr, 0); 831 cshift--; 832 } 833 } 834 } 835 return (0); 836 } 837 838 #define STORE_STRING(s,a,pos) \ 839 do { if (store_string((s),(a),(pos))) return (1); } while (0) 840 841 static int 842 store_string(s, a, pos) 843 char *s; 844 int a; 845 POSITION pos; 846 { 847 if (!fits_on_screen(strlen(s), a)) 848 return 1; 849 for ( ; *s != 0; s++) 850 STORE_CHAR(*s, a, NULL, pos); 851 return 0; 852 } 853 854 /* 855 * Append a tab to the line buffer. 856 * Store spaces to represent the tab. 857 */ 858 #define STORE_TAB(a,pos) \ 859 do { if (store_tab((a),(pos))) return (1); } while (0) 860 861 static int 862 store_tab(attr, pos) 863 int attr; 864 POSITION pos; 865 { 866 int to_tab = end_column - linebuf.pfx_end; 867 868 if (ntabstops < 2 || to_tab >= tabstops[ntabstops-1]) 869 to_tab = tabdefault - 870 ((to_tab - tabstops[ntabstops-1]) % tabdefault); 871 else 872 { 873 int i; 874 for (i = ntabstops - 2; i >= 0; i--) 875 if (to_tab >= tabstops[i]) 876 break; 877 to_tab = tabstops[i+1] - to_tab; 878 } 879 880 do { 881 STORE_CHAR(' ', attr, " ", pos); 882 } while (--to_tab > 0); 883 return 0; 884 } 885 886 #define STORE_PRCHAR(c, pos) \ 887 do { if (store_prchar((c), (pos))) return 1; } while (0) 888 889 static int 890 store_prchar(c, pos) 891 LWCHAR c; 892 POSITION pos; 893 { 894 /* 895 * Convert to printable representation. 896 */ 897 STORE_STRING(prchar(c), AT_BINARY|AT_COLOR_CTRL, pos); 898 return 0; 899 } 900 901 static int 902 flush_mbc_buf(pos) 903 POSITION pos; 904 { 905 int i; 906 907 for (i = 0; i < mbc_buf_index; i++) 908 if (store_prchar(mbc_buf[i], pos)) 909 return mbc_buf_index - i; 910 return 0; 911 } 912 913 /* 914 * Append a character to the line buffer. 915 * Expand tabs into spaces, handle underlining, boldfacing, etc. 916 * Returns 0 if ok, 1 if couldn't fit in buffer. 917 */ 918 public int 919 pappend(c, pos) 920 int c; 921 POSITION pos; 922 { 923 int r; 924 925 if (pendc) 926 { 927 if (c == '\r' && pendc == '\r') 928 return (0); 929 if (do_append(pendc, NULL, pendpos)) 930 /* 931 * Oops. We've probably lost the char which 932 * was in pendc, since caller won't back up. 933 */ 934 return (1); 935 pendc = '\0'; 936 } 937 938 if (c == '\r' && bs_mode == BS_SPECIAL) 939 { 940 if (mbc_buf_len > 0) /* utf_mode must be on. */ 941 { 942 /* Flush incomplete (truncated) sequence. */ 943 r = flush_mbc_buf(mbc_pos); 944 mbc_buf_index = r + 1; 945 mbc_buf_len = 0; 946 if (r) 947 return (mbc_buf_index); 948 } 949 950 /* 951 * Don't put the CR into the buffer until we see 952 * the next char. If the next char is a newline, 953 * discard the CR. 954 */ 955 pendc = c; 956 pendpos = pos; 957 return (0); 958 } 959 960 if (!utf_mode) 961 { 962 r = do_append(c, NULL, pos); 963 } else 964 { 965 /* Perform strict validation in all possible cases. */ 966 if (mbc_buf_len == 0) 967 { 968 retry: 969 mbc_buf_index = 1; 970 *mbc_buf = c; 971 if (IS_ASCII_OCTET(c)) 972 r = do_append(c, NULL, pos); 973 else if (IS_UTF8_LEAD(c)) 974 { 975 mbc_buf_len = utf_len(c); 976 mbc_pos = pos; 977 return (0); 978 } else 979 /* UTF8_INVALID or stray UTF8_TRAIL */ 980 r = flush_mbc_buf(pos); 981 } else if (IS_UTF8_TRAIL(c)) 982 { 983 mbc_buf[mbc_buf_index++] = c; 984 if (mbc_buf_index < mbc_buf_len) 985 return (0); 986 if (is_utf8_well_formed(mbc_buf, mbc_buf_index)) 987 r = do_append(get_wchar(mbc_buf), mbc_buf, mbc_pos); 988 else 989 /* Complete, but not shortest form, sequence. */ 990 mbc_buf_index = r = flush_mbc_buf(mbc_pos); 991 mbc_buf_len = 0; 992 } else 993 { 994 /* Flush incomplete (truncated) sequence. */ 995 r = flush_mbc_buf(mbc_pos); 996 mbc_buf_index = r + 1; 997 mbc_buf_len = 0; 998 /* Handle new char. */ 999 if (!r) 1000 goto retry; 1001 } 1002 } 1003 if (r) 1004 { 1005 /* How many chars should caller back up? */ 1006 r = (!utf_mode) ? 1 : mbc_buf_index; 1007 } 1008 return (r); 1009 } 1010 1011 static int 1012 store_control_char(ch, rep, pos) 1013 LWCHAR ch; 1014 char *rep; 1015 POSITION pos; 1016 { 1017 if (ctldisp == OPT_ON) 1018 { 1019 /* Output the character itself. */ 1020 STORE_CHAR(ch, AT_NORMAL, rep, pos); 1021 } else 1022 { 1023 /* Output a printable representation of the character. */ 1024 STORE_PRCHAR((char) ch, pos); 1025 } 1026 return (0); 1027 } 1028 1029 static int 1030 store_ansi(ch, rep, pos) 1031 LWCHAR ch; 1032 char *rep; 1033 POSITION pos; 1034 { 1035 switch (ansi_step(line_ansi, ch)) 1036 { 1037 case ANSI_MID: 1038 STORE_CHAR(ch, AT_ANSI, rep, pos); 1039 if (line_ansi->hlink) 1040 hlink_in_line = 1; 1041 xbuf_add(&last_ansi, ch); 1042 break; 1043 case ANSI_END: 1044 STORE_CHAR(ch, AT_ANSI, rep, pos); 1045 ansi_done(line_ansi); 1046 line_ansi = NULL; 1047 xbuf_add(&last_ansi, ch); 1048 xbuf_set(&last_ansis[curr_last_ansi], &last_ansi); 1049 xbuf_reset(&last_ansi); 1050 curr_last_ansi = (curr_last_ansi + 1) % NUM_LAST_ANSIS; 1051 break; 1052 case ANSI_ERR: 1053 { 1054 /* Remove whole unrecognized sequence. */ 1055 char *start = (cshift < hshift) ? shifted_ansi.data : linebuf.buf; 1056 int *end = (cshift < hshift) ? &shifted_ansi.end : &linebuf.end; 1057 char *p = start + *end; 1058 LWCHAR bch; 1059 do { 1060 bch = step_char(&p, -1, start); 1061 } while (p > start && !IS_CSI_START(bch)); 1062 *end = (int) (p - start); 1063 } 1064 xbuf_reset(&last_ansi); 1065 ansi_done(line_ansi); 1066 line_ansi = NULL; 1067 break; 1068 } 1069 return (0); 1070 } 1071 1072 static int 1073 store_bs(ch, rep, pos) 1074 LWCHAR ch; 1075 char *rep; 1076 POSITION pos; 1077 { 1078 if (bs_mode == BS_CONTROL) 1079 return store_control_char(ch, rep, pos); 1080 if (linebuf.end > 0 && 1081 ((linebuf.end <= linebuf.print && linebuf.buf[linebuf.end-1] == '\0') || 1082 (linebuf.end > 0 && linebuf.attr[linebuf.end - 1] & (AT_ANSI|AT_BINARY)))) 1083 STORE_PRCHAR('\b', pos); 1084 else if (bs_mode == BS_NORMAL) 1085 STORE_CHAR(ch, AT_NORMAL, NULL, pos); 1086 else if (bs_mode == BS_SPECIAL) 1087 overstrike = backc(); 1088 return 0; 1089 } 1090 1091 static int 1092 do_append(ch, rep, pos) 1093 LWCHAR ch; 1094 char *rep; 1095 POSITION pos; 1096 { 1097 int a = AT_NORMAL; 1098 1099 if (ctldisp == OPT_ONPLUS && line_ansi == NULL) 1100 { 1101 line_ansi = ansi_start(ch); 1102 if (line_ansi != NULL) 1103 ansi_in_line = 1; 1104 } 1105 1106 if (line_ansi != NULL) 1107 return store_ansi(ch, rep, pos); 1108 1109 if (ch == '\b') 1110 return store_bs(ch, rep, pos); 1111 1112 if (overstrike > 0) 1113 { 1114 /* 1115 * Overstrike the character at the current position 1116 * in the line buffer. This will cause either 1117 * underline (if a "_" is overstruck), 1118 * bold (if an identical character is overstruck), 1119 * or just replacing the character in the buffer. 1120 */ 1121 LWCHAR prev_ch; 1122 overstrike = utf_mode ? -1 : 0; 1123 if (utf_mode) 1124 { 1125 /* To be correct, this must be a base character. */ 1126 prev_ch = get_wchar(&linebuf.buf[linebuf.end]); 1127 } else 1128 { 1129 prev_ch = (unsigned char) linebuf.buf[linebuf.end]; 1130 } 1131 a = linebuf.attr[linebuf.end]; 1132 if (ch == prev_ch) 1133 { 1134 /* 1135 * Overstriking a char with itself means make it bold. 1136 * But overstriking an underscore with itself is 1137 * ambiguous. It could mean make it bold, or 1138 * it could mean make it underlined. 1139 * Use the previous overstrike to resolve it. 1140 */ 1141 if (ch == '_') 1142 { 1143 if ((a & (AT_BOLD|AT_UNDERLINE)) != AT_NORMAL) 1144 a |= (AT_BOLD|AT_UNDERLINE); 1145 else if (last_overstrike != AT_NORMAL) 1146 a |= last_overstrike; 1147 else 1148 a |= AT_BOLD; 1149 } else 1150 a |= AT_BOLD; 1151 } else if (ch == '_') 1152 { 1153 a |= AT_UNDERLINE; 1154 ch = prev_ch; 1155 rep = &linebuf.buf[linebuf.end]; 1156 } else if (prev_ch == '_') 1157 { 1158 a |= AT_UNDERLINE; 1159 } 1160 /* Else we replace prev_ch, but we keep its attributes. */ 1161 } else if (overstrike < 0) 1162 { 1163 if ( is_composing_char(ch) 1164 || is_combining_char(get_wchar(&linebuf.buf[linebuf.end]), ch)) 1165 /* Continuation of the same overstrike. */ 1166 a = last_overstrike; 1167 else 1168 overstrike = 0; 1169 } 1170 1171 if (ch == '\t') 1172 { 1173 /* 1174 * Expand a tab into spaces. 1175 */ 1176 switch (bs_mode) 1177 { 1178 case BS_CONTROL: 1179 return store_control_char(ch, rep, pos); 1180 case BS_NORMAL: 1181 case BS_SPECIAL: 1182 STORE_TAB(a, pos); 1183 break; 1184 } 1185 return (0); 1186 } 1187 if ((!utf_mode || is_ascii_char(ch)) && control_char((char)ch)) 1188 { 1189 return store_control_char(ch, rep, pos); 1190 } else if (utf_mode && ctldisp != OPT_ON && is_ubin_char(ch)) 1191 { 1192 STORE_STRING(prutfchar(ch), AT_BINARY, pos); 1193 } else 1194 { 1195 STORE_CHAR(ch, a, rep, pos); 1196 } 1197 return (0); 1198 } 1199 1200 /* 1201 * 1202 */ 1203 public int 1204 pflushmbc(VOID_PARAM) 1205 { 1206 int r = 0; 1207 1208 if (mbc_buf_len > 0) 1209 { 1210 /* Flush incomplete (truncated) sequence. */ 1211 r = flush_mbc_buf(mbc_pos); 1212 mbc_buf_len = 0; 1213 } 1214 return r; 1215 } 1216 1217 /* 1218 * Switch to normal attribute at end of line. 1219 */ 1220 static void 1221 add_attr_normal(VOID_PARAM) 1222 { 1223 if (ctldisp != OPT_ONPLUS || !is_ansi_end('m')) 1224 return; 1225 addstr_linebuf("\033[m", AT_ANSI, 0); 1226 if (hlink_in_line) /* Don't send hyperlink clear if we know we don't need to. */ 1227 addstr_linebuf("\033]8;;\033\\", AT_ANSI, 0); 1228 } 1229 1230 /* 1231 * Terminate the line in the line buffer. 1232 */ 1233 public void 1234 pdone(endline, chopped, forw) 1235 int endline; 1236 int chopped; 1237 int forw; 1238 { 1239 (void) pflushmbc(); 1240 1241 if (pendc && (pendc != '\r' || !endline)) 1242 /* 1243 * If we had a pending character, put it in the buffer. 1244 * But discard a pending CR if we are at end of line 1245 * (that is, discard the CR in a CR/LF sequence). 1246 */ 1247 (void) do_append(pendc, NULL, pendpos); 1248 1249 if (chopped && rscroll_char) 1250 { 1251 /* 1252 * Display the right scrolling char. 1253 * If we've already filled the rightmost screen char 1254 * (in the buffer), overwrite it. 1255 */ 1256 if (end_column >= sc_width + cshift) 1257 { 1258 /* We've already written in the rightmost char. */ 1259 end_column = right_column; 1260 linebuf.end = right_curr; 1261 } 1262 add_attr_normal(); 1263 while (end_column < sc_width-1 + cshift) 1264 { 1265 /* 1266 * Space to last (rightmost) char on screen. 1267 * This may be necessary if the char we overwrote 1268 * was double-width. 1269 */ 1270 add_linebuf(' ', rscroll_attr, 1); 1271 } 1272 /* Print rscroll char. It must be single-width. */ 1273 add_linebuf(rscroll_char, rscroll_attr, 1); 1274 } else 1275 { 1276 add_attr_normal(); 1277 } 1278 1279 /* 1280 * If we're coloring a status line, fill out the line with spaces. 1281 */ 1282 if (status_line && line_mark_attr != 0) { 1283 while (end_column +1 < sc_width + cshift) 1284 add_linebuf(' ', line_mark_attr, 1); 1285 } 1286 1287 /* 1288 * Add a newline if necessary, 1289 * and append a '\0' to the end of the line. 1290 * We output a newline if we're not at the right edge of the screen, 1291 * or if the terminal doesn't auto wrap, 1292 * or if this is really the end of the line AND the terminal ignores 1293 * a newline at the right edge. 1294 * (In the last case we don't want to output a newline if the terminal 1295 * doesn't ignore it since that would produce an extra blank line. 1296 * But we do want to output a newline if the terminal ignores it in case 1297 * the next line is blank. In that case the single newline output for 1298 * that blank line would be ignored!) 1299 */ 1300 if (end_column < sc_width + cshift || !auto_wrap || (endline && ignaw) || ctldisp == OPT_ON) 1301 { 1302 add_linebuf('\n', AT_NORMAL, 0); 1303 } 1304 else if (ignaw && end_column >= sc_width + cshift && forw) 1305 { 1306 /* 1307 * Terminals with "ignaw" don't wrap until they *really* need 1308 * to, i.e. when the character *after* the last one to fit on a 1309 * line is output. But they are too hard to deal with when they 1310 * get in the state where a full screen width of characters 1311 * have been output but the cursor is sitting on the right edge 1312 * instead of at the start of the next line. 1313 * So we nudge them into wrapping by outputting a space 1314 * character plus a backspace. But do this only if moving 1315 * forward; if we're moving backward and drawing this line at 1316 * the top of the screen, the space would overwrite the first 1317 * char on the next line. We don't need to do this "nudge" 1318 * at the top of the screen anyway. 1319 */ 1320 add_linebuf(' ', AT_NORMAL, 1); 1321 add_linebuf('\b', AT_NORMAL, -1); 1322 } 1323 set_linebuf(linebuf.end, '\0', AT_NORMAL); 1324 } 1325 1326 /* 1327 * Set an attribute on each char of the line in the line buffer. 1328 */ 1329 public void 1330 set_attr_line(a) 1331 int a; 1332 { 1333 int i; 1334 1335 for (i = linebuf.print; i < linebuf.end; i++) 1336 linebuf.attr[i] |= a; 1337 } 1338 1339 /* 1340 * Set the char to be displayed in the status column. 1341 */ 1342 public void 1343 set_status_col(c, attr) 1344 int c; 1345 int attr; 1346 { 1347 set_pfx(0, c, attr); 1348 } 1349 1350 /* 1351 * Get a character from the current line. 1352 * Return the character as the function return value, 1353 * and the character attribute in *ap. 1354 */ 1355 public int 1356 gline(i, ap) 1357 int i; 1358 int *ap; 1359 { 1360 if (is_null_line) 1361 { 1362 /* 1363 * If there is no current line, we pretend the line is 1364 * either "~" or "", depending on the "twiddle" flag. 1365 */ 1366 if (twiddle) 1367 { 1368 if (i == 0) 1369 { 1370 *ap = AT_BOLD; 1371 return '~'; 1372 } 1373 --i; 1374 } 1375 /* Make sure we're back to AT_NORMAL before the '\n'. */ 1376 *ap = AT_NORMAL; 1377 return i ? '\0' : '\n'; 1378 } 1379 1380 if (i < linebuf.pfx_end) 1381 { 1382 *ap = linebuf.pfx_attr[i]; 1383 return linebuf.pfx[i]; 1384 } 1385 i += linebuf.print - linebuf.pfx_end; 1386 *ap = linebuf.attr[i]; 1387 return (linebuf.buf[i] & 0xFF); 1388 } 1389 1390 /* 1391 * Indicate that there is no current line. 1392 */ 1393 public void 1394 null_line(VOID_PARAM) 1395 { 1396 is_null_line = 1; 1397 cshift = 0; 1398 } 1399 1400 /* 1401 * Analogous to forw_line(), but deals with "raw lines": 1402 * lines which are not split for screen width. 1403 * {{ This is supposed to be more efficient than forw_line(). }} 1404 */ 1405 public POSITION 1406 forw_raw_line(curr_pos, linep, line_lenp) 1407 POSITION curr_pos; 1408 char **linep; 1409 int *line_lenp; 1410 { 1411 int n; 1412 int c; 1413 POSITION new_pos; 1414 1415 if (curr_pos == NULL_POSITION || ch_seek(curr_pos) || 1416 (c = ch_forw_get()) == EOI) 1417 return (NULL_POSITION); 1418 1419 n = 0; 1420 for (;;) 1421 { 1422 if (c == '\n' || c == EOI || ABORT_SIGS()) 1423 { 1424 new_pos = ch_tell(); 1425 break; 1426 } 1427 if (n >= size_linebuf-1) 1428 { 1429 if (expand_linebuf()) 1430 { 1431 /* 1432 * Overflowed the input buffer. 1433 * Pretend the line ended here. 1434 */ 1435 new_pos = ch_tell() - 1; 1436 break; 1437 } 1438 } 1439 linebuf.buf[n++] = c; 1440 c = ch_forw_get(); 1441 } 1442 linebuf.buf[n] = '\0'; 1443 if (linep != NULL) 1444 *linep = linebuf.buf; 1445 if (line_lenp != NULL) 1446 *line_lenp = n; 1447 return (new_pos); 1448 } 1449 1450 /* 1451 * Analogous to back_line(), but deals with "raw lines". 1452 * {{ This is supposed to be more efficient than back_line(). }} 1453 */ 1454 public POSITION 1455 back_raw_line(curr_pos, linep, line_lenp) 1456 POSITION curr_pos; 1457 char **linep; 1458 int *line_lenp; 1459 { 1460 int n; 1461 int c; 1462 POSITION new_pos; 1463 1464 if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() || 1465 ch_seek(curr_pos-1)) 1466 return (NULL_POSITION); 1467 1468 n = size_linebuf; 1469 linebuf.buf[--n] = '\0'; 1470 for (;;) 1471 { 1472 c = ch_back_get(); 1473 if (c == '\n' || ABORT_SIGS()) 1474 { 1475 /* 1476 * This is the newline ending the previous line. 1477 * We have hit the beginning of the line. 1478 */ 1479 new_pos = ch_tell() + 1; 1480 break; 1481 } 1482 if (c == EOI) 1483 { 1484 /* 1485 * We have hit the beginning of the file. 1486 * This must be the first line in the file. 1487 * This must, of course, be the beginning of the line. 1488 */ 1489 new_pos = ch_zero(); 1490 break; 1491 } 1492 if (n <= 0) 1493 { 1494 int old_size_linebuf = size_linebuf; 1495 char *fm; 1496 char *to; 1497 if (expand_linebuf()) 1498 { 1499 /* 1500 * Overflowed the input buffer. 1501 * Pretend the line ended here. 1502 */ 1503 new_pos = ch_tell() + 1; 1504 break; 1505 } 1506 /* 1507 * Shift the data to the end of the new linebuf. 1508 */ 1509 for (fm = linebuf.buf + old_size_linebuf - 1, 1510 to = linebuf.buf + size_linebuf - 1; 1511 fm >= linebuf.buf; fm--, to--) 1512 *to = *fm; 1513 n = size_linebuf - old_size_linebuf; 1514 } 1515 linebuf.buf[--n] = c; 1516 } 1517 if (linep != NULL) 1518 *linep = &linebuf.buf[n]; 1519 if (line_lenp != NULL) 1520 *line_lenp = size_linebuf - 1 - n; 1521 return (new_pos); 1522 } 1523 1524 /* 1525 * Append a string to the line buffer. 1526 */ 1527 static int 1528 pappstr(str) 1529 constant char *str; 1530 { 1531 while (*str != '\0') 1532 { 1533 if (pappend(*str++, NULL_POSITION)) 1534 /* Doesn't fit on screen. */ 1535 return 1; 1536 } 1537 return 0; 1538 } 1539 1540 /* 1541 * Load a string into the line buffer. 1542 * If the string is too long to fit on the screen, 1543 * truncate the beginning of the string to fit. 1544 */ 1545 public void 1546 load_line(str) 1547 constant char *str; 1548 { 1549 int save_hshift = hshift; 1550 1551 hshift = 0; 1552 for (;;) 1553 { 1554 prewind(); 1555 if (pappstr(str) == 0) 1556 break; 1557 /* 1558 * Didn't fit on screen; increase left shift by one. 1559 * {{ This gets very inefficient if the string 1560 * is much longer than the screen width. }} 1561 */ 1562 hshift += 1; 1563 } 1564 set_linebuf(linebuf.end, '\0', AT_NORMAL); 1565 hshift = save_hshift; 1566 } 1567 1568 /* 1569 * Find the shift necessary to show the end of the longest displayed line. 1570 */ 1571 public int 1572 rrshift(VOID_PARAM) 1573 { 1574 POSITION pos; 1575 int save_width; 1576 int line; 1577 int longest = 0; 1578 1579 save_width = sc_width; 1580 sc_width = INT_MAX; 1581 pos = position(TOP); 1582 for (line = 0; line < sc_height && pos != NULL_POSITION; line++) 1583 { 1584 pos = forw_line(pos); 1585 if (end_column > longest) 1586 longest = end_column; 1587 } 1588 sc_width = save_width; 1589 if (longest < sc_width) 1590 return 0; 1591 return longest - sc_width; 1592 } 1593 1594 /* 1595 * Get the color_map index associated with a given attribute. 1596 */ 1597 static int 1598 color_index(attr) 1599 int attr; 1600 { 1601 if (use_color) 1602 { 1603 switch (attr & AT_COLOR) 1604 { 1605 case AT_COLOR_ATTN: return 0; 1606 case AT_COLOR_BIN: return 1; 1607 case AT_COLOR_CTRL: return 2; 1608 case AT_COLOR_ERROR: return 3; 1609 case AT_COLOR_LINENUM: return 4; 1610 case AT_COLOR_MARK: return 5; 1611 case AT_COLOR_PROMPT: return 6; 1612 case AT_COLOR_RSCROLL: return 7; 1613 case AT_COLOR_SEARCH: return 8; 1614 case AT_COLOR_HEADER: return 9; 1615 } 1616 } 1617 if (attr & AT_UNDERLINE) 1618 return 10; 1619 if (attr & AT_BOLD) 1620 return 11; 1621 if (attr & AT_BLINK) 1622 return 12; 1623 if (attr & AT_STANDOUT) 1624 return 13; 1625 return -1; 1626 } 1627 1628 /* 1629 * Set the color string to use for a given attribute. 1630 */ 1631 public int 1632 set_color_map(attr, colorstr) 1633 int attr; 1634 char *colorstr; 1635 { 1636 int cx = color_index(attr); 1637 if (cx < 0) 1638 return -1; 1639 if (strlen(colorstr)+1 > sizeof(color_map[cx])) 1640 return -1; 1641 if (*colorstr != '\0' && parse_color(colorstr, NULL, NULL) == CT_NULL) 1642 return -1; 1643 strcpy(color_map[cx], colorstr); 1644 return 0; 1645 } 1646 1647 /* 1648 * Get the color string to use for a given attribute. 1649 */ 1650 public char * 1651 get_color_map(attr) 1652 int attr; 1653 { 1654 int cx = color_index(attr); 1655 if (cx < 0) 1656 return NULL; 1657 return color_map[cx]; 1658 } 1659