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