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 21 public int errmsgs; /* Count of messages displayed by error() */ 22 public int need_clr; 23 public int final_attr; 24 public int at_prompt; 25 26 extern int sigs; 27 extern int sc_width; 28 extern int so_s_width, so_e_width; 29 extern int screen_trashed; 30 extern int any_display; 31 extern int is_tty; 32 extern int oldbot; 33 34 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 35 extern int ctldisp; 36 extern int nm_fg_color, nm_bg_color; 37 extern int bo_fg_color, bo_bg_color; 38 extern int ul_fg_color, ul_bg_color; 39 extern int so_fg_color, so_bg_color; 40 extern 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 47 put_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 77 static char obuf[OUTBUF_SIZE]; 78 static 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 97 flush() 98 { 99 register int n; 100 register int fd; 101 102 n = ob - obuf; 103 if (n == 0) 104 return; 105 #if MSDOS_COMPILER==WIN32C 106 if (is_tty && any_display) 107 { 108 char *op; 109 DWORD nwritten = 0; 110 CONSOLE_SCREEN_BUFFER_INFO scr; 111 int row; 112 int col; 113 int olen; 114 extern HANDLE con_out; 115 116 olen = ob - obuf; 117 /* 118 * There is a bug in Win32 WriteConsole() if we're 119 * writing in the last cell with a different color. 120 * To avoid color problems in the bottom line, 121 * we scroll the screen manually, before writing. 122 */ 123 GetConsoleScreenBufferInfo(con_out, &scr); 124 col = scr.dwCursorPosition.X; 125 row = scr.dwCursorPosition.Y; 126 for (op = obuf; op < obuf + olen; op++) 127 { 128 if (*op == '\n') 129 { 130 col = 0; 131 row++; 132 } else if (*op == '\r') 133 { 134 col = 0; 135 } else 136 { 137 col++; 138 if (col >= sc_width) 139 { 140 col = 0; 141 row++; 142 } 143 } 144 } 145 if (row > scr.srWindow.Bottom) 146 win32_scroll_up(row - scr.srWindow.Bottom); 147 WriteConsole(con_out, obuf, olen, &nwritten, NULL); 148 ob = obuf; 149 return; 150 } 151 #else 152 #if MSDOS_COMPILER==MSOFTC 153 if (is_tty && any_display) 154 { 155 *ob = '\0'; 156 _outtext(obuf); 157 ob = obuf; 158 return; 159 } 160 #else 161 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 162 if (is_tty && any_display) 163 { 164 *ob = '\0'; 165 if (ctldisp != OPT_ONPLUS) 166 cputs(obuf); 167 else 168 { 169 /* 170 * Look for SGR escape sequences, and convert them 171 * to color commands. Replace bold, underline, 172 * and italic escapes into colors specified via 173 * the -D command-line option. 174 */ 175 char *anchor, *p, *p_next; 176 int buflen = ob - obuf; 177 unsigned char fg, bg, norm_attr; 178 /* 179 * Only dark colors mentioned here, so that 180 * bold has visible effect. 181 */ 182 static enum COLORS screen_color[] = { 183 BLACK, RED, GREEN, BROWN, 184 BLUE, MAGENTA, CYAN, LIGHTGRAY 185 }; 186 187 /* Normal text colors are used as baseline. */ 188 bg = nm_bg_color & 0xf; 189 fg = nm_fg_color & 0xf; 190 norm_attr = (bg << 4) | fg; 191 for (anchor = p_next = obuf; 192 (p_next = memchr (p_next, ESC, 193 buflen - (p_next - obuf))) 194 != NULL; ) 195 { 196 p = p_next; 197 198 /* 199 * Handle the null escape sequence 200 * (ESC-[m), which is used to restore 201 * the original color. 202 */ 203 if (p[1] == '[' && is_ansi_end(p[2])) 204 { 205 textattr(norm_attr); 206 p += 3; 207 anchor = p_next = p; 208 continue; 209 } 210 211 if (p[1] == '[') /* "Esc-[" sequence */ 212 { 213 /* 214 * If some chars seen since 215 * the last escape sequence, 216 * write it out to the screen 217 * using current text attributes. 218 */ 219 if (p > anchor) 220 { 221 *p = '\0'; 222 cputs (anchor); 223 *p = ESC; 224 anchor = p; 225 } 226 p += 2; 227 p_next = p; 228 while (!is_ansi_end(*p)) 229 { 230 char *q; 231 long code = strtol(p, &q, 10); 232 233 if (!*q) 234 { 235 /* 236 * Incomplete sequence. 237 * Leave it unprocessed 238 * in the buffer. 239 */ 240 int slop = q - anchor; 241 strcpy(obuf, anchor); 242 ob = &obuf[slop]; 243 return; 244 } 245 246 if (q == p 247 || code > 49 || code < 0 248 || (!is_ansi_end(*q) 249 && *q != ';')) 250 { 251 p_next = q; 252 break; 253 } 254 if (*q == ';') 255 q++; 256 257 switch (code) 258 { 259 case 1: /* bold on */ 260 fg = bo_fg_color; 261 bg = bo_bg_color; 262 break; 263 case 3: /* italic on */ 264 fg = so_fg_color; 265 bg = so_bg_color; 266 break; 267 case 4: /* underline on */ 268 fg = ul_fg_color; 269 bg = ul_bg_color; 270 break; 271 case 8: /* concealed on */ 272 fg = (bg & 7) | 8; 273 break; 274 case 0: /* all attrs off */ 275 case 22:/* bold off */ 276 case 23:/* italic off */ 277 case 24:/* underline off */ 278 fg = nm_fg_color; 279 bg = nm_bg_color; 280 break; 281 case 30: case 31: case 32: 282 case 33: case 34: case 35: 283 case 36: case 37: 284 fg = (fg & 8) | (screen_color[code - 30]); 285 break; 286 case 39: /* default fg */ 287 fg = nm_fg_color; 288 break; 289 case 40: case 41: case 42: 290 case 43: case 44: case 45: 291 case 46: case 47: 292 bg = (bg & 8) | (screen_color[code - 40]); 293 break; 294 case 49: /* default fg */ 295 bg = nm_bg_color; 296 break; 297 } 298 p = q; 299 } 300 if (is_ansi_end(*p) && p > p_next) 301 { 302 bg &= 15; 303 fg &= 15; 304 textattr ((bg << 4)| fg); 305 p_next = anchor = p + 1; 306 } else 307 break; 308 } else 309 p_next++; 310 } 311 312 /* Output what's left in the buffer. */ 313 cputs (anchor); 314 } 315 ob = obuf; 316 return; 317 } 318 #endif 319 #endif 320 #endif 321 fd = (any_display) ? 1 : 2; 322 if (write(fd, obuf, n) != n) 323 screen_trashed = 1; 324 ob = obuf; 325 } 326 327 /* 328 * Output a character. 329 */ 330 public int 331 putchr(c) 332 int c; 333 { 334 #if 0 /* fake UTF-8 output for testing */ 335 extern int utf_mode; 336 if (utf_mode) 337 { 338 static char ubuf[MAX_UTF_CHAR_LEN]; 339 static int ubuf_len = 0; 340 static int ubuf_index = 0; 341 if (ubuf_len == 0) 342 { 343 ubuf_len = utf_len(c); 344 ubuf_index = 0; 345 } 346 ubuf[ubuf_index++] = c; 347 if (ubuf_index < ubuf_len) 348 return c; 349 c = get_wchar(ubuf) & 0xFF; 350 ubuf_len = 0; 351 } 352 #endif 353 if (need_clr) 354 { 355 need_clr = 0; 356 clear_bot(); 357 } 358 #if MSDOS_COMPILER 359 if (c == '\n' && is_tty) 360 { 361 /* remove_top(1); */ 362 putchr('\r'); 363 } 364 #else 365 #ifdef _OSK 366 if (c == '\n' && is_tty) /* In OS-9, '\n' == 0x0D */ 367 putchr(0x0A); 368 #endif 369 #endif 370 /* 371 * Some versions of flush() write to *ob, so we must flush 372 * when we are still one char from the end of obuf. 373 */ 374 if (ob >= &obuf[sizeof(obuf)-1]) 375 flush(); 376 *ob++ = c; 377 at_prompt = 0; 378 return (c); 379 } 380 381 /* 382 * Output a string. 383 */ 384 public void 385 putstr(s) 386 register char *s; 387 { 388 while (*s != '\0') 389 putchr(*s++); 390 } 391 392 393 /* 394 * Convert an integral type to a string. 395 */ 396 #define TYPE_TO_A_FUNC(funcname, type) \ 397 void funcname(num, buf) \ 398 type num; \ 399 char *buf; \ 400 { \ 401 int neg = (num < 0); \ 402 char tbuf[INT_STRLEN_BOUND(num)+2]; \ 403 register char *s = tbuf + sizeof(tbuf); \ 404 if (neg) num = -num; \ 405 *--s = '\0'; \ 406 do { \ 407 *--s = (num % 10) + '0'; \ 408 } while ((num /= 10) != 0); \ 409 if (neg) *--s = '-'; \ 410 strcpy(buf, s); \ 411 } 412 413 TYPE_TO_A_FUNC(postoa, POSITION) 414 TYPE_TO_A_FUNC(linenumtoa, LINENUM) 415 TYPE_TO_A_FUNC(inttoa, int) 416 417 /* 418 * Output an integer in a given radix. 419 */ 420 static int 421 iprint_int(num) 422 int num; 423 { 424 char buf[INT_STRLEN_BOUND(num)]; 425 426 inttoa(num, buf); 427 putstr(buf); 428 return (strlen(buf)); 429 } 430 431 /* 432 * Output a line number in a given radix. 433 */ 434 static int 435 iprint_linenum(num) 436 LINENUM num; 437 { 438 char buf[INT_STRLEN_BOUND(num)]; 439 440 linenumtoa(num, buf); 441 putstr(buf); 442 return (strlen(buf)); 443 } 444 445 /* 446 * This function implements printf-like functionality 447 * using a more portable argument list mechanism than printf's. 448 */ 449 static int 450 less_printf(fmt, parg) 451 register char *fmt; 452 PARG *parg; 453 { 454 register char *s; 455 register int col; 456 457 col = 0; 458 while (*fmt != '\0') 459 { 460 if (*fmt != '%') 461 { 462 putchr(*fmt++); 463 col++; 464 } else 465 { 466 ++fmt; 467 switch (*fmt++) 468 { 469 case 's': 470 s = parg->p_string; 471 parg++; 472 while (*s != '\0') 473 { 474 putchr(*s++); 475 col++; 476 } 477 break; 478 case 'd': 479 col += iprint_int(parg->p_int); 480 parg++; 481 break; 482 case 'n': 483 col += iprint_linenum(parg->p_linenum); 484 parg++; 485 break; 486 } 487 } 488 } 489 return (col); 490 } 491 492 /* 493 * Get a RETURN. 494 * If some other non-trivial char is pressed, unget it, so it will 495 * become the next command. 496 */ 497 public void 498 get_return() 499 { 500 int c; 501 502 #if ONLY_RETURN 503 while ((c = getchr()) != '\n' && c != '\r') 504 bell(); 505 #else 506 c = getchr(); 507 if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR) 508 ungetcc(c); 509 #endif 510 } 511 512 /* 513 * Output a message in the lower left corner of the screen 514 * and wait for carriage return. 515 */ 516 public void 517 error(fmt, parg) 518 char *fmt; 519 PARG *parg; 520 { 521 int col = 0; 522 static char return_to_continue[] = " (press RETURN)"; 523 524 errmsgs++; 525 526 if (any_display && is_tty) 527 { 528 if (!oldbot) 529 squish_check(); 530 at_exit(); 531 clear_bot(); 532 at_enter(AT_STANDOUT); 533 col += so_s_width; 534 } 535 536 col += less_printf(fmt, parg); 537 538 if (!(any_display && is_tty)) 539 { 540 putchr('\n'); 541 return; 542 } 543 544 putstr(return_to_continue); 545 at_exit(); 546 col += sizeof(return_to_continue) + so_e_width; 547 548 get_return(); 549 lower_left(); 550 551 if (col >= sc_width) 552 /* 553 * Printing the message has probably scrolled the screen. 554 * {{ Unless the terminal doesn't have auto margins, 555 * in which case we just hammered on the right margin. }} 556 */ 557 screen_trashed = 1; 558 559 flush(); 560 } 561 562 static char intr_to_abort[] = "... (interrupt to abort)"; 563 564 /* 565 * Output a message in the lower left corner of the screen 566 * and don't wait for carriage return. 567 * Usually used to warn that we are beginning a potentially 568 * time-consuming operation. 569 */ 570 public void 571 ierror(fmt, parg) 572 char *fmt; 573 PARG *parg; 574 { 575 at_exit(); 576 clear_bot(); 577 at_enter(AT_STANDOUT); 578 (void) less_printf(fmt, parg); 579 putstr(intr_to_abort); 580 at_exit(); 581 flush(); 582 need_clr = 1; 583 } 584 585 /* 586 * Output a message in the lower left corner of the screen 587 * and return a single-character response. 588 */ 589 public int 590 query(fmt, parg) 591 char *fmt; 592 PARG *parg; 593 { 594 register int c; 595 int col = 0; 596 597 if (any_display && is_tty) 598 clear_bot(); 599 600 (void) less_printf(fmt, parg); 601 c = getchr(); 602 603 if (!(any_display && is_tty)) 604 { 605 putchr('\n'); 606 return (c); 607 } 608 609 lower_left(); 610 if (col >= sc_width) 611 screen_trashed = 1; 612 flush(); 613 614 return (c); 615 } 616