Files
inetutils/telnetd/term.c
2025-01-01 18:21:25 +01:00

775 lines
13 KiB
C

/*
Copyright (C) 2002-2025 Free Software Foundation, Inc.
This file is part of GNU Inetutils.
GNU Inetutils is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
GNU Inetutils is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see `http://www.gnu.org/licenses/'. */
#include <config.h>
#include <telnetd.h>
#include <fcntl.h>
typedef struct termios TERMDESC;
#define _term_getattr tcgetattr
#define _term_setattr(fd, tp) tcsetattr (fd, TCSANOW, tp)
TERMDESC termbuf, termbuf2;
#ifdef IOCTL_INTERFACE
void
_term_setattr (int fd, TERMDESC *tp)
{
ioctl (fd, TIOCSETN, (char *) &tp->sg);
ioctl (fd, TIOCSETC, (char *) &tp->tc);
ioctl (fd, TIOCSLTC, (char *) &tp->ltc);
ioctl (fd, TIOCLSET, &tp->lflags);
}
void
term_send_eof (void)
{
/*nothing */
}
int
term_change_eof (void)
{
return 0;
}
int
tty_linemode (void)
{
return termbuf.state & TS_EXTPROC;
}
void
tty_setlinemode (int on)
{
# ifdef TIOCEXT
set_termbuf ();
ioctl (pty, TIOCEXT, (char *) &on);
init_termbuf ();
# else/* !TIOCEXT */
# ifdef EXTPROC
if (on)
termbuf.c_lflag |= EXTPROC;
else
termbuf.c_lflag &= ~EXTPROC;
# endif
# endif/* TIOCEXT */
}
int
tty_isecho (void)
{
return termbuf.sg.sg_flags & ECHO;
}
int
tty_flowmode (void)
{
return ((termbuf.tc.t_startc) > 0 && (termbuf.tc.t_stopc) > 0) ? 1 : 0;
}
int
tty_restartany (void)
{
# ifdef DECCTQ
return (termbuf.lflags & DECCTQ) ? 0 : 1;
# else
return -1;
# endif
}
void
tty_setecho (int on)
{
if (on)
termbuf.sg.sg_flags |= ECHO | CRMOD;
else
termbuf.sg.sg_flags &= ~(ECHO | CRMOD);
}
int
tty_israw (void)
{
return termbuf.sg.sg_flags & RAW;
}
# if defined AUTHENTICATION && defined NO_LOGIN_F && defined LOGIN_R
int
tty_setraw (int on)
{
if (on)
termbuf.sg.sg_flags |= RAW;
else
termbuf.sg.sg_flags &= ~RAW;
}
# endif
void
tty_binaryin (int on)
{
if (on)
termbuf.lflags |= LPASS8;
else
termbuf.lflags &= ~LPASS8;
}
void
tty_binaryout (int on)
{
if (on)
termbuf.lflags |= LLITOUT;
else
termbuf.lflags &= ~LLITOUT;
}
int
tty_isbinaryin (void)
{
return termbuf.lflags & LPASS8;
}
int
tty_isbinaryout (void)
{
return termbuf.lflags & LLITOUT;
}
int
tty_isediting (void)
{
return !(termbuf.sg.sg_flags & (CBREAK | RAW));
}
int
tty_istrapsig (void)
{
return !(termbuf.sg.sg_flags & RAW);
}
void
tty_setedit (int on)
{
if (on)
termbuf.sg.sg_flags &= ~CBREAK;
else
termbuf.sg.sg_flags |= CBREAK;
}
void
tty_setsig (int on)
{
}
int
tty_issofttab (void)
{
return termbuf.sg.sg_flags & XTABS;
}
void
tty_setsofttab (int on)
{
if (on)
termbuf.sg.sg_flags |= XTABS;
else
termbuf.sg.sg_flags &= ~XTABS;
}
int
tty_islitecho (void)
{
return !(termbuf.lflags & LCTLECH);
}
void
tty_setlitecho (int on)
{
if (on)
termbuf.lflags &= ~LCTLECH;
else
termbuf.lflags |= LCTLECH;
}
int
tty_iscrnl (void)
{
return termbuf.sg.sg_flags & CRMOD;
}
#else /* !IOCTL_INTERFACE */
# define termdesc_eofc c_cc[VEOF]
# define termdesc_erase c_cc[VERASE]
# define termdesc_kill c_cc[VKILL]
# define termdesc_ip c_cc[VINTR]
# define termdesc_abort c_cc[VQUIT]
# ifdef VSTART
# define termdesc_xon c_cc[VSTART]
# endif
# ifdef VSTOP
# define termdesc_xoff c_cc[VSTOP]
# endif
# if !defined VDISCARD && defined VFLUSHO
# define VDISCARD VFLUSHO
# endif
# ifdef VDISCARD
# define termdesc_ao c_cc[VDISCARD]
# endif
# ifdef VSUSP
# define termdesc_susp c_cc[VSUSP]
# endif
# ifdef VWERASE
# define termdesc_ew c_cc[VWERASE]
# endif
# ifdef VREPRINT
# define termdesc_rp c_cc[VREPRINT]
# endif
# ifdef VLNEXT
# define termdesc_lnext c_cc[VLNEXT]
# endif
# ifdef VEOL
# define termdesc_forw1 c_cc[VEOL]
# endif
# ifdef VEOL2
# define termdesc_forw2 c_cc[VEOL2]
# endif
# ifdef VSTATUS
# define termdesc_status c_cc[VSTATUS]
# endif
# if VEOF == VMIN
static cc_t oldeofc = '\004';
# endif
void
term_send_eof (void)
{
# if VEOF == VMIN
if (!tty_isediting ())
pty_output_byte (oldeofc);
# endif
}
int
term_change_eof (void)
{
# if VEOF == VMIN
if (!tty_isediting ())
return 1;
if (slctab[SLC_EOF].sptr)
oldeofc = *slctab[SLC_EOF].sptr;
# endif
return 0;
}
int
tty_linemode (void)
{
# ifdef EXTPROC
return (termbuf.c_lflag & EXTPROC);
# else
return 0; /* Can't ever set it either. */
# endif/* EXTPROC */
}
void
tty_setlinemode (int on)
{
# ifdef TIOCEXT
set_termbuf ();
ioctl (pty, TIOCEXT, (char *) &on);
init_termbuf ();
# else/* !TIOCEXT */
# ifdef EXTPROC
if (on)
termbuf.c_lflag |= EXTPROC;
else
termbuf.c_lflag &= ~EXTPROC;
# else
/* !EXTPROC */
(void) on; /* Silence warnings. */
# endif
# endif/* TIOCEXT */
}
int
tty_isecho (void)
{
return termbuf.c_lflag & ECHO;
}
int
tty_flowmode (void)
{
return (termbuf.c_iflag & IXON) ? 1 : 0;
}
int
tty_restartany (void)
{
return (termbuf.c_iflag & IXANY) ? 1 : 0;
}
void
tty_setecho (int on)
{
if (on)
termbuf.c_lflag |= ECHO;
else
termbuf.c_lflag &= ~ECHO;
}
int
tty_israw (void)
{
return !(termbuf.c_lflag & ICANON);
}
# if defined AUTHENTICATION && defined NO_LOGIN_F && defined LOGIN_R
int
tty_setraw (int on)
{
if (on)
termbuf.c_lflag &= ~ICANON;
else
termbuf.c_lflag |= ICANON;
}
# endif
void
tty_binaryin (int on)
{
if (on)
termbuf.c_iflag &= ~ISTRIP;
else
termbuf.c_iflag |= ISTRIP;
}
void
tty_binaryout (int on)
{
if (on)
{
termbuf.c_cflag &= ~(CSIZE | PARENB);
termbuf.c_cflag |= CS8;
termbuf.c_oflag &= ~OPOST;
}
else
{
termbuf.c_cflag &= ~CSIZE;
termbuf.c_cflag |= CS7 | PARENB;
termbuf.c_oflag |= OPOST;
}
}
int
tty_isbinaryin (void)
{
return !(termbuf.c_iflag & ISTRIP);
}
int
tty_isbinaryout (void)
{
return !(termbuf.c_oflag & OPOST);
}
int
tty_isediting (void)
{
return termbuf.c_lflag & ICANON;
}
int
tty_istrapsig (void)
{
return termbuf.c_lflag & ISIG;
}
void
tty_setedit (int on)
{
if (on)
termbuf.c_lflag |= ICANON;
else
termbuf.c_lflag &= ~ICANON;
}
void
tty_setsig (int on)
{
if (on)
termbuf.c_lflag |= ISIG;
else
termbuf.c_lflag &= ~ISIG;
}
int
tty_issofttab (void)
{
# ifdef OXTABS
return termbuf.c_oflag & OXTABS;
# endif
# ifdef TABDLY
return (termbuf.c_oflag & TABDLY) == TAB3;
# endif
}
void
tty_setsofttab (int on)
{
if (on)
{
# ifdef OXTABS
termbuf.c_oflag |= OXTABS;
# endif
# ifdef TABDLY
termbuf.c_oflag &= ~TABDLY;
termbuf.c_oflag |= TAB3;
# endif
}
else
{
# ifdef OXTABS
termbuf.c_oflag &= ~OXTABS;
# endif
# ifdef TABDLY
termbuf.c_oflag &= ~TABDLY;
termbuf.c_oflag |= TAB0;
# endif
}
}
int
tty_islitecho (void)
{
# ifdef ECHOCTL
return !(termbuf.c_lflag & ECHOCTL);
# endif
# ifdef TCTLECH
return !(termbuf.c_lflag & TCTLECH);
# endif
# if !defined ECHOCTL && !defined TCTLECH
return 0; /* assumes ctl chars are echoed '^x' */
# endif
}
void
tty_setlitecho (int on)
{
# ifdef ECHOCTL
if (on)
termbuf.c_lflag &= ~ECHOCTL;
else
termbuf.c_lflag |= ECHOCTL;
# endif
# ifdef TCTLECH
if (on)
termbuf.c_lflag &= ~TCTLECH;
else
termbuf.c_lflag |= TCTLECH;
# endif
}
int
tty_iscrnl (void)
{
return termbuf.c_iflag & ICRNL;
}
#endif /* !IOCTL_INTERFACE */
void
init_termbuf (void)
{
#if !defined SOLARIS10 && !defined SOLARIS
_term_getattr (pty, &termbuf);
#else /* SOLARIS || SOLARIS10 */
/* On Solaris the master PTY is not able to report terminal
* settings about the slave TTY, since it is only the slave
* that is working with an designated line discipline.
* Therefore we must determine the slave descriptor, which
* exists only if ptsname() returns a non-empty string.
* This happens for the parent process. The child process
* sees an empty name, so undergoes minimal processing.
*/
int tty = pty;
char *name = ptsname (pty);
if (name)
/* Minimal access means read only! */
tty = open (name, O_RDONLY | O_NONBLOCK);
_term_getattr (tty, &termbuf);
if (name)
close (tty);
#endif /* SOLARIS || SOLARIS10 */
termbuf2 = termbuf;
}
#if defined TIOCPKT_IOCTL
/*FIXME: Hardly needed?
* Built by OpenSolaris and BSD, though. */
void
copy_termbuf (void)
{
size_t len = 0;
char *cp = (char *) &termbuf;
while (pty_input_level () > 0)
{
if (len >= sizeof (termbuf))
break;
*cp++ = pty_get_char (0);
len++;
}
termbuf2 = termbuf;
}
#endif
void
set_termbuf (void)
{
if (memcmp (&termbuf, &termbuf2, sizeof (termbuf)))
#if !defined SOLARIS10 && !defined SOLARIS
_term_setattr (pty, &termbuf);
#else /* SOLARIS || SOLARIS10 */
{
/* Same reason as with _term_getattr. */
int tty = pty;
char *name = ptsname (pty);
if (name)
tty = open (name, O_RDWR | O_NONBLOCK | O_NOCTTY);
_term_setattr (tty, &termbuf);
if (name)
close (tty);
}
#endif /* SOLARIS || SOLARIS10 */
}
/* spcset(func, valp, valpp)
This function takes various special characters (func), and
sets *valp to the current value of that character, and
*valpp to point to where in the "termbuf" structure that
value is kept.
It returns the SLC_ level of support for this function. */
#define setval(a, b) *valp = termbuf.a ; \
*valpp = &termbuf.a ; \
return b;
#define defval(a) *valp = ((cc_t)a); \
*valpp = (cc_t *)0; \
return SLC_DEFAULT;
int
spcset (int func, cc_t *valp, cc_t **valpp)
{
switch (func)
{
case SLC_EOF:
setval (termdesc_eofc, SLC_VARIABLE);
case SLC_EC:
setval (termdesc_erase, SLC_VARIABLE);
case SLC_EL:
setval (termdesc_kill, SLC_VARIABLE);
case SLC_IP:
setval (termdesc_ip, SLC_VARIABLE | SLC_FLUSHIN | SLC_FLUSHOUT);
case SLC_ABORT:
setval (termdesc_abort, SLC_VARIABLE | SLC_FLUSHIN | SLC_FLUSHOUT);
case SLC_XON:
#ifdef termdesc_xon
setval (termdesc_xon, SLC_VARIABLE);
#else
defval (0x13);
#endif
case SLC_XOFF:
#ifdef termdesc_xoff
setval (termdesc_xoff, SLC_VARIABLE);
#else
defval (0x11);
#endif
case SLC_EW:
#ifdef termdesc_ew
setval (termdesc_ew, SLC_VARIABLE);
#else
defval (0);
#endif
case SLC_RP:
#ifdef termdesc_rp
setval (termdesc_rp, SLC_VARIABLE);
#else
defval (0);
#endif
case SLC_LNEXT:
#ifdef termdesc_lnext
setval (termdesc_lnext, SLC_VARIABLE);
#else
defval (0);
#endif
case SLC_AO:
#ifdef termdesc_ao
setval (termdesc_ao, SLC_VARIABLE | SLC_FLUSHOUT);
#else
defval (0);
#endif
case SLC_SUSP:
#ifdef termdesc_susp
setval (termdesc_susp, SLC_VARIABLE | SLC_FLUSHIN);
#else
defval (0);
#endif
#ifdef termdesc_forw1
case SLC_FORW1:
setval (termdesc_forw1, SLC_VARIABLE);
#endif
#ifdef termdesc_forw2
case SLC_FORW2:
setval (termdesc_forw2, SLC_VARIABLE);
#endif
case SLC_AYT:
#ifdef termdesc_status
setval (termdesc_status, SLC_VARIABLE);
#else
defval (0);
#endif
case SLC_BRK:
case SLC_SYNCH:
case SLC_EOR:
defval (0);
default:
*valp = 0;
*valpp = 0;
return SLC_NOSUPPORT;
}
}
#if B4800 != 4800
# define DECODE_BAUD
#endif
#ifdef DECODE_BAUD
/*
* A table of available terminal speeds
*/
struct termspeeds
{
int speed;
int value;
} termspeeds[] = {
{0, B0},
{50, B50},
{75, B75},
{110, B110},
{134, B134},
{150, B150},
{200, B200},
{300, B300},
{600, B600},
{1200, B1200},
{1800, B1800},
{2400, B2400},
{4800, B4800},
# ifdef B7200
{7200, B7200},
# endif
{9600, B9600},
# ifdef B14400
{14400, B14400},
# endif
# ifdef B19200
{19200, B19200},
# endif
# ifdef B28800
{28800, B28800},
# endif
# ifdef B38400
{38400, B38400},
# endif
# ifdef B57600
{57600, B57600},
# endif
# ifdef B115200
{115200, B115200},
# endif
# ifdef B230400
{230400, B230400},
# endif
{-1, 0}
};
#endif /* DECODE_BAUD */
void
tty_tspeed (int val)
{
#ifdef DECODE_BAUD
struct termspeeds *tp;
for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++)
;
if (tp->speed == -1) /* back up to last valid value */
--tp;
val = tp->value;
#endif
cfsetospeed (&termbuf, val);
}
void
tty_rspeed (int val)
{
#ifdef DECODE_BAUD
struct termspeeds *tp;
for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++)
;
if (tp->speed == -1) /* back up to last valid value */
--tp;
val = tp->value;
#endif
cfsetispeed (&termbuf, val);
}