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