1a5f0fb15SPaul Saab /* 2*c77c4889SXin LI * Copyright (C) 1984-2024 Mark Nudelman 3a5f0fb15SPaul Saab * 4a5f0fb15SPaul Saab * You may distribute under the terms of either the GNU General Public 5a5f0fb15SPaul Saab * License or the Less License, as specified in the README file. 6a5f0fb15SPaul Saab * 796e55cc7SXin LI * For more information, see the README file. 8a5f0fb15SPaul Saab */ 9a5f0fb15SPaul Saab 10a5f0fb15SPaul Saab /* 11a5f0fb15SPaul Saab * Routines to manipulate the "line buffer". 12a5f0fb15SPaul Saab * The line buffer holds a line of output as it is being built 13a5f0fb15SPaul Saab * in preparation for output to the screen. 14a5f0fb15SPaul Saab */ 15a5f0fb15SPaul Saab 16a5f0fb15SPaul Saab #include "less.h" 1789dd99dcSXin LI #include "charset.h" 18f6b74a7dSXin LI #include "position.h" 19a5f0fb15SPaul Saab 20b2ea2440SXin LI #if MSDOS_COMPILER==WIN32C 21b2ea2440SXin LI #define WIN32_LEAN_AND_MEAN 22b2ea2440SXin LI #include <windows.h> 23b2ea2440SXin LI #endif 24b2ea2440SXin LI 252235c7feSXin LI #define MAX_PFX_WIDTH (MAX_LINENUM_WIDTH + MAX_STATUSCOL_WIDTH + 1) 262235c7feSXin LI static struct { 272235c7feSXin LI char *buf; /* Buffer which holds the current output line */ 282235c7feSXin LI int *attr; /* Parallel to buf, to hold attributes */ 29*c77c4889SXin LI size_t print; /* Index in buf of first printable char */ 30*c77c4889SXin LI size_t end; /* Number of chars in buf */ 312235c7feSXin LI char pfx[MAX_PFX_WIDTH]; /* Holds status column and line number */ 322235c7feSXin LI int pfx_attr[MAX_PFX_WIDTH]; 33*c77c4889SXin LI size_t pfx_end; /* Number of chars in pfx */ 342235c7feSXin LI } linebuf; 35a5f0fb15SPaul Saab 3695270f73SXin LI /* 3795270f73SXin LI * Buffer of ansi sequences which have been shifted off the left edge 3895270f73SXin LI * of the screen. 3995270f73SXin LI */ 40d713e089SXin LI static struct xbuffer shifted_ansi; 4195270f73SXin LI 4295270f73SXin LI /* 4395270f73SXin LI * Ring buffer of last ansi sequences sent. 4495270f73SXin LI * While sending a line, these will be resent at the end 4595270f73SXin LI * of any highlighted string, to restore text modes. 4695270f73SXin LI * {{ Not ideal, since we don't really know how many to resend. }} 4795270f73SXin LI */ 4895270f73SXin LI #define NUM_LAST_ANSIS 3 4995270f73SXin LI static struct xbuffer last_ansi; 5095270f73SXin LI static struct xbuffer last_ansis[NUM_LAST_ANSIS]; 5195270f73SXin LI static int curr_last_ansi; 522235c7feSXin LI 53*c77c4889SXin LI public size_t size_linebuf = 0; /* Size of line buffer (and attr buffer) */ 542235c7feSXin LI static struct ansi_state *line_ansi = NULL; 55*c77c4889SXin LI static lbool ansi_in_line; 5695270f73SXin LI static int hlink_in_line; 5795270f73SXin LI static int line_mark_attr; 5889dd99dcSXin LI static int cshift; /* Current left-shift of output line buffer */ 59a5f0fb15SPaul Saab public int hshift; /* Desired left-shift of output line buffer */ 60c9346414SPaul Saab public int tabstops[TABSTOP_MAX] = { 0 }; /* Custom tabstops */ 61c9346414SPaul Saab public int ntabstops = 1; /* Number of tabstops */ 62c9346414SPaul Saab public int tabdefault = 8; /* Default repeated tabstops */ 6396e55cc7SXin LI public POSITION highest_hilite; /* Pos of last hilite in file found so far */ 64d713e089SXin LI static POSITION line_pos; 65a5f0fb15SPaul Saab 662235c7feSXin LI static int end_column; /* Printable length, accounting for backspaces, etc. */ 67b2ea2440SXin LI static int right_curr; 68b2ea2440SXin LI static int right_column; 69a5f0fb15SPaul Saab static int overstrike; /* Next char should overstrike previous char */ 70000ba3e8STim J. Robbins static int last_overstrike = AT_NORMAL; 71*c77c4889SXin LI static lbool is_null_line; /* There is no current line */ 72a15691bfSXin LI static LWCHAR pendc; 73a5f0fb15SPaul Saab static POSITION pendpos; 74*c77c4889SXin LI static constant char *end_ansi_chars; 75*c77c4889SXin LI static constant char *mid_ansi_chars; 7630a1828cSXin LI static int in_hilite; 77a5f0fb15SPaul Saab 78d713e089SXin LI static int attr_swidth(int a); 79d713e089SXin LI static int attr_ewidth(int a); 80*c77c4889SXin LI static int do_append(LWCHAR ch, constant char *rep, POSITION pos); 81a5f0fb15SPaul Saab 8289dd99dcSXin LI extern int sigs; 83a5f0fb15SPaul Saab extern int bs_mode; 84d713e089SXin LI extern int proc_backspace; 85d713e089SXin LI extern int proc_tab; 86d713e089SXin LI extern int proc_return; 87a5f0fb15SPaul Saab extern int linenums; 88a5f0fb15SPaul Saab extern int ctldisp; 89a5f0fb15SPaul Saab extern int twiddle; 908ed69c6fSPaul Saab extern int status_col; 912235c7feSXin LI extern int status_col_width; 922235c7feSXin LI extern int linenum_width; 93a5f0fb15SPaul Saab extern int auto_wrap, ignaw; 94a5f0fb15SPaul Saab extern int bo_s_width, bo_e_width; 95a5f0fb15SPaul Saab extern int ul_s_width, ul_e_width; 96a5f0fb15SPaul Saab extern int bl_s_width, bl_e_width; 97a5f0fb15SPaul Saab extern int so_s_width, so_e_width; 98a5f0fb15SPaul Saab extern int sc_width, sc_height; 99a5f0fb15SPaul Saab extern int utf_mode; 1008ed69c6fSPaul Saab extern POSITION start_attnpos; 1018ed69c6fSPaul Saab extern POSITION end_attnpos; 102*c77c4889SXin LI extern LWCHAR rscroll_char; 103b2ea2440SXin LI extern int rscroll_attr; 1042235c7feSXin LI extern int use_color; 10595270f73SXin LI extern int status_line; 106a5f0fb15SPaul Saab 10789dd99dcSXin LI static char mbc_buf[MAX_UTF_CHAR_LEN]; 10889dd99dcSXin LI static int mbc_buf_len = 0; 10989dd99dcSXin LI static int mbc_buf_index = 0; 11089dd99dcSXin LI static POSITION mbc_pos; 111*c77c4889SXin LI static size_t saved_line_end; 112d713e089SXin LI static int saved_end_column; 11389dd99dcSXin LI 1142235c7feSXin LI /* Configurable color map */ 115d713e089SXin LI struct color_map { int attr; char color[12]; }; 116d713e089SXin LI static struct color_map color_map[] = { 117d713e089SXin LI { AT_UNDERLINE, "" }, 118d713e089SXin LI { AT_BOLD, "" }, 119d713e089SXin LI { AT_BLINK, "" }, 120d713e089SXin LI { AT_STANDOUT, "" }, 121d713e089SXin LI { AT_COLOR_ATTN, "Wm" }, 122d713e089SXin LI { AT_COLOR_BIN, "kR" }, 123d713e089SXin LI { AT_COLOR_CTRL, "kR" }, 124d713e089SXin LI { AT_COLOR_ERROR, "kY" }, 125d713e089SXin LI { AT_COLOR_LINENUM, "c" }, 126d713e089SXin LI { AT_COLOR_MARK, "Wb" }, 127d713e089SXin LI { AT_COLOR_PROMPT, "kC" }, 128d713e089SXin LI { AT_COLOR_RSCROLL, "kc" }, 129d713e089SXin LI { AT_COLOR_HEADER, "" }, 130d713e089SXin LI { AT_COLOR_SEARCH, "kG" }, 131d713e089SXin LI { AT_COLOR_SUBSEARCH(1), "ky" }, 132d713e089SXin LI { AT_COLOR_SUBSEARCH(2), "wb" }, 133d713e089SXin LI { AT_COLOR_SUBSEARCH(3), "YM" }, 134d713e089SXin LI { AT_COLOR_SUBSEARCH(4), "Yr" }, 135d713e089SXin LI { AT_COLOR_SUBSEARCH(5), "Wc" }, 1362235c7feSXin LI }; 1372235c7feSXin LI 1382235c7feSXin LI /* State while processing an ANSI escape sequence */ 1392235c7feSXin LI struct ansi_state { 140*c77c4889SXin LI int oindex; /* Index into OSC8 prefix */ 141*c77c4889SXin LI osc8_state ostate; /* State while processing OSC8 sequence */ 1422235c7feSXin LI }; 1432235c7feSXin LI 144a5f0fb15SPaul Saab /* 145a5f0fb15SPaul Saab * Initialize from environment variables. 146a5f0fb15SPaul Saab */ 147d713e089SXin LI public void init_line(void) 148a5f0fb15SPaul Saab { 14995270f73SXin LI int ax; 15095270f73SXin LI 151a5f0fb15SPaul Saab end_ansi_chars = lgetenv("LESSANSIENDCHARS"); 152b7780dbeSXin LI if (isnullenv(end_ansi_chars)) 153a5f0fb15SPaul Saab end_ansi_chars = "m"; 15489dd99dcSXin LI 15589dd99dcSXin LI mid_ansi_chars = lgetenv("LESSANSIMIDCHARS"); 156b7780dbeSXin LI if (isnullenv(mid_ansi_chars)) 157a15691bfSXin LI mid_ansi_chars = "0123456789:;[?!\"'#%()*+ "; 15889dd99dcSXin LI 1592235c7feSXin LI linebuf.buf = (char *) ecalloc(LINEBUF_SIZE, sizeof(char)); 1602235c7feSXin LI linebuf.attr = (int *) ecalloc(LINEBUF_SIZE, sizeof(int)); 161c9346414SPaul Saab size_linebuf = LINEBUF_SIZE; 16230a1828cSXin LI xbuf_init(&shifted_ansi); 16330a1828cSXin LI xbuf_init(&last_ansi); 16495270f73SXin LI for (ax = 0; ax < NUM_LAST_ANSIS; ax++) 16595270f73SXin LI xbuf_init(&last_ansis[ax]); 16695270f73SXin LI curr_last_ansi = 0; 167c9346414SPaul Saab } 168c9346414SPaul Saab 169c9346414SPaul Saab /* 170c9346414SPaul Saab * Expand the line buffer. 171c9346414SPaul Saab */ 172d713e089SXin LI static int expand_linebuf(void) 173c9346414SPaul Saab { 17489dd99dcSXin LI /* Double the size of the line buffer. */ 175*c77c4889SXin LI size_t new_size = size_linebuf * 2; 176c9346414SPaul Saab char *new_buf = (char *) calloc(new_size, sizeof(char)); 1772235c7feSXin LI int *new_attr = (int *) calloc(new_size, sizeof(int)); 178c9346414SPaul Saab if (new_buf == NULL || new_attr == NULL) 179c9346414SPaul Saab { 180c9346414SPaul Saab if (new_attr != NULL) 181c9346414SPaul Saab free(new_attr); 182c9346414SPaul Saab if (new_buf != NULL) 183c9346414SPaul Saab free(new_buf); 184c9346414SPaul Saab return 1; 185c9346414SPaul Saab } 18689dd99dcSXin LI /* 18789dd99dcSXin LI * We just calloc'd the buffers; copy the old contents. 18889dd99dcSXin LI */ 1892235c7feSXin LI memcpy(new_buf, linebuf.buf, size_linebuf * sizeof(char)); 1902235c7feSXin LI memcpy(new_attr, linebuf.attr, size_linebuf * sizeof(int)); 1912235c7feSXin LI free(linebuf.attr); 1922235c7feSXin LI free(linebuf.buf); 1932235c7feSXin LI linebuf.buf = new_buf; 1942235c7feSXin LI linebuf.attr = new_attr; 195c9346414SPaul Saab size_linebuf = new_size; 196c9346414SPaul Saab return 0; 197a5f0fb15SPaul Saab } 198a5f0fb15SPaul Saab 199a5f0fb15SPaul Saab /* 20089dd99dcSXin LI * Is a character ASCII? 20189dd99dcSXin LI */ 202*c77c4889SXin LI public lbool is_ascii_char(LWCHAR ch) 20389dd99dcSXin LI { 20489dd99dcSXin LI return (ch <= 0x7F); 20589dd99dcSXin LI } 20689dd99dcSXin LI 20789dd99dcSXin LI /* 2082235c7feSXin LI */ 209d713e089SXin LI static void inc_end_column(int w) 2102235c7feSXin LI { 2112235c7feSXin LI if (end_column > right_column && w > 0) 2122235c7feSXin LI { 2132235c7feSXin LI right_column = end_column; 214*c77c4889SXin LI right_curr = (int) linebuf.end; 2152235c7feSXin LI } 2162235c7feSXin LI end_column += w; 2172235c7feSXin LI } 2182235c7feSXin LI 219d713e089SXin LI public POSITION line_position(void) 220d713e089SXin LI { 221d713e089SXin LI return line_pos; 222d713e089SXin LI } 223d713e089SXin LI 2242235c7feSXin LI /* 225a5f0fb15SPaul Saab * Rewind the line buffer. 226a5f0fb15SPaul Saab */ 227d713e089SXin LI public void prewind(void) 228a5f0fb15SPaul Saab { 22995270f73SXin LI int ax; 23095270f73SXin LI 2312235c7feSXin LI linebuf.print = 6; /* big enough for longest UTF-8 sequence */ 2322235c7feSXin LI linebuf.pfx_end = 0; 2332235c7feSXin LI for (linebuf.end = 0; linebuf.end < linebuf.print; linebuf.end++) 2342235c7feSXin LI { 2352235c7feSXin LI linebuf.buf[linebuf.end] = '\0'; 2362235c7feSXin LI linebuf.attr[linebuf.end] = 0; 2372235c7feSXin LI } 2382235c7feSXin LI 2392235c7feSXin LI end_column = 0; 240b2ea2440SXin LI right_curr = 0; 241b2ea2440SXin LI right_column = 0; 24289dd99dcSXin LI cshift = 0; 243a5f0fb15SPaul Saab overstrike = 0; 24489dd99dcSXin LI last_overstrike = AT_NORMAL; 24589dd99dcSXin LI mbc_buf_len = 0; 246*c77c4889SXin LI is_null_line = FALSE; 247a5f0fb15SPaul Saab pendc = '\0'; 24830a1828cSXin LI in_hilite = 0; 249*c77c4889SXin LI ansi_in_line = FALSE; 25095270f73SXin LI hlink_in_line = 0; 25195270f73SXin LI line_mark_attr = 0; 252d713e089SXin LI line_pos = NULL_POSITION; 25330a1828cSXin LI xbuf_reset(&shifted_ansi); 25430a1828cSXin LI xbuf_reset(&last_ansi); 25595270f73SXin LI for (ax = 0; ax < NUM_LAST_ANSIS; ax++) 25695270f73SXin LI xbuf_reset(&last_ansis[ax]); 25795270f73SXin LI curr_last_ansi = 0; 258b2ea2440SXin LI } 259b2ea2440SXin LI 260b2ea2440SXin LI /* 261b2ea2440SXin LI * Set a character in the line buffer. 262b2ea2440SXin LI */ 263*c77c4889SXin LI static void set_linebuf(size_t n, char ch, int attr) 264b2ea2440SXin LI { 265d713e089SXin LI if (n >= size_linebuf) 266d713e089SXin LI { 267d713e089SXin LI /* 268d713e089SXin LI * Won't fit in line buffer. 269d713e089SXin LI * Try to expand it. 270d713e089SXin LI */ 271d713e089SXin LI if (expand_linebuf()) 272d713e089SXin LI return; 273d713e089SXin LI } 2742235c7feSXin LI linebuf.buf[n] = ch; 2752235c7feSXin LI linebuf.attr[n] = attr; 276b2ea2440SXin LI } 277b2ea2440SXin LI 278b2ea2440SXin LI /* 279b2ea2440SXin LI * Append a character to the line buffer. 280b2ea2440SXin LI */ 281d713e089SXin LI static void add_linebuf(char ch, int attr, int w) 282b2ea2440SXin LI { 2832235c7feSXin LI set_linebuf(linebuf.end++, ch, attr); 2842235c7feSXin LI inc_end_column(w); 285a5f0fb15SPaul Saab } 286a5f0fb15SPaul Saab 287a5f0fb15SPaul Saab /* 28895270f73SXin LI * Append a string to the line buffer. 28995270f73SXin LI */ 290*c77c4889SXin LI static void addstr_linebuf(constant char *s, int attr, int cw) 29195270f73SXin LI { 29295270f73SXin LI for ( ; *s != '\0'; s++) 29395270f73SXin LI add_linebuf(*s, attr, cw); 29495270f73SXin LI } 29595270f73SXin LI 29695270f73SXin LI /* 2972235c7feSXin LI * Set a character in the line prefix buffer. 2982235c7feSXin LI */ 299*c77c4889SXin LI static void set_pfx(size_t n, char ch, int attr) 3002235c7feSXin LI { 3012235c7feSXin LI linebuf.pfx[n] = ch; 3022235c7feSXin LI linebuf.pfx_attr[n] = attr; 3032235c7feSXin LI } 3042235c7feSXin LI 3052235c7feSXin LI /* 3062235c7feSXin LI * Append a character to the line prefix buffer. 3072235c7feSXin LI */ 308d713e089SXin LI static void add_pfx(char ch, int attr) 3092235c7feSXin LI { 3102235c7feSXin LI set_pfx(linebuf.pfx_end++, ch, attr); 3112235c7feSXin LI } 3122235c7feSXin LI 3132235c7feSXin LI /* 3142235c7feSXin LI * Insert the status column and line number into the line buffer. 315a5f0fb15SPaul Saab */ 316d713e089SXin LI public void plinestart(POSITION pos) 317a5f0fb15SPaul Saab { 3181ea31627SRobert Watson LINENUM linenum = 0; 319a5f0fb15SPaul Saab 3208ed69c6fSPaul Saab if (linenums == OPT_ONPLUS) 3218ed69c6fSPaul Saab { 322a5f0fb15SPaul Saab /* 323a5f0fb15SPaul Saab * Get the line number and put it in the current line. 324a5f0fb15SPaul Saab * {{ Note: since find_linenum calls forw_raw_line, 325a5f0fb15SPaul Saab * it may seek in the input file, requiring the caller 3262235c7feSXin LI * of plinestart to re-seek if necessary. }} 3278ed69c6fSPaul Saab * {{ Since forw_raw_line modifies linebuf, we must 3288ed69c6fSPaul Saab * do this first, before storing anything in linebuf. }} 329a5f0fb15SPaul Saab */ 330000ba3e8STim J. Robbins linenum = find_linenum(pos); 3318ed69c6fSPaul Saab } 332a5f0fb15SPaul Saab 333a5f0fb15SPaul Saab /* 3348ed69c6fSPaul Saab * Display a status column if the -J option is set. 335a5f0fb15SPaul Saab */ 33695270f73SXin LI if (status_col || status_line) 3378ed69c6fSPaul Saab { 338b2ea2440SXin LI char c = posmark(pos); 339b2ea2440SXin LI if (c != 0) 34095270f73SXin LI line_mark_attr = AT_HILITE|AT_COLOR_MARK; 34195270f73SXin LI else if (start_attnpos != NULL_POSITION && 342b2ea2440SXin LI pos >= start_attnpos && pos <= end_attnpos) 34395270f73SXin LI line_mark_attr = AT_HILITE|AT_COLOR_ATTN; 34495270f73SXin LI if (status_col) 34595270f73SXin LI { 34695270f73SXin LI add_pfx(c ? c : ' ', line_mark_attr); /* column 0: status */ 347*c77c4889SXin LI while (linebuf.pfx_end < (size_t) status_col_width) /*{{type-issue}}*/ 3482235c7feSXin LI add_pfx(' ', AT_NORMAL); 349b2ea2440SXin LI } 35095270f73SXin LI } 351b2ea2440SXin LI 3528ed69c6fSPaul Saab /* 3538ed69c6fSPaul Saab * Display the line number at the start of each line 3548ed69c6fSPaul Saab * if the -N option is set. 3558ed69c6fSPaul Saab */ 3568ed69c6fSPaul Saab if (linenums == OPT_ONPLUS) 3578ed69c6fSPaul Saab { 358b2ea2440SXin LI char buf[INT_STRLEN_BOUND(linenum) + 2]; 359*c77c4889SXin LI size_t len; 360*c77c4889SXin LI size_t i; 361000ba3e8STim J. Robbins 36295270f73SXin LI linenum = vlinenum(linenum); 36395270f73SXin LI if (linenum == 0) 36495270f73SXin LI len = 0; 36595270f73SXin LI else 36695270f73SXin LI { 367d713e089SXin LI linenumtoa(linenum, buf, 10); 368*c77c4889SXin LI len = strlen(buf); 36995270f73SXin LI } 370*c77c4889SXin LI for (i = 0; i + len < (size_t) linenum_width; i++) 3712235c7feSXin LI add_pfx(' ', AT_NORMAL); 3722235c7feSXin LI for (i = 0; i < len; i++) 37395270f73SXin LI add_pfx(buf[i], AT_BOLD|AT_COLOR_LINENUM); 3742235c7feSXin LI add_pfx(' ', AT_NORMAL); 3758ed69c6fSPaul Saab } 376*c77c4889SXin LI end_column = (int) linebuf.pfx_end; /*{{type-issue}}*/ 377a5f0fb15SPaul Saab } 378a5f0fb15SPaul Saab 379a5f0fb15SPaul Saab /* 3802235c7feSXin LI * Return the width of the line prefix (status column and line number). 3812235c7feSXin LI * {{ Actual line number can be wider than linenum_width. }} 3822235c7feSXin LI */ 383d713e089SXin LI public int line_pfx_width(void) 3842235c7feSXin LI { 3852235c7feSXin LI int width = 0; 3862235c7feSXin LI if (status_col) 3872235c7feSXin LI width += status_col_width; 3882235c7feSXin LI if (linenums == OPT_ONPLUS) 3892235c7feSXin LI width += linenum_width + 1; 3902235c7feSXin LI return width; 3912235c7feSXin LI } 3922235c7feSXin LI 3932235c7feSXin LI /* 3942235c7feSXin LI * Shift line left so that the last char is just to the left 3952235c7feSXin LI * of the first visible column. 39689dd99dcSXin LI */ 397d713e089SXin LI public void pshift_all(void) 39889dd99dcSXin LI { 399*c77c4889SXin LI size_t i; 4002235c7feSXin LI for (i = linebuf.print; i < linebuf.end; i++) 4012235c7feSXin LI if (linebuf.attr[i] == AT_ANSI) 402*c77c4889SXin LI xbuf_add_char(&shifted_ansi, linebuf.buf[i]); 4032235c7feSXin LI linebuf.end = linebuf.print; 404*c77c4889SXin LI end_column = (int) linebuf.pfx_end; /*{{type-issue}}*/ 405*c77c4889SXin LI line_pos = NULL_POSITION; 406a5f0fb15SPaul Saab } 407a5f0fb15SPaul Saab 408a5f0fb15SPaul Saab /* 409a5f0fb15SPaul Saab * Return the printing width of the start (enter) sequence 410a5f0fb15SPaul Saab * for a given character attribute. 411a5f0fb15SPaul Saab */ 412d713e089SXin LI static int attr_swidth(int a) 413a5f0fb15SPaul Saab { 41489dd99dcSXin LI int w = 0; 41589dd99dcSXin LI 41689dd99dcSXin LI a = apply_at_specials(a); 41789dd99dcSXin LI 41889dd99dcSXin LI if (a & AT_UNDERLINE) 41989dd99dcSXin LI w += ul_s_width; 42089dd99dcSXin LI if (a & AT_BOLD) 42189dd99dcSXin LI w += bo_s_width; 42289dd99dcSXin LI if (a & AT_BLINK) 42389dd99dcSXin LI w += bl_s_width; 42489dd99dcSXin LI if (a & AT_STANDOUT) 42589dd99dcSXin LI w += so_s_width; 42689dd99dcSXin LI 42789dd99dcSXin LI return w; 428a5f0fb15SPaul Saab } 429a5f0fb15SPaul Saab 430a5f0fb15SPaul Saab /* 431a5f0fb15SPaul Saab * Return the printing width of the end (exit) sequence 432a5f0fb15SPaul Saab * for a given character attribute. 433a5f0fb15SPaul Saab */ 434d713e089SXin LI static int attr_ewidth(int a) 435a5f0fb15SPaul Saab { 43689dd99dcSXin LI int w = 0; 43789dd99dcSXin LI 43889dd99dcSXin LI a = apply_at_specials(a); 43989dd99dcSXin LI 44089dd99dcSXin LI if (a & AT_UNDERLINE) 44189dd99dcSXin LI w += ul_e_width; 44289dd99dcSXin LI if (a & AT_BOLD) 44389dd99dcSXin LI w += bo_e_width; 44489dd99dcSXin LI if (a & AT_BLINK) 44589dd99dcSXin LI w += bl_e_width; 44689dd99dcSXin LI if (a & AT_STANDOUT) 44789dd99dcSXin LI w += so_e_width; 44889dd99dcSXin LI 44989dd99dcSXin LI return w; 450a5f0fb15SPaul Saab } 451a5f0fb15SPaul Saab 452a5f0fb15SPaul Saab /* 453a5f0fb15SPaul Saab * Return the printing width of a given character and attribute, 4542235c7feSXin LI * if the character were added after prev_ch. 455a5f0fb15SPaul Saab * Adding a character with a given attribute may cause an enter or exit 456a5f0fb15SPaul Saab * attribute sequence to be inserted, so this must be taken into account. 457a5f0fb15SPaul Saab */ 458d713e089SXin LI public int pwidth(LWCHAR ch, int a, LWCHAR prev_ch, int prev_a) 459a5f0fb15SPaul Saab { 46089dd99dcSXin LI int w; 461a5f0fb15SPaul Saab 46289dd99dcSXin LI if (ch == '\b') 4632235c7feSXin LI { 464a5f0fb15SPaul Saab /* 46589dd99dcSXin LI * Backspace moves backwards one or two positions. 466a5f0fb15SPaul Saab */ 4672235c7feSXin LI if (prev_a & (AT_ANSI|AT_BINARY)) 468*c77c4889SXin LI return (int) strlen(prchar('\b')); /*{{type-issue}}*/ 46989dd99dcSXin LI return (utf_mode && is_wide_char(prev_ch)) ? -2 : -1; 4702235c7feSXin LI } 471a5f0fb15SPaul Saab 47289dd99dcSXin LI if (!utf_mode || is_ascii_char(ch)) 47389dd99dcSXin LI { 474*c77c4889SXin LI if (control_char(ch)) 47589dd99dcSXin LI { 476a5f0fb15SPaul Saab /* 47789dd99dcSXin LI * Control characters do unpredictable things, 478a5f0fb15SPaul Saab * so we don't even try to guess; say it doesn't move. 479a5f0fb15SPaul Saab * This can only happen if the -r flag is in effect. 480a5f0fb15SPaul Saab */ 481a5f0fb15SPaul Saab return (0); 48289dd99dcSXin LI } 48389dd99dcSXin LI } else 48489dd99dcSXin LI { 48589dd99dcSXin LI if (is_composing_char(ch) || is_combining_char(prev_ch, ch)) 48689dd99dcSXin LI { 48789dd99dcSXin LI /* 48889dd99dcSXin LI * Composing and combining chars take up no space. 48989dd99dcSXin LI * 49089dd99dcSXin LI * Some terminals, upon failure to compose a 49189dd99dcSXin LI * composing character with the character(s) that 4922235c7feSXin LI * precede(s) it will actually take up one end_column 49389dd99dcSXin LI * for the composing character; there isn't much 49489dd99dcSXin LI * we could do short of testing the (complex) 49589dd99dcSXin LI * composition process ourselves and printing 49689dd99dcSXin LI * a binary representation when it fails. 49789dd99dcSXin LI */ 49889dd99dcSXin LI return (0); 49989dd99dcSXin LI } 50089dd99dcSXin LI } 501a5f0fb15SPaul Saab 502a5f0fb15SPaul Saab /* 50389dd99dcSXin LI * Other characters take one or two columns, 504a5f0fb15SPaul Saab * plus the width of any attribute enter/exit sequence. 505a5f0fb15SPaul Saab */ 506a5f0fb15SPaul Saab w = 1; 50789dd99dcSXin LI if (is_wide_char(ch)) 50889dd99dcSXin LI w++; 5092235c7feSXin LI if (linebuf.end > 0 && !is_at_equiv(linebuf.attr[linebuf.end-1], a)) 5102235c7feSXin LI w += attr_ewidth(linebuf.attr[linebuf.end-1]); 5112235c7feSXin LI if (apply_at_specials(a) != AT_NORMAL && 5122235c7feSXin LI (linebuf.end == 0 || !is_at_equiv(linebuf.attr[linebuf.end-1], a))) 513a5f0fb15SPaul Saab w += attr_swidth(a); 514a5f0fb15SPaul Saab return (w); 515a5f0fb15SPaul Saab } 516a5f0fb15SPaul Saab 517a5f0fb15SPaul Saab /* 51889dd99dcSXin LI * Delete to the previous base character in the line buffer. 519a5f0fb15SPaul Saab */ 520d713e089SXin LI static int backc(void) 521a5f0fb15SPaul Saab { 5222235c7feSXin LI LWCHAR ch; 523f6b74a7dSXin LI char *p; 524a5f0fb15SPaul Saab 5252235c7feSXin LI if (linebuf.end == 0) 5262235c7feSXin LI return (0); 5272235c7feSXin LI p = &linebuf.buf[linebuf.end]; 5282235c7feSXin LI ch = step_char(&p, -1, linebuf.buf); 5292235c7feSXin LI /* Skip back to the next nonzero-width char. */ 5302235c7feSXin LI while (p > linebuf.buf) 531a5f0fb15SPaul Saab { 5322235c7feSXin LI LWCHAR prev_ch; 5332235c7feSXin LI int width; 534*c77c4889SXin LI linebuf.end = ptr_diff(p, linebuf.buf); 5352235c7feSXin LI prev_ch = step_char(&p, -1, linebuf.buf); 5362235c7feSXin LI width = pwidth(ch, linebuf.attr[linebuf.end], prev_ch, linebuf.attr[linebuf.end-1]); 5372235c7feSXin LI end_column -= width; 5382235c7feSXin LI /* {{ right_column? }} */ 5392235c7feSXin LI if (width > 0) 5402235c7feSXin LI break; 5412235c7feSXin LI ch = prev_ch; 542a5f0fb15SPaul Saab } 5432235c7feSXin LI return (1); 544a5f0fb15SPaul Saab } 545a5f0fb15SPaul Saab 546a5f0fb15SPaul Saab /* 547d713e089SXin LI * Preserve the current position in the line buffer (for word wrapping). 548d713e089SXin LI */ 549d713e089SXin LI public void savec(void) 550d713e089SXin LI { 551d713e089SXin LI saved_line_end = linebuf.end; 552d713e089SXin LI saved_end_column = end_column; 553d713e089SXin LI } 554d713e089SXin LI 555d713e089SXin LI /* 556d713e089SXin LI * Restore the position in the line buffer (start of line for word wrapping). 557d713e089SXin LI */ 558d713e089SXin LI public void loadc(void) 559d713e089SXin LI { 560d713e089SXin LI linebuf.end = saved_line_end; 561d713e089SXin LI end_column = saved_end_column; 562d713e089SXin LI } 563d713e089SXin LI 564d713e089SXin LI /* 565c9346414SPaul Saab * Is a character the end of an ANSI escape sequence? 566c9346414SPaul Saab */ 567*c77c4889SXin LI public lbool is_ansi_end(LWCHAR ch) 568c9346414SPaul Saab { 56989dd99dcSXin LI if (!is_ascii_char(ch)) 570*c77c4889SXin LI return (FALSE); 57189dd99dcSXin LI return (strchr(end_ansi_chars, (char) ch) != NULL); 57289dd99dcSXin LI } 57389dd99dcSXin LI 57489dd99dcSXin LI /* 575b2ea2440SXin LI * Can a char appear in an ANSI escape sequence, before the end char? 57689dd99dcSXin LI */ 577*c77c4889SXin LI public lbool is_ansi_middle(LWCHAR ch) 57889dd99dcSXin LI { 57989dd99dcSXin LI if (!is_ascii_char(ch)) 580*c77c4889SXin LI return (FALSE); 58189dd99dcSXin LI if (is_ansi_end(ch)) 582*c77c4889SXin LI return (FALSE); 58389dd99dcSXin LI return (strchr(mid_ansi_chars, (char) ch) != NULL); 584c9346414SPaul Saab } 585c9346414SPaul Saab 586c9346414SPaul Saab /* 587b2ea2440SXin LI * Skip past an ANSI escape sequence. 588b2ea2440SXin LI * pp is initially positioned just after the CSI_START char. 589b2ea2440SXin LI */ 590*c77c4889SXin LI public void skip_ansi(struct ansi_state *pansi, constant char **pp, constant char *limit) 591b2ea2440SXin LI { 592b2ea2440SXin LI LWCHAR c; 593b2ea2440SXin LI do { 594*c77c4889SXin LI c = step_charc(pp, +1, limit); 5952235c7feSXin LI } while (*pp < limit && ansi_step(pansi, c) == ANSI_MID); 5962235c7feSXin LI /* Note that we discard final char, for which is_ansi_end is true. */ 597b2ea2440SXin LI } 598b2ea2440SXin LI 5992235c7feSXin LI /* 6002235c7feSXin LI * Determine if a character starts an ANSI escape sequence. 6012235c7feSXin LI * If so, return an ansi_state struct; otherwise return NULL. 6022235c7feSXin LI */ 603d713e089SXin LI public struct ansi_state * ansi_start(LWCHAR ch) 6042235c7feSXin LI { 6052235c7feSXin LI struct ansi_state *pansi; 6062235c7feSXin LI 6072235c7feSXin LI if (!IS_CSI_START(ch)) 6082235c7feSXin LI return NULL; 6092235c7feSXin LI pansi = ecalloc(1, sizeof(struct ansi_state)); 610*c77c4889SXin LI pansi->oindex = 0; 611*c77c4889SXin LI pansi->ostate = OSC8_PREFIX; 6122235c7feSXin LI return pansi; 6132235c7feSXin LI } 6142235c7feSXin LI 6152235c7feSXin LI /* 6162235c7feSXin LI * Determine whether the next char in an ANSI escape sequence 6172235c7feSXin LI * ends the sequence. 6182235c7feSXin LI */ 619*c77c4889SXin LI public ansi_state ansi_step(struct ansi_state *pansi, LWCHAR ch) 6202235c7feSXin LI { 621*c77c4889SXin LI static constant char osc8_prefix[] = ESCS "]8;"; 622*c77c4889SXin LI 623*c77c4889SXin LI switch (pansi->ostate) 6242235c7feSXin LI { 625*c77c4889SXin LI case OSC8_PREFIX: 626*c77c4889SXin LI if (ch != (LWCHAR) osc8_prefix[pansi->oindex] && 627*c77c4889SXin LI !(pansi->oindex == 0 && IS_CSI_START(ch))) 628*c77c4889SXin LI { 629*c77c4889SXin LI pansi->ostate = OSC8_NOT; /* not an OSC8 sequence */ 630*c77c4889SXin LI break; 631*c77c4889SXin LI } 632*c77c4889SXin LI pansi->oindex++; 633*c77c4889SXin LI if (osc8_prefix[pansi->oindex] == '\0') /* end of prefix */ 634*c77c4889SXin LI pansi->ostate = OSC8_PARAMS; 635*c77c4889SXin LI return ANSI_MID; 636*c77c4889SXin LI case OSC8_PARAMS: 637*c77c4889SXin LI if (ch == ';') 638*c77c4889SXin LI pansi->ostate = OSC8_URI; 639*c77c4889SXin LI return ANSI_MID; 640*c77c4889SXin LI case OSC8_URI: 641*c77c4889SXin LI /* URI ends with \7 or ESC-backslash. */ 6422235c7feSXin LI if (ch == '\7') 643*c77c4889SXin LI { 644*c77c4889SXin LI pansi->ostate = OSC8_END; 6452235c7feSXin LI return ANSI_END; 6462235c7feSXin LI } 647*c77c4889SXin LI if (ch == ESC) 648*c77c4889SXin LI pansi->ostate = OSC8_ST_ESC; 6492235c7feSXin LI return ANSI_MID; 650*c77c4889SXin LI case OSC8_ST_ESC: 651*c77c4889SXin LI if (ch != '\\') 652*c77c4889SXin LI { 653*c77c4889SXin LI return ANSI_ERR; 6542235c7feSXin LI } 655*c77c4889SXin LI pansi->ostate = OSC8_END; 656*c77c4889SXin LI return ANSI_END; 657*c77c4889SXin LI case OSC8_END: 658*c77c4889SXin LI return ANSI_END; 659*c77c4889SXin LI case OSC8_NOT: 660*c77c4889SXin LI break; 6612235c7feSXin LI } 6622235c7feSXin LI /* Check for SGR sequences */ 6632235c7feSXin LI if (is_ansi_middle(ch)) 6642235c7feSXin LI return ANSI_MID; 6652235c7feSXin LI if (is_ansi_end(ch)) 6662235c7feSXin LI return ANSI_END; 6672235c7feSXin LI return ANSI_ERR; 6682235c7feSXin LI } 6692235c7feSXin LI 6702235c7feSXin LI /* 671*c77c4889SXin LI * Return the current OSC8 parsing state. 672*c77c4889SXin LI */ 673*c77c4889SXin LI public osc8_state ansi_osc8_state(struct ansi_state *pansi) 674*c77c4889SXin LI { 675*c77c4889SXin LI return pansi->ostate; 676*c77c4889SXin LI } 677*c77c4889SXin LI 678*c77c4889SXin LI /* 6792235c7feSXin LI * Free an ansi_state structure. 6802235c7feSXin LI */ 681d713e089SXin LI public void ansi_done(struct ansi_state *pansi) 6822235c7feSXin LI { 6832235c7feSXin LI free(pansi); 6842235c7feSXin LI } 685b2ea2440SXin LI 686b2ea2440SXin LI /* 68795270f73SXin LI * Will w characters in attribute a fit on the screen? 68895270f73SXin LI */ 689d713e089SXin LI static int fits_on_screen(int w, int a) 69095270f73SXin LI { 69195270f73SXin LI if (ctldisp == OPT_ON) 69295270f73SXin LI /* We're not counting, so say that everything fits. */ 69395270f73SXin LI return 1; 69495270f73SXin LI return (end_column - cshift + w + attr_ewidth(a) <= sc_width); 69595270f73SXin LI } 69695270f73SXin LI 69795270f73SXin LI /* 698a5f0fb15SPaul Saab * Append a character and attribute to the line buffer. 699a5f0fb15SPaul Saab */ 70089dd99dcSXin LI #define STORE_CHAR(ch,a,rep,pos) \ 70189dd99dcSXin LI do { \ 70289dd99dcSXin LI if (store_char((ch),(a),(rep),(pos))) return (1); \ 70389dd99dcSXin LI } while (0) 704c9346414SPaul Saab 705*c77c4889SXin LI static int store_char(LWCHAR ch, int a, constant char *rep, POSITION pos) 706a5f0fb15SPaul Saab { 70789dd99dcSXin LI int w; 708*c77c4889SXin LI size_t i; 709*c77c4889SXin LI size_t replen; 71089dd99dcSXin LI char cs; 711*c77c4889SXin LI int ov; 712a5f0fb15SPaul Saab 713*c77c4889SXin LI ov = (a & (AT_UNDERLINE|AT_BOLD)); 714*c77c4889SXin LI if (ov != AT_NORMAL) 715*c77c4889SXin LI last_overstrike = ov; 71689dd99dcSXin LI 717a5f0fb15SPaul Saab #if HILITE_SEARCH 71889dd99dcSXin LI { 71989dd99dcSXin LI int matches; 72030a1828cSXin LI int resend_last = 0; 721d713e089SXin LI int hl_attr = 0; 72295270f73SXin LI 72395270f73SXin LI if (pos == NULL_POSITION) 72495270f73SXin LI { 72595270f73SXin LI /* Color the prompt unless it has ansi sequences in it. */ 72695270f73SXin LI hl_attr = ansi_in_line ? 0 : AT_STANDOUT|AT_COLOR_PROMPT; 727d713e089SXin LI } else if (a != AT_ANSI) 72895270f73SXin LI { 72995270f73SXin LI hl_attr = is_hilited_attr(pos, pos+1, 0, &matches); 73095270f73SXin LI if (hl_attr == 0 && status_line) 73195270f73SXin LI hl_attr = line_mark_attr; 73295270f73SXin LI } 7332235c7feSXin LI if (hl_attr) 734c9346414SPaul Saab { 735a5f0fb15SPaul Saab /* 736a5f0fb15SPaul Saab * This character should be highlighted. 737a5f0fb15SPaul Saab * Override the attribute passed in. 738a5f0fb15SPaul Saab */ 739d713e089SXin LI a |= hl_attr; 74095270f73SXin LI if (highest_hilite != NULL_POSITION && pos != NULL_POSITION && pos > highest_hilite) 74196e55cc7SXin LI highest_hilite = pos; 74230a1828cSXin LI in_hilite = 1; 74330a1828cSXin LI } else 74430a1828cSXin LI { 74530a1828cSXin LI if (in_hilite) 74630a1828cSXin LI { 74730a1828cSXin LI /* 74830a1828cSXin LI * This is the first non-hilited char after a hilite. 74930a1828cSXin LI * Resend the last ANSI seq to restore color. 75030a1828cSXin LI */ 75130a1828cSXin LI resend_last = 1; 75230a1828cSXin LI } 75330a1828cSXin LI in_hilite = 0; 75430a1828cSXin LI } 75530a1828cSXin LI if (resend_last) 75630a1828cSXin LI { 75795270f73SXin LI int ai; 75895270f73SXin LI for (ai = 0; ai < NUM_LAST_ANSIS; ai++) 75995270f73SXin LI { 76095270f73SXin LI int ax = (curr_last_ansi + ai) % NUM_LAST_ANSIS; 76195270f73SXin LI for (i = 0; i < last_ansis[ax].end; i++) 76295270f73SXin LI STORE_CHAR(last_ansis[ax].data[i], AT_ANSI, NULL, pos); 76395270f73SXin LI } 764c9346414SPaul Saab } 76596e55cc7SXin LI } 766a5f0fb15SPaul Saab #endif 76789dd99dcSXin LI 7682235c7feSXin LI if (a == AT_ANSI) { 769a5f0fb15SPaul Saab w = 0; 7702235c7feSXin LI } else { 7712235c7feSXin LI char *p = &linebuf.buf[linebuf.end]; 7722235c7feSXin LI LWCHAR prev_ch = (linebuf.end > 0) ? step_char(&p, -1, linebuf.buf) : 0; 7732235c7feSXin LI int prev_a = (linebuf.end > 0) ? linebuf.attr[linebuf.end-1] : 0; 7742235c7feSXin LI w = pwidth(ch, a, prev_ch, prev_a); 77589dd99dcSXin LI } 77689dd99dcSXin LI 77795270f73SXin LI if (!fits_on_screen(w, a)) 778a5f0fb15SPaul Saab return (1); 779a5f0fb15SPaul Saab 78089dd99dcSXin LI if (rep == NULL) 78189dd99dcSXin LI { 78289dd99dcSXin LI cs = (char) ch; 78389dd99dcSXin LI rep = &cs; 78489dd99dcSXin LI replen = 1; 78589dd99dcSXin LI } else 78689dd99dcSXin LI { 787*c77c4889SXin LI replen = (size_t) utf_len(rep[0]); /*{{type-issue}}*/ 78889dd99dcSXin LI } 789a5f0fb15SPaul Saab 790d713e089SXin LI if (cshift == hshift) 791d713e089SXin LI { 792d713e089SXin LI if (line_pos == NULL_POSITION) 793d713e089SXin LI line_pos = pos; 794d713e089SXin LI if (shifted_ansi.end > 0) 795b2ea2440SXin LI { 7962235c7feSXin LI /* Copy shifted ANSI sequences to beginning of line. */ 7972235c7feSXin LI for (i = 0; i < shifted_ansi.end; i++) 798*c77c4889SXin LI add_linebuf((char) shifted_ansi.data[i], AT_ANSI, 0); 79930a1828cSXin LI xbuf_reset(&shifted_ansi); 800b2ea2440SXin LI } 801d713e089SXin LI } 802d713e089SXin LI 8032235c7feSXin LI /* Add the char to the buf, even if we will left-shift it next. */ 8042235c7feSXin LI inc_end_column(w); 8052235c7feSXin LI for (i = 0; i < replen; i++) 806b2ea2440SXin LI add_linebuf(*rep++, a, 0); 8072235c7feSXin LI 8082235c7feSXin LI if (cshift < hshift) 8092235c7feSXin LI { 8102235c7feSXin LI /* We haven't left-shifted enough yet. */ 8112235c7feSXin LI if (a == AT_ANSI) 812*c77c4889SXin LI xbuf_add_char(&shifted_ansi, (char) ch); /* Save ANSI attributes */ 8132235c7feSXin LI if (linebuf.end > linebuf.print) 8142235c7feSXin LI { 8152235c7feSXin LI /* Shift left enough to put last byte of this char at print-1. */ 816*c77c4889SXin LI size_t i; 81730a1828cSXin LI for (i = 0; i < linebuf.print; i++) 81830a1828cSXin LI { 81930a1828cSXin LI linebuf.buf[i] = linebuf.buf[i+replen]; 82030a1828cSXin LI linebuf.attr[i] = linebuf.attr[i+replen]; 82130a1828cSXin LI } 8222235c7feSXin LI linebuf.end -= replen; 8232235c7feSXin LI cshift += w; 8242235c7feSXin LI /* 8252235c7feSXin LI * If the char we just left-shifted was double width, 8262235c7feSXin LI * the 2 spaces we shifted may be too much. 8272235c7feSXin LI * Represent the "half char" at start of line with a highlighted space. 8282235c7feSXin LI */ 8292235c7feSXin LI while (cshift > hshift) 8302235c7feSXin LI { 8312235c7feSXin LI add_linebuf(' ', rscroll_attr, 0); 8322235c7feSXin LI cshift--; 83389dd99dcSXin LI } 8342235c7feSXin LI } 8352235c7feSXin LI } 836a5f0fb15SPaul Saab return (0); 837a5f0fb15SPaul Saab } 838a5f0fb15SPaul Saab 83995270f73SXin LI #define STORE_STRING(s,a,pos) \ 84095270f73SXin LI do { if (store_string((s),(a),(pos))) return (1); } while (0) 84195270f73SXin LI 842*c77c4889SXin LI static int store_string(constant char *s, int a, POSITION pos) 84395270f73SXin LI { 844*c77c4889SXin LI if (!fits_on_screen((int) strlen(s), a)) 84595270f73SXin LI return 1; 84695270f73SXin LI for ( ; *s != 0; s++) 847*c77c4889SXin LI STORE_CHAR((LWCHAR)*s, a, NULL, pos); 84895270f73SXin LI return 0; 84995270f73SXin LI } 85095270f73SXin LI 851a5f0fb15SPaul Saab /* 852*c77c4889SXin LI * Return number of spaces from col to the next tab stop. 853c9346414SPaul Saab */ 854*c77c4889SXin LI static int tab_spaces(int col) 855c9346414SPaul Saab { 856*c77c4889SXin LI int to_tab = col - (int) linebuf.pfx_end; /*{{type-issue}}*/ 857c9346414SPaul Saab 858c9346414SPaul Saab if (ntabstops < 2 || to_tab >= tabstops[ntabstops-1]) 859c9346414SPaul Saab to_tab = tabdefault - 860c9346414SPaul Saab ((to_tab - tabstops[ntabstops-1]) % tabdefault); 861c9346414SPaul Saab else 862c9346414SPaul Saab { 8632235c7feSXin LI int i; 864c9346414SPaul Saab for (i = ntabstops - 2; i >= 0; i--) 865c9346414SPaul Saab if (to_tab >= tabstops[i]) 866c9346414SPaul Saab break; 867c9346414SPaul Saab to_tab = tabstops[i+1] - to_tab; 868c9346414SPaul Saab } 869*c77c4889SXin LI return to_tab; 870*c77c4889SXin LI } 871c9346414SPaul Saab 872*c77c4889SXin LI /* 873*c77c4889SXin LI * Append a tab to the line buffer. 874*c77c4889SXin LI * Store spaces to represent the tab. 875*c77c4889SXin LI */ 876*c77c4889SXin LI #define STORE_TAB(a,pos) \ 877*c77c4889SXin LI do { if (store_tab((a),(pos))) return (1); } while (0) 878*c77c4889SXin LI 879*c77c4889SXin LI static int store_tab(int attr, POSITION pos) 880*c77c4889SXin LI { 881*c77c4889SXin LI int to_tab = tab_spaces(end_column); 882c9346414SPaul Saab do { 88389dd99dcSXin LI STORE_CHAR(' ', attr, " ", pos); 884c9346414SPaul Saab } while (--to_tab > 0); 885c9346414SPaul Saab return 0; 886c9346414SPaul Saab } 887c9346414SPaul Saab 88889dd99dcSXin LI #define STORE_PRCHAR(c, pos) \ 88989dd99dcSXin LI do { if (store_prchar((c), (pos))) return 1; } while (0) 89089dd99dcSXin LI 891d713e089SXin LI static int store_prchar(LWCHAR c, POSITION pos) 89289dd99dcSXin LI { 89389dd99dcSXin LI /* 89489dd99dcSXin LI * Convert to printable representation. 89589dd99dcSXin LI */ 89695270f73SXin LI STORE_STRING(prchar(c), AT_BINARY|AT_COLOR_CTRL, pos); 89789dd99dcSXin LI return 0; 89889dd99dcSXin LI } 89989dd99dcSXin LI 900d713e089SXin LI static int flush_mbc_buf(POSITION pos) 90189dd99dcSXin LI { 90289dd99dcSXin LI int i; 90389dd99dcSXin LI 90489dd99dcSXin LI for (i = 0; i < mbc_buf_index; i++) 905*c77c4889SXin LI if (store_prchar((LWCHAR) mbc_buf[i], pos)) 90689dd99dcSXin LI return mbc_buf_index - i; 90789dd99dcSXin LI return 0; 90889dd99dcSXin LI } 90989dd99dcSXin LI 910c9346414SPaul Saab /* 911a5f0fb15SPaul Saab * Append a character to the line buffer. 912a5f0fb15SPaul Saab * Expand tabs into spaces, handle underlining, boldfacing, etc. 913a5f0fb15SPaul Saab * Returns 0 if ok, 1 if couldn't fit in buffer. 914a5f0fb15SPaul Saab */ 915*c77c4889SXin LI public int pappend_b(char c, POSITION pos, lbool before_pendc) 916a5f0fb15SPaul Saab { 917*c77c4889SXin LI LWCHAR ch = c & 0377; 918a5f0fb15SPaul Saab int r; 919a5f0fb15SPaul Saab 920*c77c4889SXin LI if (pendc && !before_pendc) 921a5f0fb15SPaul Saab { 922*c77c4889SXin LI if (ch == '\r' && pendc == '\r') 923a15691bfSXin LI return (0); 92489dd99dcSXin LI if (do_append(pendc, NULL, pendpos)) 925a5f0fb15SPaul Saab /* 926a5f0fb15SPaul Saab * Oops. We've probably lost the char which 927a5f0fb15SPaul Saab * was in pendc, since caller won't back up. 928a5f0fb15SPaul Saab */ 929a5f0fb15SPaul Saab return (1); 930a5f0fb15SPaul Saab pendc = '\0'; 931a5f0fb15SPaul Saab } 932a5f0fb15SPaul Saab 933*c77c4889SXin LI if (ch == '\r' && (proc_return == OPT_ON || (bs_mode == BS_SPECIAL && proc_return == OPT_OFF))) 934a5f0fb15SPaul Saab { 93589dd99dcSXin LI if (mbc_buf_len > 0) /* utf_mode must be on. */ 93689dd99dcSXin LI { 93789dd99dcSXin LI /* Flush incomplete (truncated) sequence. */ 93889dd99dcSXin LI r = flush_mbc_buf(mbc_pos); 93989dd99dcSXin LI mbc_buf_index = r + 1; 94089dd99dcSXin LI mbc_buf_len = 0; 94189dd99dcSXin LI if (r) 94289dd99dcSXin LI return (mbc_buf_index); 94389dd99dcSXin LI } 94489dd99dcSXin LI 945a5f0fb15SPaul Saab /* 946a5f0fb15SPaul Saab * Don't put the CR into the buffer until we see 947a5f0fb15SPaul Saab * the next char. If the next char is a newline, 948a5f0fb15SPaul Saab * discard the CR. 949a5f0fb15SPaul Saab */ 950*c77c4889SXin LI pendc = ch; 951a5f0fb15SPaul Saab pendpos = pos; 952a5f0fb15SPaul Saab return (0); 953a5f0fb15SPaul Saab } 954a5f0fb15SPaul Saab 95589dd99dcSXin LI if (!utf_mode) 95689dd99dcSXin LI { 957*c77c4889SXin LI r = do_append(ch, NULL, pos); 95889dd99dcSXin LI } else 95989dd99dcSXin LI { 96089dd99dcSXin LI /* Perform strict validation in all possible cases. */ 96189dd99dcSXin LI if (mbc_buf_len == 0) 96289dd99dcSXin LI { 96389dd99dcSXin LI retry: 96489dd99dcSXin LI mbc_buf_index = 1; 96589dd99dcSXin LI *mbc_buf = c; 96689dd99dcSXin LI if (IS_ASCII_OCTET(c)) 967*c77c4889SXin LI r = do_append(ch, NULL, pos); 96889dd99dcSXin LI else if (IS_UTF8_LEAD(c)) 96989dd99dcSXin LI { 97089dd99dcSXin LI mbc_buf_len = utf_len(c); 97189dd99dcSXin LI mbc_pos = pos; 97289dd99dcSXin LI return (0); 97389dd99dcSXin LI } else 97489dd99dcSXin LI /* UTF8_INVALID or stray UTF8_TRAIL */ 97589dd99dcSXin LI r = flush_mbc_buf(pos); 97689dd99dcSXin LI } else if (IS_UTF8_TRAIL(c)) 97789dd99dcSXin LI { 97889dd99dcSXin LI mbc_buf[mbc_buf_index++] = c; 97989dd99dcSXin LI if (mbc_buf_index < mbc_buf_len) 98089dd99dcSXin LI return (0); 981a15691bfSXin LI if (is_utf8_well_formed(mbc_buf, mbc_buf_index)) 98289dd99dcSXin LI r = do_append(get_wchar(mbc_buf), mbc_buf, mbc_pos); 98389dd99dcSXin LI else 98489dd99dcSXin LI /* Complete, but not shortest form, sequence. */ 98589dd99dcSXin LI mbc_buf_index = r = flush_mbc_buf(mbc_pos); 98689dd99dcSXin LI mbc_buf_len = 0; 98789dd99dcSXin LI } else 98889dd99dcSXin LI { 98989dd99dcSXin LI /* Flush incomplete (truncated) sequence. */ 99089dd99dcSXin LI r = flush_mbc_buf(mbc_pos); 99189dd99dcSXin LI mbc_buf_index = r + 1; 99289dd99dcSXin LI mbc_buf_len = 0; 99389dd99dcSXin LI /* Handle new char. */ 99489dd99dcSXin LI if (!r) 99589dd99dcSXin LI goto retry; 99689dd99dcSXin LI } 99789dd99dcSXin LI } 99889dd99dcSXin LI if (r) 99989dd99dcSXin LI { 100089dd99dcSXin LI /* How many chars should caller back up? */ 100189dd99dcSXin LI r = (!utf_mode) ? 1 : mbc_buf_index; 100289dd99dcSXin LI } 1003a5f0fb15SPaul Saab return (r); 1004a5f0fb15SPaul Saab } 1005a5f0fb15SPaul Saab 1006*c77c4889SXin LI public int pappend(char c, POSITION pos) 1007*c77c4889SXin LI { 1008*c77c4889SXin LI return pappend_b(c, pos, FALSE); 1009*c77c4889SXin LI } 1010*c77c4889SXin LI 1011*c77c4889SXin LI static int store_control_char(LWCHAR ch, constant char *rep, POSITION pos) 1012a5f0fb15SPaul Saab { 10132235c7feSXin LI if (ctldisp == OPT_ON) 10142235c7feSXin LI { 10152235c7feSXin LI /* Output the character itself. */ 10162235c7feSXin LI STORE_CHAR(ch, AT_NORMAL, rep, pos); 10172235c7feSXin LI } else 10182235c7feSXin LI { 10192235c7feSXin LI /* Output a printable representation of the character. */ 1020*c77c4889SXin LI STORE_PRCHAR(ch, pos); 10212235c7feSXin LI } 10222235c7feSXin LI return (0); 10232235c7feSXin LI } 1024a5f0fb15SPaul Saab 1025*c77c4889SXin LI static int store_ansi(LWCHAR ch, constant char *rep, POSITION pos) 10262235c7feSXin LI { 10272235c7feSXin LI switch (ansi_step(line_ansi, ch)) 10282235c7feSXin LI { 10292235c7feSXin LI case ANSI_MID: 10302235c7feSXin LI STORE_CHAR(ch, AT_ANSI, rep, pos); 1031*c77c4889SXin LI if (ansi_osc8_state(line_ansi) == OSC8_PARAMS) 103295270f73SXin LI hlink_in_line = 1; 1033*c77c4889SXin LI xbuf_add_char(&last_ansi, (char) ch); 10342235c7feSXin LI break; 10352235c7feSXin LI case ANSI_END: 10362235c7feSXin LI STORE_CHAR(ch, AT_ANSI, rep, pos); 10372235c7feSXin LI ansi_done(line_ansi); 10382235c7feSXin LI line_ansi = NULL; 1039*c77c4889SXin LI xbuf_add_char(&last_ansi, (char) ch); 104095270f73SXin LI xbuf_set(&last_ansis[curr_last_ansi], &last_ansi); 104195270f73SXin LI xbuf_reset(&last_ansi); 104295270f73SXin LI curr_last_ansi = (curr_last_ansi + 1) % NUM_LAST_ANSIS; 10432235c7feSXin LI break; 104495270f73SXin LI case ANSI_ERR: 104595270f73SXin LI { 10462235c7feSXin LI /* Remove whole unrecognized sequence. */ 1047*c77c4889SXin LI constant char *start = (cshift < hshift) ? xbuf_char_data(&shifted_ansi): linebuf.buf; 1048*c77c4889SXin LI size_t *end = (cshift < hshift) ? &shifted_ansi.end : &linebuf.end; 1049*c77c4889SXin LI constant char *p = start + *end; 10502235c7feSXin LI LWCHAR bch; 10512235c7feSXin LI do { 1052*c77c4889SXin LI bch = step_charc(&p, -1, start); 10532235c7feSXin LI } while (p > start && !IS_CSI_START(bch)); 1054*c77c4889SXin LI *end = ptr_diff(p, start); 105595270f73SXin LI } 105695270f73SXin LI xbuf_reset(&last_ansi); 10572235c7feSXin LI ansi_done(line_ansi); 10582235c7feSXin LI line_ansi = NULL; 105995270f73SXin LI break; 1060*c77c4889SXin LI default: 1061*c77c4889SXin LI break; 10622235c7feSXin LI } 10632235c7feSXin LI return (0); 10642235c7feSXin LI } 1065a5f0fb15SPaul Saab 1066*c77c4889SXin LI static int store_bs(LWCHAR ch, constant char *rep, POSITION pos) 1067a5f0fb15SPaul Saab { 1068d713e089SXin LI if (proc_backspace == OPT_ONPLUS || (bs_mode == BS_CONTROL && proc_backspace == OPT_OFF)) 10692235c7feSXin LI return store_control_char(ch, rep, pos); 10702235c7feSXin LI if (linebuf.end > 0 && 10712235c7feSXin LI ((linebuf.end <= linebuf.print && linebuf.buf[linebuf.end-1] == '\0') || 10722235c7feSXin LI (linebuf.end > 0 && linebuf.attr[linebuf.end - 1] & (AT_ANSI|AT_BINARY)))) 107389dd99dcSXin LI STORE_PRCHAR('\b', pos); 1074d713e089SXin LI else if (proc_backspace == OPT_OFF && bs_mode == BS_NORMAL) 107589dd99dcSXin LI STORE_CHAR(ch, AT_NORMAL, NULL, pos); 1076d713e089SXin LI else if (proc_backspace == OPT_ON || (bs_mode == BS_SPECIAL && proc_backspace == OPT_OFF)) 107789dd99dcSXin LI overstrike = backc(); 107889dd99dcSXin LI return 0; 1079a5f0fb15SPaul Saab } 108089dd99dcSXin LI 1081*c77c4889SXin LI static int do_append(LWCHAR ch, constant char *rep, POSITION pos) 10822235c7feSXin LI { 10832235c7feSXin LI int a = AT_NORMAL; 1084d713e089SXin LI int in_overstrike = overstrike; 10852235c7feSXin LI 10862235c7feSXin LI if (ctldisp == OPT_ONPLUS && line_ansi == NULL) 108730a1828cSXin LI { 10882235c7feSXin LI line_ansi = ansi_start(ch); 108930a1828cSXin LI if (line_ansi != NULL) 1090*c77c4889SXin LI ansi_in_line = TRUE; 109130a1828cSXin LI } 10922235c7feSXin LI 1093d713e089SXin LI overstrike = 0; 10942235c7feSXin LI if (line_ansi != NULL) 10952235c7feSXin LI return store_ansi(ch, rep, pos); 10962235c7feSXin LI 10972235c7feSXin LI if (ch == '\b') 10982235c7feSXin LI return store_bs(ch, rep, pos); 10992235c7feSXin LI 1100d713e089SXin LI if (in_overstrike > 0) 1101a5f0fb15SPaul Saab { 1102a5f0fb15SPaul Saab /* 1103a5f0fb15SPaul Saab * Overstrike the character at the current position 1104a5f0fb15SPaul Saab * in the line buffer. This will cause either 1105a5f0fb15SPaul Saab * underline (if a "_" is overstruck), 1106a5f0fb15SPaul Saab * bold (if an identical character is overstruck), 11072235c7feSXin LI * or just replacing the character in the buffer. 1108a5f0fb15SPaul Saab */ 11092235c7feSXin LI LWCHAR prev_ch; 111089dd99dcSXin LI overstrike = utf_mode ? -1 : 0; 1111a15691bfSXin LI if (utf_mode) 1112a15691bfSXin LI { 111389dd99dcSXin LI /* To be correct, this must be a base character. */ 11142235c7feSXin LI prev_ch = get_wchar(&linebuf.buf[linebuf.end]); 1115a15691bfSXin LI } else 1116a15691bfSXin LI { 11172235c7feSXin LI prev_ch = (unsigned char) linebuf.buf[linebuf.end]; 1118a15691bfSXin LI } 11192235c7feSXin LI a = linebuf.attr[linebuf.end]; 112089dd99dcSXin LI if (ch == prev_ch) 1121c9346414SPaul Saab { 1122000ba3e8STim J. Robbins /* 1123000ba3e8STim J. Robbins * Overstriking a char with itself means make it bold. 1124000ba3e8STim J. Robbins * But overstriking an underscore with itself is 1125000ba3e8STim J. Robbins * ambiguous. It could mean make it bold, or 1126000ba3e8STim J. Robbins * it could mean make it underlined. 1127000ba3e8STim J. Robbins * Use the previous overstrike to resolve it. 1128000ba3e8STim J. Robbins */ 112989dd99dcSXin LI if (ch == '_') 113089dd99dcSXin LI { 113189dd99dcSXin LI if ((a & (AT_BOLD|AT_UNDERLINE)) != AT_NORMAL) 113289dd99dcSXin LI a |= (AT_BOLD|AT_UNDERLINE); 113389dd99dcSXin LI else if (last_overstrike != AT_NORMAL) 113489dd99dcSXin LI a |= last_overstrike; 1135000ba3e8STim J. Robbins else 113689dd99dcSXin LI a |= AT_BOLD; 113789dd99dcSXin LI } else 113889dd99dcSXin LI a |= AT_BOLD; 113989dd99dcSXin LI } else if (ch == '_') 1140c9346414SPaul Saab { 114189dd99dcSXin LI a |= AT_UNDERLINE; 114289dd99dcSXin LI ch = prev_ch; 11432235c7feSXin LI rep = &linebuf.buf[linebuf.end]; 114489dd99dcSXin LI } else if (prev_ch == '_') 1145c9346414SPaul Saab { 114689dd99dcSXin LI a |= AT_UNDERLINE; 1147000ba3e8STim J. Robbins } 114889dd99dcSXin LI /* Else we replace prev_ch, but we keep its attributes. */ 1149d713e089SXin LI } else if (in_overstrike < 0) 1150c9346414SPaul Saab { 115189dd99dcSXin LI if ( is_composing_char(ch) 11522235c7feSXin LI || is_combining_char(get_wchar(&linebuf.buf[linebuf.end]), ch)) 115389dd99dcSXin LI /* Continuation of the same overstrike. */ 115489dd99dcSXin LI a = last_overstrike; 1155a5f0fb15SPaul Saab else 115689dd99dcSXin LI overstrike = 0; 115789dd99dcSXin LI } 115889dd99dcSXin LI 115989dd99dcSXin LI if (ch == '\t') 1160a5f0fb15SPaul Saab { 1161a5f0fb15SPaul Saab /* 1162a5f0fb15SPaul Saab * Expand a tab into spaces. 1163a5f0fb15SPaul Saab */ 1164d713e089SXin LI if (proc_tab == OPT_ONPLUS || (bs_mode == BS_CONTROL && proc_tab == OPT_OFF)) 11652235c7feSXin LI return store_control_char(ch, rep, pos); 116689dd99dcSXin LI STORE_TAB(a, pos); 11672235c7feSXin LI return (0); 116889dd99dcSXin LI } 1169*c77c4889SXin LI if ((!utf_mode || is_ascii_char(ch)) && control_char(ch)) 11702235c7feSXin LI { 11712235c7feSXin LI return store_control_char(ch, rep, pos); 117289dd99dcSXin LI } else if (utf_mode && ctldisp != OPT_ON && is_ubin_char(ch)) 117389dd99dcSXin LI { 117495270f73SXin LI STORE_STRING(prutfchar(ch), AT_BINARY, pos); 1175a5f0fb15SPaul Saab } else 1176a5f0fb15SPaul Saab { 117789dd99dcSXin LI STORE_CHAR(ch, a, rep, pos); 117889dd99dcSXin LI } 117989dd99dcSXin LI return (0); 1180a5f0fb15SPaul Saab } 1181a5f0fb15SPaul Saab 118289dd99dcSXin LI /* 118389dd99dcSXin LI * 118489dd99dcSXin LI */ 1185d713e089SXin LI public int pflushmbc(void) 118689dd99dcSXin LI { 118789dd99dcSXin LI int r = 0; 118889dd99dcSXin LI 118989dd99dcSXin LI if (mbc_buf_len > 0) 119089dd99dcSXin LI { 119189dd99dcSXin LI /* Flush incomplete (truncated) sequence. */ 119289dd99dcSXin LI r = flush_mbc_buf(mbc_pos); 119389dd99dcSXin LI mbc_buf_len = 0; 119489dd99dcSXin LI } 119589dd99dcSXin LI return r; 1196a5f0fb15SPaul Saab } 1197a5f0fb15SPaul Saab 1198a5f0fb15SPaul Saab /* 1199b2ea2440SXin LI * Switch to normal attribute at end of line. 1200b2ea2440SXin LI */ 1201d713e089SXin LI static void add_attr_normal(void) 1202b2ea2440SXin LI { 1203b2ea2440SXin LI if (ctldisp != OPT_ONPLUS || !is_ansi_end('m')) 1204b2ea2440SXin LI return; 120595270f73SXin LI addstr_linebuf("\033[m", AT_ANSI, 0); 120695270f73SXin LI if (hlink_in_line) /* Don't send hyperlink clear if we know we don't need to. */ 120795270f73SXin LI addstr_linebuf("\033]8;;\033\\", AT_ANSI, 0); 1208b2ea2440SXin LI } 1209b2ea2440SXin LI 1210b2ea2440SXin LI /* 1211a5f0fb15SPaul Saab * Terminate the line in the line buffer. 1212a5f0fb15SPaul Saab */ 1213d713e089SXin LI public void pdone(int endline, int chopped, int forw) 1214a5f0fb15SPaul Saab { 121589dd99dcSXin LI (void) pflushmbc(); 121689dd99dcSXin LI 1217a5f0fb15SPaul Saab if (pendc && (pendc != '\r' || !endline)) 1218a5f0fb15SPaul Saab /* 1219a5f0fb15SPaul Saab * If we had a pending character, put it in the buffer. 1220a5f0fb15SPaul Saab * But discard a pending CR if we are at end of line 1221a5f0fb15SPaul Saab * (that is, discard the CR in a CR/LF sequence). 1222a5f0fb15SPaul Saab */ 122389dd99dcSXin LI (void) do_append(pendc, NULL, pendpos); 1224a5f0fb15SPaul Saab 1225b2ea2440SXin LI if (chopped && rscroll_char) 122689dd99dcSXin LI { 1227*c77c4889SXin LI char rscroll_utf8[MAX_UTF_CHAR_LEN+1]; 1228*c77c4889SXin LI char *up = rscroll_utf8; 1229*c77c4889SXin LI 1230b2ea2440SXin LI /* 1231b2ea2440SXin LI * Display the right scrolling char. 1232b2ea2440SXin LI * If we've already filled the rightmost screen char 1233b2ea2440SXin LI * (in the buffer), overwrite it. 1234b2ea2440SXin LI */ 12352235c7feSXin LI if (end_column >= sc_width + cshift) 123689dd99dcSXin LI { 1237b2ea2440SXin LI /* We've already written in the rightmost char. */ 12382235c7feSXin LI end_column = right_column; 1239*c77c4889SXin LI linebuf.end = (size_t) right_curr; 124089dd99dcSXin LI } 1241b2ea2440SXin LI add_attr_normal(); 12422235c7feSXin LI while (end_column < sc_width-1 + cshift) 1243b2ea2440SXin LI { 1244b2ea2440SXin LI /* 1245b2ea2440SXin LI * Space to last (rightmost) char on screen. 1246b2ea2440SXin LI * This may be necessary if the char we overwrote 1247b2ea2440SXin LI * was double-width. 1248b2ea2440SXin LI */ 1249*c77c4889SXin LI add_linebuf(' ', 0, 1); 1250b2ea2440SXin LI } 1251*c77c4889SXin LI /* Print rscroll char. */ 1252*c77c4889SXin LI put_wchar(&up, rscroll_char); 1253*c77c4889SXin LI *up = '\0'; 1254*c77c4889SXin LI addstr_linebuf(rscroll_utf8, rscroll_attr, 0); 1255*c77c4889SXin LI inc_end_column(1); /* assume rscroll_char is single-width */ 1256b2ea2440SXin LI } else 1257b2ea2440SXin LI { 1258b2ea2440SXin LI add_attr_normal(); 125989dd99dcSXin LI } 126089dd99dcSXin LI 1261a5f0fb15SPaul Saab /* 126295270f73SXin LI * If we're coloring a status line, fill out the line with spaces. 126395270f73SXin LI */ 126495270f73SXin LI if (status_line && line_mark_attr != 0) { 126595270f73SXin LI while (end_column +1 < sc_width + cshift) 126695270f73SXin LI add_linebuf(' ', line_mark_attr, 1); 126795270f73SXin LI } 126895270f73SXin LI 126995270f73SXin LI /* 1270a5f0fb15SPaul Saab * Add a newline if necessary, 1271a5f0fb15SPaul Saab * and append a '\0' to the end of the line. 1272720c436cSXin LI * We output a newline if we're not at the right edge of the screen, 1273720c436cSXin LI * or if the terminal doesn't auto wrap, 1274720c436cSXin LI * or if this is really the end of the line AND the terminal ignores 1275720c436cSXin LI * a newline at the right edge. 1276720c436cSXin LI * (In the last case we don't want to output a newline if the terminal 1277720c436cSXin LI * doesn't ignore it since that would produce an extra blank line. 1278720c436cSXin LI * But we do want to output a newline if the terminal ignores it in case 1279720c436cSXin LI * the next line is blank. In that case the single newline output for 1280720c436cSXin LI * that blank line would be ignored!) 1281a5f0fb15SPaul Saab */ 12822235c7feSXin LI if (end_column < sc_width + cshift || !auto_wrap || (endline && ignaw) || ctldisp == OPT_ON) 1283a5f0fb15SPaul Saab { 1284b2ea2440SXin LI add_linebuf('\n', AT_NORMAL, 0); 1285a5f0fb15SPaul Saab } 12862235c7feSXin LI else if (ignaw && end_column >= sc_width + cshift && forw) 1287423c5ce5SXin LI { 1288423c5ce5SXin LI /* 12897374caaaSXin LI * Terminals with "ignaw" don't wrap until they *really* need 12907374caaaSXin LI * to, i.e. when the character *after* the last one to fit on a 12917374caaaSXin LI * line is output. But they are too hard to deal with when they 12927374caaaSXin LI * get in the state where a full screen width of characters 12937374caaaSXin LI * have been output but the cursor is sitting on the right edge 12947374caaaSXin LI * instead of at the start of the next line. 1295f0be0a1fSXin LI * So we nudge them into wrapping by outputting a space 1296f0be0a1fSXin LI * character plus a backspace. But do this only if moving 1297f0be0a1fSXin LI * forward; if we're moving backward and drawing this line at 1298f0be0a1fSXin LI * the top of the screen, the space would overwrite the first 1299f0be0a1fSXin LI * char on the next line. We don't need to do this "nudge" 1300f0be0a1fSXin LI * at the top of the screen anyway. 1301423c5ce5SXin LI */ 1302b2ea2440SXin LI add_linebuf(' ', AT_NORMAL, 1); 1303b2ea2440SXin LI add_linebuf('\b', AT_NORMAL, -1); 1304423c5ce5SXin LI } 13052235c7feSXin LI set_linebuf(linebuf.end, '\0', AT_NORMAL); 1306c9346414SPaul Saab } 13077374caaaSXin LI 13087374caaaSXin LI /* 1309*c77c4889SXin LI * Return the column number (screen position) of a given file position in its line. 1310*c77c4889SXin LI * linepos = position of first char in line 1311*c77c4889SXin LI * spos = position of char being queried 1312*c77c4889SXin LI * saved_pos = position of a known column, or NULL_POSITION if no known column 1313*c77c4889SXin LI * saved_col = column number of a known column, or -1 if no known column 1314*c77c4889SXin LI * 1315*c77c4889SXin LI * This attempts to mimic the logic in pappend() and the store_*() functions. 1316*c77c4889SXin LI * Duplicating this complicated logic is not a good design. 1317*c77c4889SXin LI */ 1318*c77c4889SXin LI 1319*c77c4889SXin LI struct col_pos { int col; POSITION pos; }; 1320*c77c4889SXin LI 1321*c77c4889SXin LI static void col_vs_pos(POSITION linepos, mutable struct col_pos *cp, POSITION saved_pos, int saved_col) 1322*c77c4889SXin LI { 1323*c77c4889SXin LI int col = (saved_col < 0) ? 0 : saved_col; 1324*c77c4889SXin LI LWCHAR prev_ch = 0; 1325*c77c4889SXin LI struct ansi_state *pansi = NULL; 1326*c77c4889SXin LI char utf8_buf[MAX_UTF_CHAR_LEN]; 1327*c77c4889SXin LI int utf8_len = 0; 1328*c77c4889SXin LI POSITION chpos; 1329*c77c4889SXin LI 1330*c77c4889SXin LI if (ch_seek(saved_pos != NULL_POSITION ? saved_pos : linepos)) 1331*c77c4889SXin LI return; 1332*c77c4889SXin LI for (;;) 1333*c77c4889SXin LI { 1334*c77c4889SXin LI int ich; 1335*c77c4889SXin LI char ch; 1336*c77c4889SXin LI int cw = 0; 1337*c77c4889SXin LI 1338*c77c4889SXin LI chpos = ch_tell(); 1339*c77c4889SXin LI ich = ch_forw_get(); 1340*c77c4889SXin LI ch = (char) ich; 1341*c77c4889SXin LI if (ich == EOI || ch == '\n') 1342*c77c4889SXin LI break; 1343*c77c4889SXin LI if (pansi != NULL) 1344*c77c4889SXin LI { 1345*c77c4889SXin LI if (ansi_step(pansi, ch) != ANSI_MID) 1346*c77c4889SXin LI { 1347*c77c4889SXin LI ansi_done(pansi); 1348*c77c4889SXin LI pansi = NULL; 1349*c77c4889SXin LI } 1350*c77c4889SXin LI } else if (ctldisp == OPT_ONPLUS && (pansi = ansi_start(ch)) != NULL) 1351*c77c4889SXin LI { 1352*c77c4889SXin LI /* start of ansi sequence */ 1353*c77c4889SXin LI (void) ansi_step(pansi, ch); 1354*c77c4889SXin LI } else if (ch == '\b') 1355*c77c4889SXin LI { 1356*c77c4889SXin LI if (proc_backspace == OPT_ONPLUS || (bs_mode == BS_CONTROL && proc_backspace == OPT_OFF)) 1357*c77c4889SXin LI cw = strlen(prchar(ch)); 1358*c77c4889SXin LI else 1359*c77c4889SXin LI cw = (utf_mode && is_wide_char(prev_ch)) ? -2 : -1; 1360*c77c4889SXin LI } else if (ch == '\t') 1361*c77c4889SXin LI { 1362*c77c4889SXin LI if (proc_tab == OPT_ONPLUS || (bs_mode == BS_CONTROL && proc_tab == OPT_OFF)) 1363*c77c4889SXin LI cw = strlen(prchar(ch)); 1364*c77c4889SXin LI else 1365*c77c4889SXin LI cw = tab_spaces(col); 1366*c77c4889SXin LI } else if ((!utf_mode || is_ascii_char(ch)) && control_char(ch)) 1367*c77c4889SXin LI { 1368*c77c4889SXin LI cw = strlen(prchar(ch)); 1369*c77c4889SXin LI } else if (utf8_len < MAX_UTF_CHAR_LEN) 1370*c77c4889SXin LI { 1371*c77c4889SXin LI utf8_buf[utf8_len++] = ch; 1372*c77c4889SXin LI if (is_utf8_well_formed(utf8_buf, utf8_len)) 1373*c77c4889SXin LI { 1374*c77c4889SXin LI LWCHAR wch = get_wchar(utf8_buf); 1375*c77c4889SXin LI utf8_len = 0; 1376*c77c4889SXin LI int attr = 0; /* {{ ignoring attribute is not correct for magic cookie terminals }} */ 1377*c77c4889SXin LI if (utf_mode && ctldisp != OPT_ON && is_ubin_char(wch)) 1378*c77c4889SXin LI cw = strlen(prutfchar(wch)); 1379*c77c4889SXin LI else 1380*c77c4889SXin LI cw = pwidth(wch, attr, prev_ch, attr); 1381*c77c4889SXin LI prev_ch = wch; 1382*c77c4889SXin LI } 1383*c77c4889SXin LI } else 1384*c77c4889SXin LI { 1385*c77c4889SXin LI utf8_len = 0; /* flush invalid UTF-8 */ 1386*c77c4889SXin LI } 1387*c77c4889SXin LI 1388*c77c4889SXin LI if (cp->pos != NULL_POSITION && chpos == cp->pos) /* found the position we want */ 1389*c77c4889SXin LI break; 1390*c77c4889SXin LI if (cp->col >= 0 && col >= cp->col && cw > 0) /* found the column we want */ 1391*c77c4889SXin LI break; 1392*c77c4889SXin LI col += cw; 1393*c77c4889SXin LI prev_ch = ch; 1394*c77c4889SXin LI } 1395*c77c4889SXin LI cp->col = col; 1396*c77c4889SXin LI cp->pos = chpos; 1397*c77c4889SXin LI } 1398*c77c4889SXin LI 1399*c77c4889SXin LI public int col_from_pos(POSITION linepos, POSITION spos, POSITION saved_pos, int saved_col) 1400*c77c4889SXin LI { 1401*c77c4889SXin LI struct col_pos cp; 1402*c77c4889SXin LI cp.pos = spos; 1403*c77c4889SXin LI cp.col = -1; 1404*c77c4889SXin LI col_vs_pos(linepos, &cp, saved_pos, saved_col); 1405*c77c4889SXin LI return cp.col; 1406*c77c4889SXin LI } 1407*c77c4889SXin LI 1408*c77c4889SXin LI public POSITION pos_from_col(POSITION linepos, int col, POSITION saved_pos, int saved_col) 1409*c77c4889SXin LI { 1410*c77c4889SXin LI struct col_pos cp; 1411*c77c4889SXin LI cp.col = col + hshift - line_pfx_width(); 1412*c77c4889SXin LI cp.pos = NULL_POSITION; 1413*c77c4889SXin LI col_vs_pos(linepos, &cp, saved_pos, saved_col); 1414*c77c4889SXin LI return cp.pos; 1415*c77c4889SXin LI } 1416*c77c4889SXin LI 1417*c77c4889SXin LI /* 141895270f73SXin LI * Set an attribute on each char of the line in the line buffer. 141995270f73SXin LI */ 1420d713e089SXin LI public void set_attr_line(int a) 142195270f73SXin LI { 1422*c77c4889SXin LI size_t i; 142395270f73SXin LI 142495270f73SXin LI for (i = linebuf.print; i < linebuf.end; i++) 1425d713e089SXin LI if ((linebuf.attr[i] & AT_COLOR) == 0 || (a & AT_COLOR) == 0) 142695270f73SXin LI linebuf.attr[i] |= a; 142795270f73SXin LI } 142895270f73SXin LI 142995270f73SXin LI /* 143095270f73SXin LI * Set the char to be displayed in the status column. 14317374caaaSXin LI */ 1432d713e089SXin LI public void set_status_col(char c, int attr) 14337374caaaSXin LI { 14342235c7feSXin LI set_pfx(0, c, attr); 1435a5f0fb15SPaul Saab } 1436a5f0fb15SPaul Saab 1437a5f0fb15SPaul Saab /* 1438a5f0fb15SPaul Saab * Get a character from the current line. 1439a5f0fb15SPaul Saab * Return the character as the function return value, 1440a5f0fb15SPaul Saab * and the character attribute in *ap. 1441a5f0fb15SPaul Saab */ 1442*c77c4889SXin LI public int gline(size_t i, int *ap) 1443a5f0fb15SPaul Saab { 1444a5f0fb15SPaul Saab if (is_null_line) 1445a5f0fb15SPaul Saab { 1446a5f0fb15SPaul Saab /* 1447a5f0fb15SPaul Saab * If there is no current line, we pretend the line is 1448a5f0fb15SPaul Saab * either "~" or "", depending on the "twiddle" flag. 1449a5f0fb15SPaul Saab */ 145089dd99dcSXin LI if (twiddle) 145189dd99dcSXin LI { 145289dd99dcSXin LI if (i == 0) 145389dd99dcSXin LI { 1454a5f0fb15SPaul Saab *ap = AT_BOLD; 145589dd99dcSXin LI return '~'; 145689dd99dcSXin LI } 145789dd99dcSXin LI --i; 145889dd99dcSXin LI } 145989dd99dcSXin LI /* Make sure we're back to AT_NORMAL before the '\n'. */ 146089dd99dcSXin LI *ap = AT_NORMAL; 146189dd99dcSXin LI return i ? '\0' : '\n'; 1462a5f0fb15SPaul Saab } 1463a5f0fb15SPaul Saab 14642235c7feSXin LI if (i < linebuf.pfx_end) 14652235c7feSXin LI { 14662235c7feSXin LI *ap = linebuf.pfx_attr[i]; 14672235c7feSXin LI return linebuf.pfx[i]; 14682235c7feSXin LI } 14692235c7feSXin LI i += linebuf.print - linebuf.pfx_end; 14702235c7feSXin LI *ap = linebuf.attr[i]; 14712235c7feSXin LI return (linebuf.buf[i] & 0xFF); 1472a5f0fb15SPaul Saab } 1473a5f0fb15SPaul Saab 1474a5f0fb15SPaul Saab /* 1475a5f0fb15SPaul Saab * Indicate that there is no current line. 1476a5f0fb15SPaul Saab */ 1477d713e089SXin LI public void null_line(void) 1478a5f0fb15SPaul Saab { 1479*c77c4889SXin LI is_null_line = TRUE; 1480a5f0fb15SPaul Saab cshift = 0; 1481a5f0fb15SPaul Saab } 1482a5f0fb15SPaul Saab 1483a5f0fb15SPaul Saab /* 1484a5f0fb15SPaul Saab * Analogous to forw_line(), but deals with "raw lines": 1485a5f0fb15SPaul Saab * lines which are not split for screen width. 1486a5f0fb15SPaul Saab * {{ This is supposed to be more efficient than forw_line(). }} 1487a5f0fb15SPaul Saab */ 1488*c77c4889SXin LI public POSITION forw_raw_line_len(POSITION curr_pos, size_t read_len, constant char **linep, size_t *line_lenp) 1489a5f0fb15SPaul Saab { 1490*c77c4889SXin LI size_t n; 14911ea31627SRobert Watson int c; 1492a5f0fb15SPaul Saab POSITION new_pos; 1493a5f0fb15SPaul Saab 1494a5f0fb15SPaul Saab if (curr_pos == NULL_POSITION || ch_seek(curr_pos) || 1495a5f0fb15SPaul Saab (c = ch_forw_get()) == EOI) 1496a5f0fb15SPaul Saab return (NULL_POSITION); 1497a5f0fb15SPaul Saab 1498c9346414SPaul Saab n = 0; 1499a5f0fb15SPaul Saab for (;;) 1500a5f0fb15SPaul Saab { 150189dd99dcSXin LI if (c == '\n' || c == EOI || ABORT_SIGS()) 1502a5f0fb15SPaul Saab { 1503a5f0fb15SPaul Saab new_pos = ch_tell(); 1504a5f0fb15SPaul Saab break; 1505a5f0fb15SPaul Saab } 1506c9346414SPaul Saab if (n >= size_linebuf-1) 1507c9346414SPaul Saab { 1508c9346414SPaul Saab if (expand_linebuf()) 1509a5f0fb15SPaul Saab { 1510a5f0fb15SPaul Saab /* 1511a5f0fb15SPaul Saab * Overflowed the input buffer. 1512a5f0fb15SPaul Saab * Pretend the line ended here. 1513a5f0fb15SPaul Saab */ 1514a5f0fb15SPaul Saab new_pos = ch_tell() - 1; 1515a5f0fb15SPaul Saab break; 1516a5f0fb15SPaul Saab } 1517c9346414SPaul Saab } 1518*c77c4889SXin LI linebuf.buf[n++] = (char) c; 1519*c77c4889SXin LI if (read_len != size_t_null && read_len > 0 && n >= read_len) 1520*c77c4889SXin LI { 1521*c77c4889SXin LI new_pos = ch_tell(); 1522*c77c4889SXin LI break; 1523*c77c4889SXin LI } 1524a5f0fb15SPaul Saab c = ch_forw_get(); 1525a5f0fb15SPaul Saab } 15262235c7feSXin LI linebuf.buf[n] = '\0'; 1527a5f0fb15SPaul Saab if (linep != NULL) 15282235c7feSXin LI *linep = linebuf.buf; 1529720c436cSXin LI if (line_lenp != NULL) 1530720c436cSXin LI *line_lenp = n; 1531a5f0fb15SPaul Saab return (new_pos); 1532a5f0fb15SPaul Saab } 1533a5f0fb15SPaul Saab 1534*c77c4889SXin LI public POSITION forw_raw_line(POSITION curr_pos, constant char **linep, size_t *line_lenp) 1535*c77c4889SXin LI { 1536*c77c4889SXin LI return forw_raw_line_len(curr_pos, size_t_null, linep, line_lenp); 1537*c77c4889SXin LI } 1538*c77c4889SXin LI 1539a5f0fb15SPaul Saab /* 1540a5f0fb15SPaul Saab * Analogous to back_line(), but deals with "raw lines". 1541a5f0fb15SPaul Saab * {{ This is supposed to be more efficient than back_line(). }} 1542a5f0fb15SPaul Saab */ 1543*c77c4889SXin LI public POSITION back_raw_line(POSITION curr_pos, constant char **linep, size_t *line_lenp) 1544a5f0fb15SPaul Saab { 1545*c77c4889SXin LI size_t n; 15461ea31627SRobert Watson int c; 1547a5f0fb15SPaul Saab POSITION new_pos; 1548a5f0fb15SPaul Saab 1549a5f0fb15SPaul Saab if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() || 1550a5f0fb15SPaul Saab ch_seek(curr_pos-1)) 1551a5f0fb15SPaul Saab return (NULL_POSITION); 1552a5f0fb15SPaul Saab 1553c9346414SPaul Saab n = size_linebuf; 15542235c7feSXin LI linebuf.buf[--n] = '\0'; 1555a5f0fb15SPaul Saab for (;;) 1556a5f0fb15SPaul Saab { 1557a5f0fb15SPaul Saab c = ch_back_get(); 155889dd99dcSXin LI if (c == '\n' || ABORT_SIGS()) 1559a5f0fb15SPaul Saab { 1560a5f0fb15SPaul Saab /* 1561a5f0fb15SPaul Saab * This is the newline ending the previous line. 1562a5f0fb15SPaul Saab * We have hit the beginning of the line. 1563a5f0fb15SPaul Saab */ 1564a5f0fb15SPaul Saab new_pos = ch_tell() + 1; 1565a5f0fb15SPaul Saab break; 1566a5f0fb15SPaul Saab } 1567a5f0fb15SPaul Saab if (c == EOI) 1568a5f0fb15SPaul Saab { 1569a5f0fb15SPaul Saab /* 1570a5f0fb15SPaul Saab * We have hit the beginning of the file. 1571a5f0fb15SPaul Saab * This must be the first line in the file. 1572a5f0fb15SPaul Saab * This must, of course, be the beginning of the line. 1573a5f0fb15SPaul Saab */ 1574a5f0fb15SPaul Saab new_pos = ch_zero(); 1575a5f0fb15SPaul Saab break; 1576a5f0fb15SPaul Saab } 1577c9346414SPaul Saab if (n <= 0) 1578c9346414SPaul Saab { 1579*c77c4889SXin LI size_t old_size_linebuf = size_linebuf; 1580c9346414SPaul Saab char *fm; 1581c9346414SPaul Saab char *to; 1582c9346414SPaul Saab if (expand_linebuf()) 1583a5f0fb15SPaul Saab { 1584a5f0fb15SPaul Saab /* 1585a5f0fb15SPaul Saab * Overflowed the input buffer. 1586a5f0fb15SPaul Saab * Pretend the line ended here. 1587a5f0fb15SPaul Saab */ 1588a5f0fb15SPaul Saab new_pos = ch_tell() + 1; 1589a5f0fb15SPaul Saab break; 1590a5f0fb15SPaul Saab } 1591c9346414SPaul Saab /* 1592c9346414SPaul Saab * Shift the data to the end of the new linebuf. 1593c9346414SPaul Saab */ 15942235c7feSXin LI for (fm = linebuf.buf + old_size_linebuf - 1, 15952235c7feSXin LI to = linebuf.buf + size_linebuf - 1; 15962235c7feSXin LI fm >= linebuf.buf; fm--, to--) 1597c9346414SPaul Saab *to = *fm; 1598c9346414SPaul Saab n = size_linebuf - old_size_linebuf; 1599c9346414SPaul Saab } 1600*c77c4889SXin LI linebuf.buf[--n] = (char) c; 1601a5f0fb15SPaul Saab } 1602a5f0fb15SPaul Saab if (linep != NULL) 16032235c7feSXin LI *linep = &linebuf.buf[n]; 1604720c436cSXin LI if (line_lenp != NULL) 1605720c436cSXin LI *line_lenp = size_linebuf - 1 - n; 1606a5f0fb15SPaul Saab return (new_pos); 1607a5f0fb15SPaul Saab } 1608f6b74a7dSXin LI 1609f6b74a7dSXin LI /* 1610d713e089SXin LI * Skip cols printable columns at the start of line. 1611d713e089SXin LI * Return number of bytes skipped. 1612d713e089SXin LI */ 1613*c77c4889SXin LI public int skip_columns(int cols, constant char **linep, size_t *line_lenp) 1614d713e089SXin LI { 1615*c77c4889SXin LI constant char *line = *linep; 1616*c77c4889SXin LI constant char *eline = line + *line_lenp; 1617d713e089SXin LI LWCHAR pch = 0; 1618*c77c4889SXin LI size_t bytes; 1619d713e089SXin LI 1620d713e089SXin LI while (cols > 0 && line < eline) 1621d713e089SXin LI { 1622*c77c4889SXin LI LWCHAR ch = step_charc(&line, +1, eline); 1623d713e089SXin LI struct ansi_state *pansi = ansi_start(ch); 1624d713e089SXin LI if (pansi != NULL) 1625d713e089SXin LI { 1626d713e089SXin LI skip_ansi(pansi, &line, eline); 1627d713e089SXin LI ansi_done(pansi); 1628d713e089SXin LI pch = 0; 1629d713e089SXin LI } else 1630d713e089SXin LI { 1631d713e089SXin LI int w = pwidth(ch, 0, pch, 0); 1632d713e089SXin LI cols -= w; 1633d713e089SXin LI pch = ch; 1634d713e089SXin LI } 1635d713e089SXin LI } 1636*c77c4889SXin LI bytes = ptr_diff(line, *linep); 1637d713e089SXin LI *linep = line; 1638d713e089SXin LI *line_lenp -= bytes; 1639*c77c4889SXin LI return (int) bytes; /*{{type-issue}}*/ 1640d713e089SXin LI } 1641d713e089SXin LI 1642d713e089SXin LI /* 164395270f73SXin LI * Append a string to the line buffer. 164495270f73SXin LI */ 1645d713e089SXin LI static int pappstr(constant char *str) 164695270f73SXin LI { 164795270f73SXin LI while (*str != '\0') 164895270f73SXin LI { 164995270f73SXin LI if (pappend(*str++, NULL_POSITION)) 165095270f73SXin LI /* Doesn't fit on screen. */ 165195270f73SXin LI return 1; 165295270f73SXin LI } 165395270f73SXin LI return 0; 165495270f73SXin LI } 165595270f73SXin LI 165695270f73SXin LI /* 165795270f73SXin LI * Load a string into the line buffer. 165895270f73SXin LI * If the string is too long to fit on the screen, 165995270f73SXin LI * truncate the beginning of the string to fit. 166095270f73SXin LI */ 1661d713e089SXin LI public void load_line(constant char *str) 166295270f73SXin LI { 166395270f73SXin LI int save_hshift = hshift; 166495270f73SXin LI 166595270f73SXin LI hshift = 0; 166695270f73SXin LI for (;;) 166795270f73SXin LI { 166895270f73SXin LI prewind(); 166995270f73SXin LI if (pappstr(str) == 0) 167095270f73SXin LI break; 167195270f73SXin LI /* 167295270f73SXin LI * Didn't fit on screen; increase left shift by one. 167395270f73SXin LI * {{ This gets very inefficient if the string 167495270f73SXin LI * is much longer than the screen width. }} 167595270f73SXin LI */ 167695270f73SXin LI hshift += 1; 167795270f73SXin LI } 167895270f73SXin LI set_linebuf(linebuf.end, '\0', AT_NORMAL); 167995270f73SXin LI hshift = save_hshift; 168095270f73SXin LI } 168195270f73SXin LI 168295270f73SXin LI /* 1683f6b74a7dSXin LI * Find the shift necessary to show the end of the longest displayed line. 1684f6b74a7dSXin LI */ 1685d713e089SXin LI public int rrshift(void) 1686f6b74a7dSXin LI { 1687f6b74a7dSXin LI POSITION pos; 1688f6b74a7dSXin LI int save_width; 1689*c77c4889SXin LI int sline; 1690f6b74a7dSXin LI int longest = 0; 1691f6b74a7dSXin LI 1692f6b74a7dSXin LI save_width = sc_width; 1693*c77c4889SXin LI sc_width = INT_MAX; /* so forw_line() won't chop */ 1694*c77c4889SXin LI for (sline = TOP; sline < sc_height; sline++) 1695*c77c4889SXin LI if ((pos = position(sline)) != NULL_POSITION) 1696*c77c4889SXin LI break; 1697*c77c4889SXin LI for (; sline < sc_height && pos != NULL_POSITION; sline++) 1698f6b74a7dSXin LI { 1699f6b74a7dSXin LI pos = forw_line(pos); 17002235c7feSXin LI if (end_column > longest) 17012235c7feSXin LI longest = end_column; 1702f6b74a7dSXin LI } 1703f6b74a7dSXin LI sc_width = save_width; 1704f6b74a7dSXin LI if (longest < sc_width) 1705f6b74a7dSXin LI return 0; 1706f6b74a7dSXin LI return longest - sc_width; 1707f6b74a7dSXin LI } 17082235c7feSXin LI 17092235c7feSXin LI /* 17102235c7feSXin LI * Get the color_map index associated with a given attribute. 17112235c7feSXin LI */ 1712d713e089SXin LI static int lookup_color_index(int attr) 17132235c7feSXin LI { 1714d713e089SXin LI int cx; 1715*c77c4889SXin LI for (cx = 0; cx < countof(color_map); cx++) 1716d713e089SXin LI if (color_map[cx].attr == attr) 1717d713e089SXin LI return cx; 1718d713e089SXin LI return -1; 17192235c7feSXin LI } 1720d713e089SXin LI 1721d713e089SXin LI static int color_index(int attr) 1722d713e089SXin LI { 1723d713e089SXin LI if (use_color && (attr & AT_COLOR)) 1724d713e089SXin LI return lookup_color_index(attr & AT_COLOR); 17252235c7feSXin LI if (attr & AT_UNDERLINE) 1726d713e089SXin LI return lookup_color_index(AT_UNDERLINE); 172795270f73SXin LI if (attr & AT_BOLD) 1728d713e089SXin LI return lookup_color_index(AT_BOLD); 172995270f73SXin LI if (attr & AT_BLINK) 1730d713e089SXin LI return lookup_color_index(AT_BLINK); 173195270f73SXin LI if (attr & AT_STANDOUT) 1732d713e089SXin LI return lookup_color_index(AT_STANDOUT); 17332235c7feSXin LI return -1; 17342235c7feSXin LI } 17352235c7feSXin LI 17362235c7feSXin LI /* 17372235c7feSXin LI * Set the color string to use for a given attribute. 17382235c7feSXin LI */ 1739*c77c4889SXin LI public int set_color_map(int attr, constant char *colorstr) 17402235c7feSXin LI { 17412235c7feSXin LI int cx = color_index(attr); 17422235c7feSXin LI if (cx < 0) 17432235c7feSXin LI return -1; 1744d713e089SXin LI if (strlen(colorstr)+1 > sizeof(color_map[cx].color)) 17452235c7feSXin LI return -1; 1746*c77c4889SXin LI if (*colorstr != '\0' && parse_color(colorstr, NULL, NULL, NULL) == CT_NULL) 17472235c7feSXin LI return -1; 1748d713e089SXin LI strcpy(color_map[cx].color, colorstr); 17492235c7feSXin LI return 0; 17502235c7feSXin LI } 17512235c7feSXin LI 17522235c7feSXin LI /* 17532235c7feSXin LI * Get the color string to use for a given attribute. 17542235c7feSXin LI */ 1755*c77c4889SXin LI public constant char * get_color_map(int attr) 17562235c7feSXin LI { 17572235c7feSXin LI int cx = color_index(attr); 17582235c7feSXin LI if (cx < 0) 17592235c7feSXin LI return NULL; 1760d713e089SXin LI return color_map[cx].color; 17612235c7feSXin LI } 1762