line.c revision 149487
1149487Stjr/* $FreeBSD: head/contrib/less/line.c 149487 2005-08-26 10:05:59Z tjr $ */ 260786Sps/* 3128345Stjr * Copyright (C) 1984-2002 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 * 860786Sps * For more information about less, or for information on how to 960786Sps * contact the author, see the README file. 1060786Sps */ 1160786Sps 1260786Sps 1360786Sps/* 1460786Sps * Routines to manipulate the "line buffer". 1560786Sps * The line buffer holds a line of output as it is being built 1660786Sps * in preparation for output to the screen. 1760786Sps */ 1860786Sps 1960786Sps#include "less.h" 2060786Sps 2160786Sps#define IS_CONT(c) (((c) & 0xC0) == 0x80) 2260786Sps 2389019Spspublic char *linebuf = NULL; /* Buffer which holds the current output line */ 2489019Spsstatic char *attr = NULL; /* Extension of linebuf to hold attributes */ 2589019Spspublic int size_linebuf = 0; /* Size of line buffer (and attr buffer) */ 2660786Sps 2760786Spspublic int cshift; /* Current left-shift of output line buffer */ 2860786Spspublic int hshift; /* Desired left-shift of output line buffer */ 2989019Spspublic int tabstops[TABSTOP_MAX] = { 0 }; /* Custom tabstops */ 3089019Spspublic int ntabstops = 1; /* Number of tabstops */ 3189019Spspublic int tabdefault = 8; /* Default repeated tabstops */ 3260786Sps 3360786Spsstatic int curr; /* Index into linebuf */ 3460786Spsstatic int column; /* Printable length, accounting for 3560786Sps backspaces, etc. */ 3660786Spsstatic int overstrike; /* Next char should overstrike previous char */ 37128345Stjrstatic int last_overstrike = AT_NORMAL; 3860786Spsstatic int is_null_line; /* There is no current line */ 3963128Spsstatic int lmargin; /* Left margin */ 4089019Spsstatic int hilites; /* Number of hilites in this line */ 4160786Spsstatic char pendc; 4260786Spsstatic POSITION pendpos; 4360786Spsstatic char *end_ansi_chars; 4460786Sps 4560786Spsstatic int do_append(); 4660786Sps 4760786Spsextern int bs_mode; 4860786Spsextern int linenums; 4960786Spsextern int ctldisp; 5060786Spsextern int twiddle; 5160786Spsextern int binattr; 5263128Spsextern int status_col; 5360786Spsextern int auto_wrap, ignaw; 5460786Spsextern int bo_s_width, bo_e_width; 5560786Spsextern int ul_s_width, ul_e_width; 5660786Spsextern int bl_s_width, bl_e_width; 5760786Spsextern int so_s_width, so_e_width; 5860786Spsextern int sc_width, sc_height; 5960786Spsextern int utf_mode; 6063128Spsextern POSITION start_attnpos; 6163128Spsextern POSITION end_attnpos; 6260786Sps 6360786Sps/* 6460786Sps * Initialize from environment variables. 6560786Sps */ 6660786Sps public void 6760786Spsinit_line() 6860786Sps{ 6960786Sps end_ansi_chars = lgetenv("LESSANSIENDCHARS"); 7060786Sps if (end_ansi_chars == NULL || *end_ansi_chars == '\0') 7160786Sps end_ansi_chars = "m"; 7289019Sps linebuf = (char *) ecalloc(LINEBUF_SIZE, sizeof(char)); 7389019Sps attr = (char *) ecalloc(LINEBUF_SIZE, sizeof(char)); 7489019Sps size_linebuf = LINEBUF_SIZE; 7560786Sps} 7660786Sps 7760786Sps/* 7889019Sps * Expand the line buffer. 7989019Sps */ 8089019Sps static int 8189019Spsexpand_linebuf() 8289019Sps{ 8389019Sps int new_size = size_linebuf + LINEBUF_SIZE; 8489019Sps char *new_buf = (char *) calloc(new_size, sizeof(char)); 8589019Sps char *new_attr = (char *) calloc(new_size, sizeof(char)); 8689019Sps if (new_buf == NULL || new_attr == NULL) 8789019Sps { 8889019Sps if (new_attr != NULL) 8989019Sps free(new_attr); 9089019Sps if (new_buf != NULL) 9189019Sps free(new_buf); 9289019Sps return 1; 9389019Sps } 9489019Sps memcpy(new_buf, linebuf, size_linebuf * sizeof(char)); 9589019Sps memcpy(new_attr, attr, size_linebuf * sizeof(char)); 96128345Stjr free(attr); 97128345Stjr free(linebuf); 9889019Sps linebuf = new_buf; 9989019Sps attr = new_attr; 10089019Sps size_linebuf = new_size; 10189019Sps return 0; 10289019Sps} 10389019Sps 10489019Sps/* 10560786Sps * Rewind the line buffer. 10660786Sps */ 10760786Sps public void 10860786Spsprewind() 10960786Sps{ 11060786Sps curr = 0; 11160786Sps column = 0; 11260786Sps overstrike = 0; 11360786Sps is_null_line = 0; 11460786Sps pendc = '\0'; 11563128Sps lmargin = 0; 116128345Stjr if (status_col) 117128345Stjr lmargin += 1; 11889019Sps#if HILITE_SEARCH 11989019Sps hilites = 0; 12089019Sps#endif 12160786Sps} 12260786Sps 12360786Sps/* 12460786Sps * Insert the line number (of the given position) into the line buffer. 12560786Sps */ 12660786Sps public void 12760786Spsplinenum(pos) 12860786Sps POSITION pos; 12960786Sps{ 130128345Stjr register LINENUM linenum = 0; 13160786Sps register int i; 13260786Sps 13363128Sps if (linenums == OPT_ONPLUS) 13463128Sps { 13563128Sps /* 13663128Sps * Get the line number and put it in the current line. 13763128Sps * {{ Note: since find_linenum calls forw_raw_line, 13863128Sps * it may seek in the input file, requiring the caller 13963128Sps * of plinenum to re-seek if necessary. }} 14063128Sps * {{ Since forw_raw_line modifies linebuf, we must 14163128Sps * do this first, before storing anything in linebuf. }} 14263128Sps */ 143128345Stjr linenum = find_linenum(pos); 14463128Sps } 14563128Sps 14660786Sps /* 14763128Sps * Display a status column if the -J option is set. 14860786Sps */ 14963128Sps if (status_col) 15063128Sps { 15163128Sps linebuf[curr] = ' '; 15263128Sps if (start_attnpos != NULL_POSITION && 15363128Sps pos >= start_attnpos && pos < end_attnpos) 15463128Sps attr[curr] = AT_STANDOUT; 15563128Sps else 15663128Sps attr[curr] = 0; 15763128Sps curr++; 15863128Sps column++; 15963128Sps } 16060786Sps /* 16163128Sps * Display the line number at the start of each line 16263128Sps * if the -N option is set. 16360786Sps */ 16463128Sps if (linenums == OPT_ONPLUS) 16563128Sps { 166128345Stjr char buf[INT_STRLEN_BOUND(pos) + 2]; 167128345Stjr int n; 168128345Stjr 169128345Stjr linenumtoa(linenum, buf); 170128345Stjr n = strlen(buf); 171128345Stjr if (n < MIN_LINENUM_WIDTH) 172128345Stjr n = MIN_LINENUM_WIDTH; 173128345Stjr sprintf(linebuf+curr, "%*s ", n, buf); 174128345Stjr n++; /* One space after the line number. */ 175128345Stjr for (i = 0; i < n; i++) 176128345Stjr attr[curr+i] = AT_NORMAL; 177128345Stjr curr += n; 178128345Stjr column += n; 179128345Stjr lmargin += n; 18063128Sps } 181128345Stjr 18260786Sps /* 18363128Sps * Append enough spaces to bring us to the lmargin. 18460786Sps */ 18563128Sps while (column < lmargin) 18660786Sps { 18760786Sps linebuf[curr] = ' '; 18860786Sps attr[curr++] = AT_NORMAL; 18960786Sps column++; 19063128Sps } 19160786Sps} 19260786Sps 19360786Sps/* 19489019Sps * Determine how many characters are required to shift N columns. 19560786Sps */ 19660786Sps static int 19789019Spsshift_chars(s, len) 19889019Sps char *s; 19989019Sps int len; 20060786Sps{ 20189019Sps char *p = s; 20289019Sps 20389019Sps /* 20489019Sps * Each char counts for one column, except ANSI color escape 20589019Sps * sequences use no columns since they don't move the cursor. 20689019Sps */ 20789019Sps while (*p != '\0' && len > 0) 20889019Sps { 20989019Sps if (*p++ != ESC) 21089019Sps { 21189019Sps len--; 21289019Sps } else 21389019Sps { 21489019Sps while (*p != '\0') 21589019Sps { 21689019Sps if (is_ansi_end(*p++)) 21789019Sps break; 21889019Sps } 21989019Sps } 22089019Sps } 22189019Sps return (p - s); 22289019Sps} 22389019Sps 22489019Sps/* 22589019Sps * Determine how many characters are required to shift N columns (UTF version). 22689019Sps * {{ FIXME: what about color escape sequences in UTF mode? }} 22789019Sps */ 22889019Sps static int 22989019Spsutf_shift_chars(s, len) 23089019Sps char *s; 23189019Sps int len; 23289019Sps{ 23360786Sps int ulen = 0; 23460786Sps 23560786Sps while (*s != '\0' && len > 0) 23660786Sps { 23760786Sps if (!IS_CONT(*s)) 23860786Sps len--; 23960786Sps s++; 24060786Sps ulen++; 24160786Sps } 24260786Sps while (IS_CONT(*s)) 24360786Sps { 24460786Sps s++; 24560786Sps ulen++; 24660786Sps } 24760786Sps return (ulen); 24860786Sps} 24960786Sps 25060786Sps/* 25160786Sps * Shift the input line left. 25260786Sps * This means discarding N printable chars at the start of the buffer. 25360786Sps */ 25460786Sps static void 25560786Spspshift(shift) 25660786Sps int shift; 25760786Sps{ 25860786Sps int i; 25989019Sps int nchars; 26060786Sps 26163128Sps if (shift > column - lmargin) 26263128Sps shift = column - lmargin; 26363128Sps if (shift > curr - lmargin) 26463128Sps shift = curr - lmargin; 26560786Sps 26689019Sps if (utf_mode) 26789019Sps nchars = utf_shift_chars(linebuf + lmargin, shift); 26860786Sps else 26989019Sps nchars = shift_chars(linebuf + lmargin, shift); 27089019Sps if (nchars > curr) 27189019Sps nchars = curr; 27289019Sps for (i = 0; i < curr - nchars; i++) 27360786Sps { 27489019Sps linebuf[lmargin + i] = linebuf[lmargin + i + nchars]; 27589019Sps attr[lmargin + i] = attr[lmargin + i + nchars]; 27660786Sps } 27789019Sps curr -= nchars; 27860786Sps column -= shift; 27960786Sps cshift += shift; 28060786Sps} 28160786Sps 28260786Sps/* 28360786Sps * Return the printing width of the start (enter) sequence 28460786Sps * for a given character attribute. 28560786Sps */ 28660786Sps static int 28760786Spsattr_swidth(a) 28860786Sps int a; 28960786Sps{ 29060786Sps switch (a) 29160786Sps { 29260786Sps case AT_BOLD: return (bo_s_width); 29360786Sps case AT_UNDERLINE: return (ul_s_width); 29460786Sps case AT_BLINK: return (bl_s_width); 29560786Sps case AT_STANDOUT: return (so_s_width); 29660786Sps } 29760786Sps return (0); 29860786Sps} 29960786Sps 30060786Sps/* 30160786Sps * Return the printing width of the end (exit) sequence 30260786Sps * for a given character attribute. 30360786Sps */ 30460786Sps static int 30560786Spsattr_ewidth(a) 30660786Sps int a; 30760786Sps{ 30860786Sps switch (a) 30960786Sps { 31060786Sps case AT_BOLD: return (bo_e_width); 31160786Sps case AT_UNDERLINE: return (ul_e_width); 31260786Sps case AT_BLINK: return (bl_e_width); 31360786Sps case AT_STANDOUT: return (so_e_width); 31460786Sps } 31560786Sps return (0); 31660786Sps} 31760786Sps 31860786Sps/* 31960786Sps * Return the printing width of a given character and attribute, 32060786Sps * if the character were added to the current position in the line buffer. 32160786Sps * Adding a character with a given attribute may cause an enter or exit 32260786Sps * attribute sequence to be inserted, so this must be taken into account. 32360786Sps */ 32460786Sps static int 32560786Spspwidth(c, a) 32660786Sps int c; 32760786Sps int a; 32860786Sps{ 32960786Sps register int w; 33060786Sps 33160786Sps if (utf_mode && IS_CONT(c)) 33260786Sps return (0); 33360786Sps 33460786Sps if (c == '\b') 33560786Sps /* 33660786Sps * Backspace moves backwards one position. 33760786Sps */ 33860786Sps return (-1); 33960786Sps 34060786Sps if (control_char(c)) 34160786Sps /* 34260786Sps * Control characters do unpredicatable things, 34360786Sps * so we don't even try to guess; say it doesn't move. 34460786Sps * This can only happen if the -r flag is in effect. 34560786Sps */ 34660786Sps return (0); 34760786Sps 34860786Sps /* 34960786Sps * Other characters take one space, 35060786Sps * plus the width of any attribute enter/exit sequence. 35160786Sps */ 35260786Sps w = 1; 35360786Sps if (curr > 0 && attr[curr-1] != a) 35460786Sps w += attr_ewidth(attr[curr-1]); 35560786Sps if (a && (curr == 0 || attr[curr-1] != a)) 35660786Sps w += attr_swidth(a); 35760786Sps return (w); 35860786Sps} 35960786Sps 36060786Sps/* 36160786Sps * Delete the previous character in the line buffer. 36260786Sps */ 36360786Sps static void 36460786Spsbackc() 36560786Sps{ 36660786Sps curr--; 36760786Sps column -= pwidth(linebuf[curr], attr[curr]); 36860786Sps} 36960786Sps 37060786Sps/* 37160786Sps * Are we currently within a recognized ANSI escape sequence? 37260786Sps */ 37360786Sps static int 37460786Spsin_ansi_esc_seq() 37560786Sps{ 37660786Sps int i; 37760786Sps 37860786Sps /* 37960786Sps * Search backwards for either an ESC (which means we ARE in a seq); 38060786Sps * or an end char (which means we're NOT in a seq). 38160786Sps */ 38260786Sps for (i = curr-1; i >= 0; i--) 38360786Sps { 38460786Sps if (linebuf[i] == ESC) 38560786Sps return (1); 38689019Sps if (is_ansi_end(linebuf[i])) 38760786Sps return (0); 38860786Sps } 38960786Sps return (0); 39060786Sps} 39160786Sps 39260786Sps/* 39389019Sps * Is a character the end of an ANSI escape sequence? 39489019Sps */ 39589019Sps public int 39689019Spsis_ansi_end(c) 39789019Sps char c; 39889019Sps{ 39989019Sps return (strchr(end_ansi_chars, c) != NULL); 40089019Sps} 40189019Sps 40289019Sps/* 40360786Sps * Append a character and attribute to the line buffer. 40460786Sps */ 40589019Sps#define STORE_CHAR(c,a,pos) \ 40689019Sps do { if (store_char((c),(a),(pos))) return (1); else curr++; } while (0) 40789019Sps 40860786Sps static int 40989019Spsstore_char(c, a, pos) 41060786Sps int c; 41160786Sps int a; 41260786Sps POSITION pos; 41360786Sps{ 41460786Sps register int w; 41560786Sps 416128345Stjr if (a != AT_NORMAL) 417128345Stjr last_overstrike = a; 41860786Sps#if HILITE_SEARCH 41960786Sps if (is_hilited(pos, pos+1, 0)) 42089019Sps { 42160786Sps /* 42260786Sps * This character should be highlighted. 42360786Sps * Override the attribute passed in. 42460786Sps */ 42560786Sps a = AT_STANDOUT; 42689019Sps hilites++; 42789019Sps } 42860786Sps#endif 42960786Sps if (ctldisp == OPT_ONPLUS && in_ansi_esc_seq()) 43060786Sps w = 0; 43160786Sps else 43260786Sps w = pwidth(c, a); 43360786Sps if (ctldisp != OPT_ON && column + w + attr_ewidth(a) > sc_width) 43460786Sps /* 43560786Sps * Won't fit on screen. 43660786Sps */ 43760786Sps return (1); 43860786Sps 43989019Sps if (curr >= size_linebuf-2) 44089019Sps { 44160786Sps /* 44260786Sps * Won't fit in line buffer. 44389019Sps * Try to expand it. 44460786Sps */ 44589019Sps if (expand_linebuf()) 44689019Sps return (1); 44789019Sps } 44860786Sps 44960786Sps /* 45060786Sps * Special handling for "magic cookie" terminals. 45160786Sps * If an attribute enter/exit sequence has a printing width > 0, 45260786Sps * and the sequence is adjacent to a space, delete the space. 45360786Sps * We just mark the space as invisible, to avoid having too 45460786Sps * many spaces deleted. 45560786Sps * {{ Note that even if the attribute width is > 1, we 45660786Sps * delete only one space. It's not worth trying to do more. 45760786Sps * It's hardly worth doing this much. }} 45860786Sps */ 45960786Sps if (curr > 0 && a != AT_NORMAL && 46060786Sps linebuf[curr-1] == ' ' && attr[curr-1] == AT_NORMAL && 46160786Sps attr_swidth(a) > 0) 46260786Sps { 46360786Sps /* 46460786Sps * We are about to append an enter-attribute sequence 46560786Sps * just after a space. Delete the space. 46660786Sps */ 46760786Sps attr[curr-1] = AT_INVIS; 46860786Sps column--; 46960786Sps } else if (curr > 0 && attr[curr-1] != AT_NORMAL && 47060786Sps attr[curr-1] != AT_INVIS && c == ' ' && a == AT_NORMAL && 47160786Sps attr_ewidth(attr[curr-1]) > 0) 47260786Sps { 47360786Sps /* 47460786Sps * We are about to append a space just after an 47560786Sps * exit-attribute sequence. Delete the space. 47660786Sps */ 47760786Sps a = AT_INVIS; 47860786Sps column--; 47960786Sps } 48060786Sps /* End of magic cookie handling. */ 48160786Sps 48260786Sps linebuf[curr] = c; 48360786Sps attr[curr] = a; 48460786Sps column += w; 48560786Sps return (0); 48660786Sps} 48760786Sps 48860786Sps/* 48989019Sps * Append a tab to the line buffer. 49089019Sps * Store spaces to represent the tab. 49189019Sps */ 49289019Sps#define STORE_TAB(a,pos) \ 49389019Sps do { if (store_tab((a),(pos))) return (1); } while (0) 49489019Sps 49589019Sps static int 49689019Spsstore_tab(attr, pos) 49789019Sps int attr; 49889019Sps POSITION pos; 49989019Sps{ 50089019Sps int to_tab = column + cshift - lmargin; 50189019Sps int i; 50289019Sps 50389019Sps if (ntabstops < 2 || to_tab >= tabstops[ntabstops-1]) 50489019Sps to_tab = tabdefault - 50589019Sps ((to_tab - tabstops[ntabstops-1]) % tabdefault); 50689019Sps else 50789019Sps { 50889019Sps for (i = ntabstops - 2; i >= 0; i--) 50989019Sps if (to_tab >= tabstops[i]) 51089019Sps break; 51189019Sps to_tab = tabstops[i+1] - to_tab; 51289019Sps } 51389019Sps 51489019Sps do { 51589019Sps STORE_CHAR(' ', attr, pos); 51689019Sps } while (--to_tab > 0); 51789019Sps return 0; 51889019Sps} 51989019Sps 52089019Sps/* 52160786Sps * Append a character to the line buffer. 52260786Sps * Expand tabs into spaces, handle underlining, boldfacing, etc. 52360786Sps * Returns 0 if ok, 1 if couldn't fit in buffer. 52460786Sps */ 52560786Sps public int 52660786Spspappend(c, pos) 52760786Sps register int c; 52860786Sps POSITION pos; 52960786Sps{ 53060786Sps int r; 53160786Sps 53260786Sps if (pendc) 53360786Sps { 53460786Sps if (do_append(pendc, pendpos)) 53560786Sps /* 53660786Sps * Oops. We've probably lost the char which 53760786Sps * was in pendc, since caller won't back up. 53860786Sps */ 53960786Sps return (1); 54060786Sps pendc = '\0'; 54160786Sps } 54260786Sps 54360786Sps if (c == '\r' && bs_mode == BS_SPECIAL) 54460786Sps { 54560786Sps /* 54660786Sps * Don't put the CR into the buffer until we see 54760786Sps * the next char. If the next char is a newline, 54860786Sps * discard the CR. 54960786Sps */ 55060786Sps pendc = c; 55160786Sps pendpos = pos; 55260786Sps return (0); 55360786Sps } 55460786Sps 55560786Sps r = do_append(c, pos); 55660786Sps /* 55760786Sps * If we need to shift the line, do it. 55860786Sps * But wait until we get to at least the middle of the screen, 55960786Sps * so shifting it doesn't affect the chars we're currently 56060786Sps * pappending. (Bold & underline can get messed up otherwise.) 56160786Sps */ 56260786Sps if (cshift < hshift && column > sc_width / 2) 56389019Sps { 56489019Sps linebuf[curr] = '\0'; 56560786Sps pshift(hshift - cshift); 56689019Sps } 56760786Sps return (r); 56860786Sps} 56960786Sps 570128345Stjr#define IS_UTF8_4BYTE(c) ( ((c) & 0xf8) == 0xf0 ) 571128345Stjr#define IS_UTF8_3BYTE(c) ( ((c) & 0xf0) == 0xe0 ) 572128345Stjr#define IS_UTF8_2BYTE(c) ( ((c) & 0xe0) == 0xc0 ) 573128345Stjr#define IS_UTF8_TRAIL(c) ( ((c) & 0xc0) == 0x80 ) 574128345Stjr 57560786Sps static int 57660786Spsdo_append(c, pos) 57760786Sps int c; 57860786Sps POSITION pos; 57960786Sps{ 58060786Sps register char *s; 58160786Sps register int a; 58260786Sps 58389019Sps#define STOREC(c,a) \ 58489019Sps if ((c) == '\t') STORE_TAB((a),pos); else STORE_CHAR((c),(a),pos) 58560786Sps 58660786Sps if (c == '\b') 58760786Sps { 58860786Sps switch (bs_mode) 58960786Sps { 59060786Sps case BS_NORMAL: 59189019Sps STORE_CHAR(c, AT_NORMAL, pos); 59260786Sps break; 59360786Sps case BS_CONTROL: 59460786Sps goto do_control_char; 59560786Sps case BS_SPECIAL: 59660786Sps if (curr == 0) 59760786Sps break; 59860786Sps backc(); 59960786Sps overstrike = 1; 60060786Sps break; 60160786Sps } 60260786Sps } else if (overstrike) 60360786Sps { 60460786Sps /* 60560786Sps * Overstrike the character at the current position 60660786Sps * in the line buffer. This will cause either 60760786Sps * underline (if a "_" is overstruck), 60860786Sps * bold (if an identical character is overstruck), 60960786Sps * or just deletion of the character in the buffer. 61060786Sps */ 61189019Sps overstrike--; 612128345Stjr if (utf_mode && IS_UTF8_4BYTE(c) && curr > 2 && (char)c == linebuf[curr-3]) 61389019Sps { 61489019Sps backc(); 61589019Sps backc(); 616128345Stjr backc(); 617128345Stjr STORE_CHAR(linebuf[curr], AT_BOLD, pos); 618128345Stjr overstrike = 3; 619128345Stjr } else if (utf_mode && (IS_UTF8_3BYTE(c) || (overstrike==2 && IS_UTF8_TRAIL(c))) && curr > 1 && (char)c == linebuf[curr-2]) 620128345Stjr { 621128345Stjr backc(); 622128345Stjr backc(); 623128345Stjr STORE_CHAR(linebuf[curr], AT_BOLD, pos); 62489019Sps overstrike = 2; 625128345Stjr } else if (utf_mode && curr > 0 && (IS_UTF8_2BYTE(c) || (overstrike==1 && IS_UTF8_TRAIL(c))) && (char)c == linebuf[curr-1]) 62689019Sps { 62789019Sps backc(); 62889019Sps STORE_CHAR(linebuf[curr], AT_BOLD, pos); 62989019Sps overstrike = 1; 630128345Stjr } else if (utf_mode && curr > 0 && IS_UTF8_TRAIL(c) && attr[curr-1] == AT_UNDERLINE) 631128345Stjr { 632128345Stjr STOREC(c, AT_UNDERLINE); 63389019Sps } else if ((char)c == linebuf[curr]) 63489019Sps { 635128345Stjr /* 636128345Stjr * Overstriking a char with itself means make it bold. 637128345Stjr * But overstriking an underscore with itself is 638128345Stjr * ambiguous. It could mean make it bold, or 639128345Stjr * it could mean make it underlined. 640128345Stjr * Use the previous overstrike to resolve it. 641128345Stjr */ 642128345Stjr if (c == '_' && last_overstrike != AT_NORMAL) 643128345Stjr STOREC(c, last_overstrike); 644128345Stjr else 645128345Stjr STOREC(c, AT_BOLD); 64689019Sps } else if (c == '_') 64789019Sps { 64889019Sps if (utf_mode) 64989019Sps { 650128345Stjr int i; 651128345Stjr for (i = 0; i < 5; i++) 652128345Stjr { 653128345Stjr if (curr <= i || !IS_CONT(linebuf[curr-i])) 654128345Stjr break; 655128345Stjr attr[curr-i-1] = AT_UNDERLINE; 656128345Stjr } 65789019Sps } 65860786Sps STOREC(linebuf[curr], AT_UNDERLINE); 65989019Sps } else if (linebuf[curr] == '_') 66089019Sps { 661128345Stjr if (utf_mode) 662128345Stjr { 663128345Stjr if (IS_UTF8_2BYTE(c)) 664128345Stjr overstrike = 1; 665128345Stjr else if (IS_UTF8_3BYTE(c)) 666128345Stjr overstrike = 2; 667128345Stjr else if (IS_UTF8_4BYTE(c)) 668128345Stjr overstrike = 3; 669128345Stjr } 67060786Sps STOREC(c, AT_UNDERLINE); 67189019Sps } else if (control_char(c)) 67260786Sps goto do_control_char; 67360786Sps else 67460786Sps STOREC(c, AT_NORMAL); 67560786Sps } else if (c == '\t') 67660786Sps { 67760786Sps /* 67860786Sps * Expand a tab into spaces. 67960786Sps */ 68060786Sps switch (bs_mode) 68160786Sps { 68260786Sps case BS_CONTROL: 68360786Sps goto do_control_char; 68460786Sps case BS_NORMAL: 68560786Sps case BS_SPECIAL: 68689019Sps STORE_TAB(AT_NORMAL, pos); 68760786Sps break; 68860786Sps } 68960786Sps } else if (control_char(c)) 69060786Sps { 69160786Sps do_control_char: 69260786Sps if (ctldisp == OPT_ON || (ctldisp == OPT_ONPLUS && c == ESC)) 69360786Sps { 69460786Sps /* 69560786Sps * Output as a normal character. 69660786Sps */ 69789019Sps STORE_CHAR(c, AT_NORMAL, pos); 69860786Sps } else 69960786Sps { 70060786Sps /* 70160786Sps * Convert to printable representation. 70260786Sps */ 70360786Sps s = prchar(c); 70460786Sps a = binattr; 70560786Sps 70660786Sps /* 70760786Sps * Make sure we can get the entire representation 70860786Sps * of the character on this line. 70960786Sps */ 71060786Sps if (column + (int) strlen(s) + 71160786Sps attr_swidth(a) + attr_ewidth(a) > sc_width) 71260786Sps return (1); 71360786Sps 71460786Sps for ( ; *s != 0; s++) 71589019Sps STORE_CHAR(*s, a, pos); 71660786Sps } 71760786Sps } else 71860786Sps { 71960786Sps STOREC(c, AT_NORMAL); 72060786Sps } 72160786Sps 72260786Sps return (0); 72360786Sps} 72460786Sps 72560786Sps/* 72660786Sps * Terminate the line in the line buffer. 72760786Sps */ 72860786Sps public void 72960786Spspdone(endline) 73060786Sps int endline; 73160786Sps{ 73260786Sps if (pendc && (pendc != '\r' || !endline)) 73360786Sps /* 73460786Sps * If we had a pending character, put it in the buffer. 73560786Sps * But discard a pending CR if we are at end of line 73660786Sps * (that is, discard the CR in a CR/LF sequence). 73760786Sps */ 73860786Sps (void) do_append(pendc, pendpos); 73960786Sps 74060786Sps /* 74160786Sps * Make sure we've shifted the line, if we need to. 74260786Sps */ 74360786Sps if (cshift < hshift) 74460786Sps pshift(hshift - cshift); 74560786Sps 74660786Sps /* 74760786Sps * Add a newline if necessary, 74860786Sps * and append a '\0' to the end of the line. 74960786Sps */ 75060786Sps if (column < sc_width || !auto_wrap || ignaw || ctldisp == OPT_ON) 75160786Sps { 75260786Sps linebuf[curr] = '\n'; 75360786Sps attr[curr] = AT_NORMAL; 75460786Sps curr++; 75560786Sps } 75660786Sps linebuf[curr] = '\0'; 75760786Sps attr[curr] = AT_NORMAL; 75889019Sps 75989019Sps#if HILITE_SEARCH 76089019Sps if (status_col && hilites > 0) 76189019Sps { 76289019Sps linebuf[0] = '*'; 76389019Sps attr[0] = AT_STANDOUT; 76489019Sps } 76589019Sps#endif 76660786Sps /* 76760786Sps * If we are done with this line, reset the current shift. 76860786Sps */ 76960786Sps if (endline) 77060786Sps cshift = 0; 77160786Sps} 77260786Sps 77360786Sps/* 77460786Sps * Get a character from the current line. 77560786Sps * Return the character as the function return value, 77660786Sps * and the character attribute in *ap. 77760786Sps */ 77860786Sps public int 77960786Spsgline(i, ap) 78060786Sps register int i; 78160786Sps register int *ap; 78260786Sps{ 78360786Sps char *s; 78460786Sps 78560786Sps if (is_null_line) 78660786Sps { 78760786Sps /* 78860786Sps * If there is no current line, we pretend the line is 78960786Sps * either "~" or "", depending on the "twiddle" flag. 79060786Sps */ 79160786Sps *ap = AT_BOLD; 79260786Sps s = (twiddle) ? "~\n" : "\n"; 79360786Sps return (s[i]); 79460786Sps } 79560786Sps 79660786Sps *ap = attr[i]; 79760786Sps return (linebuf[i] & 0377); 79860786Sps} 79960786Sps 80060786Sps/* 80160786Sps * Indicate that there is no current line. 80260786Sps */ 80360786Sps public void 80460786Spsnull_line() 80560786Sps{ 80660786Sps is_null_line = 1; 80760786Sps cshift = 0; 80860786Sps} 80960786Sps 81060786Sps/* 81160786Sps * Analogous to forw_line(), but deals with "raw lines": 81260786Sps * lines which are not split for screen width. 81360786Sps * {{ This is supposed to be more efficient than forw_line(). }} 81460786Sps */ 81560786Sps public POSITION 81660786Spsforw_raw_line(curr_pos, linep) 81760786Sps POSITION curr_pos; 81860786Sps char **linep; 81960786Sps{ 82089019Sps register int n; 82160786Sps register int c; 82260786Sps POSITION new_pos; 82360786Sps 82460786Sps if (curr_pos == NULL_POSITION || ch_seek(curr_pos) || 82560786Sps (c = ch_forw_get()) == EOI) 82660786Sps return (NULL_POSITION); 82760786Sps 82889019Sps n = 0; 82960786Sps for (;;) 83060786Sps { 83160786Sps if (c == '\n' || c == EOI) 83260786Sps { 83360786Sps new_pos = ch_tell(); 83460786Sps break; 83560786Sps } 83689019Sps if (n >= size_linebuf-1) 83760786Sps { 83889019Sps if (expand_linebuf()) 83989019Sps { 84089019Sps /* 84189019Sps * Overflowed the input buffer. 84289019Sps * Pretend the line ended here. 84389019Sps */ 84489019Sps new_pos = ch_tell() - 1; 84589019Sps break; 84689019Sps } 84760786Sps } 84889019Sps linebuf[n++] = c; 84960786Sps c = ch_forw_get(); 85060786Sps } 85189019Sps linebuf[n] = '\0'; 85260786Sps if (linep != NULL) 85360786Sps *linep = linebuf; 85460786Sps return (new_pos); 85560786Sps} 85660786Sps 85760786Sps/* 85860786Sps * Analogous to back_line(), but deals with "raw lines". 85960786Sps * {{ This is supposed to be more efficient than back_line(). }} 86060786Sps */ 86160786Sps public POSITION 86260786Spsback_raw_line(curr_pos, linep) 86360786Sps POSITION curr_pos; 86460786Sps char **linep; 86560786Sps{ 86689019Sps register int n; 86760786Sps register int c; 86860786Sps POSITION new_pos; 86960786Sps 87060786Sps if (curr_pos == NULL_POSITION || curr_pos <= ch_zero() || 87160786Sps ch_seek(curr_pos-1)) 87260786Sps return (NULL_POSITION); 87360786Sps 87489019Sps n = size_linebuf; 87589019Sps linebuf[--n] = '\0'; 87660786Sps for (;;) 87760786Sps { 87860786Sps c = ch_back_get(); 87960786Sps if (c == '\n') 88060786Sps { 88160786Sps /* 88260786Sps * This is the newline ending the previous line. 88360786Sps * We have hit the beginning of the line. 88460786Sps */ 88560786Sps new_pos = ch_tell() + 1; 88660786Sps break; 88760786Sps } 88860786Sps if (c == EOI) 88960786Sps { 89060786Sps /* 89160786Sps * We have hit the beginning of the file. 89260786Sps * This must be the first line in the file. 89360786Sps * This must, of course, be the beginning of the line. 89460786Sps */ 89560786Sps new_pos = ch_zero(); 89660786Sps break; 89760786Sps } 89889019Sps if (n <= 0) 89960786Sps { 90089019Sps int old_size_linebuf = size_linebuf; 90189019Sps char *fm; 90289019Sps char *to; 90389019Sps if (expand_linebuf()) 90489019Sps { 90589019Sps /* 90689019Sps * Overflowed the input buffer. 90789019Sps * Pretend the line ended here. 90889019Sps */ 90989019Sps new_pos = ch_tell() + 1; 91089019Sps break; 91189019Sps } 91260786Sps /* 91389019Sps * Shift the data to the end of the new linebuf. 91460786Sps */ 915149487Stjr for (fm = linebuf + old_size_linebuf - 1, 916149487Stjr to = linebuf + size_linebuf - 1; 91789019Sps fm >= linebuf; fm--, to--) 91889019Sps *to = *fm; 91989019Sps n = size_linebuf - old_size_linebuf; 92060786Sps } 92189019Sps linebuf[--n] = c; 92260786Sps } 92360786Sps if (linep != NULL) 92489019Sps *linep = &linebuf[n]; 92560786Sps return (new_pos); 92660786Sps} 927