/*
- * Copyright (c) 1983, 1986 Regents of the University of California.
+ * Copyright (c) 1989 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
#ifndef lint
char copyright[] =
-"@(#) Copyright (c) 1983, 1986 Regents of the University of California.\n\
+"@(#) Copyright (c) 1989 Regents of the University of California.\n\
All rights reserved.\n";
#endif /* not lint */
#ifndef lint
-static char sccsid[] = "@(#)telnetd.c 5.36 (Berkeley) %G%";
+static char sccsid[] = "@(#)telnetd.c 5.39 (Berkeley) %G%";
#endif /* not lint */
-/*
- * Telnet server.
- */
-#include <sys/param.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <sys/file.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-
-#include <netinet/in.h>
-
-#include <arpa/telnet.h>
-
-#include <stdio.h>
-#include <signal.h>
-#include <errno.h>
-#include <sgtty.h>
-#include <netdb.h>
-#include <syslog.h>
-#include <ctype.h>
-#include "pathnames.h"
-
-#define OPT_NO 0 /* won't do this option */
-#define OPT_YES 1 /* will do this option */
-#define OPT_YES_BUT_ALWAYS_LOOK 2
-#define OPT_NO_BUT_ALWAYS_LOOK 3
-char hisopts[256];
-char myopts[256];
-
-char doopt[] = { IAC, DO, '%', 'c', 0 };
-char dont[] = { IAC, DONT, '%', 'c', 0 };
-char will[] = { IAC, WILL, '%', 'c', 0 };
-char wont[] = { IAC, WONT, '%', 'c', 0 };
+#include "telnetd.h"
/*
- * I/O data buffers, pointers, and counters.
+ * I/O data buffers,
+ * pointers, and counters.
*/
char ptyibuf[BUFSIZ], *ptyip = ptyibuf;
+char ptyibuf2[BUFSIZ];
-char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
-
-char netibuf[BUFSIZ], *netip = netibuf;
-#define NIACCUM(c) { *netip++ = c; \
- ncc++; \
- }
-
-char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
-char *neturg = 0; /* one past last bye of urgent data */
- /* the remote system seems to NOT be an old 4.2 */
-int not42 = 1;
-
-#define BANNER "\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r"
-
- /* buffer for sub-options */
-char subbuffer[100], *subpointer= subbuffer, *subend= subbuffer;
-#define SB_CLEAR() subpointer = subbuffer;
-#define SB_TERM() { subend = subpointer; SB_CLEAR(); }
-#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \
- *subpointer++ = (c); \
- }
-#define SB_GET() ((*subpointer++)&0xff)
-#define SB_EOF() (subpointer >= subend)
+#ifdef CRAY
+int hostinfo = 1; /* do we print login banner? */
+#endif
-int pcc, ncc;
+#ifdef CRAY
+extern int newmap; /* nonzero if \n maps to ^M^J */
+int lowpty = 0, highpty = 128; /* low, high pty numbers */
+#endif /* CRAY */
-int pty, net;
-int inter;
-extern char **environ;
-extern int errno;
-char *line;
-int SYNCHing = 0; /* we are in TELNET SYNCH mode */
-/*
- * The following are some clocks used to decide how to interpret
- * the relationship between various variables.
- */
+int debug = 0;
+char *progname;
-struct {
- int
- system, /* what the current time is */
- echotoggle, /* last time user entered echo character */
- modenegotiated, /* last time operating mode negotiated */
- didnetreceive, /* last time we read data from network */
- ttypeopt, /* ttype will/won't received */
- ttypesubopt, /* ttype subopt is received */
- getterminal, /* time started to get terminal information */
- gotDM; /* when did we last see a data mark */
-} clocks;
-
-#define settimer(x) (clocks.x = ++clocks.system)
-#define sequenceIs(x,y) (clocks.x < clocks.y)
-\f
main(argc, argv)
char *argv[];
{
struct sockaddr_in from;
int on = 1, fromlen;
- if ((argc > 1) && (strcmp(argv[1], "-debug") == 0)) {
+ pfrontp = pbackp = ptyobuf;
+ netip = netibuf;
+ nfrontp = nbackp = netobuf;
+
+ progname = *argv;
+top:
+ argc--, argv++;
+
+ if (argc > 0 && strcmp(*argv, "-debug") == 0) {
+ debug++;
+ goto top;
+ }
+
+#ifdef LINEMODE
+ if (argc > 0 && !strcmp(*argv, "-l")) {
+ alwayslinemode = 1;
+ goto top;
+ }
+#endif /* LINEMODE */
+
+#ifdef CRAY
+ if (argc > 0 && !strcmp(*argv, "-h")) {
+ hostinfo = 0;
+ goto top;
+ }
+
+ if (argc > 0 && !strncmp(*argv, "-r", 2)) {
+ char *strchr();
+ char *c;
+
+ *argv += 2;
+ if (**argv == '\0' && argc)
+ argv++, argc--;
+ c = strchr(*argv, '-');
+ if (c) {
+ *c++ = '\0';
+ highpty = atoi(c);
+ } else
+ highpty = -1;
+ lowpty = atoi(*argv);
+ if ((lowpty > highpty) || (lowpty < 0) || (highpty > 999)) {
+ usage:
+ fprintf(stderr, "Usage: telnetd [-debug] [-h] ");
+# ifdef NEWINIT
+ fprintf(stderr, "[-Iinitid] ");
+# endif /* NEWINIT */
+ fprintf(stderr, "[-l] [-rlowpty-highpty] [port]\n");
+ exit(1);
+ }
+ goto top;
+ }
+# ifdef NEWINIT
+ if (argc > 0 && !strncmp(*argv, "-I", 2)) {
+ extern char *gen_id;
+
+ *argv += 2;
+ if (**argv == '\0') {
+ if (argc < 2)
+ goto usage;
+ argv++, argc--;
+ if (**argv == '\0')
+ goto usage;
+ }
+ gen_id = *argv;
+ goto top;
+ }
+# endif /* NEWINIT */
+#endif /* CRAY */
+
+ if (debug) {
int s, ns, foo;
struct servent *sp;
static struct sockaddr_in sin = { AF_INET };
- argc--, argv++;
- if (argc > 1) {
- sin.sin_port = atoi(argv[1]);
- sin.sin_port = htons((u_short)sin.sin_port);
+ if (argc > 0) {
+ if (sp = getservbyname(*argv, "tcp")) {
+ sin.sin_port = sp->s_port;
+ } else {
+ sin.sin_port = atoi(*argv);
+ if ((int)sin.sin_port <= 0) {
+ fprintf(stderr, "telnetd: %s: bad port #\n", *argv);
+ exit(1);
+ }
+ sin.sin_port = htons((u_short)sin.sin_port);
+ }
} else {
sp = getservbyname("telnet", "tcp");
if (sp == 0) {
fprintf(stderr,
"telnetd: tcp/telnet: unknown service\n");
- exit(1);
+ exit(1);
}
sin.sin_port = sp->s_port;
}
perror("telnetd: socket");;
exit(1);
}
- if (bind(s, &sin, sizeof sin) < 0) {
+ (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
perror("bind");
exit(1);
}
exit(1);
}
foo = sizeof sin;
- ns = accept(s, &sin, &foo);
+ ns = accept(s, (struct sockaddr *)&sin, &foo);
if (ns < 0) {
perror("accept");
exit(1);
}
- dup2(ns, 0);
- close(s);
+ (void) dup2(ns, 0);
+ (void) close(ns);
+ (void) close(s);
}
+
openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
fromlen = sizeof (from);
- if (getpeername(0, &from, &fromlen) < 0) {
- fprintf(stderr, "%s: ", argv[0]);
+ if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
+ fprintf(stderr, "%s: ", progname);
perror("getpeername");
_exit(1);
}
if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
}
- doit(0, &from);
-}
+ net = 0;
+ doit(&from);
+ /* NOTREACHED */
+} /* end of main */
-char *terminaltype = 0;
-char *envinit[2];
int cleanup();
-/*
- * ttloop
- *
- * A small subroutine to flush the network output buffer, get some data
- * from the network, and pass it through the telnet state machine. We
- * also flush the pty input buffer (by dropping its data) if it becomes
- * too full.
- */
-
-void
-ttloop()
-{
- if (nfrontp-nbackp) {
- netflush();
- }
- ncc = read(net, netibuf, sizeof netibuf);
- if (ncc < 0) {
- syslog(LOG_INFO, "ttloop: read: %m\n");
- exit(1);
- } else if (ncc == 0) {
- syslog(LOG_INFO, "ttloop: peer died: %m\n");
- exit(1);
- }
- netip = netibuf;
- telrcv(); /* state machine */
- if (ncc > 0) {
- pfrontp = pbackp = ptyobuf;
- telrcv();
- }
-}
-
-/*
- * getterminalspeed
- *
- * Ask the other end to send along its terminal speed.
- * subopt does the rest.
- */
-
-void
-getterminalspeed()
-{
- static char sbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
-
- bcopy(sbuf, nfrontp, sizeof sbuf);
- nfrontp += sizeof sbuf;
-}
-
/*
* getterminaltype
*
- * Ask the other end to send along its terminal type.
+ * Ask the other end to send along its terminal type and speed.
* Output is the variable terminaltype filled in.
*/
-
+static char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
void
getterminaltype()
{
- static char sbuf[] = { IAC, DO, TELOPT_TTYPE };
+ void ttloop();
- settimer(getterminal);
- bcopy(sbuf, nfrontp, sizeof sbuf);
- nfrontp += sizeof sbuf;
- hisopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK;
- while (sequenceIs(ttypeopt, getterminal)) {
+ settimer(baseline);
+ willoption(TELOPT_TTYPE, 1);
+ willoption(TELOPT_TSPEED, 1);
+ while ((hiswants[TELOPT_TTYPE] != hisopts[TELOPT_TTYPE]) ||
+ (hiswants[TELOPT_TSPEED] != hisopts[TELOPT_TSPEED])) {
ttloop();
}
- if (hisopts[TELOPT_TTYPE] == OPT_YES) {
- static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };
+ if (hisopts[TELOPT_TSPEED] == OPT_YES) {
+ static char sbbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
bcopy(sbbuf, nfrontp, sizeof sbbuf);
nfrontp += sizeof sbbuf;
- while (sequenceIs(ttypesubopt, getterminal)) {
+ }
+ if (hisopts[TELOPT_TTYPE] == OPT_YES) {
+
+ bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
+ nfrontp += sizeof ttytype_sbbuf;
+ }
+ if (hisopts[TELOPT_TSPEED] == OPT_YES) {
+ while (sequenceIs(tspeedsubopt, baseline))
+ ttloop();
+ }
+ if (hisopts[TELOPT_TTYPE] == OPT_YES) {
+ char first[256], last[256];
+
+ while (sequenceIs(ttypesubopt, baseline))
ttloop();
+
+ if (!terminaltypeok(&terminaltype[5])) {
+ (void) strncpy(first, terminaltype, sizeof(first));
+ for(;;) {
+ /*
+ * Save the unknown name, and request the next name.
+ */
+ (void) strncpy(last, terminaltype, sizeof(last));
+ _gettermname();
+ if (terminaltypeok(&terminaltype[5]))
+ break;
+ if (strncmp(last, terminaltype, sizeof(last)) == 0) {
+ /*
+ * We've hit the end. If this is the same as
+ * the first name, just go with it.
+ */
+ if (strncmp(first, terminaltype, sizeof(first) == 0))
+ break;
+ /*
+ * Get the terminal name one more type, so that
+ * RFC1091 compliant telnets will cycle back to
+ * the start of the list.
+ */
+ _gettermname();
+ if (strncmp(first, terminaltype, sizeof(first) != 0))
+ (void) strncpy(terminaltype, first, sizeof(first));
+ break;
+ }
+ }
}
}
+} /* end of getterminaltype */
+
+_gettermname()
+{
+ settimer(baseline);
+ bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf);
+ nfrontp += sizeof ttytype_sbbuf;
+ while (sequenceIs(ttypesubopt, baseline))
+ ttloop();
+}
+
+terminaltypeok(s)
+char *s;
+{
+ char buf[1024];
+
+ if (terminaltype == NULL)
+ return(1);
+
+ /*
+ * tgetent() will return 1 if the type is known, and
+ * 0 if it is not known. If it returns -1, it couldn't
+ * open the database. But if we can't open the database,
+ * it won't help to say we failed, because we won't be
+ * able to verify anything else. So, we treat -1 like 1.
+ */
+ if (tgetent(buf, s) == 0)
+ return(0);
+ return(1);
}
/*
* Get a pty, scan input lines.
*/
-doit(f, who)
- int f;
+doit(who)
struct sockaddr_in *who;
{
char *host, *inet_ntoa();
- int i, p, t;
- struct sgttyb b;
+ int t;
struct hostent *hp;
- int c;
- for (c = 'p'; c <= 's'; c++) {
- struct stat stb;
+ /*
+ * Find an available pty to use.
+ */
+ pty = getpty();
+ if (pty < 0)
+ fatal(net, "All network ports in use");
- line = "/dev/ptyXX";
- line[strlen("/dev/pty")] = c;
- line[strlen("/dev/ptyp")] = '0';
- if (stat(line, &stb) < 0)
- break;
- for (i = 0; i < 16; i++) {
- line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i];
- p = open(line, O_RDWR);
- if (p > 0)
- goto gotpty;
- }
- }
- fatal(f, "All network ports in use");
- /*NOTREACHED*/
-gotpty:
- dup2(f, 0);
- line[strlen("/dev/")] = 't';
- t = open("/dev/tty", O_RDWR);
- if (t >= 0) {
- ioctl(t, TIOCNOTTY, 0);
- close(t);
- }
- t = open(line, O_RDWR);
- if (t < 0)
- fatalperror(f, line);
- if (fchmod(t, 0))
- fatalperror(f, line);
- (void)signal(SIGHUP, SIG_IGN);
- vhangup();
- (void)signal(SIGHUP, SIG_DFL);
- t = open(line, O_RDWR);
- if (t < 0)
- fatalperror(f, line);
- ioctl(t, TIOCGETP, &b);
- b.sg_flags = CRMOD|XTABS|ANYP;
- ioctl(t, TIOCSETP, &b);
- ioctl(p, TIOCGETP, &b);
- b.sg_flags &= ~ECHO;
- b.sg_ospeed = b.sg_ispeed = B9600;
- ioctl(p, TIOCSETP, &b);
- hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr),
+ t = getptyslave();
+
+ /* get name of connected client */
+ hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr),
who->sin_family);
if (hp)
host = hp->h_name;
else
host = inet_ntoa(who->sin_addr);
- net = f;
- pty = p;
-
/*
- * get terminal type and size.
+ * get terminal type.
*/
getterminaltype();
+ if (terminaltype == NULL)
+ terminaltype = "TERM=network";
- if ((i = fork()) < 0)
- fatalperror(f, "fork");
- if (i) {
- close(t);
- telnet(f, p);
- }
- if (setsid() < 0)
- fatalperror(f, "setsid");
- if (ioctl(t, TIOCSCTTY, 0) < 0)
- fatalperror(f, "ioctl(sctty)");
- close(f);
- close(p);
- dup2(t, 0);
- dup2(t, 1);
- dup2(t, 2);
- close(t);
- envinit[0] = terminaltype;
- envinit[1] = 0;
- environ = envinit;
/*
- * -h : pass on name of host.
- * WARNING: -h is accepted by login if and only if
- * getuid() == 0.
- * -p : don't clobber the environment (so terminal type stays set).
+ * Start up the login process on the slave side of the terminal
*/
- execl(_PATH_LOGIN, "login", "-h", host,
- terminaltype ? "-p" : 0, 0);
- syslog(LOG_ERR, "%s: %m\n", _PATH_LOGIN);
- fatalperror(2, _PATH_LOGIN);
- /*NOTREACHED*/
-}
-
-fatal(f, msg)
- int f;
- char *msg;
-{
- char buf[BUFSIZ];
-
- (void) sprintf(buf, "telnetd: %s.\r\n", msg);
- (void) write(f, buf, strlen(buf));
- exit(1);
-}
-
-fatalperror(f, msg)
- int f;
- char *msg;
-{
- char buf[BUFSIZ];
- extern char *sys_errlist[];
-
- (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);
- fatal(f, buf);
-}
-
-
-/*
- * Check a descriptor to see if out of band data exists on it.
- */
+ startslave(t, host);
+ telnet(net, pty); /* begin server processing */
+ /*NOTREACHED*/
+} /* end of doit */
-stilloob(s)
-int s; /* socket number */
-{
- static struct timeval timeout = { 0 };
- fd_set excepts;
- int value;
-
- do {
- FD_ZERO(&excepts);
- FD_SET(s, &excepts);
- value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
- } while ((value == -1) && (errno == EINTR));
-
- if (value < 0) {
- fatalperror(pty, "select");
- }
- if (FD_ISSET(s, &excepts)) {
- return 1;
- } else {
- return 0;
- }
-}
-\f
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif MAXHOSTNAMELEN
/*
* Main loop. Select from pty and network, and
* hand data to telnet receiver finite state machine.
*/
telnet(f, p)
+int f, p;
{
int on = 1;
char hostname[MAXHOSTNAMELEN];
+#ifdef CRAY2
+ int termstat();
+ int interrupt(), sendbrk();
+#endif
#define TABBUFSIZ 512
char defent[TABBUFSIZ];
char defstrs[TABBUFSIZ];
char *HE;
char *HN;
char *IM;
-
- ioctl(f, FIONBIO, &on);
- ioctl(p, FIONBIO, &on);
- ioctl(p, TIOCPKT, &on);
-#if defined(SO_OOBINLINE)
- setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
-#endif /* defined(SO_OOBINLINE) */
- signal(SIGTSTP, SIG_IGN);
+ void netflush();
+
/*
- * Ignoring SIGTTOU keeps the kernel from blocking us
- * in ttioctl() in /sys/tty.c.
+ * Initialize the slc mapping table.
*/
- signal(SIGTTOU, SIG_IGN);
- signal(SIGCHLD, cleanup);
- setpgrp(0, 0);
+ get_slc_defaults();
/*
- * Request to do remote echo and to suppress go ahead.
+ * Do some tests where it is desireable to wait for a response.
+ * Rather than doing them slowly, one at a time, do them all
+ * at once.
*/
- if (!myopts[TELOPT_ECHO]) {
- dooption(TELOPT_ECHO);
- }
- if (!myopts[TELOPT_SGA]) {
- dooption(TELOPT_SGA);
- }
- if (!hisopts[TELOPT_NAWS]) {
- willoption(TELOPT_NAWS);
- hisopts[TELOPT_NAWS] = OPT_NO;
- }
- if (!hisopts[TELOPT_TSPEED]) {
- willoption(TELOPT_TSPEED);
- hisopts[TELOPT_TSPEED] = OPT_NO;
- }
- if (!hisopts[TELOPT_LFLOW]) {
- willoption(TELOPT_LFLOW);
- hisopts[TELOPT_LFLOW] = OPT_NO;
- }
-
+ if (!myopts[TELOPT_SGA])
+ dooption(TELOPT_SGA);
/*
* Is the client side a 4.2 (NOT 4.3) system? We need to know this
* because 4.2 clients are unable to deal with TCP urgent data.
* WE, the server, sends it; it does NOT mean that the client will
* echo the terminal input).
*/
- (void) sprintf(nfrontp, doopt, TELOPT_ECHO);
- nfrontp += sizeof doopt-2;
- hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK;
+ willoption(TELOPT_ECHO, 1);
+
+#ifdef LINEMODE
+ if (hisopts[TELOPT_LINEMODE] == OPT_NO) {
+ /* Query the peer for linemode support by trying to negotiate
+ * the linemode option.
+ */
+ linemode = 1;
+ editmode = 0;
+ willoption(TELOPT_LINEMODE, 1); /* send do linemode */
+ }
+#endif /* LINEMODE */
+
+ /*
+ * Send along a couple of other options that we wish to negotiate.
+ */
+ willoption(TELOPT_NAWS, 1);
+ dooption(TELOPT_STATUS, 1);
+ flowmode = 1; /* default flow control state */
+ willoption(TELOPT_LFLOW, 1);
+
+ /*
+ * Spin, waiting for a response from the DO ECHO. However,
+ * some REALLY DUMB telnets out there might not respond
+ * to the DO ECHO. So, we spin looking for NAWS, (most dumb
+ * telnets so far seem to respond with WONT for a DO that
+ * they don't understand...) because by the time we get the
+ * response, it will already have processed the DO ECHO.
+ * Kludge upon kludge.
+ */
+ while (hiswants[TELOPT_NAWS] != hisopts[TELOPT_NAWS])
+ ttloop();
+
+ /*
+ * On the off chance that the telnet client is broken and does not
+ * respond to the DO ECHO we sent, (after all, we did send the
+ * DO NAWS negotiation after the DO ECHO, and we won't get here
+ * until a response to the DO NAWS comes back) simulate the
+ * receipt of a will echo. This will also send a WONT ECHO
+ * to the client, since we assume that the client failed to
+ * respond because it believes that it is already in DO ECHO
+ * mode, which we do not want.
+ */
+ if (hiswants[TELOPT_ECHO] == OPT_YES)
+ willoption(TELOPT_ECHO, 0);
+
+ /*
+ * Finally, to clean things up, we turn on our echo. This
+ * will break stupid 4.2 telnets out of local terminal echo.
+ */
+
+ if (!myopts[TELOPT_ECHO])
+ dooption(TELOPT_ECHO);
+
+ /*
+ * Turn on packet mode, and default to line at at time mode.
+ */
+ (void) ioctl(p, TIOCPKT, (char *)&on);
+#ifdef LINEMODE
+ tty_setlinemode(1);
+
+# ifdef KLUDGELINEMODE
+ /*
+ * Continuing line mode support. If client does not support
+ * real linemode, attempt to negotiate kludge linemode by sending
+ * the do timing mark sequence.
+ */
+ if (lmodetype < REAL_LINEMODE)
+ willoption(TELOPT_TM, 1);
+# endif /* KLUDGELINEMODE */
+#endif /* LINEMODE */
+
+ /*
+ * Call telrcv() once to pick up anything received during
+ * terminal type negotiation, 4.2/4.3 determination, and
+ * linemode negotiation.
+ */
+ telrcv();
+
+ (void) ioctl(f, FIONBIO, (char *)&on);
+ (void) ioctl(p, FIONBIO, (char *)&on);
+#ifdef CRAY2
+ init_termdriver(f, p, interrupt, sendbrk);
+#endif
+
+#if defined(SO_OOBINLINE)
+ (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
+#endif /* defined(SO_OOBINLINE) */
+
+#ifdef SIGTSTP
+ (void) signal(SIGTSTP, SIG_IGN);
+#endif
+#ifdef SIGTTOU
+ /*
+ * Ignoring SIGTTOU keeps the kernel from blocking us
+ * in ttioct() in /sys/tty.c.
+ */
+ (void) signal(SIGTTOU, SIG_IGN);
+#endif
+
+ (void) signal(SIGCHLD, cleanup);
+
+#if defined(CRAY2)
+ /*
+ * Cray-2 will send a signal when pty modes are changed by slave
+ * side. Set up signal handler now.
+ */
+ if ((int)signal(SIGUSR1, termstat) < 0)
+ perror("signal");
+ else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0)
+ perror("ioctl:TCSIGME");
+ /*
+ * Make processing loop check terminal characteristics early on.
+ */
+ termstat();
+#endif
+
+ (void) setpgrp(0, 0);
/*
* Show banner that getty never gave.
* other pty --> client data.
*/
- gethostname(hostname, sizeof (hostname));
+ (void) gethostname(hostname, sizeof (hostname));
+
if (getent(defent, "default") == 1) {
char *getstr();
- char *p=defstrs;
+ char *cp=defstrs;
- HE = getstr("he", &p);
- HN = getstr("hn", &p);
- IM = getstr("im", &p);
+ HE = getstr("he", &cp);
+ HN = getstr("hn", &cp);
+ IM = getstr("im", &cp);
if (HN && *HN)
- strcpy(hostname, HN);
- edithost(HE, hostname);
- if (IM && *IM)
- putf(IM, ptyibuf+1);
+ (void) strcpy(hostname, HN);
+ if (IM == 0)
+ IM = "";
} else {
- sprintf(ptyibuf+1, BANNER, hostname);
- }
-
- ptyip = ptyibuf+1; /* Prime the pump */
- pcc = strlen(ptyip); /* ditto */
-
- /* Clear ptybuf[0] - where the packet information is received */
- ptyibuf[0] = 0;
-
- /*
- * Call telrcv() once to pick up anything received during
- * terminal type negotiation.
- */
- telrcv();
+#ifdef CRAY
+ if (hostinfo == 0)
+ IM = 0;
+ else
+#endif
+ IM = DEFAULT_IM;
+ HE = 0;
+ }
+ edithost(HE, hostname);
+ if (IM && *IM)
+ putf(IM, ptyibuf2);
+
+ if (pcc)
+ (void) strncat(ptyibuf2, ptyip, pcc+1);
+ ptyip = ptyibuf2;
+ pcc = strlen(ptyip);
for (;;) {
fd_set ibits, obits, xbits;
if (ncc < 0 && pcc < 0)
break;
+#ifdef CRAY2
+ if (needtermstat)
+ _termstat();
+#endif /* CRAY2 */
FD_ZERO(&ibits);
FD_ZERO(&obits);
FD_ZERO(&xbits);
if (!SYNCHing) {
FD_SET(f, &xbits);
}
- FD_SET(p, &xbits);
if ((c = select(16, &ibits, &obits, &xbits,
(struct timeval *)0)) < 1) {
if (c == -1) {
if (SYNCHing) {
int atmark;
- ioctl(net, SIOCATMARK, (char *)&atmark);
+ (void) ioctl(net, SIOCATMARK, (char *)&atmark);
if (atmark) {
ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
if ((ncc == -1) && (errno == EINVAL)) {
/*
* Something to read from the pty...
*/
- if (FD_ISSET(p, &ibits) || FD_ISSET(p, &xbits)) {
+ if (FD_ISSET(p, &ibits)) {
pcc = read(p, ptyibuf, BUFSIZ);
if (pcc < 0 && errno == EWOULDBLOCK)
pcc = 0;
else {
if (pcc <= 0)
break;
+#ifndef CRAY2
+#ifdef LINEMODE
+ /*
+ * If ioctl from pty, pass it through net
+ */
+ if (ptyibuf[0] & TIOCPKT_IOCTL) {
+ copy_termbuf(ptyibuf+1, pcc-1);
+ localstat();
+ pcc = 1;
+ }
+#endif LINEMODE
if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
- netclear(); /* clear buffer back */
+ netclear(); /* clear buffer back */
*nfrontp++ = IAC;
*nfrontp++ = DM;
neturg = nfrontp-1; /* off by one XXX */
}
if (hisopts[TELOPT_LFLOW] &&
(ptyibuf[0] &
- (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
- sprintf(nfrontp,"%c%c%c%c%c%c",
+ (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
+ (void) sprintf(nfrontp, "%c%c%c%c%c%c",
IAC, SB, TELOPT_LFLOW,
ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0,
IAC, SE);
}
pcc--;
ptyip = ptyibuf+1;
- }
+#else /* CRAY2 */
+ if (!uselinemode) {
+ pcc = term_output(ptyibuf, ptyibuf2,
+ pcc, BUFSIZ);
+ ptyip = ptyibuf2;
+ } else
+ ptyip = ptyibuf;
+#endif /* CRAY2 */
+ }
}
while (pcc > 0) {
c = *ptyip++ & 0377, pcc--;
if (c == IAC)
*nfrontp++ = c;
+#ifdef CRAY2
+ else if (c == '\n' &&
+ myopts[TELOPT_BINARY] == OPT_NO && newmap)
+ *nfrontp++ = '\r';
+#endif /* CRAY2 */
*nfrontp++ = c;
- /* Don't do CR-NUL if we are in binary mode */
if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) {
if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
*nfrontp++ = *ptyip++ & 0377;
ptyflush();
}
cleanup();
-}
+} /* end of telnet */
-/*
- * State for recv fsm
- */
-#define TS_DATA 0 /* base state */
-#define TS_IAC 1 /* look for double IAC's */
-#define TS_CR 2 /* CR-LF ->'s CR */
-#define TS_SB 3 /* throw away begin's... */
-#define TS_SE 4 /* ...end's (suboption negotiation) */
-#define TS_WILL 5 /* will option negotiation */
-#define TS_WONT 6 /* wont " */
-#define TS_DO 7 /* do " */
-#define TS_DONT 8 /* dont " */
-
-telrcv()
-{
- register int c;
- static int state = TS_DATA;
-
- while (ncc > 0) {
- if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)
- return;
- c = *netip++ & 0377, ncc--;
- switch (state) {
-
- case TS_CR:
- state = TS_DATA;
- /* Strip off \n or \0 after a \r */
- if ((c == 0) || (c == '\n')) {
- break;
- }
- /* FALL THROUGH */
-
- case TS_DATA:
- if (c == IAC) {
- state = TS_IAC;
- break;
- }
- if (inter > 0)
- break;
- /*
- * We now map \r\n ==> \r for pragmatic reasons.
- * Many client implementations send \r\n when
- * the user hits the CarriageReturn key.
- *
- * We USED to map \r\n ==> \n, since \r\n says
- * that we want to be in column 1 of the next
- * printable line, and \n is the standard
- * unix way of saying that (\r is only good
- * if CRMOD is set, which it normally is).
- */
- if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) {
- state = TS_CR;
- }
- *pfrontp++ = c;
- break;
-
- case TS_IAC:
- switch (c) {
-
- /*
- * Send the process on the pty side an
- * interrupt. Do this with a NULL or
- * interrupt char; depending on the tty mode.
- */
- case IP:
- interrupt();
- break;
-
- case BREAK:
- sendbrk();
- break;
-
- /*
- * Are You There?
- */
- case AYT:
- strcpy(nfrontp, "\r\n[Yes]\r\n");
- nfrontp += 9;
- break;
-
- /*
- * Abort Output
- */
- case AO: {
- struct ltchars tmpltc;
-
- ptyflush(); /* half-hearted */
- ioctl(pty, TIOCGLTC, &tmpltc);
- if (tmpltc.t_flushc != '\377') {
- *pfrontp++ = tmpltc.t_flushc;
- }
- netclear(); /* clear buffer back */
- *nfrontp++ = IAC;
- *nfrontp++ = DM;
- neturg = nfrontp-1; /* off by one XXX */
- break;
- }
-
- /*
- * Erase Character and
- * Erase Line
- */
- case EC:
- case EL: {
- struct sgttyb b;
- char ch;
-
- ptyflush(); /* half-hearted */
- ioctl(pty, TIOCGETP, &b);
- ch = (c == EC) ?
- b.sg_erase : b.sg_kill;
- if (ch != '\377') {
- *pfrontp++ = ch;
- }
- break;
- }
-
- /*
- * Check for urgent data...
- */
- case DM:
- SYNCHing = stilloob(net);
- settimer(gotDM);
- break;
-
-
- /*
- * Begin option subnegotiation...
- */
- case SB:
- state = TS_SB;
- SB_CLEAR();
- continue;
-
- case WILL:
- state = TS_WILL;
- continue;
-
- case WONT:
- state = TS_WONT;
- continue;
-
- case DO:
- state = TS_DO;
- continue;
-
- case DONT:
- state = TS_DONT;
- continue;
-
- case IAC:
- *pfrontp++ = c;
- break;
- }
- state = TS_DATA;
- break;
-
- case TS_SB:
- if (c == IAC) {
- state = TS_SE;
- } else {
- SB_ACCUM(c);
- }
- break;
-
- case TS_SE:
- if (c != SE) {
- if (c != IAC) {
- SB_ACCUM(IAC);
- }
- SB_ACCUM(c);
- state = TS_SB;
- } else {
- SB_TERM();
- suboption(); /* handle sub-option */
- state = TS_DATA;
- }
- break;
-
- case TS_WILL:
- if (hisopts[c] != OPT_YES)
- willoption(c);
- state = TS_DATA;
- if (c == TELOPT_TSPEED)
- getterminalspeed();
- continue;
-
- case TS_WONT:
- if (hisopts[c] != OPT_NO)
- wontoption(c);
- state = TS_DATA;
- continue;
-
- case TS_DO:
- if (myopts[c] != OPT_YES)
- dooption(c);
- state = TS_DATA;
- continue;
-
- case TS_DONT:
- if (myopts[c] != OPT_NO) {
- dontoption(c);
- }
- state = TS_DATA;
- continue;
-
- default:
- syslog(LOG_ERR, "telnetd: panic state=%d\n", state);
- printf("telnetd: panic state=%d\n", state);
- exit(1);
- }
- }
-}
-
-willoption(option)
- int option;
-{
- char *fmt;
-
- switch (option) {
-
- case TELOPT_BINARY:
- mode(RAW, 0);
- fmt = doopt;
- break;
-
- case TELOPT_ECHO:
- not42 = 0; /* looks like a 4.2 system */
- /*
- * Now, in a 4.2 system, to break them out of ECHOing
- * (to the terminal) mode, we need to send a "WILL ECHO".
- * Kludge upon kludge!
- */
- if (myopts[TELOPT_ECHO] == OPT_YES) {
- dooption(TELOPT_ECHO);
- }
- fmt = dont;
- break;
-
- case TELOPT_TTYPE:
- settimer(ttypeopt);
- if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) {
- hisopts[TELOPT_TTYPE] = OPT_YES;
- return;
- }
- fmt = doopt;
- break;
-
- case TELOPT_NAWS:
- case TELOPT_TSPEED:
- case TELOPT_LFLOW:
- case TELOPT_SGA:
- fmt = doopt;
- break;
-
- case TELOPT_TM:
- fmt = dont;
- break;
-
- default:
- fmt = dont;
- break;
- }
- if (fmt == doopt) {
- hisopts[option] = OPT_YES;
- } else {
- hisopts[option] = OPT_NO;
- }
- (void) sprintf(nfrontp, fmt, option);
- nfrontp += sizeof (dont) - 2;
-}
-
-wontoption(option)
- int option;
-{
- char *fmt;
-
- switch (option) {
- case TELOPT_ECHO:
- not42 = 1; /* doesn't seem to be a 4.2 system */
- break;
-
- case TELOPT_BINARY:
- mode(0, RAW);
- break;
-
- case TELOPT_TTYPE:
- settimer(ttypeopt);
- break;
- }
-
- fmt = dont;
- hisopts[option] = OPT_NO;
- (void) sprintf(nfrontp, fmt, option);
- nfrontp += sizeof (doopt) - 2;
-}
-
-dooption(option)
- int option;
-{
- char *fmt;
-
- switch (option) {
-
- case TELOPT_TM:
- fmt = wont;
- break;
-
- case TELOPT_ECHO:
- mode(ECHO|CRMOD, 0);
- fmt = will;
- break;
-
- case TELOPT_BINARY:
- mode(RAW, 0);
- fmt = will;
- break;
-
- case TELOPT_SGA:
- fmt = will;
- break;
-
- default:
- fmt = wont;
- break;
- }
- if (fmt == will) {
- myopts[option] = OPT_YES;
- } else {
- myopts[option] = OPT_NO;
- }
- (void) sprintf(nfrontp, fmt, option);
- nfrontp += sizeof (doopt) - 2;
-}
-
-
-dontoption(option)
-int option;
-{
- char *fmt;
-
- switch (option) {
- case TELOPT_ECHO: /* we should stop echoing */
- mode(0, ECHO);
- fmt = wont;
- break;
-
- default:
- fmt = wont;
- break;
- }
-
- if (fmt = wont) {
- myopts[option] = OPT_NO;
- } else {
- myopts[option] = OPT_YES;
- }
- (void) sprintf(nfrontp, fmt, option);
- nfrontp += sizeof (wont) - 2;
-}
-
-/*
- * Given a string, assign the "best" speed which we support.
- *
- * "Best" is defined as rounding up, unless what is presented is
- * higher than the highest.
- */
-
-string2speed(s)
- char *s;
-{
- /*
- * The order here is important. The index of each speed needs to
- * correspond with the sgtty structure value for that speed.
- *
- * Additionally, the search algorithm assumes the table is in
- * ascending sequence.
- */
- static int ttyspeeds[] = {
- 0, 50, 75, 110, 134, 150, 200, 300,
- 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400 };
-#define NUMSPEEDS sizeof ttyspeeds/sizeof ttyspeeds[0]
- int i;
- int theirspeed = atoi(s);
-
- for (i = 0; i < NUMSPEEDS; i++) {
- if (ttyspeeds[i] == theirspeed) { /* Exact match */
- return(i);
- } else if (ttyspeeds[i] > theirspeed) {
- if (i > 0) {
- return i-1;
- }
- }
- }
- /* Their number is greater than any of our numbers */
- return(NUMSPEEDS-1);
-}
-
-/*
- * suboption()
- *
- * Look at the sub-option buffer, and try to be helpful to the other
- * side.
- *
- * Currently we recognize:
- *
- * Terminal type is
- * Terminal size
- * Terminal speed is
- */
-
-suboption()
-{
- switch (SB_GET()) {
- case TELOPT_TTYPE: { /* Yaaaay! */
- static char terminalname[5+41] = "TERM=";
-
- settimer(ttypesubopt);
-
- if (SB_GET() != TELQUAL_IS) {
- return; /* ??? XXX but, this is the most robust */
- }
-
- terminaltype = terminalname+strlen(terminalname);
-
- while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
- !SB_EOF()) {
- register int c;
-
- c = SB_GET();
- if (isupper(c)) {
- c = tolower(c);
- }
- *terminaltype++ = c; /* accumulate name */
- }
- *terminaltype = 0;
- terminaltype = terminalname;
- break;
- }
- case TELOPT_NAWS: {
- struct winsize win;
-
- ioctl(pty, TIOCGWINSZ, &win);
- settimer(ttypesubopt);
-
- syslog(LOG_INFO, "%x %x %x %x",
- subpointer[0],subpointer[1],subpointer[2],subpointer[3]);
- win.ws_col = SB_GET() << 8;
- win.ws_col |= SB_GET();
- win.ws_row = SB_GET() << 8;
- win.ws_row |= SB_GET();
- syslog(LOG_INFO, "col %d row %d", win.ws_col, win.ws_row);
- ioctl(pty, TIOCSWINSZ, &win);
- break;
- }
- case TELOPT_TSPEED: {
- char speeds[41],*cp=speeds;
- struct sgttyb b;
- int ispeed,ospeed;
- char *ispeeds,*ospeeds;
-
- if (SB_GET() != TELQUAL_IS) {
- return; /* ??? XXX but, this is the most robust */
- }
-
- ispeeds = NULL;
- ospeeds = speeds;
- ispeed = 0;
- ospeed = 0;
- while ((cp < (speeds + sizeof speeds-1)) && !SB_EOF()) {
- register int c;
-
- c = SB_GET();
- if (c == ',') {
- c = 0;
- ispeeds = cp+1;
- }
- *cp++ = c; /* accumulate name */
- }
- *cp = 0;
-
- if (ispeeds)
- ispeed = string2speed(ispeeds);
- if (ospeeds)
- ospeed = string2speed(ospeeds);
-
- if (ispeed && ospeed) {
- ioctl(pty, TIOCGETP, &b);
- b.sg_ospeed = ospeed;
- b.sg_ispeed = ispeed;
- ioctl(pty, TIOCSETP, &b);
- }
-
- break;
- }
- default:
- ;
- }
-}
-
-mode(on, off)
- int on, off;
-{
- struct sgttyb b;
-
- ptyflush();
- ioctl(pty, TIOCGETP, &b);
- b.sg_flags |= on;
- b.sg_flags &= ~off;
- ioctl(pty, TIOCSETP, &b);
-}
+#ifndef TCSIG
+# ifdef TIOCSIG
+# define TCSIG TIOCSIG
+# endif
+#endif
/*
* Send interrupt to process on other side of pty.
*/
interrupt()
{
- struct sgttyb b;
- struct tchars tchars;
-
ptyflush(); /* half-hearted */
- ioctl(pty, TIOCGETP, &b);
- if (b.sg_flags & RAW) {
- *pfrontp++ = '\0';
- return;
- }
- *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
- '\177' : tchars.t_intrc;
+
+#ifdef TCSIG
+ (void) ioctl(pty, TCSIG, (char *)SIGINT);
+#else /* TCSIG */
+ init_termbuf();
+ *pfrontp++ = slctab[SLC_IP].sptr ? *slctab[SLC_IP].sptr : '\177';
+#endif /* TCSIG */
}
/*
*/
sendbrk()
{
- struct sgttyb b;
- struct tchars tchars;
-
ptyflush(); /* half-hearted */
- ioctl(pty, TIOCGETP, &b);
- if (b.sg_flags & RAW) {
- *pfrontp++ = '\0';
- return;
- }
- *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?
- '\034' : tchars.t_quitc;
-}
-
-ptyflush()
-{
- int n;
-
- if ((n = pfrontp - pbackp) > 0)
- n = write(pty, pbackp, n);
- if (n < 0)
- return;
- pbackp += n;
- if (pbackp == pfrontp)
- pbackp = pfrontp = ptyobuf;
+#ifdef TCSIG
+ (void) ioctl(pty, TCSIG, (char *)SIGQUIT);
+#else /* TCSIG */
+ init_termbuf();
+ *pfrontp++ = slctab[SLC_ABORT].sptr ? *slctab[SLC_ABORT].sptr : '\034';
+#endif /* TCSIG */
}
-\f
-/*
- * nextitem()
- *
- * Return the address of the next "item" in the TELNET data
- * stream. This will be the address of the next character if
- * the current address is a user data character, or it will
- * be the address of the character following the TELNET command
- * if the current address is a TELNET IAC ("I Am a Command")
- * character.
- */
-char *
-nextitem(current)
-char *current;
+sendsusp()
{
- if ((*current&0xff) != IAC) {
- return current+1;
- }
- switch (*(current+1)&0xff) {
- case DO:
- case DONT:
- case WILL:
- case WONT:
- return current+3;
- case SB: /* loop forever looking for the SE */
- {
- register char *look = current+2;
-
- for (;;) {
- if ((*look++&0xff) == IAC) {
- if ((*look++&0xff) == SE) {
- return look;
- }
- }
- }
- }
- default:
- return current+2;
- }
-}
-
-
-/*
- * netclear()
- *
- * We are about to do a TELNET SYNCH operation. Clear
- * the path to the network.
- *
- * Things are a bit tricky since we may have sent the first
- * byte or so of a previous TELNET command into the network.
- * So, we have to scan the network buffer from the beginning
- * until we are up to where we want to be.
- *
- * A side effect of what we do, just to keep things
- * simple, is to clear the urgent data pointer. The principal
- * caller should be setting the urgent data pointer AFTER calling
- * us in any case.
- */
-
-netclear()
-{
- register char *thisitem, *next;
- char *good;
-#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \
- ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
-
- thisitem = netobuf;
-
- while ((next = nextitem(thisitem)) <= nbackp) {
- thisitem = next;
- }
-
- /* Now, thisitem is first before/at boundary. */
-
- good = netobuf; /* where the good bytes go */
-
- while (nfrontp > thisitem) {
- if (wewant(thisitem)) {
- int length;
-
- next = thisitem;
- do {
- next = nextitem(next);
- } while (wewant(next) && (nfrontp > next));
- length = next-thisitem;
- bcopy(thisitem, good, length);
- good += length;
- thisitem = next;
- } else {
- thisitem = nextitem(thisitem);
- }
- }
-
- nbackp = netobuf;
- nfrontp = good; /* next byte to be sent */
- neturg = 0;
-}
-\f
-/*
- * netflush
- * Send as much data as possible to the network,
- * handling requests for urgent data.
- */
-
-
-netflush()
-{
- int n;
-
- if ((n = nfrontp - nbackp) > 0) {
- /*
- * if no urgent data, or if the other side appears to be an
- * old 4.2 client (and thus unable to survive TCP urgent data),
- * write the entire buffer in non-OOB mode.
- */
- if ((neturg == 0) || (not42 == 0)) {
- n = write(net, nbackp, n); /* normal write */
- } else {
- n = neturg - nbackp;
- /*
- * In 4.2 (and 4.3) systems, there is some question about
- * what byte in a sendOOB operation is the "OOB" data.
- * To make ourselves compatible, we only send ONE byte
- * out of band, the one WE THINK should be OOB (though
- * we really have more the TCP philosophy of urgent data
- * rather than the Unix philosophy of OOB data).
- */
- if (n > 1) {
- n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */
- } else {
- n = send(net, nbackp, n, MSG_OOB); /* URGENT data */
- }
- }
- }
- if (n < 0) {
- if (errno == EWOULDBLOCK)
- return;
- /* should blow this guy away... */
- return;
- }
- nbackp += n;
- if (nbackp >= neturg) {
- neturg = 0;
- }
- if (nbackp == nfrontp) {
- nbackp = nfrontp = netobuf;
- }
-}
-
-cleanup()
-{
- char *p;
-
- p = line + sizeof("/dev/") - 1;
- if (logout(p))
- logwtmp(p, "", "");
- (void)chmod(line, 0666);
- (void)chown(line, 0, 0);
- *p = 'p';
- (void)chmod(line, 0666);
- (void)chown(line, 0, 0);
- shutdown(net, 2);
- exit(1);
-}
-
-char editedhost[32];
-
-edithost(pat, host)
- register char *pat;
- register char *host;
-{
- register char *res = editedhost;
-
- if (!pat)
- pat = "";
- while (*pat) {
- switch (*pat) {
-
- case '#':
- if (*host)
- host++;
- break;
-
- case '@':
- if (*host)
- *res++ = *host++;
- break;
-
- default:
- *res++ = *pat;
- break;
-
- }
- if (res == &editedhost[sizeof editedhost - 1]) {
- *res = '\0';
- return;
- }
- pat++;
- }
- if (*host)
- strncpy(res, host, sizeof editedhost - (res - editedhost) - 1);
- else
- *res = '\0';
- editedhost[sizeof editedhost - 1] = '\0';
-}
-
-static char *putlocation;
-
-puts(s)
-register char *s;
-{
-
- while (*s)
- putchr(*s++);
-}
-
-putchr(cc)
-{
- *putlocation++ = cc;
+#ifdef SIGTSTP
+ ptyflush(); /* half-hearted */
+# ifdef TCSIG
+ (void) ioctl(pty, TCSIG, (char *)SIGTSTP);
+# else /* TCSIG */
+ *pfrontp++ = slctab[SLC_SUSP].sptr ? *slctab[SLC_SUSP].sptr : '\032';
+# endif /* TCSIG */
+#endif /* SIGTSTP */
}
-putf(cp, where)
-register char *cp;
-char *where;
+doeof()
{
- char *slash;
- char datebuffer[60];
- extern char *rindex();
-
- putlocation = where;
+ init_termbuf();
- while (*cp) {
- if (*cp != '%') {
- putchr(*cp++);
- continue;
- }
- switch (*++cp) {
-
- case 't':
- slash = rindex(line, '/');
- if (slash == (char *) 0)
- puts(line);
- else
- puts(&slash[1]);
- break;
-
- case 'h':
- puts(editedhost);
- break;
-
- case 'd':
- get_date(datebuffer);
- puts(datebuffer);
- break;
-
- case '%':
- putchr('%');
- break;
- }
- cp++;
- }
+ *pfrontp++ = slctab[SLC_EOF].sptr ? *slctab[SLC_EOF].sptr : '\004';
}