2 * Copyright (C) 1984-2023 Mark Nudelman
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.
7 * For more information, see the README file.
12 * High level routines dealing with the output to the screen.
16 #if MSDOS_COMPILER==WIN32C
18 #ifndef COMMON_LVB_UNDERSCORE
19 #define COMMON_LVB_UNDERSCORE 0x8000
23 public int errmsgs
; /* Count of messages displayed by error() */
25 public int final_attr
;
30 extern int so_s_width
, so_e_width
;
31 extern int screen_trashed
;
34 extern char intr_char
;
36 #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
38 extern int nm_fg_color
, nm_bg_color
;
39 extern int bo_fg_color
, bo_bg_color
;
40 extern int ul_fg_color
, ul_bg_color
;
41 extern int so_fg_color
, so_bg_color
;
42 extern int bl_fg_color
, bl_bg_color
;
44 #if MSDOS_COMPILER==WIN32C
45 extern int vt_enabled
;
50 * Display the line which is in the line buffer.
52 public void put_line(void)
61 * Don't output if a signal is pending.
67 final_attr
= AT_NORMAL
;
69 for (i
= 0; (c
= gline(i
, &a
)) != '\0'; i
++)
82 static char obuf
[OUTBUF_SIZE
];
83 static char *ob
= obuf
;
84 static int outfd
= 2; /* stderr */
86 #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
87 static void win_flush(void)
89 if (ctldisp
!= OPT_ONPLUS
|| (vt_enabled
&& sgr_mode
))
90 WIN32textout(obuf
, ob
- obuf
);
94 * Look for SGR escape sequences, and convert them
95 * to color commands. Replace bold, underline,
96 * and italic escapes into colors specified via
97 * the -D command-line option.
99 char *anchor
, *p
, *p_next
;
100 static int fg
, fgi
, bg
, bgi
;
103 #if MSDOS_COMPILER==WIN32C
104 /* Screen colors used by 3x and 4x SGR commands. */
105 static unsigned char screen_color
[] = {
109 FOREGROUND_RED
|FOREGROUND_GREEN
,
111 FOREGROUND_BLUE
|FOREGROUND_RED
,
112 FOREGROUND_BLUE
|FOREGROUND_GREEN
,
113 FOREGROUND_BLUE
|FOREGROUND_GREEN
|FOREGROUND_RED
116 static enum COLORS screen_color
[] = {
117 BLACK
, RED
, GREEN
, BROWN
,
118 BLUE
, MAGENTA
, CYAN
, LIGHTGRAY
122 if (fg
== 0 && bg
== 0)
124 fg
= nm_fg_color
& 7;
125 fgi
= nm_fg_color
& 8;
126 bg
= nm_bg_color
& 7;
127 bgi
= nm_bg_color
& 8;
129 for (anchor
= p_next
= obuf
;
130 (p_next
= memchr(p_next
, ESC
, ob
- p_next
)) != NULL
; )
133 if (p
[1] == '[') /* "ESC-[" sequence */
138 * If some chars seen since
139 * the last escape sequence,
140 * write them out to the screen.
142 WIN32textout(anchor
, p
-anchor
);
145 p
+= 2; /* Skip the "ESC-[" */
149 * Handle null escape sequence
150 * "ESC[m", which restores
155 fg
= nm_fg_color
& 7;
156 fgi
= nm_fg_color
& 8;
157 bg
= nm_bg_color
& 7;
158 bgi
= nm_bg_color
& 8;
160 WIN32setcolors(nm_fg_color
, nm_bg_color
);
167 * Select foreground/background colors
168 * based on the escape sequence.
170 while (!is_ansi_end(*p
))
173 long code
= strtol(p
, &q
, 10);
178 * Incomplete sequence.
179 * Leave it unprocessed
182 int slop
= (int) (q
- anchor
);
183 /* {{ strcpy args overlap! }} */
184 strcpy(obuf
, anchor
);
190 code
> 49 || code
< 0 ||
191 (!is_ansi_end(*q
) && *q
!= ';'))
205 /* case 0: all attrs off */
206 fg
= nm_fg_color
& 7;
207 bg
= nm_bg_color
& 7;
212 * \e[0;...m resets them
220 fgi
= nm_fg_color
& 8;
221 bgi
= nm_bg_color
& 8;
224 case 1: /* bold on */
228 case 3: /* italic on */
229 case 7: /* inverse on */
232 case 4: /* underline on */
236 case 5: /* slow blink on */
237 case 6: /* fast blink on */
241 case 8: /* concealed on */
244 case 22: /* bold off */
248 case 23: /* italic off */
249 case 27: /* inverse off */
252 case 24: /* underline off */
256 case 28: /* concealed off */
259 case 30: case 31: case 32:
260 case 33: case 34: case 35:
262 fg
= screen_color
[code
- 30];
265 case 39: /* default fg */
266 fg
= nm_fg_color
& 7;
269 case 40: case 41: case 42:
270 case 43: case 44: case 45:
272 bg
= screen_color
[code
- 40];
275 case 49: /* default bg */
276 bg
= nm_bg_color
& 7;
282 if (!is_ansi_end(*p
) || p
== p_next
)
285 * In SGR mode, the ANSI sequence is
286 * always honored; otherwise if an attr
287 * is used by itself ("\e[1m" versus
288 * "\e[1;33m", for example), set the
289 * color assigned to that attribute.
291 if (sgr_mode
|| (at
& 32))
328 #if MSDOS_COMPILER==WIN32C
329 f
&= 0xf | COMMON_LVB_UNDERSCORE
;
334 WIN32setcolors(f
, b
);
335 p_next
= anchor
= p
+ 1;
340 /* Output what's left in the buffer. */
341 WIN32textout(anchor
, ob
- anchor
);
348 * Flush buffered output.
350 * If we haven't displayed any file data yet,
351 * output messages on error output (file descriptor 2),
352 * otherwise output on standard output (file descriptor 1).
354 * This has the desirable effect of producing all
355 * error messages on error output if standard output
356 * is directed to a file. It also does the same if
357 * we never produce any real output; for example, if
358 * the input file(s) cannot be opened. If we do
359 * eventually produce output, code in edit() makes
360 * sure these messages can be seen before they are
361 * overwritten or scrolled away.
363 public void flush(void)
367 n
= (int) (ob
- obuf
);
372 #if MSDOS_COMPILER==MSOFTC
380 #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
391 if (write(outfd
, obuf
, n
) != n
)
396 * Set the output file descriptor (1=stdout or 2=stderr).
398 public void set_output(int fd
)
405 * Output a character.
407 public int putchr(int c
)
409 #if 0 /* fake UTF-8 output for testing */
413 static char ubuf
[MAX_UTF_CHAR_LEN
];
414 static int ubuf_len
= 0;
415 static int ubuf_index
= 0;
418 ubuf_len
= utf_len(c
);
421 ubuf
[ubuf_index
++] = c
;
422 if (ubuf_index
< ubuf_len
)
424 c
= get_wchar(ubuf
) & 0xFF;
428 clear_bot_if_needed();
430 if (c
== '\n' && is_tty
)
437 if (c
== '\n' && is_tty
) /* In OS-9, '\n' == 0x0D */
442 * Some versions of flush() write to *ob, so we must flush
443 * when we are still one char from the end of obuf.
445 if (ob
>= &obuf
[sizeof(obuf
)-1])
452 public void clear_bot_if_needed(void)
463 public void putstr(constant
char *s
)
471 * Convert an integral type to a string.
473 #define TYPE_TO_A_FUNC(funcname, type) \
474 void funcname(type num, char *buf, int radix) \
476 int neg = (num < 0); \
477 char tbuf[INT_STRLEN_BOUND(num)+2]; \
478 char *s = tbuf + sizeof(tbuf); \
479 if (neg) num = -num; \
482 *--s = "0123456789ABCDEF"[num % radix]; \
483 } while ((num /= radix) != 0); \
484 if (neg) *--s = '-'; \
488 TYPE_TO_A_FUNC(postoa
, POSITION
)
489 TYPE_TO_A_FUNC(linenumtoa
, LINENUM
)
490 TYPE_TO_A_FUNC(inttoa
, int)
493 * Convert a string to an integral type. Return ((type) -1) on overflow.
495 #define STR_TO_TYPE_FUNC(funcname, type) \
496 type funcname(char *buf, char **ebuf, int radix) \
502 int digit = (c >= '0' && c <= '9') ? c - '0' : (c >= 'a' && c <= 'f') ? c - 'a' + 10 : (c >= 'A' && c <= 'F') ? c - 'A' + 10 : -1; \
503 if (digit < 0 || digit >= radix) break; \
504 v |= ckd_mul(&val, val, radix); \
505 v |= ckd_add(&val, val, digit); \
507 if (ebuf != NULL) *ebuf = buf; \
508 return v ? -1 : val; \
511 STR_TO_TYPE_FUNC(lstrtopos
, POSITION
)
512 STR_TO_TYPE_FUNC(lstrtoi
, int)
513 STR_TO_TYPE_FUNC(lstrtoul
, unsigned long)
516 * Print an integral type.
518 #define IPRINT_FUNC(funcname, type, typetoa) \
519 static int funcname(type num, int radix) \
521 char buf[INT_STRLEN_BOUND(num)]; \
522 typetoa(num, buf, radix); \
524 return (int) strlen(buf); \
527 IPRINT_FUNC(iprint_int
, int, inttoa
)
528 IPRINT_FUNC(iprint_linenum
, LINENUM
, linenumtoa
)
531 * This function implements printf-like functionality
532 * using a more portable argument list mechanism than printf's.
534 * {{ This paranoia about the portability of printf dates from experiences
535 * with systems in the 1980s and is of course no longer necessary. }}
537 public int less_printf(char *fmt
, PARG
*parg
)
564 col
+= iprint_int(parg
->p_int
, 10);
568 col
+= iprint_int(parg
->p_int
, 16);
572 col
+= iprint_linenum(parg
->p_linenum
, 10);
576 s
= prchar(parg
->p_char
);
595 * If some other non-trivial char is pressed, unget it, so it will
596 * become the next command.
598 public void get_return(void)
603 while ((c
= getchr()) != '\n' && c
!= '\r')
607 if (c
!= '\n' && c
!= '\r' && c
!= ' ' && c
!= READ_INTR
)
613 * Output a message in the lower left corner of the screen
614 * and wait for carriage return.
616 public void error(char *fmt
, PARG
*parg
)
619 static char return_to_continue
[] = " (press RETURN)";
625 less_printf(fmt
, parg
);
634 at_enter(AT_STANDOUT
|AT_COLOR_ERROR
);
636 col
+= less_printf(fmt
, parg
);
637 putstr(return_to_continue
);
639 col
+= sizeof(return_to_continue
) + so_e_width
;
647 * Printing the message has probably scrolled the screen.
648 * {{ Unless the terminal doesn't have auto margins,
649 * in which case we just hammered on the right margin. }}
657 * Output a message in the lower left corner of the screen
658 * and don't wait for carriage return.
659 * Usually used to warn that we are beginning a potentially
660 * time-consuming operation.
662 static void ierror_suffix(char *fmt
, PARG
*parg
, char *suffix1
, char *suffix2
, char *suffix3
)
666 at_enter(AT_STANDOUT
|AT_COLOR_ERROR
);
667 (void) less_printf(fmt
, parg
);
676 public void ierror(char *fmt
, PARG
*parg
)
678 ierror_suffix(fmt
, parg
, "... (interrupt to abort)", "", "");
681 public void ixerror(char *fmt
, PARG
*parg
)
683 if (!supports_ctrl_x())
686 ierror_suffix(fmt
, parg
,
687 "... (", prchar(intr_char
), " or interrupt to abort)");
691 * Output a message in the lower left corner of the screen
692 * and return a single-character response.
694 public int query(char *fmt
, PARG
*parg
)
702 (void) less_printf(fmt
, parg
);