1 /*
2 Copyright (C) 2004-2017,2018 John E. Davis
3 
4 This file is part of the S-Lang Library.
5 
6 The S-Lang Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
10 
11 The S-Lang Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with this library; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19 USA.
20 */
21 
22 #include "slinclud.h"
23 
24 #include <time.h>
25 #include <ctype.h>
26 
27 #if !defined(VMS) || (__VMS_VER >= 70000000)
28 # include <sys/time.h>
29 # ifdef __QNX__
30 #  include <sys/select.h>
31 # endif
32 # include <sys/types.h>
33 #endif
34 
35 #if defined(__BEOS__) && !defined(__HAIKU__)
36 /* Prototype for select */
37 # include <net/socket.h>
38 #endif
39 
40 #ifdef HAVE_TERMIOS_H
41 # include <termios.h>
42 #endif
43 
44 #ifdef VMS
45 # include <unixlib.h>
46 # include <unixio.h>
47 # include <dvidef.h>
48 # include <descrip.h>
49 # include <lib$routines.h>
50 # include <starlet.h>
51 #else
52 # if !defined(sun)
53 #  include <sys/ioctl.h>
54 # endif
55 #endif
56 
57 #ifdef SYSV
58 # include <sys/termio.h>
59 # include <sys/stream.h>
60 # include <sys/ptem.h>
61 # include <sys/tty.h>
62 #endif
63 
64 #if defined (_AIX) && !defined (FD_SET)
65 # include <sys/select.h>	/* for FD_ISSET, FD_SET, FD_ZERO */
66 #endif
67 
68 #include <errno.h>
69 
70 #if defined(__DECC) && defined(VMS)
71 /* These get prototypes for write an sleep */
72 # include <unixio.h>
73 #endif
74 #include <signal.h>
75 
76 #include "slang.h"
77 #include "_slang.h"
78 
79 #define CROSS_COMPILE_FOR_VMS	0
80 #ifdef VMS
81 # define VMS_SYSTEM 1
82 #else
83 # if CROSS_COMPILE_FOR_VMS
84 #  define VMS 1
85 #  undef __unix__
86 # endif
87 #endif
88 
89 /* Colors:  These definitions are used for the display.  However, the
90  * application only uses object handles which get mapped to this
91  * internal representation.  The mapping is performed by the Color_Map
92  * structure below.
93  *
94  * Colors are encoded in 25 bits as follows:
95  * - Values 0-255 for standard palette
96  * - 256 for terminal's default color
97  * - 0x1RRGGBB for true colors
98  *
99  * The bits are split so we can maintain ABI compatibility on 64 bit machines.
100  */
101 #if SLTT_HAS_TRUECOLOR_SUPPORT && (SIZEOF_LONG == 8)
102 static int Has_True_Color = 0;
103 # if 0
104 #  define FG_MASK_LOW		0x0000000000FFFFFFULL
105 #  define FG_MASK_HIGH		0x0200000000000000ULL
106 #  define BG_MASK		0x01FFFFFF00000000ULL
107 #  define ATTR_MASK		0x000000003F000000ULL
108 #  define INVALID_ATTR		0xFFFFFFFFFFFFFFFFULL
109 #  define SLSMG_COLOR_DEFAULT	0x0000000000000100ULL
110 
111 #  define TRUE_COLOR_BIT		0x01000000ULL
112 #  define IS_TRUE_COLOR(f)	((f) & TRUE_COLOR_BIT)
113 #  define GET_FG(fgbg) ((((fgbg) & FG_MASK_HIGH) >> 33) | ((fgbg) & FG_MASK_LOW))
114 #  define GET_BG(fgbg) (((fgbg) & BG_MASK) >> 32)
115 #  define MAKE_COLOR(f, b) \
116    ((((f) << 33) & FG_MASK_HIGH) | ((f) & FG_MASK_LOW) \
117      | (((b) << 32) & BG_MASK))
118 # else
119 #  define FG_MASK_LOW		0x000000000000FF00ULL
120 #  define FG_MASK_HIGH		0x0000FFFF00000000ULL
121 #  define FG_TRUE_COLOR		0x0000000000000001ULL
122 #  define BG_MASK_LOW		0x0000000000FF0000ULL
123 #  define BG_MASK_HIGH		0xFFFF000000000000ULL
124 #  define BG_TRUE_COLOR		0x0000000000000002ULL
125 #  define ATTR_MASK		0x000000003F000000ULL
126 #  define INVALID_ATTR		0xFFFFFFFFFFFFFFFFULL
127 
128 #  define SLSMG_COLOR_DEFAULT		0x00000100ULL
129 #  define TRUE_COLOR_BIT		0x01000000ULL
130 #  define IS_TRUE_COLOR(f)	((f) & TRUE_COLOR_BIT)
131 #  define GET_FG(fgbg) \
132    ((((fgbg)&FG_TRUE_COLOR)<<24) | (((fgbg)&FG_MASK_HIGH)>>24) \
133      | (((fgbg)&FG_MASK_LOW)>>8))
134 #  define GET_BG(fgbg) \
135    ((((fgbg)&BG_TRUE_COLOR)<<23) | (((fgbg)&BG_MASK_HIGH)>>40) \
136      | (((fgbg)&BG_MASK_LOW)>>16))
137 #  define MAKE_COLOR(f, b) \
138    (((((f)&0x1000000)>>24) | (((f)&0xFF)<<8) | (((f)&(0xFFFF00))<<24)) \
139      | ((((b)&0x1000000)>>23) | (((b)&0xFF)<<16) | (((b)&(0xFFFF00))<<40)))
140 # endif
141 #else
142 # undef SLTT_HAS_TRUECOLOR_SUPPORT
143 # define SLTT_HAS_TRUECOLOR_SUPPORT 0
144 # define FG_MASK_LOW		0x0000FF00UL
145 # define FG_MASK_HIGH		0x00000000UL
146 # define BG_MASK		0x00FF0000UL
147 # define ATTR_MASK		0x3F000000UL
148 # define INVALID_ATTR		0xFFFFFFFFUL
149 # define SLSMG_COLOR_DEFAULT	0x000000FFUL
150 # define IS_TRUE_COLOR(f)	(0)
151 # define GET_FG(fgbg) (((fgbg) & FG_MASK_LOW)>>8)
152 # define GET_BG(fgbg) (((fgbg) & BG_MASK) >> 16)
153 # define MAKE_COLOR(fg, bg) (((fg)<<8) | ((bg)<<16))
154 #endif
155 
156 int SLtt_Screen_Cols = 80;
157 int SLtt_Screen_Rows = 24;
158 int SLtt_Term_Cannot_Insert = 0;
159 int SLtt_Term_Cannot_Scroll = 0;
160 int SLtt_Use_Ansi_Colors = 0;
161 int SLtt_Blink_Mode = 0;
162 int SLtt_Use_Blink_For_ACS = 0;
163 int SLtt_Newline_Ok = 0;
164 int SLtt_Has_Alt_Charset = 0;
165 int SLtt_Force_Keypad_Init = -1;
166 
167 static int Use_Relative_Cursor_Addressing = 0;
168 static int Max_Relative_Cursor_r = 0;
169 
170 /* static int UTF8_Mode = -1; */
171 
172 void (*_pSLtt_color_changed_hook)(void);
173 
174 static int Bce_Color_Offset = 0;
175 static int Can_Background_Color_Erase = 1;
176 
177 /* -1 means unknown */
178 int SLtt_Has_Status_Line = -1;	       /* hs */
179 int SLang_TT_Write_FD = -1;
180 
181 static int TT_Is_Initialized = 0;
182 static int Automatic_Margins;
183 /* static int No_Move_In_Standout; */
184 static int Worthless_Highlight;
185 #define HP_GLITCH_CODE
186 #ifdef HP_GLITCH_CODE
187 /* This glitch is exclusive to HP term.  Basically it means that to clear
188  * attributes, one has to erase to the end of the line.
189  */
190 static int Has_HP_Glitch;
191 #endif
192 
193 static char *Reset_Color_String;
194 static int Is_Color_Terminal = 0;
195 
196 static int Linux_Console;
197 static int Mouse_Mode = -1;
198 
199 /* The following comment is nolonger valid, but is here in case there are
200  * some apps that use SLtt_Use_Blink_For_ACS and still need porting to v2.
201  * -------
202  * It is crucial that JMAX_COLORS must be less than 128 since the high bit
203  * is used to indicate a character from the ACS (alt char set).  The exception
204  * to this rule is if SLtt_Use_Blink_For_ACS is true.  This means that of
205  * the highbit is set, we interpret that as a blink character.  This is
206  * exploited by DOSemu.
207  */
208 #ifndef SLTT_MAX_COLORS
209 # define SLTT_MAX_COLORS 0x8000       /* consistent with SLSMG_COLOR_MASK */
210 #endif
211 
212 #define JMAX_COLORS SLTT_MAX_COLORS
213 #define JNORMAL_COLOR 0
214 
215 typedef struct
216 {
217    SLtt_Char_Type fgbg;
218    SLtt_Char_Type mono;
219 }
220 Brush_Info_Type;
221 
222 static Brush_Info_Type Brush_Table[JMAX_COLORS];
223 
224 /* 0 if least significant bit is blue, not red */
225 static int Is_Fg_BGR = 0;
226 static int Is_Bg_BGR = 0;
227 #define COLOR_ARG(color, is_bgr) ((is_bgr) ? RGB_to_BGR[(color)&0x7] : (color))
228 static SLCONST int RGB_to_BGR[] =
229 {
230      0, 4, 2, 6, 1, 5, 3, 7
231 };
232 
233 static SLCONST char *Color_Fg_Str = "\033[3%dm";
234 static SLCONST char *Color_Bg_Str = "\033[4%dm";
235 #if SLTT_HAS_TRUECOLOR_SUPPORT
236 static SLCONST char *Color_RGB_Fg_Str = "\033[38;2;%d;%d;%dm";
237 static SLCONST char *Color_RGB_Bg_Str = "\033[48;2;%d;%d;%dm";
238 #endif
239 static SLCONST char *Default_Color_Fg_Str = "\033[39m";
240 static SLCONST char *Default_Color_Bg_Str = "\033[49m";
241 
242 static int Max_Terminfo_Colors = 8;	       /* termcap Co */
243 
244 char *SLtt_Graphics_Char_Pairs = NULL;	       /* ac termcap string -- def is vt100 */
245 
246 /* 1 if terminal lacks the ability to go into insert mode or into delete
247    mode. Currently controlled by S-Lang but later perhaps termcap. */
248 
249 static SLCONST char *UnderLine_Vid_Str;
250 static SLCONST char *Blink_Vid_Str;
251 static SLCONST char *Italic_Vid_Str;
252 static SLCONST char *Bold_Vid_Str;
253 static SLCONST char *Ins_Mode_Str; /* = "\033[4h"; */   /* ins mode (im) */
254 static SLCONST char *Eins_Mode_Str; /* = "\033[4l"; */  /* end ins mode (ei) */
255 static SLCONST char *Scroll_R_Str; /* = "\033[%d;%dr"; */ /* scroll region */
256 static SLCONST char *Cls_Str; /* = "\033[2J\033[H"; */  /* cl termcap STR  for ansi terminals */
257 static SLCONST char *Rev_Vid_Str; /* = "\033[7m"; */    /* mr,so termcap string */
258 static SLCONST char *Norm_Vid_Str; /* = "\033[m"; */   /* me,se termcap string */
259 static SLCONST char *Del_Eol_Str; /* = "\033[K"; */	       /* ce */
260 static SLCONST char *Del_Bol_Str; /* = "\033[1K"; */	       /* cb */
261 static SLCONST char *Del_Char_Str; /* = "\033[P"; */   /* dc */
262 static SLCONST char *Del_N_Lines_Str; /* = "\033[%dM"; */  /* DL */
263 static SLCONST char *Add_N_Lines_Str; /* = "\033[%dL"; */  /* AL */
264 static SLCONST char *Rev_Scroll_Str;
265 static SLCONST char *Curs_Up_Str;      /* "up" */
266 static SLCONST char *Curs_UpN_Str;     /* "UP" */
267 static SLCONST char *Curs_Dn_Str;      /* "do" */
268 static SLCONST char *Curs_DnN_Str;     /* "DO" */
269 static SLCONST char *Curs_Right_Str;    /* "nd" */
270 static SLCONST char *Curs_RightN_Str;    /* "RI" */
271 static SLCONST char *Curs_Left_Str;    /* "bc", "le" */
272 static SLCONST char *Curs_LeftN_Str;    /* "LE" */
273 static SLCONST char *Clear_EOS_Str;   /* cd */
274 
275 static SLCONST char *Cursor_Visible_Str;    /* ve termcap string */
276 static SLCONST char *Cursor_Invisible_Str;    /* vi termcap string */
277 #if 0
278 static SLCONST char *Start_Mouse_Rpt_Str;  /* Start mouse reporting mode */
279 static SLCONST char *End_Mouse_Rpt_Str;  /* End mouse reporting mode */
280 #endif
281 static SLCONST char *Start_Alt_Chars_Str;  /* as */
282 static SLCONST char *End_Alt_Chars_Str;   /* ae */
283 static SLCONST char *Enable_Alt_Char_Set;  /* eA */
284 
285 static SLCONST char *Start_Abs_Cursor_Addressing_Mode;
286 static SLCONST char *Keypad_Init_Str;
287 static SLCONST char *End_Abs_Cursor_Addressing_Mode;
288 static SLCONST char *Keypad_Reset_Str;
289 
290 /* status line functions */
291 static SLCONST char *Disable_Status_line_Str;  /* ds */
292 static SLCONST char *Return_From_Status_Line_Str;   /* fs */
293 static SLCONST char *Goto_Status_Line_Str;     /* ts */
294 /* static int Num_Status_Line_Columns; */   /* ws */
295 /* static int Status_Line_Esc_Ok;	 */       /* es */
296 
297 /* cm string has %i%d since termcap numbers columns from 0 */
298 /* char *CURS_POS_STR = "\033[%d;%df";  ansi-- hor and vert pos */
299 static SLCONST char *Abs_Curs_Pos_Str; /* = "\033[%i%d;%dH";*/   /* cm termcap string */
300 
301 /* scrolling region */
302 static int Scroll_r1 = 0, Scroll_r2 = 23;
303 static int Cursor_r, Cursor_c;	       /* 0 based */
304 
305 /* current attributes --- initialized to impossible value */
306 static SLtt_Char_Type Current_Fgbg = INVALID_ATTR;
307 
308 static int Cursor_Set;		       /* 1 if cursor position known, 0
309 					* if not.  -1 if only row is known
310 					*/
311 /* This is used only in relative-cursor-addressing mode */
312 static SLsmg_Char_Type Display_Start_Chars[SLTT_MAX_SCREEN_ROWS];
313 
314 #define MAX_OUTPUT_BUFFER_SIZE 4096
315 
316 static unsigned char Output_Buffer[MAX_OUTPUT_BUFFER_SIZE];
317 static unsigned char *Output_Bufferp = Output_Buffer;
318 
319 unsigned long SLtt_Num_Chars_Output = 0;
320 
_pSLusleep(unsigned long usecs)321 int _pSLusleep (unsigned long usecs)
322 {
323 #if !defined(VMS) || (__VMS_VER >= 70000000)
324    struct timeval tv;
325    tv.tv_sec = usecs / 1000000;
326    tv.tv_usec = usecs % 1000000;
327    return select(0, NULL, NULL, NULL, &tv);
328 #else
329    return 0;
330 #endif
331 }
332 
SLtt_flush_output(void)333 int SLtt_flush_output (void)
334 {
335    size_t total;
336    size_t n = (Output_Bufferp - Output_Buffer);
337 
338    SLtt_Num_Chars_Output += n;
339 
340    total = 0;
341    while (n > 0)
342      {
343 	ssize_t nwrite = write (SLang_TT_Write_FD, (char *) Output_Buffer + total, n);
344 	if (nwrite == -1)
345 	  {
346 	     nwrite = 0;
347 #ifdef EAGAIN
348 	     if (errno == EAGAIN)
349 	       {
350 		  _pSLusleep (100000);   /* 1/10 sec */
351 		  continue;
352 	       }
353 #endif
354 #ifdef EWOULDBLOCK
355 	     if (errno == EWOULDBLOCK)
356 	       {
357 		  _pSLusleep (100000);
358 		  continue;
359 	       }
360 #endif
361 #ifdef EINTR
362 	     if (errno == EINTR) continue;
363 #endif
364 	     break;
365 	  }
366 	n -= nwrite;
367 	total += nwrite;
368      }
369    Output_Bufferp = Output_Buffer;
370    return n;
371 }
372 
373 int SLtt_Baud_Rate = 0;
tt_write(SLCONST char * str,SLstrlen_Type n)374 static void tt_write(SLCONST char *str, SLstrlen_Type n)
375 {
376    static unsigned long last_time;
377    static SLstrlen_Type total;
378 
379    if ((str == NULL) || (n == 0)) return;
380    total += n;
381 
382    while (1)
383      {
384 	size_t ndiff = MAX_OUTPUT_BUFFER_SIZE - (Output_Bufferp - Output_Buffer);
385 	if (ndiff < n)
386 	  {
387 	     memcpy ((char *) Output_Bufferp, str, ndiff);
388 	     Output_Bufferp += ndiff;
389 	     SLtt_flush_output ();
390 	     n -= ndiff;
391 	     str += ndiff;
392 	  }
393 	else
394 	  {
395 	     memcpy ((char *) Output_Bufferp, str, n);
396 	     Output_Bufferp += n;
397 	     break;
398 	  }
399      }
400 
401    if (((SLtt_Baud_Rate > 150) && (SLtt_Baud_Rate <= 9600))
402        && (10 * total > (unsigned int)SLtt_Baud_Rate))
403      {
404 	unsigned long now;
405 	total = 0;
406 	if ((now = (unsigned long) time(NULL)) - last_time <= 1)
407 	  {
408 	     SLtt_flush_output ();
409 	     sleep((unsigned) 1);
410 	  }
411 	last_time = now;
412      }
413 }
414 
tt_write_string(SLCONST char * str)415 static void tt_write_string (SLCONST char *str)
416 {
417    if (str != NULL) tt_write(str, strlen(str));
418 }
419 
SLtt_write_string(SLFUTURE_CONST char * str)420 void SLtt_write_string (SLFUTURE_CONST char *str)
421 {
422    tt_write_string (str);
423    Cursor_Set = 0;
424 }
425 
SLtt_putchar(char ch)426 void SLtt_putchar (char ch)
427 {
428    SLtt_normal_video ();
429    if (Cursor_Set == 1)
430      {
431 	if (ch >= ' ') Cursor_c++;
432 	else if (ch == '\b') Cursor_c--;
433 	else if (ch == '\r') Cursor_c = 0;
434 	else Cursor_Set = 0;
435 
436 	if ((Cursor_c + 1 == SLtt_Screen_Cols)
437 	    && Automatic_Margins) Cursor_Set = 0;
438      }
439 
440    if (Output_Bufferp < Output_Buffer + MAX_OUTPUT_BUFFER_SIZE)
441      {
442 	*Output_Bufferp++ = (unsigned char) ch;
443      }
444    else tt_write (&ch, 1);
445 }
446 
447 #if defined(__GNUC__)
448 # pragma GCC diagnostic ignored "-Wformat-nonliteral"
449 #endif
tt_sprintf(char * buf,unsigned int buflen,SLCONST char * fmt,int x,int y)450 static unsigned int tt_sprintf(char *buf, unsigned int buflen, SLCONST char *fmt, int x, int y)
451 {
452    SLCONST char *fmt_max;
453    unsigned char *b, *bmax;
454    int offset;
455    int z, z1, parse_level;
456    int zero_pad;
457    int field_width;
458    int variables [26];
459    int stack [64];
460    unsigned int stack_len;
461    int parms [10];
462 #define STACK_POP (stack_len ? stack[--stack_len] : 0)
463 
464    if (fmt == NULL)
465      {
466 	*buf = 0;
467 	return 0;
468      }
469 
470    stack [0] = y;	       /* pushed for termcap */
471    stack [1] = x;
472    stack_len = 2;
473 
474    parms [1] = x;	       /* p1 */
475    parms [2] = y;	       /* p2 */
476 
477    offset = 0;
478    zero_pad = 0;
479    field_width = 0;
480 
481    b = (unsigned char *) buf;
482    bmax = b + buflen;
483 
484    fmt_max = fmt + strlen (fmt);
485 
486    while ((fmt < fmt_max) && (b < bmax))
487      {
488 	unsigned char ch = *fmt++;
489 
490 	if (ch != '%')
491 	  {
492 	     *b++ = ch;
493 	     continue;
494 	  }
495 
496 	if (fmt == fmt_max) break;
497 	ch = *fmt++;
498 
499 	switch (ch)
500 	  {
501 	   default:
502 	     *b++ = ch;
503 	     break;
504 
505 	   case 'p':
506 
507 	     if (fmt == fmt_max) break;
508 	     ch = *fmt++;
509 	     if ((ch >= '0') && (ch <= '9'))
510 	       stack [stack_len++] = parms [ch - '0'];
511 	     break;
512 
513 	   case '\'':   /* 'x' */
514 	     if (fmt == fmt_max) break;
515 	     stack [stack_len++] = *fmt++;
516 	     if (fmt < fmt_max) fmt++;     /* skip ' */
517 	     break;
518 
519 	   case '{':	       /* literal constant, e.g. {30} */
520 	     z = 0;
521 	     while ((fmt < fmt_max) && ((ch = *fmt) <= '9') && (ch >= '0'))
522 	       {
523 		  z = z * 10 + (ch - '0');
524 		  fmt++;
525 	       }
526 	     stack [stack_len++] = z;
527 	     if ((ch == '}') && (fmt < fmt_max)) fmt++;
528 	     break;
529 
530 	   case '0':
531 	     if (fmt == fmt_max) break;
532 	     ch = *fmt;
533 	     if ((ch != '2') && (ch != '3'))
534 	       break;
535 	     zero_pad = 1;
536 	     fmt++;
537 	     /* drop */
538 
539 	   case '2':
540 	   case '3':
541 	     if (fmt == fmt_max) break;
542 	     if (*fmt == 'x')
543 	       {
544 		  char x_fmt_buf [4];
545 		  char *x_fmt_buf_ptr;
546 
547 		  x_fmt_buf_ptr = x_fmt_buf;
548 		  if (zero_pad) *x_fmt_buf_ptr++ = '0';
549 		  *x_fmt_buf_ptr++ = ch;
550 		  *x_fmt_buf_ptr++ = 'X';
551 		  *x_fmt_buf_ptr = 0;
552 
553 		  z = STACK_POP;
554 		  z += offset;
555 
556 		  sprintf ((char *)b, x_fmt_buf, z);
557 		  b += strlen ((char *)b);
558 		  zero_pad = 0;
559 		  break;
560 	       }
561 
562 	     field_width = (ch - '0');
563 		  /* drop */
564 
565 	   case 'd':
566 	     z = STACK_POP;
567 	     z += offset;
568 	     if (z >= 100)
569 	       {
570 		  *b++ = z / 100 + '0';
571 		  z = z % 100;
572 		  zero_pad = 1;
573 		  field_width = 2;
574 	       }
575 	     else if (zero_pad && (field_width == 3))
576 	       *b++ = '0';
577 
578 	     if (b == bmax) break;
579 	     if (z >= 10)
580 	       {
581 		  *b++ = z / 10 + '0';
582 		  z = z % 10;
583 	       }
584 	     else if (zero_pad && (field_width >= 2))
585 	       *b++ = '0';
586 
587 	     if (b == bmax) break;
588 	     *b++ = z + '0';
589 	     field_width = zero_pad = 0;
590 	     break;
591 
592 	   case 'x':
593 	     z = STACK_POP;
594 	     z += offset;
595 	     if (b + 16 >= bmax)
596 	       break;
597 	     sprintf ((char *) b, "%X", z);
598 	     b += strlen ((char *)b);
599 	     break;
600 
601 	   case 'i':
602 	     offset = 1;
603 	     break;
604 
605 	   case '+':
606 	     /* Handling this depends upon whether or not we are parsing
607 	      * terminfo.  Terminfo requires the stack so use it as an
608 	      * indicator.
609 	      */
610 	     if (stack_len > 2)
611 	       {
612 		  z = STACK_POP;
613 		  stack [stack_len - 1] += z;
614 	       }
615 	     else if (fmt < fmt_max)
616 	       {
617 		  ch = *fmt++;
618 		  if ((unsigned char) ch == 128) ch = 0;
619 		  ch = ch + (unsigned char) STACK_POP;
620 		  if (ch == '\n') ch++;
621 		  *b++ = ch;
622 	       }
623 	     break;
624 
625 	     /* Binary operators */
626 	   case '-':
627 	   case '*':
628 	   case '/':
629 	   case 'm':
630 	   case '&':
631 	   case '|':
632 	   case '^':
633 	   case '=':
634 	   case '>':
635 	   case '<':
636 	   case 'A':
637 	   case 'O':
638 	     z1 = STACK_POP;
639 	     z = STACK_POP;
640 	     switch (ch)
641 	       {
642 		case '-': z = (z - z1); break;
643 		case '*': z = (z * z1); break;
644 		case '/': z = (z / z1); break;
645 		case 'm': z = (z % z1); break;
646 		case '&': z = (z & z1); break;
647 		case '|': z = (z | z1); break;
648 		case '^': z = (z ^ z1); break;
649 		case '=': z = (z == z1); break;
650 		case '>': z = (z > z1); break;
651 		case '<': z = (z < z1); break;
652 		case 'A': z = (z && z1); break;
653 		case 'O': z = (z || z1); break;
654 	       }
655 	     stack [stack_len++] = z;
656 	     break;
657 
658 	     /* unary */
659 	   case '!':
660 	     z = STACK_POP;
661 	     stack [stack_len++] = !z;
662 	     break;
663 
664 	   case '~':
665 	     z = STACK_POP;
666 	     stack [stack_len++] = ~z;
667 	     break;
668 
669 	   case 'r':		       /* termcap -- swap parameters */
670 	     z = stack [0];
671 	     stack [0] = stack [1];
672 	     stack [1] = z;
673 	     break;
674 
675 	   case '.':		       /* termcap */
676 	   case 'c':
677 	     ch = (unsigned char) STACK_POP;
678 	     if (ch == '\n') ch++;
679 	     *b++ = ch;
680 	     break;
681 
682 	   case 'g':
683 	     if (fmt == fmt_max) break;
684 	     ch = *fmt++;
685 	     if ((ch >= 'a') && (ch <= 'z'))
686 	       stack [stack_len++] = variables [ch - 'a'];
687 	     break;
688 
689 	   case 'P':
690 	     if (fmt == fmt_max) break;
691 	     ch = *fmt++;
692 	     if ((ch >= 'a') && (ch <= 'z'))
693 	       variables [ch - 'a'] = STACK_POP;
694 	     break;
695 
696 	     /* If then else parsing.  Actually, this is rather easy.  The
697 	      * key is to notice that 'then' does all the work.  'if' simply
698 	      * there to indicate the start of a test and endif indicates
699 	      * the end of tests.  If 'else' is seen, then skip to
700 	      * endif.
701 	      */
702 	   case '?':		       /* if */
703 	   case ';':		       /* endif */
704 	     break;
705 
706 	   case 't':		       /* then */
707 	     z = STACK_POP;
708 	     if (z != 0)
709 	       break;		       /* good.  Continue parsing. */
710 
711 	     /* z == 0 and test has failed.  So, skip past this entire if
712 	      * expression to the matching else or matching endif.
713 	      */
714 	     /* drop */
715 	   case 'e':		       /* else */
716 
717 	     parse_level = 0;
718 	     while (fmt < fmt_max)
719 	       {
720 		  unsigned char ch1;
721 
722 		  ch1 = *fmt++;
723 		  if ((ch1 != '%') || (fmt == fmt_max))
724 		    continue;
725 
726 		  ch1 = *fmt++;
727 
728 		  if (ch1 == '?') parse_level++;   /* new if */
729 		  else if (ch1 == 'e')
730 		    {
731 		       if ((ch != 'e') && (parse_level == 0))
732 			 break;
733 		    }
734 		  else if (ch1 == ';')
735 		    {
736 		       if (parse_level == 0)
737 			 break;
738 		       parse_level--;
739 		    }
740 	       }
741 	     break;
742 	  }
743      }
744    if (b >= bmax)
745      b = bmax - 1;
746    *b = 0;
747 
748    return (unsigned int) (b - (unsigned char *) buf);
749 }
750 #if defined(__GNUC__)
751 # pragma GCC diagnostic warning "-Wformat-nonliteral"
752 #endif
753 
tt_printf(SLCONST char * fmt,int x,int y)754 static void tt_printf(SLCONST char *fmt, int x, int y)
755 {
756    char buf[1024];
757    unsigned int n;
758    if (fmt == NULL) return;
759    n = tt_sprintf(buf, sizeof (buf), fmt, x, y);
760    tt_write(buf, n);
761 }
762 
SLtt_set_scroll_region(int r1,int r2)763 void SLtt_set_scroll_region (int r1, int r2)
764 {
765    if (Use_Relative_Cursor_Addressing)
766      return;
767 
768    Scroll_r1 = r1;
769    Scroll_r2 = r2;
770    tt_printf (Scroll_R_Str, Scroll_r1, Scroll_r2);
771    Cursor_Set = 0;
772 }
773 
SLtt_reset_scroll_region(void)774 void SLtt_reset_scroll_region (void)
775 {
776    SLtt_set_scroll_region(0, SLtt_Screen_Rows - 1);
777 }
778 
SLtt_set_cursor_visibility(int show)779 int SLtt_set_cursor_visibility (int show)
780 {
781    if ((Cursor_Visible_Str == NULL) || (Cursor_Invisible_Str == NULL))
782      return -1;
783 
784    tt_write_string (show ? Cursor_Visible_Str : Cursor_Invisible_Str);
785    return 0;
786 }
787 
cursor_motion(SLCONST char * s1,SLCONST char * sN,int n)788 static void cursor_motion (SLCONST char *s1, SLCONST char *sN, int n)
789 {
790    if ((n == 1) && (s1 != NULL))
791      {
792 	tt_write_string (s1);
793 	return;
794      }
795 
796    if (n <= 0)
797      return;
798 
799    if (sN != NULL)
800      tt_printf (sN, n, 0);
801    else while (n > 0)
802      {
803 	tt_write_string (s1);
804 	n--;
805      }
806 }
807 
goto_relative_rc(int r,int c)808 static void goto_relative_rc (int r, int c)
809 {
810    if (r < 0)
811      return;
812 
813    if (Cursor_Set != 1)
814      {
815 	/* Do not know where the cursor is.  At least get the column correct */
816 	tt_write ("\r", 1);
817 	Cursor_c = 0;
818 	Cursor_Set = 1;		       /* pretend we know the row */
819      }
820 
821    if (Cursor_r > r)
822      cursor_motion (Curs_Up_Str, Curs_UpN_Str, Cursor_r-r);
823    else if (Cursor_r < r)
824      {
825 	tt_write ("\r", 1); Cursor_c = 0;
826 	if (r > Max_Relative_Cursor_r)
827 	  {
828 	     cursor_motion (Curs_Dn_Str, Curs_DnN_Str, Max_Relative_Cursor_r-Cursor_r);
829 	     Cursor_r = Max_Relative_Cursor_r;
830 	     while (Cursor_r < r)
831 	       {
832 		  tt_write ("\n", 1);
833 		  Cursor_r++;
834 	       }
835 	  }
836 	else
837 	  cursor_motion (Curs_Dn_Str, Curs_DnN_Str, r-Cursor_r);
838      }
839    Cursor_r = r;
840    if (r > Max_Relative_Cursor_r)
841      Max_Relative_Cursor_r = r;
842 
843    if (Cursor_c > c)
844      cursor_motion (Curs_Left_Str, Curs_LeftN_Str, Cursor_c-c);
845    else if (Cursor_c < c)
846      cursor_motion (Curs_Right_Str, Curs_RightN_Str, c-Cursor_c);
847 
848    Cursor_c = c;
849    Cursor_Set = 1;
850 }
851 
852 /* the goto_rc function moves to row relative to scrolling region */
SLtt_goto_rc(int r,int c)853 void SLtt_goto_rc(int r, int c)
854 {
855    char *s = NULL;
856    char buf[6];
857 
858    if ((c < 0) || (r < 0))
859      {
860 	Cursor_Set = 0;
861 	Cursor_c = 0;
862 	Cursor_r = 0;
863 	tt_write("\r", 1);
864 	return;
865      }
866    if (Use_Relative_Cursor_Addressing)
867      {
868 	goto_relative_rc (r, c);
869 	return;
870      }
871 
872    r += Scroll_r1;
873 
874    if ((Cursor_Set > 0)
875        || ((Cursor_Set < 0) && !Automatic_Margins))
876      {
877 	int n = r - Cursor_r;
878 	if ((n == -1) && (Cursor_Set > 0) && (Cursor_c == c)
879 	    && (Curs_Up_Str != NULL))
880 	  {
881 	     s = (char *)Curs_Up_Str;
882 	  }
883 	else if ((n >= 0) && (n <= 4))
884 	  {
885 	     if ((n == 0) && (Cursor_Set == 1)
886 		 && ((c > 1) || (c == Cursor_c)))
887 	       {
888 		  if (Cursor_c == c) return;
889 		  if (Cursor_c == c + 1)
890 		    {
891 		       s = buf;
892 		       *s++ = '\b'; *s = 0;
893 		       s = buf;
894 		    }
895 	       }
896 	     else if (c == 0)
897 	       {
898 		  s = buf;
899 		  if ((Cursor_Set != 1) || (Cursor_c != 0)) *s++ = '\r';
900 		  while (n--) *s++ = '\n';
901 #ifdef VMS
902 		  /* Need to add this after \n to start a new record.  Sheesh. */
903 		  *s++ = '\r';
904 #endif
905 		  *s = 0;
906 		  s = buf;
907 	       }
908 	     /* Will fail on VMS */
909 #ifndef VMS
910 	     else if (SLtt_Newline_Ok && (Cursor_Set == 1) &&
911 		      (Cursor_c >= c) && (c + 3 > Cursor_c))
912 	       {
913 		  s = buf;
914 		  while (n--) *s++ = '\n';
915 		  n = Cursor_c - c;
916 		  while (n--) *s++ = '\b';
917 		  *s = 0;
918 		  s = buf;
919 	       }
920 #endif
921 	  }
922      }
923    if (s != NULL) tt_write_string(s);
924    else
925      {
926 	tt_printf(Abs_Curs_Pos_Str, r, c);
927      }
928    Cursor_c = c; Cursor_r = r;
929    Cursor_Set = 1;
930 }
931 
SLtt_begin_insert(void)932 void SLtt_begin_insert (void)
933 {
934    tt_write_string(Ins_Mode_Str);
935 }
936 
SLtt_end_insert(void)937 void SLtt_end_insert (void)
938 {
939    tt_write_string(Eins_Mode_Str);
940 }
941 
SLtt_delete_char(void)942 void SLtt_delete_char (void)
943 {
944    SLtt_normal_video ();
945    tt_write_string(Del_Char_Str);
946 }
947 
SLtt_erase_line(void)948 void SLtt_erase_line (void)
949 {
950    tt_write ("\r", 1);
951    Cursor_Set = 1; Cursor_c = 0;
952    SLtt_del_eol();
953    /* Put the cursor back at the beginning of the line */
954    tt_write_string("\r");
955    Cursor_Set = 1; Cursor_c = 0;
956 }
957 
958 /* It appears that the Linux console, and most likely others do not
959  * like scrolling regions that consist of one line.  So I have to
960  * resort to this stupidity to make up for that stupidity.
961  */
delete_line_in_scroll_region(void)962 static void delete_line_in_scroll_region (void)
963 {
964    SLtt_goto_rc (Cursor_r - Scroll_r1, 0);
965    SLtt_del_eol ();
966 }
967 
SLtt_delete_nlines(int nn)968 void SLtt_delete_nlines (int nn)
969 {
970    unsigned int n;
971 
972    if (nn <= 0) return;
973    n = (unsigned int) nn;
974 
975    SLtt_normal_video ();
976 
977    if (Scroll_r1 == Scroll_r2)
978      {
979 	delete_line_in_scroll_region ();
980 	return;
981      }
982 
983    if (Del_N_Lines_Str != NULL) tt_printf(Del_N_Lines_Str, n, 0);
984    else
985    /* get a new terminal */
986      {
987 	char buf[80];
988 	int curs, r1;
989 	unsigned int dn = n;
990 
991 	if (dn > sizeof (buf))
992 	  dn = sizeof (buf);
993 
994 	SLMEMSET (buf, '\n', dn);
995 	while (n > dn)
996 	  {
997 	     tt_write (buf, dn);
998 	     n -= dn;
999 	  }
1000 	tt_write (buf, n);
1001 
1002 	r1 = Scroll_r1;
1003 	curs = Cursor_r;
1004 	SLtt_set_scroll_region(curs, Scroll_r2);
1005 	SLtt_goto_rc(Scroll_r2 - Scroll_r1, 0);
1006 	SLMEMSET(buf, '\n', (unsigned int) n);
1007 	tt_write(buf, (unsigned int) n);
1008 	/* while (n--) tt_putchar('\n'); */
1009 	SLtt_set_scroll_region(r1, Scroll_r2);
1010 	SLtt_goto_rc(curs, 0);
1011      }
1012 }
1013 
cls_internal(SLCONST char * escseq,int rmin)1014 static void cls_internal (SLCONST char *escseq, int rmin)
1015 {
1016    /* If the terminal is a color terminal but the user wants black and
1017     * white, then make sure that the colors are reset.  This appears to be
1018     * necessary.
1019     */
1020    if ((SLtt_Use_Ansi_Colors == 0) && Is_Color_Terminal)
1021      {
1022 	if (Reset_Color_String != NULL)
1023 	  tt_write_string (Reset_Color_String);
1024 	else
1025 	  tt_write_string ("\033[0m\033[m");
1026      }
1027 
1028    SLtt_normal_video();
1029    SLtt_reset_scroll_region ();
1030 
1031    tt_write_string(escseq);
1032 
1033    if (Use_Relative_Cursor_Addressing)
1034      {
1035 	int r, rmax = SLtt_Screen_Rows;
1036 	for (r = rmin; r < rmax; r++)
1037 	  Display_Start_Chars[r].nchars = 0;
1038      }
1039 }
1040 
SLtt_cls(void)1041 void SLtt_cls (void)
1042 {
1043    cls_internal (Cls_Str, 0);
1044 }
1045 
_pSLtt_clear_eos(void)1046 static void _pSLtt_clear_eos (void)
1047 {
1048    int rmin = Cursor_r;
1049    if (Cursor_c > 0) rmin++;
1050    cls_internal (Clear_EOS_Str, rmin);
1051 }
1052 
SLtt_reverse_index(int n)1053 void SLtt_reverse_index (int n)
1054 {
1055    if (!n) return;
1056 
1057    SLtt_normal_video();
1058 
1059    if (Scroll_r1 == Scroll_r2)
1060      {
1061 	delete_line_in_scroll_region ();
1062 	return;
1063      }
1064 
1065    if (Add_N_Lines_Str != NULL) tt_printf(Add_N_Lines_Str,n, 0);
1066    else
1067      {
1068 	while(n--) tt_write_string(Rev_Scroll_Str);
1069      }
1070 }
1071 
1072 int SLtt_Ignore_Beep = 1;
1073 static SLCONST char *Visible_Bell_Str;
1074 
SLtt_beep(void)1075 void SLtt_beep (void)
1076 {
1077    if (SLtt_Ignore_Beep & 0x1) SLtt_putchar('\007');
1078 
1079    if (SLtt_Ignore_Beep & 0x2)
1080      {
1081 	if (Visible_Bell_Str != NULL) tt_write_string (Visible_Bell_Str);
1082 #ifdef __linux__
1083 	else if (Linux_Console)
1084 	  {
1085 	     tt_write ("\033[?5h", 5);
1086 	     SLtt_flush_output ();
1087 	     _pSLusleep (50000);
1088 	     tt_write ("\033[?5l", 5);
1089 	  }
1090 #endif
1091      }
1092    SLtt_flush_output ();
1093 }
1094 
1095 static void write_string_with_care (SLCONST char *);
1096 
del_eol(void)1097 static void del_eol (void)
1098 {
1099    if ((Cursor_c == 0)
1100        && (Use_Relative_Cursor_Addressing)
1101        && (Cursor_r < SLTT_MAX_SCREEN_ROWS))
1102      {
1103 	Display_Start_Chars[Cursor_r].nchars = 0;
1104      }
1105 
1106    if ((Del_Eol_Str != NULL)
1107        && (Can_Background_Color_Erase || (Current_Fgbg == 0)))
1108      {
1109 	tt_write_string(Del_Eol_Str);
1110 	return;
1111      }
1112 
1113    while (Cursor_c < SLtt_Screen_Cols)
1114      {
1115 	write_string_with_care (" ");
1116 	Cursor_c++;
1117      }
1118    Cursor_c = SLtt_Screen_Cols - 1;
1119    Cursor_Set = 0;
1120 }
1121 
SLtt_del_eol(void)1122 void SLtt_del_eol (void)
1123 {
1124    if (Current_Fgbg != INVALID_ATTR) SLtt_normal_video ();
1125    del_eol ();
1126 }
1127 
1128 typedef SLCONST struct
1129 {
1130    SLCONST char *name;
1131    SLtt_Char_Type color;
1132 }
1133 Color_Def_Type;
1134 
1135 #define MAX_COLOR_NAMES 17
1136 static Color_Def_Type Color_Defs [MAX_COLOR_NAMES] =
1137 {
1138      {"black",		SLSMG_COLOR_BLACK},
1139      {"red",		SLSMG_COLOR_RED},
1140      {"green",		SLSMG_COLOR_GREEN},
1141      {"brown",		SLSMG_COLOR_BROWN},
1142      {"blue",		SLSMG_COLOR_BLUE},
1143      {"magenta",	SLSMG_COLOR_MAGENTA},
1144      {"cyan",		SLSMG_COLOR_CYAN},
1145      {"lightgray",	SLSMG_COLOR_LGRAY},
1146      {"gray",		SLSMG_COLOR_GRAY},
1147      {"brightred",	SLSMG_COLOR_BRIGHT_RED},
1148      {"brightgreen",	SLSMG_COLOR_BRIGHT_GREEN},
1149      {"yellow",		SLSMG_COLOR_BRIGHT_BROWN},
1150      {"brightblue",	SLSMG_COLOR_BRIGHT_BLUE},
1151      {"brightmagenta",	SLSMG_COLOR_BRIGHT_MAGENTA},
1152      {"brightcyan",	SLSMG_COLOR_BRIGHT_CYAN},
1153      {"white",		SLSMG_COLOR_BRIGHT_WHITE},
1154      {"default",	SLSMG_COLOR_DEFAULT}
1155 };
1156 
1157 static int Brushes_Initialized = 0;
1158 
initialize_brushes(void)1159 static int initialize_brushes (void)
1160 {
1161    Brush_Info_Type *b, *bmax;
1162    SLtt_Char_Type bg;
1163 
1164    b = Brush_Table;
1165    bmax = b + JMAX_COLORS;
1166 
1167    bg = 0;
1168    while (b < bmax)
1169      {
1170 	SLtt_Char_Type fg = 7;
1171 	while (b < bmax)
1172 	  {
1173 	     if (fg != bg)
1174 	       {
1175 		  b->fgbg = MAKE_COLOR(fg,bg);
1176 		  b->mono = SLTT_REV_MASK;
1177 		  b++;
1178 	       }
1179 	     if (fg == 0)
1180 	       break;
1181 	     fg--;
1182 	  }
1183 	bg++;
1184 	if (bg == 8)
1185 	  bg = 0;
1186      }
1187 
1188    Brush_Table[0].mono = 0;
1189    Brushes_Initialized = 1;
1190    return 0;
1191 }
1192 
get_brush_info(SLsmg_Color_Type color)1193 static Brush_Info_Type *get_brush_info (SLsmg_Color_Type color)
1194 {
1195    if (Brushes_Initialized == 0)
1196      initialize_brushes ();
1197 
1198    color &= SLSMG_COLOR_MASK;
1199 
1200    if (color >= JMAX_COLORS)
1201      color = 0;
1202 
1203    return Brush_Table + color;
1204 }
1205 
get_brush_attr(SLsmg_Color_Type color)1206 static SLtt_Char_Type get_brush_attr (SLsmg_Color_Type color)
1207 {
1208    Brush_Info_Type *b;
1209 
1210    if (NULL == (b = get_brush_info (color)))
1211      return INVALID_ATTR;
1212 
1213    if (SLtt_Use_Ansi_Colors)
1214      return b->fgbg;
1215 
1216    return b->mono;
1217 }
1218 
get_brush_fgbg(SLsmg_Color_Type color)1219 static SLtt_Char_Type get_brush_fgbg (SLsmg_Color_Type color)
1220 {
1221    Brush_Info_Type *b = get_brush_info(color);
1222    if (b == NULL)
1223      return INVALID_ATTR;
1224    return b->fgbg;
1225 }
1226 
SLtt_set_mono(int obj,SLFUTURE_CONST char * what,SLtt_Char_Type mask)1227 int SLtt_set_mono (int obj, SLFUTURE_CONST char *what, SLtt_Char_Type mask)
1228 {
1229    Brush_Info_Type *b;
1230 
1231    (void) what;
1232    if (NULL == (b = get_brush_info (obj)))
1233      return -1;
1234 
1235    b->mono = mask & ATTR_MASK;
1236    return 0;
1237 }
1238 
check_color_for_digit_form(SLCONST char * color)1239 static SLCONST char *check_color_for_digit_form (SLCONST char *color)
1240 {
1241    unsigned int i, ich;
1242    unsigned char *s = (unsigned char *) color;
1243 
1244    i = 0;
1245    while ((ich = (unsigned int) *s) != 0)
1246      {
1247 	if ((ich < '0') || (ich > '9'))
1248 	  return color;
1249 
1250 	i = i * 10 + (ich - '0');
1251 	s++;
1252      }
1253 
1254    if (i < MAX_COLOR_NAMES)
1255      color = Color_Defs[i].name;
1256 
1257    return color;
1258 }
1259 
get_default_colors(SLCONST char ** fgp,SLCONST char ** bgp)1260 static int get_default_colors (SLCONST char **fgp, SLCONST char **bgp)
1261 {
1262    static char fg_buf[16], bg_buf[16];
1263    static SLCONST char *bg, *fg;
1264    static int already_parsed;
1265    char *p, *pmax;
1266 
1267    if (already_parsed == -1)
1268      return -1;
1269 
1270    if (already_parsed)
1271      {
1272 	*fgp = fg;
1273 	*bgp = bg;
1274 	return 0;
1275      }
1276 
1277    already_parsed = -1;
1278 
1279    bg = getenv ("COLORFGBG");
1280 
1281    if (bg == NULL)
1282      {
1283 	bg = getenv ("DEFAULT_COLORS");
1284 	if (bg == NULL)
1285 	  return -1;
1286      }
1287 
1288    p = fg_buf;
1289    pmax = p + (sizeof (fg_buf) - 1);
1290 
1291    while ((*bg != 0) && (*bg != ';'))
1292      {
1293 	if (p < pmax) *p++ = *bg;
1294 	bg++;
1295      }
1296    *p = 0;
1297 
1298    if (*bg) bg++;
1299 
1300    p = bg_buf;
1301    pmax = p + (sizeof (bg_buf) - 1);
1302 
1303    /* Mark suggested allowing for extra application specific stuff following
1304     * the background color.  That is what the check for the semi-colon is for.
1305     */
1306    while ((*bg != 0) && (*bg != ';'))
1307      {
1308 	if (p < pmax) *p++ = *bg;
1309 	bg++;
1310      }
1311    *p = 0;
1312 
1313    if (!strcmp (fg_buf, "default") || !strcmp(bg_buf, "default"))
1314      {
1315 	*fgp = *bgp = fg = bg = "default";
1316      }
1317    else
1318      {
1319 	*fgp = fg = check_color_for_digit_form (fg_buf);
1320 	*bgp = bg = check_color_for_digit_form (bg_buf);
1321      }
1322    already_parsed = 1;
1323    return 0;
1324 }
1325 
1326 static int Color_0_Modified = 0;
1327 
SLtt_set_color_object(int obj,SLtt_Char_Type attr)1328 int SLtt_set_color_object (int obj, SLtt_Char_Type attr)
1329 {
1330    Brush_Info_Type *b;
1331 
1332    if (NULL == (b = get_brush_info (obj)))
1333      return -1;
1334 
1335    b->fgbg = attr;
1336    if (obj == 0) Color_0_Modified = 1;
1337 
1338    if (_pSLtt_color_changed_hook != NULL)
1339      (*_pSLtt_color_changed_hook)();
1340 
1341    return 0;
1342 }
1343 
SLtt_get_color_object(int obj)1344 SLtt_Char_Type SLtt_get_color_object (int obj)
1345 {
1346    return get_brush_fgbg (obj);
1347 }
1348 
SLtt_add_color_attribute(int obj,SLtt_Char_Type attr)1349 int SLtt_add_color_attribute (int obj, SLtt_Char_Type attr)
1350 {
1351    Brush_Info_Type *b;
1352 
1353    if (NULL == (b = get_brush_info (obj)))
1354      return -1;
1355 
1356    b->fgbg |= (attr & ATTR_MASK);
1357 
1358    if (obj == 0) Color_0_Modified = 1;
1359    if (_pSLtt_color_changed_hook != NULL)
1360      (*_pSLtt_color_changed_hook)();
1361 
1362    return 0;
1363 }
1364 
fb_to_fgbg(SLtt_Char_Type f,SLtt_Char_Type b)1365 static SLtt_Char_Type fb_to_fgbg (SLtt_Char_Type f, SLtt_Char_Type b)
1366 {
1367    SLtt_Char_Type attr;
1368 
1369    if ((Max_Terminfo_Colors != 8)
1370 #if SLTT_HAS_TRUECOLOR_SUPPORT
1371        || Has_True_Color
1372 #endif
1373       )
1374      {
1375 	if ((f != SLSMG_COLOR_DEFAULT) && (0 == IS_TRUE_COLOR(f)))
1376 	  f %= Max_Terminfo_Colors;
1377 	if ((b != SLSMG_COLOR_DEFAULT) && (0 == IS_TRUE_COLOR(b)))
1378 	  b %= Max_Terminfo_Colors;
1379 	return MAKE_COLOR(f,b);
1380      }
1381 
1382    /* Otherwise we have 8 ansi colors.  Try to get bright versions
1383     * by using the BOLD and BLINK attributes.
1384     */
1385 
1386    attr = 0;
1387 
1388    /* Note:  If f represents default, it will have the value 0xFF */
1389    if (f != SLSMG_COLOR_DEFAULT)
1390      {
1391 	if (f & 0x8) attr = SLTT_BOLD_MASK;
1392 	f &= 0x7;
1393      }
1394 
1395    if (b != SLSMG_COLOR_DEFAULT)
1396      {
1397 	if (b & 0x8) attr |= SLTT_BLINK_MASK;
1398 	b &= 0x7;
1399      }
1400 
1401    return MAKE_COLOR(f,b) | attr;
1402 }
1403 
1404 #if SLTT_HAS_TRUECOLOR_SUPPORT
parse_hex_digit(char ch)1405 static int parse_hex_digit (char ch)
1406 {
1407    if (('0' <= ch) && (ch <= '9')) return ch - '0';
1408    if (('A' <= ch) && (ch <= 'F')) return 10 + ch - 'A';
1409    if (('a' <= ch) && (ch <= 'f')) return 10 + ch - 'a';
1410    return -1;
1411 }
1412 
parse_true_color(const char * color,SLtt_Char_Type * c)1413 static int parse_true_color (const char *color, SLtt_Char_Type *c)
1414 {
1415    SLtt_Char_Type rgb;
1416    unsigned int i;
1417    int h[6];
1418 
1419    i = 0;
1420    while (i < 6)
1421      {
1422 	if (-1 == (h[i] = parse_hex_digit (color[i])))
1423 	  return -1;
1424 	i++;
1425      }
1426    if (color[i] != 0)
1427      return -1;
1428 
1429    if (i == 3)			       /* RRGGBB */
1430      rgb = (h[0] << 20) | (h[0] << 16) | (h[1] << 12) | (h[1] << 8) | (h[2] << 4) | h[2];
1431    else if ((i == 6) && (color[i] == 0))
1432      rgb = (h[0] << 20) | (h[1] << 16) | (h[2] << 12) | (h[3] << 8) | (h[4] << 4) | h[5];
1433    else
1434      return -1;
1435 
1436    *c = rgb | TRUE_COLOR_BIT;
1437    return 0;
1438 }
1439 #endif
1440 
1441 /* This looks for colors with name form 'colorN'.  If color is of this
1442  * form, N is passed back via parameter list.
1443  */
parse_color_digit_name(SLCONST char * color,SLtt_Char_Type * f)1444 static int parse_color_digit_name (SLCONST char *color, SLtt_Char_Type *f)
1445 {
1446    unsigned int i;
1447 
1448 #if SLTT_HAS_TRUECOLOR_SUPPORT
1449    if (Has_True_Color && (color[0] == '#'))
1450      return parse_true_color (color+1, f);
1451 #endif
1452 
1453    if (strncmp (color, "color", 5))
1454      return -1;
1455 
1456    color += 5;
1457    if (*color == 0)
1458      return -1;
1459 
1460    i = 0;
1461    while (1)
1462      {
1463 	unsigned int j;
1464 	unsigned char ch;
1465 
1466 	ch = (unsigned char) *color++;
1467 	if (ch == 0)
1468 	  break;
1469 	if ((ch > '9') || (ch < '0'))
1470 	  return -1;
1471 
1472 	if (i > 0xFFFFFFFFU / 10)
1473 	  return -1;
1474 	j = (i *= 10);
1475 	i += (ch - '0');
1476 	if (i < j)
1477 	  return -1;
1478      }
1479 
1480    *f = (SLtt_Char_Type) i;
1481    return 0;
1482 }
1483 
1484 /* Here whitespace is not allowed.  That is, "blue;blink" is ok but
1485  * "blue; blink" or "blue ;blink" are not.
1486  */
parse_color_and_attributes(SLCONST char * f,char * buf,size_t buflen,SLtt_Char_Type * attrp)1487 static int parse_color_and_attributes (SLCONST char *f, char *buf, size_t buflen, SLtt_Char_Type *attrp)
1488 {
1489    SLCONST char *s;
1490    unsigned int len;
1491    SLtt_Char_Type a;
1492 
1493    *attrp = a = 0;
1494 
1495    s = strchr (f, ';');
1496    if (s == NULL) return 0;
1497 
1498    len = s - f;
1499    if (len >= buflen) len = buflen-1;
1500    strncpy (buf, f, len);
1501    buf[len] = 0;
1502 
1503    while ((*s == ';') || (*s == ' ') || (*s == '\t')) s++;
1504    f = s;
1505    while (*f)
1506      {
1507 	s = strchr (f, ';');
1508 	if (s == NULL)
1509 	  s = f + strlen (f);
1510 
1511 	len = s - f;
1512 	if (len)
1513 	  {
1514 	     if (0 == strncmp (f, "italic", 6))
1515 	       a |= SLTT_ITALIC_MASK;
1516 	     else if (0 == strncmp (f, "blink", 5))
1517 	       a |= SLTT_BLINK_MASK;
1518 	     else if (0 == strncmp (f, "underline", 9))
1519 	       a |= SLTT_ULINE_MASK;
1520 	     else if (0 == strncmp (f, "bold", 4))
1521 	       a |= SLTT_BOLD_MASK;
1522 	  }
1523 	while ((*s == ';') || (*s == ' ') || (*s == '\t')) s++;
1524 	f = s;
1525      }
1526    *attrp = a;
1527    return 1;
1528 }
1529 
make_color_fgbg(SLCONST char * fg,SLCONST char * bg,SLtt_Char_Type * fgbg)1530 static int make_color_fgbg (SLCONST char *fg, SLCONST char *bg, SLtt_Char_Type *fgbg)
1531 {
1532    SLtt_Char_Type f = INVALID_ATTR, b = INVALID_ATTR;
1533    SLCONST char *dfg, *dbg;
1534    unsigned int i;
1535    char bgbuf[16], fgbuf[16];
1536    SLtt_Char_Type fattr= 0, battr = 0;
1537 
1538    if ((fg != NULL) && (*fg == 0)) fg = NULL;
1539    if ((bg != NULL) && (*bg == 0)) bg = NULL;
1540 
1541    if ((fg == NULL) || (bg == NULL))
1542      {
1543 	if (-1 == get_default_colors (&dfg, &dbg))
1544 	  return -1;
1545 
1546 	if (fg == NULL) fg = dfg;
1547 	if (bg == NULL) bg = dbg;
1548      }
1549 
1550    if (1 == parse_color_and_attributes (fg, fgbuf, sizeof(fgbuf), &fattr))
1551      fg = fgbuf;
1552 
1553    if (-1 == parse_color_digit_name (fg, &f))
1554      {
1555 	for (i = 0; i < MAX_COLOR_NAMES; i++)
1556 	  {
1557 	     if (strcmp(fg, Color_Defs[i].name)) continue;
1558 	     f = Color_Defs[i].color;
1559 	     break;
1560 	  }
1561      }
1562 
1563    if (1 == parse_color_and_attributes (bg, bgbuf, sizeof(bgbuf), &battr))
1564      bg = bgbuf;
1565 
1566    if (-1 == parse_color_digit_name (bg, &b))
1567      {
1568 	for (i = 0; i < MAX_COLOR_NAMES; i++)
1569 	  {
1570 	     if (strcmp(bg, Color_Defs[i].name)) continue;
1571 	     b = Color_Defs[i].color;
1572 	     break;
1573 	  }
1574      }
1575 
1576    if ((f == INVALID_ATTR) || (b == INVALID_ATTR))
1577      return -1;
1578 
1579    *fgbg = fb_to_fgbg (f, b) | fattr | battr;
1580    return 0;
1581 }
1582 
tt_set_color(int obj,SLCONST char * what,SLCONST char * fg,SLCONST char * bg)1583 static int tt_set_color (int obj, SLCONST char *what, SLCONST char *fg, SLCONST char *bg)
1584 {
1585    SLtt_Char_Type fgbg;
1586 
1587    (void) what;
1588 
1589    if (-1 == make_color_fgbg (fg, bg, &fgbg))
1590      return -1;
1591 
1592    return SLtt_set_color_object (obj, fgbg);
1593 }
1594 
SLtt_set_color(int obj,SLFUTURE_CONST char * what,SLFUTURE_CONST char * fg,SLFUTURE_CONST char * bg)1595 int SLtt_set_color (int obj, SLFUTURE_CONST char *what, SLFUTURE_CONST char *fg, SLFUTURE_CONST char *bg)
1596 {
1597    return tt_set_color (obj, what, fg, bg);
1598 }
1599 
SLtt_set_color_fgbg(int obj,SLtt_Char_Type f,SLtt_Char_Type b)1600 int SLtt_set_color_fgbg (int obj, SLtt_Char_Type f, SLtt_Char_Type b)
1601 {
1602    return SLtt_set_color_object (obj, fb_to_fgbg (f, b));
1603 }
1604 
SLtt_set_alt_char_set(int i)1605 void SLtt_set_alt_char_set (int i)
1606 {
1607    static int last_i;
1608    if (SLtt_Has_Alt_Charset == 0) return;
1609 
1610    i = (i != 0);
1611 
1612    if (i == last_i) return;
1613    tt_write_string (i ? Start_Alt_Chars_Str : End_Alt_Chars_Str );
1614    last_i = i;
1615 }
1616 
1617 #if SLTT_HAS_TRUECOLOR_SUPPORT
1618 # if defined(__GNUC__)
1619 #  pragma GCC diagnostic ignored "-Wformat-nonliteral"
1620 # endif
write_truecolor(const char * fmt,SLtt_Char_Type c)1621 static void write_truecolor (const char *fmt, SLtt_Char_Type c)
1622 {
1623    char tmpbuf[32];
1624    int r, g, b;
1625 
1626    r = (int)((c>>16) & 0xFF);
1627    g = (int)((c>>8) & 0xFF);
1628    b = (int)(c & 0xFF);
1629 
1630    sprintf (tmpbuf, fmt, r, g, b);
1631    tt_write_string (tmpbuf);
1632 }
1633 # if defined(__GNUC__)
1634 #  pragma GCC diagnostic warning "-Wformat-nonliteral"
1635 # endif
1636 #endif				       /* SLTT_HAS_TRUECOLOR_SUPPORT */
1637 
write_attributes(SLtt_Char_Type fgbg)1638 static void write_attributes (SLtt_Char_Type fgbg)
1639 {
1640    int unknown_attributes;
1641 
1642    if (Worthless_Highlight) return;
1643    if (fgbg == Current_Fgbg) return;
1644 
1645    unknown_attributes = 0;
1646 
1647    /* Before spitting out colors, fix attributes */
1648    if ((fgbg & ATTR_MASK) != (Current_Fgbg & ATTR_MASK))
1649      {
1650 	if (Current_Fgbg & ATTR_MASK)
1651 	  {
1652 	     tt_write_string(Norm_Vid_Str);
1653 	     /* In case normal video turns off ALL attributes: */
1654 	     if (fgbg & SLTT_ALTC_MASK)
1655 	       Current_Fgbg &= ~SLTT_ALTC_MASK;
1656 	     SLtt_set_alt_char_set (0);
1657 	  }
1658 
1659 	if ((fgbg & SLTT_ALTC_MASK)
1660 	    != (Current_Fgbg & SLTT_ALTC_MASK))
1661 	  {
1662 	     SLtt_set_alt_char_set ((int) (fgbg & SLTT_ALTC_MASK));
1663 	  }
1664 
1665 	if (fgbg & SLTT_ULINE_MASK) tt_write_string (UnderLine_Vid_Str);
1666 	if (fgbg & SLTT_BOLD_MASK) SLtt_bold_video ();
1667 	if (fgbg & SLTT_REV_MASK) tt_write_string (Rev_Vid_Str);
1668 	if (fgbg & SLTT_ITALIC_MASK) tt_write_string (Italic_Vid_Str);
1669 	if (fgbg & SLTT_BLINK_MASK)
1670 	  {
1671 	     /* Someday Linux will have a blink mode that set high intensity
1672 	      * background.  Lets be prepared.
1673 	      */
1674 	     if (SLtt_Blink_Mode) tt_write_string (Blink_Vid_Str);
1675 	  }
1676 	unknown_attributes = 1;
1677      }
1678 
1679    if (SLtt_Use_Ansi_Colors)
1680      {
1681 	int bg0, fg0;
1682 	fg0 = (int) GET_FG(fgbg);
1683 	bg0 = (int) GET_BG(fgbg);
1684 
1685 	if (unknown_attributes
1686 	    || (fg0 != (int)GET_FG(Current_Fgbg)))
1687 	  {
1688 	     if (fg0 == SLSMG_COLOR_DEFAULT)
1689 	       tt_write_string (Default_Color_Fg_Str);
1690 #if SLTT_HAS_TRUECOLOR_SUPPORT
1691 	     else if (IS_TRUE_COLOR(fg0))
1692 	       write_truecolor (Color_RGB_Fg_Str, fg0);
1693 #endif
1694 	     else
1695 	       tt_printf (Color_Fg_Str, COLOR_ARG(fg0, Is_Fg_BGR), 0);
1696 	  }
1697 
1698 	if (unknown_attributes
1699 	    || (bg0 != (int)GET_BG(Current_Fgbg)))
1700 	  {
1701 	     if (bg0 == SLSMG_COLOR_DEFAULT)
1702 	       tt_write_string (Default_Color_Bg_Str);
1703 #if SLTT_HAS_TRUECOLOR_SUPPORT
1704 	     else if (IS_TRUE_COLOR(bg0))
1705 	       write_truecolor (Color_RGB_Bg_Str, bg0);
1706 #endif
1707 	     else
1708 	       tt_printf (Color_Bg_Str, COLOR_ARG(bg0, Is_Bg_BGR), 0);
1709 	  }
1710      }
1711 
1712    Current_Fgbg = fgbg;
1713 }
1714 
1715 static int Video_Initialized;
1716 
SLtt_reverse_video(int color)1717 void SLtt_reverse_video (int color)
1718 {
1719    SLtt_Char_Type fgbg;
1720 
1721    if (Worthless_Highlight) return;
1722 
1723    if (Video_Initialized == 0)
1724      {
1725 	if (color == JNORMAL_COLOR)
1726 	  {
1727 	     tt_write_string (Norm_Vid_Str);
1728 	  }
1729 	else tt_write_string (Rev_Vid_Str);
1730 	Current_Fgbg = INVALID_ATTR;
1731 	return;
1732      }
1733 
1734   fgbg = get_brush_attr (color);
1735 
1736    if (fgbg == Current_Fgbg) return;
1737    write_attributes (fgbg);
1738 }
1739 
SLtt_normal_video(void)1740 void SLtt_normal_video (void)
1741 {
1742    SLtt_reverse_video(JNORMAL_COLOR);
1743 }
1744 
SLtt_narrow_width(void)1745 void SLtt_narrow_width (void)
1746 {
1747    tt_write ("\033[?3l", 5);
1748 }
1749 
SLtt_wide_width(void)1750 void SLtt_wide_width (void)
1751 {
1752    tt_write ("\033[?3h", 5);
1753 }
1754 
1755 /* Highest bit represents the character set. */
1756 #define COLOR_OF(a) ((a)->color & SLSMG_COLOR_MASK)
1757 
bce_colors_eq(SLsmg_Color_Type ca,SLsmg_Color_Type cb,int just_bg)1758 static int bce_colors_eq (SLsmg_Color_Type ca, SLsmg_Color_Type cb, int just_bg)
1759 {
1760    Brush_Info_Type *ba, *bb;
1761 
1762    if (ca == cb)
1763      return 1;
1764 
1765    ba = get_brush_info (ca);
1766    bb = get_brush_info (cb);
1767 
1768    if (SLtt_Use_Ansi_Colors == 0)
1769      return ba->mono == bb->mono;
1770 
1771    if (Bce_Color_Offset)
1772      {
1773 	/* If either are color 0, then we do not know what that means since the
1774 	 * terminal does not support BCE
1775 	 */
1776 	if ((ca == 0) || (cb == 0))
1777 	  return 0;
1778 	ba = get_brush_info (ca-1);
1779 	bb = get_brush_info (cb-1);
1780      }
1781 
1782    if (ba->fgbg == bb->fgbg)
1783      return 1;
1784    if (just_bg)
1785      {
1786 	return (ba->mono == bb->mono)
1787 	  && GET_BG(ba->fgbg) == GET_BG(bb->fgbg);
1788      }
1789    return 0;
1790 }
1791 
1792 /* The whole point of this routine is to prevent writing to the last column
1793  * and last row on terminals with automatic margins.
1794  */
write_string_with_care(SLCONST char * str)1795 static void write_string_with_care (SLCONST char *str)
1796 {
1797    SLstrlen_Type len;
1798 
1799    if (str == NULL) return;
1800 
1801    len = strlen (str);
1802    if (Automatic_Margins && (Cursor_r + 1 == SLtt_Screen_Rows))
1803      {
1804 	if (_pSLtt_UTF8_Mode == 0)
1805 	 {
1806 	   if (len + (unsigned int) Cursor_c >= (unsigned int) SLtt_Screen_Cols)
1807 	     {
1808 	        /* For now, just do not write there.  Later, something more
1809 	        * sophisticated will be implemented.
1810 	        */
1811 	        if (SLtt_Screen_Cols > Cursor_c)
1812 	          len = SLtt_Screen_Cols - Cursor_c - 1;
1813 	        else
1814 	          len = 0;
1815 	     }
1816 	 }
1817        else
1818 	 {
1819 	    SLstrlen_Type nchars = SLutf8_strlen((SLuchar_Type *)str, 1);
1820 	    if (nchars + (unsigned int) Cursor_c >= (unsigned int) SLtt_Screen_Cols)
1821 	     {
1822 	       if (SLtt_Screen_Cols > Cursor_c)
1823 	         {
1824 		    char *p;
1825 		    nchars = (SLstrlen_Type)(SLtt_Screen_Cols - Cursor_c - 1);
1826 		    p = (char *)SLutf8_skip_chars((SLuchar_Type *) str, (SLuchar_Type *)(str + len), nchars, NULL, 1);
1827 		    len = p - str;
1828 		 }
1829 	       else
1830 		  len = 0;
1831 	     }
1832 	 }
1833      }
1834    tt_write (str, len);
1835 }
1836 
send_attr_str(SLsmg_Char_Type * s,SLsmg_Char_Type * smax)1837 static void send_attr_str (SLsmg_Char_Type *s, SLsmg_Char_Type *smax)
1838 {
1839    unsigned char out[1+SLUTF8_MAX_MBLEN*SLSMG_MAX_CHARS_PER_CELL*SLTT_MAX_SCREEN_COLS];
1840    unsigned char *p, *pmax;
1841    register SLtt_Char_Type attr;
1842    SLsmg_Color_Type color, last_color = (SLsmg_Color_Type)-1;
1843    int dcursor_c;
1844 
1845    p = out;
1846    pmax = p + (sizeof (out)-1);
1847 
1848    if ((Cursor_c == 0)
1849        && (Use_Relative_Cursor_Addressing)
1850        && (Cursor_r < SLTT_MAX_SCREEN_ROWS))
1851      {
1852 	if (s < smax)
1853 	  Display_Start_Chars[Cursor_r] = *s;
1854 	else
1855 	  Display_Start_Chars[Cursor_r].nchars = 0;
1856      }
1857 
1858    dcursor_c = 0;
1859    while (s < smax)
1860      {
1861 	SLwchar_Type wch;
1862 	unsigned int nchars;
1863 
1864 	if (0 == (nchars = s->nchars))
1865 	  {
1866 	     /* 2nd element of a char that occupies two columns */
1867 	     s++;
1868 	     if (_pSLtt_UTF8_Mode == 0)
1869 	       *p++ = ' ';
1870 	     dcursor_c++;
1871 	     continue;
1872 	  }
1873 
1874 	color = s->color;
1875 
1876 #if SLTT_HAS_NON_BCE_SUPPORT
1877 	if (Bce_Color_Offset
1878 	    && (color >= Bce_Color_Offset))
1879 	  color -= Bce_Color_Offset;
1880 #endif
1881 
1882 	wch = s->wchars[0];
1883 
1884 	if (color != last_color)
1885 	  {
1886 	     attr = get_brush_attr (color);
1887 
1888 	     if (color & SLSMG_ACS_MASK) /* alternate char set */
1889 	       {
1890 		  if (SLtt_Use_Blink_For_ACS)
1891 		    {
1892 		       if (SLtt_Blink_Mode) attr |= SLTT_BLINK_MASK;
1893 		    }
1894 		  else attr |= SLTT_ALTC_MASK;
1895 	       }
1896 
1897 	     if (attr != Current_Fgbg)
1898 	       {
1899 		  if ((wch != ' ')
1900 		      || (nchars > 1)
1901 		      /* it is a space so only consider it different if it
1902 		       * has different attributes.
1903 		       */
1904 		      || (attr != Current_Fgbg)
1905 		      )
1906 		    {
1907 		       if (p != out)
1908 			 {
1909 			    *p = 0;
1910 			    write_string_with_care ((char *) out);
1911 			    p = out;
1912 			    Cursor_c += dcursor_c;
1913 			    dcursor_c = 0;
1914 			 }
1915 		       write_attributes (attr);
1916 		       last_color = color;
1917 		    }
1918 	       }
1919 	  }
1920 
1921 	if ((wch < 0x80) && (nchars == 1))
1922 	  *p++ = (unsigned char) wch;
1923 	else if (_pSLtt_UTF8_Mode == 0)
1924 	  {
1925 	     if (wch > 255)
1926 	       wch = '?';
1927 	     else if (wch < (SLwchar_Type)SLsmg_Display_Eight_Bit)
1928 	       wch = '?';
1929 	     *p++ = (unsigned char) wch;
1930 	  }
1931 	else
1932 	  {
1933 	     unsigned int i;
1934 	     for (i = 0; i < nchars; i++)
1935 	       {
1936 		  if (NULL == (p = SLutf8_encode (s->wchars[i], p, pmax-p)))
1937 		    {
1938 		       fprintf (stderr, "*** send_attr_str: buffer too small\n");
1939 		       return;
1940 		    }
1941 	       }
1942 	  }
1943 	dcursor_c++;
1944 	s++;
1945      }
1946    *p = 0;
1947    if (p != out) write_string_with_care ((char *) out);
1948    Cursor_c += dcursor_c;
1949 }
1950 
forward_cursor(unsigned int n,int row)1951 static void forward_cursor (unsigned int n, int row)
1952 {
1953    char buf [1024];
1954 
1955    /* if (Current_Fgbg & ~0xFF) */
1956    /*   { */
1957    /* 	unsigned int num = 0; */
1958    /* 	while (num < n) */
1959    /* 	  { */
1960    /* 	     write_string_with_care (" "); */
1961    /* 	     num++; */
1962    /* 	  } */
1963    /* 	Cursor_c += n; */
1964    /* 	return; */
1965    /*   } */
1966 
1967    if (n <= 4)
1968      {
1969 	SLtt_normal_video ();
1970 #if 0
1971 	if (n >= sizeof (buf))
1972 	  n = sizeof (buf) - 1;
1973 #endif
1974 	SLMEMSET (buf, ' ', n);
1975 	buf[n] = 0;
1976 	write_string_with_care (buf);
1977 	Cursor_c += n;
1978      }
1979    else if (Curs_RightN_Str != NULL)
1980      {
1981 	Cursor_c += n;
1982 	n = tt_sprintf(buf, sizeof (buf), Curs_RightN_Str, (int) n, 0);
1983 	tt_write(buf, n);
1984      }
1985    else SLtt_goto_rc (row, (int) (Cursor_c + n));
1986 }
1987 
1988 /* FIXME!!  If the terminal does not support color, then this route has
1989  * problems of color object 0 has been assigned some monochrome attribute
1990  * such as reverse video.  In such a case, space_char=' ' is not a simple
1991  * space character as is assumed below.
1992  */
1993 
1994 #define COLORS_EQ(ca,cb) (((ca) == (cb)) || bce_colors_eq((ca), (cb), 0))
1995 #define BG_COLORS_EQ(ca,cb) (((ca) == (cb)) || (bce_colors_eq((ca),(cb),1)))
1996 
1997 #define COLORS_OF_EQ(a,b) COLORS_EQ(COLOR_OF(a),COLOR_OF(b))
1998 
1999 #define CHARSET(a) ((a)->color&SLSMG_ACS_MASK)
2000 #define CHAR_EQS(a, b) (((a)->nchars==(b)->nchars) \
2001 			   && (((a)->nchars == 0) \
2002 				  || ((((a)->wchars[0]==(b)->wchars[0]) \
2003 					 && (0 == memcmp((a)->wchars, (b)->wchars, (a)->nchars*sizeof(SLwchar_Type)))) \
2004 					 && (COLORS_OF_EQ(a,b)) \
2005 					 && (CHARSET(a)==CHARSET(b)))))
2006 
2007 #if 0
2008 # define CHAR_EQS_SPACE(a) \
2009    (((a)->wchars[0]==' ') && ((a)->color==0) && ((a)->nchars==1))
2010 #else
2011 # define CHAR_EQS_SPACE(a) \
2012    (((a)->wchars[0]==' ') && ((a)->nchars==1) \
2013      && BG_COLORS_EQ(COLOR_OF(a),Bce_Color_Offset))
2014 #endif
2015 
SLtt_smart_puts(SLsmg_Char_Type * neww,SLsmg_Char_Type * oldd,int len,int row)2016 void SLtt_smart_puts(SLsmg_Char_Type *neww, SLsmg_Char_Type *oldd, int len, int row)
2017 {
2018    register SLsmg_Char_Type *p, *q, *qmax, *pmax, *buf;
2019    SLsmg_Char_Type buffer[SLTT_MAX_SCREEN_COLS+1];
2020    SLsmg_Char_Type *space_match, *last_buffered_match;
2021 #ifdef HP_GLITCH_CODE
2022    int handle_hp_glitch = 0;
2023 #endif
2024    SLsmg_Char_Type *space_char;
2025    SLsmg_Char_Type space_char_buf;
2026 
2027 #define SLTT_USE_INSERT_HACK 1
2028 #if SLTT_USE_INSERT_HACK
2029    SLsmg_Char_Type *insert_hack_prev = NULL;
2030    SLsmg_Char_Type *insert_hack_char = NULL;
2031 
2032    if ((row + 1 == SLtt_Screen_Rows)
2033        && (len == SLtt_Screen_Cols)
2034        && (len > 1)
2035        && (SLtt_Term_Cannot_Insert == 0)
2036        && Automatic_Margins)
2037      {
2038 	SLsmg_Char_Type *a, *b;
2039 	insert_hack_char = &neww[len-1];
2040 
2041 	a = oldd+(len-1);
2042 	b = neww+(len-1);
2043 
2044 	if (CHAR_EQS(a,b))
2045 	  insert_hack_char = NULL;
2046 	else
2047 	  insert_hack_prev = &neww[len-2];
2048      }
2049 #endif
2050 
2051    memset ((char *) &space_char_buf, 0, sizeof (SLsmg_Char_Type));
2052    space_char = &space_char_buf;
2053    space_char->nchars = 1;
2054    space_char->wchars[0] = ' ';
2055 
2056    if (len > SLTT_MAX_SCREEN_COLS)
2057      len = SLTT_MAX_SCREEN_COLS;
2058 
2059    q = oldd; p = neww;
2060    qmax = oldd + len;
2061    pmax = p + len;
2062 
2063    /* Find out where to begin --- while they match, we are ok */
2064    while (1)
2065      {
2066 	if (q == qmax) return;
2067 #if SLANG_HAS_KANJI_SUPPORT
2068 # undef SLANG_HAS_KANJI_SUPPORT
2069 # define SLANG_HAS_KANJI_SUPPORT 0
2070 #endif
2071 #if SLANG_HAS_KANJI_SUPPORT
2072 	if (*p & 0x80)
2073 	  { /* new is kanji */
2074 	     if ((*q & 0x80) && ((q + 1) < qmax))
2075 	       { /* old is also kanji */
2076 		  if (((0xFF & *q) != (0xFF & *p))
2077 		      || ((0xFF & q[1]) != (0xFF & p[1])))
2078 		    break; /* both kanji, but not match */
2079 
2080 		  else
2081 		    { /* kanji match ! */
2082 		       if (!COLORS_OF_EQ(*q, *p)) break;
2083 		       q++; p++;
2084 		       if (!COLORS_OF_EQ(*q, *p)) break;
2085 		       /* really match! */
2086 		       q++; p++;
2087 		       continue;
2088 		    }
2089 	       }
2090 	     else break; /* old is not kanji */
2091 	  }
2092 	else
2093 	  { /* new is not kanji */
2094 	     if (*q & 0x80) break; /* old is kanji */
2095 	  }
2096 #endif
2097 	if (!CHAR_EQS(q, p)) break;
2098 	q++; p++;
2099      }
2100 
2101 #ifdef HP_GLITCH_CODE
2102    if (Has_HP_Glitch)
2103      {
2104 	SLsmg_Char_Type *qq = q;
2105 
2106 	SLtt_goto_rc (row, (int) (p - neww));
2107 
2108 	while (qq < qmax)
2109 	  {
2110 	     if (qq->color)
2111 	       {
2112 		  SLtt_normal_video ();
2113 		  SLtt_del_eol ();
2114 		  qmax = q;
2115 		  handle_hp_glitch = 1;
2116 		  break;
2117 	       }
2118 	     qq++;
2119 	  }
2120      }
2121 #endif
2122    /* Find where the last non-blank character on old/new screen is */
2123 
2124    /* if (CHAR_EQS_SPACE(pmax-1)) */
2125    if (((pmax-1)->wchars[0]==' ') && ((pmax-1)->nchars==1))
2126      {
2127 	/* If we get here, then we can erase to the end of the line to create
2128 	 * the final space.  However, this will only work _if_ erasing will
2129 	 * get us the correct color.  If the terminal supports BCE, then this
2130 	 * is easy.  If it does not, then we can only perform this operation
2131 	 * if the color is known via something like COLORFGBG.  For now,
2132 	 * I just will not perform the optimization for such terminals.
2133 	 */
2134 	if (Can_Background_Color_Erase
2135 	    && SLtt_Use_Ansi_Colors)
2136 	  {
2137 	     SLtt_Char_Type fgbg;
2138 
2139 	     fgbg = get_brush_attr (COLOR_OF(pmax-1));
2140 	     if (0 == (fgbg & ATTR_MASK))
2141 	       space_char = pmax - 1;
2142 	  }
2143 
2144 	while (pmax > p)
2145 	  {
2146 	     pmax--;
2147 	     if (!CHAR_EQS(pmax, space_char))
2148 	       {
2149 		  pmax++;
2150 		  break;
2151 	       }
2152 	  }
2153      }
2154 
2155    while (qmax > q)
2156      {
2157 	qmax--;
2158 	if (!CHAR_EQS(qmax, space_char))
2159 	  {
2160 	     qmax++;
2161 	     break;
2162 	  }
2163      }
2164 
2165    last_buffered_match = buf = buffer;		       /* buffer is empty */
2166 
2167 #ifdef HP_GLITCH_CODE
2168    if (handle_hp_glitch)
2169      {
2170 	while (p < pmax)
2171 	  {
2172 	     *buf++ = *p++;
2173 	  }
2174      }
2175 #endif
2176 
2177 #ifdef HP_GLITCH_CODE
2178    if (Has_HP_Glitch == 0)
2179      {
2180 #endif
2181 	/* Try use use erase to bol if possible */
2182 	if ((Del_Bol_Str != NULL) && (CHAR_EQS_SPACE(neww)))
2183 	  {
2184 	     SLsmg_Char_Type *p1;
2185 	     SLsmg_Color_Type blank_color = 0;
2186 
2187 	     p1 = neww;
2188 	     if ((Can_Background_Color_Erase)
2189 		 && SLtt_Use_Ansi_Colors)
2190 	       {
2191 		  SLsmg_Char_Type *blank = p1;
2192 		  blank_color = COLOR_OF(blank);
2193 		  while ((p1 < pmax) && (CHAR_EQS (p1, blank)))
2194 		    p1++;
2195 	       }
2196 	     else
2197 	       {
2198 		  /* black+white attributes do not support bce */
2199 		  while ((p1 < pmax) && (CHAR_EQS_SPACE (p1)))
2200 		    p1++;
2201 	       }
2202 
2203 	     /* Is this optimization worth it?  Assume Del_Bol_Str is ESC [ 1 K
2204 	      * It costs 4 chars + the space needed to properly position the
2205 	      * cursor, e.g., ESC [ 10;10H. So, it costs at least 13 characters.
2206 	      */
2207 	     if ((p1 > neww + 13)
2208 		 && (p1 >= p)
2209 		 /* Avoid erasing from the end of the line */
2210 		 && ((p1 != pmax) || (pmax < neww + len)))
2211 	       {
2212 		  int ofs = (int) (p1 - neww);
2213 		  q = oldd + ofs;
2214 		  p = p1;
2215 		  SLtt_goto_rc (row, ofs - 1);
2216 		  SLtt_reverse_video (blank_color);
2217 		  tt_write_string (Del_Bol_Str);
2218 		  tt_write (" ", 1);
2219 		  Cursor_c += 1;
2220 	       }
2221 	     else
2222 	       SLtt_goto_rc (row, (int) (p - neww));
2223 	  }
2224 	else
2225 	  SLtt_goto_rc (row, (int) (p - neww));
2226 #ifdef HP_GLITCH_CODE
2227      }
2228 #endif
2229 
2230    /* loop using overwrite then skip algorithm until done */
2231    while (1)
2232      {
2233 	/* while they do not match and we do not hit a space, buffer them up */
2234 	unsigned int n_spaces = 0;
2235 	while (p < pmax)
2236 	  {
2237 	     if (CHAR_EQS_SPACE(q) && CHAR_EQS_SPACE(p))
2238 	       {
2239 		  /* If *q is not a space, we would have to overwrite it.
2240 		   * However, if *q is a space, then while *p is also one,
2241 		   * we only need to skip over the blank field.
2242 		   */
2243 		  space_match = p;
2244 		  p++; q++;
2245 		  while ((p < pmax)
2246 			 && CHAR_EQS_SPACE(q)
2247 			 && CHAR_EQS_SPACE(p))
2248 		    {
2249 		       p++;
2250 		       q++;
2251 		    }
2252 		  n_spaces = (unsigned int) (p - space_match);
2253 		  break;
2254 	       }
2255 #if SLANG_HAS_KANJI_SUPPORT
2256 	     if ((*p & 0x80) && ((p + 1) < pmax))
2257 	       { /* new is kanji */
2258 		  if (*q & 0x80)
2259 		    { /* old is also kanji */
2260 		       if (((0xFF & *q) != (0xFF & *p))
2261 			   || ((0xFF & q[1]) != (0xFF & p[1])))
2262 			 {
2263 			    /* both kanji, but not match */
2264 			    *buf++ = *p++;
2265 			    *buf++ = *p++;
2266 			    q += 2;
2267 			    continue;
2268 			 }
2269 		       else
2270 			 { /* kanji match ? */
2271 			    if (!COLORS_OF_EQ(*q, *p) || !COLORS_OF_EQ(*(q+1), *(p+1)))
2272 			      {
2273 				 /* code is match, but color is diff */
2274 				 *buf++ = *p++;
2275 				 *buf++ = *p++;
2276 				 q += 2;
2277 				 continue;
2278 			      }
2279 			    /* really match ! */
2280 			    break;
2281 			 }
2282 		    }
2283  		  else
2284 		    { /* old is not kanji */
2285 		       *buf++ = *p++;
2286 		       *buf++ = *p++;
2287 		       q += 2;
2288 		       continue;
2289 		    }
2290 	       }
2291 	     else
2292 	       { /* new is not kanji */
2293  		  if (*q & 0x80)
2294 		    { /* old is kanji */
2295 		       *buf++ = *p++;
2296 		       q++;
2297 		       continue;
2298 		    }
2299 	       }
2300 #endif
2301 
2302 	     if (CHAR_EQS(q, p))
2303 	       {
2304 		  /* Could be the second half of a double width character */
2305 		  if (p->nchars || q->nchars)
2306 		    break;
2307 	       }
2308 	     *buf++ = *p++;
2309 	     q++;
2310 	  }
2311 
2312 	/* At this point, the buffer contains characters that do not match */
2313 	if (buf != buffer) send_attr_str (buffer, buf);
2314 	buf = buffer;
2315 
2316 	if (n_spaces
2317 	    && ((p < pmax) 	       /* erase to eol will achieve this effect*/
2318 		|| (!CHAR_EQS_SPACE(space_char))))/* unless space_char is not a simple space */
2319 	  {
2320 	     forward_cursor (n_spaces, row);
2321 	  }
2322 	/* Now we overwrote what we could and cursor is placed at position
2323 	 * of a possible match of new and old.  If this is the case, skip
2324 	 * some more.
2325 	 */
2326 
2327 	/* Note that from here on, the buffer will contain matched characters */
2328 #if !SLANG_HAS_KANJI_SUPPORT
2329 	while ((p < pmax) && CHAR_EQS(p, q))
2330 	  {
2331 	     *buf++ = *p++;
2332 	     q++;
2333 	  }
2334 #else
2335 	/* Kanji */
2336 	while (p < pmax)
2337 	  {
2338 	     if ((*p & 0x80) && ((p + 1) < pmax))
2339 	       { /* new is kanji */
2340 		  if (*q & 0x80)
2341 		    { /* old is also kanji */
2342 		       if (((0xFF & *q) == (0xFF & *p))
2343 			   && ((0xFF & q[1]) == (0xFF & p[1])))
2344 			 {
2345 			    /* kanji match ? */
2346 			    if (!COLORS_OF_EQ(*q, *p)
2347 				|| !COLORS_OF_EQ(q[1], p[1]))
2348 			      break;
2349 
2350 			    *buf++ = *p++;
2351 			    q++;
2352 			    if (p >= pmax)
2353 			      {
2354 				 *buf++ = 32;
2355 				 p++;
2356 				 break;
2357 			      }
2358 			    else
2359 			      {
2360 				 *buf++ = *p++;
2361 				 q++;
2362 				 continue;
2363 			      }
2364 			 }
2365 		       else break; /* both kanji, but not match */
2366 		    }
2367 		  else break; /* old is not kanji */
2368 	       }
2369 	     else
2370 	       {  /* new is not kanji */
2371 		  if (*q & 0x80) break; /* old is kanji */
2372 		  if (!CHAR_EQS(*q, *p)) break;
2373 		  *buf++ = *p++;
2374 		  q++;
2375 	       }
2376 	  }
2377 #endif
2378 	last_buffered_match = buf;
2379 	if (p >= pmax) break;
2380 
2381 	/* jump to new position is it is greater than 5 otherwise
2382 	 * let it sit in the buffer and output it later.
2383 	 */
2384 	if ((int) (buf - buffer) >= 5)
2385 	  {
2386 	     forward_cursor ((unsigned int) (buf - buffer), row);
2387 	     last_buffered_match = buf = buffer;
2388 	  }
2389      }
2390 
2391    /* At this point we have reached the end of the new string with the
2392     * exception of space_chars hanging off the end of it, but we may not have
2393     * reached the end of the old string if they did not match.
2394     */
2395 
2396    /* Here the buffer will consist only of characters that have matched */
2397    if (buf != buffer)
2398      {
2399 	if (q < qmax)
2400 	  {
2401 	     if ((buf == last_buffered_match)
2402 		 && ((int) (buf - buffer) >= 5))
2403 	       {
2404 		  forward_cursor ((unsigned int) (buf - buffer), row);
2405 	       }
2406 	     else
2407 	       {
2408 		  send_attr_str (buffer, buf);
2409 	       }
2410 	  }
2411      }
2412 
2413    if (q < qmax)
2414      {
2415 	SLtt_reverse_video (COLOR_OF(space_char));
2416 	del_eol ();
2417      }
2418 
2419 #if SLTT_USE_INSERT_HACK
2420    else if (insert_hack_char != NULL)
2421      {
2422 	SLtt_goto_rc (SLtt_Screen_Rows-1, SLtt_Screen_Cols-2);
2423 	send_attr_str (insert_hack_char, insert_hack_char+1);
2424 	SLtt_goto_rc (SLtt_Screen_Rows-1, SLtt_Screen_Cols-2);
2425 	SLtt_begin_insert ();
2426 	send_attr_str (insert_hack_prev, insert_hack_prev+1);
2427 	SLtt_end_insert ();
2428      }
2429 #endif
2430 
2431    if (Cursor_c >= SLtt_Screen_Cols)
2432      {
2433 	if (Use_Relative_Cursor_Addressing)
2434 	  {
2435 	     /* Where is the cursor?  If we have automatic margins, then the
2436 	      * cursor _may_ be at the beginning of the next row.  But, it may
2437 	      * not if the terminal permits writing to the LR corner without
2438 	      * scrolling.  In relative-cursor-addressing mode, we do not know.
2439 	      *
2440 	      * The trick is to force the cursor to the next line if
2441 	      * it is at the end of the current line, or if it is at
2442 	      * the beginning of the next do nothing to change that line.
2443 	      * The only thing I can think of is to write the character that
2444 	      * is already there.
2445 	      */
2446 	     Cursor_c = 0;
2447 	     Cursor_r++;
2448 	     if (Cursor_r < SLTT_MAX_SCREEN_ROWS)
2449 	       {
2450 		  SLsmg_Char_Type *c = Display_Start_Chars + Cursor_r;
2451 		  if (c->nchars)
2452 		    send_attr_str (c, c+1);
2453 		  else
2454 		    tt_write (" ", 1);
2455 	       }
2456 	     else
2457 	       tt_write (" ", 1);
2458 	     tt_write ("\r", 1);
2459 	     Cursor_c =0;
2460 	  }
2461 	else if (Automatic_Margins)
2462 	  Cursor_Set = 0;
2463      }
2464 }
2465 
get_color_info(void)2466 static void get_color_info (void)
2467 {
2468    SLCONST char *fg, *bg;
2469    char *ct;
2470 
2471    ct = getenv ("COLORTERM");
2472 
2473    if (ct != NULL)
2474      {
2475 	/* Allow easy mechanism to override inadequate termcap/terminfo files. */
2476 	SLtt_Use_Ansi_Colors = 1;
2477 #if SLTT_HAS_TRUECOLOR_SUPPORT
2478 	if ((0 == strcmp(ct, "truecolor")) || (0 == strcmp(ct, "24bit")))
2479 	  Has_True_Color = 1;
2480 #endif
2481      }
2482 
2483    if (SLtt_Use_Ansi_Colors)
2484      Is_Color_Terminal = 1;
2485 
2486 #if SLTT_HAS_NON_BCE_SUPPORT
2487    if (Can_Background_Color_Erase == 0)
2488      Can_Background_Color_Erase = (NULL != getenv ("COLORTERM_BCE"));
2489 #endif
2490 
2491    /* terminfo does not support truecolor */
2492 
2493    if (-1 == get_default_colors (&fg, &bg))
2494      return;
2495 
2496    /* Check to see if application has already set them. */
2497    if (Color_0_Modified)
2498      return;
2499 
2500    tt_set_color (0, NULL, fg, bg);
2501    tt_set_color (1, NULL, bg, fg);
2502 }
2503 
2504 /* termcap stuff */
2505 
2506 #ifdef __unix__
2507 
2508 static int Termcap_Initialized = 0;
2509 
2510 /* #define USE_TERMCAP 1 */
2511 #ifdef USE_TERMCAP
2512 /* Termcap based system */
2513 static char Termcap_Buf[4096];
2514 /* static char Termcap_String_Buf[4096]; */
2515 /* static char *Termcap_String_Ptr; */
2516 extern char *tgetstr(char *, char **);
2517 extern int tgetent(char *, char *);
2518 extern int tgetnum(char *);
2519 extern int tgetflag(char *);
2520 #else
2521 /* Terminfo */
2522 static SLterminfo_Type *Terminfo;
2523 #endif
2524 
2525 #define TGETFLAG(x) (tt_tgetflag(x) > 0)
2526 
fixup_tgetstr(char * what)2527 static char *fixup_tgetstr (char *what)
2528 {
2529    register char *w, *w1;
2530    char *wsave;
2531 
2532    if (what == NULL)
2533      return NULL;
2534 
2535    /* Check for AIX brain-damage */
2536    if (*what == '@')
2537      return NULL;
2538 
2539    /* lose pad info --- with today's technology, term is a loser if
2540     it is really needed */
2541    while ((*what == '.') ||
2542 	  ((*what >= '0') && (*what <= '9'))) what++;
2543    if (*what == '*') what++;
2544 
2545    /* lose terminfo padding--- looks like $<...> */
2546    w = what;
2547    while (*w) if ((*w++ == '$') && (*w == '<'))
2548      {
2549 	w1 = w - 1;
2550 	while (*w && (*w != '>')) w++;
2551 	if (*w == 0) break;
2552 	w++;
2553 	wsave = w1;
2554 	while ((*w1++ = *w++) != 0)
2555 	  ;
2556 	w = wsave;
2557      }
2558 
2559    if (*what == 0) what = NULL;
2560    return what;
2561 }
2562 
tt_tgetstr(SLCONST char * cap)2563 static char *tt_tgetstr (SLCONST char *cap)
2564 {
2565    char *s;
2566 #ifdef USE_TERMCAP
2567    char area_buf[4096];
2568    char *area;
2569 #endif
2570    if (Termcap_Initialized == 0)
2571      return NULL;
2572 
2573 #ifdef USE_TERMCAP
2574    /* tmp_area = &Termcap_String_Buf; */
2575    area = area_buf;
2576    s = tgetstr (cap, &area);
2577    if (area > area_buf + sizeof(area_buf))
2578      {
2579 	SLang_exit_error ("\
2580 The termcap tgetstr appears to have overflowed a buffer.\n\
2581 The integrity of this program has been violated.\n");
2582      }
2583 #else
2584    s = _pSLtt_tigetstr (Terminfo, cap);
2585 #endif
2586 
2587    /* Do not strip pad info for alternate character set.  I need to make
2588     * this more general.
2589     */
2590    /* FIXME: Priority=low; */
2591    if (0 != strcmp (cap, "ac"))
2592      s = fixup_tgetstr (s);
2593 
2594 #ifdef USE_TERMCAP
2595    if ((s >= area_buf) && (s < area_buf + sizeof(area_buf)))
2596      {
2597 	/* It looks like tgetstr placed the object in the buffer and
2598 	 * returned a pointer to that buffer.  So, we have to make a
2599 	 * copy of it.
2600 	 *
2601 	 * Yes, this introduces a leak...
2602 	 */
2603 	s = SLmake_string (s);
2604      }
2605 #endif
2606    return s;
2607 }
2608 
SLtt_tgetstr(SLFUTURE_CONST char * cap)2609 char *SLtt_tgetstr (SLFUTURE_CONST char *cap)
2610 {
2611    return tt_tgetstr (cap);
2612 }
2613 
tt_tgetnum(SLCONST char * s)2614 static int tt_tgetnum (SLCONST char *s)
2615 {
2616    if (Termcap_Initialized == 0)
2617      return -1;
2618 #ifdef USE_TERMCAP
2619    return tgetnum (s);
2620 #else
2621    return _pSLtt_tigetnum (Terminfo, s);
2622 #endif
2623 }
2624 
SLtt_tgetnum(SLFUTURE_CONST char * s)2625 int SLtt_tgetnum (SLFUTURE_CONST char *s)
2626 {
2627    return tt_tgetnum (s);
2628 }
2629 
tt_tgetflag(SLCONST char * s)2630 static int tt_tgetflag (SLCONST char *s)
2631 {
2632    if (Termcap_Initialized == 0)
2633      return -1;
2634 #ifdef USE_TERMCAP
2635    return tgetflag (s);
2636 #else
2637    return _pSLtt_tigetflag (Terminfo, s);
2638 #endif
2639 }
2640 
SLtt_tgetflag(SLFUTURE_CONST char * s)2641 int SLtt_tgetflag (SLFUTURE_CONST char *s)
2642 {
2643    return tt_tgetflag (s);
2644 }
2645 
SLtt_tgetent(char * term)2646 int SLtt_tgetent(char *term)
2647 {
2648    return 0 == SLtt_initialize(term);
2649 }
2650 
SLtt_tputs(char * str,int affcnt,int (* p)(int))2651 int SLtt_tputs(char *str, int affcnt, int (*p)(int))
2652 {
2653    (void) affcnt;
2654    while (*str) (*p)(*str++);
2655    return 0;
2656 }
2657 
SLtt_tgoto(char * cap,int col,int row)2658 char *SLtt_tgoto (char *cap, int col, int row)
2659 {
2660    static char buf[64];
2661    /* beware of overflows. 2^64 is 20 bytes printed */
2662    if (strlen(cap) > 23)
2663      strcpy(buf, "capability too long");
2664    else
2665      tt_sprintf(buf, sizeof(buf), cap, row, col);
2666     return buf;
2667 }
2668 
2669 static int Vt100_Like = 0;
2670 
SLtt_get_terminfo(void)2671 void SLtt_get_terminfo (void)
2672 {
2673    char *term;
2674    int status;
2675 
2676    term = getenv ("TERM");
2677    if (term == NULL)
2678      SLang_exit_error("%s", "TERM environment variable needs set.");
2679 
2680    if (0 == (status = SLtt_initialize (term)))
2681      return;
2682 
2683    if (status == -1)
2684      {
2685 	SLang_exit_error ("Unknown terminal: %s\n\
2686 Check the TERM environment variable.\n\
2687 Also make sure that the terminal is defined in the terminfo database.\n\
2688 Alternatively, set the TERMCAP environment variable to the desired\n\
2689 termcap entry.",
2690 			  term);
2691      }
2692 
2693    if (status == -2)
2694      {
2695 	SLang_exit_error ("\
2696 Your terminal lacks the ability to clear the screen or position the cursor.\n");
2697      }
2698 }
2699 
2700 /* Returns 0 if all goes well, -1 if terminal capabilities cannot be deduced,
2701  * or -2 if terminal cannot position the cursor.
2702  */
SLtt_initialize(SLFUTURE_CONST char * term)2703 int SLtt_initialize (SLFUTURE_CONST char *term)
2704 {
2705    SLCONST char *t;
2706    char ch;
2707    int is_xterm;
2708    int almost_vtxxx;
2709 
2710    if (_pSLtt_UTF8_Mode == -1)
2711      _pSLtt_UTF8_Mode = _pSLutf8_mode;
2712 
2713    if (SLang_TT_Write_FD == -1)
2714      {
2715 	/* Apparantly, this cannot fail according to the man pages. */
2716 	SLang_TT_Write_FD = fileno (stdout);
2717      }
2718 
2719    if (term == NULL)
2720      {
2721 	term = getenv ("TERM");
2722 	if (term == NULL)
2723 	  return -1;
2724      }
2725 
2726    if (_pSLsecure_issetugid ()
2727        && ((term[0] == '.') || (NULL != strchr(term, '/'))))
2728      return -1;
2729 
2730    Linux_Console = (!strncmp (term, "linux", 5)
2731 # ifdef linux
2732 		    || !strncmp(term, "con", 3)
2733 # endif
2734 		    );
2735 
2736    t = term;
2737 
2738    if (strcmp(t, "vt52") && (*t++ == 'v') && (*t++ == 't')
2739        && (ch = *t, (ch >= '1') && (ch <= '9'))) Vt100_Like = 1;
2740 
2741    is_xterm = ((0 == strncmp (term, "xterm", 5))
2742 	       || (0 == strncmp (term, "rxvt", 4))
2743 	       || (0 == strncmp (term, "Eterm", 5)));
2744 
2745    almost_vtxxx = (Vt100_Like
2746 		   || Linux_Console
2747 		   || is_xterm
2748 		   || !strcmp (term, "screen"));
2749 
2750 # ifndef USE_TERMCAP
2751    if (Terminfo != NULL)
2752      _pSLtt_tifreeent (Terminfo);
2753 
2754    Termcap_Initialized = 0;	       /* resetting things */
2755 
2756    if (NULL == (Terminfo = _pSLtt_tigetent (term)))
2757      {
2758 	if (almost_vtxxx) /* Special cases. */
2759 	  {
2760 	     int vt102 = 1;
2761 	     if (!strcmp (term, "vt100")) vt102 = 0;
2762 	     get_color_info ();
2763    	     SLtt_set_term_vtxxx (&vt102);
2764 	     (void) SLtt_get_screen_size ();
2765 	     return 0;
2766 	  }
2767 	return -1;
2768      }
2769 # else				       /* USE_TERMCAP */
2770    if (1 != tgetent(Termcap_Buf, term))
2771      return -1;
2772    /* Termcap_String_Ptr = Termcap_String_Buf; */
2773 # endif				       /* NOT USE_TERMCAP */
2774 
2775    Termcap_Initialized = 1;
2776 
2777    Cls_Str = tt_tgetstr ("cl");
2778    Abs_Curs_Pos_Str = tt_tgetstr ("cm");
2779 
2780    if ((NULL == (Ins_Mode_Str = tt_tgetstr("im")))
2781        || ( NULL == (Eins_Mode_Str = tt_tgetstr("ei")))
2782        || ( NULL == (Del_Char_Str = tt_tgetstr("dc"))))
2783      SLtt_Term_Cannot_Insert = 1;
2784 
2785    Visible_Bell_Str = tt_tgetstr ("vb");
2786 
2787    Curs_Up_Str = tt_tgetstr ("up");
2788    Curs_UpN_Str = tt_tgetstr ("UP");
2789 
2790    /* Avoid \n for moving down because we cannot rely upon the resulting column */
2791    Curs_Dn_Str = tt_tgetstr ("do");
2792    if ((Curs_Dn_Str != NULL) && (*Curs_Dn_Str == '\n'))
2793      Curs_Dn_Str = NULL;
2794 
2795    Curs_DnN_Str = tt_tgetstr ("DO");
2796    Curs_Left_Str = tt_tgetstr ("le");
2797    if (Curs_Left_Str == NULL)
2798      {
2799 	Curs_Left_Str = tt_tgetstr ("bc");
2800 	if (Curs_Left_Str == NULL)
2801 	  Curs_Left_Str = "\b";
2802      }
2803    Curs_LeftN_Str = tt_tgetstr ("LE");
2804    Curs_Right_Str = tt_tgetstr ("nd");
2805    Curs_RightN_Str = tt_tgetstr ("RI");
2806    Clear_EOS_Str = tt_tgetstr ("cd");
2807 
2808    Rev_Scroll_Str = tt_tgetstr("sr");
2809    Del_N_Lines_Str = tt_tgetstr("DL");
2810    Add_N_Lines_Str = tt_tgetstr("AL");
2811 
2812    /* Actually these are used to initialize terminals that use cursor
2813     * addressing.  Hard to believe.
2814     */
2815    Start_Abs_Cursor_Addressing_Mode = tt_tgetstr ("ti");
2816    End_Abs_Cursor_Addressing_Mode = tt_tgetstr ("te");
2817 
2818    /* If I do this for vtxxx terminals, arrow keys start sending ESC O A,
2819     * which I do not want.  This is mainly for HP terminals.
2820     */
2821    Keypad_Init_Str = tt_tgetstr ("ks");
2822    Keypad_Reset_Str = tt_tgetstr ("ke");
2823    if ((almost_vtxxx == 0) && (SLtt_Force_Keypad_Init == -1))
2824      SLtt_Force_Keypad_Init = 1;
2825 
2826    /* Make up for defective termcap/terminfo databases */
2827    if ((Vt100_Like && (term[2] != '1'))
2828        || Linux_Console
2829        || is_xterm
2830        )
2831      {
2832 	if (Del_N_Lines_Str == NULL) Del_N_Lines_Str = "\033[%dM";
2833 	if (Add_N_Lines_Str == NULL) Add_N_Lines_Str = "\033[%dL";
2834      }
2835 
2836    Scroll_R_Str = tt_tgetstr("cs");
2837 
2838    SLtt_get_screen_size ();
2839 
2840    if ((Scroll_R_Str == NULL)
2841        || (((NULL == Del_N_Lines_Str) || (NULL == Add_N_Lines_Str))
2842 	   && (NULL == Rev_Scroll_Str)))
2843      {
2844 	if (is_xterm
2845 	    || Linux_Console
2846 	    )
2847 	  {
2848 	     /* Defective termcap mode!!!! */
2849 	     SLtt_set_term_vtxxx (NULL);
2850 	  }
2851 	else SLtt_Term_Cannot_Scroll = 1;
2852      }
2853 
2854    Del_Eol_Str = tt_tgetstr("ce");
2855    Del_Bol_Str = tt_tgetstr("cb");
2856    if (is_xterm && (Del_Bol_Str == NULL))
2857      Del_Bol_Str = "\033[1K";
2858    if (is_xterm && (Del_Eol_Str == NULL))
2859      Del_Eol_Str = "\033[K";
2860 
2861    Rev_Vid_Str = tt_tgetstr("mr");
2862    if (Rev_Vid_Str == NULL) Rev_Vid_Str = tt_tgetstr("so");
2863 
2864    Bold_Vid_Str = tt_tgetstr("md");
2865 
2866    /* Although xterm cannot blink, it does display the blinking characters
2867     * as bold ones.  Some Rxvt will display the background as high intensity.
2868     */
2869    if ((NULL == (Blink_Vid_Str = tt_tgetstr("mb")))
2870        && is_xterm)
2871      Blink_Vid_Str = "\033[5m";
2872 
2873    UnderLine_Vid_Str = tt_tgetstr("us");
2874    Italic_Vid_Str = "\033[3m";
2875 
2876    Start_Alt_Chars_Str = tt_tgetstr ("as");   /* smacs */
2877    End_Alt_Chars_Str = tt_tgetstr ("ae");   /* rmacs */
2878    Enable_Alt_Char_Set = tt_tgetstr ("eA");   /* enacs */
2879    SLtt_Graphics_Char_Pairs = tt_tgetstr ("ac");
2880 
2881    if (NULL == SLtt_Graphics_Char_Pairs)
2882      {
2883 	/* make up for defective termcap/terminfo */
2884 	if (Vt100_Like)
2885 	  {
2886 	     Start_Alt_Chars_Str = "\016";
2887 	     End_Alt_Chars_Str = "\017";
2888 	     Enable_Alt_Char_Set = "\033)0";
2889 	  }
2890      }
2891 
2892     /* aixterm added by willi */
2893    if (is_xterm || !strncmp (term, "aixterm", 7))
2894      {
2895 #if 0
2896 	Start_Alt_Chars_Str = "\016";
2897 	End_Alt_Chars_Str = "\017";
2898 	Enable_Alt_Char_Set = "\033(B\033)0";
2899 #else
2900 	Start_Alt_Chars_Str = "\033(0";
2901 	End_Alt_Chars_Str = "\033(B";
2902 	Enable_Alt_Char_Set = "";
2903 #endif
2904      }
2905 
2906    if ((SLtt_Graphics_Char_Pairs == NULL) &&
2907        ((Start_Alt_Chars_Str == NULL) || (End_Alt_Chars_Str == NULL)))
2908      {
2909 	SLtt_Has_Alt_Charset = 0;
2910 	Enable_Alt_Char_Set = NULL;
2911      }
2912    else SLtt_Has_Alt_Charset = 1;
2913 
2914 #ifdef AMIGA
2915    Enable_Alt_Char_Set = Start_Alt_Chars_Str = End_Alt_Chars_Str = NULL;
2916 #endif
2917 
2918    /* status line capabilities */
2919    if ((SLtt_Has_Status_Line == -1)
2920        && (0 != (SLtt_Has_Status_Line = TGETFLAG ("hs"))))
2921      {
2922 	Disable_Status_line_Str = tt_tgetstr ("ds");
2923 	Return_From_Status_Line_Str = tt_tgetstr ("fs");
2924 	Goto_Status_Line_Str = tt_tgetstr ("ts");
2925 	/* Status_Line_Esc_Ok = TGETFLAG("es"); */
2926 	/* Num_Status_Line_Columns = tt_tgetnum ("ws"); */
2927 	/* if (Num_Status_Line_Columns < 0) Num_Status_Line_Columns = 0; */
2928      }
2929 
2930    if (NULL == (Norm_Vid_Str = tt_tgetstr("me")))
2931      {
2932 	Norm_Vid_Str = tt_tgetstr("se");
2933      }
2934 
2935    Cursor_Invisible_Str = tt_tgetstr("vi");
2936    Cursor_Visible_Str = tt_tgetstr("ve");
2937 
2938    Automatic_Margins = TGETFLAG ("am");
2939    /* No_Move_In_Standout = !TGETFLAG ("ms"); */
2940 # ifdef HP_GLITCH_CODE
2941    Has_HP_Glitch = TGETFLAG ("xs");
2942 # else
2943    Worthless_Highlight = TGETFLAG ("xs");
2944 # endif
2945 
2946    if (Worthless_Highlight == 0)
2947      {				       /* Magic cookie glitch */
2948 	Worthless_Highlight = (tt_tgetnum ("sg") > 0);
2949      }
2950 
2951    if (Worthless_Highlight)
2952      SLtt_Has_Alt_Charset = 0;
2953 
2954    Reset_Color_String = tt_tgetstr ("op");
2955 
2956    /* Apparantly the difference between "AF" and "Sf" is that AF uses RGB,
2957     * but Sf uses BGR.
2958     */
2959    Color_Fg_Str = tt_tgetstr ("AF"); /* ANSI setaf */
2960    if (Color_Fg_Str == NULL)
2961      {
2962 	Color_Fg_Str = tt_tgetstr ("Sf");   /* setf */
2963 	Is_Fg_BGR = (Color_Fg_Str != NULL);
2964      }
2965    Color_Bg_Str = tt_tgetstr ("AB"); /* ANSI setbf */
2966    if (Color_Bg_Str == NULL)
2967      {
2968 	Color_Bg_Str = tt_tgetstr ("Sb");   /* setb */
2969 	Is_Bg_BGR = (Color_Bg_Str != NULL);
2970      }
2971 
2972    if ((Max_Terminfo_Colors = tt_tgetnum ("Co")) < 0)
2973      Max_Terminfo_Colors = 8;
2974 
2975    if ((Color_Bg_Str != NULL) && (Color_Fg_Str != NULL))
2976      SLtt_Use_Ansi_Colors = 1;
2977    else
2978      {
2979 #if 0
2980 	Color_Fg_Str = "%?%p1%{7}%>%t\033[1;3%p1%{8}%m%dm%e\033[3%p1%dm%;";
2981 	Color_Bg_Str = "%?%p1%{7}%>%t\033[5;4%p1%{8}%m%dm%e\033[4%p1%dm%;";
2982 	Max_Terminfo_Colors = 16;
2983 #else
2984 	Color_Fg_Str = "\033[3%dm";
2985 	Color_Bg_Str = "\033[4%dm";
2986 	Max_Terminfo_Colors = 8;
2987 #endif
2988      }
2989 
2990 #if SLTT_HAS_NON_BCE_SUPPORT
2991    Can_Background_Color_Erase = TGETFLAG ("ut");   /* bce */
2992    /* Modern xterms have the BCE capability as well as the linux console */
2993    if (Can_Background_Color_Erase == 0)
2994      {
2995 	Can_Background_Color_Erase = (Linux_Console
2996 # if SLTT_XTERM_ALWAYS_BCE
2997 				      || is_xterm
2998 # endif
2999 				      );
3000      }
3001 #endif
3002    get_color_info ();
3003 
3004    TT_Is_Initialized = 1;
3005 
3006    if ((Cls_Str == NULL)
3007        || (Abs_Curs_Pos_Str == NULL))
3008      return -2;
3009 
3010    return 0;
3011 }
3012 #endif
3013 /* Unix */
3014 
3015 /* specific to vtxxx only */
SLtt_enable_cursor_keys(void)3016 void SLtt_enable_cursor_keys (void)
3017 {
3018 #ifdef __unix__
3019    if (Vt100_Like)
3020 #endif
3021      tt_write_string("\033=\033[?1l");
3022 }
3023 
3024 #ifdef VMS
SLtt_initialize(char * term)3025 int SLtt_initialize (char *term)
3026 {
3027    SLtt_get_terminfo ();
3028    TT_Is_Initialized = 1;
3029    return 0;
3030 }
3031 
SLtt_get_terminfo()3032 void SLtt_get_terminfo ()
3033 {
3034    int zero = 0;
3035 
3036    /* Apparantly, this cannot fail according to the man pages. */
3037    if (SLang_TT_Write_FD == -1)
3038      SLang_TT_Write_FD = fileno (stdout);
3039 
3040    Can_Background_Color_Erase = 0;
3041 
3042    Color_Fg_Str = "\033[3%dm";
3043    Color_Bg_Str = "\033[4%dm";
3044    Max_Terminfo_Colors = 8;
3045 
3046    get_color_info ();
3047 
3048    SLtt_set_term_vtxxx(&zero);
3049    Start_Alt_Chars_Str = "\016";
3050    End_Alt_Chars_Str = "\017";
3051    SLtt_Has_Alt_Charset = 1;
3052    SLtt_Graphics_Char_Pairs = "aaffgghhjjkkllmmnnooqqssttuuvvwwxx";
3053    Enable_Alt_Char_Set = "\033(B\033)0";
3054    SLtt_get_screen_size ();
3055 }
3056 #endif
3057 
3058 /* This sets term for vt102 terminals it parameter vt100 is 0.  If vt100
3059  * is non-zero, set terminal appropriate for a only vt100
3060  * (no add line capability). */
3061 
SLtt_set_term_vtxxx(int * vt100)3062 void SLtt_set_term_vtxxx(int *vt100)
3063 {
3064    Norm_Vid_Str = "\033[m";
3065 
3066    Scroll_R_Str = "\033[%i%d;%dr";
3067    Cls_Str = "\033[2J\033[H";
3068    Rev_Vid_Str = "\033[7m";
3069    Bold_Vid_Str = "\033[1m";
3070    Blink_Vid_Str = "\033[5m";
3071    UnderLine_Vid_Str = "\033[4m";
3072    Italic_Vid_Str = "\033[3m";
3073    Del_Eol_Str = "\033[K";
3074    Del_Bol_Str = "\033[1K";
3075    Rev_Scroll_Str = "\033M";
3076 
3077    Curs_Up_Str = "\033[A";
3078    Curs_Dn_Str = "\033[B";
3079    Curs_Right_Str = "\033[C";
3080    Curs_Left_Str = "\033[D";
3081    Curs_UpN_Str = "\033[%dA";
3082    Curs_DnN_Str = "\033[%dB";
3083    Curs_RightN_Str = "\033[%dC";
3084    Curs_LeftN_Str = "\033[%dD";
3085 
3086    Abs_Curs_Pos_Str = "\033[%i%d;%dH";
3087    if ((vt100 == NULL) || (*vt100 == 0))
3088      {
3089 	Ins_Mode_Str = "\033[4h";
3090 	Eins_Mode_Str = "\033[4l";
3091 	Del_Char_Str =  "\033[P";
3092 	Del_N_Lines_Str = "\033[%dM";
3093 	Add_N_Lines_Str = "\033[%dL";
3094 	SLtt_Term_Cannot_Insert = 0;
3095      }
3096    else
3097      {
3098 	Del_N_Lines_Str = NULL;
3099 	Add_N_Lines_Str = NULL;
3100 	SLtt_Term_Cannot_Insert = 1;
3101      }
3102    SLtt_Term_Cannot_Scroll = 0;
3103    /* No_Move_In_Standout = 0; */
3104 }
3105 
SLtt_init_keypad(void)3106 void SLtt_init_keypad (void)
3107 {
3108    if (SLtt_Force_Keypad_Init > 0)
3109      {
3110 	tt_write_string (Keypad_Init_Str);
3111 	SLtt_flush_output ();
3112      }
3113 }
3114 
SLtt_deinit_keypad(void)3115 void SLtt_deinit_keypad (void)
3116 {
3117    if (SLtt_Force_Keypad_Init > 0)
3118      {
3119 	tt_write_string (Keypad_Reset_Str);
3120 	SLtt_flush_output ();
3121      }
3122 }
3123 
SLtt_init_video(void)3124 int SLtt_init_video (void)
3125 {
3126    /*   send_string_to_term("\033[?6h"); */
3127    /* relative origin mode */
3128    if (Use_Relative_Cursor_Addressing == 0)
3129      tt_write_string (Start_Abs_Cursor_Addressing_Mode);
3130    SLtt_init_keypad ();
3131    SLtt_reset_scroll_region();
3132    SLtt_end_insert();
3133    tt_write_string (Enable_Alt_Char_Set);
3134    Video_Initialized = 1;
3135    return 0;
3136 }
3137 
_pSLtt_init_cmdline_mode(void)3138 int _pSLtt_init_cmdline_mode (void)
3139 {
3140    if (TT_Is_Initialized == 0)
3141      {
3142 	int status = SLtt_initialize (NULL);
3143 	if (status < 0)
3144 	  {
3145 	     if (status == -1)
3146 	       SLang_vmessage ("%s", "**WARNING: Unknown terminal capabilities.\n");
3147 	     return 0;
3148 	  }
3149      }
3150    /* We need to be able to use relative cursor addressing in this mode */
3151    if (((Curs_UpN_Str == NULL) && (Curs_Up_Str == NULL))
3152        || ((Curs_Dn_Str == NULL) && (Curs_DnN_Str == NULL))
3153        || ((Curs_Right_Str == NULL) && (Curs_RightN_Str == NULL))
3154        || ((Curs_Left_Str == NULL) && (Curs_LeftN_Str == NULL)))
3155      return 0;
3156 
3157    SLtt_Term_Cannot_Scroll = 1;
3158    SLtt_Use_Ansi_Colors = 0;
3159    Use_Relative_Cursor_Addressing = 1;
3160    return 1;
3161 }
3162 
_pSLtt_cmdline_mode_reset(void)3163 void _pSLtt_cmdline_mode_reset (void)
3164 {
3165    Cursor_Set = 0;
3166    Cursor_r = Cursor_c = 0;
3167    Max_Relative_Cursor_r = 0;
3168    tt_write ("\r", 1);
3169    _pSLtt_clear_eos ();
3170 }
3171 
SLtt_reset_video(void)3172 int SLtt_reset_video (void)
3173 {
3174    SLtt_goto_rc (SLtt_Screen_Rows - 1, 0);
3175    Cursor_Set = 0;
3176    SLtt_normal_video ();	       /* MSKermit requires this  */
3177    tt_write_string(Norm_Vid_Str);
3178 
3179    Current_Fgbg = INVALID_ATTR;
3180    SLtt_set_alt_char_set (0);
3181    if (SLtt_Use_Ansi_Colors)
3182      {
3183 	if (Reset_Color_String == NULL)
3184 	  {
3185 	     SLtt_Char_Type attr;
3186 	     if (-1 != make_color_fgbg (NULL, NULL, &attr))
3187 	       write_attributes (attr);
3188 	     else tt_write_string ("\033[0m\033[m");
3189 	  }
3190 	else tt_write_string (Reset_Color_String);
3191 	Current_Fgbg = INVALID_ATTR;
3192      }
3193    SLtt_erase_line ();
3194    SLtt_deinit_keypad ();
3195 
3196    if (Use_Relative_Cursor_Addressing == 0)
3197      tt_write_string (End_Abs_Cursor_Addressing_Mode);
3198 
3199    if (Mouse_Mode == 1)
3200      SLtt_set_mouse_mode (0, 1);
3201 
3202    SLtt_flush_output ();
3203    Video_Initialized = 0;
3204    return 0;
3205 }
3206 
SLtt_bold_video(void)3207 void SLtt_bold_video (void)
3208 {
3209    tt_write_string (Bold_Vid_Str);
3210 }
3211 
SLtt_set_mouse_mode(int mode,int force)3212 int SLtt_set_mouse_mode (int mode, int force)
3213 {
3214    if (force == 0)
3215      {
3216 	char *term;
3217 
3218 	if (NULL == (term = (char *) getenv("TERM"))) return -1;
3219 	if (strncmp ("xterm", term, 5))
3220 	  return -1;
3221      }
3222 
3223    Mouse_Mode = (mode != 0);
3224 
3225    if (mode)
3226      tt_write_string ("\033[?9h");
3227    else
3228      tt_write_string ("\033[?9l");
3229 
3230    return 0;
3231 }
3232 
SLtt_disable_status_line(void)3233 void SLtt_disable_status_line (void)
3234 {
3235    if (SLtt_Has_Status_Line > 0)
3236      {
3237 	tt_write_string (Disable_Status_line_Str);
3238 	SLtt_flush_output ();
3239      }
3240 }
3241 
SLtt_write_to_status_line(SLFUTURE_CONST char * s,int col)3242 int SLtt_write_to_status_line (SLFUTURE_CONST char *s, int col)
3243 {
3244    if ((SLtt_Has_Status_Line <= 0)
3245        || (Goto_Status_Line_Str == NULL)
3246        || (Return_From_Status_Line_Str == NULL))
3247      return -1;
3248 
3249    tt_printf (Goto_Status_Line_Str, col, 0);
3250    tt_write_string (s);
3251    tt_write_string (Return_From_Status_Line_Str);
3252    return 0;
3253 }
3254 
SLtt_get_screen_size(void)3255 void SLtt_get_screen_size (void)
3256 {
3257 #ifdef VMS_SYSTEM
3258    int status, code;
3259    unsigned short chan;
3260    $DESCRIPTOR(dev_dsc, "SYS$INPUT:");
3261 #endif
3262    int r = 0, c = 0;
3263 
3264 #ifdef TIOCGWINSZ
3265    struct winsize wind_struct;
3266 
3267    do
3268      {
3269 	if ((ioctl(1,TIOCGWINSZ,&wind_struct) == 0)
3270 	    || (ioctl(0, TIOCGWINSZ, &wind_struct) == 0)
3271 	    || (ioctl(2, TIOCGWINSZ, &wind_struct) == 0))
3272 	  {
3273 	     c = (int) wind_struct.ws_col;
3274 	     r = (int) wind_struct.ws_row;
3275 	     break;
3276 	  }
3277      }
3278    while (errno == EINTR);
3279 
3280 #endif
3281 
3282 #ifdef VMS_SYSTEM
3283    status = sys$assign(&dev_dsc,&chan,0,0,0);
3284    if (status & 1)
3285      {
3286 	code = DVI$_DEVBUFSIZ;
3287 	status = lib$getdvi(&code, &chan,0, &c, 0,0);
3288 	if (!(status & 1))
3289 	  c = 80;
3290 	code = DVI$_TT_PAGE;
3291 	status = lib$getdvi(&code, &chan,0, &r, 0,0);
3292 	if (!(status & 1))
3293 	  r = 24;
3294 	sys$dassgn(chan);
3295      }
3296 #endif
3297 
3298    if (r <= 0)
3299      {
3300 	char *s = getenv ("LINES");
3301 	if (s != NULL) r = atoi (s);
3302      }
3303 
3304    if (c <= 0)
3305      {
3306 	char *s = getenv ("COLUMNS");
3307 	if (s != NULL) c = atoi (s);
3308      }
3309 
3310    if ((r <= 0) || (r > SLTT_MAX_SCREEN_ROWS)) r = 24;
3311    if ((c <= 0) || (c > SLTT_MAX_SCREEN_COLS)) c = 80;
3312    SLtt_Screen_Rows = r;
3313    SLtt_Screen_Cols = c;
3314 }
3315 
3316 #if SLTT_HAS_NON_BCE_SUPPORT
_pSLtt_get_bce_color_offset(void)3317 int _pSLtt_get_bce_color_offset (void)
3318 {
3319    if ((SLtt_Use_Ansi_Colors == 0)
3320        || Can_Background_Color_Erase
3321        || SLtt_Use_Blink_For_ACS)      /* in this case, we cannot lose a color */
3322      Bce_Color_Offset = 0;
3323    else
3324      {
3325 	SLtt_Char_Type fgbg = get_brush_fgbg (0);
3326 	if (GET_BG(fgbg) == SLSMG_COLOR_DEFAULT)
3327 	  Bce_Color_Offset = 0;
3328 	else
3329 	  Bce_Color_Offset = 1;
3330      }
3331 
3332    return Bce_Color_Offset;
3333 }
3334 #endif
3335 
SLtt_utf8_enable(int mode)3336 int SLtt_utf8_enable (int mode)
3337 {
3338    if (mode == -1)
3339      mode = _pSLutf8_mode;
3340 
3341    return _pSLtt_UTF8_Mode = mode;
3342 }
3343 
SLtt_is_utf8_mode(void)3344 int SLtt_is_utf8_mode (void)
3345 {
3346    int mode = _pSLtt_UTF8_Mode;
3347    if (mode == -1)
3348      mode = _pSLutf8_mode;
3349 
3350    return mode;
3351 }
3352