output.c revision 191930
160786Sps/* 2191930Sdelphij * Copyright (C) 1984-2008 Mark Nudelman 360786Sps * 460786Sps * You may distribute under the terms of either the GNU General Public 560786Sps * License or the Less License, as specified in the README file. 660786Sps * 760786Sps * For more information about less, or for information on how to 860786Sps * contact the author, see the README file. 960786Sps */ 1060786Sps 1160786Sps 1260786Sps/* 1360786Sps * High level routines dealing with the output to the screen. 1460786Sps */ 1560786Sps 1660786Sps#include "less.h" 1760786Sps#if MSDOS_COMPILER==WIN32C 1860786Sps#include "windows.h" 1960786Sps#endif 2060786Sps 2160786Spspublic int errmsgs; /* Count of messages displayed by error() */ 2260786Spspublic int need_clr; 2360786Spspublic int final_attr; 24170256Sdelphijpublic int at_prompt; 2560786Sps 2660786Spsextern int sigs; 2760786Spsextern int sc_width; 2860786Spsextern int so_s_width, so_e_width; 2960786Spsextern int screen_trashed; 3060786Spsextern int any_display; 3160786Spsextern int is_tty; 32170256Sdelphijextern int oldbot; 3360786Sps 34191930Sdelphij#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 35128345Stjrextern int ctldisp; 36128345Stjrextern int nm_fg_color, nm_bg_color; 37128345Stjrextern int bo_fg_color, bo_bg_color; 38128345Stjrextern int ul_fg_color, ul_bg_color; 39128345Stjrextern int so_fg_color, so_bg_color; 40128345Stjrextern int bl_fg_color, bl_bg_color; 41128345Stjr#endif 42128345Stjr 4360786Sps/* 4460786Sps * Display the line which is in the line buffer. 4560786Sps */ 4660786Sps public void 4760786Spsput_line() 4860786Sps{ 4960786Sps register int c; 5060786Sps register int i; 5160786Sps int a; 5260786Sps 5360786Sps if (ABORT_SIGS()) 5460786Sps { 5560786Sps /* 5660786Sps * Don't output if a signal is pending. 5760786Sps */ 5860786Sps screen_trashed = 1; 5960786Sps return; 6060786Sps } 6160786Sps 62161475Sdelphij final_attr = AT_NORMAL; 6360786Sps 6460786Sps for (i = 0; (c = gline(i, &a)) != '\0'; i++) 6560786Sps { 66161475Sdelphij at_switch(a); 67161475Sdelphij final_attr = a; 6860786Sps if (c == '\b') 6960786Sps putbs(); 7060786Sps else 7160786Sps putchr(c); 7260786Sps } 7360786Sps 74161475Sdelphij at_exit(); 7560786Sps} 7660786Sps 7760786Spsstatic char obuf[OUTBUF_SIZE]; 7860786Spsstatic char *ob = obuf; 7960786Sps 8060786Sps/* 8160786Sps * Flush buffered output. 8260786Sps * 8360786Sps * If we haven't displayed any file data yet, 8460786Sps * output messages on error output (file descriptor 2), 8560786Sps * otherwise output on standard output (file descriptor 1). 8660786Sps * 8760786Sps * This has the desirable effect of producing all 8860786Sps * error messages on error output if standard output 8960786Sps * is directed to a file. It also does the same if 9060786Sps * we never produce any real output; for example, if 9160786Sps * the input file(s) cannot be opened. If we do 9260786Sps * eventually produce output, code in edit() makes 9360786Sps * sure these messages can be seen before they are 9460786Sps * overwritten or scrolled away. 9560786Sps */ 9660786Sps public void 9760786Spsflush() 9860786Sps{ 9960786Sps register int n; 10060786Sps register int fd; 10160786Sps 10260786Sps n = ob - obuf; 10360786Sps if (n == 0) 10460786Sps return; 10560786Sps 10660786Sps#if MSDOS_COMPILER==MSOFTC 10760786Sps if (is_tty && any_display) 10860786Sps { 10960786Sps *ob = '\0'; 11060786Sps _outtext(obuf); 11160786Sps ob = obuf; 11260786Sps return; 11360786Sps } 11460786Sps#else 115191930Sdelphij#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 11660786Sps if (is_tty && any_display) 11760786Sps { 11860786Sps *ob = '\0'; 119128345Stjr if (ctldisp != OPT_ONPLUS) 120191930Sdelphij WIN32textout(obuf, ob - obuf); 121128345Stjr else 122128345Stjr { 123128345Stjr /* 124128345Stjr * Look for SGR escape sequences, and convert them 125128345Stjr * to color commands. Replace bold, underline, 126128345Stjr * and italic escapes into colors specified via 127128345Stjr * the -D command-line option. 128128345Stjr */ 129128345Stjr char *anchor, *p, *p_next; 130191930Sdelphij unsigned char fg, bg; 131191930Sdelphij static unsigned char at; 132191930Sdelphij#if MSDOS_COMPILER==WIN32C 133191930Sdelphij /* Screen colors used by 3x and 4x SGR commands. */ 134191930Sdelphij static unsigned char screen_color[] = { 135191930Sdelphij 0, /* BLACK */ 136191930Sdelphij FOREGROUND_RED, 137191930Sdelphij FOREGROUND_GREEN, 138191930Sdelphij FOREGROUND_RED|FOREGROUND_GREEN, 139191930Sdelphij FOREGROUND_BLUE, 140191930Sdelphij FOREGROUND_BLUE|FOREGROUND_RED, 141191930Sdelphij FOREGROUND_BLUE|FOREGROUND_GREEN, 142191930Sdelphij FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED 143191930Sdelphij }; 144191930Sdelphij#else 145128345Stjr static enum COLORS screen_color[] = { 146128345Stjr BLACK, RED, GREEN, BROWN, 147128345Stjr BLUE, MAGENTA, CYAN, LIGHTGRAY 148128345Stjr }; 149191930Sdelphij#endif 150128345Stjr 151128345Stjr for (anchor = p_next = obuf; 152191930Sdelphij (p_next = memchr(p_next, ESC, ob - p_next)) != NULL; ) 153128345Stjr { 154128345Stjr p = p_next; 155191930Sdelphij if (p[1] == '[') /* "ESC-[" sequence */ 156128345Stjr { 157128345Stjr if (p > anchor) 158128345Stjr { 159191930Sdelphij /* 160191930Sdelphij * If some chars seen since 161191930Sdelphij * the last escape sequence, 162191930Sdelphij * write them out to the screen. 163191930Sdelphij */ 164191930Sdelphij WIN32textout(anchor, p-anchor); 165128345Stjr anchor = p; 166128345Stjr } 167191930Sdelphij p += 2; /* Skip the "ESC-[" */ 168191930Sdelphij if (is_ansi_end(*p)) 169191930Sdelphij { 170191930Sdelphij /* 171191930Sdelphij * Handle null escape sequence 172191930Sdelphij * "ESC[m", which restores 173191930Sdelphij * the normal color. 174191930Sdelphij */ 175191930Sdelphij p++; 176191930Sdelphij anchor = p_next = p; 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 { 275191930Sdelphij fg = bo_fg_color; 276191930Sdelphij bg = bo_bg_color; 277191930Sdelphij } else if (at & 2) 278191930Sdelphij { 279191930Sdelphij fg = so_fg_color; 280191930Sdelphij bg = so_bg_color; 281191930Sdelphij } else if (at & 4) 282191930Sdelphij { 283191930Sdelphij fg = ul_fg_color; 284191930Sdelphij bg = ul_bg_color; 285191930Sdelphij } else if (at & 8) 286191930Sdelphij { 287191930Sdelphij fg = bl_fg_color; 288191930Sdelphij bg = bl_bg_color; 289191930Sdelphij } 290191930Sdelphij fg &= 0xf; 291191930Sdelphij bg &= 0xf; 292191930Sdelphij WIN32setcolors(fg, bg); 293191930Sdelphij p_next = anchor = p + 1; 294128345Stjr } else 295128345Stjr p_next++; 296128345Stjr } 297128345Stjr 298128345Stjr /* Output what's left in the buffer. */ 299191930Sdelphij WIN32textout(anchor, ob - anchor); 300128345Stjr } 30160786Sps ob = obuf; 30260786Sps return; 30360786Sps } 30460786Sps#endif 30560786Sps#endif 30660786Sps fd = (any_display) ? 1 : 2; 30760786Sps if (write(fd, obuf, n) != n) 30860786Sps screen_trashed = 1; 30960786Sps ob = obuf; 31060786Sps} 31160786Sps 31260786Sps/* 31360786Sps * Output a character. 31460786Sps */ 31560786Sps public int 31660786Spsputchr(c) 31760786Sps int c; 31860786Sps{ 319161475Sdelphij#if 0 /* fake UTF-8 output for testing */ 320161475Sdelphij extern int utf_mode; 321161475Sdelphij if (utf_mode) 322161475Sdelphij { 323161475Sdelphij static char ubuf[MAX_UTF_CHAR_LEN]; 324161475Sdelphij static int ubuf_len = 0; 325161475Sdelphij static int ubuf_index = 0; 326161475Sdelphij if (ubuf_len == 0) 327161475Sdelphij { 328161475Sdelphij ubuf_len = utf_len(c); 329161475Sdelphij ubuf_index = 0; 330161475Sdelphij } 331161475Sdelphij ubuf[ubuf_index++] = c; 332161475Sdelphij if (ubuf_index < ubuf_len) 333161475Sdelphij return c; 334161475Sdelphij c = get_wchar(ubuf) & 0xFF; 335161475Sdelphij ubuf_len = 0; 336161475Sdelphij } 337161475Sdelphij#endif 33860786Sps if (need_clr) 33960786Sps { 34060786Sps need_clr = 0; 34160786Sps clear_bot(); 34260786Sps } 34360786Sps#if MSDOS_COMPILER 34460786Sps if (c == '\n' && is_tty) 34560786Sps { 34660786Sps /* remove_top(1); */ 34760786Sps putchr('\r'); 34860786Sps } 34960786Sps#else 35060786Sps#ifdef _OSK 35160786Sps if (c == '\n' && is_tty) /* In OS-9, '\n' == 0x0D */ 35260786Sps putchr(0x0A); 35360786Sps#endif 35460786Sps#endif 35560786Sps /* 35660786Sps * Some versions of flush() write to *ob, so we must flush 35760786Sps * when we are still one char from the end of obuf. 35860786Sps */ 35960786Sps if (ob >= &obuf[sizeof(obuf)-1]) 36060786Sps flush(); 36160786Sps *ob++ = c; 362170256Sdelphij at_prompt = 0; 36360786Sps return (c); 36460786Sps} 36560786Sps 36660786Sps/* 36760786Sps * Output a string. 36860786Sps */ 36960786Sps public void 37060786Spsputstr(s) 37160786Sps register char *s; 37260786Sps{ 37360786Sps while (*s != '\0') 37460786Sps putchr(*s++); 37560786Sps} 37660786Sps 37760786Sps 37860786Sps/* 379128345Stjr * Convert an integral type to a string. 380128345Stjr */ 381128345Stjr#define TYPE_TO_A_FUNC(funcname, type) \ 382128345Stjrvoid funcname(num, buf) \ 383128345Stjr type num; \ 384128345Stjr char *buf; \ 385128345Stjr{ \ 386128345Stjr int neg = (num < 0); \ 387128345Stjr char tbuf[INT_STRLEN_BOUND(num)+2]; \ 388128345Stjr register char *s = tbuf + sizeof(tbuf); \ 389128345Stjr if (neg) num = -num; \ 390128345Stjr *--s = '\0'; \ 391128345Stjr do { \ 392128345Stjr *--s = (num % 10) + '0'; \ 393128345Stjr } while ((num /= 10) != 0); \ 394128345Stjr if (neg) *--s = '-'; \ 395128345Stjr strcpy(buf, s); \ 396128345Stjr} 397128345Stjr 398128345StjrTYPE_TO_A_FUNC(postoa, POSITION) 399128345StjrTYPE_TO_A_FUNC(linenumtoa, LINENUM) 400128345StjrTYPE_TO_A_FUNC(inttoa, int) 401128345Stjr 402128345Stjr/* 40360786Sps * Output an integer in a given radix. 40460786Sps */ 40560786Sps static int 406128345Stjriprint_int(num) 40760786Sps int num; 40860786Sps{ 40989019Sps char buf[INT_STRLEN_BOUND(num)]; 41060786Sps 411128345Stjr inttoa(num, buf); 412128345Stjr putstr(buf); 413128345Stjr return (strlen(buf)); 414128345Stjr} 41560786Sps 416128345Stjr/* 417128345Stjr * Output a line number in a given radix. 418128345Stjr */ 419128345Stjr static int 420128345Stjriprint_linenum(num) 421128345Stjr LINENUM num; 422128345Stjr{ 423128345Stjr char buf[INT_STRLEN_BOUND(num)]; 42460786Sps 425128345Stjr linenumtoa(num, buf); 426128345Stjr putstr(buf); 427128345Stjr return (strlen(buf)); 42860786Sps} 42960786Sps 43060786Sps/* 43160786Sps * This function implements printf-like functionality 43260786Sps * using a more portable argument list mechanism than printf's. 43360786Sps */ 43460786Sps static int 43560786Spsless_printf(fmt, parg) 43660786Sps register char *fmt; 43760786Sps PARG *parg; 43860786Sps{ 43960786Sps register char *s; 44060786Sps register int col; 44160786Sps 44260786Sps col = 0; 44360786Sps while (*fmt != '\0') 44460786Sps { 44560786Sps if (*fmt != '%') 44660786Sps { 44760786Sps putchr(*fmt++); 44860786Sps col++; 44960786Sps } else 45060786Sps { 45160786Sps ++fmt; 452128345Stjr switch (*fmt++) 453128345Stjr { 45460786Sps case 's': 45560786Sps s = parg->p_string; 45660786Sps parg++; 45760786Sps while (*s != '\0') 45860786Sps { 45960786Sps putchr(*s++); 46060786Sps col++; 46160786Sps } 46260786Sps break; 46360786Sps case 'd': 464128345Stjr col += iprint_int(parg->p_int); 46560786Sps parg++; 46660786Sps break; 467128345Stjr case 'n': 468128345Stjr col += iprint_linenum(parg->p_linenum); 469128345Stjr parg++; 470128345Stjr break; 47160786Sps } 47260786Sps } 47360786Sps } 47460786Sps return (col); 47560786Sps} 47660786Sps 47760786Sps/* 47860786Sps * Get a RETURN. 47960786Sps * If some other non-trivial char is pressed, unget it, so it will 48060786Sps * become the next command. 48160786Sps */ 48260786Sps public void 48360786Spsget_return() 48460786Sps{ 48560786Sps int c; 48660786Sps 48760786Sps#if ONLY_RETURN 48860786Sps while ((c = getchr()) != '\n' && c != '\r') 48960786Sps bell(); 49060786Sps#else 49160786Sps c = getchr(); 49260786Sps if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR) 49360786Sps ungetcc(c); 49460786Sps#endif 49560786Sps} 49660786Sps 49760786Sps/* 49860786Sps * Output a message in the lower left corner of the screen 49960786Sps * and wait for carriage return. 50060786Sps */ 50160786Sps public void 50260786Spserror(fmt, parg) 50360786Sps char *fmt; 50460786Sps PARG *parg; 50560786Sps{ 50660786Sps int col = 0; 50760786Sps static char return_to_continue[] = " (press RETURN)"; 50860786Sps 50960786Sps errmsgs++; 51060786Sps 51160786Sps if (any_display && is_tty) 51260786Sps { 513170256Sdelphij if (!oldbot) 514170256Sdelphij squish_check(); 515161475Sdelphij at_exit(); 51660786Sps clear_bot(); 517161475Sdelphij at_enter(AT_STANDOUT); 51860786Sps col += so_s_width; 51960786Sps } 52060786Sps 52160786Sps col += less_printf(fmt, parg); 52260786Sps 52360786Sps if (!(any_display && is_tty)) 52460786Sps { 52560786Sps putchr('\n'); 52660786Sps return; 52760786Sps } 52860786Sps 52960786Sps putstr(return_to_continue); 530161475Sdelphij at_exit(); 53160786Sps col += sizeof(return_to_continue) + so_e_width; 53260786Sps 53360786Sps get_return(); 53460786Sps lower_left(); 53560786Sps 53660786Sps if (col >= sc_width) 53760786Sps /* 53860786Sps * Printing the message has probably scrolled the screen. 53960786Sps * {{ Unless the terminal doesn't have auto margins, 54060786Sps * in which case we just hammered on the right margin. }} 54160786Sps */ 54260786Sps screen_trashed = 1; 54360786Sps 54460786Sps flush(); 54560786Sps} 54660786Sps 54760786Spsstatic char intr_to_abort[] = "... (interrupt to abort)"; 54860786Sps 54960786Sps/* 55060786Sps * Output a message in the lower left corner of the screen 55160786Sps * and don't wait for carriage return. 55260786Sps * Usually used to warn that we are beginning a potentially 55360786Sps * time-consuming operation. 55460786Sps */ 55560786Sps public void 55660786Spsierror(fmt, parg) 55760786Sps char *fmt; 55860786Sps PARG *parg; 55960786Sps{ 560161475Sdelphij at_exit(); 56160786Sps clear_bot(); 562161475Sdelphij at_enter(AT_STANDOUT); 56360786Sps (void) less_printf(fmt, parg); 56460786Sps putstr(intr_to_abort); 565161475Sdelphij at_exit(); 56660786Sps flush(); 56760786Sps need_clr = 1; 56860786Sps} 56960786Sps 57060786Sps/* 57160786Sps * Output a message in the lower left corner of the screen 57260786Sps * and return a single-character response. 57360786Sps */ 57460786Sps public int 57560786Spsquery(fmt, parg) 57660786Sps char *fmt; 57760786Sps PARG *parg; 57860786Sps{ 57960786Sps register int c; 58060786Sps int col = 0; 58160786Sps 58260786Sps if (any_display && is_tty) 58360786Sps clear_bot(); 58460786Sps 58560786Sps (void) less_printf(fmt, parg); 58660786Sps c = getchr(); 58760786Sps 58860786Sps if (!(any_display && is_tty)) 58960786Sps { 59060786Sps putchr('\n'); 59160786Sps return (c); 59260786Sps } 59360786Sps 59460786Sps lower_left(); 59560786Sps if (col >= sc_width) 59660786Sps screen_trashed = 1; 59760786Sps flush(); 59860786Sps 59960786Sps return (c); 60060786Sps} 601