Annotation of src/usr.bin/less/output.c, Revision 1.8
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:
1.7 millert 26: extern volatile sig_atomic_t sigs;
1.1 etheisen 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.8 ! millert 101: ssize_t nwritten;
1.1 etheisen 102:
103: n = ob - obuf;
104: if (n == 0)
105: return;
1.5 millert 106:
107: #if MSDOS_COMPILER==MSOFTC
108: if (is_tty && any_display)
109: {
110: *ob = '\0';
111: _outtext(obuf);
112: ob = obuf;
113: return;
114: }
115: #else
1.6 shadchin 116: #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
1.5 millert 117: if (is_tty && any_display)
118: {
119: *ob = '\0';
120: if (ctldisp != OPT_ONPLUS)
1.6 shadchin 121: WIN32textout(obuf, ob - obuf);
1.5 millert 122: else
123: {
124: /*
125: * Look for SGR escape sequences, and convert them
126: * to color commands. Replace bold, underline,
127: * and italic escapes into colors specified via
128: * the -D command-line option.
129: */
130: char *anchor, *p, *p_next;
1.6 shadchin 131: unsigned char fg, bg;
132: static unsigned char at;
133: #if MSDOS_COMPILER==WIN32C
134: /* Screen colors used by 3x and 4x SGR commands. */
135: static unsigned char screen_color[] = {
136: 0, /* BLACK */
137: FOREGROUND_RED,
138: FOREGROUND_GREEN,
139: FOREGROUND_RED|FOREGROUND_GREEN,
140: FOREGROUND_BLUE,
141: FOREGROUND_BLUE|FOREGROUND_RED,
142: FOREGROUND_BLUE|FOREGROUND_GREEN,
143: FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED
144: };
145: #else
1.5 millert 146: static enum COLORS screen_color[] = {
147: BLACK, RED, GREEN, BROWN,
148: BLUE, MAGENTA, CYAN, LIGHTGRAY
149: };
1.6 shadchin 150: #endif
1.5 millert 151:
152: for (anchor = p_next = obuf;
1.6 shadchin 153: (p_next = memchr(p_next, ESC, ob - p_next)) != NULL; )
1.5 millert 154: {
155: p = p_next;
1.6 shadchin 156: if (p[1] == '[') /* "ESC-[" sequence */
1.5 millert 157: {
158: if (p > anchor)
159: {
1.6 shadchin 160: /*
161: * If some chars seen since
162: * the last escape sequence,
163: * write them out to the screen.
164: */
165: WIN32textout(anchor, p-anchor);
1.5 millert 166: anchor = p;
167: }
1.6 shadchin 168: p += 2; /* Skip the "ESC-[" */
169: if (is_ansi_end(*p))
170: {
171: /*
172: * Handle null escape sequence
173: * "ESC[m", which restores
174: * the normal color.
175: */
176: p++;
177: anchor = p_next = p;
178: WIN32setcolors(nm_fg_color, nm_bg_color);
179: continue;
180: }
1.5 millert 181: p_next = p;
1.6 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.5 millert 189: while (!is_ansi_end(*p))
190: {
191: char *q;
192: long code = strtol(p, &q, 10);
193:
1.6 shadchin 194: if (*q == '\0')
1.5 millert 195: {
196: /*
197: * Incomplete sequence.
198: * Leave it unprocessed
199: * in the buffer.
200: */
201: int slop = q - anchor;
1.6 shadchin 202: /* {{ strlcpy args overlap! }} */
1.5 millert 203: strlcpy(obuf, anchor,
204: sizeof(obuf));
205: ob = &obuf[slop];
206: return;
207: }
208:
1.6 shadchin 209: if (q == p ||
210: code > 49 || code < 0 ||
211: (!is_ansi_end(*q) && *q != ';'))
1.5 millert 212: {
213: p_next = q;
214: break;
215: }
216: if (*q == ';')
217: q++;
218:
219: switch (code)
220: {
1.6 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.5 millert 227: case 1: /* bold on */
1.6 shadchin 228: at |= 1;
1.5 millert 229: break;
230: case 3: /* italic on */
1.6 shadchin 231: case 7: /* inverse on */
232: at |= 2;
1.5 millert 233: break;
234: case 4: /* underline on */
1.6 shadchin 235: at |= 4;
236: break;
237: case 5: /* slow blink on */
238: case 6: /* fast blink on */
239: at |= 8;
1.5 millert 240: break;
241: case 8: /* concealed on */
242: fg = (bg & 7) | 8;
243: break;
1.6 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.5 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.6 shadchin 273: if (!is_ansi_end(*p) || p == p_next)
274: break;
275: if (at & 1)
276: {
277: fg = bo_fg_color;
278: bg = bo_bg_color;
279: } else if (at & 2)
280: {
281: fg = so_fg_color;
282: bg = so_bg_color;
283: } else if (at & 4)
284: {
285: fg = ul_fg_color;
286: bg = ul_bg_color;
287: } else if (at & 8)
1.5 millert 288: {
1.6 shadchin 289: fg = bl_fg_color;
290: bg = bl_bg_color;
291: }
292: fg &= 0xf;
293: bg &= 0xf;
294: WIN32setcolors(fg, bg);
295: p_next = anchor = p + 1;
1.5 millert 296: } else
297: p_next++;
298: }
299:
300: /* Output what's left in the buffer. */
1.6 shadchin 301: WIN32textout(anchor, ob - anchor);
1.5 millert 302: }
303: ob = obuf;
304: return;
305: }
306: #endif
307: #endif
1.6 shadchin 308: fd = (any_display) ? STDOUT_FILENO : STDERR_FILENO;
1.8 ! millert 309: nwritten = write(fd, obuf, n);
! 310: if (nwritten != n) {
! 311: if (nwritten == -1)
! 312: quit(QUIT_ERROR);
1.1 etheisen 313: screen_trashed = 1;
1.8 ! millert 314: }
1.1 etheisen 315: ob = obuf;
316: }
317:
318: /*
319: * Output a character.
320: */
321: public int
322: putchr(c)
323: int c;
324: {
1.6 shadchin 325: #if 0 /* fake UTF-8 output for testing */
326: extern int utf_mode;
327: if (utf_mode)
328: {
329: static char ubuf[MAX_UTF_CHAR_LEN];
330: static int ubuf_len = 0;
331: static int ubuf_index = 0;
332: if (ubuf_len == 0)
333: {
334: ubuf_len = utf_len(c);
335: ubuf_index = 0;
336: }
337: ubuf[ubuf_index++] = c;
338: if (ubuf_index < ubuf_len)
339: return c;
340: c = get_wchar(ubuf) & 0xFF;
341: ubuf_len = 0;
342: }
343: #endif
1.1 etheisen 344: if (need_clr)
345: {
346: need_clr = 0;
347: clear_bot();
348: }
1.5 millert 349: #if MSDOS_COMPILER
350: if (c == '\n' && is_tty)
351: {
352: /* remove_top(1); */
1.1 etheisen 353: putchr('\r');
1.5 millert 354: }
355: #else
356: #ifdef _OSK
357: if (c == '\n' && is_tty) /* In OS-9, '\n' == 0x0D */
358: putchr(0x0A);
1.1 etheisen 359: #endif
1.5 millert 360: #endif
361: /*
362: * Some versions of flush() write to *ob, so we must flush
363: * when we are still one char from the end of obuf.
364: */
365: if (ob >= &obuf[sizeof(obuf)-1])
366: flush();
1.1 etheisen 367: *ob++ = c;
1.6 shadchin 368: at_prompt = 0;
1.1 etheisen 369: return (c);
370: }
371:
372: /*
373: * Output a string.
374: */
375: public void
376: putstr(s)
1.5 millert 377: register char *s;
1.1 etheisen 378: {
379: while (*s != '\0')
380: putchr(*s++);
381: }
382:
383:
384: /*
1.5 millert 385: * Convert an integral type to a string.
386: */
387: #define TYPE_TO_A_FUNC(funcname, type) \
388: void funcname(num, buf, len) \
389: type num; \
390: char *buf; \
391: size_t len; \
392: { \
393: int neg = (num < 0); \
394: char tbuf[INT_STRLEN_BOUND(num)+2]; \
395: register char *s = tbuf + sizeof(tbuf); \
396: if (neg) num = -num; \
397: *--s = '\0'; \
398: do { \
399: *--s = (num % 10) + '0'; \
400: } while ((num /= 10) != 0); \
401: if (neg) *--s = '-'; \
402: strlcpy(buf, s, len); \
403: }
404:
405: TYPE_TO_A_FUNC(postoa, POSITION)
406: TYPE_TO_A_FUNC(linenumtoa, LINENUM)
407: TYPE_TO_A_FUNC(inttoa, int)
408:
409: /*
1.1 etheisen 410: * Output an integer in a given radix.
411: */
412: static int
1.5 millert 413: iprint_int(num)
1.1 etheisen 414: int num;
415: {
1.5 millert 416: char buf[INT_STRLEN_BOUND(num)];
1.1 etheisen 417:
1.5 millert 418: inttoa(num, buf, sizeof(buf));
419: putstr(buf);
420: return (strlen(buf));
421: }
1.1 etheisen 422:
1.5 millert 423: /*
424: * Output a line number in a given radix.
425: */
426: static int
427: iprint_linenum(num)
428: LINENUM num;
429: {
430: char buf[INT_STRLEN_BOUND(num)];
1.1 etheisen 431:
1.5 millert 432: linenumtoa(num, buf, sizeof(buf));
433: putstr(buf);
434: return (strlen(buf));
1.1 etheisen 435: }
436:
437: /*
438: * This function implements printf-like functionality
439: * using a more portable argument list mechanism than printf's.
440: */
441: static int
1.5 millert 442: less_printf(fmt, parg)
443: register char *fmt;
1.1 etheisen 444: PARG *parg;
445: {
1.5 millert 446: register char *s;
447: register int col;
1.1 etheisen 448:
449: col = 0;
450: while (*fmt != '\0')
451: {
452: if (*fmt != '%')
453: {
454: putchr(*fmt++);
455: col++;
456: } else
457: {
458: ++fmt;
1.5 millert 459: switch (*fmt++)
460: {
1.1 etheisen 461: case 's':
462: s = parg->p_string;
463: parg++;
464: while (*s != '\0')
465: {
466: putchr(*s++);
467: col++;
468: }
469: break;
470: case 'd':
1.5 millert 471: col += iprint_int(parg->p_int);
472: parg++;
473: break;
474: case 'n':
475: col += iprint_linenum(parg->p_linenum);
1.1 etheisen 476: parg++;
477: break;
478: }
479: }
480: }
481: return (col);
482: }
483:
484: /*
1.5 millert 485: * Get a RETURN.
486: * If some other non-trivial char is pressed, unget it, so it will
487: * become the next command.
488: */
489: public void
490: get_return()
491: {
492: int c;
493:
494: #if ONLY_RETURN
495: while ((c = getchr()) != '\n' && c != '\r')
496: bell();
497: #else
498: c = getchr();
499: if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
500: ungetcc(c);
501: #endif
502: }
503:
504: /*
1.1 etheisen 505: * Output a message in the lower left corner of the screen
506: * and wait for carriage return.
507: */
508: public void
509: error(fmt, parg)
510: char *fmt;
511: PARG *parg;
512: {
513: int col = 0;
514: static char return_to_continue[] = " (press RETURN)";
515:
516: errmsgs++;
517:
1.5 millert 518: if (any_display && is_tty)
1.1 etheisen 519: {
1.6 shadchin 520: if (!oldbot)
521: squish_check();
522: at_exit();
1.1 etheisen 523: clear_bot();
1.6 shadchin 524: at_enter(AT_STANDOUT);
1.1 etheisen 525: col += so_s_width;
526: }
527:
1.5 millert 528: col += less_printf(fmt, parg);
1.1 etheisen 529:
1.5 millert 530: if (!(any_display && is_tty))
1.1 etheisen 531: {
532: putchr('\n');
533: return;
534: }
535:
536: putstr(return_to_continue);
1.6 shadchin 537: at_exit();
1.1 etheisen 538: col += sizeof(return_to_continue) + so_e_width;
539:
1.5 millert 540: get_return();
1.1 etheisen 541: lower_left();
1.6 shadchin 542: clear_eol();
1.1 etheisen 543:
544: if (col >= sc_width)
545: /*
546: * Printing the message has probably scrolled the screen.
547: * {{ Unless the terminal doesn't have auto margins,
548: * in which case we just hammered on the right margin. }}
549: */
550: screen_trashed = 1;
551:
552: flush();
553: }
554:
555: static char intr_to_abort[] = "... (interrupt to abort)";
556:
557: /*
558: * Output a message in the lower left corner of the screen
559: * and don't wait for carriage return.
560: * Usually used to warn that we are beginning a potentially
561: * time-consuming operation.
562: */
563: public void
564: ierror(fmt, parg)
565: char *fmt;
566: PARG *parg;
567: {
1.6 shadchin 568: at_exit();
1.1 etheisen 569: clear_bot();
1.6 shadchin 570: at_enter(AT_STANDOUT);
1.5 millert 571: (void) less_printf(fmt, parg);
1.1 etheisen 572: putstr(intr_to_abort);
1.6 shadchin 573: at_exit();
1.1 etheisen 574: flush();
575: need_clr = 1;
576: }
577:
578: /*
579: * Output a message in the lower left corner of the screen
580: * and return a single-character response.
581: */
582: public int
583: query(fmt, parg)
584: char *fmt;
585: PARG *parg;
586: {
1.5 millert 587: register int c;
1.1 etheisen 588: int col = 0;
589:
1.5 millert 590: if (any_display && is_tty)
1.1 etheisen 591: clear_bot();
592:
1.5 millert 593: (void) less_printf(fmt, parg);
1.1 etheisen 594: c = getchr();
595:
1.5 millert 596: if (!(any_display && is_tty))
1.1 etheisen 597: {
598: putchr('\n');
599: return (c);
600: }
601:
602: lower_left();
603: if (col >= sc_width)
604: screen_trashed = 1;
605: flush();
606:
607: return (c);
608: }