Annotation of src/usr.bin/less/output.c, Revision 1.6
1.1 etheisen 1: /*
1.6 ! shadchin 2: * Copyright (C) 1984-2011 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.6 ! shadchin 24: public int at_prompt;
1.1 etheisen 25:
26: extern int sigs;
27: extern int sc_width;
28: extern int so_s_width, so_e_width;
29: extern int screen_trashed;
30: extern int any_display;
1.5 millert 31: extern int is_tty;
1.6 ! shadchin 32: extern int oldbot;
1.5 millert 33:
1.6 ! shadchin 34: #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
1.5 millert 35: extern int ctldisp;
36: extern int nm_fg_color, nm_bg_color;
37: extern int bo_fg_color, bo_bg_color;
38: extern int ul_fg_color, ul_bg_color;
39: extern int so_fg_color, so_bg_color;
40: extern int bl_fg_color, bl_bg_color;
41: #endif
1.1 etheisen 42:
43: /*
44: * Display the line which is in the line buffer.
45: */
46: public void
47: put_line()
48: {
1.5 millert 49: register int c;
50: register int i;
1.1 etheisen 51: int a;
52:
53: if (ABORT_SIGS())
54: {
55: /*
56: * Don't output if a signal is pending.
57: */
58: screen_trashed = 1;
59: return;
60: }
61:
1.6 ! shadchin 62: final_attr = AT_NORMAL;
1.1 etheisen 63:
64: for (i = 0; (c = gline(i, &a)) != '\0'; i++)
65: {
1.6 ! shadchin 66: at_switch(a);
! 67: final_attr = a;
1.1 etheisen 68: if (c == '\b')
69: putbs();
70: else
71: putchr(c);
72: }
73:
1.6 ! shadchin 74: at_exit();
1.1 etheisen 75: }
76:
1.5 millert 77: static char obuf[OUTBUF_SIZE];
1.1 etheisen 78: static char *ob = obuf;
79:
80: /*
81: * Flush buffered output.
82: *
83: * If we haven't displayed any file data yet,
84: * output messages on error output (file descriptor 2),
85: * otherwise output on standard output (file descriptor 1).
86: *
87: * This has the desirable effect of producing all
88: * error messages on error output if standard output
89: * is directed to a file. It also does the same if
90: * we never produce any real output; for example, if
91: * the input file(s) cannot be opened. If we do
92: * eventually produce output, code in edit() makes
93: * sure these messages can be seen before they are
94: * overwritten or scrolled away.
95: */
96: public void
97: flush()
98: {
1.5 millert 99: register int n;
100: register int fd;
1.1 etheisen 101:
102: n = ob - obuf;
103: if (n == 0)
104: return;
1.5 millert 105:
106: #if MSDOS_COMPILER==MSOFTC
107: if (is_tty && any_display)
108: {
109: *ob = '\0';
110: _outtext(obuf);
111: ob = obuf;
112: return;
113: }
114: #else
1.6 ! shadchin 115: #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
1.5 millert 116: if (is_tty && any_display)
117: {
118: *ob = '\0';
119: if (ctldisp != OPT_ONPLUS)
1.6 ! shadchin 120: WIN32textout(obuf, ob - obuf);
1.5 millert 121: else
122: {
123: /*
124: * Look for SGR escape sequences, and convert them
125: * to color commands. Replace bold, underline,
126: * and italic escapes into colors specified via
127: * the -D command-line option.
128: */
129: char *anchor, *p, *p_next;
1.6 ! shadchin 130: unsigned char fg, bg;
! 131: static unsigned char at;
! 132: #if MSDOS_COMPILER==WIN32C
! 133: /* Screen colors used by 3x and 4x SGR commands. */
! 134: static unsigned char screen_color[] = {
! 135: 0, /* BLACK */
! 136: FOREGROUND_RED,
! 137: FOREGROUND_GREEN,
! 138: FOREGROUND_RED|FOREGROUND_GREEN,
! 139: FOREGROUND_BLUE,
! 140: FOREGROUND_BLUE|FOREGROUND_RED,
! 141: FOREGROUND_BLUE|FOREGROUND_GREEN,
! 142: FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED
! 143: };
! 144: #else
1.5 millert 145: static enum COLORS screen_color[] = {
146: BLACK, RED, GREEN, BROWN,
147: BLUE, MAGENTA, CYAN, LIGHTGRAY
148: };
1.6 ! shadchin 149: #endif
1.5 millert 150:
151: for (anchor = p_next = obuf;
1.6 ! shadchin 152: (p_next = memchr(p_next, ESC, ob - p_next)) != NULL; )
1.5 millert 153: {
154: p = p_next;
1.6 ! shadchin 155: if (p[1] == '[') /* "ESC-[" sequence */
1.5 millert 156: {
157: if (p > anchor)
158: {
1.6 ! shadchin 159: /*
! 160: * If some chars seen since
! 161: * the last escape sequence,
! 162: * write them out to the screen.
! 163: */
! 164: WIN32textout(anchor, p-anchor);
1.5 millert 165: anchor = p;
166: }
1.6 ! shadchin 167: p += 2; /* Skip the "ESC-[" */
! 168: if (is_ansi_end(*p))
! 169: {
! 170: /*
! 171: * Handle null escape sequence
! 172: * "ESC[m", which restores
! 173: * the normal color.
! 174: */
! 175: p++;
! 176: anchor = p_next = p;
! 177: WIN32setcolors(nm_fg_color, nm_bg_color);
! 178: continue;
! 179: }
1.5 millert 180: p_next = p;
1.6 ! shadchin 181:
! 182: /*
! 183: * Select foreground/background colors
! 184: * based on the escape sequence.
! 185: */
! 186: fg = nm_fg_color;
! 187: bg = nm_bg_color;
1.5 millert 188: while (!is_ansi_end(*p))
189: {
190: char *q;
191: long code = strtol(p, &q, 10);
192:
1.6 ! shadchin 193: if (*q == '\0')
1.5 millert 194: {
195: /*
196: * Incomplete sequence.
197: * Leave it unprocessed
198: * in the buffer.
199: */
200: int slop = q - anchor;
1.6 ! shadchin 201: /* {{ strlcpy args overlap! }} */
1.5 millert 202: strlcpy(obuf, anchor,
203: sizeof(obuf));
204: ob = &obuf[slop];
205: return;
206: }
207:
1.6 ! shadchin 208: if (q == p ||
! 209: code > 49 || code < 0 ||
! 210: (!is_ansi_end(*q) && *q != ';'))
1.5 millert 211: {
212: p_next = q;
213: break;
214: }
215: if (*q == ';')
216: q++;
217:
218: switch (code)
219: {
1.6 ! shadchin 220: default:
! 221: /* case 0: all attrs off */
! 222: fg = nm_fg_color;
! 223: bg = nm_bg_color;
! 224: at = 0;
! 225: break;
1.5 millert 226: case 1: /* bold on */
1.6 ! shadchin 227: at |= 1;
1.5 millert 228: break;
229: case 3: /* italic on */
1.6 ! shadchin 230: case 7: /* inverse on */
! 231: at |= 2;
1.5 millert 232: break;
233: case 4: /* underline on */
1.6 ! shadchin 234: at |= 4;
! 235: break;
! 236: case 5: /* slow blink on */
! 237: case 6: /* fast blink on */
! 238: at |= 8;
1.5 millert 239: break;
240: case 8: /* concealed on */
241: fg = (bg & 7) | 8;
242: break;
1.6 ! shadchin 243: case 22: /* bold off */
! 244: at &= ~1;
! 245: break;
! 246: case 23: /* italic off */
! 247: case 27: /* inverse off */
! 248: at &= ~2;
! 249: break;
! 250: case 24: /* underline off */
! 251: at &= ~4;
1.5 millert 252: break;
253: case 30: case 31: case 32:
254: case 33: case 34: case 35:
255: case 36: case 37:
256: fg = (fg & 8) | (screen_color[code - 30]);
257: break;
258: case 39: /* default fg */
259: fg = nm_fg_color;
260: break;
261: case 40: case 41: case 42:
262: case 43: case 44: case 45:
263: case 46: case 47:
264: bg = (bg & 8) | (screen_color[code - 40]);
265: break;
266: case 49: /* default fg */
267: bg = nm_bg_color;
268: break;
269: }
270: p = q;
271: }
1.6 ! shadchin 272: if (!is_ansi_end(*p) || p == p_next)
! 273: break;
! 274: if (at & 1)
! 275: {
! 276: fg = bo_fg_color;
! 277: bg = bo_bg_color;
! 278: } else if (at & 2)
! 279: {
! 280: fg = so_fg_color;
! 281: bg = so_bg_color;
! 282: } else if (at & 4)
! 283: {
! 284: fg = ul_fg_color;
! 285: bg = ul_bg_color;
! 286: } else if (at & 8)
1.5 millert 287: {
1.6 ! shadchin 288: fg = bl_fg_color;
! 289: bg = bl_bg_color;
! 290: }
! 291: fg &= 0xf;
! 292: bg &= 0xf;
! 293: WIN32setcolors(fg, bg);
! 294: p_next = anchor = p + 1;
1.5 millert 295: } else
296: p_next++;
297: }
298:
299: /* Output what's left in the buffer. */
1.6 ! shadchin 300: WIN32textout(anchor, ob - anchor);
1.5 millert 301: }
302: ob = obuf;
303: return;
304: }
305: #endif
306: #endif
1.6 ! shadchin 307: fd = (any_display) ? STDOUT_FILENO : STDERR_FILENO;
1.1 etheisen 308: if (write(fd, obuf, n) != n)
309: screen_trashed = 1;
310: ob = obuf;
311: }
312:
313: /*
314: * Output a character.
315: */
316: public int
317: putchr(c)
318: int c;
319: {
1.6 ! shadchin 320: #if 0 /* fake UTF-8 output for testing */
! 321: extern int utf_mode;
! 322: if (utf_mode)
! 323: {
! 324: static char ubuf[MAX_UTF_CHAR_LEN];
! 325: static int ubuf_len = 0;
! 326: static int ubuf_index = 0;
! 327: if (ubuf_len == 0)
! 328: {
! 329: ubuf_len = utf_len(c);
! 330: ubuf_index = 0;
! 331: }
! 332: ubuf[ubuf_index++] = c;
! 333: if (ubuf_index < ubuf_len)
! 334: return c;
! 335: c = get_wchar(ubuf) & 0xFF;
! 336: ubuf_len = 0;
! 337: }
! 338: #endif
1.1 etheisen 339: if (need_clr)
340: {
341: need_clr = 0;
342: clear_bot();
343: }
1.5 millert 344: #if MSDOS_COMPILER
345: if (c == '\n' && is_tty)
346: {
347: /* remove_top(1); */
1.1 etheisen 348: putchr('\r');
1.5 millert 349: }
350: #else
351: #ifdef _OSK
352: if (c == '\n' && is_tty) /* In OS-9, '\n' == 0x0D */
353: putchr(0x0A);
1.1 etheisen 354: #endif
1.5 millert 355: #endif
356: /*
357: * Some versions of flush() write to *ob, so we must flush
358: * when we are still one char from the end of obuf.
359: */
360: if (ob >= &obuf[sizeof(obuf)-1])
361: flush();
1.1 etheisen 362: *ob++ = c;
1.6 ! shadchin 363: at_prompt = 0;
1.1 etheisen 364: return (c);
365: }
366:
367: /*
368: * Output a string.
369: */
370: public void
371: putstr(s)
1.5 millert 372: register char *s;
1.1 etheisen 373: {
374: while (*s != '\0')
375: putchr(*s++);
376: }
377:
378:
379: /*
1.5 millert 380: * Convert an integral type to a string.
381: */
382: #define TYPE_TO_A_FUNC(funcname, type) \
383: void funcname(num, buf, len) \
384: type num; \
385: char *buf; \
386: size_t len; \
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: strlcpy(buf, s, len); \
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: /*
1.1 etheisen 405: * Output an integer in a given radix.
406: */
407: static int
1.5 millert 408: iprint_int(num)
1.1 etheisen 409: int num;
410: {
1.5 millert 411: char buf[INT_STRLEN_BOUND(num)];
1.1 etheisen 412:
1.5 millert 413: inttoa(num, buf, sizeof(buf));
414: putstr(buf);
415: return (strlen(buf));
416: }
1.1 etheisen 417:
1.5 millert 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)];
1.1 etheisen 426:
1.5 millert 427: linenumtoa(num, buf, sizeof(buf));
428: putstr(buf);
429: return (strlen(buf));
1.1 etheisen 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
1.5 millert 437: less_printf(fmt, parg)
438: register char *fmt;
1.1 etheisen 439: PARG *parg;
440: {
1.5 millert 441: register char *s;
442: register int col;
1.1 etheisen 443:
444: col = 0;
445: while (*fmt != '\0')
446: {
447: if (*fmt != '%')
448: {
449: putchr(*fmt++);
450: col++;
451: } else
452: {
453: ++fmt;
1.5 millert 454: switch (*fmt++)
455: {
1.1 etheisen 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':
1.5 millert 466: col += iprint_int(parg->p_int);
467: parg++;
468: break;
469: case 'n':
470: col += iprint_linenum(parg->p_linenum);
1.1 etheisen 471: parg++;
472: break;
473: }
474: }
475: }
476: return (col);
477: }
478:
479: /*
1.5 millert 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: /*
1.1 etheisen 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:
1.5 millert 513: if (any_display && is_tty)
1.1 etheisen 514: {
1.6 ! shadchin 515: if (!oldbot)
! 516: squish_check();
! 517: at_exit();
1.1 etheisen 518: clear_bot();
1.6 ! shadchin 519: at_enter(AT_STANDOUT);
1.1 etheisen 520: col += so_s_width;
521: }
522:
1.5 millert 523: col += less_printf(fmt, parg);
1.1 etheisen 524:
1.5 millert 525: if (!(any_display && is_tty))
1.1 etheisen 526: {
527: putchr('\n');
528: return;
529: }
530:
531: putstr(return_to_continue);
1.6 ! shadchin 532: at_exit();
1.1 etheisen 533: col += sizeof(return_to_continue) + so_e_width;
534:
1.5 millert 535: get_return();
1.1 etheisen 536: lower_left();
1.6 ! shadchin 537: clear_eol();
1.1 etheisen 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: {
1.6 ! shadchin 563: at_exit();
1.1 etheisen 564: clear_bot();
1.6 ! shadchin 565: at_enter(AT_STANDOUT);
1.5 millert 566: (void) less_printf(fmt, parg);
1.1 etheisen 567: putstr(intr_to_abort);
1.6 ! shadchin 568: at_exit();
1.1 etheisen 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: {
1.5 millert 582: register int c;
1.1 etheisen 583: int col = 0;
584:
1.5 millert 585: if (any_display && is_tty)
1.1 etheisen 586: clear_bot();
587:
1.5 millert 588: (void) less_printf(fmt, parg);
1.1 etheisen 589: c = getchr();
590:
1.5 millert 591: if (!(any_display && is_tty))
1.1 etheisen 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: }