Annotation of src/usr.bin/less/output.c, Revision 1.1.1.4
1.1 etheisen 1: /*
1.1.1.4 ! shadchin 2: * Copyright (C) 1984-2012 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.4 ! shadchin 7: * For more information, see the README file.
1.1 etheisen 8: */
9:
10:
11: /*
12: * High level routines dealing with the output to the screen.
13: */
14:
15: #include "less.h"
1.1.1.2 millert 16: #if MSDOS_COMPILER==WIN32C
17: #include "windows.h"
18: #endif
1.1 etheisen 19:
20: public int errmsgs; /* Count of messages displayed by error() */
21: public int need_clr;
1.1.1.2 millert 22: public int final_attr;
1.1.1.3 shadchin 23: public int at_prompt;
1.1 etheisen 24:
1.1.1.4 ! shadchin 25: extern volatile sig_atomic_t sigs;
1.1 etheisen 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;
1.1.1.3 shadchin 31: extern int oldbot;
1.1.1.2 millert 32:
1.1.1.3 shadchin 33: #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
1.1.1.2 millert 34: extern int ctldisp;
35: extern int nm_fg_color, nm_bg_color;
36: extern int bo_fg_color, bo_bg_color;
37: extern int ul_fg_color, ul_bg_color;
38: extern int so_fg_color, so_bg_color;
39: extern int bl_fg_color, bl_bg_color;
40: #endif
1.1 etheisen 41:
42: /*
43: * Display the line which is in the line buffer.
44: */
45: public void
46: put_line()
47: {
48: register int c;
49: register int i;
50: int a;
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:
1.1.1.3 shadchin 61: final_attr = AT_NORMAL;
1.1 etheisen 62:
63: for (i = 0; (c = gline(i, &a)) != '\0'; i++)
64: {
1.1.1.3 shadchin 65: at_switch(a);
66: final_attr = a;
1.1 etheisen 67: if (c == '\b')
68: putbs();
69: else
70: putchr(c);
71: }
72:
1.1.1.3 shadchin 73: at_exit();
1.1 etheisen 74: }
75:
1.1.1.2 millert 76: static char obuf[OUTBUF_SIZE];
1.1 etheisen 77: static char *ob = obuf;
78:
79: /*
80: * Flush buffered output.
81: *
82: * If we haven't displayed any file data yet,
83: * output messages on error output (file descriptor 2),
84: * otherwise output on standard output (file descriptor 1).
85: *
86: * This has the desirable effect of producing all
87: * error messages on error output if standard output
88: * is directed to a file. It also does the same if
89: * we never produce any real output; for example, if
90: * the input file(s) cannot be opened. If we do
91: * eventually produce output, code in edit() makes
92: * sure these messages can be seen before they are
93: * overwritten or scrolled away.
94: */
95: public void
96: flush()
97: {
98: register int n;
99: register int fd;
1.1.1.4 ! shadchin 100: ssize_t nwritten;
1.1 etheisen 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;
1.1.1.4 ! shadchin 177: at = 0;
1.1.1.3 shadchin 178: WIN32setcolors(nm_fg_color, nm_bg_color);
179: continue;
180: }
1.1.1.2 millert 181: p_next = p;
1.1.1.3 shadchin 182:
183: /*
184: * Select foreground/background colors
185: * based on the escape sequence.
186: */
187: fg = nm_fg_color;
188: bg = nm_bg_color;
1.1.1.2 millert 189: while (!is_ansi_end(*p))
190: {
191: char *q;
192: long code = strtol(p, &q, 10);
193:
1.1.1.3 shadchin 194: if (*q == '\0')
1.1.1.2 millert 195: {
196: /*
197: * Incomplete sequence.
198: * Leave it unprocessed
199: * in the buffer.
200: */
201: int slop = q - anchor;
1.1.1.4 ! shadchin 202: /* {{ strlcpy args overlap! }} */
! 203: strlcpy(obuf, anchor,
! 204: sizeof(obuf));
1.1.1.2 millert 205: ob = &obuf[slop];
206: return;
207: }
208:
1.1.1.3 shadchin 209: if (q == p ||
210: code > 49 || code < 0 ||
211: (!is_ansi_end(*q) && *q != ';'))
1.1.1.2 millert 212: {
213: p_next = q;
214: break;
215: }
216: if (*q == ';')
217: q++;
218:
219: switch (code)
220: {
1.1.1.3 shadchin 221: default:
222: /* case 0: all attrs off */
223: fg = nm_fg_color;
224: bg = nm_bg_color;
225: at = 0;
226: break;
1.1.1.2 millert 227: case 1: /* bold on */
1.1.1.3 shadchin 228: at |= 1;
1.1.1.2 millert 229: break;
230: case 3: /* italic on */
1.1.1.3 shadchin 231: case 7: /* inverse on */
232: at |= 2;
1.1.1.2 millert 233: break;
234: case 4: /* underline on */
1.1.1.3 shadchin 235: at |= 4;
236: break;
237: case 5: /* slow blink on */
238: case 6: /* fast blink on */
239: at |= 8;
1.1.1.2 millert 240: break;
241: case 8: /* concealed on */
242: fg = (bg & 7) | 8;
243: break;
1.1.1.3 shadchin 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;
1.1.1.2 millert 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: }
1.1.1.3 shadchin 273: if (!is_ansi_end(*p) || p == p_next)
1.1.1.2 millert 274: break;
1.1.1.3 shadchin 275: if (at & 1)
276: {
1.1.1.4 ! shadchin 277: /*
! 278: * If \e[1m use defined bold
! 279: * color, else set intensity.
! 280: */
! 281: if (p[-2] == '[')
! 282: {
! 283: #if MSDOS_COMPILER==WIN32C
! 284: fg |= FOREGROUND_INTENSITY;
! 285: bg |= BACKGROUND_INTENSITY;
! 286: #else
1.1.1.3 shadchin 287: fg = bo_fg_color;
288: bg = bo_bg_color;
1.1.1.4 ! shadchin 289: #endif
! 290: } else
! 291: fg |= 8;
1.1.1.3 shadchin 292: } else if (at & 2)
293: {
1.1.1.4 ! shadchin 294: fg = so_fg_color;
! 295: bg = so_bg_color;
1.1.1.3 shadchin 296: } else if (at & 4)
297: {
1.1.1.4 ! shadchin 298: fg = ul_fg_color;
! 299: bg = ul_bg_color;
1.1.1.3 shadchin 300: } else if (at & 8)
301: {
1.1.1.4 ! shadchin 302: fg = bl_fg_color;
! 303: bg = bl_bg_color;
1.1.1.3 shadchin 304: }
305: fg &= 0xf;
306: bg &= 0xf;
307: WIN32setcolors(fg, bg);
308: p_next = anchor = p + 1;
1.1.1.2 millert 309: } else
310: p_next++;
311: }
312:
313: /* Output what's left in the buffer. */
1.1.1.3 shadchin 314: WIN32textout(anchor, ob - anchor);
1.1.1.2 millert 315: }
316: ob = obuf;
317: return;
318: }
319: #endif
320: #endif
1.1.1.4 ! shadchin 321: fd = (any_display) ? STDOUT_FILENO : STDERR_FILENO;
! 322: nwritten = write(fd, obuf, n);
! 323: if (nwritten != n) {
! 324: if (nwritten == -1)
! 325: quit(QUIT_ERROR);
1.1 etheisen 326: screen_trashed = 1;
1.1.1.4 ! shadchin 327: }
1.1 etheisen 328: ob = obuf;
329: }
330:
331: /*
332: * Output a character.
333: */
334: public int
335: putchr(c)
336: int c;
337: {
1.1.1.3 shadchin 338: #if 0 /* fake UTF-8 output for testing */
339: extern int utf_mode;
340: if (utf_mode)
341: {
342: static char ubuf[MAX_UTF_CHAR_LEN];
343: static int ubuf_len = 0;
344: static int ubuf_index = 0;
345: if (ubuf_len == 0)
346: {
347: ubuf_len = utf_len(c);
348: ubuf_index = 0;
349: }
350: ubuf[ubuf_index++] = c;
351: if (ubuf_index < ubuf_len)
352: return c;
353: c = get_wchar(ubuf) & 0xFF;
354: ubuf_len = 0;
355: }
356: #endif
1.1 etheisen 357: if (need_clr)
358: {
359: need_clr = 0;
360: clear_bot();
361: }
1.1.1.2 millert 362: #if MSDOS_COMPILER
363: if (c == '\n' && is_tty)
364: {
365: /* remove_top(1); */
1.1 etheisen 366: putchr('\r');
1.1.1.2 millert 367: }
368: #else
369: #ifdef _OSK
370: if (c == '\n' && is_tty) /* In OS-9, '\n' == 0x0D */
371: putchr(0x0A);
372: #endif
1.1 etheisen 373: #endif
1.1.1.2 millert 374: /*
375: * Some versions of flush() write to *ob, so we must flush
376: * when we are still one char from the end of obuf.
377: */
378: if (ob >= &obuf[sizeof(obuf)-1])
379: flush();
1.1 etheisen 380: *ob++ = c;
1.1.1.3 shadchin 381: at_prompt = 0;
1.1 etheisen 382: return (c);
383: }
384:
385: /*
386: * Output a string.
387: */
388: public void
389: putstr(s)
390: register char *s;
391: {
392: while (*s != '\0')
393: putchr(*s++);
394: }
395:
396:
397: /*
1.1.1.2 millert 398: * Convert an integral type to a string.
399: */
400: #define TYPE_TO_A_FUNC(funcname, type) \
1.1.1.4 ! shadchin 401: void funcname(num, buf, len) \
1.1.1.2 millert 402: type num; \
403: char *buf; \
1.1.1.4 ! shadchin 404: size_t len; \
1.1.1.2 millert 405: { \
406: int neg = (num < 0); \
407: char tbuf[INT_STRLEN_BOUND(num)+2]; \
408: register char *s = tbuf + sizeof(tbuf); \
409: if (neg) num = -num; \
410: *--s = '\0'; \
411: do { \
412: *--s = (num % 10) + '0'; \
413: } while ((num /= 10) != 0); \
414: if (neg) *--s = '-'; \
1.1.1.4 ! shadchin 415: strlcpy(buf, s, len); \
1.1.1.2 millert 416: }
417:
418: TYPE_TO_A_FUNC(postoa, POSITION)
419: TYPE_TO_A_FUNC(linenumtoa, LINENUM)
420: TYPE_TO_A_FUNC(inttoa, int)
421:
422: /*
1.1 etheisen 423: * Output an integer in a given radix.
424: */
425: static int
1.1.1.2 millert 426: iprint_int(num)
1.1 etheisen 427: int num;
428: {
1.1.1.2 millert 429: char buf[INT_STRLEN_BOUND(num)];
430:
1.1.1.4 ! shadchin 431: inttoa(num, buf, sizeof(buf));
1.1.1.2 millert 432: putstr(buf);
433: return (strlen(buf));
434: }
435:
436: /*
437: * Output a line number in a given radix.
438: */
439: static int
440: iprint_linenum(num)
441: LINENUM num;
442: {
443: char buf[INT_STRLEN_BOUND(num)];
444:
1.1.1.4 ! shadchin 445: linenumtoa(num, buf, sizeof(buf));
1.1.1.2 millert 446: putstr(buf);
447: return (strlen(buf));
1.1 etheisen 448: }
449:
450: /*
451: * This function implements printf-like functionality
452: * using a more portable argument list mechanism than printf's.
453: */
454: static int
1.1.1.2 millert 455: less_printf(fmt, parg)
1.1 etheisen 456: register char *fmt;
457: PARG *parg;
458: {
459: register char *s;
460: register int col;
461:
462: col = 0;
463: while (*fmt != '\0')
464: {
465: if (*fmt != '%')
466: {
467: putchr(*fmt++);
468: col++;
469: } else
470: {
471: ++fmt;
1.1.1.2 millert 472: switch (*fmt++)
473: {
1.1 etheisen 474: case 's':
475: s = parg->p_string;
476: parg++;
477: while (*s != '\0')
478: {
479: putchr(*s++);
480: col++;
481: }
482: break;
483: case 'd':
1.1.1.2 millert 484: col += iprint_int(parg->p_int);
485: parg++;
486: break;
487: case 'n':
488: col += iprint_linenum(parg->p_linenum);
1.1 etheisen 489: parg++;
490: break;
491: }
492: }
493: }
494: return (col);
495: }
496:
497: /*
1.1.1.2 millert 498: * Get a RETURN.
499: * If some other non-trivial char is pressed, unget it, so it will
500: * become the next command.
501: */
502: public void
503: get_return()
504: {
505: int c;
506:
507: #if ONLY_RETURN
508: while ((c = getchr()) != '\n' && c != '\r')
509: bell();
510: #else
511: c = getchr();
512: if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
513: ungetcc(c);
514: #endif
515: }
516:
517: /*
1.1 etheisen 518: * Output a message in the lower left corner of the screen
519: * and wait for carriage return.
520: */
521: public void
522: error(fmt, parg)
523: char *fmt;
524: PARG *parg;
525: {
526: int col = 0;
527: static char return_to_continue[] = " (press RETURN)";
528:
529: errmsgs++;
530:
1.1.1.2 millert 531: if (any_display && is_tty)
1.1 etheisen 532: {
1.1.1.3 shadchin 533: if (!oldbot)
534: squish_check();
535: at_exit();
1.1 etheisen 536: clear_bot();
1.1.1.3 shadchin 537: at_enter(AT_STANDOUT);
1.1 etheisen 538: col += so_s_width;
539: }
540:
1.1.1.2 millert 541: col += less_printf(fmt, parg);
1.1 etheisen 542:
1.1.1.2 millert 543: if (!(any_display && is_tty))
1.1 etheisen 544: {
545: putchr('\n');
546: return;
547: }
548:
549: putstr(return_to_continue);
1.1.1.3 shadchin 550: at_exit();
1.1 etheisen 551: col += sizeof(return_to_continue) + so_e_width;
552:
1.1.1.2 millert 553: get_return();
1.1 etheisen 554: lower_left();
1.1.1.3 shadchin 555: clear_eol();
1.1 etheisen 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: {
1.1.1.3 shadchin 581: at_exit();
1.1 etheisen 582: clear_bot();
1.1.1.3 shadchin 583: at_enter(AT_STANDOUT);
1.1.1.2 millert 584: (void) less_printf(fmt, parg);
1.1 etheisen 585: putstr(intr_to_abort);
1.1.1.3 shadchin 586: at_exit();
1.1 etheisen 587: flush();
588: need_clr = 1;
589: }
590:
591: /*
592: * Output a message in the lower left corner of the screen
593: * and return a single-character response.
594: */
595: public int
596: query(fmt, parg)
597: char *fmt;
598: PARG *parg;
599: {
600: register int c;
601: int col = 0;
602:
1.1.1.2 millert 603: if (any_display && is_tty)
1.1 etheisen 604: clear_bot();
605:
1.1.1.2 millert 606: (void) less_printf(fmt, parg);
1.1 etheisen 607: c = getchr();
608:
1.1.1.2 millert 609: if (!(any_display && is_tty))
1.1 etheisen 610: {
611: putchr('\n');
612: return (c);
613: }
614:
615: lower_left();
616: if (col >= sc_width)
617: screen_trashed = 1;
618: flush();
619:
620: return (c);
621: }