1 /* 2 * Copyright (C) 1984-2026 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 size_t print; /* Index in buf of first printable char */ 30 size_t end; /* Number of chars in buf */ 31 size_t prev_end; /* Number of chars in buf for previous line */ 32 char pfx[MAX_PFX_WIDTH]; /* Holds status column and line number */ 33 int pfx_attr[MAX_PFX_WIDTH]; 34 size_t pfx_end; /* Number of chars in pfx */ 35 } linebuf; 36 37 /* 38 * Buffer of ansi sequences which have been shifted off the left edge 39 * of the screen. 40 */ 41 static struct xbuffer shifted_ansi; 42 43 /* 44 * Ring buffer of last ansi sequences sent. 45 * While sending a line, these will be resent at the end 46 * of any highlighted string, to restore text modes. 47 * {{ Not ideal, since we don't really know how many to resend. }} 48 */ 49 #define NUM_LAST_ANSIS 3 50 static struct xbuffer last_ansi; 51 static struct xbuffer last_ansis[NUM_LAST_ANSIS]; 52 static int curr_last_ansi; 53 54 static size_t size_linebuf = 0; /* Size of line buffer (and attr buffer) */ 55 static struct ansi_state *line_ansi = NULL; 56 static lbool ansi_in_line; 57 static int ff_starts_line; 58 static int line_mark_attr; 59 static int cshift; /* Current left-shift of output line buffer */ 60 public int hshift; /* Desired left-shift of output line buffer */ 61 public int tabstops[TABSTOP_MAX] = { 0 }; /* Custom tabstops */ 62 public int ntabstops = 1; /* Number of tabstops */ 63 public int tabdefault = 8; /* Default repeated tabstops */ 64 public POSITION highest_hilite; /* Pos of last hilite in file found so far */ 65 static POSITION line_pos; 66 static POSITION line_contig_pos = NULL_POSITION; /* One after last byte processed */ 67 68 static int end_column; /* Printable length, accounting for backspaces, etc. */ 69 static int right_curr; 70 static int right_column; 71 static int overstrike; /* Next char should overstrike previous char */ 72 static int last_overstrike = AT_NORMAL; 73 static lbool is_null_line; /* There is no current line */ 74 static LWCHAR pendc; 75 static POSITION pendpos; 76 static constant char *end_ansi_chars; 77 static constant char *mid_ansi_chars; 78 static constant char *osc_ansi_chars; 79 static int osc_ansi_allow_count; 80 static long *osc_ansi_allow; 81 static lbool in_osc8_link; 82 static lbool in_hilite; 83 static lbool clear_after_line; 84 85 static int attr_swidth(int a); 86 static int attr_ewidth(int a); 87 static int do_append(LWCHAR ch, constant char *rep, POSITION pos); 88 static void resend_last_ansi(POSITION pos); 89 90 extern int sigs; 91 extern int bs_mode; 92 extern int proc_backspace; 93 extern int proc_tab; 94 extern int proc_return; 95 extern int linenums; 96 extern int ctldisp; 97 extern int twiddle; 98 extern int status_col; 99 extern int status_col_width; 100 extern int linenum_width; 101 extern int auto_wrap, defer_wrap; 102 extern int bo_s_width, bo_e_width; 103 extern int ul_s_width, ul_e_width; 104 extern int bl_s_width, bl_e_width; 105 extern int so_s_width, so_e_width; 106 extern int sc_width, sc_height; 107 extern int utf_mode; 108 extern POSITION start_attnpos; 109 extern POSITION end_attnpos; 110 extern LWCHAR rscroll_char; 111 extern int rscroll_attr; 112 extern int use_color; 113 extern int status_line; 114 115 static char mbc_buf[MAX_UTF_CHAR_LEN]; 116 static int mbc_buf_len = 0; 117 static int mbc_buf_index = 0; 118 static POSITION mbc_pos; 119 static size_t saved_line_end; 120 static int saved_end_column; 121 122 /* Configurable color map */ 123 struct color_map { int attr; char color[12]; }; 124 static struct color_map color_map[] = { 125 { AT_UNDERLINE, "" }, 126 { AT_BOLD, "" }, 127 { AT_BLINK, "" }, 128 { AT_STANDOUT, "" }, 129 { AT_COLOR_ATTN, "Wm" }, 130 { AT_COLOR_BIN, "kR" }, 131 { AT_COLOR_CTRL, "kR" }, 132 { AT_COLOR_ERROR, "kY" }, 133 { AT_COLOR_LINENUM, "c*" }, 134 { AT_COLOR_MARK, "Wb" }, 135 { AT_COLOR_PROMPT, "kC" }, 136 { AT_COLOR_RSCROLL, "kc" }, 137 { AT_COLOR_HEADER, "" }, 138 { AT_COLOR_SEARCH, "kG" }, 139 { AT_COLOR_TILDE, "-d" }, 140 { AT_COLOR_TARGET, "-u" }, 141 { AT_COLOR_OSC8, "-u" }, 142 { AT_COLOR_SUBSEARCH(1), "ky" }, 143 { AT_COLOR_SUBSEARCH(2), "wb" }, 144 { AT_COLOR_SUBSEARCH(3), "YM" }, 145 { AT_COLOR_SUBSEARCH(4), "Yr" }, 146 { AT_COLOR_SUBSEARCH(5), "Wc" }, 147 }; 148 149 /* State while processing an ANSI escape sequence */ 150 struct ansi_state { 151 osc8_state ostate; /* State while processing OSC8 sequence */ 152 unsigned int otype; /* OSC type number */ 153 unsigned int escs_in_seq; 154 }; 155 156 /* 157 * Initialize from environment variables. 158 */ 159 public void init_line(void) 160 { 161 int ax; 162 constant char *s; 163 164 end_ansi_chars = lgetenv("LESSANSIENDCHARS"); 165 if (isnullenv(end_ansi_chars)) 166 end_ansi_chars = "m"; 167 168 mid_ansi_chars = lgetenv("LESSANSIMIDCHARS"); 169 if (isnullenv(mid_ansi_chars)) 170 mid_ansi_chars = "0123456789:;[?!\"'#%()*+ "; 171 172 osc_ansi_chars = lgetenv("LESSANSIOSCCHARS"); 173 if (isnullenv(osc_ansi_chars)) 174 osc_ansi_chars = ""; 175 176 osc_ansi_allow_count = 0; 177 s = lgetenv("LESSANSIOSCALLOW"); 178 if (!isnullenv(s)) 179 { 180 struct xbuffer xbuf; 181 xbuf_init(&xbuf); 182 for (;;) 183 { 184 long num; 185 s = skipspc(s); 186 if (*s == '\0') 187 break; 188 num = lstrtoulc(s, &s, 10); 189 s = skipspc(s); 190 if (*s == ',') 191 ++s; 192 xbuf_add_data(&xbuf, &num, sizeof(num)); 193 ++osc_ansi_allow_count; 194 } 195 osc_ansi_allow = (long *) xbuf.data; 196 } 197 198 linebuf.buf = (char *) ecalloc(LINEBUF_SIZE, sizeof(char)); 199 linebuf.attr = (int *) ecalloc(LINEBUF_SIZE, sizeof(int)); 200 size_linebuf = LINEBUF_SIZE; 201 xbuf_init(&shifted_ansi); 202 xbuf_init(&last_ansi); 203 for (ax = 0; ax < NUM_LAST_ANSIS; ax++) 204 xbuf_init(&last_ansis[ax]); 205 curr_last_ansi = 0; 206 } 207 208 /* 209 * Expand the line buffer. 210 */ 211 static int expand_linebuf(void) 212 { 213 /* Double the size of the line buffer. */ 214 size_t new_size = size_linebuf * 2; 215 char *new_buf = (char *) calloc(new_size, sizeof(char)); 216 int *new_attr = (int *) calloc(new_size, sizeof(int)); 217 if (new_buf == NULL || new_attr == NULL) 218 { 219 if (new_attr != NULL) 220 free(new_attr); 221 if (new_buf != NULL) 222 free(new_buf); 223 return 1; 224 } 225 /* 226 * We just calloc'd the buffers; copy the old contents. 227 */ 228 memcpy(new_buf, linebuf.buf, size_linebuf * sizeof(char)); 229 memcpy(new_attr, linebuf.attr, size_linebuf * sizeof(int)); 230 free(linebuf.attr); 231 free(linebuf.buf); 232 linebuf.buf = new_buf; 233 linebuf.attr = new_attr; 234 size_linebuf = new_size; 235 return 0; 236 } 237 238 /* 239 * Is a character ASCII? 240 */ 241 public lbool is_ascii_char(LWCHAR ch) 242 { 243 return (ch <= 0x7F); 244 } 245 246 /* 247 */ 248 static void inc_end_column(int w) 249 { 250 if (end_column > right_column && w > 0) 251 { 252 right_column = end_column; 253 right_curr = (int) linebuf.end; 254 } 255 end_column += w; 256 } 257 258 public POSITION line_position(void) 259 { 260 return line_pos; 261 } 262 263 /* 264 * Is this byte the next one after the previous byte processed? 265 */ 266 public lbool is_line_contig_pos(POSITION pos) 267 { 268 return pos == line_contig_pos; 269 } 270 271 /* 272 * Set the position of the next byte to be processed. 273 */ 274 public void set_line_contig_pos(POSITION pos) 275 { 276 line_contig_pos = pos; 277 } 278 279 /* 280 * Copy any ANSI sequences from line buffer to shifted_ansi. 281 */ 282 static void pshift(size_t end) 283 { 284 size_t i; 285 for (i = linebuf.print; i < end; i++) 286 if (linebuf.attr[i] == AT_ANSI) 287 xbuf_add_char(&shifted_ansi, linebuf.buf[i]); 288 } 289 290 /* 291 * Rewind the line buffer. 292 */ 293 public void prewind(lbool contig) 294 { 295 int ax; 296 297 xbuf_reset(&shifted_ansi); 298 if (contig) 299 { 300 if (linebuf.prev_end != 0) 301 pshift(linebuf.prev_end); 302 /* Don't reset in_osc8_link, since we may be in a wrapped OSC 8 sequence. */ 303 } else 304 { 305 in_osc8_link = FALSE; 306 } 307 linebuf.print = 6; /* big enough for longest UTF-8 sequence */ 308 linebuf.pfx_end = 0; 309 for (linebuf.end = 0; linebuf.end < linebuf.print; linebuf.end++) 310 { 311 linebuf.buf[linebuf.end] = '\0'; 312 linebuf.attr[linebuf.end] = 0; 313 } 314 315 end_column = 0; 316 right_curr = 0; 317 right_column = 0; 318 cshift = 0; 319 overstrike = 0; 320 last_overstrike = AT_NORMAL; 321 mbc_buf_len = 0; 322 is_null_line = FALSE; 323 pendc = '\0'; 324 in_hilite = FALSE; 325 ansi_in_line = FALSE; 326 ff_starts_line = -1; 327 clear_after_line = FALSE; 328 line_mark_attr = 0; 329 line_pos = NULL_POSITION; 330 xbuf_reset(&last_ansi); 331 for (ax = 0; ax < NUM_LAST_ANSIS; ax++) 332 xbuf_reset(&last_ansis[ax]); 333 curr_last_ansi = 0; 334 } 335 336 /* 337 * Set a character in the line buffer. 338 */ 339 static void set_linebuf(size_t n, char ch, int attr) 340 { 341 if (n >= size_linebuf) 342 { 343 /* 344 * Won't fit in line buffer. 345 * Try to expand it. 346 */ 347 if (expand_linebuf()) 348 return; 349 } 350 linebuf.buf[n] = ch; 351 linebuf.attr[n] = attr; 352 } 353 354 /* 355 * Append a character to the line buffer. 356 */ 357 static void add_linebuf(char ch, int attr, int w) 358 { 359 set_linebuf(linebuf.end++, ch, attr); 360 inc_end_column(w); 361 } 362 363 /* 364 * Append a string to the line buffer. 365 */ 366 static void addstr_linebuf(constant char *s, int attr, int cw) 367 { 368 for ( ; *s != '\0'; s++) 369 add_linebuf(*s, attr, cw); 370 } 371 372 /* 373 * Set a character in the line prefix buffer. 374 */ 375 static void set_pfx(size_t n, char ch, int attr) 376 { 377 linebuf.pfx[n] = ch; 378 linebuf.pfx_attr[n] = attr; 379 } 380 381 /* 382 * Append a character to the line prefix buffer. 383 */ 384 static void add_pfx(char ch, int attr) 385 { 386 set_pfx(linebuf.pfx_end++, ch, attr); 387 } 388 389 /* 390 * Insert the status column and line number into the line buffer. 391 */ 392 public void plinestart(POSITION line_pos, POSITION curr_pos) 393 { 394 LINENUM linenum = 0; 395 396 if (linenums == OPT_ONPLUS) 397 { 398 /* 399 * Get the line number and put it in the current line. 400 * {{ Note: since find_linenum calls forw_raw_line, 401 * it may seek in the input file, requiring the caller 402 * of plinestart to re-seek if necessary. }} 403 * {{ Since forw_raw_line modifies linebuf, we must 404 * do this first, before storing anything in linebuf. }} 405 */ 406 linenum = find_linenum(line_pos); 407 } 408 409 /* 410 * Display a status column if the -J option is set. 411 */ 412 if (status_col || status_line) 413 { 414 char c = posmark(curr_pos); 415 if (c != 0) 416 line_mark_attr = AT_HILITE|AT_COLOR_MARK; 417 else if (start_attnpos != NULL_POSITION && 418 line_pos >= start_attnpos && line_pos <= end_attnpos) 419 line_mark_attr = AT_HILITE|AT_COLOR_ATTN; 420 if (status_col) 421 { 422 add_pfx(c ? c : ' ', line_mark_attr); /* column 0: status */ 423 while (linebuf.pfx_end < (size_t) status_col_width) /*{{type-issue}}*/ 424 add_pfx(' ', AT_NORMAL); 425 } 426 } 427 428 /* 429 * Display the line number at the start of each line 430 * if the -N option is set. 431 */ 432 if (linenums == OPT_ONPLUS) 433 { 434 char buf[INT_STRLEN_BOUND(linenum) + 2]; 435 size_t len; 436 size_t i; 437 438 linenum = vlinenum(linenum); 439 if (linenum == 0) 440 len = 0; 441 else 442 { 443 linenumtoa(linenum, buf, 10); 444 len = strlen(buf); 445 } 446 for (i = 0; i + len < (size_t) linenum_width; i++) 447 add_pfx(' ', AT_NORMAL); 448 for (i = 0; i < len; i++) 449 add_pfx(buf[i], use_color ? AT_COLOR_LINENUM : AT_BOLD); 450 add_pfx(' ', AT_NORMAL); 451 } 452 end_column = (int) linebuf.pfx_end; /*{{type-issue}}*/ 453 } 454 455 /* 456 * Return the width of the line prefix (status column and line number). 457 * {{ Actual line number can be wider than linenum_width. }} 458 */ 459 public unsigned line_pfx_width(void) 460 { 461 int width = 0; 462 if (status_col) 463 width += status_col_width; 464 if (linenums == OPT_ONPLUS) 465 width += linenum_width + 1; 466 return width; 467 } 468 469 /* 470 * Shift line left so that the last char is just to the left 471 * of the first visible column. 472 */ 473 public void pshift_all(void) 474 { 475 pshift(linebuf.end); 476 linebuf.end = linebuf.print; 477 end_column = (int) linebuf.pfx_end; /*{{type-issue}}*/ 478 line_pos = NULL_POSITION; 479 } 480 481 /* 482 * Return the printing width of the start (enter) sequence 483 * for a given character attribute. 484 */ 485 static int attr_swidth(int a) 486 { 487 int w = 0; 488 489 a = apply_at_specials(a); 490 491 if (a & AT_UNDERLINE) 492 w += ul_s_width; 493 if (a & AT_BOLD) 494 w += bo_s_width; 495 if (a & AT_BLINK) 496 w += bl_s_width; 497 if (a & AT_STANDOUT) 498 w += so_s_width; 499 500 return w; 501 } 502 503 /* 504 * Return the printing width of the end (exit) sequence 505 * for a given character attribute. 506 */ 507 static int attr_ewidth(int a) 508 { 509 int w = 0; 510 511 a = apply_at_specials(a); 512 513 if (a & AT_UNDERLINE) 514 w += ul_e_width; 515 if (a & AT_BOLD) 516 w += bo_e_width; 517 if (a & AT_BLINK) 518 w += bl_e_width; 519 if (a & AT_STANDOUT) 520 w += so_e_width; 521 522 return w; 523 } 524 525 /* 526 * Return the printing width of a given character and attribute, 527 * if the character were added after prev_ch. 528 * Adding a character with a given attribute may cause an enter or exit 529 * attribute sequence to be inserted, so this must be taken into account. 530 */ 531 public int pwidth(LWCHAR ch, int a, LWCHAR prev_ch, int prev_a) 532 { 533 int w; 534 535 if (ch == '\b') 536 { 537 /* 538 * Backspace moves backwards one or two positions. 539 */ 540 if (prev_a & (AT_ANSI|AT_BINARY)) 541 return (int) strlen(prchar('\b')); /*{{type-issue}}*/ 542 return (utf_mode && is_wide_char(prev_ch)) ? -2 : -1; 543 } 544 545 if (!utf_mode || is_ascii_char(ch)) 546 { 547 if (control_char(ch)) 548 { 549 /* 550 * Control characters do unpredictable things, 551 * so we don't even try to guess; say it doesn't move. 552 * This can only happen if the -r flag is in effect. 553 */ 554 return (0); 555 } 556 } else 557 { 558 if (ch == VARSEL_15) 559 /* If prev char was double width, make it single width. */ 560 return (prev_ch != 0 && pwidth(prev_ch, a, 0, 0) == 2) ? -1 : 0; 561 if (ch == VARSEL_16) 562 /* If prev char was single width, make it double width. */ 563 return (prev_ch != 0 && pwidth(prev_ch, a, 0, 0) == 1) ? +1 : 0; 564 if (is_composing_char(ch) || is_combining_char(prev_ch, ch)) 565 { 566 /* 567 * Composing and combining chars take up no space. 568 * 569 * Some terminals, upon failure to compose a 570 * composing character with the character(s) that 571 * precede(s) it will actually take up one end_column 572 * for the composing character; there isn't much 573 * we could do short of testing the (complex) 574 * composition process ourselves and printing 575 * a binary representation when it fails. 576 */ 577 return (0); 578 } 579 } 580 581 /* 582 * Other characters take one or two columns, 583 * plus the width of any attribute enter/exit sequence. 584 */ 585 w = 1; 586 if (is_wide_char(ch)) 587 w++; 588 if (linebuf.end > 0 && !is_at_equiv(linebuf.attr[linebuf.end-1], a)) 589 w += attr_ewidth(linebuf.attr[linebuf.end-1]); 590 if (apply_at_specials(a) != AT_NORMAL && 591 (linebuf.end == 0 || !is_at_equiv(linebuf.attr[linebuf.end-1], a))) 592 w += attr_swidth(a); 593 return (w); 594 } 595 596 /* 597 * Delete to the previous base character in the line buffer. 598 */ 599 static int backc(void) 600 { 601 LWCHAR ch; 602 char *p; 603 604 if (linebuf.end == 0) 605 return (0); 606 p = &linebuf.buf[linebuf.end]; 607 ch = step_char(&p, -1, linebuf.buf); 608 /* Skip back to the next nonzero-width char. */ 609 while (p > linebuf.buf) 610 { 611 LWCHAR prev_ch; 612 int width; 613 linebuf.end = ptr_diff(p, linebuf.buf); 614 prev_ch = step_char(&p, -1, linebuf.buf); 615 width = pwidth(ch, linebuf.attr[linebuf.end], prev_ch, linebuf.attr[linebuf.end-1]); 616 end_column -= width; 617 /* {{ right_column? }} */ 618 if (width > 0) 619 break; 620 ch = prev_ch; 621 } 622 return (1); 623 } 624 625 /* 626 * Preserve the current position in the line buffer (for word wrapping). 627 */ 628 public void savec(void) 629 { 630 saved_line_end = linebuf.end; 631 saved_end_column = end_column; 632 } 633 634 /* 635 * Restore the position in the line buffer (start of line for word wrapping). 636 */ 637 public void loadc(void) 638 { 639 linebuf.end = saved_line_end; 640 end_column = saved_end_column; 641 } 642 643 /* 644 * Is a character the end of an ANSI escape sequence? 645 */ 646 public lbool is_ansi_end(LWCHAR ch) 647 { 648 if (!is_ascii_char(ch)) 649 return (FALSE); 650 return (ch != 0 && strchr(end_ansi_chars, (char) ch) != NULL); 651 } 652 653 /* 654 * Can a char appear in an ANSI escape sequence, before the end char? 655 */ 656 public lbool is_ansi_middle(LWCHAR ch) 657 { 658 if (!is_ascii_char(ch)) 659 return (FALSE); 660 if (is_ansi_end(ch)) 661 return (FALSE); 662 return (ch != 0 && strchr(mid_ansi_chars, (char) ch) != NULL); 663 } 664 665 /* 666 * Skip past an ANSI escape sequence. 667 * pp is initially positioned just after the CSI_START char. 668 */ 669 public void skip_ansi(struct ansi_state *pansi, LWCHAR ch, constant char **pp, constant char *limit) 670 { 671 ansi_step(pansi, ch); 672 do { 673 ch = step_charc(pp, +1, limit); 674 } while (*pp < limit && ansi_step(pansi, ch) == ANSI_MID); 675 /* Note that we discard final char, for which is_ansi_end is true. */ 676 } 677 678 /* 679 * Determine if a character starts an ANSI escape sequence. 680 * If so, return an ansi_state struct; otherwise return NULL. 681 */ 682 public struct ansi_state * ansi_start(LWCHAR ch) 683 { 684 struct ansi_state *pansi; 685 686 if (!IS_CSI_START(ch)) 687 return NULL; 688 pansi = ecalloc(1, sizeof(struct ansi_state)); 689 pansi->ostate = OSC_START; 690 pansi->otype = 0; 691 pansi->escs_in_seq = 0; 692 return pansi; 693 } 694 695 /* 696 * Is a character a valid intro char for an OSC sequence? 697 * An intro char is the one immediately after the ESC, usually ']'. 698 */ 699 static lbool valid_osc_intro(char ch, lbool content) 700 { 701 constant char *p = strchr(osc_ansi_chars, ch); 702 if (p == NULL || *p == '\0') 703 return FALSE; 704 return (!content || p[1] == '*'); 705 } 706 707 /* 708 * Is a given number a valid OSC type? 709 */ 710 static lbool valid_osc_type(int otype, lbool content) 711 { 712 int i; 713 if (!content) 714 return TRUE; 715 if (otype == 8) 716 return TRUE; 717 for (i = 0; i < osc_ansi_allow_count; i++) 718 if (osc_ansi_allow[i] == otype) 719 return TRUE; 720 return FALSE; 721 } 722 723 /* 724 * Helper function for ansi_step. 725 */ 726 static ansi_state osc_return(struct ansi_state *pansi, osc8_state ostate, ansi_state astate) 727 { 728 pansi->ostate = ostate; 729 return astate; 730 } 731 732 /* 733 * Determine whether the next char in an ANSI escape sequence 734 * ends the sequence. 735 */ 736 static ansi_state ansi_step2(struct ansi_state *pansi, LWCHAR ch, lbool content) 737 { 738 /* 739 * Pass thru OS commands. Assume OSC commands do not move the cursor. 740 * A "typed" OSC starts with ESC ] <integer> <semicolon>, followed by an 741 * arbitrary string, and ends with a String Terminator (ESC-backslash or BEL). 742 * An untyped OSC starts with ESC ] or ESC x where x is in osc_ansi_chars, 743 * and ends with ST. 744 * The only typed OSC we actually parse is OSC 8. 745 */ 746 switch (pansi->ostate) 747 { 748 case OSC_START: 749 if (IS_CSI_START(ch)) 750 return osc_return(pansi, OSC_INTRO, ANSI_MID); 751 break; 752 case OSC_INTRO: 753 if (ch == ']') 754 return osc_return(pansi, OSC_TYPENUM, ANSI_MID); 755 if (is_ascii_char(ch) && valid_osc_intro((char) ch, content)) 756 return osc_return(pansi, OSC_STRING, ANSI_MID); 757 if (IS_CSI_START(ch)) 758 return osc_return(pansi, OSC_INTRO, ANSI_MID); 759 /* ESC not followed by bracket; restart. */ 760 pansi->ostate = OSC_START; 761 break; 762 case OSC_TYPENUM: 763 if (ch >= '0' && ch <= '9') 764 { 765 if (ckd_mul(&pansi->otype, pansi->otype, 10) || 766 ckd_add(&pansi->otype, pansi->otype, ch - '0')) 767 return osc_return(pansi, OSC_STRING, ANSI_MID); 768 return osc_return(pansi, OSC_TYPENUM, ANSI_MID); 769 } 770 if (ch == ';') 771 return osc_return(pansi, (pansi->otype == 8) ? OSC8_PARAMS : OSC_STRING, ANSI_MID); 772 /* OSC is untyped */ 773 if (IS_CSI_START(ch)) 774 return osc_return(pansi, OSC_STRING_CSI, ANSI_MID); 775 if (ch == '\7') 776 return osc_return(pansi, OSC_END, ANSI_END); 777 return osc_return(pansi, OSC_STRING, ANSI_MID); 778 case OSC8_PARAMS: 779 if (ch == ';') 780 return osc_return(pansi, OSC8_URI, ANSI_MID); 781 /* FALLTHRU */ 782 case OSC8_URI: 783 case OSC_STRING: 784 /* Look for ST. */ 785 if (ch == '\7') 786 return osc_return(pansi, OSC_END, valid_osc_type(pansi->otype, content) ? ANSI_END : ANSI_ERR); 787 if (IS_CSI_START(ch)) 788 { 789 pansi->escs_in_seq++; 790 return osc_return(pansi, 791 pansi->ostate == OSC8_URI ? OSC8_URI_CSI : OSC_STRING_CSI, ANSI_MID); 792 } 793 /* Stay in same ostate */ 794 return ANSI_MID; 795 case OSC8_URI_CSI: 796 case OSC_STRING_CSI: 797 /* Got ESC of ST, expect backslash next. */ 798 if (ch == '\\') 799 return osc_return(pansi, OSC_END, valid_osc_type(pansi->otype, content) ? ANSI_END : ANSI_ERR); 800 /* ESC not followed by backslash. */ 801 return osc_return(pansi, pansi->ostate == OSC8_URI_CSI ? OSC8_URI : OSC_STRING, ANSI_MID); 802 case OSC_END: 803 return ANSI_END; 804 case OSC8_NOT: 805 /* cannot happen */ 806 break; 807 } 808 /* Check for SGR sequences */ 809 if (is_ansi_middle(ch)) 810 return ANSI_MID; 811 if (is_ansi_end(ch)) 812 return ANSI_END; 813 return ANSI_ERR; 814 } 815 816 public ansi_state ansi_step(struct ansi_state *pansi, LWCHAR ch) 817 { 818 return ansi_step2(pansi, ch, TRUE); 819 } 820 821 /* 822 * Return the current OSC8 parsing state. 823 */ 824 public osc8_state ansi_osc8_state(struct ansi_state *pansi) 825 { 826 return pansi->ostate; 827 } 828 829 /* 830 * Free an ansi_state structure. 831 */ 832 public void ansi_done(struct ansi_state *pansi) 833 { 834 free(pansi); 835 } 836 837 /* 838 * Will w characters in attribute a fit on the screen? 839 */ 840 static lbool fits_on_screen(int w, int a) 841 { 842 if (ctldisp == OPT_ON) 843 /* We're not counting, so say that everything fits. */ 844 return TRUE; 845 return (end_column - cshift + w + attr_ewidth(a) <= sc_width); 846 } 847 848 /* 849 * Append a character and attribute to the line buffer. 850 */ 851 #define STORE_CHAR(ch,a,rep,pos) \ 852 do { \ 853 if (store_char((ch),(a),(rep),(pos))) return (1); \ 854 } while (0) 855 856 static int store_char(LWCHAR ch, int a, constant char *rep, POSITION pos) 857 { 858 int w; 859 size_t i; 860 size_t replen; 861 char cs; 862 int ov; 863 lbool need_shift; 864 865 ov = (a & (AT_UNDERLINE|AT_BOLD)); 866 if (ov != AT_NORMAL) 867 last_overstrike = ov; 868 869 #if HILITE_SEARCH 870 { 871 int matches; 872 int hl_attr = 0; 873 int link_attr = 0; 874 875 if (pos != NULL_POSITION && a != AT_ANSI) 876 { 877 hl_attr = is_hilited_attr(pos, pos+1, 0, &matches); 878 if (hl_attr == 0 && status_line) 879 hl_attr = line_mark_attr; 880 if (in_osc8_link) 881 { 882 if (hl_attr != 0) 883 link_attr = hl_attr | AT_UNDERLINE; 884 else 885 link_attr = use_color ? AT_COLOR_OSC8 : AT_UNDERLINE; 886 } 887 } 888 if (hl_attr) 889 { 890 /* 891 * This character should be highlighted. 892 * Override the attribute passed in. 893 */ 894 a |= hl_attr; 895 if (highest_hilite != NULL_POSITION && pos != NULL_POSITION && pos > highest_hilite) 896 highest_hilite = pos; 897 in_hilite = TRUE; 898 } else 899 { 900 a |= link_attr; 901 if (in_hilite) 902 { 903 /* 904 * This is the first non-hilited char after a hilite. 905 * Resend the last ANSI sequence(s) to restore color. 906 */ 907 in_hilite = FALSE; 908 resend_last_ansi(pos); 909 } 910 } 911 } 912 #endif 913 914 if (a == AT_ANSI) { 915 w = 0; 916 } else { 917 char *p = &linebuf.buf[linebuf.end]; 918 LWCHAR prev_ch = (linebuf.end > 0) ? step_char(&p, -1, linebuf.buf) : 0; 919 int prev_a = (linebuf.end > 0) ? linebuf.attr[linebuf.end-1] : 0; 920 w = pwidth(ch, a, prev_ch, prev_a); 921 } 922 923 if (!fits_on_screen(w, a)) 924 return (1); 925 926 if (rep == NULL) 927 { 928 cs = (char) ch; 929 rep = &cs; 930 replen = 1; 931 } else 932 { 933 replen = (size_t) utf_len(rep[0]); /*{{type-issue}}*/ 934 } 935 936 if (cshift == hshift) 937 { 938 if (line_pos == NULL_POSITION) 939 line_pos = pos; 940 if (shifted_ansi.end > 0) 941 { 942 /* Copy shifted ANSI sequences to beginning of line. */ 943 for (i = 0; i < shifted_ansi.end; i++) 944 add_linebuf((char) shifted_ansi.data[i], AT_ANSI, 0); 945 xbuf_reset(&shifted_ansi); 946 } 947 if (linebuf.end == linebuf.print+1) 948 { 949 /* If first char is a placeholder, the one before it is double-width. 950 * VS15 changes the double-width char to single-width, so replace the 951 * placeholder with this VS15. */ 952 if (ch == VARSEL_15 && (linebuf.attr[linebuf.end-1] & AT_PLACEHOLDER)) 953 { 954 linebuf.end--; 955 inc_end_column(-1); 956 } 957 } else if (linebuf.end == linebuf.print) 958 { 959 /* VS16 changes the previous single-width char to double-width. 960 * Add a placeholder to represent the second half of the 961 * double-width char. */ 962 if (ch == VARSEL_16) 963 { 964 char *p = &linebuf.buf[linebuf.end]; 965 LWCHAR prev_ch = (linebuf.end > 0) ? step_char(&p, -1, linebuf.buf) : 0; 966 if (prev_ch != 0 && pwidth(prev_ch, a, 0, 0) == 1) 967 add_linebuf(' ', rscroll_attr|AT_PLACEHOLDER, 0); 968 } 969 } 970 } 971 972 /* Add the char to the buf, even if we will left-shift it next. */ 973 need_shift = (cshift < hshift); 974 if (!need_shift && w <= 0 && linebuf.end <= linebuf.print+1 && is_composing_char(ch) && 975 (linebuf.end == linebuf.print || (linebuf.end == linebuf.print+1 && (linebuf.attr[linebuf.end-1] & AT_PLACEHOLDER)))) 976 need_shift = TRUE; 977 inc_end_column(w); 978 for (i = 0; i < replen; i++) 979 add_linebuf(*rep++, a, 0); 980 981 if (need_shift) 982 { 983 /* We haven't left-shifted enough yet. */ 984 if (a == AT_ANSI) 985 xbuf_add_char(&shifted_ansi, (char) ch); /* Save ANSI attributes */ 986 if (linebuf.end > linebuf.print) 987 { 988 /* Shift left enough to put last byte of this char at print-1. */ 989 size_t i; 990 for (i = 0; i < linebuf.print; i++) 991 { 992 linebuf.buf[i] = linebuf.buf[i+replen]; 993 linebuf.attr[i] = linebuf.attr[i+replen]; 994 } 995 linebuf.end -= replen; 996 cshift += w; 997 /* 998 * If the char we just left-shifted was double width, 999 * the 2 spaces we shifted may be too much. 1000 * Represent the "half char" at start of line with a highlighted space. 1001 */ 1002 while (cshift > hshift) 1003 { 1004 add_linebuf(' ', rscroll_attr|AT_PLACEHOLDER, 0); 1005 cshift--; 1006 } 1007 } 1008 } 1009 return (0); 1010 } 1011 1012 #define STORE_STRING(s,a,pos) \ 1013 do { if (store_string((s),(a),(pos))) return (1); } while (0) 1014 1015 static int store_string(constant char *s, int a, POSITION pos) 1016 { 1017 if (!fits_on_screen((int) strlen(s), a)) 1018 return 1; 1019 for ( ; *s != 0; s++) 1020 STORE_CHAR((LWCHAR)*s, a, NULL, pos); 1021 return 0; 1022 } 1023 1024 /* 1025 * Return number of spaces from col to the next tab stop. 1026 */ 1027 static int tab_spaces(int col) 1028 { 1029 int to_tab = col - (int) linebuf.pfx_end; /*{{type-issue}}*/ 1030 1031 if (ntabstops < 2 || to_tab >= tabstops[ntabstops-1]) 1032 to_tab = tabdefault - 1033 ((to_tab - tabstops[ntabstops-1]) % tabdefault); 1034 else 1035 { 1036 int i; 1037 for (i = ntabstops - 2; i >= 0; i--) 1038 if (to_tab >= tabstops[i]) 1039 break; 1040 to_tab = tabstops[i+1] - to_tab; 1041 } 1042 return to_tab; 1043 } 1044 1045 /* 1046 * Append a tab to the line buffer. 1047 * Store spaces to represent the tab. 1048 */ 1049 #define STORE_TAB(a,pos) \ 1050 do { if (store_tab((a),(pos))) return (1); } while (0) 1051 1052 static int store_tab(int attr, POSITION pos) 1053 { 1054 int to_tab = tab_spaces(end_column); 1055 do { 1056 STORE_CHAR(' ', attr, " ", pos); 1057 } while (--to_tab > 0); 1058 return 0; 1059 } 1060 1061 #define STORE_PRCHAR(c, pos) \ 1062 do { if (store_prchar((c), (pos))) return 1; } while (0) 1063 1064 static int store_prchar(LWCHAR c, POSITION pos) 1065 { 1066 /* 1067 * Convert to printable representation. 1068 */ 1069 STORE_STRING(prchar(c), AT_BINARY|AT_COLOR_CTRL, pos); 1070 return 0; 1071 } 1072 1073 static int flush_mbc_buf(POSITION pos) 1074 { 1075 int i; 1076 1077 for (i = 0; i < mbc_buf_index; i++) 1078 if (store_prchar((LWCHAR) (unsigned char) mbc_buf[i], pos)) 1079 return mbc_buf_index - i; 1080 return 0; 1081 } 1082 1083 /* 1084 * Append a character to the line buffer. 1085 * Expand tabs into spaces, handle underlining, boldfacing, etc. 1086 * Returns 0 if ok, 1 if couldn't fit in buffer. 1087 */ 1088 public int pappend_b(char c, POSITION pos, lbool before_pendc) 1089 { 1090 LWCHAR ch = (unsigned char) c; 1091 int r; 1092 1093 if (pendc && !before_pendc) 1094 { 1095 if (ch == '\r' && pendc == '\r') 1096 return (0); 1097 if (do_append(pendc, NULL, pendpos)) 1098 /* 1099 * Oops. We've probably lost the char which 1100 * was in pendc, since caller won't back up. 1101 */ 1102 return (1); 1103 pendc = '\0'; 1104 } 1105 1106 if (ch == '\r' && (proc_return == OPT_ON || (bs_mode == BS_SPECIAL && proc_return == OPT_OFF))) 1107 { 1108 if (mbc_buf_len > 0) /* utf_mode must be on. */ 1109 { 1110 /* Flush incomplete (truncated) sequence. */ 1111 r = flush_mbc_buf(mbc_pos); 1112 mbc_buf_index = r + 1; 1113 mbc_buf_len = 0; 1114 if (r) 1115 return (mbc_buf_index); 1116 } 1117 1118 /* 1119 * Don't put the CR into the buffer until we see 1120 * the next char. If the next char is a newline, 1121 * discard the CR. 1122 */ 1123 pendc = ch; 1124 pendpos = pos; 1125 return (0); 1126 } 1127 1128 if (!utf_mode) 1129 { 1130 r = do_append(ch, NULL, pos); 1131 } else 1132 { 1133 /* Perform strict validation in all possible cases. */ 1134 if (mbc_buf_len == 0) 1135 { 1136 retry: 1137 mbc_buf_index = 1; 1138 *mbc_buf = c; 1139 if (IS_ASCII_OCTET(c)) 1140 r = do_append(ch, NULL, pos); 1141 else if (IS_UTF8_LEAD(c)) 1142 { 1143 mbc_buf_len = utf_len(c); 1144 mbc_pos = pos; 1145 return (0); 1146 } else 1147 /* UTF8_INVALID or stray UTF8_TRAIL */ 1148 r = flush_mbc_buf(pos); 1149 } else if (IS_UTF8_TRAIL(c)) 1150 { 1151 mbc_buf[mbc_buf_index++] = c; 1152 if (mbc_buf_index < mbc_buf_len) 1153 return (0); 1154 if (is_utf8_well_formed(mbc_buf, mbc_buf_index)) 1155 r = do_append(get_wchar(mbc_buf), mbc_buf, mbc_pos); 1156 else 1157 /* Complete, but not shortest form, sequence. */ 1158 mbc_buf_index = r = flush_mbc_buf(mbc_pos); 1159 mbc_buf_len = 0; 1160 } else 1161 { 1162 /* Flush incomplete (truncated) sequence. */ 1163 r = flush_mbc_buf(mbc_pos); 1164 mbc_buf_index = r + 1; 1165 mbc_buf_len = 0; 1166 /* Handle new char. */ 1167 if (!r) 1168 goto retry; 1169 } 1170 } 1171 if (r) 1172 { 1173 /* How many chars should caller back up? */ 1174 r = (!utf_mode) ? 1 : mbc_buf_index; 1175 } 1176 return (r); 1177 } 1178 1179 public int pappend(char c, POSITION pos) 1180 { 1181 if (ff_starts_line < 0) 1182 ff_starts_line = (c == CONTROL('L')); 1183 return pappend_b(c, pos, FALSE); 1184 } 1185 1186 public lbool line_is_ff(void) 1187 { 1188 return (ff_starts_line == 1); 1189 } 1190 1191 static int store_control_char(LWCHAR ch, constant char *rep, POSITION pos) 1192 { 1193 if (ctldisp == OPT_ON) 1194 { 1195 /* Output the character itself. */ 1196 STORE_CHAR(ch, AT_NORMAL, rep, pos); 1197 } else 1198 { 1199 /* Output a printable representation of the character. */ 1200 STORE_PRCHAR(ch, pos); 1201 } 1202 return (0); 1203 } 1204 1205 /* 1206 * Remove invalid ANSI sequence. 1207 */ 1208 static void remove_ansi(void) 1209 { 1210 constant char *start = (cshift < hshift) ? xbuf_char_data(&shifted_ansi): linebuf.buf; 1211 size_t *end = (cshift < hshift) ? &shifted_ansi.end : &linebuf.end; 1212 constant char *p = start + *end; 1213 LWCHAR bch; 1214 do { 1215 bch = step_charc(&p, -1, start); 1216 } while (p > start && (!IS_CSI_START(bch) || line_ansi->escs_in_seq-- > 0)); 1217 *end = ptr_diff(p, start); 1218 xbuf_reset(&last_ansi); 1219 } 1220 1221 static int store_ansi(LWCHAR ch, constant char *rep, POSITION pos) 1222 { 1223 osc8_state prev_ostate = ansi_osc8_state(line_ansi); 1224 switch (ansi_step2(line_ansi, ch, pos != NULL_POSITION)) 1225 { 1226 case ANSI_MID: { 1227 STORE_CHAR(ch, AT_ANSI, rep, pos); 1228 switch (ansi_osc8_state(line_ansi)) 1229 { 1230 case OSC8_PARAMS: in_osc8_link = FALSE; break; 1231 case OSC8_URI: if (prev_ostate == OSC8_URI) in_osc8_link = TRUE; break; 1232 default: break; 1233 } 1234 xbuf_add_char(&last_ansi, (char) ch); 1235 break; } 1236 case ANSI_END: 1237 STORE_CHAR(ch, AT_ANSI, rep, pos); 1238 /* Save the ANSI sequence, in case we need to resend it. 1239 * But don't save OSC 8 sequences; they never need to be resent. */ 1240 if (ansi_osc8_state(line_ansi) == OSC_START) 1241 { 1242 xbuf_add_char(&last_ansi, (char) ch); 1243 xbuf_set(&last_ansis[curr_last_ansi], &last_ansi); 1244 curr_last_ansi = (curr_last_ansi + 1) % NUM_LAST_ANSIS; 1245 } 1246 xbuf_reset(&last_ansi); 1247 ansi_done(line_ansi); 1248 line_ansi = NULL; 1249 /* After ending an OSC 8 sequence, resend the last SGR sequence to restore color. */ 1250 if (prev_ostate != OSC_START) 1251 resend_last_ansi(pos); 1252 break; 1253 case ANSI_ERR: 1254 remove_ansi(); 1255 ansi_done(line_ansi); 1256 line_ansi = NULL; 1257 break; 1258 default: 1259 break; 1260 } 1261 return (0); 1262 } 1263 1264 /* 1265 * Resend the last ANSI sequence(s) to restore color after we have temporarily 1266 * overridden the color with a hilite or OSC 8 link. 1267 */ 1268 static void resend_last_ansi(POSITION pos) 1269 { 1270 int ai; 1271 for (ai = 0; ai < NUM_LAST_ANSIS; ai++) 1272 { 1273 int ax = (curr_last_ansi + ai) % NUM_LAST_ANSIS; 1274 size_t i; 1275 for (i = 0; i < last_ansis[ax].end; i++) 1276 store_char(last_ansis[ax].data[i], AT_ANSI, NULL, pos); 1277 } 1278 } 1279 1280 static int store_bs(LWCHAR ch, constant char *rep, POSITION pos) 1281 { 1282 if (proc_backspace == OPT_ONPLUS || (bs_mode == BS_CONTROL && proc_backspace == OPT_OFF)) 1283 return store_control_char(ch, rep, pos); 1284 if (linebuf.end > 0 && 1285 ((linebuf.end <= linebuf.print && linebuf.buf[linebuf.end-1] == '\0') || 1286 (linebuf.end > 0 && linebuf.attr[linebuf.end - 1] & (AT_ANSI|AT_BINARY)))) 1287 STORE_PRCHAR('\b', pos); 1288 else if (proc_backspace == OPT_OFF && bs_mode == BS_NORMAL) 1289 STORE_CHAR(ch, AT_NORMAL, NULL, pos); 1290 else if (proc_backspace == OPT_ON || (bs_mode == BS_SPECIAL && proc_backspace == OPT_OFF)) 1291 overstrike = backc(); 1292 return 0; 1293 } 1294 1295 static int do_append(LWCHAR ch, constant char *rep, POSITION pos) 1296 { 1297 int a = AT_NORMAL; 1298 int in_overstrike = overstrike; 1299 1300 if ((ctldisp == OPT_ONPLUS || pos == NULL_POSITION) && line_ansi == NULL) 1301 { 1302 line_ansi = ansi_start(ch); 1303 if (line_ansi != NULL) 1304 ansi_in_line = TRUE; 1305 } 1306 1307 overstrike = 0; 1308 if (line_ansi != NULL) 1309 return store_ansi(ch, rep, pos); 1310 1311 if (ch == '\b') 1312 return store_bs(ch, rep, pos); 1313 1314 if (in_overstrike > 0) 1315 { 1316 /* 1317 * Overstrike the character at the current position 1318 * in the line buffer. This will cause either 1319 * underline (if a "_" is overstruck), 1320 * bold (if an identical character is overstruck), 1321 * or just replacing the character in the buffer. 1322 */ 1323 LWCHAR prev_ch; 1324 overstrike = utf_mode ? -1 : 0; 1325 if (utf_mode) 1326 { 1327 /* To be correct, this must be a base character. */ 1328 prev_ch = get_wchar(&linebuf.buf[linebuf.end]); 1329 } else 1330 { 1331 prev_ch = (unsigned char) linebuf.buf[linebuf.end]; 1332 } 1333 a = linebuf.attr[linebuf.end]; 1334 if (ch == prev_ch) 1335 { 1336 /* 1337 * Overstriking a char with itself means make it bold. 1338 * But overstriking an underscore with itself is 1339 * ambiguous. It could mean make it bold, or 1340 * it could mean make it underlined. 1341 * Use the previous overstrike to resolve it. 1342 */ 1343 if (ch == '_') 1344 { 1345 if ((a & (AT_BOLD|AT_UNDERLINE)) != AT_NORMAL) 1346 a |= (AT_BOLD|AT_UNDERLINE); 1347 else if (last_overstrike != AT_NORMAL) 1348 a |= last_overstrike; 1349 else 1350 a |= AT_BOLD; 1351 } else 1352 a |= AT_BOLD; 1353 } else if (ch == '_') 1354 { 1355 a |= AT_UNDERLINE; 1356 ch = prev_ch; 1357 rep = &linebuf.buf[linebuf.end]; 1358 } else if (prev_ch == '_') 1359 { 1360 a |= AT_UNDERLINE; 1361 } 1362 /* Else we replace prev_ch, but we keep its attributes. */ 1363 } else if (in_overstrike < 0) 1364 { 1365 if ( is_composing_char(ch) 1366 || is_combining_char(get_wchar(&linebuf.buf[linebuf.end]), ch)) 1367 /* Continuation of the same overstrike. */ 1368 a = last_overstrike; 1369 else 1370 overstrike = 0; 1371 } 1372 1373 if (is_omit_char(ch)) 1374 { 1375 if (bs_mode == BS_CONTROL) 1376 { 1377 if (utf_mode) 1378 STORE_STRING(prutfchar(ch), AT_BINARY, pos); 1379 else 1380 STORE_PRCHAR(ch, pos); 1381 } 1382 return (0); /* omit the character. */ 1383 } 1384 if (ch == '\t') 1385 { 1386 /* 1387 * Expand a tab into spaces. 1388 */ 1389 if (proc_tab == OPT_ONPLUS || (bs_mode == BS_CONTROL && proc_tab == OPT_OFF)) 1390 return store_control_char(ch, rep, pos); 1391 STORE_TAB(a, pos); 1392 return (0); 1393 } 1394 if ((!utf_mode || is_ascii_char(ch)) && control_char(ch)) 1395 { 1396 return store_control_char(ch, rep, pos); 1397 } else if (utf_mode && ctldisp != OPT_ON && is_ubin_char(ch)) 1398 { 1399 STORE_STRING(prutfchar(ch), AT_BINARY, pos); 1400 } else 1401 { 1402 STORE_CHAR(ch, a, rep, pos); 1403 } 1404 return (0); 1405 } 1406 1407 /* 1408 * 1409 */ 1410 public int pflushmbc(void) 1411 { 1412 int r = 0; 1413 1414 if (mbc_buf_len > 0) 1415 { 1416 /* Flush incomplete (truncated) sequence. */ 1417 r = flush_mbc_buf(mbc_pos); 1418 mbc_buf_len = 0; 1419 } 1420 return r; 1421 } 1422 1423 /* 1424 * Switch to normal attribute at end of line. 1425 */ 1426 static void add_attr_normal(void) 1427 { 1428 if (line_ansi != NULL) 1429 { 1430 switch (line_ansi->ostate) 1431 { 1432 case OSC_START: 1433 case OSC_END: 1434 break; 1435 default: 1436 /* We're in an unterminated OSC sequence; remove it. */ 1437 remove_ansi(); 1438 break; 1439 } 1440 ansi_done(line_ansi); 1441 line_ansi = NULL; 1442 } 1443 if (ctldisp != OPT_ONPLUS || !is_ansi_end('m')) 1444 return; 1445 addstr_linebuf("\033[m", AT_ANSI, 0); 1446 if (in_osc8_link) /* Don't send hyperlink clear if we know we don't need to. */ 1447 addstr_linebuf("\033]8;;\033\\", AT_ANSI, 0); 1448 } 1449 1450 /* 1451 * Terminate the line in the line buffer. 1452 */ 1453 public void pdone(lbool endline, lbool chopped, lbool forw, lbool full_pad) 1454 { 1455 (void) pflushmbc(); 1456 linebuf.prev_end = (!endline && !chopped) ? linebuf.end : 0; 1457 1458 if (pendc && (pendc != '\r' || !endline)) 1459 /* 1460 * If we had a pending character, put it in the buffer. 1461 * But discard a pending CR if we are at end of line 1462 * (that is, discard the CR in a CR/LF sequence). 1463 */ 1464 (void) do_append(pendc, NULL, pendpos); 1465 1466 if (chopped && rscroll_char) 1467 { 1468 char rscroll_utf8[MAX_UTF_CHAR_LEN+1]; 1469 char *up = rscroll_utf8; 1470 1471 /* 1472 * Display the right scrolling char. 1473 * If we've already filled the rightmost screen char 1474 * (in the buffer), overwrite it. 1475 */ 1476 if (end_column >= sc_width + cshift) 1477 { 1478 /* We've already written in the rightmost char. */ 1479 end_column = right_column; 1480 linebuf.end = (size_t) right_curr; 1481 } 1482 add_attr_normal(); 1483 while (end_column < sc_width-1 + cshift) 1484 { 1485 /* 1486 * Space to last (rightmost) char on screen. 1487 * This may be necessary if the char we overwrote 1488 * was double-width. 1489 */ 1490 add_linebuf(' ', 0, 1); 1491 } 1492 /* Print rscroll char. */ 1493 put_wchar(&up, rscroll_char); 1494 *up = '\0'; 1495 addstr_linebuf(rscroll_utf8, rscroll_attr, 0); 1496 inc_end_column(1); /* assume rscroll_char is single-width */ 1497 } else 1498 { 1499 add_attr_normal(); 1500 } 1501 1502 /* 1503 * If we're coloring a status line, fill out the line with spaces. 1504 */ 1505 if (status_line && (line_mark_attr != 0 || full_pad)) { 1506 while (end_column < sc_width + cshift) 1507 add_linebuf(' ', line_mark_attr, 1); 1508 } 1509 1510 /* 1511 * Add a newline if necessary, 1512 * and append a '\0' to the end of the line. 1513 * We output a newline if we're not at the right edge of the screen, 1514 * or if the terminal doesn't auto wrap, 1515 * or if this is really the end of the line AND the terminal ignores 1516 * a newline at the right edge. 1517 * (In the last case we don't want to output a newline if the terminal 1518 * doesn't ignore it since that would produce an extra blank line. 1519 * But we do want to output a newline if the terminal ignores it in case 1520 * the next line is blank. In that case the single newline output for 1521 * that blank line would be ignored!) 1522 */ 1523 if (end_column < sc_width + cshift || !auto_wrap || (endline && defer_wrap) || ctldisp == OPT_ON) 1524 { 1525 add_linebuf('\n', AT_NORMAL, 0); 1526 } 1527 else if (defer_wrap && end_column >= sc_width + cshift && forw) 1528 { 1529 /* 1530 * Terminals with "defer_wrap" don't wrap until they *really* need 1531 * to, i.e. when the character *after* the last one to fit on a 1532 * line is output. But they are too hard to deal with when they 1533 * get in the state where a full screen width of characters 1534 * have been output but the cursor is sitting on the right edge 1535 * instead of at the start of the next line. 1536 * So we nudge them into wrapping by outputting a space 1537 * character plus a backspace. But do this only if moving 1538 * forward; if we're moving backward and drawing this line at 1539 * the top of the screen, the space would overwrite the first 1540 * char on the next line. We don't need to do this "nudge" 1541 * at the top of the screen anyway. 1542 */ 1543 add_linebuf(' ', AT_NORMAL, 1); 1544 add_linebuf('\b', AT_NORMAL, -1); 1545 } 1546 /* 1547 * If a terminal moves the cursor to the next line immediately after 1548 * writing into the last char of a line, the following line may get 1549 * colored with the last char's background color before the color 1550 * reset sequence is sent. Clear the line to reset the background color. 1551 */ 1552 if (auto_wrap && !defer_wrap && end_column >= sc_width + cshift) 1553 clear_after_line = TRUE; 1554 set_linebuf(linebuf.end, '\0', AT_NORMAL); 1555 } 1556 1557 /* 1558 * Return the column number (screen position) of a given file position in its line. 1559 * linepos = position of first char in line 1560 * spos = position of char being queried 1561 * saved_pos = position of a known column, or NULL_POSITION if no known column 1562 * saved_col = column number of a known column, or -1 if no known column 1563 * 1564 * This attempts to mimic the logic in pappend() and the store_*() functions. 1565 * Duplicating this complicated logic is not a good design. 1566 */ 1567 1568 struct col_pos { int col; POSITION pos; }; 1569 1570 static void col_vs_pos(POSITION linepos, mutable struct col_pos *cp, POSITION saved_pos, int saved_col) 1571 { 1572 int col = (saved_col < 0) ? 0 : saved_col; 1573 LWCHAR prev_ch = 0; 1574 struct ansi_state *pansi = NULL; 1575 char utf8_buf[MAX_UTF_CHAR_LEN]; 1576 int utf8_len = 0; 1577 POSITION chpos; 1578 1579 if (ch_seek(saved_pos != NULL_POSITION ? saved_pos : linepos)) 1580 return; 1581 for (;;) 1582 { 1583 int ich; 1584 char ch; 1585 int cw = 0; 1586 1587 chpos = ch_tell(); 1588 ich = ch_forw_get(); 1589 ch = (char) ich; 1590 if (ich == EOI || ch == '\n') 1591 break; 1592 if (pansi != NULL) 1593 { 1594 if (ansi_step(pansi, ch) != ANSI_MID) 1595 { 1596 ansi_done(pansi); 1597 pansi = NULL; 1598 } 1599 } else if (ctldisp == OPT_ONPLUS && (pansi = ansi_start(ch)) != NULL) 1600 { 1601 /* start of ansi sequence */ 1602 (void) ansi_step(pansi, ch); 1603 } else if (ch == '\b') 1604 { 1605 if (proc_backspace == OPT_ONPLUS || (bs_mode == BS_CONTROL && proc_backspace == OPT_OFF)) 1606 cw = (int) strlen(prchar(ch)); 1607 else 1608 cw = (utf_mode && is_wide_char(prev_ch)) ? -2 : -1; 1609 } else if (ch == '\t') 1610 { 1611 if (proc_tab == OPT_ONPLUS || (bs_mode == BS_CONTROL && proc_tab == OPT_OFF)) 1612 cw = (int) strlen(prchar(ch)); 1613 else 1614 cw = tab_spaces(col); 1615 } else if ((!utf_mode || is_ascii_char(ch)) && control_char(ch)) 1616 { 1617 cw = (int) strlen(prchar(ch)); 1618 } else if (utf8_len < MAX_UTF_CHAR_LEN) 1619 { 1620 utf8_buf[utf8_len++] = ch; 1621 if (is_utf8_well_formed(utf8_buf, utf8_len)) 1622 { 1623 LWCHAR wch = get_wchar(utf8_buf); 1624 int attr = 0; /* {{ ignoring attribute is not correct for magic cookie terminals }} */ 1625 utf8_len = 0; 1626 if (is_omit_char(wch)) 1627 { 1628 if (bs_mode == BS_CONTROL) 1629 cw = strlen(utf_mode ? prutfchar(wch) : prchar(wch)); 1630 } else if (utf_mode && ctldisp != OPT_ON && is_ubin_char(wch)) 1631 cw = (int) strlen(prutfchar(wch)); 1632 else 1633 cw = pwidth(wch, attr, prev_ch, attr); 1634 prev_ch = wch; 1635 } 1636 } else 1637 { 1638 utf8_len = 0; /* flush invalid UTF-8 */ 1639 } 1640 1641 if (cp->pos != NULL_POSITION && chpos == cp->pos) /* found the position we want */ 1642 break; 1643 if (cp->col >= 0 && col >= cp->col && cw > 0) /* found the column we want */ 1644 break; 1645 col += cw; 1646 prev_ch = ch; 1647 } 1648 cp->col = col; 1649 cp->pos = chpos; 1650 } 1651 1652 public int col_from_pos(POSITION linepos, POSITION spos, POSITION saved_pos, int saved_col) 1653 { 1654 struct col_pos cp; 1655 cp.pos = spos; 1656 cp.col = -1; 1657 col_vs_pos(linepos, &cp, saved_pos, saved_col); 1658 return cp.col; 1659 } 1660 1661 public POSITION pos_from_col(POSITION linepos, int col, POSITION saved_pos, int saved_col) 1662 { 1663 struct col_pos cp; 1664 cp.col = col + hshift - line_pfx_width(); 1665 cp.pos = NULL_POSITION; 1666 col_vs_pos(linepos, &cp, saved_pos, saved_col); 1667 return cp.pos; 1668 } 1669 1670 /* 1671 * Set an attribute on each char of the line in the line buffer. 1672 */ 1673 public void set_attr_line(int a) 1674 { 1675 size_t i; 1676 1677 for (i = linebuf.print; i < linebuf.end; i++) 1678 if ((linebuf.attr[i] & AT_COLOR) == 0 || (a & AT_COLOR) == 0) 1679 linebuf.attr[i] |= a; 1680 } 1681 1682 /* 1683 * Set the char to be displayed in the status column. 1684 */ 1685 public void set_status_col(char c, int attr) 1686 { 1687 set_pfx(0, c, attr); 1688 } 1689 1690 /* 1691 * Get a character from the current line. 1692 * Return the character as the function return value, 1693 * and the character attribute in *ap. 1694 */ 1695 public int gline(size_t i, int *ap) 1696 { 1697 if (is_null_line) 1698 { 1699 /* 1700 * If there is no current line, we pretend the line is 1701 * either "~" or "", depending on the "twiddle" flag. 1702 */ 1703 if (twiddle) 1704 { 1705 if (i == 0) 1706 { 1707 *ap = use_color ? AT_COLOR_TILDE : AT_BOLD; 1708 return '~'; 1709 } 1710 --i; 1711 } 1712 /* Make sure we're back to AT_NORMAL before the '\n'. */ 1713 *ap = AT_NORMAL; 1714 return i ? '\0' : '\n'; 1715 } 1716 1717 if (i < linebuf.pfx_end) 1718 { 1719 *ap = linebuf.pfx_attr[i]; 1720 return linebuf.pfx[i]; 1721 } 1722 i += linebuf.print - linebuf.pfx_end; 1723 *ap = linebuf.attr[i]; 1724 return (unsigned char) linebuf.buf[i]; 1725 } 1726 1727 /* 1728 * Should we clear to end of line after printing this line? 1729 */ 1730 public lbool should_clear_after_line(void) 1731 { 1732 return clear_after_line; 1733 } 1734 1735 /* 1736 * Indicate that there is no current line. 1737 */ 1738 public void null_line(void) 1739 { 1740 is_null_line = TRUE; 1741 cshift = 0; 1742 } 1743 1744 /* 1745 * Analogous to forw_line(), but deals with "raw lines": 1746 * lines which are not split for screen width. 1747 * {{ This is supposed to be more efficient than forw_line(). }} 1748 */ 1749 public POSITION forw_raw_line_len(POSITION curr_pos, size_t read_len, constant char **linep, size_t *line_lenp) 1750 { 1751 size_t n; 1752 int c; 1753 POSITION new_pos; 1754 1755 if (curr_pos == NULL_POSITION || ch_seek(curr_pos) || 1756 (c = ch_forw_get()) == EOI) 1757 return (NULL_POSITION); 1758 1759 set_line_contig_pos(NULL_POSITION); 1760 n = 0; 1761 for (;;) 1762 { 1763 if (c == '\n' || c == EOI || ABORT_SIGS()) 1764 { 1765 new_pos = ch_tell(); 1766 break; 1767 } 1768 if (n >= size_linebuf-1) 1769 { 1770 if (expand_linebuf()) 1771 { 1772 /* 1773 * Overflowed the input buffer. 1774 * Pretend the line ended here. 1775 */ 1776 new_pos = ch_tell() - 1; 1777 break; 1778 } 1779 } 1780 linebuf.buf[n++] = (char) c; 1781 if (read_len != size_t_null && read_len > 0 && n >= read_len) 1782 { 1783 new_pos = ch_tell(); 1784 break; 1785 } 1786 c = ch_forw_get(); 1787 } 1788 linebuf.buf[n] = '\0'; 1789 if (linep != NULL) 1790 *linep = linebuf.buf; 1791 if (line_lenp != NULL) 1792 *line_lenp = n; 1793 return (new_pos); 1794 } 1795 1796 public POSITION forw_raw_line(POSITION curr_pos, constant char **linep, size_t *line_lenp) 1797 { 1798 return forw_raw_line_len(curr_pos, size_t_null, linep, line_lenp); 1799 } 1800 1801 /* 1802 * Analogous to back_line(), but deals with "raw lines". 1803 * {{ This is supposed to be more efficient than back_line(). }} 1804 */ 1805 public POSITION back_raw_line(POSITION curr_pos, constant char **linep, size_t *line_lenp) 1806 { 1807 size_t n; 1808 int c; 1809 POSITION new_pos; 1810 1811 if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() || 1812 ch_seek(curr_pos-1)) 1813 return (NULL_POSITION); 1814 1815 set_line_contig_pos(NULL_POSITION); 1816 n = size_linebuf; 1817 linebuf.buf[--n] = '\0'; 1818 for (;;) 1819 { 1820 c = ch_back_get(); 1821 if (c == '\n' || ABORT_SIGS()) 1822 { 1823 /* 1824 * This is the newline ending the previous line. 1825 * We have hit the beginning of the line. 1826 */ 1827 new_pos = ch_tell() + 1; 1828 break; 1829 } 1830 if (c == EOI) 1831 { 1832 /* 1833 * We have hit the beginning of the file. 1834 * This must be the first line in the file. 1835 * This must, of course, be the beginning of the line. 1836 */ 1837 new_pos = ch_zero(); 1838 break; 1839 } 1840 if (n <= 0) 1841 { 1842 size_t old_size_linebuf = size_linebuf; 1843 char *fm; 1844 char *to; 1845 if (expand_linebuf()) 1846 { 1847 /* 1848 * Overflowed the input buffer. 1849 * Pretend the line ended here. 1850 */ 1851 new_pos = ch_tell() + 1; 1852 break; 1853 } 1854 /* 1855 * Shift the data to the end of the new linebuf. 1856 */ 1857 for (fm = linebuf.buf + old_size_linebuf - 1, 1858 to = linebuf.buf + size_linebuf - 1; 1859 fm >= linebuf.buf; fm--, to--) 1860 *to = *fm; 1861 n = size_linebuf - old_size_linebuf; 1862 } 1863 linebuf.buf[--n] = (char) c; 1864 } 1865 if (linep != NULL) 1866 *linep = &linebuf.buf[n]; 1867 if (line_lenp != NULL) 1868 *line_lenp = size_linebuf - 1 - n; 1869 return (new_pos); 1870 } 1871 1872 /* 1873 * Skip cols printable columns at the start of line. 1874 * Return number of bytes skipped. 1875 */ 1876 public int skip_columns(int cols, constant char **linep, size_t *line_lenp) 1877 { 1878 constant char *line = *linep; 1879 constant char *eline = line + *line_lenp; 1880 LWCHAR pch = 0; 1881 size_t bytes; 1882 1883 while (cols > 0 && line < eline) 1884 { 1885 LWCHAR ch = step_charc(&line, +1, eline); 1886 struct ansi_state *pansi = ansi_start(ch); 1887 if (pansi != NULL) 1888 { 1889 skip_ansi(pansi, ch, &line, eline); 1890 ansi_done(pansi); 1891 pch = 0; 1892 } else 1893 { 1894 int w = pwidth(ch, 0, pch, 0); 1895 cols -= w; 1896 pch = ch; 1897 } 1898 } 1899 bytes = ptr_diff(line, *linep); 1900 *linep = line; 1901 *line_lenp -= bytes; 1902 return (int) bytes; /*{{type-issue}}*/ 1903 } 1904 1905 /* 1906 * Append a string to the line buffer. 1907 */ 1908 static int pappstr(constant char *str) 1909 { 1910 while (*str != '\0') 1911 { 1912 if (pappend(*str++, NULL_POSITION)) 1913 /* Doesn't fit on screen. */ 1914 return 1; 1915 } 1916 return 0; 1917 } 1918 1919 /* 1920 * Load a string into the line buffer. 1921 * If the string is too long to fit on the screen (minus reserve chars), 1922 * truncate the beginning of the string to fit. 1923 */ 1924 public void load_line(constant char *str, int attr, int reserve) 1925 { 1926 int save_hshift = hshift; 1927 hshift = 0; 1928 1929 /* We're overwriting the line buffer, so what's in it will no longer be contiguous. */ 1930 set_line_contig_pos(NULL_POSITION); 1931 1932 sc_width -= reserve; 1933 for (;;) 1934 { 1935 prewind(FALSE); 1936 if (pappstr(str) == 0) 1937 break; 1938 /* 1939 * Didn't fit on screen; increase left shift by one. 1940 * {{ This gets very inefficient if the string 1941 * is much longer than the screen width. }} 1942 */ 1943 hshift += 1; 1944 } 1945 set_linebuf(linebuf.end, '\0', AT_NORMAL); 1946 linebuf.prev_end = 0; 1947 sc_width += reserve; 1948 1949 /* Color the prompt unless it has ansi sequences in it. */ 1950 if (!ansi_in_line) 1951 { 1952 size_t i; 1953 for (i = linebuf.print; i < linebuf.end; i++) 1954 set_linebuf(i, linebuf.buf[i], attr); 1955 } 1956 hshift = save_hshift; 1957 } 1958 1959 /* 1960 * Find the length of the longest displayed line on the screen. 1961 */ 1962 public int longest_line_width(void) 1963 { 1964 POSITION pos; 1965 int save_width; 1966 int sindex; 1967 int longest = 0; 1968 1969 save_width = sc_width; 1970 sc_width = INT_MAX; /* so forw_line() won't chop */ 1971 for (sindex = TOP; sindex < sc_height-1; sindex++) 1972 if ((pos = position(sindex)) != NULL_POSITION) 1973 break; 1974 for (; sindex < sc_height-1 && pos != NULL_POSITION; sindex++) 1975 { 1976 pos = forw_line(pos, NULL, NULL); 1977 if (end_column > longest) 1978 longest = end_column; 1979 } 1980 sc_width = save_width; 1981 return longest; 1982 } 1983 1984 /* 1985 * Find the shift necessary to show the end of the longest displayed line. 1986 */ 1987 public int rrshift(void) 1988 { 1989 int longest = longest_line_width(); 1990 if (longest < sc_width) 1991 return 0; 1992 return longest - sc_width; 1993 } 1994 1995 /* 1996 * Get the color_map index associated with a given attribute. 1997 */ 1998 static int lookup_color_index(int attr) 1999 { 2000 int cx; 2001 for (cx = 0; cx < countof(color_map); cx++) 2002 if (color_map[cx].attr == attr) 2003 return cx; 2004 return -1; 2005 } 2006 2007 static int color_index(int attr) 2008 { 2009 if (use_color && (attr & AT_COLOR)) 2010 return lookup_color_index(attr & AT_COLOR); 2011 if (attr & AT_UNDERLINE) 2012 return lookup_color_index(AT_UNDERLINE); 2013 if (attr & AT_BOLD) 2014 return lookup_color_index(AT_BOLD); 2015 if (attr & AT_BLINK) 2016 return lookup_color_index(AT_BLINK); 2017 if (attr & AT_STANDOUT) 2018 return lookup_color_index(AT_STANDOUT); 2019 return -1; 2020 } 2021 2022 /* 2023 * Set the color string to use for a given attribute. 2024 */ 2025 public int set_color_map(int attr, constant char *colorstr) 2026 { 2027 int cx = color_index(attr); 2028 if (cx < 0) 2029 return -1; 2030 if (strlen(colorstr)+1 > sizeof(color_map[cx].color)) 2031 return -1; 2032 if (*colorstr != '\0' && parse_color(colorstr, NULL, NULL, NULL) == CT_NULL) 2033 return -1; 2034 strcpy(color_map[cx].color, colorstr); 2035 return 0; 2036 } 2037 2038 /* 2039 * Get the color string to use for a given attribute. 2040 */ 2041 public constant char * get_color_map(int attr) 2042 { 2043 int cx = color_index(attr); 2044 if (cx < 0) 2045 return NULL; 2046 return color_map[cx].color; 2047 } 2048