Upgrade to less-429.
[dragonfly.git] / contrib / less / output.c
1 /*
2  * Copyright (C) 1984-2008  Mark Nudelman
3  *
4  * You may distribute under the terms of either the GNU General Public
5  * License or the Less License, as specified in the README file.
6  *
7  * For more information about less, or for information on how to 
8  * contact the author, see the README file.
9  */
10
11
12 /*
13  * High level routines dealing with the output to the screen.
14  */
15
16 #include "less.h"
17 #if MSDOS_COMPILER==WIN32C
18 #include "windows.h"
19 #endif
20
21 public int errmsgs;     /* Count of messages displayed by error() */
22 public int need_clr;
23 public int final_attr;
24 public int at_prompt;
25
26 extern int sigs;
27 extern int sc_width;
28 extern int so_s_width, so_e_width;
29 extern int screen_trashed;
30 extern int any_display;
31 extern int is_tty;
32 extern int oldbot;
33
34 #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
35 extern int ctldisp;
36 extern int nm_fg_color, nm_bg_color;
37 extern int bo_fg_color, bo_bg_color;
38 extern int ul_fg_color, ul_bg_color;
39 extern int so_fg_color, so_bg_color;
40 extern int bl_fg_color, bl_bg_color;
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 = 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                         unsigned char fg, bg;
131                         static unsigned char at;
132 #if MSDOS_COMPILER==WIN32C
133                         /* Screen colors used by 3x and 4x SGR commands. */
134                         static unsigned char screen_color[] = {
135                                 0, /* BLACK */
136                                 FOREGROUND_RED,
137                                 FOREGROUND_GREEN,
138                                 FOREGROUND_RED|FOREGROUND_GREEN,
139                                 FOREGROUND_BLUE, 
140                                 FOREGROUND_BLUE|FOREGROUND_RED,
141                                 FOREGROUND_BLUE|FOREGROUND_GREEN,
142                                 FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED
143                         };
144 #else
145                         static enum COLORS screen_color[] = {
146                                 BLACK, RED, GREEN, BROWN,
147                                 BLUE, MAGENTA, CYAN, LIGHTGRAY
148                         };
149 #endif
150
151                         for (anchor = p_next = obuf;
152                              (p_next = memchr(p_next, ESC, ob - p_next)) != NULL; )
153                         {
154                                 p = p_next;
155                                 if (p[1] == '[')  /* "ESC-[" sequence */
156                                 {
157                                         if (p > anchor)
158                                         {
159                                                 /*
160                                                  * If some chars seen since
161                                                  * the last escape sequence,
162                                                  * write them out to the screen.
163                                                  */
164                                                 WIN32textout(anchor, p-anchor);
165                                                 anchor = p;
166                                         }
167                                         p += 2;  /* Skip the "ESC-[" */
168                                         if (is_ansi_end(*p))
169                                         {
170                                                 /*
171                                                  * Handle null escape sequence
172                                                  * "ESC[m", which restores
173                                                  * the normal color.
174                                                  */
175                                                 p++;
176                                                 anchor = p_next = p;
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                                                         fg = bo_fg_color;
276                                                         bg = bo_bg_color;
277                                         } else if (at & 2)
278                                         {
279                                                         fg = so_fg_color;
280                                                         bg = so_bg_color;
281                                         } else if (at & 4)
282                                         {
283                                                         fg = ul_fg_color;
284                                                         bg = ul_bg_color;
285                                         } else if (at & 8)
286                                         {
287                                                         fg = bl_fg_color;
288                                                         bg = bl_bg_color;
289                                         }
290                                         fg &= 0xf;
291                                         bg &= 0xf;
292                                         WIN32setcolors(fg, bg);
293                                         p_next = anchor = p + 1;
294                                 } else
295                                         p_next++;
296                         }
297
298                         /* Output what's left in the buffer.  */
299                         WIN32textout(anchor, ob - anchor);
300                 }
301                 ob = obuf;
302                 return;
303         }
304 #endif
305 #endif
306         fd = (any_display) ? 1 : 2;
307         if (write(fd, obuf, n) != n)
308                 screen_trashed = 1;
309         ob = obuf;
310 }
311
312 /*
313  * Output a character.
314  */
315         public int
316 putchr(c)
317         int c;
318 {
319 #if 0 /* fake UTF-8 output for testing */
320         extern int utf_mode;
321         if (utf_mode)
322         {
323                 static char ubuf[MAX_UTF_CHAR_LEN];
324                 static int ubuf_len = 0;
325                 static int ubuf_index = 0;
326                 if (ubuf_len == 0)
327                 {
328                         ubuf_len = utf_len(c);
329                         ubuf_index = 0;
330                 }
331                 ubuf[ubuf_index++] = c;
332                 if (ubuf_index < ubuf_len)
333                         return c;
334                 c = get_wchar(ubuf) & 0xFF;
335                 ubuf_len = 0;
336         }
337 #endif
338         if (need_clr)
339         {
340                 need_clr = 0;
341                 clear_bot();
342         }
343 #if MSDOS_COMPILER
344         if (c == '\n' && is_tty)
345         {
346                 /* remove_top(1); */
347                 putchr('\r');
348         }
349 #else
350 #ifdef _OSK
351         if (c == '\n' && is_tty)  /* In OS-9, '\n' == 0x0D */
352                 putchr(0x0A);
353 #endif
354 #endif
355         /*
356          * Some versions of flush() write to *ob, so we must flush
357          * when we are still one char from the end of obuf.
358          */
359         if (ob >= &obuf[sizeof(obuf)-1])
360                 flush();
361         *ob++ = c;
362         at_prompt = 0;
363         return (c);
364 }
365
366 /*
367  * Output a string.
368  */
369         public void
370 putstr(s)
371         register char *s;
372 {
373         while (*s != '\0')
374                 putchr(*s++);
375 }
376
377
378 /*
379  * Convert an integral type to a string.
380  */
381 #define TYPE_TO_A_FUNC(funcname, type) \
382 void funcname(num, buf) \
383         type num; \
384         char *buf; \
385 { \
386         int neg = (num < 0); \
387         char tbuf[INT_STRLEN_BOUND(num)+2]; \
388         register char *s = tbuf + sizeof(tbuf); \
389         if (neg) num = -num; \
390         *--s = '\0'; \
391         do { \
392                 *--s = (num % 10) + '0'; \
393         } while ((num /= 10) != 0); \
394         if (neg) *--s = '-'; \
395         strcpy(buf, s); \
396 }
397
398 TYPE_TO_A_FUNC(postoa, POSITION)
399 TYPE_TO_A_FUNC(linenumtoa, LINENUM)
400 TYPE_TO_A_FUNC(inttoa, int)
401
402 /*
403  * Output an integer in a given radix.
404  */
405         static int
406 iprint_int(num)
407         int num;
408 {
409         char buf[INT_STRLEN_BOUND(num)];
410
411         inttoa(num, buf);
412         putstr(buf);
413         return (strlen(buf));
414 }
415
416 /*
417  * Output a line number in a given radix.
418  */
419         static int
420 iprint_linenum(num)
421         LINENUM num;
422 {
423         char buf[INT_STRLEN_BOUND(num)];
424
425         linenumtoa(num, buf);
426         putstr(buf);
427         return (strlen(buf));
428 }
429
430 /*
431  * This function implements printf-like functionality
432  * using a more portable argument list mechanism than printf's.
433  */
434         static int
435 less_printf(fmt, parg)
436         register char *fmt;
437         PARG *parg;
438 {
439         register char *s;
440         register int col;
441
442         col = 0;
443         while (*fmt != '\0')
444         {
445                 if (*fmt != '%')
446                 {
447                         putchr(*fmt++);
448                         col++;
449                 } else
450                 {
451                         ++fmt;
452                         switch (*fmt++)
453                         {
454                         case 's':
455                                 s = parg->p_string;
456                                 parg++;
457                                 while (*s != '\0')
458                                 {
459                                         putchr(*s++);
460                                         col++;
461                                 }
462                                 break;
463                         case 'd':
464                                 col += iprint_int(parg->p_int);
465                                 parg++;
466                                 break;
467                         case 'n':
468                                 col += iprint_linenum(parg->p_linenum);
469                                 parg++;
470                                 break;
471                         }
472                 }
473         }
474         return (col);
475 }
476
477 /*
478  * Get a RETURN.
479  * If some other non-trivial char is pressed, unget it, so it will
480  * become the next command.
481  */
482         public void
483 get_return()
484 {
485         int c;
486
487 #if ONLY_RETURN
488         while ((c = getchr()) != '\n' && c != '\r')
489                 bell();
490 #else
491         c = getchr();
492         if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
493                 ungetcc(c);
494 #endif
495 }
496
497 /*
498  * Output a message in the lower left corner of the screen
499  * and wait for carriage return.
500  */
501         public void
502 error(fmt, parg)
503         char *fmt;
504         PARG *parg;
505 {
506         int col = 0;
507         static char return_to_continue[] = "  (press RETURN)";
508
509         errmsgs++;
510
511         if (any_display && is_tty)
512         {
513                 if (!oldbot)
514                         squish_check();
515                 at_exit();
516                 clear_bot();
517                 at_enter(AT_STANDOUT);
518                 col += so_s_width;
519         }
520
521         col += less_printf(fmt, parg);
522
523         if (!(any_display && is_tty))
524         {
525                 putchr('\n');
526                 return;
527         }
528
529         putstr(return_to_continue);
530         at_exit();
531         col += sizeof(return_to_continue) + so_e_width;
532
533         get_return();
534         lower_left();
535
536         if (col >= sc_width)
537                 /*
538                  * Printing the message has probably scrolled the screen.
539                  * {{ Unless the terminal doesn't have auto margins,
540                  *    in which case we just hammered on the right margin. }}
541                  */
542                 screen_trashed = 1;
543
544         flush();
545 }
546
547 static char intr_to_abort[] = "... (interrupt to abort)";
548
549 /*
550  * Output a message in the lower left corner of the screen
551  * and don't wait for carriage return.
552  * Usually used to warn that we are beginning a potentially
553  * time-consuming operation.
554  */
555         public void
556 ierror(fmt, parg)
557         char *fmt;
558         PARG *parg;
559 {
560         at_exit();
561         clear_bot();
562         at_enter(AT_STANDOUT);
563         (void) less_printf(fmt, parg);
564         putstr(intr_to_abort);
565         at_exit();
566         flush();
567         need_clr = 1;
568 }
569
570 /*
571  * Output a message in the lower left corner of the screen
572  * and return a single-character response.
573  */
574         public int
575 query(fmt, parg)
576         char *fmt;
577         PARG *parg;
578 {
579         register int c;
580         int col = 0;
581
582         if (any_display && is_tty)
583                 clear_bot();
584
585         (void) less_printf(fmt, parg);
586         c = getchr();
587
588         if (!(any_display && is_tty))
589         {
590                 putchr('\n');
591                 return (c);
592         }
593
594         lower_left();
595         if (col >= sc_width)
596                 screen_trashed = 1;
597         flush();
598
599         return (c);
600 }