1 /* 2 * Copyright (C) 1984-2015 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, see the README file. 8 */ 9 10 11 /* 12 * High level routines dealing with the output to the screen. 13 */ 14 15 #include "less.h" 16 #if MSDOS_COMPILER==WIN32C 17 #include "windows.h" 18 #endif 19 20 public int errmsgs; /* Count of messages displayed by error() */ 21 public int need_clr; 22 public int final_attr; 23 public int at_prompt; 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 extern int oldbot; 32 33 #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 34 extern int ctldisp; 35 extern int nm_fg_color, nm_bg_color; 36 extern int bo_fg_color, bo_bg_color; 37 extern int ul_fg_color, ul_bg_color; 38 extern int so_fg_color, so_bg_color; 39 extern int bl_fg_color, bl_bg_color; 40 #endif 41 42 /* 43 * Display the line which is in the line buffer. 44 */ 45 public void 46 put_line(void) 47 { 48 int c; 49 int i; 50 int a; 51 52 if (ABORT_SIGS()) 53 { 54 /* 55 * Don't output if a signal is pending. 56 */ 57 screen_trashed = 1; 58 return; 59 } 60 61 final_attr = AT_NORMAL; 62 63 for (i = 0; (c = gline(i, &a)) != '\0'; i++) 64 { 65 at_switch(a); 66 final_attr = a; 67 if (c == '\b') 68 putbs(); 69 else 70 putchr(c); 71 } 72 73 at_exit(); 74 } 75 76 static char obuf[OUTBUF_SIZE]; 77 static char *ob = obuf; 78 79 /* 80 * Flush buffered output. 81 * 82 * If we haven't displayed any file data yet, 83 * output messages on error output (file descriptor 2), 84 * otherwise output on standard output (file descriptor 1). 85 * 86 * This has the desirable effect of producing all 87 * error messages on error output if standard output 88 * is directed to a file. It also does the same if 89 * we never produce any real output; for example, if 90 * the input file(s) cannot be opened. If we do 91 * eventually produce output, code in edit() makes 92 * sure these messages can be seen before they are 93 * overwritten or scrolled away. 94 */ 95 public void 96 flush(void) 97 { 98 int n; 99 int fd; 100 101 n = (int) (ob - obuf); 102 if (n == 0) 103 return; 104 105 #if MSDOS_COMPILER==MSOFTC 106 if (is_tty && any_display) 107 { 108 *ob = '\0'; 109 _outtext(obuf); 110 ob = obuf; 111 return; 112 } 113 #else 114 #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 115 if (is_tty && any_display) 116 { 117 *ob = '\0'; 118 if (ctldisp != OPT_ONPLUS) 119 WIN32textout(obuf, ob - obuf); 120 else 121 { 122 /* 123 * Look for SGR escape sequences, and convert them 124 * to color commands. Replace bold, underline, 125 * and italic escapes into colors specified via 126 * the -D command-line option. 127 */ 128 char *anchor, *p, *p_next; 129 unsigned char fg, bg; 130 static unsigned char at; 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 at = 0; 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 = (int) (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 /* 276 * If \e[1m use defined bold 277 * color, else set intensity. 278 */ 279 if (p[-2] == '[') 280 { 281 #if MSDOS_COMPILER==WIN32C 282 fg |= FOREGROUND_INTENSITY; 283 bg |= BACKGROUND_INTENSITY; 284 #else 285 fg = bo_fg_color; 286 bg = bo_bg_color; 287 #endif 288 } else 289 fg |= 8; 290 } else if (at & 2) 291 { 292 fg = so_fg_color; 293 bg = so_bg_color; 294 } else if (at & 4) 295 { 296 fg = ul_fg_color; 297 bg = ul_bg_color; 298 } else if (at & 8) 299 { 300 fg = bl_fg_color; 301 bg = bl_bg_color; 302 } 303 fg &= 0xf; 304 bg &= 0xf; 305 WIN32setcolors(fg, bg); 306 p_next = anchor = p + 1; 307 } else 308 p_next++; 309 } 310 311 /* Output what's left in the buffer. */ 312 WIN32textout(anchor, ob - anchor); 313 } 314 ob = obuf; 315 return; 316 } 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(int c) 330 { 331 #if 0 /* fake UTF-8 output for testing */ 332 extern int utf_mode; 333 if (utf_mode) 334 { 335 static char ubuf[MAX_UTF_CHAR_LEN]; 336 static int ubuf_len = 0; 337 static int ubuf_index = 0; 338 if (ubuf_len == 0) 339 { 340 ubuf_len = utf_len(c); 341 ubuf_index = 0; 342 } 343 ubuf[ubuf_index++] = c; 344 if (ubuf_index < ubuf_len) 345 return c; 346 c = get_wchar(ubuf) & 0xFF; 347 ubuf_len = 0; 348 } 349 #endif 350 if (need_clr) 351 { 352 need_clr = 0; 353 clear_bot(); 354 } 355 #if MSDOS_COMPILER 356 if (c == '\n' && is_tty) 357 { 358 /* remove_top(1); */ 359 putchr('\r'); 360 } 361 #else 362 #ifdef _OSK 363 if (c == '\n' && is_tty) /* In OS-9, '\n' == 0x0D */ 364 putchr(0x0A); 365 #endif 366 #endif 367 /* 368 * Some versions of flush() write to *ob, so we must flush 369 * when we are still one char from the end of obuf. 370 */ 371 if (ob >= &obuf[sizeof(obuf)-1]) 372 flush(); 373 *ob++ = c; 374 at_prompt = 0; 375 return (c); 376 } 377 378 /* 379 * Output a string. 380 */ 381 public void 382 putstr(constant char *s) 383 { 384 while (*s != '\0') 385 putchr(*s++); 386 } 387 388 389 /* 390 * Convert an integral type to a string. 391 */ 392 #define TYPE_TO_A_FUNC(funcname, type) \ 393 void funcname(num, buf) \ 394 type num; \ 395 char *buf; \ 396 { \ 397 int neg = (num < 0); \ 398 char tbuf[INT_STRLEN_BOUND(num)+2]; \ 399 char *s = tbuf + sizeof(tbuf); \ 400 if (neg) num = -num; \ 401 *--s = '\0'; \ 402 do { \ 403 *--s = (num % 10) + '0'; \ 404 } while ((num /= 10) != 0); \ 405 if (neg) *--s = '-'; \ 406 strcpy(buf, s); \ 407 } 408 409 TYPE_TO_A_FUNC(postoa, POSITION) 410 TYPE_TO_A_FUNC(linenumtoa, LINENUM) 411 TYPE_TO_A_FUNC(inttoa, int) 412 413 /* 414 * Output an integer in a given radix. 415 */ 416 static int 417 iprint_int(int num) 418 { 419 char buf[INT_STRLEN_BOUND(num)]; 420 421 inttoa(num, buf); 422 putstr(buf); 423 return ((int) strlen(buf)); 424 } 425 426 /* 427 * Output a line number in a given radix. 428 */ 429 static int 430 iprint_linenum(LINENUM num) 431 { 432 char buf[INT_STRLEN_BOUND(num)]; 433 434 linenumtoa(num, buf); 435 putstr(buf); 436 return ((int) strlen(buf)); 437 } 438 439 /* 440 * This function implements printf-like functionality 441 * using a more portable argument list mechanism than printf's. 442 */ 443 static int 444 less_printf(char *fmt, PARG *parg) 445 { 446 char *s; 447 int col; 448 449 col = 0; 450 while (*fmt != '\0') 451 { 452 if (*fmt != '%') 453 { 454 putchr(*fmt++); 455 col++; 456 } else 457 { 458 ++fmt; 459 switch (*fmt++) 460 { 461 case 's': 462 s = parg->p_string; 463 parg++; 464 while (*s != '\0') 465 { 466 putchr(*s++); 467 col++; 468 } 469 break; 470 case 'd': 471 col += iprint_int(parg->p_int); 472 parg++; 473 break; 474 case 'n': 475 col += iprint_linenum(parg->p_linenum); 476 parg++; 477 break; 478 } 479 } 480 } 481 return (col); 482 } 483 484 /* 485 * Get a RETURN. 486 * If some other non-trivial char is pressed, unget it, so it will 487 * become the next command. 488 */ 489 public void 490 get_return(void) 491 { 492 int c; 493 494 #if ONLY_RETURN 495 while ((c = getchr()) != '\n' && c != '\r') 496 bell(); 497 #else 498 c = getchr(); 499 if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR) 500 ungetcc(c); 501 #endif 502 } 503 504 /* 505 * Output a message in the lower left corner of the screen 506 * and wait for carriage return. 507 */ 508 public void 509 error(char *fmt, PARG *parg) 510 { 511 int col = 0; 512 static char return_to_continue[] = " (press RETURN)"; 513 514 errmsgs++; 515 516 if (any_display && is_tty) 517 { 518 if (!oldbot) 519 squish_check(); 520 at_exit(); 521 clear_bot(); 522 at_enter(AT_STANDOUT); 523 col += so_s_width; 524 } 525 526 col += less_printf(fmt, parg); 527 528 if (!(any_display && is_tty)) 529 { 530 putchr('\n'); 531 return; 532 } 533 534 putstr(return_to_continue); 535 at_exit(); 536 col += sizeof(return_to_continue) + so_e_width; 537 538 get_return(); 539 lower_left(); 540 clear_eol(); 541 542 if (col >= sc_width) 543 /* 544 * Printing the message has probably scrolled the screen. 545 * {{ Unless the terminal doesn't have auto margins, 546 * in which case we just hammered on the right margin. }} 547 */ 548 screen_trashed = 1; 549 550 flush(); 551 } 552 553 static char intr_to_abort[] = "... (interrupt to abort)"; 554 555 /* 556 * Output a message in the lower left corner of the screen 557 * and don't wait for carriage return. 558 * Usually used to warn that we are beginning a potentially 559 * time-consuming operation. 560 */ 561 public void 562 ierror(char *fmt, PARG *parg) 563 { 564 at_exit(); 565 clear_bot(); 566 at_enter(AT_STANDOUT); 567 (void) less_printf(fmt, parg); 568 putstr(intr_to_abort); 569 at_exit(); 570 flush(); 571 need_clr = 1; 572 } 573 574 /* 575 * Output a message in the lower left corner of the screen 576 * and return a single-character response. 577 */ 578 public int 579 query(char *fmt, PARG *parg) 580 { 581 int c; 582 int col = 0; 583 584 if (any_display && is_tty) 585 clear_bot(); 586 587 (void) less_printf(fmt, parg); 588 c = getchr(); 589 590 if (!(any_display && is_tty)) 591 { 592 putchr('\n'); 593 return (c); 594 } 595 596 lower_left(); 597 if (col >= sc_width) 598 screen_trashed = 1; 599 flush(); 600 601 return (c); 602 } 603