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