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