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