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