Annotation of src/usr.bin/less/output.c, Revision 1.1.1.3
1.1 etheisen 1: /*
1.1.1.3 ! shadchin 2: * Copyright (C) 1984-2011 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.1.3 ! 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.1.1.2 millert 31: extern int is_tty;
1.1.1.3 ! shadchin 32: extern int oldbot;
1.1.1.2 millert 33:
1.1.1.3 ! shadchin 34: #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
1.1.1.2 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: {
49: register int c;
50: register int i;
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.1.1.3 ! shadchin 62: final_attr = AT_NORMAL;
1.1 etheisen 63:
64: for (i = 0; (c = gline(i, &a)) != '\0'; i++)
65: {
1.1.1.3 ! 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.1.1.3 ! shadchin 74: at_exit();
1.1 etheisen 75: }
76:
1.1.1.2 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: {
99: register int n;
100: register int fd;
101:
102: n = ob - obuf;
103: if (n == 0)
104: return;
1.1.1.2 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.1.1.3 ! shadchin 115: #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
1.1.1.2 millert 116: if (is_tty && any_display)
117: {
118: *ob = '\0';
119: if (ctldisp != OPT_ONPLUS)
1.1.1.3 ! shadchin 120: WIN32textout(obuf, ob - obuf);
1.1.1.2 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.1.1.3 ! 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.1.1.2 millert 145: static enum COLORS screen_color[] = {
146: BLACK, RED, GREEN, BROWN,
147: BLUE, MAGENTA, CYAN, LIGHTGRAY
148: };
1.1.1.3 ! shadchin 149: #endif
1.1.1.2 millert 150:
151: for (anchor = p_next = obuf;
1.1.1.3 ! shadchin 152: (p_next = memchr(p_next, ESC, ob - p_next)) != NULL; )
1.1.1.2 millert 153: {
154: p = p_next;
1.1.1.3 ! shadchin 155: if (p[1] == '[') /* "ESC-[" sequence */
1.1.1.2 millert 156: {
157: if (p > anchor)
158: {
1.1.1.3 ! 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.1.1.2 millert 165: anchor = p;
166: }
1.1.1.3 ! 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.1.1.2 millert 180: p_next = p;
1.1.1.3 ! 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.1.1.2 millert 188: while (!is_ansi_end(*p))
189: {
190: char *q;
191: long code = strtol(p, &q, 10);
192:
1.1.1.3 ! shadchin 193: if (*q == '\0')
1.1.1.2 millert 194: {
195: /*
196: * Incomplete sequence.
197: * Leave it unprocessed
198: * in the buffer.
199: */
200: int slop = q - anchor;
1.1.1.3 ! shadchin 201: /* {{ strcpy args overlap! }} */
1.1.1.2 millert 202: strcpy(obuf, anchor);
203: ob = &obuf[slop];
204: return;
205: }
206:
1.1.1.3 ! shadchin 207: if (q == p ||
! 208: code > 49 || code < 0 ||
! 209: (!is_ansi_end(*q) && *q != ';'))
1.1.1.2 millert 210: {
211: p_next = q;
212: break;
213: }
214: if (*q == ';')
215: q++;
216:
217: switch (code)
218: {
1.1.1.3 ! shadchin 219: default:
! 220: /* case 0: all attrs off */
! 221: fg = nm_fg_color;
! 222: bg = nm_bg_color;
! 223: at = 0;
! 224: break;
1.1.1.2 millert 225: case 1: /* bold on */
1.1.1.3 ! shadchin 226: at |= 1;
1.1.1.2 millert 227: break;
228: case 3: /* italic on */
1.1.1.3 ! shadchin 229: case 7: /* inverse on */
! 230: at |= 2;
1.1.1.2 millert 231: break;
232: case 4: /* underline on */
1.1.1.3 ! shadchin 233: at |= 4;
! 234: break;
! 235: case 5: /* slow blink on */
! 236: case 6: /* fast blink on */
! 237: at |= 8;
1.1.1.2 millert 238: break;
239: case 8: /* concealed on */
240: fg = (bg & 7) | 8;
241: break;
1.1.1.3 ! shadchin 242: case 22: /* bold off */
! 243: at &= ~1;
! 244: break;
! 245: case 23: /* italic off */
! 246: case 27: /* inverse off */
! 247: at &= ~2;
! 248: break;
! 249: case 24: /* underline off */
! 250: at &= ~4;
1.1.1.2 millert 251: break;
252: case 30: case 31: case 32:
253: case 33: case 34: case 35:
254: case 36: case 37:
255: fg = (fg & 8) | (screen_color[code - 30]);
256: break;
257: case 39: /* default fg */
258: fg = nm_fg_color;
259: break;
260: case 40: case 41: case 42:
261: case 43: case 44: case 45:
262: case 46: case 47:
263: bg = (bg & 8) | (screen_color[code - 40]);
264: break;
265: case 49: /* default fg */
266: bg = nm_bg_color;
267: break;
268: }
269: p = q;
270: }
1.1.1.3 ! shadchin 271: if (!is_ansi_end(*p) || p == p_next)
1.1.1.2 millert 272: break;
1.1.1.3 ! shadchin 273: if (at & 1)
! 274: {
! 275: fg = bo_fg_color;
! 276: bg = bo_bg_color;
! 277: } else if (at & 2)
! 278: {
! 279: fg = so_fg_color;
! 280: bg = so_bg_color;
! 281: } else if (at & 4)
! 282: {
! 283: fg = ul_fg_color;
! 284: bg = ul_bg_color;
! 285: } else if (at & 8)
! 286: {
! 287: fg = bl_fg_color;
! 288: bg = bl_bg_color;
! 289: }
! 290: fg &= 0xf;
! 291: bg &= 0xf;
! 292: WIN32setcolors(fg, bg);
! 293: p_next = anchor = p + 1;
1.1.1.2 millert 294: } else
295: p_next++;
296: }
297:
298: /* Output what's left in the buffer. */
1.1.1.3 ! shadchin 299: WIN32textout(anchor, ob - anchor);
1.1.1.2 millert 300: }
301: ob = obuf;
302: return;
303: }
304: #endif
305: #endif
1.1 etheisen 306: fd = (any_display) ? 1 : 2;
307: if (write(fd, obuf, n) != n)
308: screen_trashed = 1;
309: ob = obuf;
310: }
311:
312: /*
313: * Output a character.
314: */
315: public int
316: putchr(c)
317: int c;
318: {
1.1.1.3 ! shadchin 319: #if 0 /* fake UTF-8 output for testing */
! 320: extern int utf_mode;
! 321: if (utf_mode)
! 322: {
! 323: static char ubuf[MAX_UTF_CHAR_LEN];
! 324: static int ubuf_len = 0;
! 325: static int ubuf_index = 0;
! 326: if (ubuf_len == 0)
! 327: {
! 328: ubuf_len = utf_len(c);
! 329: ubuf_index = 0;
! 330: }
! 331: ubuf[ubuf_index++] = c;
! 332: if (ubuf_index < ubuf_len)
! 333: return c;
! 334: c = get_wchar(ubuf) & 0xFF;
! 335: ubuf_len = 0;
! 336: }
! 337: #endif
1.1 etheisen 338: if (need_clr)
339: {
340: need_clr = 0;
341: clear_bot();
342: }
1.1.1.2 millert 343: #if MSDOS_COMPILER
344: if (c == '\n' && is_tty)
345: {
346: /* remove_top(1); */
1.1 etheisen 347: putchr('\r');
1.1.1.2 millert 348: }
349: #else
350: #ifdef _OSK
351: if (c == '\n' && is_tty) /* In OS-9, '\n' == 0x0D */
352: putchr(0x0A);
353: #endif
1.1 etheisen 354: #endif
1.1.1.2 millert 355: /*
356: * Some versions of flush() write to *ob, so we must flush
357: * when we are still one char from the end of obuf.
358: */
359: if (ob >= &obuf[sizeof(obuf)-1])
360: flush();
1.1 etheisen 361: *ob++ = c;
1.1.1.3 ! shadchin 362: at_prompt = 0;
1.1 etheisen 363: return (c);
364: }
365:
366: /*
367: * Output a string.
368: */
369: public void
370: putstr(s)
371: register char *s;
372: {
373: while (*s != '\0')
374: putchr(*s++);
375: }
376:
377:
378: /*
1.1.1.2 millert 379: * Convert an integral type to a string.
380: */
381: #define TYPE_TO_A_FUNC(funcname, type) \
382: void funcname(num, buf) \
383: type num; \
384: char *buf; \
385: { \
386: int neg = (num < 0); \
387: char tbuf[INT_STRLEN_BOUND(num)+2]; \
388: register char *s = tbuf + sizeof(tbuf); \
389: if (neg) num = -num; \
390: *--s = '\0'; \
391: do { \
392: *--s = (num % 10) + '0'; \
393: } while ((num /= 10) != 0); \
394: if (neg) *--s = '-'; \
395: strcpy(buf, s); \
396: }
397:
398: TYPE_TO_A_FUNC(postoa, POSITION)
399: TYPE_TO_A_FUNC(linenumtoa, LINENUM)
400: TYPE_TO_A_FUNC(inttoa, int)
401:
402: /*
1.1 etheisen 403: * Output an integer in a given radix.
404: */
405: static int
1.1.1.2 millert 406: iprint_int(num)
1.1 etheisen 407: int num;
408: {
1.1.1.2 millert 409: char buf[INT_STRLEN_BOUND(num)];
410:
411: inttoa(num, buf);
412: putstr(buf);
413: return (strlen(buf));
414: }
415:
416: /*
417: * Output a line number in a given radix.
418: */
419: static int
420: iprint_linenum(num)
421: LINENUM num;
422: {
423: char buf[INT_STRLEN_BOUND(num)];
424:
425: linenumtoa(num, buf);
426: putstr(buf);
427: return (strlen(buf));
1.1 etheisen 428: }
429:
430: /*
431: * This function implements printf-like functionality
432: * using a more portable argument list mechanism than printf's.
433: */
434: static int
1.1.1.2 millert 435: less_printf(fmt, parg)
1.1 etheisen 436: register char *fmt;
437: PARG *parg;
438: {
439: register char *s;
440: register int col;
441:
442: col = 0;
443: while (*fmt != '\0')
444: {
445: if (*fmt != '%')
446: {
447: putchr(*fmt++);
448: col++;
449: } else
450: {
451: ++fmt;
1.1.1.2 millert 452: switch (*fmt++)
453: {
1.1 etheisen 454: case 's':
455: s = parg->p_string;
456: parg++;
457: while (*s != '\0')
458: {
459: putchr(*s++);
460: col++;
461: }
462: break;
463: case 'd':
1.1.1.2 millert 464: col += iprint_int(parg->p_int);
465: parg++;
466: break;
467: case 'n':
468: col += iprint_linenum(parg->p_linenum);
1.1 etheisen 469: parg++;
470: break;
471: }
472: }
473: }
474: return (col);
475: }
476:
477: /*
1.1.1.2 millert 478: * Get a RETURN.
479: * If some other non-trivial char is pressed, unget it, so it will
480: * become the next command.
481: */
482: public void
483: get_return()
484: {
485: int c;
486:
487: #if ONLY_RETURN
488: while ((c = getchr()) != '\n' && c != '\r')
489: bell();
490: #else
491: c = getchr();
492: if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
493: ungetcc(c);
494: #endif
495: }
496:
497: /*
1.1 etheisen 498: * Output a message in the lower left corner of the screen
499: * and wait for carriage return.
500: */
501: public void
502: error(fmt, parg)
503: char *fmt;
504: PARG *parg;
505: {
506: int col = 0;
507: static char return_to_continue[] = " (press RETURN)";
508:
509: errmsgs++;
510:
1.1.1.2 millert 511: if (any_display && is_tty)
1.1 etheisen 512: {
1.1.1.3 ! shadchin 513: if (!oldbot)
! 514: squish_check();
! 515: at_exit();
1.1 etheisen 516: clear_bot();
1.1.1.3 ! shadchin 517: at_enter(AT_STANDOUT);
1.1 etheisen 518: col += so_s_width;
519: }
520:
1.1.1.2 millert 521: col += less_printf(fmt, parg);
1.1 etheisen 522:
1.1.1.2 millert 523: if (!(any_display && is_tty))
1.1 etheisen 524: {
525: putchr('\n');
526: return;
527: }
528:
529: putstr(return_to_continue);
1.1.1.3 ! shadchin 530: at_exit();
1.1 etheisen 531: col += sizeof(return_to_continue) + so_e_width;
532:
1.1.1.2 millert 533: get_return();
1.1 etheisen 534: lower_left();
1.1.1.3 ! shadchin 535: clear_eol();
1.1 etheisen 536:
537: if (col >= sc_width)
538: /*
539: * Printing the message has probably scrolled the screen.
540: * {{ Unless the terminal doesn't have auto margins,
541: * in which case we just hammered on the right margin. }}
542: */
543: screen_trashed = 1;
544:
545: flush();
546: }
547:
548: static char intr_to_abort[] = "... (interrupt to abort)";
549:
550: /*
551: * Output a message in the lower left corner of the screen
552: * and don't wait for carriage return.
553: * Usually used to warn that we are beginning a potentially
554: * time-consuming operation.
555: */
556: public void
557: ierror(fmt, parg)
558: char *fmt;
559: PARG *parg;
560: {
1.1.1.3 ! shadchin 561: at_exit();
1.1 etheisen 562: clear_bot();
1.1.1.3 ! shadchin 563: at_enter(AT_STANDOUT);
1.1.1.2 millert 564: (void) less_printf(fmt, parg);
1.1 etheisen 565: putstr(intr_to_abort);
1.1.1.3 ! shadchin 566: at_exit();
1.1 etheisen 567: flush();
568: need_clr = 1;
569: }
570:
571: /*
572: * Output a message in the lower left corner of the screen
573: * and return a single-character response.
574: */
575: public int
576: query(fmt, parg)
577: char *fmt;
578: PARG *parg;
579: {
580: register int c;
581: int col = 0;
582:
1.1.1.2 millert 583: if (any_display && is_tty)
1.1 etheisen 584: clear_bot();
585:
1.1.1.2 millert 586: (void) less_printf(fmt, parg);
1.1 etheisen 587: c = getchr();
588:
1.1.1.2 millert 589: if (!(any_display && is_tty))
1.1 etheisen 590: {
591: putchr('\n');
592: return (c);
593: }
594:
595: lower_left();
596: if (col >= sc_width)
597: screen_trashed = 1;
598: flush();
599:
600: return (c);
601: }