1238730Sdelphij/* 2238730Sdelphij * Copyright (C) 1984-2012 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/* 1260786Sps * High level routines dealing with the output to the screen. 1360786Sps */ 1460786Sps 1560786Sps#include "less.h" 1660786Sps#if MSDOS_COMPILER==WIN32C 1760786Sps#include "windows.h" 1860786Sps#endif 1960786Sps 2060786Spspublic int errmsgs; /* Count of messages displayed by error() */ 2160786Spspublic int need_clr; 2260786Spspublic int final_attr; 23170256Sdelphijpublic int at_prompt; 2460786Sps 2560786Spsextern int sigs; 2660786Spsextern int sc_width; 2760786Spsextern int so_s_width, so_e_width; 2860786Spsextern int screen_trashed; 2960786Spsextern int any_display; 3060786Spsextern int is_tty; 31170256Sdelphijextern int oldbot; 3260786Sps 33191930Sdelphij#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 34128345Stjrextern int ctldisp; 35128345Stjrextern int nm_fg_color, nm_bg_color; 36128345Stjrextern int bo_fg_color, bo_bg_color; 37128345Stjrextern int ul_fg_color, ul_bg_color; 38128345Stjrextern int so_fg_color, so_bg_color; 39128345Stjrextern int bl_fg_color, bl_bg_color; 40128345Stjr#endif 41128345Stjr 4260786Sps/* 4360786Sps * Display the line which is in the line buffer. 4460786Sps */ 4560786Sps public void 4660786Spsput_line() 4760786Sps{ 4860786Sps register int c; 4960786Sps register int i; 5060786Sps int a; 5160786Sps 5260786Sps if (ABORT_SIGS()) 5360786Sps { 5460786Sps /* 5560786Sps * Don't output if a signal is pending. 5660786Sps */ 5760786Sps screen_trashed = 1; 5860786Sps return; 5960786Sps } 6060786Sps 61161475Sdelphij final_attr = AT_NORMAL; 6260786Sps 6360786Sps for (i = 0; (c = gline(i, &a)) != '\0'; i++) 6460786Sps { 65161475Sdelphij at_switch(a); 66161475Sdelphij final_attr = a; 6760786Sps if (c == '\b') 6860786Sps putbs(); 6960786Sps else 7060786Sps putchr(c); 7160786Sps } 7260786Sps 73161475Sdelphij at_exit(); 7460786Sps} 7560786Sps 7660786Spsstatic char obuf[OUTBUF_SIZE]; 7760786Spsstatic char *ob = obuf; 7860786Sps 7960786Sps/* 8060786Sps * Flush buffered output. 8160786Sps * 8260786Sps * If we haven't displayed any file data yet, 8360786Sps * output messages on error output (file descriptor 2), 8460786Sps * otherwise output on standard output (file descriptor 1). 8560786Sps * 8660786Sps * This has the desirable effect of producing all 8760786Sps * error messages on error output if standard output 8860786Sps * is directed to a file. It also does the same if 8960786Sps * we never produce any real output; for example, if 9060786Sps * the input file(s) cannot be opened. If we do 9160786Sps * eventually produce output, code in edit() makes 9260786Sps * sure these messages can be seen before they are 9360786Sps * overwritten or scrolled away. 9460786Sps */ 9560786Sps public void 9660786Spsflush() 9760786Sps{ 9860786Sps register int n; 9960786Sps register int fd; 10060786Sps 10160786Sps n = ob - obuf; 10260786Sps if (n == 0) 10360786Sps return; 10460786Sps 10560786Sps#if MSDOS_COMPILER==MSOFTC 10660786Sps if (is_tty && any_display) 10760786Sps { 10860786Sps *ob = '\0'; 10960786Sps _outtext(obuf); 11060786Sps ob = obuf; 11160786Sps return; 11260786Sps } 11360786Sps#else 114191930Sdelphij#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 11560786Sps if (is_tty && any_display) 11660786Sps { 11760786Sps *ob = '\0'; 118128345Stjr if (ctldisp != OPT_ONPLUS) 119191930Sdelphij WIN32textout(obuf, ob - obuf); 120128345Stjr else 121128345Stjr { 122128345Stjr /* 123128345Stjr * Look for SGR escape sequences, and convert them 124128345Stjr * to color commands. Replace bold, underline, 125128345Stjr * and italic escapes into colors specified via 126128345Stjr * the -D command-line option. 127128345Stjr */ 128128345Stjr char *anchor, *p, *p_next; 129191930Sdelphij unsigned char fg, bg; 130191930Sdelphij static unsigned char at; 131191930Sdelphij#if MSDOS_COMPILER==WIN32C 132191930Sdelphij /* Screen colors used by 3x and 4x SGR commands. */ 133191930Sdelphij static unsigned char screen_color[] = { 134191930Sdelphij 0, /* BLACK */ 135191930Sdelphij FOREGROUND_RED, 136191930Sdelphij FOREGROUND_GREEN, 137191930Sdelphij FOREGROUND_RED|FOREGROUND_GREEN, 138191930Sdelphij FOREGROUND_BLUE, 139191930Sdelphij FOREGROUND_BLUE|FOREGROUND_RED, 140191930Sdelphij FOREGROUND_BLUE|FOREGROUND_GREEN, 141191930Sdelphij FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED 142191930Sdelphij }; 143191930Sdelphij#else 144128345Stjr static enum COLORS screen_color[] = { 145128345Stjr BLACK, RED, GREEN, BROWN, 146128345Stjr BLUE, MAGENTA, CYAN, LIGHTGRAY 147128345Stjr }; 148191930Sdelphij#endif 149128345Stjr 150128345Stjr for (anchor = p_next = obuf; 151191930Sdelphij (p_next = memchr(p_next, ESC, ob - p_next)) != NULL; ) 152128345Stjr { 153128345Stjr p = p_next; 154191930Sdelphij if (p[1] == '[') /* "ESC-[" sequence */ 155128345Stjr { 156128345Stjr if (p > anchor) 157128345Stjr { 158191930Sdelphij /* 159191930Sdelphij * If some chars seen since 160191930Sdelphij * the last escape sequence, 161191930Sdelphij * write them out to the screen. 162191930Sdelphij */ 163191930Sdelphij WIN32textout(anchor, p-anchor); 164128345Stjr anchor = p; 165128345Stjr } 166191930Sdelphij p += 2; /* Skip the "ESC-[" */ 167191930Sdelphij if (is_ansi_end(*p)) 168191930Sdelphij { 169191930Sdelphij /* 170191930Sdelphij * Handle null escape sequence 171191930Sdelphij * "ESC[m", which restores 172191930Sdelphij * the normal color. 173191930Sdelphij */ 174191930Sdelphij p++; 175191930Sdelphij anchor = p_next = p; 176237613Sdelphij at = 0; 177191930Sdelphij WIN32setcolors(nm_fg_color, nm_bg_color); 178191930Sdelphij continue; 179191930Sdelphij } 180128345Stjr p_next = p; 181191930Sdelphij 182191930Sdelphij /* 183191930Sdelphij * Select foreground/background colors 184191930Sdelphij * based on the escape sequence. 185191930Sdelphij */ 186191930Sdelphij fg = nm_fg_color; 187191930Sdelphij bg = nm_bg_color; 188128345Stjr while (!is_ansi_end(*p)) 189128345Stjr { 190128345Stjr char *q; 191128345Stjr long code = strtol(p, &q, 10); 192128345Stjr 193191930Sdelphij if (*q == '\0') 194128345Stjr { 195128345Stjr /* 196128345Stjr * Incomplete sequence. 197128345Stjr * Leave it unprocessed 198128345Stjr * in the buffer. 199128345Stjr */ 200128345Stjr int slop = q - anchor; 201191930Sdelphij /* {{ strcpy args overlap! }} */ 202128345Stjr strcpy(obuf, anchor); 203128345Stjr ob = &obuf[slop]; 204128345Stjr return; 205128345Stjr } 206128345Stjr 207191930Sdelphij if (q == p || 208191930Sdelphij code > 49 || code < 0 || 209191930Sdelphij (!is_ansi_end(*q) && *q != ';')) 210128345Stjr { 211128345Stjr p_next = q; 212128345Stjr break; 213128345Stjr } 214128345Stjr if (*q == ';') 215128345Stjr q++; 216128345Stjr 217128345Stjr switch (code) 218128345Stjr { 219191930Sdelphij default: 220191930Sdelphij /* case 0: all attrs off */ 221191930Sdelphij fg = nm_fg_color; 222191930Sdelphij bg = nm_bg_color; 223191930Sdelphij at = 0; 224191930Sdelphij break; 225128345Stjr case 1: /* bold on */ 226191930Sdelphij at |= 1; 227128345Stjr break; 228128345Stjr case 3: /* italic on */ 229191930Sdelphij case 7: /* inverse on */ 230191930Sdelphij at |= 2; 231128345Stjr break; 232128345Stjr case 4: /* underline on */ 233191930Sdelphij at |= 4; 234128345Stjr break; 235191930Sdelphij case 5: /* slow blink on */ 236191930Sdelphij case 6: /* fast blink on */ 237191930Sdelphij at |= 8; 238191930Sdelphij break; 239128345Stjr case 8: /* concealed on */ 240128345Stjr fg = (bg & 7) | 8; 241128345Stjr break; 242191930Sdelphij case 22: /* bold off */ 243191930Sdelphij at &= ~1; 244128345Stjr break; 245191930Sdelphij case 23: /* italic off */ 246191930Sdelphij case 27: /* inverse off */ 247191930Sdelphij at &= ~2; 248191930Sdelphij break; 249191930Sdelphij case 24: /* underline off */ 250191930Sdelphij at &= ~4; 251191930Sdelphij break; 252128345Stjr case 30: case 31: case 32: 253128345Stjr case 33: case 34: case 35: 254128345Stjr case 36: case 37: 255128345Stjr fg = (fg & 8) | (screen_color[code - 30]); 256128345Stjr break; 257128345Stjr case 39: /* default fg */ 258128345Stjr fg = nm_fg_color; 259128345Stjr break; 260128345Stjr case 40: case 41: case 42: 261128345Stjr case 43: case 44: case 45: 262128345Stjr case 46: case 47: 263128345Stjr bg = (bg & 8) | (screen_color[code - 40]); 264128345Stjr break; 265128345Stjr case 49: /* default fg */ 266128345Stjr bg = nm_bg_color; 267128345Stjr break; 268128345Stjr } 269128345Stjr p = q; 270128345Stjr } 271191930Sdelphij if (!is_ansi_end(*p) || p == p_next) 272191930Sdelphij break; 273191930Sdelphij if (at & 1) 274128345Stjr { 275238730Sdelphij /* 276238730Sdelphij * If \e[1m use defined bold 277238730Sdelphij * color, else set intensity. 278238730Sdelphij */ 279238730Sdelphij if (p[-2] == '[') 280238730Sdelphij { 281242584Sdelphij#if MSDOS_COMPILER==WIN32C 282242584Sdelphij fg |= FOREGROUND_INTENSITY; 283242584Sdelphij bg |= BACKGROUND_INTENSITY; 284242584Sdelphij#else 285238730Sdelphij fg = bo_fg_color; 286238730Sdelphij bg = bo_bg_color; 287242584Sdelphij#endif 288238730Sdelphij } else 289238730Sdelphij fg |= 8; 290191930Sdelphij } else if (at & 2) 291191930Sdelphij { 292237613Sdelphij fg = so_fg_color; 293237613Sdelphij bg = so_bg_color; 294191930Sdelphij } else if (at & 4) 295191930Sdelphij { 296237613Sdelphij fg = ul_fg_color; 297237613Sdelphij bg = ul_bg_color; 298191930Sdelphij } else if (at & 8) 299191930Sdelphij { 300237613Sdelphij fg = bl_fg_color; 301237613Sdelphij bg = bl_bg_color; 302191930Sdelphij } 303191930Sdelphij fg &= 0xf; 304191930Sdelphij bg &= 0xf; 305191930Sdelphij WIN32setcolors(fg, bg); 306191930Sdelphij p_next = anchor = p + 1; 307128345Stjr } else 308128345Stjr p_next++; 309128345Stjr } 310128345Stjr 311128345Stjr /* Output what's left in the buffer. */ 312191930Sdelphij WIN32textout(anchor, ob - anchor); 313128345Stjr } 31460786Sps ob = obuf; 31560786Sps return; 31660786Sps } 31760786Sps#endif 31860786Sps#endif 31960786Sps fd = (any_display) ? 1 : 2; 32060786Sps if (write(fd, obuf, n) != n) 32160786Sps screen_trashed = 1; 32260786Sps ob = obuf; 32360786Sps} 32460786Sps 32560786Sps/* 32660786Sps * Output a character. 32760786Sps */ 32860786Sps public int 32960786Spsputchr(c) 33060786Sps int c; 33160786Sps{ 332161475Sdelphij#if 0 /* fake UTF-8 output for testing */ 333161475Sdelphij extern int utf_mode; 334161475Sdelphij if (utf_mode) 335161475Sdelphij { 336161475Sdelphij static char ubuf[MAX_UTF_CHAR_LEN]; 337161475Sdelphij static int ubuf_len = 0; 338161475Sdelphij static int ubuf_index = 0; 339161475Sdelphij if (ubuf_len == 0) 340161475Sdelphij { 341161475Sdelphij ubuf_len = utf_len(c); 342161475Sdelphij ubuf_index = 0; 343161475Sdelphij } 344161475Sdelphij ubuf[ubuf_index++] = c; 345161475Sdelphij if (ubuf_index < ubuf_len) 346161475Sdelphij return c; 347161475Sdelphij c = get_wchar(ubuf) & 0xFF; 348161475Sdelphij ubuf_len = 0; 349161475Sdelphij } 350161475Sdelphij#endif 35160786Sps if (need_clr) 35260786Sps { 35360786Sps need_clr = 0; 35460786Sps clear_bot(); 35560786Sps } 35660786Sps#if MSDOS_COMPILER 35760786Sps if (c == '\n' && is_tty) 35860786Sps { 35960786Sps /* remove_top(1); */ 36060786Sps putchr('\r'); 36160786Sps } 36260786Sps#else 36360786Sps#ifdef _OSK 36460786Sps if (c == '\n' && is_tty) /* In OS-9, '\n' == 0x0D */ 36560786Sps putchr(0x0A); 36660786Sps#endif 36760786Sps#endif 36860786Sps /* 36960786Sps * Some versions of flush() write to *ob, so we must flush 37060786Sps * when we are still one char from the end of obuf. 37160786Sps */ 37260786Sps if (ob >= &obuf[sizeof(obuf)-1]) 37360786Sps flush(); 37460786Sps *ob++ = c; 375170256Sdelphij at_prompt = 0; 37660786Sps return (c); 37760786Sps} 37860786Sps 37960786Sps/* 38060786Sps * Output a string. 38160786Sps */ 38260786Sps public void 38360786Spsputstr(s) 38460786Sps register char *s; 38560786Sps{ 38660786Sps while (*s != '\0') 38760786Sps putchr(*s++); 38860786Sps} 38960786Sps 39060786Sps 39160786Sps/* 392128345Stjr * Convert an integral type to a string. 393128345Stjr */ 394128345Stjr#define TYPE_TO_A_FUNC(funcname, type) \ 395128345Stjrvoid funcname(num, buf) \ 396128345Stjr type num; \ 397128345Stjr char *buf; \ 398128345Stjr{ \ 399128345Stjr int neg = (num < 0); \ 400128345Stjr char tbuf[INT_STRLEN_BOUND(num)+2]; \ 401128345Stjr register char *s = tbuf + sizeof(tbuf); \ 402128345Stjr if (neg) num = -num; \ 403128345Stjr *--s = '\0'; \ 404128345Stjr do { \ 405128345Stjr *--s = (num % 10) + '0'; \ 406128345Stjr } while ((num /= 10) != 0); \ 407128345Stjr if (neg) *--s = '-'; \ 408128345Stjr strcpy(buf, s); \ 409128345Stjr} 410128345Stjr 411128345StjrTYPE_TO_A_FUNC(postoa, POSITION) 412128345StjrTYPE_TO_A_FUNC(linenumtoa, LINENUM) 413128345StjrTYPE_TO_A_FUNC(inttoa, int) 414128345Stjr 415128345Stjr/* 41660786Sps * Output an integer in a given radix. 41760786Sps */ 41860786Sps static int 419128345Stjriprint_int(num) 42060786Sps int num; 42160786Sps{ 42289019Sps char buf[INT_STRLEN_BOUND(num)]; 42360786Sps 424128345Stjr inttoa(num, buf); 425128345Stjr putstr(buf); 426128345Stjr return (strlen(buf)); 427128345Stjr} 42860786Sps 429128345Stjr/* 430128345Stjr * Output a line number in a given radix. 431128345Stjr */ 432128345Stjr static int 433128345Stjriprint_linenum(num) 434128345Stjr LINENUM num; 435128345Stjr{ 436128345Stjr char buf[INT_STRLEN_BOUND(num)]; 43760786Sps 438128345Stjr linenumtoa(num, buf); 439128345Stjr putstr(buf); 440128345Stjr return (strlen(buf)); 44160786Sps} 44260786Sps 44360786Sps/* 44460786Sps * This function implements printf-like functionality 44560786Sps * using a more portable argument list mechanism than printf's. 44660786Sps */ 44760786Sps static int 44860786Spsless_printf(fmt, parg) 44960786Sps register char *fmt; 45060786Sps PARG *parg; 45160786Sps{ 45260786Sps register char *s; 45360786Sps register int col; 45460786Sps 45560786Sps col = 0; 45660786Sps while (*fmt != '\0') 45760786Sps { 45860786Sps if (*fmt != '%') 45960786Sps { 46060786Sps putchr(*fmt++); 46160786Sps col++; 46260786Sps } else 46360786Sps { 46460786Sps ++fmt; 465128345Stjr switch (*fmt++) 466128345Stjr { 46760786Sps case 's': 46860786Sps s = parg->p_string; 46960786Sps parg++; 47060786Sps while (*s != '\0') 47160786Sps { 47260786Sps putchr(*s++); 47360786Sps col++; 47460786Sps } 47560786Sps break; 47660786Sps case 'd': 477128345Stjr col += iprint_int(parg->p_int); 47860786Sps parg++; 47960786Sps break; 480128345Stjr case 'n': 481128345Stjr col += iprint_linenum(parg->p_linenum); 482128345Stjr parg++; 483128345Stjr break; 48460786Sps } 48560786Sps } 48660786Sps } 48760786Sps return (col); 48860786Sps} 48960786Sps 49060786Sps/* 49160786Sps * Get a RETURN. 49260786Sps * If some other non-trivial char is pressed, unget it, so it will 49360786Sps * become the next command. 49460786Sps */ 49560786Sps public void 49660786Spsget_return() 49760786Sps{ 49860786Sps int c; 49960786Sps 50060786Sps#if ONLY_RETURN 50160786Sps while ((c = getchr()) != '\n' && c != '\r') 50260786Sps bell(); 50360786Sps#else 50460786Sps c = getchr(); 50560786Sps if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR) 50660786Sps ungetcc(c); 50760786Sps#endif 50860786Sps} 50960786Sps 51060786Sps/* 51160786Sps * Output a message in the lower left corner of the screen 51260786Sps * and wait for carriage return. 51360786Sps */ 51460786Sps public void 51560786Spserror(fmt, parg) 51660786Sps char *fmt; 51760786Sps PARG *parg; 51860786Sps{ 51960786Sps int col = 0; 52060786Sps static char return_to_continue[] = " (press RETURN)"; 52160786Sps 52260786Sps errmsgs++; 52360786Sps 52460786Sps if (any_display && is_tty) 52560786Sps { 526170256Sdelphij if (!oldbot) 527170256Sdelphij squish_check(); 528161475Sdelphij at_exit(); 52960786Sps clear_bot(); 530161475Sdelphij at_enter(AT_STANDOUT); 53160786Sps col += so_s_width; 53260786Sps } 53360786Sps 53460786Sps col += less_printf(fmt, parg); 53560786Sps 53660786Sps if (!(any_display && is_tty)) 53760786Sps { 53860786Sps putchr('\n'); 53960786Sps return; 54060786Sps } 54160786Sps 54260786Sps putstr(return_to_continue); 543161475Sdelphij at_exit(); 54460786Sps col += sizeof(return_to_continue) + so_e_width; 54560786Sps 54660786Sps get_return(); 54760786Sps lower_left(); 548221715Sdelphij clear_eol(); 54960786Sps 55060786Sps if (col >= sc_width) 55160786Sps /* 55260786Sps * Printing the message has probably scrolled the screen. 55360786Sps * {{ Unless the terminal doesn't have auto margins, 55460786Sps * in which case we just hammered on the right margin. }} 55560786Sps */ 55660786Sps screen_trashed = 1; 55760786Sps 55860786Sps flush(); 55960786Sps} 56060786Sps 56160786Spsstatic char intr_to_abort[] = "... (interrupt to abort)"; 56260786Sps 56360786Sps/* 56460786Sps * Output a message in the lower left corner of the screen 56560786Sps * and don't wait for carriage return. 56660786Sps * Usually used to warn that we are beginning a potentially 56760786Sps * time-consuming operation. 56860786Sps */ 56960786Sps public void 57060786Spsierror(fmt, parg) 57160786Sps char *fmt; 57260786Sps PARG *parg; 57360786Sps{ 574161475Sdelphij at_exit(); 57560786Sps clear_bot(); 576161475Sdelphij at_enter(AT_STANDOUT); 57760786Sps (void) less_printf(fmt, parg); 57860786Sps putstr(intr_to_abort); 579161475Sdelphij at_exit(); 58060786Sps flush(); 58160786Sps need_clr = 1; 58260786Sps} 58360786Sps 58460786Sps/* 58560786Sps * Output a message in the lower left corner of the screen 58660786Sps * and return a single-character response. 58760786Sps */ 58860786Sps public int 58960786Spsquery(fmt, parg) 59060786Sps char *fmt; 59160786Sps PARG *parg; 59260786Sps{ 59360786Sps register int c; 59460786Sps int col = 0; 59560786Sps 59660786Sps if (any_display && is_tty) 59760786Sps clear_bot(); 59860786Sps 59960786Sps (void) less_printf(fmt, parg); 60060786Sps c = getchr(); 60160786Sps 60260786Sps if (!(any_display && is_tty)) 60360786Sps { 60460786Sps putchr('\n'); 60560786Sps return (c); 60660786Sps } 60760786Sps 60860786Sps lower_left(); 60960786Sps if (col >= sc_width) 61060786Sps screen_trashed = 1; 61160786Sps flush(); 61260786Sps 61360786Sps return (c); 61460786Sps} 615