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