1
2 static char rcsid[] = "@(#)$Id: curses.c,v 1.6 1999/03/24 14:03:58 wfp5p Exp $";
3
4 /*******************************************************************************
5 * The Elm Mail System - $Revision: 1.6 $ $State: Exp $
6 *
7 * Copyright (c) 1988-1995 USENET Community Trust
8 * Copyright (c) 1986,1987 Dave Taylor
9 *******************************************************************************
10 * Bug reports, patches, comments, suggestions should be sent to:
11 *
12 * Bill Pemberton, Elm Coordinator
13 * flash@virginia.edu
14 *
15 *******************************************************************************
16 * $Log: curses.c,v $
17 * Revision 1.6 1999/03/24 14:03:58 wfp5p
18 * elm 2.5PL0
19 *
20 * Revision 1.5 1996/05/09 15:51:18 wfp5p
21 * Alpha 10
22 *
23 * Revision 1.4 1996/03/14 17:27:56 wfp5p
24 * Alpha 9
25 *
26 * Revision 1.3 1995/09/29 17:42:01 wfp5p
27 * Alpha 8 (Chip's big changes)
28 *
29 * Revision 1.2 1995/09/11 15:19:03 wfp5p
30 * Alpha 7
31 *
32 * Revision 1.1.1.1 1995/04/19 20:38:35 wfp5p
33 * Initial import of elm 2.4 PL0 as base for elm 2.5.
34 *
35 ******************************************************************************/
36
37 /** This library gives programs the ability to easily access the
38 termcap information and write screen oriented and raw input
39 programs. The routines can be called as needed, except that
40 to use the cursor / screen routines there must be a call to
41 InitScreen() first.
42 **/
43
44 #include "elm_defs.h"
45 #include "elm_globals.h"
46 #include "port_termios.h"
47 #include "s_elm.h"
48
49 #include <assert.h>
50 #ifdef I_STDARG
51 # include <stdarg.h>
52 #else
53 # include <varargs.h>
54 #endif
55
56 #define S_(sel, str) catgets(elm_msg_cat, ElmSet, (sel), (str))
57
58 #define DEFAULT_LINES 24
59 #define DEFAULT_COLS 80
60
61 static struct termios TC_raw_tty; /* tty settings for raw mode */
62 static struct termios TC_original_tty; /* tty settings for cooked mode */
63 static char TC_tcapbuf[1024]; /* storage for terminal entry */
64 static char TC_tcapstrs[1024]; /* storage for extracted strings*/
65 static int TC_curr_line, TC_curr_col; /* current cursor position */
66
67 /* termcap capabilities */
68 static int TC_errors;
69 static char *TC_start_termcap, *TC_end_termcap;
70 static char *TC_clearscreen, *TC_cleartoeoln, *TC_cleartoeos;
71 static char *TC_moveto, *TC_up, *TC_down, *TC_right, *TC_left;
72 static char *TC_start_insert, *TC_end_insert, *TC_char_insert, *TC_pad_insert;
73 static char *TC_start_delete, *TC_end_delete, *TC_char_delete;
74 static char *TC_start_standout, *TC_end_standout;
75 static char *TC_transmit_on, *TC_transmit_off;
76 static int TC_lines, TC_columns, TC_tabspacing;
77 static int TC_automargin, TC_eatnewlineglitch;
78
79 static void tget_complain P_((const char *, const char *));
80
81
82 /*
83 * Multi-byte special function keys are parsed by traversing
84 * a tree constructed of (struct knode) elements.
85 */
86 #define KCHUNK 8
87 struct knode {
88 int kcode; /* return value for terminal nodes */
89 char *kval; /* list of "next byte" values */
90 struct knode **knext; /* nodes corresponding to those values */
91 int keylist_len; /* number entries in above lists */
92 int keylist_alloc; /* allocated size of these lists */
93 };
94
95 /* root of the multi-byte parsing tree */
96 static struct knode *Knode_root;
97
98 static struct knode *knode_new P_((void));
99 static int knode_addkey P_((const char *, char **, int));
100
101 static void SetScreenSize P_((void));
102 static int outchar P_((int));
103
104 extern char *tgetstr(), *tgoto();
105
106
InitScreen()107 PUBLIC int InitScreen()
108 {
109 int i;
110 char *tp, *cp;
111
112 assert(OPMODE_IS_INTERACTIVE(opmode));
113
114 if ((cp = getenv("TERM")) == NULL) {
115 fprintf(stderr, S_(ElmInitScreenNoTerm,
116 "You must set the \"TERM\" environment parameter so that I know what\n\
117 kind of terminal you are using.\n"));
118 return -1;
119 }
120
121 i = tgetent(TC_tcapbuf, cp);
122 switch (i) {
123 case 1:
124 /* ok! */
125 break;
126 case 0:
127 fprintf(stderr, S_(ElmInitScreenUnknownTerm,
128 "I cannot find any information on terminal type \"%s\" in your\n\
129 termcap/terminfo database. Please check your \"TERM\" setting.\n"), cp);
130 return -1;
131 case -1:
132 fprintf(stderr, S_(ElmInitScreenNoTcap,
133 "I cannot access your termcap/terminfo database to load the\n\
134 information on your terminal.\n"));
135 return -1;
136 default:
137 fprintf(stderr, S_(ElmInitScreenGetentFailed,
138 "Unable to retrieve termcap/terminfo database entry for your\n\
139 terminal (tgetent return code %d).\n"), i);
140 return -1;
141 }
142
143 /* load in termcap capabilities we need */
144 TC_errors = 0;
145 tp = TC_tcapstrs;
146
147 TC_start_termcap = tgetstr("ti", &tp);
148 TC_end_termcap = tgetstr("te", &tp);
149
150 TC_clearscreen = tgetstr("cl", &tp);
151 if (!TC_clearscreen)
152 tget_complain("cl", S_(ElmInitScreenTcapcl, "clear screen"));
153 TC_cleartoeoln = tgetstr("ce", &tp);
154 if (!TC_cleartoeoln)
155 tget_complain("ce", S_(ElmInitScreenTcapce, "clear to end of line"));
156 TC_cleartoeos = tgetstr("cd", &tp);
157 if (!TC_cleartoeos)
158 tget_complain("cd", S_(ElmInitScreenTcapcd, "clear to end of display"));
159
160 TC_moveto = tgetstr("cm", &tp);
161 if (!TC_moveto)
162 tget_complain("cm", S_(ElmInitScreenTcapcm, "cursor motion"));
163
164 TC_up = tgetstr("up", &tp);
165 if (!TC_up)
166 tget_complain("cm", S_(ElmInitScreenTcapcu, "move cursor up"));
167
168 TC_down = tgetstr("do", &tp);
169 if (TC_down && streq(TC_down, "\n"))
170 TC_down = NULL;
171
172 TC_right = tgetstr("nd", &tp);
173 if (!TC_right)
174 tget_complain("nd", S_(ElmInitScreenTcapnd, "move cursor right"));
175
176 TC_left = tgetstr("le", &tp);
177 if (!TC_left) {
178 if (tgetflag("bs")) {
179 if ((TC_left = tgetstr("bc", &tp)) == NULL)
180 TC_left = "\b";
181 } else {
182 #ifdef notdef
183 tget_complain("le", S_(ElmInitScreenTcaple, "move cursor left"));
184 #else
185 /*
186 * In theory, we ought to be bailing out here. If the system
187 * doesn't enable the "bs" flag, then it ought to define
188 * a "bc" capability. In practice, some termcap entries are
189 * busted, and do neither when backspace should be used.
190 * So rather than complain, just presume backspace is ok.
191 */
192 TC_left = "\b";
193 #endif
194 }
195 }
196
197 TC_start_insert = tgetstr("im", &tp);
198 TC_end_insert = tgetstr("ei", &tp);
199 TC_char_insert = tgetstr("ic", &tp);
200 TC_pad_insert = tgetstr("ip", &tp);
201
202 TC_start_delete = tgetstr("dm", &tp);
203 TC_end_delete = tgetstr("ed", &tp);
204 TC_char_delete = tgetstr("dc", &tp);
205
206 TC_start_standout = tgetstr("so", &tp);
207 TC_end_standout = tgetstr("se", &tp);
208 TC_transmit_on = tgetstr("ks", &tp);
209 TC_transmit_off = tgetstr("ke", &tp);
210
211 if ((TC_lines = tgetnum("li")) < 0)
212 TC_lines = DEFAULT_LINES;
213 if ((TC_columns = tgetnum("co")) < 0)
214 TC_columns = DEFAULT_COLS;
215 if ((TC_tabspacing = tgetnum("it")) < 0)
216 TC_tabspacing = 8;
217
218 TC_automargin = tgetflag("am");
219 TC_eatnewlineglitch = tgetflag("xn");
220
221 if (TC_errors)
222 return -1;
223
224 /* see if environment overrides termcap screen size */
225 if ((cp = getenv("LINES")) != NULL && sscanf(cp, "%d", &i) == 1)
226 TC_lines = i;
227 if ((cp = getenv("COLS")) != NULL && sscanf(cp, "%d", &i) == 1)
228 TC_columns = i;
229
230 /* get the cursor control keys... */
231 Knode_root = knode_new();
232 (void) knode_addkey("kd", &tp, KEY_DOWN);
233 (void) knode_addkey("ku", &tp, KEY_UP);
234 (void) knode_addkey("kl", &tp, KEY_LEFT);
235 (void) knode_addkey("kr", &tp, KEY_RIGHT);
236 (void) knode_addkey("kh", &tp, KEY_HOME);
237 (void) knode_addkey("kD", &tp, KEY_DC);
238 (void) knode_addkey("kI", &tp, KEY_IC);
239 (void) knode_addkey("kN", &tp, KEY_NPAGE);
240 (void) knode_addkey("kP", &tp, KEY_PPAGE);
241 (void) knode_addkey("kB", &tp, KEY_BTAB);
242 (void) knode_addkey("kH", &tp, KEY_END); /* termcap-ish */
243 (void) knode_addkey("@7", &tp, KEY_END); /* terminfo-ish */
244
245 /* retrieve tty line settings */
246 if (tcgetattr(STDIN_FILENO, &TC_original_tty) < 0) {
247 fprintf(stderr, S_(ElmInitScreenGetattrFailed,
248 "Cannot retrieve attributes for your terminal line. [%s]\n"),
249 strerror(errno));
250 return -1;
251 }
252
253 /* setup configuration for raw mode */
254 bcopy((char *)&TC_original_tty, (char *)&TC_raw_tty,
255 sizeof(struct termios));
256 #if defined(TERMIO) || defined(TERMIOS)
257 #ifdef notdef /*FOO - I would like to run raw - but that breaks builtin editor*/
258 TC_raw_tty.c_lflag &= ~(ICANON|ISIG|ECHO);
259 #else
260 TC_raw_tty.c_lflag &= ~(ICANON|ECHO);
261 #endif
262 TC_raw_tty.c_cc[VMIN] = 1;
263 TC_raw_tty.c_cc[VTIME] = 0;
264 #else
265 TC_raw_tty.sg_flags &= ~(ECHO);
266 #ifdef notdef /*FOO - I would like to run raw - but that breaks builtin editor*/
267 TC_raw_tty.sg_flags |= RAW;
268 #else
269 TC_raw_tty.sg_flags |= CBREAK;
270 #endif
271 #endif
272
273 /* setup terminal information structure */
274 Term.status = TERM_IS_INIT;
275 if (TC_start_standout && TC_end_standout)
276 Term.status |= TERM_CAN_SO;
277 if (TC_char_delete)
278 Term.status |= TERM_CAN_DC;
279 if (TC_start_insert || TC_char_insert)
280 Term.status |= TERM_CAN_IC;
281
282 #if defined(TERMIOS) || defined(TERMIO)
283 Term.erase_char = TC_original_tty.c_cc[VERASE];
284 Term.kill_char = TC_original_tty.c_cc[VKILL];
285 #ifdef VWERASE
286 Term.werase_char = TC_original_tty.c_cc[VWERASE];
287 #else
288 Term.werase_char = ctrl('W');
289 #endif
290 Term.intr_char = TC_original_tty.c_cc[VINTR];
291 #else
292 Term.erase_char = TC_original_tty.sg_erase;
293 Term.kill_char = TC_original_tty.sg_kill;
294 Term.werase_char = ctrl('W');
295 Term.intr_char = ctrl('C');
296 #endif
297
298 /* haven't a clue where the cursor is */
299 InvalidateCursor();
300
301 /* initialize window size */
302 SetScreenSize();
303
304 /* force "->" if screen cannot do standout */
305 /* assuming elmrc has been processed already... */
306 if (!(Term.status & TERM_CAN_SO))
307 arrow_cursor = TRUE;
308
309 return 0;
310 }
311
312
tget_complain(capname,descrip)313 static void tget_complain(capname, descrip)
314 const char *capname;
315 const char *descrip;
316 {
317 fprintf(stderr, S_(ElmInitScreenTcapMissing,
318 "Your terminal does not support the \"%s\" function (%s).\n"),
319 descrip, capname);
320 ++TC_errors;
321 }
322
323
ShutdownTerm()324 PUBLIC void ShutdownTerm()
325 {
326 int err;
327
328 /*
329 * This routine commonly is called on the way out due to an error.
330 * Preserve the errno status across the shutdown so that diagnostics
331 * can be produced.
332 */
333 err = errno;
334
335 if (Term.status & TERM_IS_INIT) {
336 softkeys_off();
337 EnableFkeys(OFF);
338 MoveCursor(LINES, 0);
339 NewLine();
340 Raw(OFF);
341 }
342 Term.status &= ~TERM_IS_INIT;
343
344 errno = err;
345 }
346
347
SetScreenSize()348 static void SetScreenSize()
349 {
350 LINES = TC_lines - 1; /* I'm sure I don't understand this lines-1 jazz */
351 COLS = TC_columns;
352 #ifdef TIOCGWINSZ
353 {
354 struct winsize w;
355 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0) {
356 if (w.ws_row > 0)
357 LINES = w.ws_row - 1;
358 if (w.ws_col > 0)
359 COLS = w.ws_col;
360 }
361 }
362 #endif
363 }
364
365
366 /*
367 * Allocate an empty (struct knode).
368 */
knode_new()369 static struct knode *knode_new()
370 {
371 struct knode *knode;
372
373 knode = (struct knode *) safe_malloc(sizeof(struct knode));
374 knode->kcode = 0;
375 knode->kval = (char *) safe_malloc(KCHUNK);
376 knode->knext = (struct knode **) safe_malloc(KCHUNK*sizeof(struct knode *));
377 knode->keylist_len = 0;
378 knode->keylist_alloc = KCHUNK;
379 return(knode);
380 }
381
382
383 /*
384 * Place key rooted at "knode". The "kstr" is the string that
385 * corresponds to the value when the key is struck, and the "kcode"
386 * is the code that should be assigned to this key.
387 */
knode_addkey(capname,tp,kcode)388 static int knode_addkey(capname, tp, kcode)
389 const char *capname;
390 char **tp;
391 int kcode;
392 {
393 int i;
394 char *kstr;
395 struct knode *knode;
396
397 if ((kstr = tgetstr(capname, tp)) == NULL || strlen(kstr) < 2)
398 return -1;
399
400 knode = Knode_root;
401 while (*kstr != '\0') {
402
403 /* see if this character already is in the tree */
404 for (i = 0 ; i < knode->keylist_len ; ++i) {
405 if (knode->kval[i] == *kstr)
406 break;
407 }
408
409 /* nope ... need to add it */
410 if (i >= knode->keylist_len) {
411 if (knode->keylist_len >= knode->keylist_alloc) {
412 knode->keylist_alloc += KCHUNK;
413 knode->kval = (char *) safe_realloc((malloc_t)knode->kval,
414 knode->keylist_alloc);
415 knode->knext = (struct knode **) safe_realloc(
416 (malloc_t)knode->knext,
417 knode->keylist_alloc * sizeof(struct knode *));
418 }
419 assert(i == knode->keylist_len);
420 knode->kval[i] = *kstr;
421 knode->knext[i] = knode_new();
422 ++knode->keylist_len;
423 }
424
425 /* descend into the tree */
426 knode = knode->knext[i];
427 ++kstr;
428
429 }
430
431 /* place this key code at the terminal node */
432 assert(knode->keylist_len == 0);
433 knode->kcode = kcode;
434
435 return 0;
436 }
437
438
439 /*
440 * Iteratively parse a multi-byte special function key.
441 *
442 * This routine should be called once with a "kval" of zero.
443 * Then it is called iteratively for each byte in the key sequence.
444 * Returns a positive value (the key code) once the key sequence is resolved.
445 * Returns 0 if more bytes are needed to resolve this key.
446 * Returns -1 if the sequence cannot be mapped to a key.
447 */
knode_parse(kval)448 PUBLIC int knode_parse(kval)
449 int kval;
450 {
451 static struct knode *knode = NULL;
452 int code, i;
453
454 /* initialize for new key sequence */
455 if (kval == 0) {
456 knode = Knode_root;
457 return 0;
458 }
459
460 assert(knode != NULL);
461
462 /* locate this byte in the tree */
463 code = -1;
464 for (i = 0 ; i < knode->keylist_len ; ++i) {
465 if (knode->kval[i] == (char)kval) {
466 knode = knode->knext[i];
467 code = knode->kcode;
468 break;
469 }
470 }
471
472 if (code != 0) {
473 /* either key is resolved, or it is something we don't understand */
474 knode = NULL;
475 }
476 return code;
477 }
478
479
EnableFkeys(newstate)480 PUBLIC void EnableFkeys(newstate)
481 int newstate;
482 {
483 /*
484 * Emit the sequence that some terminals require to
485 * enable/disable the special function keys.
486 */
487 if (!TC_transmit_on || !TC_transmit_off)
488 return;
489 if (!newstate == !(Term.status & TERM_IS_FKEY))
490 return;
491 if (newstate) {
492 tputs(TC_transmit_on, 1, outchar);
493 Term.status |= TERM_IS_FKEY;
494 } else {
495 tputs(TC_transmit_off, 1, outchar);
496 Term.status &= ~TERM_IS_FKEY;
497 }
498 /* fflush(stdout);*/
499 FlushOutput();
500 }
501
502
503 #if defined(SIGWINCH) || defined(SIGCONT)
504 /* if screen size changed, update and return TRUE */
ResizeScreen()505 PUBLIC void ResizeScreen()
506 {
507 /* reset the flag that indicates a sig was caught */
508 caught_screen_change_sig = 0;
509 #ifdef SIGWINCH
510 /* reload the LINES/COLS values */
511 SetScreenSize();
512 #endif
513 }
514 #endif /* defined(SIGWINCH) || defined(SIGCONT) */
515
516
GetCursorPos(line_p,col_p)517 PUBLIC void GetCursorPos(line_p, col_p)
518 int *line_p, *col_p;
519 {
520 assert(TC_curr_col >= 0 && TC_curr_col < COLS);
521 assert(TC_curr_line >= 0 && TC_curr_line <= LINES);
522 *line_p = TC_curr_line;
523 *col_p = TC_curr_col;
524 }
525
526
InvalidateCursor()527 PUBLIC void InvalidateCursor()
528 {
529 /* indicate the cursor might not be where we think it is */
530 TC_curr_line = -1;
531 TC_curr_col = -1;
532 }
533
534
MoveCursor(line,col)535 PUBLIC void MoveCursor(line, col)
536 {
537 int d_col, d_line, d_total;
538 assert(col >= 0 && col < COLS && line >= 0 && line <= LINES);
539
540 /* cursor is correct */
541 if (TC_curr_col == col && TC_curr_line == line)
542 return;
543
544 /* if we don't know where the cursor is right now, perform absolute move */
545 if (TC_curr_col < 0 || TC_curr_line < 0) {
546 do_absolute_motion:
547 tputs(tgoto(TC_moveto, col, line), 1, outchar);
548 TC_curr_col = col;
549 TC_curr_line = line;
550 return;
551 }
552
553 /* move to beginning of line */
554 if (col == 0) {
555 if (TC_curr_col > 0) {
556 putchar('\r');
557 TC_curr_col = 0;
558 }
559 if (TC_curr_line == line) {
560 /* beginning of same line */
561 return;
562 }
563 if (TC_curr_line == line-1) {
564 /* beginning of next line */
565 putchar('\n');
566 ++TC_curr_line;
567 return;
568 }
569 }
570
571 /* see if we are close enough so that relative motion makes sense */
572 d_col = col - TC_curr_col;
573 d_line = line - TC_curr_line;
574 d_total = (d_col < 0 ? -d_col : d_col) + (d_line < 0 ? -d_line : d_line);
575 if (d_total > 5)
576 goto do_absolute_motion;
577
578 /* relative horizontal motion */
579 if (d_col != 0) {
580 assert(TC_left != NULL && TC_right != NULL);
581 while (d_col < 0) {
582 /* negative value => physical > logical => must move left */
583 tputs(TC_left, 1, outchar);
584 ++d_col;
585 }
586 while (d_col > 0) {
587 /* positive value => logical > physiacl => must move right */
588 tputs(TC_right, 1, outchar);
589 --d_col;
590 }
591 TC_curr_col = col;
592 }
593
594 /* relative vertical motion */
595 if (d_line != 0) {
596 if (TC_up == NULL || TC_down == NULL)
597 goto do_absolute_motion;
598 while (d_line < 0) {
599 /* negative value => physical > logical => must move up */
600 tputs(TC_up, 1, outchar);
601 ++d_line;
602 }
603 while (d_line > 0) {
604 /* positive value => logical > physical => must move down */
605 tputs(TC_down, 1, outchar);
606 --d_line;
607 }
608 TC_curr_line = line;
609 }
610
611 assert(TC_curr_col == col);
612 assert(TC_curr_line == line);
613 }
614
615
ClearScreen()616 PUBLIC void ClearScreen()
617 {
618 assert(TC_clearscreen != NULL);
619 tputs(TC_clearscreen, 1, outchar);
620 TC_curr_line = TC_curr_col = 0;
621 FlushOutput();
622 }
623
624
CleartoEOLN()625 PUBLIC void CleartoEOLN()
626 {
627 assert(TC_cleartoeoln != NULL);
628 tputs(TC_cleartoeoln, 1, outchar);
629 FlushOutput();
630 }
631
632
CleartoEOS()633 PUBLIC void CleartoEOS()
634 {
635 assert(TC_cleartoeos != NULL);
636 tputs(TC_cleartoeos, 1, outchar);
637 FlushOutput();
638 }
639
640
StartStandout()641 PUBLIC void StartStandout()
642 {
643 assert(TC_start_standout != NULL);
644 tputs(TC_start_standout, 1, outchar);
645 FlushOutput();
646 }
647
648
EndStandout()649 PUBLIC void EndStandout()
650 {
651 assert(TC_end_standout != NULL);
652 tputs(TC_end_standout, 1, outchar);
653 FlushOutput();
654 }
655
656
WriteChar(ch)657 PUBLIC void WriteChar(ch)
658 int ch;
659 {
660 /** write a character to the current screen location. **/
661
662 static int wrappedlastchar = 0;
663 int justwrapped, nt;
664
665 ch &= 0xFF;
666 justwrapped = 0;
667
668 /* if return, just go to left column. */
669 if(ch == '\r') {
670 if (wrappedlastchar)
671 justwrapped = 1; /* preserve wrap flag */
672 else {
673 putchar('\r');
674 TC_curr_col = 0;
675 }
676 }
677
678 /* if newline and terminal just did a newline without our asking,
679 * do nothing, else output a newline and increment the line count */
680 else if (ch == '\n') {
681 if (!wrappedlastchar) {
682 putchar('\n');
683 if (TC_curr_line < LINES)
684 ++TC_curr_line;
685 }
686 }
687
688 /* if backspace, move back one space if not already in column 0 */
689 else if (ch == BACKSPACE) {
690 if (TC_curr_col != 0) {
691 assert(TC_left != NULL);
692 tputs(TC_left, 1, outchar);
693 TC_curr_col--;
694 }
695 else if (TC_curr_line > 0) {
696 TC_curr_col = COLS - 1;
697 TC_curr_line--;
698 tputs(tgoto(TC_moveto, TC_curr_col, TC_curr_line), 1, outchar);
699 }
700 /* else BACKSPACE does nothing */
701 }
702
703 /* if bell, ring the bell but don't advance the column */
704 else if (ch == '\007') {
705 putchar(ch);
706 }
707
708 /* if a tab, output it */
709 else if (ch == '\t') {
710 if ((nt = tabstop(TC_curr_col)) >= COLS) {
711 putchar('\r');
712 putchar('\n');
713 TC_curr_col = 0;
714 ++TC_curr_line;
715 } else {
716 if (TC_tabspacing == 8) {
717 putchar(ch);
718 TC_curr_col = nt;
719 } else {
720 while (TC_curr_col < nt) {
721 putchar(' ');
722 ++TC_curr_col;
723 }
724 }
725 }
726 }
727
728 else {
729 /* if some kind of non-printable character change to a '?' */
730 #ifdef ASCII_CTYPE
731 if(!isascii(ch) || !isprint(ch))
732 #else
733 if ( (!isprint(ch) && !(ch & ~0x7f)) || ch == 0226)
734 /* 0226 can mess up xterm */
735 #endif
736 ch = '?';
737
738 /* if we only have one column left, simulate automargins if
739 * the terminal doesn't have them */
740 if (TC_curr_col == COLS - 1) {
741 putchar(ch);
742 if (!TC_automargin || TC_eatnewlineglitch) {
743 putchar('\r');
744 putchar('\n');
745 }
746 if (TC_curr_line < LINES)
747 ++TC_curr_line;
748 TC_curr_col = 0;
749 justwrapped = 1;
750 }
751
752 /* if we are here this means we have no interference from the
753 * right margin - just output the character and increment the
754 * column position. */
755 else {
756 putchar(ch);
757 TC_curr_col++;
758 }
759 }
760
761 wrappedlastchar = justwrapped;
762 }
763
764
765 /*
766 * Insert a character at the current screen location, shifting the rest
767 * of the line right one column (char in last col is lost). Returns 0
768 * if insert successful, -1 if insert capability not available. Can be
769 * invoked with a negative value to check whether insert can be done
770 * on this terminal.
771 *
772 * This routine is guaranteed for only a *limited* set of circumstances.
773 * The character must be a plain printing character, and the insertion
774 * cannot occur in the last column of the line.
775 */
InsertChar(ch)776 PUBLIC void InsertChar(ch)
777 int ch;
778 {
779 assert(TC_start_insert || TC_char_insert);
780 assert(isprint(ch) && TC_curr_col < COLS-1);
781 if (TC_start_insert && *TC_start_insert)
782 tputs(TC_start_insert, 1, outchar);
783 if (TC_char_insert && *TC_char_insert)
784 tputs(TC_char_insert, 1, outchar);
785 outchar(ch);
786 ++TC_curr_col;
787 if (TC_pad_insert && *TC_pad_insert)
788 tputs(TC_pad_insert, 1, outchar);
789 if (TC_end_insert && *TC_end_insert)
790 tputs(TC_end_insert, 1, outchar);
791 }
792
793
794 /*
795 * Delete a number characters from the display starting at the current
796 * location, shifting the rest of the line left to fill in the deleted
797 * spaces. Returns 0 if deletion successful, -1 if delete capability
798 * not available. Can be invoked with a negative value to check whether
799 * delete can be done on this terminal.
800 */
DeleteChar(n)801 PUBLIC void DeleteChar(n)
802 int n;
803 {
804 assert (TC_char_delete && n > 0);
805 if (TC_start_delete && *TC_start_delete)
806 tputs(TC_start_delete, 1, outchar);
807 while (--n >= 0)
808 tputs(TC_char_delete, 1, outchar);
809 if (TC_end_delete && *TC_end_delete)
810 tputs(TC_end_delete, 1, outchar);
811 }
812
813
Raw(state)814 PUBLIC void Raw(state)
815 int state;
816 {
817 int do_tite;
818
819 do_tite = !(state & NO_TITE);
820 state &= ~NO_TITE;
821
822 /* don't bother if already in desired state */
823 if (!state == !(Term.status & TERM_IS_RAW))
824 return;
825
826 FlushOutput();
827 if (state) {
828 (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &TC_raw_tty);
829 if (do_tite && use_tite && TC_start_termcap)
830 tputs(TC_start_termcap, 1, outchar);
831 Term.status |= TERM_IS_RAW;
832 } else {
833 if (do_tite && use_tite && TC_end_termcap) {
834 tputs(TC_end_termcap, 1, outchar);
835 fflush(stdout);
836 }
837 (void) tcsetattr(STDIN_FILENO, TCSADRAIN, &TC_original_tty);
838 Term.status &= ~TERM_IS_RAW;
839 }
840 }
841
842
ReadCh()843 PUBLIC int ReadCh()
844 {
845 /*
846 * read a character with Raw mode set!
847 *
848 * EAGAIN & EWOULDBLOCK are recognized just in case
849 * O_NONBLOCK happens to be in effect.
850 */
851
852 int ch;
853
854 FlushOutput();
855 while ((ch = getchar()) <= 0) {
856 if (errno == EINTR)
857 continue;
858 #ifdef EAGAIN
859 if (errno == EAGAIN)
860 continue;
861 #endif
862 #ifdef EWOULDBLOCK
863 if (errno == EWOULDBLOCK)
864 continue;
865 #endif
866 ShutdownTerm();
867 if (feof(stdin))
868 error("Unexepcted EOF from terminal!");
869 else
870 error1("Error reading terminal! [%s]", strerror(errno));
871 leave(LEAVE_EMERGENCY);
872 }
873 return ch;
874 }
875
876
UnreadCh(ch)877 PUBLIC void UnreadCh(ch)
878 int ch;
879 {
880 ungetc(ch, stdin);
881 }
882
883
FlushOutput()884 PUBLIC void FlushOutput()
885 {
886 (void) fflush(stdout);
887 (void) fflush(stderr);
888 }
889
890
FlushInput()891 PUBLIC void FlushInput()
892 {
893 (void) fflush(stdin);
894 (void) tcflush(STDIN_FILENO, TCIFLUSH);
895 }
896
897
outchar(c)898 static int outchar(c)
899 int c;
900 {
901 /** output the given character. From tputs... **/
902 /** Note: this CANNOT be a macro! **/
903
904 return putc(c, stdout);
905 }
906
907
908
909 static void debug_termtitle P_((int, int, const char *));
910 static void debug_termstr P_((int, int, const char *, const char *));
911 static void debug_termnum P_((int, int, const char *, int));
912 static void debug_termflag P_((int, int, const char *, int));
913
debug_terminal()914 PUBLIC void debug_terminal()
915 {
916 int line;
917
918 ClearScreen();
919 CenterLine(0, "--- Debug Display - Terminal Settings ---");
920
921 line = 2;
922 debug_termstr(line, 0, "start_termcap", TC_start_termcap);
923 debug_termstr(line, 1, "end_termcap", TC_end_termcap);
924 ++line;
925 debug_termstr(line, 0, "start_standout", TC_start_standout);
926 debug_termstr(line, 1, "end_standout", TC_end_standout);
927 ++line;
928 debug_termstr(line, 0, "transmit_on", TC_transmit_on);
929 debug_termstr(line, 1, "transmit_off", TC_transmit_off);
930
931 line += 2;
932 debug_termflag(line, 0, "ic supported?", !!(Term.status & TERM_CAN_IC));
933 ++line;
934 debug_termstr(line, 0, "start_insert", TC_start_insert);
935 debug_termstr(line, 1, "end_insert", TC_end_insert);
936 ++line;
937 debug_termstr(line, 0, "char_insert", TC_char_insert);
938 debug_termstr(line, 1, "pad_insert", TC_pad_insert);
939
940 line += 2;
941 debug_termflag(line, 0, "dc supported?", !!(Term.status & TERM_CAN_DC));
942 ++line;
943 debug_termstr(line, 0, "start_delete", TC_start_delete);
944 debug_termstr(line, 1, "end_delete", TC_end_delete);
945 ++line;
946 debug_termstr(line, 0, "char_delete", TC_char_delete);
947
948 line += 2;
949 debug_termstr(line, 0, "moveto", TC_moveto);
950 ++line;
951 debug_termstr(line, 0, "up", TC_up);
952 debug_termstr(line, 1, "down", TC_down);
953 ++line;
954 debug_termstr(line, 0, "left", TC_left);
955 debug_termstr(line, 1, "right", TC_right);
956
957 line += 2;
958 debug_termnum(line, 0, "lines", TC_lines);
959 debug_termnum(line, 1, "columns", TC_columns);
960 ++line;
961 debug_termnum(line, 0, "tabspacing", TC_tabspacing);
962 }
963
964
debug_termtitle(line,col,title)965 static void debug_termtitle(line, col, title)
966 int line, col;
967 const char *title;
968 {
969 PutLine0(line, col*40, title);
970 MoveCursor(line, col*40+16);
971 return;
972 }
973
974
debug_termstr(line,col,title,value)975 static void debug_termstr(line, col, title, value)
976 int line, col;
977 const char *title, *value;
978 {
979 debug_termtitle(line, col, title);
980 if (value == NULL) {
981 PutLine0(-1, -1, "(null)");
982 return;
983 }
984 if (*value == '\0') {
985 PutLine0(-1, -1, "(empty)");
986 return;
987 }
988 for ( ; *value ; ++value) {
989 if (isprint(*value) || isspace(*value)) {
990 WriteChar(*value);
991 } else if (*value < 040) {
992 WriteChar('^');
993 WriteChar(*value | 0100);
994 } else {
995 PutLine1(-1, -1, "\\%03o", *value);
996 }
997 }
998 }
999
1000
debug_termnum(line,col,title,value)1001 static void debug_termnum(line, col, title, value)
1002 int line, col;
1003 const char *title;
1004 int value;
1005 {
1006 debug_termtitle(line, col, title);
1007 PutLine1(-1, -1, "%d", value);
1008 }
1009
1010
debug_termflag(line,col,title,value)1011 static void debug_termflag(line, col, title, value)
1012 int line, col;
1013 const char *title;
1014 int value;
1015 {
1016 debug_termtitle(line, col, title);
1017 PutLine0(-1, -1, (value ? "Yes" : "No"));
1018 }
1019
1020