1 /* $NetBSD: output.c,v 1.4 2013/09/04 19:44:21 tron Exp $ */ 2 3 /* 4 * Copyright (C) 1984-2012 Mark Nudelman 5 * 6 * You may distribute under the terms of either the GNU General Public 7 * License or the Less License, as specified in the README file. 8 * 9 * For more information, see the README file. 10 */ 11 12 13 /* 14 * High level routines dealing with the output to the screen. 15 */ 16 17 #include "less.h" 18 #if MSDOS_COMPILER==WIN32C 19 #include "windows.h" 20 #endif 21 22 public int errmsgs; /* Count of messages displayed by error() */ 23 public int need_clr; 24 public int final_attr; 25 public int at_prompt; 26 27 extern int sigs; 28 extern int sc_width; 29 extern int so_s_width, so_e_width; 30 extern int screen_trashed; 31 extern int any_display; 32 extern int is_tty; 33 extern int oldbot; 34 35 #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 36 extern int ctldisp; 37 extern int nm_fg_color, nm_bg_color; 38 extern int bo_fg_color, bo_bg_color; 39 extern int ul_fg_color, ul_bg_color; 40 extern int so_fg_color, so_bg_color; 41 extern int bl_fg_color, bl_bg_color; 42 #endif 43 44 /* 45 * Display the line which is in the line buffer. 46 */ 47 public void 48 put_line() 49 { 50 register int c; 51 register int i; 52 int a; 53 54 if (ABORT_SIGS()) 55 { 56 /* 57 * Don't output if a signal is pending. 58 */ 59 screen_trashed = 1; 60 return; 61 } 62 63 final_attr = AT_NORMAL; 64 65 for (i = 0; (c = gline(i, &a)) != '\0'; i++) 66 { 67 at_switch(a); 68 final_attr = a; 69 if (c == '\b') 70 putbs(); 71 else 72 putchr(c); 73 } 74 75 at_exit(); 76 } 77 78 static char obuf[OUTBUF_SIZE]; 79 static char *ob = obuf; 80 81 /* 82 * Flush buffered output. 83 * 84 * If we haven't displayed any file data yet, 85 * output messages on error output (file descriptor 2), 86 * otherwise output on standard output (file descriptor 1). 87 * 88 * This has the desirable effect of producing all 89 * error messages on error output if standard output 90 * is directed to a file. It also does the same if 91 * we never produce any real output; for example, if 92 * the input file(s) cannot be opened. If we do 93 * eventually produce output, code in edit() makes 94 * sure these messages can be seen before they are 95 * overwritten or scrolled away. 96 */ 97 public void 98 flush() 99 { 100 register int n; 101 register int fd; 102 103 n = ob - obuf; 104 if (n == 0) 105 return; 106 107 #if MSDOS_COMPILER==MSOFTC 108 if (is_tty && any_display) 109 { 110 *ob = '\0'; 111 _outtext(obuf); 112 ob = obuf; 113 return; 114 } 115 #else 116 #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC 117 if (is_tty && any_display) 118 { 119 *ob = '\0'; 120 if (ctldisp != OPT_ONPLUS) 121 WIN32textout(obuf, ob - obuf); 122 else 123 { 124 /* 125 * Look for SGR escape sequences, and convert them 126 * to color commands. Replace bold, underline, 127 * and italic escapes into colors specified via 128 * the -D command-line option. 129 */ 130 char *anchor, *p, *p_next; 131 unsigned char fg, bg; 132 static unsigned char at; 133 #if MSDOS_COMPILER==WIN32C 134 /* Screen colors used by 3x and 4x SGR commands. */ 135 static unsigned char screen_color[] = { 136 0, /* BLACK */ 137 FOREGROUND_RED, 138 FOREGROUND_GREEN, 139 FOREGROUND_RED|FOREGROUND_GREEN, 140 FOREGROUND_BLUE, 141 FOREGROUND_BLUE|FOREGROUND_RED, 142 FOREGROUND_BLUE|FOREGROUND_GREEN, 143 FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED 144 }; 145 #else 146 static enum COLORS screen_color[] = { 147 BLACK, RED, GREEN, BROWN, 148 BLUE, MAGENTA, CYAN, LIGHTGRAY 149 }; 150 #endif 151 152 for (anchor = p_next = obuf; 153 (p_next = memchr(p_next, ESC, ob - p_next)) != NULL; ) 154 { 155 p = p_next; 156 if (p[1] == '[') /* "ESC-[" sequence */ 157 { 158 if (p > anchor) 159 { 160 /* 161 * If some chars seen since 162 * the last escape sequence, 163 * write them out to the screen. 164 */ 165 WIN32textout(anchor, p-anchor); 166 anchor = p; 167 } 168 p += 2; /* Skip the "ESC-[" */ 169 if (is_ansi_end(*p)) 170 { 171 /* 172 * Handle null escape sequence 173 * "ESC[m", which restores 174 * the normal color. 175 */ 176 p++; 177 anchor = p_next = p; 178 at = 0; 179 WIN32setcolors(nm_fg_color, nm_bg_color); 180 continue; 181 } 182 p_next = p; 183 184 /* 185 * Select foreground/background colors 186 * based on the escape sequence. 187 */ 188 fg = nm_fg_color; 189 bg = nm_bg_color; 190 while (!is_ansi_end(*p)) 191 { 192 char *q; 193 long code = strtol(p, &q, 10); 194 195 if (*q == '\0') 196 { 197 /* 198 * Incomplete sequence. 199 * Leave it unprocessed 200 * in the buffer. 201 */ 202 int slop = q - anchor; 203 /* {{ strcpy args overlap! }} */ 204 strcpy(obuf, anchor); 205 ob = &obuf[slop]; 206 return; 207 } 208 209 if (q == p || 210 code > 49 || code < 0 || 211 (!is_ansi_end(*q) && *q != ';')) 212 { 213 p_next = q; 214 break; 215 } 216 if (*q == ';') 217 q++; 218 219 switch (code) 220 { 221 default: 222 /* case 0: all attrs off */ 223 fg = nm_fg_color; 224 bg = nm_bg_color; 225 at = 0; 226 break; 227 case 1: /* bold on */ 228 at |= 1; 229 break; 230 case 3: /* italic on */ 231 case 7: /* inverse on */ 232 at |= 2; 233 break; 234 case 4: /* underline on */ 235 at |= 4; 236 break; 237 case 5: /* slow blink on */ 238 case 6: /* fast blink on */ 239 at |= 8; 240 break; 241 case 8: /* concealed on */ 242 fg = (bg & 7) | 8; 243 break; 244 case 22: /* bold off */ 245 at &= ~1; 246 break; 247 case 23: /* italic off */ 248 case 27: /* inverse off */ 249 at &= ~2; 250 break; 251 case 24: /* underline off */ 252 at &= ~4; 253 break; 254 case 30: case 31: case 32: 255 case 33: case 34: case 35: 256 case 36: case 37: 257 fg = (fg & 8) | (screen_color[code - 30]); 258 break; 259 case 39: /* default fg */ 260 fg = nm_fg_color; 261 break; 262 case 40: case 41: case 42: 263 case 43: case 44: case 45: 264 case 46: case 47: 265 bg = (bg & 8) | (screen_color[code - 40]); 266 break; 267 case 49: /* default fg */ 268 bg = nm_bg_color; 269 break; 270 } 271 p = q; 272 } 273 if (!is_ansi_end(*p) || p == p_next) 274 break; 275 if (at & 1) 276 { 277 /* 278 * If \e[1m use defined bold 279 * color, else set intensity. 280 */ 281 if (p[-2] == '[') 282 { 283 #if MSDOS_COMPILER==WIN32C 284 fg |= FOREGROUND_INTENSITY; 285 bg |= BACKGROUND_INTENSITY; 286 #else 287 fg = bo_fg_color; 288 bg = bo_bg_color; 289 #endif 290 } else 291 fg |= 8; 292 } else if (at & 2) 293 { 294 fg = so_fg_color; 295 bg = so_bg_color; 296 } else if (at & 4) 297 { 298 fg = ul_fg_color; 299 bg = ul_bg_color; 300 } else if (at & 8) 301 { 302 fg = bl_fg_color; 303 bg = bl_bg_color; 304 } 305 fg &= 0xf; 306 bg &= 0xf; 307 WIN32setcolors(fg, bg); 308 p_next = anchor = p + 1; 309 } else 310 p_next++; 311 } 312 313 /* Output what's left in the buffer. */ 314 WIN32textout(anchor, ob - anchor); 315 } 316 ob = obuf; 317 return; 318 } 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 constant 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 clear_eol(); 551 552 if (col >= sc_width) 553 /* 554 * Printing the message has probably scrolled the screen. 555 * {{ Unless the terminal doesn't have auto margins, 556 * in which case we just hammered on the right margin. }} 557 */ 558 screen_trashed = 1; 559 560 flush(); 561 } 562 563 static char intr_to_abort[] = "... (interrupt to abort)"; 564 565 /* 566 * Output a message in the lower left corner of the screen 567 * and don't wait for carriage return. 568 * Usually used to warn that we are beginning a potentially 569 * time-consuming operation. 570 */ 571 public void 572 ierror(fmt, parg) 573 char *fmt; 574 PARG *parg; 575 { 576 at_exit(); 577 clear_bot(); 578 at_enter(AT_STANDOUT); 579 (void) less_printf(fmt, parg); 580 putstr(intr_to_abort); 581 at_exit(); 582 flush(); 583 need_clr = 1; 584 } 585 586 /* 587 * Output a message in the lower left corner of the screen 588 * and return a single-character response. 589 */ 590 public int 591 query(fmt, parg) 592 char *fmt; 593 PARG *parg; 594 { 595 register int c; 596 int col = 0; 597 598 if (any_display && is_tty) 599 clear_bot(); 600 601 (void) less_printf(fmt, parg); 602 c = getchr(); 603 604 if (!(any_display && is_tty)) 605 { 606 putchr('\n'); 607 return (c); 608 } 609 610 lower_left(); 611 if (col >= sc_width) 612 screen_trashed = 1; 613 flush(); 614 615 return (c); 616 } 617