Annotation of src/usr.bin/less/output.c, Revision 1.1.1.2
1.1 etheisen 1: /*
1.1.1.2 ! millert 2: * Copyright (C) 1984-2002 Mark Nudelman
1.1 etheisen 3: *
1.1.1.2 ! 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.1.1.2 ! 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.1.1.2 ! 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.1.1.2 ! 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.1.1.2 ! 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: {
47: register int c;
48: register int i;
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.1.1.2 ! millert 103: final_attr = curr_attr;
1.1 etheisen 104: }
105:
1.1.1.2 ! 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: {
128: register int n;
129: register int fd;
130:
131: n = ob - obuf;
132: if (n == 0)
133: return;
1.1.1.2 ! 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: strcpy(obuf, anchor);
! 271: ob = &obuf[slop];
! 272: return;
! 273: }
! 274:
! 275: if (q == p
! 276: || code > 49 || code < 0
! 277: || (!is_ansi_end(*q)
! 278: && *q != ';'))
! 279: {
! 280: p_next = q;
! 281: break;
! 282: }
! 283: if (*q == ';')
! 284: q++;
! 285:
! 286: switch (code)
! 287: {
! 288: case 1: /* bold on */
! 289: fg = bo_fg_color;
! 290: bg = bo_bg_color;
! 291: break;
! 292: case 3: /* italic on */
! 293: fg = so_fg_color;
! 294: bg = so_bg_color;
! 295: break;
! 296: case 4: /* underline on */
! 297: fg = ul_fg_color;
! 298: bg = ul_bg_color;
! 299: break;
! 300: case 8: /* concealed on */
! 301: fg = (bg & 7) | 8;
! 302: break;
! 303: case 0: /* all attrs off */
! 304: case 22:/* bold off */
! 305: case 23:/* italic off */
! 306: case 24:/* underline off */
! 307: fg = nm_fg_color;
! 308: bg = nm_bg_color;
! 309: break;
! 310: case 30: case 31: case 32:
! 311: case 33: case 34: case 35:
! 312: case 36: case 37:
! 313: fg = (fg & 8) | (screen_color[code - 30]);
! 314: break;
! 315: case 39: /* default fg */
! 316: fg = nm_fg_color;
! 317: break;
! 318: case 40: case 41: case 42:
! 319: case 43: case 44: case 45:
! 320: case 46: case 47:
! 321: bg = (bg & 8) | (screen_color[code - 40]);
! 322: break;
! 323: case 49: /* default fg */
! 324: bg = nm_bg_color;
! 325: break;
! 326: }
! 327: p = q;
! 328: }
! 329: if (is_ansi_end(*p) && p > p_next)
! 330: {
! 331: bg &= 15;
! 332: fg &= 15;
! 333: textattr ((bg << 4)| fg);
! 334: p_next = anchor = p + 1;
! 335: } else
! 336: break;
! 337: } else
! 338: p_next++;
! 339: }
! 340:
! 341: /* Output what's left in the buffer. */
! 342: cputs (anchor);
! 343: }
! 344: ob = obuf;
! 345: return;
! 346: }
! 347: #endif
! 348: #endif
! 349: #endif
1.1 etheisen 350: fd = (any_display) ? 1 : 2;
351: if (write(fd, obuf, n) != n)
352: screen_trashed = 1;
353: ob = obuf;
354: }
355:
356: /*
357: * Output a character.
358: */
359: public int
360: putchr(c)
361: int c;
362: {
363: if (need_clr)
364: {
365: need_clr = 0;
366: clear_bot();
367: }
1.1.1.2 ! millert 368: #if MSDOS_COMPILER
! 369: if (c == '\n' && is_tty)
! 370: {
! 371: /* remove_top(1); */
1.1 etheisen 372: putchr('\r');
1.1.1.2 ! millert 373: }
! 374: #else
! 375: #ifdef _OSK
! 376: if (c == '\n' && is_tty) /* In OS-9, '\n' == 0x0D */
! 377: putchr(0x0A);
! 378: #endif
1.1 etheisen 379: #endif
1.1.1.2 ! millert 380: /*
! 381: * Some versions of flush() write to *ob, so we must flush
! 382: * when we are still one char from the end of obuf.
! 383: */
! 384: if (ob >= &obuf[sizeof(obuf)-1])
! 385: flush();
1.1 etheisen 386: *ob++ = c;
387: return (c);
388: }
389:
390: /*
391: * Output a string.
392: */
393: public void
394: putstr(s)
395: register char *s;
396: {
397: while (*s != '\0')
398: putchr(*s++);
399: }
400:
401:
402: /*
1.1.1.2 ! millert 403: * Convert an integral type to a string.
! 404: */
! 405: #define TYPE_TO_A_FUNC(funcname, type) \
! 406: void funcname(num, buf) \
! 407: type num; \
! 408: char *buf; \
! 409: { \
! 410: int neg = (num < 0); \
! 411: char tbuf[INT_STRLEN_BOUND(num)+2]; \
! 412: register char *s = tbuf + sizeof(tbuf); \
! 413: if (neg) num = -num; \
! 414: *--s = '\0'; \
! 415: do { \
! 416: *--s = (num % 10) + '0'; \
! 417: } while ((num /= 10) != 0); \
! 418: if (neg) *--s = '-'; \
! 419: strcpy(buf, s); \
! 420: }
! 421:
! 422: TYPE_TO_A_FUNC(postoa, POSITION)
! 423: TYPE_TO_A_FUNC(linenumtoa, LINENUM)
! 424: TYPE_TO_A_FUNC(inttoa, int)
! 425:
! 426: /*
1.1 etheisen 427: * Output an integer in a given radix.
428: */
429: static int
1.1.1.2 ! millert 430: iprint_int(num)
1.1 etheisen 431: int num;
432: {
1.1.1.2 ! millert 433: char buf[INT_STRLEN_BOUND(num)];
! 434:
! 435: inttoa(num, buf);
! 436: putstr(buf);
! 437: return (strlen(buf));
! 438: }
! 439:
! 440: /*
! 441: * Output a line number in a given radix.
! 442: */
! 443: static int
! 444: iprint_linenum(num)
! 445: LINENUM num;
! 446: {
! 447: char buf[INT_STRLEN_BOUND(num)];
! 448:
! 449: linenumtoa(num, buf);
! 450: putstr(buf);
! 451: return (strlen(buf));
1.1 etheisen 452: }
453:
454: /*
455: * This function implements printf-like functionality
456: * using a more portable argument list mechanism than printf's.
457: */
458: static int
1.1.1.2 ! millert 459: less_printf(fmt, parg)
1.1 etheisen 460: register char *fmt;
461: PARG *parg;
462: {
463: register char *s;
464: register int col;
465:
466: col = 0;
467: while (*fmt != '\0')
468: {
469: if (*fmt != '%')
470: {
471: putchr(*fmt++);
472: col++;
473: } else
474: {
475: ++fmt;
1.1.1.2 ! millert 476: switch (*fmt++)
! 477: {
1.1 etheisen 478: case 's':
479: s = parg->p_string;
480: parg++;
481: while (*s != '\0')
482: {
483: putchr(*s++);
484: col++;
485: }
486: break;
487: case 'd':
1.1.1.2 ! millert 488: col += iprint_int(parg->p_int);
! 489: parg++;
! 490: break;
! 491: case 'n':
! 492: col += iprint_linenum(parg->p_linenum);
1.1 etheisen 493: parg++;
494: break;
495: }
496: }
497: }
498: return (col);
499: }
500:
501: /*
1.1.1.2 ! millert 502: * Get a RETURN.
! 503: * If some other non-trivial char is pressed, unget it, so it will
! 504: * become the next command.
! 505: */
! 506: public void
! 507: get_return()
! 508: {
! 509: int c;
! 510:
! 511: #if ONLY_RETURN
! 512: while ((c = getchr()) != '\n' && c != '\r')
! 513: bell();
! 514: #else
! 515: c = getchr();
! 516: if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
! 517: ungetcc(c);
! 518: #endif
! 519: }
! 520:
! 521: /*
1.1 etheisen 522: * Output a message in the lower left corner of the screen
523: * and wait for carriage return.
524: */
525: public void
526: error(fmt, parg)
527: char *fmt;
528: PARG *parg;
529: {
530: int col = 0;
531: static char return_to_continue[] = " (press RETURN)";
532:
533: errmsgs++;
534:
1.1.1.2 ! millert 535: if (any_display && is_tty)
1.1 etheisen 536: {
537: clear_bot();
538: so_enter();
539: col += so_s_width;
540: }
541:
1.1.1.2 ! millert 542: col += less_printf(fmt, parg);
1.1 etheisen 543:
1.1.1.2 ! millert 544: if (!(any_display && is_tty))
1.1 etheisen 545: {
546: putchr('\n');
547: return;
548: }
549:
550: putstr(return_to_continue);
551: so_exit();
552: col += sizeof(return_to_continue) + so_e_width;
553:
1.1.1.2 ! millert 554: get_return();
1.1 etheisen 555: lower_left();
556:
557: if (col >= sc_width)
558: /*
559: * Printing the message has probably scrolled the screen.
560: * {{ Unless the terminal doesn't have auto margins,
561: * in which case we just hammered on the right margin. }}
562: */
563: screen_trashed = 1;
564:
565: flush();
566: }
567:
568: static char intr_to_abort[] = "... (interrupt to abort)";
569:
570: /*
571: * Output a message in the lower left corner of the screen
572: * and don't wait for carriage return.
573: * Usually used to warn that we are beginning a potentially
574: * time-consuming operation.
575: */
576: public void
577: ierror(fmt, parg)
578: char *fmt;
579: PARG *parg;
580: {
581: clear_bot();
582: so_enter();
1.1.1.2 ! millert 583: (void) less_printf(fmt, parg);
1.1 etheisen 584: putstr(intr_to_abort);
585: so_exit();
586: flush();
587: need_clr = 1;
588: }
589:
590: /*
591: * Output a message in the lower left corner of the screen
592: * and return a single-character response.
593: */
594: public int
595: query(fmt, parg)
596: char *fmt;
597: PARG *parg;
598: {
599: register int c;
600: int col = 0;
601:
1.1.1.2 ! millert 602: if (any_display && is_tty)
1.1 etheisen 603: clear_bot();
604:
1.1.1.2 ! millert 605: (void) less_printf(fmt, parg);
1.1 etheisen 606: c = getchr();
607:
1.1.1.2 ! millert 608: if (!(any_display && is_tty))
1.1 etheisen 609: {
610: putchr('\n');
611: return (c);
612: }
613:
614: lower_left();
615: if (col >= sc_width)
616: screen_trashed = 1;
617: flush();
618:
619: return (c);
620: }