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