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