1/* 2 * Copyright (C) 1984-2007 Mark Nudelman 3 * 4 * You may distribute under the terms of either the GNU General Public 5 * License or the Less License, as specified in the README file. 6 * 7 * For more information about less, or for information on how to 8 * contact the author, see the README file. 9 */ 10 11 12/* 13 * High level routines dealing with the output to the screen. 14 */ 15 16#include "less.h" 17#if MSDOS_COMPILER==WIN32C 18#include "windows.h" 19#endif 20 21public int errmsgs; /* Count of messages displayed by error() */ 22public int need_clr; 23public int final_attr; 24public int at_prompt; 25 26extern int sigs; 27extern int sc_width; 28extern int so_s_width, so_e_width; 29extern int screen_trashed; 30extern int any_display; 31extern int is_tty; 32extern int oldbot; 33 34#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 35extern int ctldisp; 36extern int nm_fg_color, nm_bg_color; 37extern int bo_fg_color, bo_bg_color; 38extern int ul_fg_color, ul_bg_color; 39extern int so_fg_color, so_bg_color; 40extern int bl_fg_color, bl_bg_color; 41#endif 42 43/* 44 * Display the line which is in the line buffer. 45 */ 46 public void 47put_line() 48{ 49 register int c; 50 register int i; 51 int a; 52 53 if (ABORT_SIGS()) 54 { 55 /* 56 * Don't output if a signal is pending. 57 */ 58 screen_trashed = 1; 59 return; 60 } 61 62 final_attr = AT_NORMAL; 63 64 for (i = 0; (c = gline(i, &a)) != '\0'; i++) 65 { 66 at_switch(a); 67 final_attr = a; 68 if (c == '\b') 69 putbs(); 70 else 71 putchr(c); 72 } 73 74 at_exit(); 75} 76 77static char obuf[OUTBUF_SIZE]; 78static char *ob = obuf; 79 80/* 81 * Flush buffered output. 82 * 83 * If we haven't displayed any file data yet, 84 * output messages on error output (file descriptor 2), 85 * otherwise output on standard output (file descriptor 1). 86 * 87 * This has the desirable effect of producing all 88 * error messages on error output if standard output 89 * is directed to a file. It also does the same if 90 * we never produce any real output; for example, if 91 * the input file(s) cannot be opened. If we do 92 * eventually produce output, code in edit() makes 93 * sure these messages can be seen before they are 94 * overwritten or scrolled away. 95 */ 96 public void 97flush() 98{ 99 register int n; 100 register int fd; 101 102 n = ob - obuf; 103 if (n == 0) 104 return; 105 106#if MSDOS_COMPILER==MSOFTC 107 if (is_tty && any_display) 108 { 109 *ob = '\0'; 110 _outtext(obuf); 111 ob = obuf; 112 return; 113 } 114#else 115#if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 116 if (is_tty && any_display) 117 { 118 *ob = '\0'; 119 if (ctldisp != OPT_ONPLUS) 120 WIN32textout(obuf, ob - obuf); 121 else 122 { 123 /* 124 * Look for SGR escape sequences, and convert them 125 * to color commands. Replace bold, underline, 126 * and italic escapes into colors specified via 127 * the -D command-line option. 128 */ 129 char *anchor, *p, *p_next; 130 unsigned char fg, bg; 131#if MSDOS_COMPILER==WIN32C 132 /* Screen colors used by 3x and 4x SGR commands. */ 133 static unsigned char screen_color[] = { 134 0, /* BLACK */ 135 FOREGROUND_RED, 136 FOREGROUND_GREEN, 137 FOREGROUND_RED|FOREGROUND_GREEN, 138 FOREGROUND_BLUE, 139 FOREGROUND_BLUE|FOREGROUND_RED, 140 FOREGROUND_BLUE|FOREGROUND_GREEN, 141 FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED 142 }; 143#else 144 static enum COLORS screen_color[] = { 145 BLACK, RED, GREEN, BROWN, 146 BLUE, MAGENTA, CYAN, LIGHTGRAY 147 }; 148#endif 149 150 for (anchor = p_next = obuf; 151 (p_next = memchr(p_next, ESC, ob - p_next)) != NULL; ) 152 { 153 p = p_next; 154 if (p[1] == '[') /* "ESC-[" sequence */ 155 { 156 if (p > anchor) 157 { 158 /* 159 * If some chars seen since 160 * the last escape sequence, 161 * write them out to the screen. 162 */ 163 WIN32textout(anchor, p-anchor); 164 anchor = p; 165 } 166 p += 2; /* Skip the "ESC-[" */ 167 if (is_ansi_end(*p)) 168 { 169 /* 170 * Handle null escape sequence 171 * "ESC[m", which restores 172 * the normal color. 173 */ 174 p++; 175 anchor = p_next = p; 176 WIN32setcolors(nm_fg_color, nm_bg_color); 177 continue; 178 } 179 p_next = p; 180 181 /* 182 * Select foreground/background colors 183 * based on the escape sequence. 184 */ 185 fg = nm_fg_color; 186 bg = nm_bg_color; 187 while (!is_ansi_end(*p)) 188 { 189 char *q; 190 long code = strtol(p, &q, 10); 191 192 if (*q == '\0') 193 { 194 /* 195 * Incomplete sequence. 196 * Leave it unprocessed 197 * in the buffer. 198 */ 199 int slop = q - anchor; 200 /* {{ strcpy args overlap! }} */ 201 strcpy(obuf, anchor); 202 ob = &obuf[slop]; 203 return; 204 } 205 206 if (q == p || 207 code > 49 || code < 0 || 208 (!is_ansi_end(*q) && *q != ';')) 209 { 210 p_next = q; 211 break; 212 } 213 if (*q == ';') 214 q++; 215 216 switch (code) 217 { 218 default: 219 /* case 0: all attrs off */ 220 /* case 22: bold off */ 221 /* case 23: italic off */ 222 /* case 24: underline off */ 223 /* case 27: inverse off */ 224 fg = nm_fg_color; 225 bg = nm_bg_color; 226 break; 227 case 1: /* bold on */ 228 fg = bo_fg_color; 229 bg = bo_bg_color; 230 break; 231 case 3: /* italic on */ 232 case 7: /* inverse on */ 233 fg = so_fg_color; 234 bg = so_bg_color; 235 break; 236 case 4: /* underline on */ 237 fg = ul_fg_color; 238 bg = ul_bg_color; 239 break; 240 case 5: /* slow blink on */ 241 case 6: /* fast blink on */ 242 fg = bl_fg_color; 243 bg = bl_bg_color; 244 break; 245 case 8: /* concealed on */ 246 fg = (bg & 7) | 8; 247 break; 248 case 30: case 31: case 32: 249 case 33: case 34: case 35: 250 case 36: case 37: 251 fg = (fg & 8) | (screen_color[code - 30]); 252 break; 253 case 39: /* default fg */ 254 fg = nm_fg_color; 255 break; 256 case 40: case 41: case 42: 257 case 43: case 44: case 45: 258 case 46: case 47: 259 bg = (bg & 8) | (screen_color[code - 40]); 260 break; 261 case 49: /* default fg */ 262 bg = nm_bg_color; 263 break; 264 } 265 p = q; 266 } 267 if (!is_ansi_end(*p) || p == p_next) 268 break; 269 fg &= 0xf; 270 bg &= 0xf; 271 WIN32setcolors(fg, bg); 272 p_next = anchor = p + 1; 273 } else 274 p_next++; 275 } 276 277 /* Output what's left in the buffer. */ 278 WIN32textout(anchor, ob - anchor); 279 } 280 ob = obuf; 281 return; 282 } 283#endif 284#endif 285 fd = (any_display) ? 1 : 2; 286 if (write(fd, obuf, n) != n) 287 screen_trashed = 1; 288 ob = obuf; 289} 290 291/* 292 * Output a character. 293 */ 294 public int 295putchr(c) 296 int c; 297{ 298#if 0 /* fake UTF-8 output for testing */ 299 extern int utf_mode; 300 if (utf_mode) 301 { 302 static char ubuf[MAX_UTF_CHAR_LEN]; 303 static int ubuf_len = 0; 304 static int ubuf_index = 0; 305 if (ubuf_len == 0) 306 { 307 ubuf_len = utf_len(c); 308 ubuf_index = 0; 309 } 310 ubuf[ubuf_index++] = c; 311 if (ubuf_index < ubuf_len) 312 return c; 313 c = get_wchar(ubuf) & 0xFF; 314 ubuf_len = 0; 315 } 316#endif 317 if (need_clr) 318 { 319 need_clr = 0; 320 clear_bot(); 321 } 322#if MSDOS_COMPILER 323 if (c == '\n' && is_tty) 324 { 325 /* remove_top(1); */ 326 putchr('\r'); 327 } 328#else 329#ifdef _OSK 330 if (c == '\n' && is_tty) /* In OS-9, '\n' == 0x0D */ 331 putchr(0x0A); 332#endif 333#endif 334 /* 335 * Some versions of flush() write to *ob, so we must flush 336 * when we are still one char from the end of obuf. 337 */ 338 if (ob >= &obuf[sizeof(obuf)-1]) 339 flush(); 340 *ob++ = c; 341 at_prompt = 0; 342 return (c); 343} 344 345/* 346 * Output a string. 347 */ 348 public void 349putstr(s) 350 register char *s; 351{ 352 while (*s != '\0') 353 putchr(*s++); 354} 355 356 357/* 358 * Convert an integral type to a string. 359 */ 360#define TYPE_TO_A_FUNC(funcname, type) \ 361void funcname(num, buf) \ 362 type num; \ 363 char *buf; \ 364{ \ 365 int neg = (num < 0); \ 366 char tbuf[INT_STRLEN_BOUND(num)+2]; \ 367 register char *s = tbuf + sizeof(tbuf); \ 368 if (neg) num = -num; \ 369 *--s = '\0'; \ 370 do { \ 371 *--s = (num % 10) + '0'; \ 372 } while ((num /= 10) != 0); \ 373 if (neg) *--s = '-'; \ 374 strcpy(buf, s); \ 375} 376 377TYPE_TO_A_FUNC(postoa, POSITION) 378TYPE_TO_A_FUNC(linenumtoa, LINENUM) 379TYPE_TO_A_FUNC(inttoa, int) 380 381/* 382 * Output an integer in a given radix. 383 */ 384 static int 385iprint_int(num) 386 int num; 387{ 388 char buf[INT_STRLEN_BOUND(num)]; 389 390 inttoa(num, buf); 391 putstr(buf); 392 return (strlen(buf)); 393} 394 395/* 396 * Output a line number in a given radix. 397 */ 398 static int 399iprint_linenum(num) 400 LINENUM num; 401{ 402 char buf[INT_STRLEN_BOUND(num)]; 403 404 linenumtoa(num, buf); 405 putstr(buf); 406 return (strlen(buf)); 407} 408 409/* 410 * This function implements printf-like functionality 411 * using a more portable argument list mechanism than printf's. 412 */ 413 static int 414less_printf(fmt, parg) 415 register char *fmt; 416 PARG *parg; 417{ 418 register char *s; 419 register int col; 420 421 col = 0; 422 while (*fmt != '\0') 423 { 424 if (*fmt != '%') 425 { 426 putchr(*fmt++); 427 col++; 428 } else 429 { 430 ++fmt; 431 switch (*fmt++) 432 { 433 case 's': 434 s = parg->p_string; 435 parg++; 436 while (*s != '\0') 437 { 438 putchr(*s++); 439 col++; 440 } 441 break; 442 case 'd': 443 col += iprint_int(parg->p_int); 444 parg++; 445 break; 446 case 'n': 447 col += iprint_linenum(parg->p_linenum); 448 parg++; 449 break; 450 } 451 } 452 } 453 return (col); 454} 455 456/* 457 * Get a RETURN. 458 * If some other non-trivial char is pressed, unget it, so it will 459 * become the next command. 460 */ 461 public void 462get_return() 463{ 464 int c; 465 466#if ONLY_RETURN 467 while ((c = getchr()) != '\n' && c != '\r') 468 bell(); 469#else 470 c = getchr(); 471 if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR) 472 ungetcc(c); 473#endif 474} 475 476/* 477 * Output a message in the lower left corner of the screen 478 * and wait for carriage return. 479 */ 480 public void 481error(fmt, parg) 482 char *fmt; 483 PARG *parg; 484{ 485 int col = 0; 486 static char return_to_continue[] = " (press RETURN)"; 487 488 errmsgs++; 489 490 if (any_display && is_tty) 491 { 492 if (!oldbot) 493 squish_check(); 494 at_exit(); 495 clear_bot(); 496 at_enter(AT_STANDOUT); 497 col += so_s_width; 498 } 499 500 col += less_printf(fmt, parg); 501 502 if (!(any_display && is_tty)) 503 { 504 putchr('\n'); 505 return; 506 } 507 508 putstr(return_to_continue); 509 at_exit(); 510 col += sizeof(return_to_continue) + so_e_width; 511 512 get_return(); 513 lower_left(); 514 515 if (col >= sc_width) 516 /* 517 * Printing the message has probably scrolled the screen. 518 * {{ Unless the terminal doesn't have auto margins, 519 * in which case we just hammered on the right margin. }} 520 */ 521 screen_trashed = 1; 522 523 flush(); 524} 525 526static char intr_to_abort[] = "... (interrupt to abort)"; 527 528/* 529 * Output a message in the lower left corner of the screen 530 * and don't wait for carriage return. 531 * Usually used to warn that we are beginning a potentially 532 * time-consuming operation. 533 */ 534 public void 535ierror(fmt, parg) 536 char *fmt; 537 PARG *parg; 538{ 539 at_exit(); 540 clear_bot(); 541 at_enter(AT_STANDOUT); 542 (void) less_printf(fmt, parg); 543 putstr(intr_to_abort); 544 at_exit(); 545 flush(); 546 need_clr = 1; 547} 548 549/* 550 * Output a message in the lower left corner of the screen 551 * and return a single-character response. 552 */ 553 public int 554query(fmt, parg) 555 char *fmt; 556 PARG *parg; 557{ 558 register int c; 559 int col = 0; 560 561 if (any_display && is_tty) 562 clear_bot(); 563 564 (void) less_printf(fmt, parg); 565 c = getchr(); 566 567 if (!(any_display && is_tty)) 568 { 569 putchr('\n'); 570 return (c); 571 } 572 573 lower_left(); 574 if (col >= sc_width) 575 screen_trashed = 1; 576 flush(); 577 578 return (c); 579} 580