1 /*
2 * Copyright (C) 1984-2002 Mark Nudelman
3 *
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.
6 *
7 * For more information about less, or for information on how to
8 * contact the author, see the README file.
9 */
10
11
12 /*
13 * High level routines dealing with the output to the screen.
14 */
15
16 #include "less.h"
17 #if MSDOS_COMPILER==WIN32C
18 #include "windows.h"
19 #endif
20
21 public int errmsgs; /* Count of messages displayed by error() */
22 public int need_clr;
23 public int final_attr;
24
25 extern int sigs;
26 extern int sc_width;
27 extern int so_s_width, so_e_width;
28 extern int screen_trashed;
29 extern int any_display;
30 extern int is_tty;
31
32 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
33 extern int ctldisp;
34 extern int nm_fg_color, nm_bg_color;
35 extern int bo_fg_color, bo_bg_color;
36 extern int ul_fg_color, ul_bg_color;
37 extern int so_fg_color, so_bg_color;
38 extern int bl_fg_color, bl_bg_color;
39 #endif
40
41 /*
42 * Display the line which is in the line buffer.
43 */
44 public void
put_line()45 put_line()
46 {
47 register int c;
48 register int i;
49 int a;
50 int curr_attr;
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
61 curr_attr = AT_NORMAL;
62
63 for (i = 0; (c = gline(i, &a)) != '\0'; i++)
64 {
65 if (a != curr_attr)
66 {
67 /*
68 * Changing attributes.
69 * Display the exit sequence for the old attribute
70 * and the enter sequence for the new one.
71 */
72 switch (curr_attr)
73 {
74 case AT_UNDERLINE: ul_exit(); break;
75 case AT_BOLD: bo_exit(); break;
76 case AT_BLINK: bl_exit(); break;
77 case AT_STANDOUT: so_exit(); break;
78 }
79 switch (a)
80 {
81 case AT_UNDERLINE: ul_enter(); break;
82 case AT_BOLD: bo_enter(); break;
83 case AT_BLINK: bl_enter(); break;
84 case AT_STANDOUT: so_enter(); break;
85 }
86 curr_attr = a;
87 }
88 if (curr_attr == AT_INVIS)
89 continue;
90 if (c == '\b')
91 putbs();
92 else
93 putchr(c);
94 }
95
96 switch (curr_attr)
97 {
98 case AT_UNDERLINE: ul_exit(); break;
99 case AT_BOLD: bo_exit(); break;
100 case AT_BLINK: bl_exit(); break;
101 case AT_STANDOUT: so_exit(); break;
102 }
103 final_attr = curr_attr;
104 }
105
106 static char obuf[OUTBUF_SIZE];
107 static char *ob = obuf;
108
109 /*
110 * Flush buffered output.
111 *
112 * If we haven't displayed any file data yet,
113 * output messages on error output (file descriptor 2),
114 * otherwise output on standard output (file descriptor 1).
115 *
116 * This has the desirable effect of producing all
117 * error messages on error output if standard output
118 * is directed to a file. It also does the same if
119 * we never produce any real output; for example, if
120 * the input file(s) cannot be opened. If we do
121 * eventually produce output, code in edit() makes
122 * sure these messages can be seen before they are
123 * overwritten or scrolled away.
124 */
125 public void
flush()126 flush()
127 {
128 register int n;
129 register int fd;
130
131 n = ob - obuf;
132 if (n == 0)
133 return;
134 #if MSDOS_COMPILER==WIN32C
135 if (is_tty && any_display)
136 {
137 char *op;
138 DWORD nwritten = 0;
139 CONSOLE_SCREEN_BUFFER_INFO scr;
140 int row;
141 int col;
142 int olen;
143 extern HANDLE con_out;
144
145 olen = ob - obuf;
146 /*
147 * There is a bug in Win32 WriteConsole() if we're
148 * writing in the last cell with a different color.
149 * To avoid color problems in the bottom line,
150 * we scroll the screen manually, before writing.
151 */
152 GetConsoleScreenBufferInfo(con_out, &scr);
153 col = scr.dwCursorPosition.X;
154 row = scr.dwCursorPosition.Y;
155 for (op = obuf; op < obuf + olen; op++)
156 {
157 if (*op == '\n')
158 {
159 col = 0;
160 row++;
161 } else if (*op == '\r')
162 {
163 col = 0;
164 } else
165 {
166 col++;
167 if (col >= sc_width)
168 {
169 col = 0;
170 row++;
171 }
172 }
173 }
174 if (row > scr.srWindow.Bottom)
175 win32_scroll_up(row - scr.srWindow.Bottom);
176 WriteConsole(con_out, obuf, olen, &nwritten, NULL);
177 ob = obuf;
178 return;
179 }
180 #else
181 #if MSDOS_COMPILER==MSOFTC
182 if (is_tty && any_display)
183 {
184 *ob = '\0';
185 _outtext(obuf);
186 ob = obuf;
187 return;
188 }
189 #else
190 #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
191 if (is_tty && any_display)
192 {
193 *ob = '\0';
194 if (ctldisp != OPT_ONPLUS)
195 cputs(obuf);
196 else
197 {
198 /*
199 * Look for SGR escape sequences, and convert them
200 * to color commands. Replace bold, underline,
201 * and italic escapes into colors specified via
202 * the -D command-line option.
203 */
204 char *anchor, *p, *p_next;
205 int buflen = ob - obuf;
206 unsigned char fg, bg, norm_attr;
207 /*
208 * Only dark colors mentioned here, so that
209 * bold has visible effect.
210 */
211 static enum COLORS screen_color[] = {
212 BLACK, RED, GREEN, BROWN,
213 BLUE, MAGENTA, CYAN, LIGHTGRAY
214 };
215
216 /* Normal text colors are used as baseline. */
217 bg = nm_bg_color & 0xf;
218 fg = nm_fg_color & 0xf;
219 norm_attr = (bg << 4) | fg;
220 for (anchor = p_next = obuf;
221 (p_next = memchr (p_next, ESC,
222 buflen - (p_next - obuf)))
223 != NULL; )
224 {
225 p = p_next;
226
227 /*
228 * Handle the null escape sequence
229 * (ESC-[m), which is used to restore
230 * the original color.
231 */
232 if (p[1] == '[' && is_ansi_end(p[2]))
233 {
234 textattr(norm_attr);
235 p += 3;
236 anchor = p_next = p;
237 continue;
238 }
239
240 if (p[1] == '[') /* "Esc-[" sequence */
241 {
242 /*
243 * If some chars seen since
244 * the last escape sequence,
245 * write it out to the screen
246 * using current text attributes.
247 */
248 if (p > anchor)
249 {
250 *p = '\0';
251 cputs (anchor);
252 *p = ESC;
253 anchor = p;
254 }
255 p += 2;
256 p_next = p;
257 while (!is_ansi_end(*p))
258 {
259 char *q;
260 long code = strtol(p, &q, 10);
261
262 if (!*q)
263 {
264 /*
265 * Incomplete sequence.
266 * Leave it unprocessed
267 * in the buffer.
268 */
269 int slop = q - anchor;
270 strlcpy(obuf, anchor,
271 sizeof(obuf));
272 ob = &obuf[slop];
273 return;
274 }
275
276 if (q == p
277 || code > 49 || code < 0
278 || (!is_ansi_end(*q)
279 && *q != ';'))
280 {
281 p_next = q;
282 break;
283 }
284 if (*q == ';')
285 q++;
286
287 switch (code)
288 {
289 case 1: /* bold on */
290 fg = bo_fg_color;
291 bg = bo_bg_color;
292 break;
293 case 3: /* italic on */
294 fg = so_fg_color;
295 bg = so_bg_color;
296 break;
297 case 4: /* underline on */
298 fg = ul_fg_color;
299 bg = ul_bg_color;
300 break;
301 case 8: /* concealed on */
302 fg = (bg & 7) | 8;
303 break;
304 case 0: /* all attrs off */
305 case 22:/* bold off */
306 case 23:/* italic off */
307 case 24:/* underline off */
308 fg = nm_fg_color;
309 bg = nm_bg_color;
310 break;
311 case 30: case 31: case 32:
312 case 33: case 34: case 35:
313 case 36: case 37:
314 fg = (fg & 8) | (screen_color[code - 30]);
315 break;
316 case 39: /* default fg */
317 fg = nm_fg_color;
318 break;
319 case 40: case 41: case 42:
320 case 43: case 44: case 45:
321 case 46: case 47:
322 bg = (bg & 8) | (screen_color[code - 40]);
323 break;
324 case 49: /* default fg */
325 bg = nm_bg_color;
326 break;
327 }
328 p = q;
329 }
330 if (is_ansi_end(*p) && p > p_next)
331 {
332 bg &= 15;
333 fg &= 15;
334 textattr ((bg << 4)| fg);
335 p_next = anchor = p + 1;
336 } else
337 break;
338 } else
339 p_next++;
340 }
341
342 /* Output what's left in the buffer. */
343 cputs (anchor);
344 }
345 ob = obuf;
346 return;
347 }
348 #endif
349 #endif
350 #endif
351 fd = (any_display) ? 1 : 2;
352 if (write(fd, obuf, n) != n)
353 screen_trashed = 1;
354 ob = obuf;
355 }
356
357 /*
358 * Output a character.
359 */
360 public int
putchr(c)361 putchr(c)
362 int c;
363 {
364 if (need_clr)
365 {
366 need_clr = 0;
367 clear_bot();
368 }
369 #if MSDOS_COMPILER
370 if (c == '\n' && is_tty)
371 {
372 /* remove_top(1); */
373 putchr('\r');
374 }
375 #else
376 #ifdef _OSK
377 if (c == '\n' && is_tty) /* In OS-9, '\n' == 0x0D */
378 putchr(0x0A);
379 #endif
380 #endif
381 /*
382 * Some versions of flush() write to *ob, so we must flush
383 * when we are still one char from the end of obuf.
384 */
385 if (ob >= &obuf[sizeof(obuf)-1])
386 flush();
387 *ob++ = c;
388 return (c);
389 }
390
391 /*
392 * Output a string.
393 */
394 public void
putstr(s)395 putstr(s)
396 register char *s;
397 {
398 while (*s != '\0')
399 putchr(*s++);
400 }
401
402
403 /*
404 * Convert an integral type to a string.
405 */
406 #define TYPE_TO_A_FUNC(funcname, type) \
407 void funcname(num, buf, len) \
408 type num; \
409 char *buf; \
410 size_t len; \
411 { \
412 int neg = (num < 0); \
413 char tbuf[INT_STRLEN_BOUND(num)+2]; \
414 register char *s = tbuf + sizeof(tbuf); \
415 if (neg) num = -num; \
416 *--s = '\0'; \
417 do { \
418 *--s = (num % 10) + '0'; \
419 } while ((num /= 10) != 0); \
420 if (neg) *--s = '-'; \
421 strlcpy(buf, s, len); \
422 }
423
424 TYPE_TO_A_FUNC(postoa, POSITION)
425 TYPE_TO_A_FUNC(linenumtoa, LINENUM)
426 TYPE_TO_A_FUNC(inttoa, int)
427
428 /*
429 * Output an integer in a given radix.
430 */
431 static int
432 iprint_int(num)
433 int num;
434 {
435 char buf[INT_STRLEN_BOUND(num)];
436
437 inttoa(num, buf, sizeof(buf));
438 putstr(buf);
439 return (strlen(buf));
440 }
441
442 /*
443 * Output a line number in a given radix.
444 */
445 static int
iprint_linenum(num)446 iprint_linenum(num)
447 LINENUM num;
448 {
449 char buf[INT_STRLEN_BOUND(num)];
450
451 linenumtoa(num, buf, sizeof(buf));
452 putstr(buf);
453 return (strlen(buf));
454 }
455
456 /*
457 * This function implements printf-like functionality
458 * using a more portable argument list mechanism than printf's.
459 */
460 static int
less_printf(fmt,parg)461 less_printf(fmt, parg)
462 register char *fmt;
463 PARG *parg;
464 {
465 register char *s;
466 register int col;
467
468 col = 0;
469 while (*fmt != '\0')
470 {
471 if (*fmt != '%')
472 {
473 putchr(*fmt++);
474 col++;
475 } else
476 {
477 ++fmt;
478 switch (*fmt++)
479 {
480 case 's':
481 s = parg->p_string;
482 parg++;
483 while (*s != '\0')
484 {
485 putchr(*s++);
486 col++;
487 }
488 break;
489 case 'd':
490 col += iprint_int(parg->p_int);
491 parg++;
492 break;
493 case 'n':
494 col += iprint_linenum(parg->p_linenum);
495 parg++;
496 break;
497 }
498 }
499 }
500 return (col);
501 }
502
503 /*
504 * Get a RETURN.
505 * If some other non-trivial char is pressed, unget it, so it will
506 * become the next command.
507 */
508 public void
get_return()509 get_return()
510 {
511 int c;
512
513 #if ONLY_RETURN
514 while ((c = getchr()) != '\n' && c != '\r')
515 bell();
516 #else
517 c = getchr();
518 if (c == 'q')
519 quit(QUIT_OK);
520 if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
521 ungetcc(c);
522 #endif
523 }
524
525 /*
526 * Output a message in the lower left corner of the screen
527 * and wait for carriage return.
528 */
529 public void
error(fmt,parg)530 error(fmt, parg)
531 char *fmt;
532 PARG *parg;
533 {
534 int col = 0;
535 static char return_to_continue[] = " (press RETURN)";
536
537 errmsgs++;
538
539 if (any_display && is_tty)
540 {
541 clear_bot();
542 so_enter();
543 col += so_s_width;
544 }
545
546 col += less_printf(fmt, parg);
547
548 if (!(any_display && is_tty))
549 {
550 putchr('\n');
551 return;
552 }
553
554 putstr(return_to_continue);
555 so_exit();
556 col += sizeof(return_to_continue) + so_e_width;
557
558 get_return();
559 lower_left();
560
561 if (col >= sc_width)
562 /*
563 * Printing the message has probably scrolled the screen.
564 * {{ Unless the terminal doesn't have auto margins,
565 * in which case we just hammered on the right margin. }}
566 */
567 screen_trashed = 1;
568
569 flush();
570 }
571
572 static char intr_to_abort[] = "... (interrupt to abort)";
573
574 /*
575 * Output a message in the lower left corner of the screen
576 * and don't wait for carriage return.
577 * Usually used to warn that we are beginning a potentially
578 * time-consuming operation.
579 */
580 public void
ierror(fmt,parg)581 ierror(fmt, parg)
582 char *fmt;
583 PARG *parg;
584 {
585 clear_bot();
586 so_enter();
587 (void) less_printf(fmt, parg);
588 putstr(intr_to_abort);
589 so_exit();
590 flush();
591 need_clr = 1;
592 }
593
594 /*
595 * Output a message in the lower left corner of the screen
596 * and return a single-character response.
597 */
598 public int
query(fmt,parg)599 query(fmt, parg)
600 char *fmt;
601 PARG *parg;
602 {
603 register int c;
604 int col = 0;
605
606 if (any_display && is_tty)
607 clear_bot();
608
609 (void) less_printf(fmt, parg);
610 c = getchr();
611
612 if (!(any_display && is_tty))
613 {
614 putchr('\n');
615 return (c);
616 }
617
618 lower_left();
619 if (col >= sc_width)
620 screen_trashed = 1;
621 flush();
622
623 return (c);
624 }
625