1149487Stjr/* $FreeBSD$ */ 260786Sps/* 3240121Sdelphij * Copyright (C) 1984-2012 Mark Nudelman 460786Sps * 560786Sps * You may distribute under the terms of either the GNU General Public 660786Sps * License or the Less License, as specified in the README file. 760786Sps * 8240121Sdelphij * For more information, see the README file. 960786Sps */ 1060786Sps 1160786Sps 1260786Sps/* 1360786Sps * Routines to manipulate the "line buffer". 1460786Sps * The line buffer holds a line of output as it is being built 1560786Sps * in preparation for output to the screen. 1660786Sps */ 1760786Sps 1860786Sps#include "less.h" 19161478Sdelphij#include "charset.h" 2060786Sps 21161478Sdelphijstatic char *linebuf = NULL; /* Buffer which holds the current output line */ 2289019Spsstatic char *attr = NULL; /* Extension of linebuf to hold attributes */ 2389019Spspublic int size_linebuf = 0; /* Size of line buffer (and attr buffer) */ 2460786Sps 25161478Sdelphijstatic int cshift; /* Current left-shift of output line buffer */ 2660786Spspublic int hshift; /* Desired left-shift of output line buffer */ 2789019Spspublic int tabstops[TABSTOP_MAX] = { 0 }; /* Custom tabstops */ 2889019Spspublic int ntabstops = 1; /* Number of tabstops */ 2989019Spspublic int tabdefault = 8; /* Default repeated tabstops */ 30240121Sdelphijpublic POSITION highest_hilite; /* Pos of last hilite in file found so far */ 3160786Sps 3260786Spsstatic int curr; /* Index into linebuf */ 3360786Spsstatic int column; /* Printable length, accounting for 3460786Sps backspaces, etc. */ 3560786Spsstatic int overstrike; /* Next char should overstrike previous char */ 36128345Stjrstatic int last_overstrike = AT_NORMAL; 3760786Spsstatic int is_null_line; /* There is no current line */ 3863128Spsstatic int lmargin; /* Left margin */ 3960786Spsstatic char pendc; 4060786Spsstatic POSITION pendpos; 4160786Spsstatic char *end_ansi_chars; 42161478Sdelphijstatic char *mid_ansi_chars; 4360786Sps 44161478Sdelphijstatic int attr_swidth(); 45161478Sdelphijstatic int attr_ewidth(); 4660786Spsstatic int do_append(); 4760786Sps 48161478Sdelphijextern int sigs; 4960786Spsextern int bs_mode; 5060786Spsextern int linenums; 5160786Spsextern int ctldisp; 5260786Spsextern int twiddle; 5360786Spsextern int binattr; 5463128Spsextern int status_col; 5560786Spsextern int auto_wrap, ignaw; 5660786Spsextern int bo_s_width, bo_e_width; 5760786Spsextern int ul_s_width, ul_e_width; 5860786Spsextern int bl_s_width, bl_e_width; 5960786Spsextern int so_s_width, so_e_width; 6060786Spsextern int sc_width, sc_height; 6160786Spsextern int utf_mode; 6263128Spsextern POSITION start_attnpos; 6363128Spsextern POSITION end_attnpos; 6460786Sps 65161478Sdelphijstatic char mbc_buf[MAX_UTF_CHAR_LEN]; 66161478Sdelphijstatic int mbc_buf_len = 0; 67161478Sdelphijstatic int mbc_buf_index = 0; 68161478Sdelphijstatic POSITION mbc_pos; 69161478Sdelphij 7060786Sps/* 7160786Sps * Initialize from environment variables. 7260786Sps */ 7360786Sps public void 7460786Spsinit_line() 7560786Sps{ 7660786Sps end_ansi_chars = lgetenv("LESSANSIENDCHARS"); 7760786Sps if (end_ansi_chars == NULL || *end_ansi_chars == '\0') 7860786Sps end_ansi_chars = "m"; 79161478Sdelphij 80161478Sdelphij mid_ansi_chars = lgetenv("LESSANSIMIDCHARS"); 81161478Sdelphij if (mid_ansi_chars == NULL || *mid_ansi_chars == '\0') 82161478Sdelphij mid_ansi_chars = "0123456789;[?!\"'#%()*+ "; 83161478Sdelphij 8489019Sps linebuf = (char *) ecalloc(LINEBUF_SIZE, sizeof(char)); 8589019Sps attr = (char *) ecalloc(LINEBUF_SIZE, sizeof(char)); 8689019Sps size_linebuf = LINEBUF_SIZE; 8760786Sps} 8860786Sps 8960786Sps/* 9089019Sps * Expand the line buffer. 9189019Sps */ 92161478Sdelphij static int 9389019Spsexpand_linebuf() 9489019Sps{ 95161478Sdelphij /* Double the size of the line buffer. */ 96161478Sdelphij int new_size = size_linebuf * 2; 97161478Sdelphij 98161478Sdelphij /* Just realloc to expand the buffer, if we can. */ 99161478Sdelphij#if HAVE_REALLOC 100161478Sdelphij char *new_buf = (char *) realloc(linebuf, new_size); 101161478Sdelphij char *new_attr = (char *) realloc(attr, new_size); 102161478Sdelphij#else 10389019Sps char *new_buf = (char *) calloc(new_size, sizeof(char)); 10489019Sps char *new_attr = (char *) calloc(new_size, sizeof(char)); 105161478Sdelphij#endif 10689019Sps if (new_buf == NULL || new_attr == NULL) 10789019Sps { 10889019Sps if (new_attr != NULL) 10989019Sps free(new_attr); 11089019Sps if (new_buf != NULL) 11189019Sps free(new_buf); 11289019Sps return 1; 11389019Sps } 114161478Sdelphij#if HAVE_REALLOC 115161478Sdelphij /* 116161478Sdelphij * We realloc'd the buffers; they already have the old contents. 117161478Sdelphij */ 118161478Sdelphij #if 0 119161478Sdelphij memset(new_buf + size_linebuf, 0, new_size - size_linebuf); 120161478Sdelphij memset(new_attr + size_linebuf, 0, new_size - size_linebuf); 121161478Sdelphij #endif 122161478Sdelphij#else 123161478Sdelphij /* 124161478Sdelphij * We just calloc'd the buffers; copy the old contents. 125161478Sdelphij */ 12689019Sps memcpy(new_buf, linebuf, size_linebuf * sizeof(char)); 12789019Sps memcpy(new_attr, attr, size_linebuf * sizeof(char)); 128128345Stjr free(attr); 129128345Stjr free(linebuf); 130161478Sdelphij#endif 13189019Sps linebuf = new_buf; 13289019Sps attr = new_attr; 13389019Sps size_linebuf = new_size; 13489019Sps return 0; 13589019Sps} 13689019Sps 13789019Sps/* 138161478Sdelphij * Is a character ASCII? 139161478Sdelphij */ 140161478Sdelphij public int 141161478Sdelphijis_ascii_char(ch) 142161478Sdelphij LWCHAR ch; 143161478Sdelphij{ 144161478Sdelphij return (ch <= 0x7F); 145161478Sdelphij} 146161478Sdelphij 147161478Sdelphij/* 14860786Sps * Rewind the line buffer. 14960786Sps */ 15060786Sps public void 15160786Spsprewind() 15260786Sps{ 15360786Sps curr = 0; 15460786Sps column = 0; 155161478Sdelphij cshift = 0; 15660786Sps overstrike = 0; 157161478Sdelphij last_overstrike = AT_NORMAL; 158161478Sdelphij mbc_buf_len = 0; 15960786Sps is_null_line = 0; 16060786Sps pendc = '\0'; 16163128Sps lmargin = 0; 162128345Stjr if (status_col) 163128345Stjr lmargin += 1; 16460786Sps} 16560786Sps 16660786Sps/* 16760786Sps * Insert the line number (of the given position) into the line buffer. 16860786Sps */ 16960786Sps public void 17060786Spsplinenum(pos) 17160786Sps POSITION pos; 17260786Sps{ 173128345Stjr register LINENUM linenum = 0; 17460786Sps register int i; 17560786Sps 17663128Sps if (linenums == OPT_ONPLUS) 17763128Sps { 17863128Sps /* 17963128Sps * Get the line number and put it in the current line. 18063128Sps * {{ Note: since find_linenum calls forw_raw_line, 18163128Sps * it may seek in the input file, requiring the caller 18263128Sps * of plinenum to re-seek if necessary. }} 18363128Sps * {{ Since forw_raw_line modifies linebuf, we must 18463128Sps * do this first, before storing anything in linebuf. }} 18563128Sps */ 186128345Stjr linenum = find_linenum(pos); 18763128Sps } 18863128Sps 18960786Sps /* 19063128Sps * Display a status column if the -J option is set. 19160786Sps */ 19263128Sps if (status_col) 19363128Sps { 19463128Sps linebuf[curr] = ' '; 19563128Sps if (start_attnpos != NULL_POSITION && 19663128Sps pos >= start_attnpos && pos < end_attnpos) 197161478Sdelphij attr[curr] = AT_NORMAL|AT_HILITE; 19863128Sps else 199161478Sdelphij attr[curr] = AT_NORMAL; 20063128Sps curr++; 20163128Sps column++; 20263128Sps } 20360786Sps /* 20463128Sps * Display the line number at the start of each line 20563128Sps * if the -N option is set. 20660786Sps */ 20763128Sps if (linenums == OPT_ONPLUS) 20863128Sps { 209128345Stjr char buf[INT_STRLEN_BOUND(pos) + 2]; 210128345Stjr int n; 211128345Stjr 212128345Stjr linenumtoa(linenum, buf); 213128345Stjr n = strlen(buf); 214128345Stjr if (n < MIN_LINENUM_WIDTH) 215128345Stjr n = MIN_LINENUM_WIDTH; 216128345Stjr sprintf(linebuf+curr, "%*s ", n, buf); 217128345Stjr n++; /* One space after the line number. */ 218128345Stjr for (i = 0; i < n; i++) 219128345Stjr attr[curr+i] = AT_NORMAL; 220128345Stjr curr += n; 221128345Stjr column += n; 222128345Stjr lmargin += n; 22363128Sps } 224128345Stjr 22560786Sps /* 22663128Sps * Append enough spaces to bring us to the lmargin. 22760786Sps */ 22863128Sps while (column < lmargin) 22960786Sps { 23060786Sps linebuf[curr] = ' '; 23160786Sps attr[curr++] = AT_NORMAL; 23260786Sps column++; 23363128Sps } 23460786Sps} 23560786Sps 23660786Sps/* 237161478Sdelphij * Shift the input line left. 238161478Sdelphij * This means discarding N printable chars at the start of the buffer. 23960786Sps */ 240161478Sdelphij static void 241161478Sdelphijpshift(shift) 242161478Sdelphij int shift; 243161478Sdelphij{ 244161478Sdelphij LWCHAR prev_ch = 0; 245161478Sdelphij unsigned char c; 246161478Sdelphij int shifted = 0; 247161478Sdelphij int to; 248161478Sdelphij int from; 24989019Sps int len; 250161478Sdelphij int width; 251161478Sdelphij int prev_attr; 252161478Sdelphij int next_attr; 25389019Sps 254161478Sdelphij if (shift > column - lmargin) 255161478Sdelphij shift = column - lmargin; 256161478Sdelphij if (shift > curr - lmargin) 257161478Sdelphij shift = curr - lmargin; 258161478Sdelphij 259161478Sdelphij to = from = lmargin; 26089019Sps /* 261161478Sdelphij * We keep on going when shifted == shift 262161478Sdelphij * to get all combining chars. 26389019Sps */ 264161478Sdelphij while (shifted <= shift && from < curr) 26589019Sps { 266161478Sdelphij c = linebuf[from]; 267172471Sdelphij if (ctldisp == OPT_ONPLUS && IS_CSI_START(c)) 26889019Sps { 269161478Sdelphij /* Keep cumulative effect. */ 270161478Sdelphij linebuf[to] = c; 271161478Sdelphij attr[to++] = attr[from++]; 272161478Sdelphij while (from < curr && linebuf[from]) 273161478Sdelphij { 274161478Sdelphij linebuf[to] = linebuf[from]; 275161478Sdelphij attr[to++] = attr[from]; 276161478Sdelphij if (!is_ansi_middle(linebuf[from++])) 277161478Sdelphij break; 278161478Sdelphij } 279161478Sdelphij continue; 280161478Sdelphij } 281161478Sdelphij 282161478Sdelphij width = 0; 283161478Sdelphij 284161478Sdelphij if (!IS_ASCII_OCTET(c) && utf_mode) 285161478Sdelphij { 286161478Sdelphij /* Assumes well-formedness validation already done. */ 287161478Sdelphij LWCHAR ch; 288161478Sdelphij 289161478Sdelphij len = utf_len(c); 290161478Sdelphij if (from + len > curr) 291161478Sdelphij break; 292161478Sdelphij ch = get_wchar(linebuf + from); 293161478Sdelphij if (!is_composing_char(ch) && !is_combining_char(prev_ch, ch)) 294161478Sdelphij width = is_wide_char(ch) ? 2 : 1; 295161478Sdelphij prev_ch = ch; 29689019Sps } else 29789019Sps { 298161478Sdelphij len = 1; 299161478Sdelphij if (c == '\b') 300161478Sdelphij /* XXX - Incorrect if several '\b' in a row. */ 301161478Sdelphij width = (utf_mode && is_wide_char(prev_ch)) ? -2 : -1; 302161478Sdelphij else if (!control_char(c)) 303161478Sdelphij width = 1; 304161478Sdelphij prev_ch = 0; 305161478Sdelphij } 306161478Sdelphij 307161478Sdelphij if (width == 2 && shift - shifted == 1) { 308161478Sdelphij /* Should never happen when called by pshift_all(). */ 309161478Sdelphij attr[to] = attr[from]; 310161478Sdelphij /* 311161478Sdelphij * Assume a wide_char will never be the first half of a 312161478Sdelphij * combining_char pair, so reset prev_ch in case we're 313161478Sdelphij * followed by a '\b'. 314161478Sdelphij */ 315161478Sdelphij prev_ch = linebuf[to++] = ' '; 316161478Sdelphij from += len; 317161478Sdelphij shifted++; 318161478Sdelphij continue; 319161478Sdelphij } 320161478Sdelphij 321161478Sdelphij /* Adjust width for magic cookies. */ 322161478Sdelphij prev_attr = (to > 0) ? attr[to-1] : AT_NORMAL; 323161478Sdelphij next_attr = (from + len < curr) ? attr[from + len] : prev_attr; 324161478Sdelphij if (!is_at_equiv(attr[from], prev_attr) && 325161478Sdelphij !is_at_equiv(attr[from], next_attr)) 326161478Sdelphij { 327161478Sdelphij width += attr_swidth(attr[from]); 328161478Sdelphij if (from + len < curr) 329161478Sdelphij width += attr_ewidth(attr[from]); 330161478Sdelphij if (is_at_equiv(prev_attr, next_attr)) 33189019Sps { 332161478Sdelphij width += attr_ewidth(prev_attr); 333161478Sdelphij if (from + len < curr) 334161478Sdelphij width += attr_swidth(next_attr); 33589019Sps } 33689019Sps } 33789019Sps 338161478Sdelphij if (shift - shifted < width) 339161478Sdelphij break; 340161478Sdelphij from += len; 341161478Sdelphij shifted += width; 342161478Sdelphij if (shifted < 0) 343161478Sdelphij shifted = 0; 34460786Sps } 345161478Sdelphij while (from < curr) 34660786Sps { 347161478Sdelphij linebuf[to] = linebuf[from]; 348161478Sdelphij attr[to++] = attr[from++]; 34960786Sps } 350161478Sdelphij curr = to; 351161478Sdelphij column -= shifted; 352161478Sdelphij cshift += shifted; 35360786Sps} 35460786Sps 35560786Sps/* 356161478Sdelphij * 35760786Sps */ 358161478Sdelphij public void 359161478Sdelphijpshift_all() 36060786Sps{ 361161478Sdelphij pshift(column); 36260786Sps} 36360786Sps 36460786Sps/* 36560786Sps * Return the printing width of the start (enter) sequence 36660786Sps * for a given character attribute. 36760786Sps */ 36860786Sps static int 36960786Spsattr_swidth(a) 37060786Sps int a; 37160786Sps{ 372161478Sdelphij int w = 0; 373161478Sdelphij 374161478Sdelphij a = apply_at_specials(a); 375161478Sdelphij 376161478Sdelphij if (a & AT_UNDERLINE) 377161478Sdelphij w += ul_s_width; 378161478Sdelphij if (a & AT_BOLD) 379161478Sdelphij w += bo_s_width; 380161478Sdelphij if (a & AT_BLINK) 381161478Sdelphij w += bl_s_width; 382161478Sdelphij if (a & AT_STANDOUT) 383161478Sdelphij w += so_s_width; 384161478Sdelphij 385161478Sdelphij return w; 38660786Sps} 38760786Sps 38860786Sps/* 38960786Sps * Return the printing width of the end (exit) sequence 39060786Sps * for a given character attribute. 39160786Sps */ 39260786Sps static int 39360786Spsattr_ewidth(a) 39460786Sps int a; 39560786Sps{ 396161478Sdelphij int w = 0; 397161478Sdelphij 398161478Sdelphij a = apply_at_specials(a); 399161478Sdelphij 400161478Sdelphij if (a & AT_UNDERLINE) 401161478Sdelphij w += ul_e_width; 402161478Sdelphij if (a & AT_BOLD) 403161478Sdelphij w += bo_e_width; 404161478Sdelphij if (a & AT_BLINK) 405161478Sdelphij w += bl_e_width; 406161478Sdelphij if (a & AT_STANDOUT) 407161478Sdelphij w += so_e_width; 408161478Sdelphij 409161478Sdelphij return w; 41060786Sps} 41160786Sps 41260786Sps/* 41360786Sps * Return the printing width of a given character and attribute, 41460786Sps * if the character were added to the current position in the line buffer. 41560786Sps * Adding a character with a given attribute may cause an enter or exit 41660786Sps * attribute sequence to be inserted, so this must be taken into account. 41760786Sps */ 41860786Sps static int 419161478Sdelphijpwidth(ch, a, prev_ch) 420161478Sdelphij LWCHAR ch; 42160786Sps int a; 422161478Sdelphij LWCHAR prev_ch; 42360786Sps{ 424161478Sdelphij int w; 42560786Sps 426161478Sdelphij if (ch == '\b') 42760786Sps /* 428161478Sdelphij * Backspace moves backwards one or two positions. 429161478Sdelphij * XXX - Incorrect if several '\b' in a row. 43060786Sps */ 431161478Sdelphij return (utf_mode && is_wide_char(prev_ch)) ? -2 : -1; 43260786Sps 433161478Sdelphij if (!utf_mode || is_ascii_char(ch)) 434161478Sdelphij { 435161478Sdelphij if (control_char((char)ch)) 436161478Sdelphij { 437161478Sdelphij /* 438161478Sdelphij * Control characters do unpredictable things, 439161478Sdelphij * so we don't even try to guess; say it doesn't move. 440161478Sdelphij * This can only happen if the -r flag is in effect. 441161478Sdelphij */ 442161478Sdelphij return (0); 443161478Sdelphij } 444161478Sdelphij } else 445161478Sdelphij { 446161478Sdelphij if (is_composing_char(ch) || is_combining_char(prev_ch, ch)) 447161478Sdelphij { 448161478Sdelphij /* 449161478Sdelphij * Composing and combining chars take up no space. 450161478Sdelphij * 451161478Sdelphij * Some terminals, upon failure to compose a 452161478Sdelphij * composing character with the character(s) that 453161478Sdelphij * precede(s) it will actually take up one column 454161478Sdelphij * for the composing character; there isn't much 455161478Sdelphij * we could do short of testing the (complex) 456161478Sdelphij * composition process ourselves and printing 457161478Sdelphij * a binary representation when it fails. 458161478Sdelphij */ 459161478Sdelphij return (0); 460161478Sdelphij } 461161478Sdelphij } 46260786Sps 46360786Sps /* 464161478Sdelphij * Other characters take one or two columns, 46560786Sps * plus the width of any attribute enter/exit sequence. 46660786Sps */ 46760786Sps w = 1; 468161478Sdelphij if (is_wide_char(ch)) 469161478Sdelphij w++; 470161478Sdelphij if (curr > 0 && !is_at_equiv(attr[curr-1], a)) 47160786Sps w += attr_ewidth(attr[curr-1]); 472161478Sdelphij if ((apply_at_specials(a) != AT_NORMAL) && 473161478Sdelphij (curr == 0 || !is_at_equiv(attr[curr-1], a))) 47460786Sps w += attr_swidth(a); 47560786Sps return (w); 47660786Sps} 47760786Sps 47860786Sps/* 479161478Sdelphij * Delete to the previous base character in the line buffer. 480161478Sdelphij * Return 1 if one is found. 48160786Sps */ 482161478Sdelphij static int 48360786Spsbackc() 48460786Sps{ 485161478Sdelphij LWCHAR prev_ch; 486161478Sdelphij char *p = linebuf + curr; 487161478Sdelphij LWCHAR ch = step_char(&p, -1, linebuf + lmargin); 488161478Sdelphij int width; 489161478Sdelphij 490161478Sdelphij /* This assumes that there is no '\b' in linebuf. */ 491161478Sdelphij while ( curr > lmargin 492161478Sdelphij && column > lmargin 493161478Sdelphij && (!(attr[curr - 1] & (AT_ANSI|AT_BINARY)))) 494161478Sdelphij { 495161478Sdelphij curr = p - linebuf; 496161478Sdelphij prev_ch = step_char(&p, -1, linebuf + lmargin); 497161478Sdelphij width = pwidth(ch, attr[curr], prev_ch); 498161478Sdelphij column -= width; 499161478Sdelphij if (width > 0) 500161478Sdelphij return 1; 501161478Sdelphij ch = prev_ch; 502161478Sdelphij } 503161478Sdelphij 504161478Sdelphij return 0; 50560786Sps} 50660786Sps 50760786Sps/* 50860786Sps * Are we currently within a recognized ANSI escape sequence? 50960786Sps */ 51060786Sps static int 51160786Spsin_ansi_esc_seq() 51260786Sps{ 513161478Sdelphij char *p; 51460786Sps 51560786Sps /* 51660786Sps * Search backwards for either an ESC (which means we ARE in a seq); 51760786Sps * or an end char (which means we're NOT in a seq). 51860786Sps */ 519161478Sdelphij for (p = &linebuf[curr]; p > linebuf; ) 52060786Sps { 521161478Sdelphij LWCHAR ch = step_char(&p, -1, linebuf); 522172471Sdelphij if (IS_CSI_START(ch)) 52360786Sps return (1); 524161478Sdelphij if (!is_ansi_middle(ch)) 52560786Sps return (0); 52660786Sps } 52760786Sps return (0); 52860786Sps} 52960786Sps 53060786Sps/* 53189019Sps * Is a character the end of an ANSI escape sequence? 53289019Sps */ 53389019Sps public int 534161478Sdelphijis_ansi_end(ch) 535161478Sdelphij LWCHAR ch; 53689019Sps{ 537161478Sdelphij if (!is_ascii_char(ch)) 538161478Sdelphij return (0); 539161478Sdelphij return (strchr(end_ansi_chars, (char) ch) != NULL); 54089019Sps} 54189019Sps 54289019Sps/* 543161478Sdelphij * 544161478Sdelphij */ 545161478Sdelphij public int 546161478Sdelphijis_ansi_middle(ch) 547161478Sdelphij LWCHAR ch; 548161478Sdelphij{ 549161478Sdelphij if (!is_ascii_char(ch)) 550161478Sdelphij return (0); 551161478Sdelphij if (is_ansi_end(ch)) 552161478Sdelphij return (0); 553161478Sdelphij return (strchr(mid_ansi_chars, (char) ch) != NULL); 554161478Sdelphij} 555161478Sdelphij 556161478Sdelphij/* 55760786Sps * Append a character and attribute to the line buffer. 55860786Sps */ 559161478Sdelphij#define STORE_CHAR(ch,a,rep,pos) \ 560161478Sdelphij do { \ 561161478Sdelphij if (store_char((ch),(a),(rep),(pos))) return (1); \ 562161478Sdelphij } while (0) 56389019Sps 56460786Sps static int 565161478Sdelphijstore_char(ch, a, rep, pos) 566161478Sdelphij LWCHAR ch; 56760786Sps int a; 568161478Sdelphij char *rep; 56960786Sps POSITION pos; 57060786Sps{ 571161478Sdelphij int w; 572161478Sdelphij int replen; 573161478Sdelphij char cs; 57460786Sps 575161478Sdelphij w = (a & (AT_UNDERLINE|AT_BOLD)); /* Pre-use w. */ 576161478Sdelphij if (w != AT_NORMAL) 577161478Sdelphij last_overstrike = w; 578161478Sdelphij 57960786Sps#if HILITE_SEARCH 58089019Sps { 581161478Sdelphij int matches; 582161478Sdelphij if (is_hilited(pos, pos+1, 0, &matches)) 583161478Sdelphij { 584161478Sdelphij /* 585161478Sdelphij * This character should be highlighted. 586161478Sdelphij * Override the attribute passed in. 587161478Sdelphij */ 588161478Sdelphij if (a != AT_ANSI) 589240121Sdelphij { 590240121Sdelphij if (highest_hilite != NULL_POSITION && 591240121Sdelphij pos > highest_hilite) 592240121Sdelphij highest_hilite = pos; 593161478Sdelphij a |= AT_HILITE; 594240121Sdelphij } 595161478Sdelphij } 59689019Sps } 59760786Sps#endif 598161478Sdelphij 59960786Sps if (ctldisp == OPT_ONPLUS && in_ansi_esc_seq()) 600161478Sdelphij { 601161478Sdelphij if (!is_ansi_end(ch) && !is_ansi_middle(ch)) { 602161478Sdelphij /* Remove whole unrecognized sequence. */ 603191930Sdelphij char *p = &linebuf[curr]; 604191930Sdelphij LWCHAR bch; 605191930Sdelphij do { 606191930Sdelphij bch = step_char(&p, -1, linebuf); 607191930Sdelphij } while (p > linebuf && !IS_CSI_START(bch)); 608191930Sdelphij curr = p - linebuf; 609161478Sdelphij return 0; 610161478Sdelphij } 611161478Sdelphij a = AT_ANSI; /* Will force re-AT_'ing around it. */ 61260786Sps w = 0; 613161478Sdelphij } 614172471Sdelphij else if (ctldisp == OPT_ONPLUS && IS_CSI_START(ch)) 615161478Sdelphij { 616161478Sdelphij a = AT_ANSI; /* Will force re-AT_'ing around it. */ 617161478Sdelphij w = 0; 618161478Sdelphij } 61960786Sps else 620161478Sdelphij { 621161478Sdelphij char *p = &linebuf[curr]; 622161478Sdelphij LWCHAR prev_ch = step_char(&p, -1, linebuf); 623161478Sdelphij w = pwidth(ch, a, prev_ch); 624161478Sdelphij } 625161478Sdelphij 62660786Sps if (ctldisp != OPT_ON && column + w + attr_ewidth(a) > sc_width) 62760786Sps /* 62860786Sps * Won't fit on screen. 62960786Sps */ 63060786Sps return (1); 63160786Sps 632161478Sdelphij if (rep == NULL) 63389019Sps { 634161478Sdelphij cs = (char) ch; 635161478Sdelphij rep = &cs; 636161478Sdelphij replen = 1; 637161478Sdelphij } else 638161478Sdelphij { 639161478Sdelphij replen = utf_len(rep[0]); 640161478Sdelphij } 641161478Sdelphij if (curr + replen >= size_linebuf-6) 642161478Sdelphij { 64360786Sps /* 64460786Sps * Won't fit in line buffer. 64589019Sps * Try to expand it. 64660786Sps */ 64789019Sps if (expand_linebuf()) 64889019Sps return (1); 64989019Sps } 65060786Sps 651161478Sdelphij while (replen-- > 0) 65260786Sps { 653161478Sdelphij linebuf[curr] = *rep++; 654161478Sdelphij attr[curr] = a; 655161478Sdelphij curr++; 65660786Sps } 65760786Sps column += w; 65860786Sps return (0); 65960786Sps} 66060786Sps 66160786Sps/* 66289019Sps * Append a tab to the line buffer. 66389019Sps * Store spaces to represent the tab. 66489019Sps */ 66589019Sps#define STORE_TAB(a,pos) \ 66689019Sps do { if (store_tab((a),(pos))) return (1); } while (0) 66789019Sps 66889019Sps static int 66989019Spsstore_tab(attr, pos) 67089019Sps int attr; 67189019Sps POSITION pos; 67289019Sps{ 67389019Sps int to_tab = column + cshift - lmargin; 67489019Sps int i; 67589019Sps 67689019Sps if (ntabstops < 2 || to_tab >= tabstops[ntabstops-1]) 67789019Sps to_tab = tabdefault - 67889019Sps ((to_tab - tabstops[ntabstops-1]) % tabdefault); 67989019Sps else 68089019Sps { 68189019Sps for (i = ntabstops - 2; i >= 0; i--) 68289019Sps if (to_tab >= tabstops[i]) 68389019Sps break; 68489019Sps to_tab = tabstops[i+1] - to_tab; 68589019Sps } 68689019Sps 687161478Sdelphij if (column + to_tab - 1 + pwidth(' ', attr, 0) + attr_ewidth(attr) > sc_width) 688161478Sdelphij return 1; 689161478Sdelphij 69089019Sps do { 691161478Sdelphij STORE_CHAR(' ', attr, " ", pos); 69289019Sps } while (--to_tab > 0); 69389019Sps return 0; 69489019Sps} 69589019Sps 696161478Sdelphij#define STORE_PRCHAR(c, pos) \ 697161478Sdelphij do { if (store_prchar((c), (pos))) return 1; } while (0) 698161478Sdelphij 699161478Sdelphij static int 700161478Sdelphijstore_prchar(c, pos) 701161478Sdelphij char c; 702161478Sdelphij POSITION pos; 703161478Sdelphij{ 704161478Sdelphij char *s; 705161478Sdelphij 706161478Sdelphij /* 707161478Sdelphij * Convert to printable representation. 708161478Sdelphij */ 709161478Sdelphij s = prchar(c); 710161478Sdelphij 711161478Sdelphij /* 712161478Sdelphij * Make sure we can get the entire representation 713161478Sdelphij * of the character on this line. 714161478Sdelphij */ 715161478Sdelphij if (column + (int) strlen(s) - 1 + 716161478Sdelphij pwidth(' ', binattr, 0) + attr_ewidth(binattr) > sc_width) 717161478Sdelphij return 1; 718161478Sdelphij 719161478Sdelphij for ( ; *s != 0; s++) 720161478Sdelphij STORE_CHAR(*s, AT_BINARY, NULL, pos); 721161478Sdelphij 722161478Sdelphij return 0; 723161478Sdelphij} 724161478Sdelphij 725161478Sdelphij static int 726161478Sdelphijflush_mbc_buf(pos) 727161478Sdelphij POSITION pos; 728161478Sdelphij{ 729161478Sdelphij int i; 730161478Sdelphij 731161478Sdelphij for (i = 0; i < mbc_buf_index; i++) 732161478Sdelphij if (store_prchar(mbc_buf[i], pos)) 733161478Sdelphij return mbc_buf_index - i; 734161478Sdelphij 735161478Sdelphij return 0; 736161478Sdelphij} 737161478Sdelphij 73889019Sps/* 73960786Sps * Append a character to the line buffer. 74060786Sps * Expand tabs into spaces, handle underlining, boldfacing, etc. 74160786Sps * Returns 0 if ok, 1 if couldn't fit in buffer. 74260786Sps */ 74360786Sps public int 74460786Spspappend(c, pos) 745161478Sdelphij char c; 74660786Sps POSITION pos; 74760786Sps{ 74860786Sps int r; 74960786Sps 75060786Sps if (pendc) 75160786Sps { 752161478Sdelphij if (do_append(pendc, NULL, pendpos)) 75360786Sps /* 75460786Sps * Oops. We've probably lost the char which 75560786Sps * was in pendc, since caller won't back up. 75660786Sps */ 75760786Sps return (1); 75860786Sps pendc = '\0'; 75960786Sps } 76060786Sps 76160786Sps if (c == '\r' && bs_mode == BS_SPECIAL) 76260786Sps { 763161478Sdelphij if (mbc_buf_len > 0) /* utf_mode must be on. */ 764161478Sdelphij { 765161478Sdelphij /* Flush incomplete (truncated) sequence. */ 766161478Sdelphij r = flush_mbc_buf(mbc_pos); 767161478Sdelphij mbc_buf_index = r + 1; 768161478Sdelphij mbc_buf_len = 0; 769161478Sdelphij if (r) 770161478Sdelphij return (mbc_buf_index); 771161478Sdelphij } 772161478Sdelphij 77360786Sps /* 77460786Sps * Don't put the CR into the buffer until we see 77560786Sps * the next char. If the next char is a newline, 77660786Sps * discard the CR. 77760786Sps */ 77860786Sps pendc = c; 77960786Sps pendpos = pos; 78060786Sps return (0); 78160786Sps } 78260786Sps 783161478Sdelphij if (!utf_mode) 784161478Sdelphij { 785161478Sdelphij r = do_append((LWCHAR) c, NULL, pos); 786161478Sdelphij } else 787161478Sdelphij { 788161478Sdelphij /* Perform strict validation in all possible cases. */ 789161478Sdelphij if (mbc_buf_len == 0) 790161478Sdelphij { 791161478Sdelphij retry: 792161478Sdelphij mbc_buf_index = 1; 793161478Sdelphij *mbc_buf = c; 794161478Sdelphij if (IS_ASCII_OCTET(c)) 795161478Sdelphij r = do_append((LWCHAR) c, NULL, pos); 796161478Sdelphij else if (IS_UTF8_LEAD(c)) 797161478Sdelphij { 798161478Sdelphij mbc_buf_len = utf_len(c); 799161478Sdelphij mbc_pos = pos; 800161478Sdelphij return (0); 801161478Sdelphij } else 802161478Sdelphij /* UTF8_INVALID or stray UTF8_TRAIL */ 803161478Sdelphij r = flush_mbc_buf(pos); 804161478Sdelphij } else if (IS_UTF8_TRAIL(c)) 805161478Sdelphij { 806161478Sdelphij mbc_buf[mbc_buf_index++] = c; 807161478Sdelphij if (mbc_buf_index < mbc_buf_len) 808161478Sdelphij return (0); 809161478Sdelphij if (is_utf8_well_formed(mbc_buf)) 810161478Sdelphij r = do_append(get_wchar(mbc_buf), mbc_buf, mbc_pos); 811161478Sdelphij else 812161478Sdelphij /* Complete, but not shortest form, sequence. */ 813161478Sdelphij mbc_buf_index = r = flush_mbc_buf(mbc_pos); 814161478Sdelphij mbc_buf_len = 0; 815161478Sdelphij } else 816161478Sdelphij { 817161478Sdelphij /* Flush incomplete (truncated) sequence. */ 818161478Sdelphij r = flush_mbc_buf(mbc_pos); 819161478Sdelphij mbc_buf_index = r + 1; 820161478Sdelphij mbc_buf_len = 0; 821161478Sdelphij /* Handle new char. */ 822161478Sdelphij if (!r) 823161478Sdelphij goto retry; 824161478Sdelphij } 825161478Sdelphij } 826161478Sdelphij 82760786Sps /* 82860786Sps * If we need to shift the line, do it. 82960786Sps * But wait until we get to at least the middle of the screen, 83060786Sps * so shifting it doesn't affect the chars we're currently 83160786Sps * pappending. (Bold & underline can get messed up otherwise.) 83260786Sps */ 83360786Sps if (cshift < hshift && column > sc_width / 2) 83489019Sps { 83589019Sps linebuf[curr] = '\0'; 83660786Sps pshift(hshift - cshift); 83789019Sps } 838161478Sdelphij if (r) 839161478Sdelphij { 840161478Sdelphij /* How many chars should caller back up? */ 841161478Sdelphij r = (!utf_mode) ? 1 : mbc_buf_index; 842161478Sdelphij } 84360786Sps return (r); 84460786Sps} 84560786Sps 84660786Sps static int 847161478Sdelphijdo_append(ch, rep, pos) 848161478Sdelphij LWCHAR ch; 849161478Sdelphij char *rep; 85060786Sps POSITION pos; 85160786Sps{ 85260786Sps register int a; 853161478Sdelphij LWCHAR prev_ch; 85460786Sps 855161478Sdelphij a = AT_NORMAL; 85660786Sps 857161478Sdelphij if (ch == '\b') 85860786Sps { 859161478Sdelphij if (bs_mode == BS_CONTROL) 86060786Sps goto do_control_char; 861161478Sdelphij 862161478Sdelphij /* 863161478Sdelphij * A better test is needed here so we don't 864161478Sdelphij * backspace over part of the printed 865161478Sdelphij * representation of a binary character. 866161478Sdelphij */ 867161478Sdelphij if ( curr <= lmargin 868161478Sdelphij || column <= lmargin 869161478Sdelphij || (attr[curr - 1] & (AT_ANSI|AT_BINARY))) 870161478Sdelphij STORE_PRCHAR('\b', pos); 871161478Sdelphij else if (bs_mode == BS_NORMAL) 872161478Sdelphij STORE_CHAR(ch, AT_NORMAL, NULL, pos); 873161478Sdelphij else if (bs_mode == BS_SPECIAL) 874161478Sdelphij overstrike = backc(); 875161478Sdelphij 876161478Sdelphij return 0; 877161478Sdelphij } 878161478Sdelphij 879161478Sdelphij if (overstrike > 0) 88060786Sps { 88160786Sps /* 88260786Sps * Overstrike the character at the current position 88360786Sps * in the line buffer. This will cause either 88460786Sps * underline (if a "_" is overstruck), 88560786Sps * bold (if an identical character is overstruck), 88660786Sps * or just deletion of the character in the buffer. 88760786Sps */ 888161478Sdelphij overstrike = utf_mode ? -1 : 0; 889161478Sdelphij /* To be correct, this must be a base character. */ 890161478Sdelphij prev_ch = get_wchar(linebuf + curr); 891161478Sdelphij a = attr[curr]; 892161478Sdelphij if (ch == prev_ch) 89389019Sps { 894128345Stjr /* 895128345Stjr * Overstriking a char with itself means make it bold. 896128345Stjr * But overstriking an underscore with itself is 897128345Stjr * ambiguous. It could mean make it bold, or 898128345Stjr * it could mean make it underlined. 899128345Stjr * Use the previous overstrike to resolve it. 900128345Stjr */ 901161478Sdelphij if (ch == '_') 90289019Sps { 903161478Sdelphij if ((a & (AT_BOLD|AT_UNDERLINE)) != AT_NORMAL) 904161478Sdelphij a |= (AT_BOLD|AT_UNDERLINE); 905161478Sdelphij else if (last_overstrike != AT_NORMAL) 906161478Sdelphij a |= last_overstrike; 907161478Sdelphij else 908161478Sdelphij a |= AT_BOLD; 909161478Sdelphij } else 910161478Sdelphij a |= AT_BOLD; 911161478Sdelphij } else if (ch == '_') 91289019Sps { 913161478Sdelphij a |= AT_UNDERLINE; 914161478Sdelphij ch = prev_ch; 915161478Sdelphij rep = linebuf + curr; 916161478Sdelphij } else if (prev_ch == '_') 917161478Sdelphij { 918161478Sdelphij a |= AT_UNDERLINE; 919161478Sdelphij } 920161478Sdelphij /* Else we replace prev_ch, but we keep its attributes. */ 921161478Sdelphij } else if (overstrike < 0) 922161478Sdelphij { 923161478Sdelphij if ( is_composing_char(ch) 924161478Sdelphij || is_combining_char(get_wchar(linebuf + curr), ch)) 925161478Sdelphij /* Continuation of the same overstrike. */ 926161478Sdelphij a = last_overstrike; 92760786Sps else 928161478Sdelphij overstrike = 0; 929161478Sdelphij } 930161478Sdelphij 931161478Sdelphij if (ch == '\t') 93260786Sps { 93360786Sps /* 93460786Sps * Expand a tab into spaces. 93560786Sps */ 93660786Sps switch (bs_mode) 93760786Sps { 93860786Sps case BS_CONTROL: 93960786Sps goto do_control_char; 94060786Sps case BS_NORMAL: 94160786Sps case BS_SPECIAL: 942161478Sdelphij STORE_TAB(a, pos); 94360786Sps break; 94460786Sps } 945161478Sdelphij } else if ((!utf_mode || is_ascii_char(ch)) && control_char((char)ch)) 94660786Sps { 94760786Sps do_control_char: 948172471Sdelphij if (ctldisp == OPT_ON || (ctldisp == OPT_ONPLUS && IS_CSI_START(ch))) 94960786Sps { 95060786Sps /* 95160786Sps * Output as a normal character. 95260786Sps */ 953161478Sdelphij STORE_CHAR(ch, AT_NORMAL, rep, pos); 95460786Sps } else 95560786Sps { 956161478Sdelphij STORE_PRCHAR((char) ch, pos); 957161478Sdelphij } 958161478Sdelphij } else if (utf_mode && ctldisp != OPT_ON && is_ubin_char(ch)) 959161478Sdelphij { 960161478Sdelphij char *s; 96160786Sps 962161478Sdelphij s = prutfchar(ch); 96360786Sps 964161478Sdelphij if (column + (int) strlen(s) - 1 + 965161478Sdelphij pwidth(' ', binattr, 0) + attr_ewidth(binattr) > sc_width) 966161478Sdelphij return (1); 967161478Sdelphij 968161478Sdelphij for ( ; *s != 0; s++) 969161478Sdelphij STORE_CHAR(*s, AT_BINARY, NULL, pos); 970161478Sdelphij } else 97160786Sps { 972161478Sdelphij STORE_CHAR(ch, a, rep, pos); 97360786Sps } 974161478Sdelphij return (0); 975161478Sdelphij} 97660786Sps 977161478Sdelphij/* 978161478Sdelphij * 979161478Sdelphij */ 980161478Sdelphij public int 981161478Sdelphijpflushmbc() 982161478Sdelphij{ 983161478Sdelphij int r = 0; 984161478Sdelphij 985161478Sdelphij if (mbc_buf_len > 0) 986161478Sdelphij { 987161478Sdelphij /* Flush incomplete (truncated) sequence. */ 988161478Sdelphij r = flush_mbc_buf(mbc_pos); 989161478Sdelphij mbc_buf_len = 0; 990161478Sdelphij } 991161478Sdelphij return r; 99260786Sps} 99360786Sps 99460786Sps/* 99560786Sps * Terminate the line in the line buffer. 99660786Sps */ 99760786Sps public void 998195941Sdelphijpdone(endline, forw) 99960786Sps int endline; 1000195941Sdelphij int forw; 100160786Sps{ 1002161478Sdelphij (void) pflushmbc(); 1003161478Sdelphij 100460786Sps if (pendc && (pendc != '\r' || !endline)) 100560786Sps /* 100660786Sps * If we had a pending character, put it in the buffer. 100760786Sps * But discard a pending CR if we are at end of line 100860786Sps * (that is, discard the CR in a CR/LF sequence). 100960786Sps */ 1010161478Sdelphij (void) do_append(pendc, NULL, pendpos); 101160786Sps 101260786Sps /* 101360786Sps * Make sure we've shifted the line, if we need to. 101460786Sps */ 101560786Sps if (cshift < hshift) 101660786Sps pshift(hshift - cshift); 101760786Sps 1018161478Sdelphij if (ctldisp == OPT_ONPLUS && is_ansi_end('m')) 1019161478Sdelphij { 1020161478Sdelphij /* Switch to normal attribute at end of line. */ 1021161478Sdelphij char *p = "\033[m"; 1022161478Sdelphij for ( ; *p != '\0'; p++) 1023161478Sdelphij { 1024161478Sdelphij linebuf[curr] = *p; 1025161478Sdelphij attr[curr++] = AT_ANSI; 1026161478Sdelphij } 1027161478Sdelphij } 1028161478Sdelphij 102960786Sps /* 103060786Sps * Add a newline if necessary, 103160786Sps * and append a '\0' to the end of the line. 1032170259Sdelphij * We output a newline if we're not at the right edge of the screen, 1033170259Sdelphij * or if the terminal doesn't auto wrap, 1034170259Sdelphij * or if this is really the end of the line AND the terminal ignores 1035170259Sdelphij * a newline at the right edge. 1036170259Sdelphij * (In the last case we don't want to output a newline if the terminal 1037170259Sdelphij * doesn't ignore it since that would produce an extra blank line. 1038170259Sdelphij * But we do want to output a newline if the terminal ignores it in case 1039170259Sdelphij * the next line is blank. In that case the single newline output for 1040170259Sdelphij * that blank line would be ignored!) 104160786Sps */ 1042191930Sdelphij if (column < sc_width || !auto_wrap || (endline && ignaw) || ctldisp == OPT_ON) 104360786Sps { 104460786Sps linebuf[curr] = '\n'; 104560786Sps attr[curr] = AT_NORMAL; 104660786Sps curr++; 1047173685Sdelphij } 1048195941Sdelphij else if (ignaw && column >= sc_width && forw) 1049173685Sdelphij { 1050173685Sdelphij /* 1051191930Sdelphij * Terminals with "ignaw" don't wrap until they *really* need 1052191930Sdelphij * to, i.e. when the character *after* the last one to fit on a 1053191930Sdelphij * line is output. But they are too hard to deal with when they 1054191930Sdelphij * get in the state where a full screen width of characters 1055191930Sdelphij * have been output but the cursor is sitting on the right edge 1056191930Sdelphij * instead of at the start of the next line. 1057195941Sdelphij * So we nudge them into wrapping by outputting a space 1058195941Sdelphij * character plus a backspace. But do this only if moving 1059195941Sdelphij * forward; if we're moving backward and drawing this line at 1060195941Sdelphij * the top of the screen, the space would overwrite the first 1061195941Sdelphij * char on the next line. We don't need to do this "nudge" 1062195941Sdelphij * at the top of the screen anyway. 1063173685Sdelphij */ 1064195941Sdelphij linebuf[curr] = ' '; 1065173685Sdelphij attr[curr++] = AT_NORMAL; 1066173685Sdelphij linebuf[curr] = '\b'; 1067173685Sdelphij attr[curr++] = AT_NORMAL; 106860786Sps } 106960786Sps linebuf[curr] = '\0'; 107060786Sps attr[curr] = AT_NORMAL; 1071191930Sdelphij} 107289019Sps 1073191930Sdelphij/* 1074191930Sdelphij * 1075191930Sdelphij */ 1076191930Sdelphij public void 1077191930Sdelphijset_status_col(c) 1078191930Sdelphij char c; 1079191930Sdelphij{ 1080191930Sdelphij linebuf[0] = c; 1081191930Sdelphij attr[0] = AT_NORMAL|AT_HILITE; 108260786Sps} 108360786Sps 108460786Sps/* 108560786Sps * Get a character from the current line. 108660786Sps * Return the character as the function return value, 108760786Sps * and the character attribute in *ap. 108860786Sps */ 108960786Sps public int 109060786Spsgline(i, ap) 109160786Sps register int i; 109260786Sps register int *ap; 109360786Sps{ 109460786Sps if (is_null_line) 109560786Sps { 109660786Sps /* 109760786Sps * If there is no current line, we pretend the line is 109860786Sps * either "~" or "", depending on the "twiddle" flag. 109960786Sps */ 1100161478Sdelphij if (twiddle) 1101161478Sdelphij { 1102161478Sdelphij if (i == 0) 1103161478Sdelphij { 1104161478Sdelphij *ap = AT_BOLD; 1105161478Sdelphij return '~'; 1106161478Sdelphij } 1107161478Sdelphij --i; 1108161478Sdelphij } 1109161478Sdelphij /* Make sure we're back to AT_NORMAL before the '\n'. */ 1110161478Sdelphij *ap = AT_NORMAL; 1111161478Sdelphij return i ? '\0' : '\n'; 111260786Sps } 111360786Sps 111460786Sps *ap = attr[i]; 1115161478Sdelphij return (linebuf[i] & 0xFF); 111660786Sps} 111760786Sps 111860786Sps/* 111960786Sps * Indicate that there is no current line. 112060786Sps */ 112160786Sps public void 112260786Spsnull_line() 112360786Sps{ 112460786Sps is_null_line = 1; 112560786Sps cshift = 0; 112660786Sps} 112760786Sps 112860786Sps/* 112960786Sps * Analogous to forw_line(), but deals with "raw lines": 113060786Sps * lines which are not split for screen width. 113160786Sps * {{ This is supposed to be more efficient than forw_line(). }} 113260786Sps */ 113360786Sps public POSITION 1134170259Sdelphijforw_raw_line(curr_pos, linep, line_lenp) 113560786Sps POSITION curr_pos; 113660786Sps char **linep; 1137170259Sdelphij int *line_lenp; 113860786Sps{ 113989019Sps register int n; 114060786Sps register int c; 114160786Sps POSITION new_pos; 114260786Sps 114360786Sps if (curr_pos == NULL_POSITION || ch_seek(curr_pos) || 114460786Sps (c = ch_forw_get()) == EOI) 114560786Sps return (NULL_POSITION); 114660786Sps 114789019Sps n = 0; 114860786Sps for (;;) 114960786Sps { 1150161478Sdelphij if (c == '\n' || c == EOI || ABORT_SIGS()) 115160786Sps { 115260786Sps new_pos = ch_tell(); 115360786Sps break; 115460786Sps } 115589019Sps if (n >= size_linebuf-1) 115660786Sps { 115789019Sps if (expand_linebuf()) 115889019Sps { 115989019Sps /* 116089019Sps * Overflowed the input buffer. 116189019Sps * Pretend the line ended here. 116289019Sps */ 116389019Sps new_pos = ch_tell() - 1; 116489019Sps break; 116589019Sps } 116660786Sps } 116789019Sps linebuf[n++] = c; 116860786Sps c = ch_forw_get(); 116960786Sps } 117089019Sps linebuf[n] = '\0'; 117160786Sps if (linep != NULL) 117260786Sps *linep = linebuf; 1173170259Sdelphij if (line_lenp != NULL) 1174170259Sdelphij *line_lenp = n; 117560786Sps return (new_pos); 117660786Sps} 117760786Sps 117860786Sps/* 117960786Sps * Analogous to back_line(), but deals with "raw lines". 118060786Sps * {{ This is supposed to be more efficient than back_line(). }} 118160786Sps */ 118260786Sps public POSITION 1183170259Sdelphijback_raw_line(curr_pos, linep, line_lenp) 118460786Sps POSITION curr_pos; 118560786Sps char **linep; 1186170259Sdelphij int *line_lenp; 118760786Sps{ 118889019Sps register int n; 118960786Sps register int c; 119060786Sps POSITION new_pos; 119160786Sps 119260786Sps if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() || 119360786Sps ch_seek(curr_pos-1)) 119460786Sps return (NULL_POSITION); 119560786Sps 119689019Sps n = size_linebuf; 119789019Sps linebuf[--n] = '\0'; 119860786Sps for (;;) 119960786Sps { 120060786Sps c = ch_back_get(); 1201161478Sdelphij if (c == '\n' || ABORT_SIGS()) 120260786Sps { 120360786Sps /* 120460786Sps * This is the newline ending the previous line. 120560786Sps * We have hit the beginning of the line. 120660786Sps */ 120760786Sps new_pos = ch_tell() + 1; 120860786Sps break; 120960786Sps } 121060786Sps if (c == EOI) 121160786Sps { 121260786Sps /* 121360786Sps * We have hit the beginning of the file. 121460786Sps * This must be the first line in the file. 121560786Sps * This must, of course, be the beginning of the line. 121660786Sps */ 121760786Sps new_pos = ch_zero(); 121860786Sps break; 121960786Sps } 122089019Sps if (n <= 0) 122160786Sps { 122289019Sps int old_size_linebuf = size_linebuf; 122389019Sps char *fm; 122489019Sps char *to; 122589019Sps if (expand_linebuf()) 122689019Sps { 122789019Sps /* 122889019Sps * Overflowed the input buffer. 122989019Sps * Pretend the line ended here. 123089019Sps */ 123189019Sps new_pos = ch_tell() + 1; 123289019Sps break; 123389019Sps } 123460786Sps /* 123589019Sps * Shift the data to the end of the new linebuf. 123660786Sps */ 1237149487Stjr for (fm = linebuf + old_size_linebuf - 1, 1238149487Stjr to = linebuf + size_linebuf - 1; 123989019Sps fm >= linebuf; fm--, to--) 124089019Sps *to = *fm; 124189019Sps n = size_linebuf - old_size_linebuf; 124260786Sps } 124389019Sps linebuf[--n] = c; 124460786Sps } 124560786Sps if (linep != NULL) 124689019Sps *linep = &linebuf[n]; 1247170259Sdelphij if (line_lenp != NULL) 1248170259Sdelphij *line_lenp = size_linebuf - 1 - n; 124960786Sps return (new_pos); 125060786Sps} 1251