output.c revision 221715
1192886Sedwin/* 2192886Sedwin * Copyright (C) 1984-2011 Mark Nudelman 364499Swollman * 42742Swollman * You may distribute under the terms of either the GNU General Public 52742Swollman * License or the Less License, as specified in the README file. 6243020Sedwin * 72742Swollman * For more information about less, or for information on how to 8158421Swollman * contact the author, see the README file. 92742Swollman */ 10158421Swollman 11158421Swollman 122742Swollman/* 13248310Sedwin * High level routines dealing with the output to the screen. 14248310Sedwin */ 15248310Sedwin 16248310Sedwin#include "less.h" 1786222Swollman#if MSDOS_COMPILER==WIN32C 1820094Swollman#include "windows.h" 1920094Swollman#endif 2020094Swollman 2120094Swollmanpublic int errmsgs; /* Count of messages displayed by error() */ 2220094Swollmanpublic int need_clr; 23158421Swollmanpublic int final_attr; 24158421Swollmanpublic int at_prompt; 2520094Swollman 2619878Swollmanextern int sigs; 2719878Swollmanextern int sc_width; 2819878Swollmanextern int so_s_width, so_e_width; 2919878Swollmanextern int screen_trashed; 3019878Swollmanextern int any_display; 3119878Swollmanextern int is_tty; 32273438Sdelphijextern int oldbot; 3319878Swollman 3458787Sru#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 3558787Sruextern int ctldisp; 3658787Sruextern int nm_fg_color, nm_bg_color; 37273438Sdelphijextern int bo_fg_color, bo_bg_color; 3858787Sruextern int ul_fg_color, ul_bg_color; 3958787Sruextern int so_fg_color, so_bg_color; 40273438Sdelphijextern int bl_fg_color, bl_bg_color; 41273438Sdelphij#endif 42273438Sdelphij 4358787Sru/* 4458787Sru * Display the line which is in the line buffer. 4558787Sru */ 4658787Sru public void 4758787Sruput_line() 4858787Sru{ 49273438Sdelphij register int c; 5058787Sru register int i; 5158787Sru int a; 522742Swollman 532742Swollman if (ABORT_SIGS()) 542742Swollman { 552742Swollman /* 562742Swollman * Don't output if a signal is pending. 572742Swollman */ 582742Swollman screen_trashed = 1; 5919878Swollman return; 602742Swollman } 612742Swollman 622742Swollman final_attr = AT_NORMAL; 63273438Sdelphij 642742Swollman for (i = 0; (c = gline(i, &a)) != '\0'; i++) 652742Swollman { 66149514Swollman at_switch(a); 6721217Swollman final_attr = a; 689908Swollman if (c == '\b') 699908Swollman putbs(); 702742Swollman else 7119878Swollman putchr(c); 7219878Swollman } 7319878Swollman 7419878Swollman at_exit(); 7519878Swollman} 7619878Swollman 7719878Swollmanstatic char obuf[OUTBUF_SIZE]; 7819878Swollmanstatic char *ob = obuf; 7919878Swollman 8019878Swollman/* 8119878Swollman * Flush buffered output. 8219878Swollman * 8319878Swollman * If we haven't displayed any file data yet, 8419878Swollman * output messages on error output (file descriptor 2), 8519878Swollman * otherwise output on standard output (file descriptor 1). 8619878Swollman * 8793799Swollman * This has the desirable effect of producing all 8858787Sru * error messages on error output if standard output 8958787Sru * is directed to a file. It also does the same if 9019878Swollman * we never produce any real output; for example, if 9119878Swollman * the input file(s) cannot be opened. If we do 9219878Swollman * eventually produce output, code in edit() makes 939908Swollman * sure these messages can be seen before they are 94149514Swollman * overwritten or scrolled away. 959908Swollman */ 969908Swollman public void 97273438Sdelphijflush() 9821217Swollman{ 9919878Swollman register int n; 10019878Swollman register int fd; 1019908Swollman 102149514Swollman n = ob - obuf; 1039908Swollman if (n == 0) 1049908Swollman return; 1059908Swollman 1069908Swollman#if MSDOS_COMPILER==MSOFTC 10758787Sru if (is_tty && any_display) 10858787Sru { 10958787Sru *ob = '\0'; 11064499Swollman _outtext(obuf); 11164499Swollman ob = obuf; 112175034Sedwin return; 113175034Sedwin } 114175034Sedwin#else 115175034Sedwin#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 116175034Sedwin if (is_tty && any_display) 11758787Sru { 11858787Sru *ob = '\0'; 119273438Sdelphij if (ctldisp != OPT_ONPLUS) 12058787Sru WIN32textout(obuf, ob - obuf); 12158787Sru else 12258787Sru { 123273438Sdelphij /* 12464499Swollman * Look for SGR escape sequences, and convert them 125273438Sdelphij * to color commands. Replace bold, underline, 12664499Swollman * and italic escapes into colors specified via 12764499Swollman * the -D command-line option. 12886222Swollman */ 12986222Swollman char *anchor, *p, *p_next; 13086222Swollman unsigned char fg, bg; 13186222Swollman static unsigned char at; 13286222Swollman#if MSDOS_COMPILER==WIN32C 13386222Swollman /* Screen colors used by 3x and 4x SGR commands. */ 13486222Swollman static unsigned char screen_color[] = { 13586222Swollman 0, /* BLACK */ 13686222Swollman FOREGROUND_RED, 13786222Swollman FOREGROUND_GREEN, 13886222Swollman FOREGROUND_RED|FOREGROUND_GREEN, 13986222Swollman FOREGROUND_BLUE, 14086222Swollman FOREGROUND_BLUE|FOREGROUND_RED, 14186222Swollman FOREGROUND_BLUE|FOREGROUND_GREEN, 14286222Swollman FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED 14386222Swollman }; 14486222Swollman#else 14586222Swollman static enum COLORS screen_color[] = { 14686222Swollman BLACK, RED, GREEN, BROWN, 14786222Swollman BLUE, MAGENTA, CYAN, LIGHTGRAY 14886222Swollman }; 14986222Swollman#endif 15086222Swollman 151175034Sedwin for (anchor = p_next = obuf; 152175034Sedwin (p_next = memchr(p_next, ESC, ob - p_next)) != NULL; ) 153175034Sedwin { 154175034Sedwin p = p_next; 155175034Sedwin if (p[1] == '[') /* "ESC-[" sequence */ 156175034Sedwin { 157175034Sedwin if (p > anchor) 158273438Sdelphij { 159175034Sedwin /* 160273438Sdelphij * If some chars seen since 161175034Sedwin * the last escape sequence, 162175034Sedwin * write them out to the screen. 163175034Sedwin */ 164175034Sedwin WIN32textout(anchor, p-anchor); 165175034Sedwin anchor = p; 166175034Sedwin } 167175034Sedwin p += 2; /* Skip the "ESC-[" */ 168175034Sedwin if (is_ansi_end(*p)) 169183066Sedwin { 170183066Sedwin /* 171183066Sedwin * Handle null escape sequence 172183066Sedwin * "ESC[m", which restores 173183066Sedwin * the normal color. 174183066Sedwin */ 175183066Sedwin p++; 176183066Sedwin anchor = p_next = p; 177183864Sedwin WIN32setcolors(nm_fg_color, nm_bg_color); 178183864Sedwin continue; 179183864Sedwin } 180183864Sedwin p_next = p; 181183864Sedwin 182183864Sedwin /* 183183864Sedwin * Select foreground/background colors 184183864Sedwin * based on the escape sequence. 185183864Sedwin */ 186183864Sedwin fg = nm_fg_color; 187183864Sedwin bg = nm_bg_color; 188183864Sedwin while (!is_ansi_end(*p)) 189184406Sedwin { 190184406Sedwin char *q; 191184406Sedwin long code = strtol(p, &q, 10); 192184406Sedwin 193184406Sedwin if (*q == '\0') 194273438Sdelphij { 195273438Sdelphij /* 196273438Sdelphij * Incomplete sequence. 197273438Sdelphij * Leave it unprocessed 198184406Sedwin * in the buffer. 199184406Sedwin */ 200184406Sedwin int slop = q - anchor; 201184406Sedwin /* {{ strcpy args overlap! }} */ 202184406Sedwin strcpy(obuf, anchor); 203184406Sedwin ob = &obuf[slop]; 204198515Sedwin return; 205198515Sedwin } 206198515Sedwin 207198515Sedwin if (q == p || 208198515Sedwin code > 49 || code < 0 || 209273438Sdelphij (!is_ansi_end(*q) && *q != ';')) 210273438Sdelphij { 211273438Sdelphij p_next = q; 212273438Sdelphij break; 213273438Sdelphij } 214273438Sdelphij if (*q == ';') 215198515Sedwin q++; 216175034Sedwin 217198515Sedwin switch (code) 218198515Sedwin { 219240461Sedwin default: 220136638Swollman /* case 0: all attrs off */ 221136638Swollman fg = nm_fg_color; 222136638Swollman bg = nm_bg_color; 223136638Swollman at = 0; 224136638Swollman break; 225136638Swollman case 1: /* bold on */ 226136638Swollman at |= 1; 22793799Swollman break; 228158421Swollman case 3: /* italic on */ 229158421Swollman case 7: /* inverse on */ 230273438Sdelphij at |= 2; 231273438Sdelphij break; 23293799Swollman case 4: /* underline on */ 233158421Swollman at |= 4; 234136638Swollman break; 235136638Swollman case 5: /* slow blink on */ 236136638Swollman case 6: /* fast blink on */ 237136638Swollman at |= 8; 238136638Swollman break; 239136638Swollman case 8: /* concealed on */ 240136638Swollman fg = (bg & 7) | 8; 241136638Swollman break; 242136638Swollman case 22: /* bold off */ 243136638Swollman at &= ~1; 244136638Swollman break; 245136638Swollman case 23: /* italic off */ 246273438Sdelphij case 27: /* inverse off */ 247136638Swollman at &= ~2; 248136638Swollman break; 249273438Sdelphij case 24: /* underline off */ 250136638Swollman at &= ~4; 251136638Swollman break; 252136638Swollman case 30: case 31: case 32: 253136638Swollman case 33: case 34: case 35: 254136638Swollman case 36: case 37: 255136638Swollman fg = (fg & 8) | (screen_color[code - 30]); 256136638Swollman break; 257136638Swollman case 39: /* default fg */ 258136638Swollman fg = nm_fg_color; 259136638Swollman break; 260136638Swollman case 40: case 41: case 42: 261136638Swollman case 43: case 44: case 45: 262136638Swollman case 46: case 47: 263136638Swollman bg = (bg & 8) | (screen_color[code - 40]); 264136638Swollman break; 265136638Swollman case 49: /* default fg */ 266136638Swollman bg = nm_bg_color; 267136638Swollman break; 268136638Swollman } 269136638Swollman p = q; 270136638Swollman } 271136638Swollman if (!is_ansi_end(*p) || p == p_next) 272136638Swollman break; 273136638Swollman if (at & 1) 274136638Swollman { 275136638Swollman fg = bo_fg_color; 276136638Swollman bg = bo_bg_color; 277136638Swollman } else if (at & 2) 278136638Swollman { 27993799Swollman fg = so_fg_color; 280177591Sedwin bg = so_bg_color; 281177591Sedwin } else if (at & 4) 282177591Sedwin { 283177591Sedwin fg = ul_fg_color; 284273438Sdelphij bg = ul_bg_color; 285177591Sedwin } else if (at & 8) 286177591Sedwin { 287177591Sedwin fg = bl_fg_color; 288177591Sedwin bg = bl_bg_color; 289177591Sedwin } 290177591Sedwin fg &= 0xf; 291273438Sdelphij bg &= 0xf; 292177591Sedwin WIN32setcolors(fg, bg); 293177591Sedwin p_next = anchor = p + 1; 294273438Sdelphij } else 295177591Sedwin p_next++; 296177591Sedwin } 297177591Sedwin 298177591Sedwin /* Output what's left in the buffer. */ 299177591Sedwin WIN32textout(anchor, ob - anchor); 300240461Sedwin } 301240461Sedwin ob = obuf; 302240461Sedwin return; 303177591Sedwin } 304177591Sedwin#endif 305177591Sedwin#endif 306177591Sedwin fd = (any_display) ? 1 : 2; 307177591Sedwin if (write(fd, obuf, n) != n) 308177591Sedwin screen_trashed = 1; 309273438Sdelphij ob = obuf; 310177591Sedwin} 311177591Sedwin 312177591Sedwin/* 313177591Sedwin * Output a character. 314177591Sedwin */ 315177591Sedwin public int 316177591Sedwinputchr(c) 317177591Sedwin int c; 318177591Sedwin{ 319177591Sedwin#if 0 /* fake UTF-8 output for testing */ 320177591Sedwin extern int utf_mode; 321177591Sedwin if (utf_mode) 322177591Sedwin { 323177591Sedwin static char ubuf[MAX_UTF_CHAR_LEN]; 324177591Sedwin static int ubuf_len = 0; 325177591Sedwin static int ubuf_index = 0; 326177591Sedwin if (ubuf_len == 0) 327177591Sedwin { 328177591Sedwin ubuf_len = utf_len(c); 329177591Sedwin ubuf_index = 0; 330177591Sedwin } 331177591Sedwin ubuf[ubuf_index++] = c; 332177591Sedwin if (ubuf_index < ubuf_len) 333177591Sedwin return c; 334177591Sedwin c = get_wchar(ubuf) & 0xFF; 335181421Sedwin ubuf_len = 0; 336158421Swollman } 337158421Swollman#endif 338181421Sedwin if (need_clr) 339181421Sedwin { 340181421Sedwin need_clr = 0; 341181421Sedwin clear_bot(); 342181421Sedwin } 343190372Sedwin#if MSDOS_COMPILER 344190372Sedwin if (c == '\n' && is_tty) 345190372Sedwin { 346190372Sedwin /* remove_top(1); */ 34793799Swollman putchr('\r'); 348190372Sedwin } 349190372Sedwin#else 350273438Sdelphij#ifdef _OSK 351273438Sdelphij if (c == '\n' && is_tty) /* In OS-9, '\n' == 0x0D */ 352190372Sedwin putchr(0x0A); 353248310Sedwin#endif 354190372Sedwin#endif 355240461Sedwin /* 356248310Sedwin * Some versions of flush() write to *ob, so we must flush 357248310Sedwin * when we are still one char from the end of obuf. 358190372Sedwin */ 359190372Sedwin if (ob >= &obuf[sizeof(obuf)-1]) 360190372Sedwin flush(); 361190372Sedwin *ob++ = c; 362190372Sedwin at_prompt = 0; 363190372Sedwin return (c); 364198515Sedwin} 365198515Sedwin 366190372Sedwin/* 367198515Sedwin * Output a string. 368198515Sedwin */ 369198515Sedwin public void 370198515Sedwinputstr(s) 371198515Sedwin register char *s; 372198515Sedwin{ 373197597Sedwin while (*s != '\0') 374198515Sedwin putchr(*s++); 375197597Sedwin} 376198515Sedwin 377198515Sedwin 378198515Sedwin/* 379198515Sedwin * Convert an integral type to a string. 380198515Sedwin */ 381198515Sedwin#define TYPE_TO_A_FUNC(funcname, type) \ 382198515Sedwinvoid funcname(num, buf) \ 383198515Sedwin type num; \ 384198515Sedwin char *buf; \ 385198515Sedwin{ \ 386198515Sedwin int neg = (num < 0); \ 387198515Sedwin char tbuf[INT_STRLEN_BOUND(num)+2]; \ 388198515Sedwin register char *s = tbuf + sizeof(tbuf); \ 389198515Sedwin if (neg) num = -num; \ 390198515Sedwin *--s = '\0'; \ 391198515Sedwin do { \ 392198515Sedwin *--s = (num % 10) + '0'; \ 393198515Sedwin } while ((num /= 10) != 0); \ 394198515Sedwin if (neg) *--s = '-'; \ 395197597Sedwin strcpy(buf, s); \ 396206868Sedwin} 397206868Sedwin 398206868SedwinTYPE_TO_A_FUNC(postoa, POSITION) 399273438SdelphijTYPE_TO_A_FUNC(linenumtoa, LINENUM) 400206868SedwinTYPE_TO_A_FUNC(inttoa, int) 401206868Sedwin 402273438Sdelphij/* 403206868Sedwin * Output an integer in a given radix. 404206868Sedwin */ 405206868Sedwin static int 406206868Sedwiniprint_int(num) 407206868Sedwin int num; 408206868Sedwin{ 409206868Sedwin char buf[INT_STRLEN_BOUND(num)]; 410206868Sedwin 411206868Sedwin inttoa(num, buf); 412206868Sedwin putstr(buf); 413257682Sedwin return (strlen(buf)); 414257682Sedwin} 415257682Sedwin 416257682Sedwin/* 417257682Sedwin * Output a line number in a given radix. 418257682Sedwin */ 419257682Sedwin static int 420257682Sedwiniprint_linenum(num) 421257682Sedwin LINENUM num; 422257682Sedwin{ 423257682Sedwin char buf[INT_STRLEN_BOUND(num)]; 424248310Sedwin 425273438Sdelphij linenumtoa(num, buf); 426248310Sedwin putstr(buf); 4272742Swollman return (strlen(buf)); 42820094Swollman} 429136638Swollman 430136638Swollman/* 431273438Sdelphij * This function implements printf-like functionality 43219878Swollman * using a more portable argument list mechanism than printf's. 43358787Sru */ 43493799Swollman static int 43593799Swollmanless_printf(fmt, parg) 436175034Sedwin register char *fmt; 43720094Swollman PARG *parg; 438273438Sdelphij{ 439184406Sedwin register char *s; 44020094Swollman register int col; 441158421Swollman 44293799Swollman col = 0; 44393799Swollman while (*fmt != '\0') 44493799Swollman { 44593799Swollman if (*fmt != '%') 44693799Swollman { 44793799Swollman putchr(*fmt++); 448136638Swollman col++; 44993799Swollman } else 45020094Swollman { 45158787Sru ++fmt; 45293799Swollman switch (*fmt++) 45393799Swollman { 45493799Swollman case 's': 45593799Swollman s = parg->p_string; 456175034Sedwin parg++; 45720094Swollman while (*s != '\0') 458273438Sdelphij { 459184406Sedwin putchr(*s++); 460184406Sedwin col++; 461184406Sedwin } 462184406Sedwin break; 463184406Sedwin case 'd': 464184406Sedwin col += iprint_int(parg->p_int); 465184406Sedwin parg++; 466184406Sedwin break; 467184406Sedwin case 'n': 468184406Sedwin col += iprint_linenum(parg->p_linenum); 469184406Sedwin parg++; 470273438Sdelphij break; 471136638Swollman } 472136638Swollman } 473136638Swollman } 474136638Swollman return (col); 475136638Swollman} 476136638Swollman 477136638Swollman/* 478136638Swollman * Get a RETURN. 479136638Swollman * If some other non-trivial char is pressed, unget it, so it will 480136638Swollman * become the next command. 481175034Sedwin */ 482136638Swollman public void 483136638Swollmanget_return() 484136638Swollman{ 485136638Swollman int c; 486136638Swollman 487136638Swollman#if ONLY_RETURN 488136638Swollman while ((c = getchr()) != '\n' && c != '\r') 489136638Swollman bell(); 490136638Swollman#else 491136638Swollman c = getchr(); 492136638Swollman if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR) 493136638Swollman ungetcc(c); 494184406Sedwin#endif 495184406Sedwin} 496136638Swollman 497136638Swollman/* 498136638Swollman * Output a message in the lower left corner of the screen 499136638Swollman * and wait for carriage return. 500136638Swollman */ 501136638Swollman public void 502136638Swollmanerror(fmt, parg) 503136638Swollman char *fmt; 504136638Swollman PARG *parg; 505136638Swollman{ 506136638Swollman int col = 0; 507136638Swollman static char return_to_continue[] = " (press RETURN)"; 508184406Sedwin 509184406Sedwin errmsgs++; 510136638Swollman 51120094Swollman if (any_display && is_tty) 512136638Swollman { 51393799Swollman if (!oldbot) 51420094Swollman squish_check(); 51520094Swollman at_exit(); 51693799Swollman clear_bot(); 51793799Swollman at_enter(AT_STANDOUT); 51893799Swollman col += so_s_width; 51920094Swollman } 52093799Swollman 52193799Swollman col += less_printf(fmt, parg); 52293799Swollman 523184406Sedwin if (!(any_display && is_tty)) 524184406Sedwin { 52520094Swollman putchr('\n'); 526149514Swollman return; 527136638Swollman } 52893799Swollman 52920094Swollman putstr(return_to_continue); 53058787Sru at_exit(); 53193799Swollman col += sizeof(return_to_continue) + so_e_width; 53293799Swollman 53393799Swollman get_return(); 53493799Swollman lower_left(); 535136638Swollman clear_eol(); 536136638Swollman 537184406Sedwin if (col >= sc_width) 538184406Sedwin /* 53920094Swollman * Printing the message has probably scrolled the screen. 54020094Swollman * {{ Unless the terminal doesn't have auto margins, 541136638Swollman * in which case we just hammered on the right margin. }} 54293799Swollman */ 54320094Swollman screen_trashed = 1; 54420094Swollman 54593799Swollman flush(); 54693799Swollman} 54793799Swollman 54820094Swollmanstatic char intr_to_abort[] = "... (interrupt to abort)"; 54920094Swollman 55020094Swollman/* 55193799Swollman * Output a message in the lower left corner of the screen 55293799Swollman * and don't wait for carriage return. 553136638Swollman * Usually used to warn that we are beginning a potentially 554136638Swollman * time-consuming operation. 555184406Sedwin */ 556184406Sedwin public void 557136638Swollmanierror(fmt, parg) 558177591Sedwin char *fmt; 559198515Sedwin PARG *parg; 560206868Sedwin{ 561257682Sedwin at_exit(); 562198515Sedwin clear_bot(); 563177591Sedwin at_enter(AT_STANDOUT); 564177591Sedwin (void) less_printf(fmt, parg); 565177591Sedwin putstr(intr_to_abort); 566177591Sedwin at_exit(); 567181421Sedwin flush(); 568181421Sedwin need_clr = 1; 569181421Sedwin} 570181421Sedwin 571181421Sedwin/* 572181421Sedwin * Output a message in the lower left corner of the screen 573181421Sedwin * and return a single-character response. 574177591Sedwin */ 575177591Sedwin public int 576177591Sedwinquery(fmt, parg) 577257682Sedwin char *fmt; 578257682Sedwin PARG *parg; 579177591Sedwin{ 580136638Swollman register int c; 581136638Swollman int col = 0; 582273438Sdelphij 583136638Swollman if (any_display && is_tty) 584136638Swollman clear_bot(); 585136638Swollman 586136638Swollman (void) less_printf(fmt, parg); 587136638Swollman c = getchr(); 588136638Swollman 589184406Sedwin if (!(any_display && is_tty)) 590184406Sedwin { 591136638Swollman putchr('\n'); 592273438Sdelphij return (c); 593136638Swollman } 594273438Sdelphij 595136638Swollman lower_left(); 596136638Swollman if (col >= sc_width) 597136638Swollman screen_trashed = 1; 598136638Swollman flush(); 599136638Swollman 600136638Swollman return (c); 601184406Sedwin} 602184406Sedwin