1 /*
2 * term.c: termcap stuff...
3 *
4 * Written By Michael Sandrof
5 * HP-UX modifications by Mark T. Dame (Mark.Dame@uc.edu)
6 * Termio modifications by Stellan Klebom (d88-skl@nada.kth.se)
7 * Many, many cleanups, modifications, and some new broken code
8 * added by Scott Reynolds (scottr@edsi.org), June 1995.
9 *
10 * Copyright (c) 1990 Michael Sandrof.
11 * Copyright (c) 1991, 1992 Troy Rollo.
12 * Copyright (c) 1992-2014 Matthew R. Green.
13 * All rights reserved.
14 *
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
17 * are met:
18 * 1. Redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer.
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in the
22 * documentation and/or other materials provided with the distribution.
23 * 3. The name of the author may not be used to endorse or promote products
24 * derived from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
27 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
33 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
34 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39 #include "irc.h"
40 IRCII_RCSID("@(#)$eterna: term.c,v 1.118 2020/11/17 08:11:57 mrg Exp $");
41
42 #ifdef HAVE_SYS_IOCTL_H
43 # include <sys/ioctl.h>
44 #endif /* HAVE_SYS_IOCTL_H */
45
46 #include <sys/stat.h>
47 #include <termios.h>
48
49 #ifndef CBREAK
50 # define CBREAK RAW
51 #endif
52
53 #include "ircterm.h"
54 #include "translat.h"
55 #include "window.h"
56 #include "screen.h"
57 #include "output.h"
58
59 #ifndef STTY_ONLY
60 static int term_CE_clear_to_eol(void);
61 static int term_CS_scroll(int, int, int);
62 static int term_ALDL_scroll(int, int, int);
63 static int term_param_ALDL_scroll(int, int, int);
64 static int term_IC_insert(u_int);
65 static int term_IMEI_insert(u_int);
66 static int term_DC_delete(void);
67 static int term_null_function(void);
68 static int term_BS_cursor_left(void);
69 static int term_LE_cursor_left(void);
70 static int term_ND_cursor_right(void);
71 static void tputs_x(const char *);
72 #endif /* STTY_ONLY */
73
74
75 static int tty_des; /* descriptor for the tty */
76
77 static struct termios oldb,
78 newb;
79
80 #ifndef STTY_ONLY
81
82 #if defined(__CYGWIN__) || defined(__CYGWIN32__)
83 #define TGETENT_BUFSIZ 2048
84 #else
85 #define TGETENT_BUFSIZ 1024
86 #endif
87
88 static char termcap[TGETENT_BUFSIZ];
89
90 /*
91 * Function variables: each returns 1 if the function is not supported on the
92 * current term type, otherwise they do their thing and return 0
93 */
94 static int (*term_scroll_func)(int, int, int); /* best scroll */
95 static int (*term_insert_func)(u_int); /* best insert */
96 static int (*term_delete_func)(void); /* best delete */
97 static int (*term_cursor_left_func)(void); /* best left */
98 static int (*term_cursor_right_func)(void); /* best right */
99 static int (*term_clear_to_eol_func)(void); /* figure it out */
100
101 /* The termcap variables */
102 static char *CM,
103 *CE,
104 *CL,
105 *CR,
106 *NL,
107 *AL,
108 *DL,
109 *CS,
110 *DC,
111 *IC,
112 *IM,
113 *EI,
114 *SO,
115 *SE,
116 *US,
117 *UE,
118 *MD,
119 *ME,
120 *SF,
121 *SR,
122 *ND,
123 *LE,
124 *BL,
125 *TI,
126 *TE;
127 static int SG;
128
129 /*
130 * term_reset_flag: set to true whenever the terminal is reset, thus letting
131 * the calling program work out what to do
132 */
133 static int term_reset_flag = 0;
134
135 static FILE *term_fp = 0;
136 static int li, co;
137
138 void
term_set_fp(FILE * fp)139 term_set_fp(FILE *fp)
140 {
141 term_fp = fp;
142 }
143
144 /* putchar_x: the putchar function used by tputs */
145 TPUTSRETVAL
putchar_x(TPUTSARGVAL c)146 putchar_x(TPUTSARGVAL c)
147 {
148 fputc(c, term_fp);
149 #ifndef TPUTSVOIDRET /* what the hell is this value used for anyway? */
150 return (0);
151 #endif
152 }
153
154 void
term_flush(void)155 term_flush(void)
156 {
157 fflush(term_fp);
158 }
159
160 /*
161 * term_reset: sets terminal attributed back to what they were before the
162 * program started
163 */
164 void
term_reset(void)165 term_reset(void)
166 {
167 tcsetattr(tty_des, TCSADRAIN, &oldb);
168
169 if (CS)
170 tputs_x(tgoto(CS, get_li() - 1, 0));
171 if (!use_termcap_enterexit() && TE)
172 tputs_x(TE);
173 term_move_cursor(0, get_li() - 1);
174 term_reset_flag = 1;
175 term_flush();
176 }
177
178 /*
179 * term_cont: sets the terminal back to IRCII stuff when it is restarted
180 * after a SIGSTOP. Somewhere, this must be used in a signal() call
181 */
182 void
term_cont(int signo)183 term_cont(int signo)
184 {
185 tcsetattr(tty_des, TCSADRAIN, &newb);
186
187 if (!use_termcap_enterexit() && TI)
188 tputs_x(TI);
189
190 on_signal_occurred(signo);
191 }
192
193 /*
194 * term_pause: sets terminal back to pre-program days, then SIGSTOPs itself.
195 */
196 void
term_pause(u_int key,u_char * ptr)197 term_pause(u_int key, u_char *ptr)
198 {
199 #if !defined(SIGSTOP) || !defined(SIGTSTP)
200 say("The STOP_IRC function does not work on this system type.");
201 #else
202 term_reset();
203 kill(getpid(), SIGSTOP);
204 #endif /* !SIGSTOP */
205 }
206 #endif /* STTY_ONLY */
207
208 /*
209 * term_init: does all terminal initialization... reads termcap info, sets
210 * the terminal to CBREAK, no ECHO mode. Chooses the best of the terminal
211 * attributes to use .. for the version of this function that is called for
212 * wserv, we set the termial to RAW, no ECHO, so that all the signals are
213 * ignored.. fixes quite a few problems... -phone, jan 1993..
214 */
215 void
term_init(void)216 term_init(void)
217 {
218 #ifndef STTY_ONLY
219 char bp[TGETENT_BUFSIZ],
220 *term,
221 *ptr;
222
223 if ((term = getenv("TERM")) == NULL)
224 {
225 fprintf(stderr, "irc: No TERM variable set!\n");
226 fprintf(stderr,"irc: You may still run irc by using the -d switch\n");
227 exit(1);
228 }
229 if (tgetent(bp, term) < 1)
230 {
231 fprintf(stderr, "irc: No termcap entry for %s.\n", term);
232 fprintf(stderr,"irc: You may still run irc by using the -d switch\n");
233 exit(1);
234 }
235
236 if ((co = tgetnum("co")) == -1)
237 co = 80;
238 if ((li = tgetnum("li")) == -1)
239 li = 24;
240 ptr = termcap;
241
242 /*
243 * Thanks to Max Bell (mbell@cie.uoregon.edu) for info about TVI
244 * terminals and the sg terminal capability
245 */
246 SG = tgetnum("sg");
247 CM = tgetstr("cm", &ptr);
248 CL = tgetstr("cl", &ptr);
249 if ((CM == NULL) ||
250 (CL == NULL))
251 {
252 fprintf(stderr, "This terminal does not have the necessary "
253 "capabilities to run IRCII\n"
254 "in full screen mode. You may still run "
255 "irc by using the -d switch\n");
256 exit(1);
257 }
258 if ((CR = tgetstr("cr", &ptr)) == NULL)
259 CR = "\r";
260 if ((NL = tgetstr("nl", &ptr)) == NULL)
261 NL = "\n";
262
263 if ((CE = tgetstr("ce", &ptr)) != NULL)
264 term_clear_to_eol_func = term_CE_clear_to_eol;
265 else
266 term_clear_to_eol_func = term_null_function;
267
268 TE = tgetstr("te", &ptr);
269 if (!use_termcap_enterexit() && TE && (TI = tgetstr("ti", &ptr)) != NULL )
270 tputs_x(TI);
271 else
272 TE = TI = NULL;
273
274 /* if ((ND = tgetstr("nd", &ptr)) || (ND = tgetstr("kr", &ptr))) */
275 if ((ND = tgetstr("nd", &ptr)) != NULL)
276 term_cursor_right_func = term_ND_cursor_right;
277 else
278 term_cursor_right_func = term_null_function;
279
280 /* if ((LE = tgetstr("le", &ptr)) || (LE = tgetstr("kl", &ptr))) */
281 if ((LE = tgetstr("le", &ptr)) != NULL)
282 term_cursor_left_func = term_LE_cursor_left;
283 else if (tgetflag("bs"))
284 term_cursor_left_func = term_BS_cursor_left;
285 else
286 term_cursor_left_func = term_null_function;
287
288 SF = tgetstr("sf", &ptr);
289 SR = tgetstr("sr", &ptr);
290
291 if ((CS = tgetstr("cs", &ptr)) != NULL)
292 term_scroll_func = term_CS_scroll;
293 else if ((AL = tgetstr("AL", &ptr)) && (DL = tgetstr("DL", &ptr)))
294 term_scroll_func = term_param_ALDL_scroll;
295 else if ((AL = tgetstr("al", &ptr)) && (DL = tgetstr("dl", &ptr)))
296 term_scroll_func = term_ALDL_scroll;
297 else
298 term_scroll_func = (int (*)(int, int, int)) term_null_function;
299
300 if ((IC = tgetstr("ic", &ptr)) != NULL)
301 term_insert_func = term_IC_insert;
302 else
303 {
304 if ((IM = tgetstr("im", &ptr)) && (EI = tgetstr("ei", &ptr)))
305 term_insert_func = term_IMEI_insert;
306 else
307 term_insert_func = (int (*)(u_int)) term_null_function;
308 }
309
310 if ((DC = tgetstr("dc", &ptr)) != NULL)
311 term_delete_func = term_DC_delete;
312 else
313 term_delete_func = term_null_function;
314
315 SO = tgetstr("so", &ptr);
316 SE = tgetstr("se", &ptr);
317 if ((SO == NULL) || (SE == NULL))
318 {
319 SO = CP(empty_string());
320 SE = CP(empty_string());
321 }
322 US = tgetstr("us", &ptr);
323 UE = tgetstr("ue", &ptr);
324 if ((US == NULL) || (UE == NULL))
325 {
326 US = CP(empty_string());
327 UE = CP(empty_string());
328 }
329 MD = tgetstr("md", &ptr);
330 ME = tgetstr("me", &ptr);
331 if ((MD == NULL) || (ME == NULL))
332 {
333 MD = CP(empty_string());
334 ME = CP(empty_string());
335 }
336 if ((BL = tgetstr("bl", &ptr)) == NULL)
337 BL = "\007";
338 #endif /* STTY_ONLY */
339
340 if (getenv("IRC_DEBUG")|| (tty_des = open("/dev/tty", O_RDWR, 0)) == -1)
341 tty_des = 0;
342
343 tcgetattr(tty_des, &oldb);
344
345 newb = oldb;
346 newb.c_lflag &= ~(ICANON | ECHO); /* set equivalent of
347 * CBREAK and no ECHO
348 */
349 newb.c_cc[VMIN] = 1; /* read() satified after 1 char */
350 newb.c_cc[VTIME] = 0; /* No timer */
351
352 #ifndef _POSIX_VDISABLE
353 # define _POSIX_VDISABLE 0
354 #endif
355
356 newb.c_cc[VQUIT] = _POSIX_VDISABLE;
357 #ifdef VDISCARD
358 newb.c_cc[VDISCARD] = _POSIX_VDISABLE;
359 #endif
360 #ifdef VDSUSP
361 newb.c_cc[VDSUSP] = _POSIX_VDISABLE;
362 #endif
363 #ifdef VSUSP
364 newb.c_cc[VSUSP] = _POSIX_VDISABLE;
365 #endif
366
367 #ifndef STTY_ONLY
368 if (!use_flow_control())
369 newb.c_iflag &= ~IXON; /* No XON/XOFF */
370 #endif /* STTY_ONLY */
371
372 tcsetattr(tty_des, TCSADRAIN, &newb);
373 }
374
375
376 #ifndef STTY_ONLY
377 /*
378 * term_resize: gets the terminal height and width. Trys to get the info
379 * from the tty driver about size, if it can't... uses the termcap values. If
380 * the terminal size has changed since last time term_resize() has been
381 * called, 1 is returned. If it is unchanged, 0 is returned.
382 */
383 int
term_resize(void)384 term_resize(void)
385 {
386 int new_li = li, new_co = co;
387
388 /*
389 * if we're not the main screen, we've probably arrived here via
390 * the wserv message path, and we should have already setup the
391 * values of "li" and "co".
392 */
393 if (is_main_screen(get_current_screen()))
394 {
395 #ifdef TIOCGWINSZ
396 struct winsize window;
397
398 if (ioctl(tty_des, TIOCGWINSZ, &window) == 0)
399 {
400 new_li = window.ws_row;
401 new_co = window.ws_col;
402 }
403 #endif /* TIOCGWINSZ */
404 #ifndef TERM_USE_LAST_COLUMN
405 new_co--;
406 #endif
407 }
408 return screen_set_size(new_li, new_co);
409 }
410
411 /*
412 * term_null_function: used when a terminal is missing a particulary useful
413 * feature, such as scrolling, to warn the calling program that no such
414 * function exists
415 */
416 static int
term_null_function(void)417 term_null_function(void)
418 {
419 return (1);
420 }
421
422 /* term_CE_clear_to_eol(): the clear to eol function, right? */
423 static int
term_CE_clear_to_eol(void)424 term_CE_clear_to_eol(void)
425 {
426 tputs_x(CE);
427 return (0);
428 }
429
430 /* * term_space_erase: this can be used if term_CE_clear_to_eol() returns 1.
431 * This will erase from x to the end of the screen uses space. Actually, it
432 * doesn't reposition the cursor at all, so the cursor must be in the correct
433 * spot at the beginning and you must move it back afterwards
434 */
435 void
term_space_erase(int x)436 term_space_erase(int x)
437 {
438 int i,
439 cnt;
440
441 cnt = get_co() - x;
442 for (i = 0; i < cnt; i++)
443 fputc(' ', term_fp);
444 }
445
446 /*
447 * term_CS_scroll: should be used if the terminal has the CS capability by
448 * setting term_scroll equal to it
449 */
450 static int
term_CS_scroll(int line1,int line2,int n)451 term_CS_scroll(int line1, int line2, int n)
452 {
453 int i;
454 u_char *thing;
455
456 if (n > 0)
457 thing = UP(SF ? SF : NL);
458 else if (n < 0)
459 {
460 if (SR)
461 thing = UP(SR);
462 else
463 return 1;
464 }
465 else
466 return 0;
467 tputs_x(tgoto(CS, line2, line1)); /* shouldn't do this each time */
468 if (n < 0)
469 {
470 term_move_cursor(0, line1);
471 n = -n;
472 }
473 else
474 term_move_cursor(0, line2);
475 for (i = 0; i < n; i++)
476 tputs_x(CP(thing));
477 tputs_x(tgoto(CS, get_li() - 1, 0)); /* shouldn't do this each time */
478 return (0);
479 }
480
481 /*
482 * term_ALDL_scroll: should be used for scrolling if the term has AL and DL
483 * by setting the term_scroll function to it
484 */
485 static int
term_ALDL_scroll(int line1,int line2,int n)486 term_ALDL_scroll(int line1, int line2, int n)
487 {
488 int i;
489
490 if (n > 0)
491 {
492 term_move_cursor(0, line1);
493 for (i = 0; i < n; i++)
494 tputs_x(DL);
495 term_move_cursor(0, line2 - n + 1);
496 for (i = 0; i < n; i++)
497 tputs_x(AL);
498 }
499 else if (n < 0)
500 {
501 n = -n;
502 term_move_cursor(0, line2-n+1);
503 for (i = 0; i < n; i++)
504 tputs_x(DL);
505 term_move_cursor(0, line1);
506 for (i = 0; i < n; i++)
507 tputs_x(AL);
508 }
509 return (0);
510 }
511
512 /*
513 * term_param_ALDL_scroll: Uses the parameterized version of AL and DL
514 */
515 static int
term_param_ALDL_scroll(int line1,int line2,int n)516 term_param_ALDL_scroll(int line1, int line2, int n)
517 {
518 if (n > 0)
519 {
520 term_move_cursor(0, line1);
521 tputs_x(tgoto(DL, n, n));
522 term_move_cursor(0, line2 - n + 1);
523 tputs_x(tgoto(AL, n, n));
524 }
525 else if (n < 0)
526 {
527 n = -n;
528 term_move_cursor(0, line2-n+1);
529 tputs_x(tgoto(DL, n, n));
530 term_move_cursor(0, line1);
531 tputs_x(tgoto(AL, n, n));
532 }
533 return (0);
534 }
535
536 /*
537 * term_IC_insert: should be used for character inserts if the term has IC by
538 * setting term_insert to it.
539 */
540 static int
term_IC_insert(u_int c)541 term_IC_insert(u_int c)
542 {
543 tputs_x(IC);
544 putchar_x(c);
545 return (0);
546 }
547
548 /*
549 * term_IMEI_insert: should be used for character inserts if the term has IM
550 * and EI by setting term_insert to it
551 */
552 static int
term_IMEI_insert(u_int c)553 term_IMEI_insert(u_int c)
554 {
555 tputs_x(IM);
556 putchar_x(c);
557 tputs_x(EI);
558 return (0);
559 }
560
561 /*
562 * term_DC_delete: should be used for character deletes if the term has DC by
563 * setting term_delete to it
564 */
565 static int
term_DC_delete(void)566 term_DC_delete(void)
567 {
568 tputs_x(DC);
569 return (0);
570 }
571
572 /* term_ND_cursor_right: got it yet? */
573 static int
term_ND_cursor_right(void)574 term_ND_cursor_right(void)
575 {
576 tputs_x(ND);
577 return (0);
578 }
579
580 /* term_LE_cursor_left: shouldn't you move on to something else? */
581 static int
term_LE_cursor_left(void)582 term_LE_cursor_left(void)
583 {
584 tputs_x(LE);
585 return (0);
586 }
587
588 static int
term_BS_cursor_left(void)589 term_BS_cursor_left(void)
590 {
591 fputc('\010', term_fp);
592 return (0);
593 }
594
595 void
copy_window_size(int * nlines,int * cols)596 copy_window_size(int *nlines, int *cols)
597 {
598 if (nlines)
599 *nlines = get_li();
600 if (cols)
601 *cols = get_co();
602 }
603
604 static void
tputs_x(const char * s)605 tputs_x(const char *s)
606 {
607 tputs(s, 0, putchar_x);
608 }
609
610 void
term_underline_on(void)611 term_underline_on(void)
612 {
613 tputs_x(US);
614 }
615
616 void
term_underline_off(void)617 term_underline_off(void)
618 {
619 tputs_x(UE);
620 }
621
622 void
term_standout_on(void)623 term_standout_on(void)
624 {
625 tputs_x(SO);
626 }
627
628 void
term_standout_off(void)629 term_standout_off(void)
630 {
631 tputs_x(SE);
632 }
633
634 void
term_clear_screen(void)635 term_clear_screen(void)
636 {
637 tputs_x(CL);
638 }
639
640 void
term_move_cursor(int c,int r)641 term_move_cursor(int c, int r)
642 {
643 tputs_x(tgoto(CM, c, r));
644 }
645
646 void
term_cr(void)647 term_cr(void)
648 {
649 tputs_x(CR);
650 }
651
652 void
term_newline(void)653 term_newline(void)
654 {
655 tputs_x(NL);
656 }
657
658 void
term_beep(void)659 term_beep(void)
660 {
661 tputs_x(BL);
662 Screen *screen = get_current_screen();
663
664 fflush(screen ? screen_get_fpout(screen) : stdout);
665 }
666
667 void
term_bold_on(void)668 term_bold_on(void)
669 {
670 tputs_x(MD);
671 }
672
673 void
term_bold_off(void)674 term_bold_off(void)
675 {
676 tputs_x(ME);
677 }
678
679 int
term_eight_bit(void)680 term_eight_bit(void)
681 {
682 return (((oldb.c_cflag) & CSIZE) == CS8) ? 1 : 0;
683 }
684 #endif /* STTY_ONLY */
685
686 void
set_term_eight_bit(int value)687 set_term_eight_bit(int value)
688 {
689 #ifndef STTY_ONLY
690 if (term_basic())
691 return;
692 #endif /* STTY_ONLY */
693 if (value == ON)
694 {
695 newb.c_cflag |= CS8;
696 newb.c_iflag &= ~ISTRIP;
697 }
698 else
699 {
700 newb.c_cflag &= ~CS8;
701 newb.c_iflag |= ISTRIP;
702 }
703 tcsetattr(tty_des, TCSADRAIN, &newb);
704 }
705
706 #ifndef STTY_ONLY
707 void
term_check_refresh(void)708 term_check_refresh(void)
709 {
710 if (term_reset_flag)
711 {
712 refresh_screen(0, NULL);
713 term_reset_flag = 0;
714 }
715 }
716
717 int
term_scroll(int line1,int line2,int n)718 term_scroll(int line1, int line2, int n)
719 {
720 return (*term_scroll_func)(line1, line2, n);
721 }
722
723 int
term_insert(u_int c)724 term_insert(u_int c)
725 {
726 return (*term_insert_func)(c);
727 }
728
729 int
term_delete(void)730 term_delete(void)
731 {
732 return (*term_delete_func)();
733 }
734
735 int
term_cursor_right(void)736 term_cursor_right(void)
737 {
738 return (*term_cursor_right_func)();
739 }
740
741 int
term_cursor_left(void)742 term_cursor_left(void)
743 {
744 return (*term_cursor_left_func)();
745 }
746
747 int
term_clear_to_eol(void)748 term_clear_to_eol(void)
749 {
750 return (*term_clear_to_eol_func)();
751 }
752 #endif /* STTY_ONLY */
753