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