1238730Sdelphij/* 2330571Sdelphij * Copyright (C) 1984-2017 Mark Nudelman 3238730Sdelphij * 4238730Sdelphij * You may distribute under the terms of either the GNU General Public 5238730Sdelphij * License or the Less License, as specified in the README file. 6238730Sdelphij * 7238730Sdelphij * For more information, see the README file. 8238730Sdelphij */ 960786Sps 1060786Sps/* 1160786Sps * Routines to manipulate the "line buffer". 1260786Sps * The line buffer holds a line of output as it is being built 1360786Sps * in preparation for output to the screen. 1460786Sps */ 1560786Sps 1660786Sps#include "less.h" 17161478Sdelphij#include "charset.h" 18330571Sdelphij#include "position.h" 1960786Sps 20330571Sdelphij#if MSDOS_COMPILER==WIN32C 21330571Sdelphij#define WIN32_LEAN_AND_MEAN 22330571Sdelphij#include <windows.h> 23330571Sdelphij#endif 24330571Sdelphij 25161478Sdelphijstatic char *linebuf = NULL; /* Buffer which holds the current output line */ 2689019Spsstatic char *attr = NULL; /* Extension of linebuf to hold attributes */ 2789019Spspublic int size_linebuf = 0; /* Size of line buffer (and attr buffer) */ 2860786Sps 29161478Sdelphijstatic int cshift; /* Current left-shift of output line buffer */ 3060786Spspublic int hshift; /* Desired left-shift of output line buffer */ 3189019Spspublic int tabstops[TABSTOP_MAX] = { 0 }; /* Custom tabstops */ 3289019Spspublic int ntabstops = 1; /* Number of tabstops */ 3389019Spspublic int tabdefault = 8; /* Default repeated tabstops */ 34237613Sdelphijpublic POSITION highest_hilite; /* Pos of last hilite in file found so far */ 3560786Sps 3660786Spsstatic int curr; /* Index into linebuf */ 3760786Spsstatic int column; /* Printable length, accounting for 3860786Sps backspaces, etc. */ 39330571Sdelphijstatic int right_curr; 40330571Sdelphijstatic int right_column; 4160786Spsstatic int overstrike; /* Next char should overstrike previous char */ 42128345Stjrstatic int last_overstrike = AT_NORMAL; 4360786Spsstatic int is_null_line; /* There is no current line */ 4463128Spsstatic int lmargin; /* Left margin */ 45294286Sdelphijstatic LWCHAR pendc; 4660786Spsstatic POSITION pendpos; 4760786Spsstatic char *end_ansi_chars; 48161478Sdelphijstatic char *mid_ansi_chars; 4960786Sps 50330571Sdelphijstatic int attr_swidth LESSPARAMS ((int a)); 51330571Sdelphijstatic int attr_ewidth LESSPARAMS ((int a)); 52330571Sdelphijstatic int do_append LESSPARAMS ((LWCHAR ch, char *rep, POSITION pos)); 5360786Sps 54161478Sdelphijextern int sigs; 5560786Spsextern int bs_mode; 5660786Spsextern int linenums; 5760786Spsextern int ctldisp; 5860786Spsextern int twiddle; 5960786Spsextern int binattr; 6063128Spsextern int status_col; 6160786Spsextern int auto_wrap, ignaw; 6260786Spsextern int bo_s_width, bo_e_width; 6360786Spsextern int ul_s_width, ul_e_width; 6460786Spsextern int bl_s_width, bl_e_width; 6560786Spsextern int so_s_width, so_e_width; 6660786Spsextern int sc_width, sc_height; 6760786Spsextern int utf_mode; 6863128Spsextern POSITION start_attnpos; 6963128Spsextern POSITION end_attnpos; 70330571Sdelphijextern LWCHAR rscroll_char; 71330571Sdelphijextern int rscroll_attr; 7260786Sps 73161478Sdelphijstatic char mbc_buf[MAX_UTF_CHAR_LEN]; 74161478Sdelphijstatic int mbc_buf_len = 0; 75161478Sdelphijstatic int mbc_buf_index = 0; 76161478Sdelphijstatic POSITION mbc_pos; 77161478Sdelphij 7860786Sps/* 7960786Sps * Initialize from environment variables. 8060786Sps */ 8160786Sps public void 8260786Spsinit_line() 8360786Sps{ 8460786Sps end_ansi_chars = lgetenv("LESSANSIENDCHARS"); 8560786Sps if (end_ansi_chars == NULL || *end_ansi_chars == '\0') 8660786Sps end_ansi_chars = "m"; 87161478Sdelphij 88161478Sdelphij mid_ansi_chars = lgetenv("LESSANSIMIDCHARS"); 89161478Sdelphij if (mid_ansi_chars == NULL || *mid_ansi_chars == '\0') 90294286Sdelphij mid_ansi_chars = "0123456789:;[?!\"'#%()*+ "; 91161478Sdelphij 9289019Sps linebuf = (char *) ecalloc(LINEBUF_SIZE, sizeof(char)); 9389019Sps attr = (char *) ecalloc(LINEBUF_SIZE, sizeof(char)); 9489019Sps size_linebuf = LINEBUF_SIZE; 9560786Sps} 9660786Sps 9760786Sps/* 9889019Sps * Expand the line buffer. 9989019Sps */ 100161478Sdelphij static int 10189019Spsexpand_linebuf() 10289019Sps{ 103161478Sdelphij /* Double the size of the line buffer. */ 104161478Sdelphij int new_size = size_linebuf * 2; 105161478Sdelphij 106161478Sdelphij /* Just realloc to expand the buffer, if we can. */ 107161478Sdelphij#if HAVE_REALLOC 108161478Sdelphij char *new_buf = (char *) realloc(linebuf, new_size); 109161478Sdelphij char *new_attr = (char *) realloc(attr, new_size); 110161478Sdelphij#else 11189019Sps char *new_buf = (char *) calloc(new_size, sizeof(char)); 11289019Sps char *new_attr = (char *) calloc(new_size, sizeof(char)); 113161478Sdelphij#endif 11489019Sps if (new_buf == NULL || new_attr == NULL) 11589019Sps { 11689019Sps if (new_attr != NULL) 11789019Sps free(new_attr); 11889019Sps if (new_buf != NULL) 11989019Sps free(new_buf); 12089019Sps return 1; 12189019Sps } 122161478Sdelphij#if HAVE_REALLOC 123161478Sdelphij /* 124161478Sdelphij * We realloc'd the buffers; they already have the old contents. 125161478Sdelphij */ 126161478Sdelphij #if 0 127161478Sdelphij memset(new_buf + size_linebuf, 0, new_size - size_linebuf); 128161478Sdelphij memset(new_attr + size_linebuf, 0, new_size - size_linebuf); 129161478Sdelphij #endif 130161478Sdelphij#else 131161478Sdelphij /* 132161478Sdelphij * We just calloc'd the buffers; copy the old contents. 133161478Sdelphij */ 13489019Sps memcpy(new_buf, linebuf, size_linebuf * sizeof(char)); 13589019Sps memcpy(new_attr, attr, size_linebuf * sizeof(char)); 136128345Stjr free(attr); 137128345Stjr free(linebuf); 138161478Sdelphij#endif 13989019Sps linebuf = new_buf; 14089019Sps attr = new_attr; 14189019Sps size_linebuf = new_size; 14289019Sps return 0; 14389019Sps} 14489019Sps 14589019Sps/* 146161478Sdelphij * Is a character ASCII? 147161478Sdelphij */ 148161478Sdelphij public int 149161478Sdelphijis_ascii_char(ch) 150161478Sdelphij LWCHAR ch; 151161478Sdelphij{ 152161478Sdelphij return (ch <= 0x7F); 153161478Sdelphij} 154161478Sdelphij 155161478Sdelphij/* 15660786Sps * Rewind the line buffer. 15760786Sps */ 15860786Sps public void 15960786Spsprewind() 16060786Sps{ 16160786Sps curr = 0; 16260786Sps column = 0; 163330571Sdelphij right_curr = 0; 164330571Sdelphij right_column = 0; 165161478Sdelphij cshift = 0; 16660786Sps overstrike = 0; 167161478Sdelphij last_overstrike = AT_NORMAL; 168161478Sdelphij mbc_buf_len = 0; 16960786Sps is_null_line = 0; 17060786Sps pendc = '\0'; 17163128Sps lmargin = 0; 172128345Stjr if (status_col) 173330571Sdelphij lmargin += 2; 17460786Sps} 17560786Sps 17660786Sps/* 177330571Sdelphij * Set a character in the line buffer. 178330571Sdelphij */ 179330571Sdelphij static void 180330571Sdelphijset_linebuf(n, ch, a) 181330571Sdelphij int n; 182330571Sdelphij LWCHAR ch; 183330571Sdelphij char a; 184330571Sdelphij{ 185330571Sdelphij linebuf[n] = ch; 186330571Sdelphij attr[n] = a; 187330571Sdelphij} 188330571Sdelphij 189330571Sdelphij/* 190330571Sdelphij * Append a character to the line buffer. 191330571Sdelphij */ 192330571Sdelphij static void 193330571Sdelphijadd_linebuf(ch, a, w) 194330571Sdelphij LWCHAR ch; 195330571Sdelphij char a; 196330571Sdelphij int w; 197330571Sdelphij{ 198330571Sdelphij set_linebuf(curr++, ch, a); 199330571Sdelphij column += w; 200330571Sdelphij} 201330571Sdelphij 202330571Sdelphij/* 20360786Sps * Insert the line number (of the given position) into the line buffer. 20460786Sps */ 20560786Sps public void 20660786Spsplinenum(pos) 20760786Sps POSITION pos; 20860786Sps{ 209330571Sdelphij LINENUM linenum = 0; 210330571Sdelphij int i; 21160786Sps 21263128Sps if (linenums == OPT_ONPLUS) 21363128Sps { 21463128Sps /* 21563128Sps * Get the line number and put it in the current line. 21663128Sps * {{ Note: since find_linenum calls forw_raw_line, 21763128Sps * it may seek in the input file, requiring the caller 21863128Sps * of plinenum to re-seek if necessary. }} 21963128Sps * {{ Since forw_raw_line modifies linebuf, we must 22063128Sps * do this first, before storing anything in linebuf. }} 22163128Sps */ 222128345Stjr linenum = find_linenum(pos); 22363128Sps } 22463128Sps 22560786Sps /* 22663128Sps * Display a status column if the -J option is set. 22760786Sps */ 22863128Sps if (status_col) 22963128Sps { 230330571Sdelphij int a = AT_NORMAL; 231330571Sdelphij char c = posmark(pos); 232330571Sdelphij if (c != 0) 233330571Sdelphij a |= AT_HILITE; 234330571Sdelphij else 235330571Sdelphij { 236330571Sdelphij c = ' '; 237330571Sdelphij if (start_attnpos != NULL_POSITION && 238330571Sdelphij pos >= start_attnpos && pos <= end_attnpos) 239330571Sdelphij a |= AT_HILITE; 240330571Sdelphij } 241330571Sdelphij add_linebuf(c, a, 1); /* column 0: status */ 242330571Sdelphij add_linebuf(' ', AT_NORMAL, 1); /* column 1: empty */ 24363128Sps } 244330571Sdelphij 24560786Sps /* 24663128Sps * Display the line number at the start of each line 24763128Sps * if the -N option is set. 24860786Sps */ 24963128Sps if (linenums == OPT_ONPLUS) 25063128Sps { 251330571Sdelphij char buf[INT_STRLEN_BOUND(linenum) + 2]; 252330571Sdelphij int pad = 0; 253128345Stjr int n; 254128345Stjr 255128345Stjr linenumtoa(linenum, buf); 256294286Sdelphij n = (int) strlen(buf); 257128345Stjr if (n < MIN_LINENUM_WIDTH) 258330571Sdelphij pad = MIN_LINENUM_WIDTH - n; 259330571Sdelphij for (i = 0; i < pad; i++) 260330571Sdelphij add_linebuf(' ', AT_NORMAL, 1); 261128345Stjr for (i = 0; i < n; i++) 262330571Sdelphij add_linebuf(buf[i], AT_BOLD, 1); 263330571Sdelphij add_linebuf(' ', AT_NORMAL, 1); 264330571Sdelphij lmargin += n + pad + 1; 26563128Sps } 26660786Sps /* 26763128Sps * Append enough spaces to bring us to the lmargin. 26860786Sps */ 26963128Sps while (column < lmargin) 27060786Sps { 271330571Sdelphij add_linebuf(' ', AT_NORMAL, 1); 27263128Sps } 27360786Sps} 27460786Sps 27560786Sps/* 276161478Sdelphij * Shift the input line left. 277161478Sdelphij * This means discarding N printable chars at the start of the buffer. 27860786Sps */ 279161478Sdelphij static void 280161478Sdelphijpshift(shift) 281161478Sdelphij int shift; 282161478Sdelphij{ 283161478Sdelphij LWCHAR prev_ch = 0; 284161478Sdelphij unsigned char c; 285161478Sdelphij int shifted = 0; 286161478Sdelphij int to; 287161478Sdelphij int from; 28889019Sps int len; 289161478Sdelphij int width; 290161478Sdelphij int prev_attr; 291161478Sdelphij int next_attr; 29289019Sps 293161478Sdelphij if (shift > column - lmargin) 294161478Sdelphij shift = column - lmargin; 295161478Sdelphij if (shift > curr - lmargin) 296161478Sdelphij shift = curr - lmargin; 297161478Sdelphij 298161478Sdelphij to = from = lmargin; 29989019Sps /* 300161478Sdelphij * We keep on going when shifted == shift 301161478Sdelphij * to get all combining chars. 30289019Sps */ 303161478Sdelphij while (shifted <= shift && from < curr) 30489019Sps { 305161478Sdelphij c = linebuf[from]; 306172471Sdelphij if (ctldisp == OPT_ONPLUS && IS_CSI_START(c)) 30789019Sps { 308161478Sdelphij /* Keep cumulative effect. */ 309161478Sdelphij linebuf[to] = c; 310161478Sdelphij attr[to++] = attr[from++]; 311161478Sdelphij while (from < curr && linebuf[from]) 312161478Sdelphij { 313161478Sdelphij linebuf[to] = linebuf[from]; 314161478Sdelphij attr[to++] = attr[from]; 315161478Sdelphij if (!is_ansi_middle(linebuf[from++])) 316161478Sdelphij break; 317161478Sdelphij } 318161478Sdelphij continue; 319161478Sdelphij } 320161478Sdelphij 321161478Sdelphij width = 0; 322161478Sdelphij 323161478Sdelphij if (!IS_ASCII_OCTET(c) && utf_mode) 324161478Sdelphij { 325161478Sdelphij /* Assumes well-formedness validation already done. */ 326161478Sdelphij LWCHAR ch; 327161478Sdelphij 328161478Sdelphij len = utf_len(c); 329161478Sdelphij if (from + len > curr) 330161478Sdelphij break; 331161478Sdelphij ch = get_wchar(linebuf + from); 332161478Sdelphij if (!is_composing_char(ch) && !is_combining_char(prev_ch, ch)) 333161478Sdelphij width = is_wide_char(ch) ? 2 : 1; 334161478Sdelphij prev_ch = ch; 33589019Sps } else 33689019Sps { 337161478Sdelphij len = 1; 338161478Sdelphij if (c == '\b') 339161478Sdelphij /* XXX - Incorrect if several '\b' in a row. */ 340161478Sdelphij width = (utf_mode && is_wide_char(prev_ch)) ? -2 : -1; 341161478Sdelphij else if (!control_char(c)) 342161478Sdelphij width = 1; 343161478Sdelphij prev_ch = 0; 344161478Sdelphij } 345161478Sdelphij 346161478Sdelphij if (width == 2 && shift - shifted == 1) { 347161478Sdelphij /* Should never happen when called by pshift_all(). */ 348161478Sdelphij attr[to] = attr[from]; 349161478Sdelphij /* 350161478Sdelphij * Assume a wide_char will never be the first half of a 351161478Sdelphij * combining_char pair, so reset prev_ch in case we're 352161478Sdelphij * followed by a '\b'. 353161478Sdelphij */ 354161478Sdelphij prev_ch = linebuf[to++] = ' '; 355161478Sdelphij from += len; 356161478Sdelphij shifted++; 357161478Sdelphij continue; 358161478Sdelphij } 359161478Sdelphij 360161478Sdelphij /* Adjust width for magic cookies. */ 361161478Sdelphij prev_attr = (to > 0) ? attr[to-1] : AT_NORMAL; 362161478Sdelphij next_attr = (from + len < curr) ? attr[from + len] : prev_attr; 363161478Sdelphij if (!is_at_equiv(attr[from], prev_attr) && 364161478Sdelphij !is_at_equiv(attr[from], next_attr)) 365161478Sdelphij { 366161478Sdelphij width += attr_swidth(attr[from]); 367161478Sdelphij if (from + len < curr) 368161478Sdelphij width += attr_ewidth(attr[from]); 369161478Sdelphij if (is_at_equiv(prev_attr, next_attr)) 37089019Sps { 371161478Sdelphij width += attr_ewidth(prev_attr); 372161478Sdelphij if (from + len < curr) 373161478Sdelphij width += attr_swidth(next_attr); 37489019Sps } 37589019Sps } 37689019Sps 377161478Sdelphij if (shift - shifted < width) 378161478Sdelphij break; 379161478Sdelphij from += len; 380161478Sdelphij shifted += width; 381161478Sdelphij if (shifted < 0) 382161478Sdelphij shifted = 0; 38360786Sps } 384161478Sdelphij while (from < curr) 38560786Sps { 386161478Sdelphij linebuf[to] = linebuf[from]; 387161478Sdelphij attr[to++] = attr[from++]; 38860786Sps } 389161478Sdelphij curr = to; 390161478Sdelphij column -= shifted; 391161478Sdelphij cshift += shifted; 39260786Sps} 39360786Sps 39460786Sps/* 395161478Sdelphij * 39660786Sps */ 397161478Sdelphij public void 398161478Sdelphijpshift_all() 39960786Sps{ 400161478Sdelphij pshift(column); 40160786Sps} 40260786Sps 40360786Sps/* 40460786Sps * Return the printing width of the start (enter) sequence 40560786Sps * for a given character attribute. 40660786Sps */ 40760786Sps static int 40860786Spsattr_swidth(a) 40960786Sps int a; 41060786Sps{ 411161478Sdelphij int w = 0; 412161478Sdelphij 413161478Sdelphij a = apply_at_specials(a); 414161478Sdelphij 415161478Sdelphij if (a & AT_UNDERLINE) 416161478Sdelphij w += ul_s_width; 417161478Sdelphij if (a & AT_BOLD) 418161478Sdelphij w += bo_s_width; 419161478Sdelphij if (a & AT_BLINK) 420161478Sdelphij w += bl_s_width; 421161478Sdelphij if (a & AT_STANDOUT) 422161478Sdelphij w += so_s_width; 423161478Sdelphij 424161478Sdelphij return w; 42560786Sps} 42660786Sps 42760786Sps/* 42860786Sps * Return the printing width of the end (exit) sequence 42960786Sps * for a given character attribute. 43060786Sps */ 43160786Sps static int 43260786Spsattr_ewidth(a) 43360786Sps int a; 43460786Sps{ 435161478Sdelphij int w = 0; 436161478Sdelphij 437161478Sdelphij a = apply_at_specials(a); 438161478Sdelphij 439161478Sdelphij if (a & AT_UNDERLINE) 440161478Sdelphij w += ul_e_width; 441161478Sdelphij if (a & AT_BOLD) 442161478Sdelphij w += bo_e_width; 443161478Sdelphij if (a & AT_BLINK) 444161478Sdelphij w += bl_e_width; 445161478Sdelphij if (a & AT_STANDOUT) 446161478Sdelphij w += so_e_width; 447161478Sdelphij 448161478Sdelphij return w; 44960786Sps} 45060786Sps 45160786Sps/* 45260786Sps * Return the printing width of a given character and attribute, 45360786Sps * if the character were added to the current position in the line buffer. 45460786Sps * Adding a character with a given attribute may cause an enter or exit 45560786Sps * attribute sequence to be inserted, so this must be taken into account. 45660786Sps */ 45760786Sps static int 458161478Sdelphijpwidth(ch, a, prev_ch) 459161478Sdelphij LWCHAR ch; 46060786Sps int a; 461161478Sdelphij LWCHAR prev_ch; 46260786Sps{ 463161478Sdelphij int w; 46460786Sps 465161478Sdelphij if (ch == '\b') 46660786Sps /* 467161478Sdelphij * Backspace moves backwards one or two positions. 468161478Sdelphij * XXX - Incorrect if several '\b' in a row. 46960786Sps */ 470161478Sdelphij return (utf_mode && is_wide_char(prev_ch)) ? -2 : -1; 47160786Sps 472161478Sdelphij if (!utf_mode || is_ascii_char(ch)) 473161478Sdelphij { 474161478Sdelphij if (control_char((char)ch)) 475161478Sdelphij { 476161478Sdelphij /* 477161478Sdelphij * Control characters do unpredictable things, 478161478Sdelphij * so we don't even try to guess; say it doesn't move. 479161478Sdelphij * This can only happen if the -r flag is in effect. 480161478Sdelphij */ 481161478Sdelphij return (0); 482161478Sdelphij } 483161478Sdelphij } else 484161478Sdelphij { 485161478Sdelphij if (is_composing_char(ch) || is_combining_char(prev_ch, ch)) 486161478Sdelphij { 487161478Sdelphij /* 488161478Sdelphij * Composing and combining chars take up no space. 489161478Sdelphij * 490161478Sdelphij * Some terminals, upon failure to compose a 491161478Sdelphij * composing character with the character(s) that 492161478Sdelphij * precede(s) it will actually take up one column 493161478Sdelphij * for the composing character; there isn't much 494161478Sdelphij * we could do short of testing the (complex) 495161478Sdelphij * composition process ourselves and printing 496161478Sdelphij * a binary representation when it fails. 497161478Sdelphij */ 498161478Sdelphij return (0); 499161478Sdelphij } 500161478Sdelphij } 50160786Sps 50260786Sps /* 503161478Sdelphij * Other characters take one or two columns, 50460786Sps * plus the width of any attribute enter/exit sequence. 50560786Sps */ 50660786Sps w = 1; 507161478Sdelphij if (is_wide_char(ch)) 508161478Sdelphij w++; 509161478Sdelphij if (curr > 0 && !is_at_equiv(attr[curr-1], a)) 51060786Sps w += attr_ewidth(attr[curr-1]); 511161478Sdelphij if ((apply_at_specials(a) != AT_NORMAL) && 512161478Sdelphij (curr == 0 || !is_at_equiv(attr[curr-1], a))) 51360786Sps w += attr_swidth(a); 51460786Sps return (w); 51560786Sps} 51660786Sps 51760786Sps/* 518161478Sdelphij * Delete to the previous base character in the line buffer. 519161478Sdelphij * Return 1 if one is found. 52060786Sps */ 521161478Sdelphij static int 52260786Spsbackc() 52360786Sps{ 524161478Sdelphij LWCHAR prev_ch; 525161478Sdelphij char *p = linebuf + curr; 526161478Sdelphij LWCHAR ch = step_char(&p, -1, linebuf + lmargin); 527161478Sdelphij int width; 528161478Sdelphij 529161478Sdelphij /* This assumes that there is no '\b' in linebuf. */ 530161478Sdelphij while ( curr > lmargin 531161478Sdelphij && column > lmargin 532161478Sdelphij && (!(attr[curr - 1] & (AT_ANSI|AT_BINARY)))) 533161478Sdelphij { 534294286Sdelphij curr = (int) (p - linebuf); 535161478Sdelphij prev_ch = step_char(&p, -1, linebuf + lmargin); 536161478Sdelphij width = pwidth(ch, attr[curr], prev_ch); 537161478Sdelphij column -= width; 538161478Sdelphij if (width > 0) 539161478Sdelphij return 1; 540161478Sdelphij ch = prev_ch; 541161478Sdelphij } 542161478Sdelphij 543161478Sdelphij return 0; 54460786Sps} 54560786Sps 54660786Sps/* 54760786Sps * Are we currently within a recognized ANSI escape sequence? 54860786Sps */ 54960786Sps static int 55060786Spsin_ansi_esc_seq() 55160786Sps{ 552161478Sdelphij char *p; 55360786Sps 55460786Sps /* 55560786Sps * Search backwards for either an ESC (which means we ARE in a seq); 55660786Sps * or an end char (which means we're NOT in a seq). 55760786Sps */ 558161478Sdelphij for (p = &linebuf[curr]; p > linebuf; ) 55960786Sps { 560161478Sdelphij LWCHAR ch = step_char(&p, -1, linebuf); 561172471Sdelphij if (IS_CSI_START(ch)) 56260786Sps return (1); 563161478Sdelphij if (!is_ansi_middle(ch)) 56460786Sps return (0); 56560786Sps } 56660786Sps return (0); 56760786Sps} 56860786Sps 56960786Sps/* 57089019Sps * Is a character the end of an ANSI escape sequence? 57189019Sps */ 57289019Sps public int 573161478Sdelphijis_ansi_end(ch) 574161478Sdelphij LWCHAR ch; 57589019Sps{ 576161478Sdelphij if (!is_ascii_char(ch)) 577161478Sdelphij return (0); 578161478Sdelphij return (strchr(end_ansi_chars, (char) ch) != NULL); 57989019Sps} 58089019Sps 58189019Sps/* 582330571Sdelphij * Can a char appear in an ANSI escape sequence, before the end char? 583161478Sdelphij */ 584161478Sdelphij public int 585161478Sdelphijis_ansi_middle(ch) 586161478Sdelphij LWCHAR ch; 587161478Sdelphij{ 588161478Sdelphij if (!is_ascii_char(ch)) 589161478Sdelphij return (0); 590161478Sdelphij if (is_ansi_end(ch)) 591161478Sdelphij return (0); 592161478Sdelphij return (strchr(mid_ansi_chars, (char) ch) != NULL); 593161478Sdelphij} 594161478Sdelphij 595161478Sdelphij/* 596330571Sdelphij * Skip past an ANSI escape sequence. 597330571Sdelphij * pp is initially positioned just after the CSI_START char. 598330571Sdelphij */ 599330571Sdelphij public void 600330571Sdelphijskip_ansi(pp, limit) 601330571Sdelphij char **pp; 602330571Sdelphij constant char *limit; 603330571Sdelphij{ 604330571Sdelphij LWCHAR c; 605330571Sdelphij do { 606330571Sdelphij c = step_char(pp, +1, limit); 607330571Sdelphij } while (*pp < limit && is_ansi_middle(c)); 608330571Sdelphij /* Note that we discard final char, for which is_ansi_middle is false. */ 609330571Sdelphij} 610330571Sdelphij 611330571Sdelphij 612330571Sdelphij/* 61360786Sps * Append a character and attribute to the line buffer. 61460786Sps */ 615161478Sdelphij#define STORE_CHAR(ch,a,rep,pos) \ 616161478Sdelphij do { \ 617161478Sdelphij if (store_char((ch),(a),(rep),(pos))) return (1); \ 618161478Sdelphij } while (0) 61989019Sps 62060786Sps static int 621161478Sdelphijstore_char(ch, a, rep, pos) 622161478Sdelphij LWCHAR ch; 62360786Sps int a; 624161478Sdelphij char *rep; 62560786Sps POSITION pos; 62660786Sps{ 627161478Sdelphij int w; 628161478Sdelphij int replen; 629161478Sdelphij char cs; 63060786Sps 631161478Sdelphij w = (a & (AT_UNDERLINE|AT_BOLD)); /* Pre-use w. */ 632161478Sdelphij if (w != AT_NORMAL) 633161478Sdelphij last_overstrike = w; 634161478Sdelphij 63560786Sps#if HILITE_SEARCH 63689019Sps { 637161478Sdelphij int matches; 638161478Sdelphij if (is_hilited(pos, pos+1, 0, &matches)) 639161478Sdelphij { 640161478Sdelphij /* 641161478Sdelphij * This character should be highlighted. 642161478Sdelphij * Override the attribute passed in. 643161478Sdelphij */ 644161478Sdelphij if (a != AT_ANSI) 645237613Sdelphij { 646237613Sdelphij if (highest_hilite != NULL_POSITION && 647237613Sdelphij pos > highest_hilite) 648237613Sdelphij highest_hilite = pos; 649161478Sdelphij a |= AT_HILITE; 650237613Sdelphij } 651161478Sdelphij } 65289019Sps } 65360786Sps#endif 654161478Sdelphij 65560786Sps if (ctldisp == OPT_ONPLUS && in_ansi_esc_seq()) 656161478Sdelphij { 657161478Sdelphij if (!is_ansi_end(ch) && !is_ansi_middle(ch)) { 658161478Sdelphij /* Remove whole unrecognized sequence. */ 659191930Sdelphij char *p = &linebuf[curr]; 660191930Sdelphij LWCHAR bch; 661191930Sdelphij do { 662191930Sdelphij bch = step_char(&p, -1, linebuf); 663191930Sdelphij } while (p > linebuf && !IS_CSI_START(bch)); 664294286Sdelphij curr = (int) (p - linebuf); 665161478Sdelphij return 0; 666161478Sdelphij } 667161478Sdelphij a = AT_ANSI; /* Will force re-AT_'ing around it. */ 66860786Sps w = 0; 669161478Sdelphij } 670172471Sdelphij else if (ctldisp == OPT_ONPLUS && IS_CSI_START(ch)) 671161478Sdelphij { 672161478Sdelphij a = AT_ANSI; /* Will force re-AT_'ing around it. */ 673161478Sdelphij w = 0; 674161478Sdelphij } 67560786Sps else 676161478Sdelphij { 677161478Sdelphij char *p = &linebuf[curr]; 678161478Sdelphij LWCHAR prev_ch = step_char(&p, -1, linebuf); 679161478Sdelphij w = pwidth(ch, a, prev_ch); 680161478Sdelphij } 681161478Sdelphij 68260786Sps if (ctldisp != OPT_ON && column + w + attr_ewidth(a) > sc_width) 68360786Sps /* 68460786Sps * Won't fit on screen. 68560786Sps */ 68660786Sps return (1); 68760786Sps 688161478Sdelphij if (rep == NULL) 68989019Sps { 690161478Sdelphij cs = (char) ch; 691161478Sdelphij rep = &cs; 692161478Sdelphij replen = 1; 693161478Sdelphij } else 694161478Sdelphij { 695161478Sdelphij replen = utf_len(rep[0]); 696161478Sdelphij } 697161478Sdelphij if (curr + replen >= size_linebuf-6) 698161478Sdelphij { 69960786Sps /* 70060786Sps * Won't fit in line buffer. 70189019Sps * Try to expand it. 70260786Sps */ 70389019Sps if (expand_linebuf()) 70489019Sps return (1); 70589019Sps } 70660786Sps 707330571Sdelphij if (column > right_column && w > 0) 708330571Sdelphij { 709330571Sdelphij right_column = column; 710330571Sdelphij right_curr = curr; 711330571Sdelphij } 712330571Sdelphij 713161478Sdelphij while (replen-- > 0) 71460786Sps { 715330571Sdelphij add_linebuf(*rep++, a, 0); 71660786Sps } 71760786Sps column += w; 71860786Sps return (0); 71960786Sps} 72060786Sps 72160786Sps/* 72289019Sps * Append a tab to the line buffer. 72389019Sps * Store spaces to represent the tab. 72489019Sps */ 72589019Sps#define STORE_TAB(a,pos) \ 72689019Sps do { if (store_tab((a),(pos))) return (1); } while (0) 72789019Sps 72889019Sps static int 72989019Spsstore_tab(attr, pos) 73089019Sps int attr; 73189019Sps POSITION pos; 73289019Sps{ 73389019Sps int to_tab = column + cshift - lmargin; 73489019Sps int i; 73589019Sps 73689019Sps if (ntabstops < 2 || to_tab >= tabstops[ntabstops-1]) 73789019Sps to_tab = tabdefault - 73889019Sps ((to_tab - tabstops[ntabstops-1]) % tabdefault); 73989019Sps else 74089019Sps { 74189019Sps for (i = ntabstops - 2; i >= 0; i--) 74289019Sps if (to_tab >= tabstops[i]) 74389019Sps break; 74489019Sps to_tab = tabstops[i+1] - to_tab; 74589019Sps } 74689019Sps 747161478Sdelphij if (column + to_tab - 1 + pwidth(' ', attr, 0) + attr_ewidth(attr) > sc_width) 748161478Sdelphij return 1; 749161478Sdelphij 75089019Sps do { 751161478Sdelphij STORE_CHAR(' ', attr, " ", pos); 75289019Sps } while (--to_tab > 0); 75389019Sps return 0; 75489019Sps} 75589019Sps 756161478Sdelphij#define STORE_PRCHAR(c, pos) \ 757161478Sdelphij do { if (store_prchar((c), (pos))) return 1; } while (0) 758161478Sdelphij 759161478Sdelphij static int 760161478Sdelphijstore_prchar(c, pos) 761294286Sdelphij LWCHAR c; 762161478Sdelphij POSITION pos; 763161478Sdelphij{ 764161478Sdelphij char *s; 765161478Sdelphij 766161478Sdelphij /* 767161478Sdelphij * Convert to printable representation. 768161478Sdelphij */ 769161478Sdelphij s = prchar(c); 770161478Sdelphij 771161478Sdelphij /* 772161478Sdelphij * Make sure we can get the entire representation 773161478Sdelphij * of the character on this line. 774161478Sdelphij */ 775161478Sdelphij if (column + (int) strlen(s) - 1 + 776161478Sdelphij pwidth(' ', binattr, 0) + attr_ewidth(binattr) > sc_width) 777161478Sdelphij return 1; 778161478Sdelphij 779161478Sdelphij for ( ; *s != 0; s++) 780161478Sdelphij STORE_CHAR(*s, AT_BINARY, NULL, pos); 781161478Sdelphij 782161478Sdelphij return 0; 783161478Sdelphij} 784161478Sdelphij 785161478Sdelphij static int 786161478Sdelphijflush_mbc_buf(pos) 787161478Sdelphij POSITION pos; 788161478Sdelphij{ 789161478Sdelphij int i; 790161478Sdelphij 791161478Sdelphij for (i = 0; i < mbc_buf_index; i++) 792161478Sdelphij if (store_prchar(mbc_buf[i], pos)) 793161478Sdelphij return mbc_buf_index - i; 794161478Sdelphij 795161478Sdelphij return 0; 796161478Sdelphij} 797161478Sdelphij 79889019Sps/* 79960786Sps * Append a character to the line buffer. 80060786Sps * Expand tabs into spaces, handle underlining, boldfacing, etc. 80160786Sps * Returns 0 if ok, 1 if couldn't fit in buffer. 80260786Sps */ 80360786Sps public int 80460786Spspappend(c, pos) 805294286Sdelphij unsigned char c; 80660786Sps POSITION pos; 80760786Sps{ 80860786Sps int r; 80960786Sps 81060786Sps if (pendc) 81160786Sps { 812294286Sdelphij if (c == '\r' && pendc == '\r') 813294286Sdelphij return (0); 814161478Sdelphij if (do_append(pendc, NULL, pendpos)) 81560786Sps /* 81660786Sps * Oops. We've probably lost the char which 81760786Sps * was in pendc, since caller won't back up. 81860786Sps */ 81960786Sps return (1); 82060786Sps pendc = '\0'; 82160786Sps } 82260786Sps 82360786Sps if (c == '\r' && bs_mode == BS_SPECIAL) 82460786Sps { 825161478Sdelphij if (mbc_buf_len > 0) /* utf_mode must be on. */ 826161478Sdelphij { 827161478Sdelphij /* Flush incomplete (truncated) sequence. */ 828161478Sdelphij r = flush_mbc_buf(mbc_pos); 829161478Sdelphij mbc_buf_index = r + 1; 830161478Sdelphij mbc_buf_len = 0; 831161478Sdelphij if (r) 832161478Sdelphij return (mbc_buf_index); 833161478Sdelphij } 834161478Sdelphij 83560786Sps /* 83660786Sps * Don't put the CR into the buffer until we see 83760786Sps * the next char. If the next char is a newline, 83860786Sps * discard the CR. 83960786Sps */ 84060786Sps pendc = c; 84160786Sps pendpos = pos; 84260786Sps return (0); 84360786Sps } 84460786Sps 845161478Sdelphij if (!utf_mode) 846161478Sdelphij { 847294286Sdelphij r = do_append(c, NULL, pos); 848161478Sdelphij } else 849161478Sdelphij { 850161478Sdelphij /* Perform strict validation in all possible cases. */ 851161478Sdelphij if (mbc_buf_len == 0) 852161478Sdelphij { 853161478Sdelphij retry: 854161478Sdelphij mbc_buf_index = 1; 855161478Sdelphij *mbc_buf = c; 856161478Sdelphij if (IS_ASCII_OCTET(c)) 857294286Sdelphij r = do_append(c, NULL, pos); 858161478Sdelphij else if (IS_UTF8_LEAD(c)) 859161478Sdelphij { 860161478Sdelphij mbc_buf_len = utf_len(c); 861161478Sdelphij mbc_pos = pos; 862161478Sdelphij return (0); 863161478Sdelphij } else 864161478Sdelphij /* UTF8_INVALID or stray UTF8_TRAIL */ 865161478Sdelphij r = flush_mbc_buf(pos); 866161478Sdelphij } else if (IS_UTF8_TRAIL(c)) 867161478Sdelphij { 868161478Sdelphij mbc_buf[mbc_buf_index++] = c; 869161478Sdelphij if (mbc_buf_index < mbc_buf_len) 870161478Sdelphij return (0); 871294286Sdelphij if (is_utf8_well_formed(mbc_buf, mbc_buf_index)) 872161478Sdelphij r = do_append(get_wchar(mbc_buf), mbc_buf, mbc_pos); 873161478Sdelphij else 874161478Sdelphij /* Complete, but not shortest form, sequence. */ 875161478Sdelphij mbc_buf_index = r = flush_mbc_buf(mbc_pos); 876161478Sdelphij mbc_buf_len = 0; 877161478Sdelphij } else 878161478Sdelphij { 879161478Sdelphij /* Flush incomplete (truncated) sequence. */ 880161478Sdelphij r = flush_mbc_buf(mbc_pos); 881161478Sdelphij mbc_buf_index = r + 1; 882161478Sdelphij mbc_buf_len = 0; 883161478Sdelphij /* Handle new char. */ 884161478Sdelphij if (!r) 885161478Sdelphij goto retry; 886161478Sdelphij } 887161478Sdelphij } 888161478Sdelphij 88960786Sps /* 89060786Sps * If we need to shift the line, do it. 89160786Sps * But wait until we get to at least the middle of the screen, 89260786Sps * so shifting it doesn't affect the chars we're currently 89360786Sps * pappending. (Bold & underline can get messed up otherwise.) 89460786Sps */ 89560786Sps if (cshift < hshift && column > sc_width / 2) 89689019Sps { 89789019Sps linebuf[curr] = '\0'; 89860786Sps pshift(hshift - cshift); 89989019Sps } 900161478Sdelphij if (r) 901161478Sdelphij { 902161478Sdelphij /* How many chars should caller back up? */ 903161478Sdelphij r = (!utf_mode) ? 1 : mbc_buf_index; 904161478Sdelphij } 90560786Sps return (r); 90660786Sps} 90760786Sps 90860786Sps static int 909161478Sdelphijdo_append(ch, rep, pos) 910161478Sdelphij LWCHAR ch; 911161478Sdelphij char *rep; 91260786Sps POSITION pos; 91360786Sps{ 914330571Sdelphij int a; 915161478Sdelphij LWCHAR prev_ch; 91660786Sps 917161478Sdelphij a = AT_NORMAL; 91860786Sps 919161478Sdelphij if (ch == '\b') 92060786Sps { 921161478Sdelphij if (bs_mode == BS_CONTROL) 92260786Sps goto do_control_char; 923161478Sdelphij 924161478Sdelphij /* 925161478Sdelphij * A better test is needed here so we don't 926161478Sdelphij * backspace over part of the printed 927161478Sdelphij * representation of a binary character. 928161478Sdelphij */ 929161478Sdelphij if ( curr <= lmargin 930161478Sdelphij || column <= lmargin 931161478Sdelphij || (attr[curr - 1] & (AT_ANSI|AT_BINARY))) 932161478Sdelphij STORE_PRCHAR('\b', pos); 933161478Sdelphij else if (bs_mode == BS_NORMAL) 934161478Sdelphij STORE_CHAR(ch, AT_NORMAL, NULL, pos); 935161478Sdelphij else if (bs_mode == BS_SPECIAL) 936161478Sdelphij overstrike = backc(); 937161478Sdelphij 938161478Sdelphij return 0; 939161478Sdelphij } 940161478Sdelphij 941161478Sdelphij if (overstrike > 0) 94260786Sps { 94360786Sps /* 94460786Sps * Overstrike the character at the current position 94560786Sps * in the line buffer. This will cause either 94660786Sps * underline (if a "_" is overstruck), 94760786Sps * bold (if an identical character is overstruck), 94860786Sps * or just deletion of the character in the buffer. 94960786Sps */ 950161478Sdelphij overstrike = utf_mode ? -1 : 0; 951294286Sdelphij if (utf_mode) 952294286Sdelphij { 953294286Sdelphij /* To be correct, this must be a base character. */ 954294286Sdelphij prev_ch = get_wchar(linebuf + curr); 955294286Sdelphij } else 956294286Sdelphij { 957294286Sdelphij prev_ch = (unsigned char) linebuf[curr]; 958294286Sdelphij } 959161478Sdelphij a = attr[curr]; 960161478Sdelphij if (ch == prev_ch) 96189019Sps { 962128345Stjr /* 963128345Stjr * Overstriking a char with itself means make it bold. 964128345Stjr * But overstriking an underscore with itself is 965128345Stjr * ambiguous. It could mean make it bold, or 966128345Stjr * it could mean make it underlined. 967128345Stjr * Use the previous overstrike to resolve it. 968128345Stjr */ 969161478Sdelphij if (ch == '_') 97089019Sps { 971161478Sdelphij if ((a & (AT_BOLD|AT_UNDERLINE)) != AT_NORMAL) 972161478Sdelphij a |= (AT_BOLD|AT_UNDERLINE); 973161478Sdelphij else if (last_overstrike != AT_NORMAL) 974161478Sdelphij a |= last_overstrike; 975161478Sdelphij else 976161478Sdelphij a |= AT_BOLD; 977161478Sdelphij } else 978161478Sdelphij a |= AT_BOLD; 979161478Sdelphij } else if (ch == '_') 98089019Sps { 981161478Sdelphij a |= AT_UNDERLINE; 982161478Sdelphij ch = prev_ch; 983161478Sdelphij rep = linebuf + curr; 984161478Sdelphij } else if (prev_ch == '_') 985161478Sdelphij { 986161478Sdelphij a |= AT_UNDERLINE; 987161478Sdelphij } 988161478Sdelphij /* Else we replace prev_ch, but we keep its attributes. */ 989161478Sdelphij } else if (overstrike < 0) 990161478Sdelphij { 991161478Sdelphij if ( is_composing_char(ch) 992161478Sdelphij || is_combining_char(get_wchar(linebuf + curr), ch)) 993161478Sdelphij /* Continuation of the same overstrike. */ 994161478Sdelphij a = last_overstrike; 99560786Sps else 996161478Sdelphij overstrike = 0; 997161478Sdelphij } 998161478Sdelphij 999161478Sdelphij if (ch == '\t') 100060786Sps { 100160786Sps /* 100260786Sps * Expand a tab into spaces. 100360786Sps */ 100460786Sps switch (bs_mode) 100560786Sps { 100660786Sps case BS_CONTROL: 100760786Sps goto do_control_char; 100860786Sps case BS_NORMAL: 100960786Sps case BS_SPECIAL: 1010161478Sdelphij STORE_TAB(a, pos); 101160786Sps break; 101260786Sps } 1013161478Sdelphij } else if ((!utf_mode || is_ascii_char(ch)) && control_char((char)ch)) 101460786Sps { 101560786Sps do_control_char: 1016172471Sdelphij if (ctldisp == OPT_ON || (ctldisp == OPT_ONPLUS && IS_CSI_START(ch))) 101760786Sps { 101860786Sps /* 101960786Sps * Output as a normal character. 102060786Sps */ 1021161478Sdelphij STORE_CHAR(ch, AT_NORMAL, rep, pos); 102260786Sps } else 102360786Sps { 1024161478Sdelphij STORE_PRCHAR((char) ch, pos); 1025161478Sdelphij } 1026161478Sdelphij } else if (utf_mode && ctldisp != OPT_ON && is_ubin_char(ch)) 1027161478Sdelphij { 1028161478Sdelphij char *s; 102960786Sps 1030161478Sdelphij s = prutfchar(ch); 103160786Sps 1032161478Sdelphij if (column + (int) strlen(s) - 1 + 1033161478Sdelphij pwidth(' ', binattr, 0) + attr_ewidth(binattr) > sc_width) 1034161478Sdelphij return (1); 1035161478Sdelphij 1036161478Sdelphij for ( ; *s != 0; s++) 1037161478Sdelphij STORE_CHAR(*s, AT_BINARY, NULL, pos); 1038161478Sdelphij } else 103960786Sps { 1040161478Sdelphij STORE_CHAR(ch, a, rep, pos); 104160786Sps } 1042161478Sdelphij return (0); 1043161478Sdelphij} 104460786Sps 1045161478Sdelphij/* 1046161478Sdelphij * 1047161478Sdelphij */ 1048161478Sdelphij public int 1049161478Sdelphijpflushmbc() 1050161478Sdelphij{ 1051161478Sdelphij int r = 0; 1052161478Sdelphij 1053161478Sdelphij if (mbc_buf_len > 0) 1054161478Sdelphij { 1055161478Sdelphij /* Flush incomplete (truncated) sequence. */ 1056161478Sdelphij r = flush_mbc_buf(mbc_pos); 1057161478Sdelphij mbc_buf_len = 0; 1058161478Sdelphij } 1059161478Sdelphij return r; 106060786Sps} 106160786Sps 106260786Sps/* 1063330571Sdelphij * Switch to normal attribute at end of line. 1064330571Sdelphij */ 1065330571Sdelphij static void 1066330571Sdelphijadd_attr_normal() 1067330571Sdelphij{ 1068330571Sdelphij char *p = "\033[m"; 1069330571Sdelphij 1070330571Sdelphij if (ctldisp != OPT_ONPLUS || !is_ansi_end('m')) 1071330571Sdelphij return; 1072330571Sdelphij for ( ; *p != '\0'; p++) 1073330571Sdelphij add_linebuf(*p, AT_ANSI, 0); 1074330571Sdelphij} 1075330571Sdelphij 1076330571Sdelphij/* 107760786Sps * Terminate the line in the line buffer. 107860786Sps */ 107960786Sps public void 1080330571Sdelphijpdone(endline, chopped, forw) 108160786Sps int endline; 1082330571Sdelphij int chopped; 1083195941Sdelphij int forw; 108460786Sps{ 1085161478Sdelphij (void) pflushmbc(); 1086161478Sdelphij 108760786Sps if (pendc && (pendc != '\r' || !endline)) 108860786Sps /* 108960786Sps * If we had a pending character, put it in the buffer. 109060786Sps * But discard a pending CR if we are at end of line 109160786Sps * (that is, discard the CR in a CR/LF sequence). 109260786Sps */ 1093161478Sdelphij (void) do_append(pendc, NULL, pendpos); 109460786Sps 109560786Sps /* 109660786Sps * Make sure we've shifted the line, if we need to. 109760786Sps */ 109860786Sps if (cshift < hshift) 109960786Sps pshift(hshift - cshift); 110060786Sps 1101330571Sdelphij if (chopped && rscroll_char) 1102161478Sdelphij { 1103330571Sdelphij /* 1104330571Sdelphij * Display the right scrolling char. 1105330571Sdelphij * If we've already filled the rightmost screen char 1106330571Sdelphij * (in the buffer), overwrite it. 1107330571Sdelphij */ 1108330571Sdelphij if (column >= sc_width) 1109161478Sdelphij { 1110330571Sdelphij /* We've already written in the rightmost char. */ 1111330571Sdelphij column = right_column; 1112330571Sdelphij curr = right_curr; 1113161478Sdelphij } 1114330571Sdelphij add_attr_normal(); 1115330571Sdelphij while (column < sc_width-1) 1116330571Sdelphij { 1117330571Sdelphij /* 1118330571Sdelphij * Space to last (rightmost) char on screen. 1119330571Sdelphij * This may be necessary if the char we overwrote 1120330571Sdelphij * was double-width. 1121330571Sdelphij */ 1122330571Sdelphij add_linebuf(' ', AT_NORMAL, 1); 1123330571Sdelphij } 1124330571Sdelphij /* Print rscroll char. It must be single-width. */ 1125330571Sdelphij add_linebuf(rscroll_char, rscroll_attr, 1); 1126330571Sdelphij } else 1127330571Sdelphij { 1128330571Sdelphij add_attr_normal(); 1129161478Sdelphij } 1130161478Sdelphij 113160786Sps /* 113260786Sps * Add a newline if necessary, 113360786Sps * and append a '\0' to the end of the line. 1134170259Sdelphij * We output a newline if we're not at the right edge of the screen, 1135170259Sdelphij * or if the terminal doesn't auto wrap, 1136170259Sdelphij * or if this is really the end of the line AND the terminal ignores 1137170259Sdelphij * a newline at the right edge. 1138170259Sdelphij * (In the last case we don't want to output a newline if the terminal 1139170259Sdelphij * doesn't ignore it since that would produce an extra blank line. 1140170259Sdelphij * But we do want to output a newline if the terminal ignores it in case 1141170259Sdelphij * the next line is blank. In that case the single newline output for 1142170259Sdelphij * that blank line would be ignored!) 114360786Sps */ 1144191930Sdelphij if (column < sc_width || !auto_wrap || (endline && ignaw) || ctldisp == OPT_ON) 114560786Sps { 1146330571Sdelphij add_linebuf('\n', AT_NORMAL, 0); 1147173685Sdelphij } 1148195941Sdelphij else if (ignaw && column >= sc_width && forw) 1149173685Sdelphij { 1150173685Sdelphij /* 1151191930Sdelphij * Terminals with "ignaw" don't wrap until they *really* need 1152191930Sdelphij * to, i.e. when the character *after* the last one to fit on a 1153191930Sdelphij * line is output. But they are too hard to deal with when they 1154191930Sdelphij * get in the state where a full screen width of characters 1155191930Sdelphij * have been output but the cursor is sitting on the right edge 1156191930Sdelphij * instead of at the start of the next line. 1157195941Sdelphij * So we nudge them into wrapping by outputting a space 1158195941Sdelphij * character plus a backspace. But do this only if moving 1159195941Sdelphij * forward; if we're moving backward and drawing this line at 1160195941Sdelphij * the top of the screen, the space would overwrite the first 1161195941Sdelphij * char on the next line. We don't need to do this "nudge" 1162195941Sdelphij * at the top of the screen anyway. 1163173685Sdelphij */ 1164330571Sdelphij add_linebuf(' ', AT_NORMAL, 1); 1165330571Sdelphij add_linebuf('\b', AT_NORMAL, -1); 116660786Sps } 1167330571Sdelphij set_linebuf(curr, '\0', AT_NORMAL); 1168191930Sdelphij} 116989019Sps 1170191930Sdelphij/* 1171191930Sdelphij * 1172191930Sdelphij */ 1173191930Sdelphij public void 1174191930Sdelphijset_status_col(c) 1175191930Sdelphij char c; 1176191930Sdelphij{ 1177330571Sdelphij set_linebuf(0, c, AT_NORMAL|AT_HILITE); 117860786Sps} 117960786Sps 118060786Sps/* 118160786Sps * Get a character from the current line. 118260786Sps * Return the character as the function return value, 118360786Sps * and the character attribute in *ap. 118460786Sps */ 118560786Sps public int 118660786Spsgline(i, ap) 1187330571Sdelphij int i; 1188330571Sdelphij int *ap; 118960786Sps{ 119060786Sps if (is_null_line) 119160786Sps { 119260786Sps /* 119360786Sps * If there is no current line, we pretend the line is 119460786Sps * either "~" or "", depending on the "twiddle" flag. 119560786Sps */ 1196161478Sdelphij if (twiddle) 1197161478Sdelphij { 1198161478Sdelphij if (i == 0) 1199161478Sdelphij { 1200161478Sdelphij *ap = AT_BOLD; 1201161478Sdelphij return '~'; 1202161478Sdelphij } 1203161478Sdelphij --i; 1204161478Sdelphij } 1205161478Sdelphij /* Make sure we're back to AT_NORMAL before the '\n'. */ 1206161478Sdelphij *ap = AT_NORMAL; 1207161478Sdelphij return i ? '\0' : '\n'; 120860786Sps } 120960786Sps 121060786Sps *ap = attr[i]; 1211161478Sdelphij return (linebuf[i] & 0xFF); 121260786Sps} 121360786Sps 121460786Sps/* 121560786Sps * Indicate that there is no current line. 121660786Sps */ 121760786Sps public void 121860786Spsnull_line() 121960786Sps{ 122060786Sps is_null_line = 1; 122160786Sps cshift = 0; 122260786Sps} 122360786Sps 122460786Sps/* 122560786Sps * Analogous to forw_line(), but deals with "raw lines": 122660786Sps * lines which are not split for screen width. 122760786Sps * {{ This is supposed to be more efficient than forw_line(). }} 122860786Sps */ 122960786Sps public POSITION 1230170259Sdelphijforw_raw_line(curr_pos, linep, line_lenp) 123160786Sps POSITION curr_pos; 123260786Sps char **linep; 1233170259Sdelphij int *line_lenp; 123460786Sps{ 1235330571Sdelphij int n; 1236330571Sdelphij int c; 123760786Sps POSITION new_pos; 123860786Sps 123960786Sps if (curr_pos == NULL_POSITION || ch_seek(curr_pos) || 124060786Sps (c = ch_forw_get()) == EOI) 124160786Sps return (NULL_POSITION); 124260786Sps 124389019Sps n = 0; 124460786Sps for (;;) 124560786Sps { 1246161478Sdelphij if (c == '\n' || c == EOI || ABORT_SIGS()) 124760786Sps { 124860786Sps new_pos = ch_tell(); 124960786Sps break; 125060786Sps } 125189019Sps if (n >= size_linebuf-1) 125260786Sps { 125389019Sps if (expand_linebuf()) 125489019Sps { 125589019Sps /* 125689019Sps * Overflowed the input buffer. 125789019Sps * Pretend the line ended here. 125889019Sps */ 125989019Sps new_pos = ch_tell() - 1; 126089019Sps break; 126189019Sps } 126260786Sps } 126389019Sps linebuf[n++] = c; 126460786Sps c = ch_forw_get(); 126560786Sps } 126689019Sps linebuf[n] = '\0'; 126760786Sps if (linep != NULL) 126860786Sps *linep = linebuf; 1269170259Sdelphij if (line_lenp != NULL) 1270170259Sdelphij *line_lenp = n; 127160786Sps return (new_pos); 127260786Sps} 127360786Sps 127460786Sps/* 127560786Sps * Analogous to back_line(), but deals with "raw lines". 127660786Sps * {{ This is supposed to be more efficient than back_line(). }} 127760786Sps */ 127860786Sps public POSITION 1279170259Sdelphijback_raw_line(curr_pos, linep, line_lenp) 128060786Sps POSITION curr_pos; 128160786Sps char **linep; 1282170259Sdelphij int *line_lenp; 128360786Sps{ 1284330571Sdelphij int n; 1285330571Sdelphij int c; 128660786Sps POSITION new_pos; 128760786Sps 128860786Sps if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() || 128960786Sps ch_seek(curr_pos-1)) 129060786Sps return (NULL_POSITION); 129160786Sps 129289019Sps n = size_linebuf; 129389019Sps linebuf[--n] = '\0'; 129460786Sps for (;;) 129560786Sps { 129660786Sps c = ch_back_get(); 1297161478Sdelphij if (c == '\n' || ABORT_SIGS()) 129860786Sps { 129960786Sps /* 130060786Sps * This is the newline ending the previous line. 130160786Sps * We have hit the beginning of the line. 130260786Sps */ 130360786Sps new_pos = ch_tell() + 1; 130460786Sps break; 130560786Sps } 130660786Sps if (c == EOI) 130760786Sps { 130860786Sps /* 130960786Sps * We have hit the beginning of the file. 131060786Sps * This must be the first line in the file. 131160786Sps * This must, of course, be the beginning of the line. 131260786Sps */ 131360786Sps new_pos = ch_zero(); 131460786Sps break; 131560786Sps } 131689019Sps if (n <= 0) 131760786Sps { 131889019Sps int old_size_linebuf = size_linebuf; 131989019Sps char *fm; 132089019Sps char *to; 132189019Sps if (expand_linebuf()) 132289019Sps { 132389019Sps /* 132489019Sps * Overflowed the input buffer. 132589019Sps * Pretend the line ended here. 132689019Sps */ 132789019Sps new_pos = ch_tell() + 1; 132889019Sps break; 132989019Sps } 133060786Sps /* 133189019Sps * Shift the data to the end of the new linebuf. 133260786Sps */ 1333149487Stjr for (fm = linebuf + old_size_linebuf - 1, 1334149487Stjr to = linebuf + size_linebuf - 1; 133589019Sps fm >= linebuf; fm--, to--) 133689019Sps *to = *fm; 133789019Sps n = size_linebuf - old_size_linebuf; 133860786Sps } 133989019Sps linebuf[--n] = c; 134060786Sps } 134160786Sps if (linep != NULL) 134289019Sps *linep = &linebuf[n]; 1343170259Sdelphij if (line_lenp != NULL) 1344170259Sdelphij *line_lenp = size_linebuf - 1 - n; 134560786Sps return (new_pos); 134660786Sps} 1347330571Sdelphij 1348330571Sdelphij/* 1349330571Sdelphij * Find the shift necessary to show the end of the longest displayed line. 1350330571Sdelphij */ 1351330571Sdelphij public int 1352330571Sdelphijrrshift() 1353330571Sdelphij{ 1354330571Sdelphij POSITION pos; 1355330571Sdelphij int save_width; 1356330571Sdelphij int line; 1357330571Sdelphij int longest = 0; 1358330571Sdelphij 1359330571Sdelphij save_width = sc_width; 1360330571Sdelphij sc_width = INT_MAX; 1361330571Sdelphij hshift = 0; 1362330571Sdelphij pos = position(TOP); 1363330571Sdelphij for (line = 0; line < sc_height && pos != NULL_POSITION; line++) 1364330571Sdelphij { 1365330571Sdelphij pos = forw_line(pos); 1366330571Sdelphij if (column > longest) 1367330571Sdelphij longest = column; 1368330571Sdelphij } 1369330571Sdelphij sc_width = save_width; 1370330571Sdelphij if (longest < sc_width) 1371330571Sdelphij return 0; 1372330571Sdelphij return longest - sc_width; 1373330571Sdelphij} 1374