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