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