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