/* * term.c: termcap stuff... * * Written By Michael Sandrof * HP-UX modifications by Mark T. Dame (Mark.Dame@uc.edu) * Termio modifications by Stellan Klebom (d88-skl@nada.kth.se) * Many, many cleanups, modifications, and some new broken code * added by Scott Reynolds (scottr@edsi.org), June 1995. * * Copyright (c) 1990 Michael Sandrof. * Copyright (c) 1991, 1992 Troy Rollo. * Copyright (c) 1992-2004 Matthew R. Green. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "irc.h" IRCII_RCSID("@(#)$eterna: term.c,v 1.95 2005/09/21 22:19:21 mrg Exp $"); #ifdef DGUX # define _SYSV3_BAUD_RATE_FLAVOR #endif /* DGUX */ #ifdef SVR3 # include # include # include # ifdef HAVE_SYS_PTEM_H # include # endif /* HAVE_SYS_PTEM_H */ # define CBREAK RAW #endif /* SVR3 */ #ifdef __SVR4 # include # include # include # include # include # define CBREAK RAW #endif /* __SVR4 */ #ifdef M_UNIX # include # include # include # include #endif /* M_UNIX */ #ifdef HAVE_SYS_IOCTL_H # include #endif /* HAVE_SYS_IOCTL_H */ #ifdef HAVE_TERMIOS_H # ifdef HPUX # define _INCLUDE_POSIX_SOURCE # endif /* HPUX */ # include #else # ifdef ISC # include # endif /* ISC */ # ifdef HAVE_SGTTY_H # include # define USE_SGTTY # else # ifdef HAVE_TERMIO_H # include # define termios termio # else # define USE_SGTTY # endif /* HAVE_TERMIO_H */ # endif /* HAVE_SGTTY_H */ #endif /* HPUX */ #include "ircterm.h" #include "translat.h" #ifdef ISC22 # undef TIOCSETC # undef TIOCGETC #endif /* ISC22 */ #if !defined(sun) && !defined(_IBMR2) # ifdef HAVE_SYS_TTOLD_H # include # endif /* HAVE_SYS_TTOLD_H */ #endif /* && !sun && !_IBMR2 */ /* Missing on ConvexOS, idea picked from SunOS */ #if defined(__convex__) && !defined(LPASS8) # define LPASS8 (L004000>>16) #endif /* __convex__ && !LPASS8 */ #include "window.h" #include "screen.h" #ifndef STTY_ONLY static int term_CE_clear_to_eol(void); static int term_CS_scroll(int, int, int); static int term_ALDL_scroll(int, int, int); static int term_param_ALDL_scroll(int, int, int); static int term_IC_insert(u_int); static int term_IMEI_insert(u_int); static int term_DC_delete(void); static int term_null_function(void); static int term_BS_cursor_left(void); static int term_LE_cursor_left(void); static int term_ND_cursor_right(void); #endif /* STTY_ONLY */ static int tty_des; /* descriptor for the tty */ #ifdef USE_SGTTY static struct tchars oldtchars, newtchars = { '\003', -1, -1, -1, -1, -1}; static struct sgttyb oldb, newb; # ifdef TIOCLSET static int old_local_modes, new_local_modes; # endif /* TIOCLSET */ #else static struct termios oldb, newb; #endif /* USE_SGTTY */ #ifndef STTY_ONLY #if defined(__CYGWIN__) || defined(__CYGWIN32__) #define TGETENT_BUFSIZ 2048 #else #define TGETENT_BUFSIZ 1024 #endif static char termcap[TGETENT_BUFSIZ]; /* * Function variables: each returns 1 if the function is not supported on the * current term type, otherwise they do their thing and return 0 */ int (*term_scroll)(int, int, int); /* best scroll available */ int (*term_insert)(u_int); /* best insert available */ int (*term_delete)(void); /* best delete available */ int (*term_cursor_left)(void); /* best left available */ int (*term_cursor_right)(void); /* best right available */ int (*term_clear_to_eol)(void); /* figure it out */ /* The termcap variables */ char *CM, *CE, *CL, *CR, *NL, *AL, *DL, *CS, *DC, *IC, *IM, *EI, *SO, *SE, *US, *UE, *MD, *ME, *SF, *SR, *ND, *LE, *BL, *TI, *TE; int SG; /* * term_reset_flag: set to true whenever the terminal is reset, thus letting * the calling program work out what to do */ int term_reset_flag = 0; static FILE *term_fp = 0; static int li, co; void term_set_fp(fp) FILE *fp; { term_fp = fp; } /* putchar_x: the putchar function used by tputs */ TPUTSRETVAL putchar_x(c) TPUTSARGVAL c; { fputc(c, term_fp); #ifndef TPUTSVOIDRET /* what the hell is this value used for anyway? */ return (0); #endif } void term_flush() { fflush(term_fp); } /* * term_reset: sets terminal attributed back to what they were before the * program started */ void term_reset() { #ifdef HAVE_TERMIOS_H tcsetattr(tty_des, TCSADRAIN, &oldb); #else # ifdef USE_SGTTY ioctl(tty_des, TIOCSETC, &oldtchars); ioctl(tty_des, TIOCSETP, &oldb); # ifdef TIOCLSET ioctl(tty_des, TIOCLSET, &old_local_modes); # endif /* TIOCLSET */ # else ioctl(tty_des, TCSETA, &oldb); # endif /* USE_SGTTY */ #endif /* HAVE_TERMIOS_H */ if (CS) tputs_x(tgoto(CS, current_screen->li - 1, 0)); if (!tflag && TE) tputs_x(TE); term_move_cursor(0, current_screen->li - 1); term_reset_flag = 1; term_flush(); } /* * term_cont: sets the terminal back to IRCII stuff when it is restarted * after a SIGSTOP. Somewhere, this must be used in a signal() call */ RETSIGTYPE term_cont(signo) int signo; { #ifdef SYSVSIGNALS (void) MY_SIGNAL(SIGCONT, term_cont, 0); /* sysv has dumb signals */ #endif /* SYSVSIGNALS */ #if defined(SIGSTOP) && defined(SIGTSTP) /* munix has no sigstop, sigtstp */ # ifdef HAVE_TERMIOS_H tcsetattr(tty_des, TCSADRAIN, &newb); # else # ifdef USE_SGTTY ioctl(tty_des, TIOCSETC, &newtchars); ioctl(tty_des, TIOCSETP, &newb); # ifdef TIOCLSET ioctl(tty_des, TIOCLSET, &new_local_modes); # endif /* TIOCLSET */ # else ioctl(tty_des, TCSETA, &newb); # endif /* USE_SGTTY */ # endif /* HAVE_TERMIOS_H */ #endif /* SIGSTOP && SIGTSTP */ if (!tflag && TI) tputs_x(TI); on_signal_occurred(signo); } /* * term_pause: sets terminal back to pre-program days, then SIGSTOPs itself. */ void term_pause(key, ptr) u_int key; u_char *ptr; { #if !defined(SIGSTOP) || !defined(SIGTSTP) || defined(_RT) say("The STOP_IRC function does not work on this system type."); #else term_reset(); kill(getpid(), SIGSTOP); #endif /* MUNIX */ } #endif /* STTY_ONLY */ /* * term_init: does all terminal initialization... reads termcap info, sets * the terminal to CBREAK, no ECHO mode. Chooses the best of the terminal * attributes to use .. for the version of this function that is called for * wserv, we set the termial to RAW, no ECHO, so that all the signals are * ignored.. fixes quite a few problems... -phone, jan 1993.. */ void term_init() { #ifndef STTY_ONLY char bp[TGETENT_BUFSIZ], *term, *ptr; if ((term = getenv("TERM")) == (char *) 0) { fprintf(stderr, "irc: No TERM variable set!\n"); fprintf(stderr,"irc: You may still run irc by using the -d switch\n"); exit(1); } if (tgetent(bp, term) < 1) { fprintf(stderr, "irc: No termcap entry for %s.\n", term); fprintf(stderr,"irc: You may still run irc by using the -d switch\n"); exit(1); } if ((co = tgetnum("co")) == -1) co = 80; if ((li = tgetnum("li")) == -1) li = 24; ptr = termcap; /* * Thanks to Max Bell (mbell@cie.uoregon.edu) for info about TVI * terminals and the sg terminal capability */ SG = tgetnum("sg"); CM = tgetstr("cm", &ptr); CL = tgetstr("cl", &ptr); if ((CM == (char *) 0) || (CL == (char *) 0)) { fprintf(stderr, "This terminal does not have the necessary capabilities to run IRCII\nin full screen mode. You may still run irc by using the -d switch\n"); exit(1); } if ((CR = tgetstr("cr", &ptr)) == (char *) 0) CR = "\r"; if ((NL = tgetstr("nl", &ptr)) == (char *) 0) NL = "\n"; if ((CE = tgetstr("ce", &ptr)) != NULL) term_clear_to_eol = term_CE_clear_to_eol; else term_clear_to_eol = term_null_function; TE = tgetstr("te", &ptr); if (!tflag && TE && (TI = tgetstr("ti", &ptr)) != (char *) 0 ) tputs_x(TI); else TE = TI = (char *) 0; /* if ((ND = tgetstr("nd", &ptr)) || (ND = tgetstr("kr", &ptr))) */ if ((ND = tgetstr("nd", &ptr)) != NULL) term_cursor_right = term_ND_cursor_right; else term_cursor_right = term_null_function; /* if ((LE = tgetstr("le", &ptr)) || (LE = tgetstr("kl", &ptr))) */ if ((LE = tgetstr("le", &ptr)) != NULL) term_cursor_left = term_LE_cursor_left; else if (tgetflag("bs")) term_cursor_left = term_BS_cursor_left; else term_cursor_left = term_null_function; SF = tgetstr("sf", &ptr); SR = tgetstr("sr", &ptr); if ((CS = tgetstr("cs", &ptr)) != NULL) term_scroll = term_CS_scroll; else if ((AL = tgetstr("AL", &ptr)) && (DL = tgetstr("DL", &ptr))) term_scroll = term_param_ALDL_scroll; else if ((AL = tgetstr("al", &ptr)) && (DL = tgetstr("dl", &ptr))) term_scroll = term_ALDL_scroll; else term_scroll = (int (*)(int, int, int)) term_null_function; if ((IC = tgetstr("ic", &ptr)) != NULL) term_insert = term_IC_insert; else { if ((IM = tgetstr("im", &ptr)) && (EI = tgetstr("ei", &ptr))) term_insert = term_IMEI_insert; else term_insert = (int (*)(u_int)) term_null_function; } if ((DC = tgetstr("dc", &ptr)) != NULL) term_delete = term_DC_delete; else term_delete = term_null_function; SO = tgetstr("so", &ptr); SE = tgetstr("se", &ptr); if ((SO == (char *) 0) || (SE == (char *) 0)) { SO = CP(empty_string); SE = CP(empty_string); } US = tgetstr("us", &ptr); UE = tgetstr("ue", &ptr); if ((US == (char *) 0) || (UE == (char *) 0)) { US = CP(empty_string); UE = CP(empty_string); } MD = tgetstr("md", &ptr); ME = tgetstr("me", &ptr); if ((MD == (char *) 0) || (ME == (char *) 0)) { MD = CP(empty_string); ME = CP(empty_string); } if ((BL = tgetstr("bl", &ptr)) == (char *) 0) BL = "\007"; #endif /* STTY_ONLY */ if (getenv("IRC_DEBUG")|| (tty_des = open("/dev/tty", O_RDWR, 0)) == -1) tty_des = 0; #ifdef HAVE_TERMIOS_H tcgetattr(tty_des, &oldb); #else # ifdef USE_SGTTY ioctl(tty_des, TIOCGETC, &oldtchars); ioctl(tty_des, TIOCGETP, &oldb); # else ioctl(tty_des, TCGETA, &oldb); # endif /* USE_SGTTY */ #endif /* HAVE_TERMIOS_H */ #ifdef USE_SGTTY newb = oldb; newb.sg_flags &= ~CRMOD; # ifdef TIOCLSET ioctl(tty_des, TIOCLGET, &old_local_modes); new_local_modes = old_local_modes | LDECCTQ | LLITOUT | LNOFLSH; ioctl(tty_des, TIOCLSET, &new_local_modes); # endif /* TIOCLSET */ # ifndef STTY_ONLY if (use_flow_control) { newtchars.t_startc = oldtchars.t_startc; newtchars.t_stopc = oldtchars.t_stopc; } newb.sg_flags |= CBREAK; # else newb.sg_flags |= RAW; # endif /* STTY_ONLY */ # if !defined(_HPUX_SOURCE) newb.sg_flags &= (~ECHO); # endif /* _HPUX_SOURCE */ #else /* USE_SGTTY */ newb = oldb; newb.c_lflag &= ~(ICANON | ECHO); /* set equivalent of * CBREAK and no ECHO */ newb.c_cc[VMIN] = 1; /* read() satified after 1 char */ newb.c_cc[VTIME] = 0; /* No timer */ #ifndef _POSIX_VDISABLE # define _POSIX_VDISABLE 0 #endif /* !_POSIX_VDISABLE */ newb.c_cc[VQUIT] = _POSIX_VDISABLE; # ifdef VDISCARD newb.c_cc[VDISCARD] = _POSIX_VDISABLE; # endif /* VDISCARD */ # ifdef VDSUSP newb.c_cc[VDSUSP] = _POSIX_VDISABLE; # endif /* VDSUSP */ # ifdef VSUSP newb.c_cc[VSUSP] = _POSIX_VDISABLE; # endif /* VSUSP */ # ifndef STTY_ONLY if (!use_flow_control) newb.c_iflag &= ~IXON; /* No XON/XOFF */ # endif /* STTY_ONLY */ #endif /* USE_SGTTY */ #ifdef HAVE_TERMIOS_H tcsetattr(tty_des, TCSADRAIN, &newb); #else # ifdef USE_SGTTY ioctl(tty_des, TIOCSETC, &newtchars); ioctl(tty_des, TIOCSETP, &newb); # else ioctl(tty_des, TCSETA, &newb); # endif /* USE_SGTTY */ #endif /* HAVE_TERMIOS_H */ } #ifndef STTY_ONLY /* * term_resize: gets the terminal height and width. Trys to get the info * from the tty driver about size, if it can't... uses the termcap values. If * the terminal size has changed since last time term_resize() has been * called, 1 is returned. If it is unchanged, 0 is returned. */ int term_resize() { /* * if we're not the main screen, we've probably arrived here via * the wserv message path, and we should have already setup the * values of "li" and "co". */ if (is_main_screen(current_screen)) { #ifndef TIOCGWINSZ current_screen->li = li; current_screen->co = co; #else struct winsize window; if (ioctl(tty_des, TIOCGWINSZ, &window) < 0) { current_screen->li = li; current_screen->co = co; } else { if ((current_screen->li = window.ws_row) == 0) current_screen->li = li; if ((current_screen->co = window.ws_col) == 0) current_screen->co = co; } #endif /* TIOCGWINSZ */ current_screen->co--; } if ((current_screen->old_term_li != current_screen->li) || (current_screen->old_term_co != current_screen->co)) { current_screen->old_term_li = current_screen->li; current_screen->old_term_co = current_screen->co; return (1); } return (0); } /* * term_null_function: used when a terminal is missing a particulary useful * feature, such as scrolling, to warn the calling program that no such * function exists */ static int term_null_function() { return (1); } /* term_CE_clear_to_eol(): the clear to eol function, right? */ static int term_CE_clear_to_eol() { tputs_x(CE); return (0); } /* * term_space_erase: this can be used if term_CE_clear_to_eol() returns 1. * This will erase from x to the end of the screen uses space. Actually, it * doesn't reposition the cursor at all, so the cursor must be in the correct * spot at the beginning and you must move it back afterwards */ void term_space_erase(x) int x; { int i, cnt; cnt = current_screen->co - x; for (i = 0; i < cnt; i++) fputc(' ', term_fp); } /* * term_CS_scroll: should be used if the terminal has the CS capability by * setting term_scroll equal to it */ static int term_CS_scroll(line1, line2, n) int line1, line2, n; { int i; u_char *thing; if (n > 0) thing = UP(SF ? SF : NL); else if (n < 0) { if (SR) thing = UP(SR); else return 1; } else return 0; tputs_x(tgoto(CS, line2, line1)); /* shouldn't do this each time */ if (n < 0) { term_move_cursor(0, line1); n = -n; } else term_move_cursor(0, line2); for (i = 0; i < n; i++) tputs_x(CP(thing)); tputs_x(tgoto(CS, current_screen->li - 1, 0)); /* shouldn't do this each time */ return (0); } /* * term_ALDL_scroll: should be used for scrolling if the term has AL and DL * by setting the term_scroll function to it */ static int term_ALDL_scroll(line1, line2, n) int line1, line2, n; { int i; if (n > 0) { term_move_cursor(0, line1); for (i = 0; i < n; i++) tputs_x(DL); term_move_cursor(0, line2 - n + 1); for (i = 0; i < n; i++) tputs_x(AL); } else if (n < 0) { n = -n; term_move_cursor(0, line2-n+1); for (i=0; i < n; i++) tputs_x(DL); term_move_cursor(0, line1); for (i=0; i < n; i++) tputs_x(AL); } return (0); } /* * term_param_ALDL_scroll: Uses the parameterized version of AL and DL */ static int term_param_ALDL_scroll(line1, line2, n) int line1, line2, n; { if (n > 0) { term_move_cursor(0, line1); tputs_x(tgoto(DL, n, n)); term_move_cursor(0, line2 - n + 1); tputs_x(tgoto(AL, n, n)); } else if (n < 0) { n = -n; term_move_cursor(0, line2-n+1); tputs_x(tgoto(DL, n, n)); term_move_cursor(0, line1); tputs_x(tgoto(AL, n, n)); } return (0); } /* * term_IC_insert: should be used for character inserts if the term has IC by * setting term_insert to it. */ static int term_IC_insert(c) u_int c; { tputs_x(IC); putchar_x(c); return (0); } /* * term_IMEI_insert: should be used for character inserts if the term has IM * and EI by setting term_insert to it */ static int term_IMEI_insert(c) u_int c; { tputs_x(IM); putchar_x(c); tputs_x(EI); return (0); } /* * term_DC_delete: should be used for character deletes if the term has DC by * setting term_delete to it */ static int term_DC_delete() { tputs_x(DC); return (0); } /* term_ND_cursor_right: got it yet? */ static int term_ND_cursor_right() { tputs_x(ND); return (0); } /* term_LE_cursor_left: shouldn't you move on to something else? */ static int term_LE_cursor_left() { tputs_x(LE); return (0); } static int term_BS_cursor_left() { fputc('\010', term_fp); return (0); } extern void copy_window_size(nlines, cols) int *nlines, *cols; { *nlines = li; *cols = co; } extern int term_eight_bit() { #ifdef USE_SGTTY return (old_local_modes & LPASS8) ? 1 : 0; #else return (((oldb.c_cflag) & CSIZE) == CS8) ? 1 : 0; #endif /* USE_SGTTY */ } #endif /* STTY_ONLY */ extern void set_term_eight_bit(value) int value; { #ifndef STTY_ONLY if (dumb) return; #endif /* STTY_ONLY */ #ifdef USE_SGTTY if (value == ON) new_local_modes |= LPASS8; else new_local_modes &= ~LPASS8; ioctl(tty_des, TIOCLSET, &new_local_modes); #else if (value == ON) { newb.c_cflag |= CS8; newb.c_iflag &= ~ISTRIP; } else { newb.c_cflag &= ~CS8; newb.c_iflag |= ISTRIP; } # ifdef HAVE_TERMIOS_H tcsetattr(tty_des, TCSADRAIN, &newb); # else ioctl(tty_des, TCSETA, &newb); # endif /* HAVE_TERMIOS_H */ #endif /* USE_SGTTY */ }