+char *ckxv = "UNIX tty I/O, 5A(089), 8 Feb 92";
+
+/* C K U T I O */
+
+/* C-Kermit interrupt, terminal control & i/o functions for UNIX */
+
+/*
+ Author: Frank da Cruz (fdc@cunixc.cc.columbia.edu, FDCCU@CUVMA.BITNET),
+ Columbia University Center for Computing Activities.
+ First released January 1985.
+ Copyright (C) 1985, 1992, Trustees of Columbia University in the City of New
+ York. Permission is granted to any individual or institution to use this
+ software as long as it is not sold for profit. This copyright notice must be
+ retained. This software may not be included in commercial products without
+ written permission of Columbia University.
+*/
+\f
+/* Includes for all Unixes (conditional includes come later) */
+
+#include "ckcdeb.h" /* This moved to here. */
+#include "ckcnet.h" /* Symbols for network types. */
+
+#include <errno.h> /* System error numbers */
+#ifdef __386BSD__
+#include <sys/errno.h> /* System error numbers */
+/* If you define _POSIX_SOURCE you cannot get ENOTCONN from sys/errno.h */
+#define ENOTCONN 57 /* Socket is not connected */
+#endif /* __386BSD__ */
+
+
+#ifdef SDIRENT /* Directory bits... */
+#define DIRENT
+#endif /* SDIRENT */
+
+#ifdef XNDIR
+#include <sys/ndir.h>
+#else /* !XNDIR */
+#ifdef NDIR
+#include <ndir.h>
+#else /* !NDIR, !XNDIR */
+#ifdef RTU
+#include "/usr/lib/ndir.h"
+#else /* !RTU, !NDIR, !XNDIR */
+#ifdef DIRENT
+#ifdef SDIRENT
+#include <sys/dirent.h>
+#else
+#include <dirent.h>
+#endif /* SDIRENT */
+#else /* !RTU, !NDIR, !XNDIR, !DIRENT, i.e. all others */
+#include <sys/dir.h>
+#endif /* DIRENT */
+#endif /* RTU */
+#endif /* NDIR */
+#endif /* XNDIR */
+
+/* Definition of HZ, used in msleep() */
+
+#ifdef MIPS
+#define HZ ( 1000 / CLOCK_TICK )
+#else
+#ifdef ATTSV
+#ifndef NAP
+#ifndef TRS16
+#include <sys/param.h>
+#else
+#define HZ ( 1000 / CLOCK_TICK )
+#endif /* TRS16 */
+#endif /* NAP */
+#endif /* ATTSV */
+#endif /* MIPS */
+
+#ifdef M_UNIX
+#undef NGROUPS_MAX /* Prevent multiple definition warnings */
+#endif /* M_UNIX */
+
+#include <signal.h> /* Signals */
+
+/* For setjmp and longjmp */
+
+#ifndef ZILOG
+#include <setjmp.h>
+#else
+#include <setret.h>
+#endif /* ZILOG */
+
+/* Maximum length for the name of a tty device */
+
+#ifndef DEVNAMLEN
+#define DEVNAMLEN 25
+#endif
+
+#ifdef NETCONN
+#undef DEVNAMLEN
+#define DEVNAMLEN 50 /* longer field for host:service */
+#endif /* NETCONN */
+
+/*
+ The following test differentiates between 4.1 BSD and 4.2 & later.
+ If you have a 4.1BSD system with the DIRENT library, this test could
+ mistakenly diagnose 4.2BSD and then later enable the use of system calls
+ that aren't defined. If indeed there are such systems, we can use some
+ other way of testing for 4.1BSD, or add yet another compile-time switch.
+*/
+#ifdef BSD4
+#ifdef MAXNAMLEN
+#ifndef FT21
+#ifndef FT18 /* Except for Fortune. */
+#define BSD42
+#endif /* FT18 */
+#endif /* FT21 */
+#endif /* MAXNAMLEN */
+#endif /* BSD4 */
+
+/*
+ Minix support added by Charles Hedrick,
+ Rutgers University: hedrick@aramis.rutgers.edu
+ Minix also has V7 enabled.
+*/
+#ifdef MINIX
+#define TANDEM 0
+#define MYREAD
+#include <limits.h>
+#endif /* MINIX */
+
+#include "ckuver.h" /* Version herald */
+char *ckxsys = HERALD;
+
+/* UUCP lock file name definition */
+
+#ifndef NOUUCP
+
+/* Name of UUCP tty device lock file */
+
+#ifdef ACUCNTRL
+#define LCKDIR
+#endif /* ACUCNTRL */
+/*
+ LOCK_DIR is the name of the lockfile directory.
+ If LOCK_DIR is already defined (e.g. on command line), we don't change it.
+ PIDSTRING means use ASCII string to represent pid in lockfile.
+*/
+#ifndef LOCK_DIR
+#ifdef RTAIX /* IBM RT PC AIX 2.2.1 */
+#define PIDSTRING
+#define LOCK_DIR "/etc/locks";
+#else
+#ifdef AIXRS
+#define PIDSTRING
+#define LOCK_DIR "/etc/locks";
+#else
+#ifdef ISIII
+#define LOCK_DIR "/etc/locks";
+#else
+#ifdef HDBUUCP
+#define PIDSTRING
+#ifdef M_SYS5 /* wht@n4hgf - SCO */
+#define LOCK_DIR "/usr/spool/uucp";
+#else
+#ifdef M_UNIX
+#define LOCK_DIR "/usr/spool/uucp";
+#else
+#ifdef SVR4
+#define LOCK_DIR "/var/spool/locks";
+#else
+#ifdef SUNOS4
+#define LOCK_DIR "/var/spool/locks";
+#else
+#define LOCK_DIR "/usr/spool/locks";
+#endif /* SUNOS4 */
+#endif /* SVR4 */
+#endif /* M_UNIX */
+#endif /* M_SYS5 */
+#else
+#ifdef LCKDIR
+#define LOCK_DIR "/usr/spool/uucp/LCK";
+#else
+#define LOCK_DIR "/usr/spool/uucp";
+#endif /* LCKDIR */
+#endif /* HDBUUCP */
+#endif /* ISIII */
+#endif /* AIXRS */
+#endif /* RTAIX */
+#endif /* !LOCK_DIR (outside ifndef) */
+
+#endif /* !NOUUCP */
+
+#ifdef ATTSV
+#define MYREAD
+#endif /* ATTSV */
+
+#ifdef ATT7300
+#define MYREAD
+/* bits for attmodem: internal modem in use, restart getty */
+#define ISMODEM 1
+#define DOGETY 512
+#endif /* ATT7300 */
+
+#ifdef BSD42
+#define MYREAD
+#endif /* BSD42 */
+
+#ifdef POSIX
+#define MYREAD
+#endif /* POSIX */
+\f
+/*
+ Variables available to outside world:
+
+ dftty -- Pointer to default tty name string, like "/dev/tty".
+ dfloc -- 0 if dftty is console, 1 if external line.
+ dfprty -- Default parity
+ dfflow -- Default flow control
+ ckxech -- Flag for who echoes console typein:
+ 1 - The program (system echo is turned off)
+ 0 - The system (or front end, or terminal).
+ functions that want to do their own echoing should check this flag
+ before doing so.
+
+ flfnam -- Name of lock file, including its path, e.g.,
+ "/usr/spool/uucp/LCK..cul0" or "/etc/locks/tty77"
+ lkflfn -- Name of link to lock file, including its paths
+ haslock -- Flag set if this kermit established a uucp lock.
+ backgrd -- Flag indicating program executing in background ( & on
+ end of shell command). Used to ignore INT and QUIT signals.
+ rtu_bug -- Set by stptrap(). RTU treats ^Z as EOF (but only when we handle
+ SIGTSTP)
+
+ Functions for assigned communication line (either external or console tty):
+
+ sysinit() -- System dependent program initialization
+ syscleanup() -- System dependent program shutdown
+ ttopen(ttname,local,mdmtyp,timo) -- Open the named tty for exclusive access.
+ ttclos() -- Close & reset the tty, releasing any access lock.
+ ttsspd(cps) -- Set the transmission speed of the tty.
+ ttgspd() -- Get (read) the the transmission speed of the tty.
+ ttpkt(speed,flow,parity) -- Put the tty in packet mode and set the speed.
+ ttvt(speed,flow) -- Put the tty in virtual terminal mode.
+ or in DIALING or CONNECTED modem control state.
+ ttres() -- Restore original tty modes.
+ ttscarr(carrier) -- Set carrier control mode, on/off/auto.
+ ttinl(dest,max,timo) -- Timed read line from the tty.
+ ttinc(timo) -- Timed read character from tty.
+ myread() -- Raw mode bulk buffer read, gives subsequent
+ chars one at a time and simulates FIONREAD.
+ myunrd(c) -- Places c back in buffer to be read (one only)
+ ttchk() -- See how many characters in tty input buffer.
+ ttxin(n,buf) -- Read n characters from tty (untimed).
+ ttol(string,length) -- Write a string to the tty.
+ ttoc(c) -- Write a character to the tty.
+ ttflui() -- Flush tty input buffer.
+ ttsndb() -- Send BREAK signal.
+ ttsndlb() -- Send Long BREAK signal.
+
+ ttlock(ttname) -- "Lock" tty device against uucp collisions.
+ ttunlck() -- Unlock tty device.
+
+ For ATT7300/Unix PC, System V:
+ attdial(ttname,speed,telnbr) -- dials ATT7300/Unix PC internal modem
+ offgetty(ttname) -- Turns off getty(1m) for comms line
+ ongetty(ttname) -- Restores getty() to comms line
+*/
+\f
+/*
+Functions for console terminal:
+
+ congm() -- Get console terminal modes.
+ concb(esc) -- Put the console in single-character wakeup mode with no echo.
+ conbin(esc) -- Put the console in binary (raw) mode.
+ conres() -- Restore the console to mode obtained by congm().
+ conoc(c) -- Unbuffered output, one character to console.
+ conol(s) -- Unbuffered output, null-terminated string to the console.
+ conola(s) -- Unbuffered output, array of strings to the console.
+ conxo(n,s) -- Unbuffered output, n characters to the console.
+ conchk() -- Check if characters available at console (bsd 4.2).
+ Check if escape char (^\) typed at console (System III/V).
+ coninc(timo) -- Timed get a character from the console.
+ congks(timo) -- Timed get keyboard scan code.
+ conint() -- Enable terminal interrupts on the console if not background.
+ connoi() -- Disable terminal interrupts on the console if not background.
+
+Time functions
+
+ msleep(m) -- Millisecond sleep
+ ztime(&s) -- Return pointer to date/time string
+ rtimer() -- Reset timer
+ gtimer() -- Get elapsed time since last call to rtimer()
+*/
+\f
+/* Conditional Includes */
+
+/* Whether to include <sys/file.h> */
+
+#ifdef RTU /* RTU doesn't */
+#define NOFILEH
+#endif /* RTU */
+
+#ifdef CIE /* CIE does. */
+#undef NOFILEH
+#endif /* CIE */
+
+#ifdef BSD41 /* 4.1 BSD doesn't */
+#define NOFILEH
+#endif /* BSD41 */
+
+#ifdef is68k /* is68k (whatever that is) */
+#define NOFILEH
+#endif /* is68k */
+
+#ifdef MINIX /* MINIX */
+#define NOFILEH
+#endif /* MINIX */
+
+#ifdef COHERENT /* Coherent */
+#define NOFILEH
+#endif /* COHERENT */
+
+#ifndef NOFILEH /* Now include if selected. */
+#include <sys/file.h>
+#endif /* NOFILEH */
+
+/* System III, System V */
+
+#ifdef ATTSV
+#include <termio.h>
+#ifdef SVR3 /* Sys V R3 and later */
+#ifdef TERMIOX
+/* Need this for termiox structure, RTS/CTS and DTR/CD flow control */
+#include <termiox.h>
+ struct termiox rctsx;
+#else
+#ifdef STERMIOX
+#include <sys/termiox.h>
+ struct termiox rctsx;
+#endif /* STERMIOX */
+#endif /* TERMIOX */
+#endif /* SVR3 */
+#endif /* ATTSV */
+
+/* POSIX */
+
+#ifdef POSIX /* POSIX uses termios.h */
+#define NCCS 20
+#include <termios.h>
+#ifndef __386BSD__
+#define NOSYSIOCTLH /* No ioctl's allowed. */
+#endif /* __386BSD__ */
+#undef ultrix /* Turn off any ultrix features. */
+#endif /* POSIX */
+
+#ifdef MINIX /* MINIX uses ioctl's */
+#define NOSYSIOCTLH /* but has no <sys/ioctl.h> */
+#endif /* MINIX */
+
+/* Others */
+
+#ifndef NOSYSIOCTLH /* Others use ioctl() */
+#include <sys/ioctl.h>
+#ifdef __386BSD__
+#include <sys/ioctl_compat.h>
+#endif /* __386BSD__ */
+#endif /* NOSYSIOCTLH */
+
+/* Whether to include <fcntl.h> */
+
+#ifndef is68k /* Only a few don't have this one. */
+#ifndef BSD41
+#ifndef FT21
+#ifndef FT18
+#ifndef COHERENT
+#include <fcntl.h>
+#endif /* COHERENT */
+#endif /* FT18 */
+#endif /* FT21 */
+#endif /* BSD41 */
+#endif /* not is68k */
+
+#ifdef COHERENT
+#include <sys/fcntl.h>
+#endif /* COHERENT */
+
+#ifdef ATT7300 /* Unix PC, internal modem dialer */
+#include <sys/phone.h>
+#endif /* ATT7300 */
+
+#ifdef HPUX /* HP-UX variations. */
+#define HPUXJOBCTL
+#include <sys/modem.h> /* HP-UX modem signals */
+#ifdef hp9000s500 /* Model 500 */
+#undef HPUXJOBCTL
+#endif /* hp9000s500 */
+#ifdef HPUXPRE65
+#undef HPUXJOBCTL
+typedef int mflag;
+#endif /* HPUXPRE65 */
+#ifdef HPUXJOBCTL
+#include <sys/bsdtty.h> /* HP-UX Berkeley tty support */
+#endif /* HPUXJOBCTL */
+#endif /* HPUX */
+
+/* BSD, V7, Coherent, Minix, et al. */
+
+#ifndef SVORPOSIX /* Not AT&T Sys V or POSIX */
+#include <sgtty.h> /* So we use <sgtty.h> */
+#ifndef PROVX1 /* Now <sys/time.h> ... */
+#ifndef V7
+#ifndef BSD41
+#ifndef COHERENT
+#include <sys/time.h> /* Clock info (for break generation) */
+#endif /* COHERENT */
+#endif /* BSD41 */
+#endif /* V7 */
+#endif /* PROVX1 */
+#endif /* SVORPOSIX */
+
+#ifdef BSD41 /* BSD 4.1 */
+#include <sys/timeb.h>
+#endif /* BSD41 */
+
+#ifdef FT21 /* For:Pro 2.1 */
+#include <sys/timeb.h>
+#endif /* FT21 */
+
+#ifdef BSD29 /* BSD 2.9 */
+#include <sys/timeb.h>
+#endif /* BSD29 */
+
+#ifdef TOWER1
+#include <sys/timeb.h> /* Clock info for NCR Tower */
+#endif /* TOWER1 */
+
+#ifdef COHERENT
+#include <sys/timeb.h> /* Clock info for NCR Tower */
+#endif /* COHERENT */
+
+#ifdef aegis
+#include "/sys/ins/base.ins.c"
+#include "/sys/ins/error.ins.c"
+#include "/sys/ins/ios.ins.c"
+#include "/sys/ins/sio.ins.c"
+#include "/sys/ins/pad.ins.c"
+#include "/sys/ins/time.ins.c"
+#include "/sys/ins/pfm.ins.c"
+#include "/sys/ins/pgm.ins.c"
+#include "/sys/ins/ec2.ins.c"
+#include "/sys/ins/type_uids.ins.c"
+#include <default_acl.h>
+#undef TIOCEXCL
+#undef FIONREAD
+#endif /* aegis */
+
+#ifdef sxaE50 /* PFU Compact A SX/A TISP V10/L50 */
+#undef FIONREAD
+#endif /* sxaE50 */
+
+/* The following #defines are catch-alls for those systems */
+/* that didn't have or couldn't find <file.h>... */
+
+#ifndef FREAD
+#define FREAD 0x01
+#endif /* FREAD */
+
+#ifndef FWRITE
+#define FWRITE 0x10
+#endif /* FWRITE */
+
+#ifndef O_RDONLY
+#define O_RDONLY 000
+#endif /* O_RDONLY */
+\f
+/* Declarations */
+
+#ifdef _POSIX_SOURCE /* This includes MINIX */
+#include <time.h>
+#ifdef __GNUC__
+#ifdef XENIX
+/*
+ Because Xenix <time.h> doesn't declare time() if we're using gcc.
+*/
+time_t time();
+#endif /* XENIX */
+#endif /* __GNUC__ */
+#else
+time_t time(); /* All Unixes should have this... */
+#endif /* _POSIX_SOURCE */
+
+/* Special stuff for V7 input buffer peeking */
+
+#ifdef V7
+int kmem[2] = { -1, -1};
+char *initrawq(), *qaddr[2]={0,0};
+#define CON 0
+#define TTY 1
+#endif /* V7 */
+
+/* dftty is the device name of the default device for file transfer */
+/* dfloc is 0 if dftty is the user's console terminal, 1 if an external line */
+
+#ifndef DFTTY
+#ifdef PROVX1
+ char *dftty = "/dev/com1.dout"; /* Only example so far of a system */
+ char *dfmdm = "none";
+ int dfloc = 1; /* that goes in local mode by default */
+#else
+#ifdef COHERENT
+ char *dftty = "/dev/modem";
+ char *dfmdm = "none";
+ int dfloc = 1;
+#else
+ char *dftty = CTTNAM; /* Remote by default, use normal */
+ char *dfmdm = "none";
+ int dfloc = 0; /* controlling terminal name. */
+#endif /* COHERENT */
+#endif /* PROVX1 */
+#else
+ char *dftty = DFTTY; /* Default location specified on */
+ char *dfmdm = "none"; /* command line. */
+ int dfloc = 1; /* controlling terminal name. */
+#endif /* DFTTY */
+
+#ifdef RTU
+ int rtu_bug = 0; /* set to 1 when returning from SIGTSTP */
+#endif /* RTU */
+
+ int dfprty = 0; /* Default parity (0 = none) */
+ int ttprty = 0; /* Parity in use. */
+ static int ttpmsk = 0377; /* Parity stripping mask. */
+ int ttmdm = 0; /* Modem in use. */
+ int ttcarr = CAR_AUT; /* Carrier handling mode. */
+ int dfflow = 1; /* Xon/Xoff flow control */
+ int backgrd = 0; /* Assume in foreground (no '&' ) */
+#ifdef ultrix
+ int iniflags = 0; /* fcntl flags for ttyfd */
+#endif /* ultrix */
+ int fdflag = 0; /* Flag for redirected stdio */
+ int ttfdflg = 0; /* Open File descriptor was given */
+ int tvtflg = 0; /* Flag that ttvt has been called */
+ long ttspeed = -1; /* For saving speed */
+ int ttflow = -9; /* For saving flow */
+ int ttld = -1; /* Line discipline */
+
+#ifdef sony_news
+ static int km_con = -1; /* Kanji mode for console tty */
+ static int km_ext = -1; /* Kanji mode for external device */
+#endif /* sony_news */
+
+extern int ttnproto;
+extern int ttnet;
+
+int ckxech = 0; /* 0 if system normally echoes console characters, else 1 */
+
+/* Declarations of variables global within this module */
+
+static time_t tcount; /* Elapsed time counter */
+SIGTYP (*saval)() = NULL; /* For saving alarm handler */
+
+/*
+ BREAKNULS is defined for systems that simulate sending a BREAK signal
+ by sending a bunch of NUL characters at low speed.
+*/
+#ifdef PROVX1
+#ifndef BREAKNULS
+#define BREAKNULS
+#endif /* BREAKNULS */
+#endif /* PROVX1 */
+
+#ifdef V7
+#ifndef BREAKNULS
+#define BREAKNULS
+#endif /* BREAKNULS */
+#endif /* V7 */
+
+#ifdef BREAKNULS
+static char /* A string of nulls */
+*brnuls = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+#endif /* BREAKNULS */
+
+static jmp_buf sjbuf; /* Longjump buffers */
+#ifdef V7
+static jmp_buf jjbuf;
+#endif /* V7 */
+
+/* static */ /* (Not static any more) */
+int ttyfd = -1; /* TTY file descriptor */
+
+int telnetfd = 0; /* File descriptor is for telnet */
+int x25fd = 0; /* File descriptor is for X.25 */
+
+static int lkf = 0, /* Line lock flag */
+ cgmf = 0, /* Flag that console modes saved */
+ xlocal = 0, /* Flag for tty local or remote */
+ curcarr = 0; /* Carrier mode: require/ignore. */
+
+static int netconn = 0; /* 1 if network connection active */
+
+static char escchr; /* Escape or attn character */
+
+#ifdef BSD42
+ static struct timeval tv; /* For getting time, from sys/time.h */
+ static struct timezone tz;
+#endif /* BSD42 */
+
+#ifdef BSD29
+ static long xclock; /* For getting time from sys/time.h */
+ static struct timeb ftp; /* And from sys/timeb.h */
+#endif /* BSD29 */
+
+#ifdef BSD41
+ static long xclock; /* For getting time from sys/time.h */
+ static struct timeb ftp; /* And from sys/timeb.h */
+#endif /* BSD41 */
+
+#ifdef FT21
+ static long xclock; /* For getting time from sys/time.h */
+ static struct timeb ftp; /* And from sys/timeb.h */
+#endif /* FT21 */
+
+#ifdef TOWER1
+ static long xclock; /* For getting time from sys/time.h */
+ static struct timeb ftp; /* And from sys/timeb.h */
+#endif /* TOWER1 */
+
+#ifdef COHERENT
+ static long xclock; /* For getting time from sys/time.h */
+ static struct timeb ftp; /* And from sys/timeb.h */
+#endif /* COHERENT */
+
+#ifdef V7
+ static long xclock;
+#endif /* V7 */
+
+/* sgtty/termio information... */
+
+#ifdef ATTSV
+
+ static struct termio ttold = {0}; /* Init'd for word alignment, */
+ static struct termio ttraw = {0}; /* which is important for some */
+ static struct termio tttvt = {0}; /* systems, like Zilog... */
+ static struct termio ttcur = {0};
+ static struct termio ccold = {0};
+ static struct termio ccraw = {0};
+ static struct termio cccbrk = {0};
+#else
+#ifdef POSIX /* POSIX */
+ static struct termios
+ ttold, ttraw, tttvt, ttcur,
+ ccold, ccraw, cccbrk;
+#else /* BSD, V7, etc */
+ static struct sgttyb /* sgtty info... */
+ ttold, ttraw, tttvt, ttcur, /* for communication line */
+ ccold, ccraw, cccbrk; /* and for console */
+#ifdef TIOCGETC
+#ifdef MINIX
+ static struct sgttyb tchold, tchnoi; /* Special chars */
+#else
+ static struct tchars tchold, tchnoi;
+#endif /* MINIX */
+ static int tcharf;
+#endif /* TIOCGETC */
+#ifdef TIOCGLTC
+#ifdef MINIX
+ static struct sgttyb ltchold, ltchnoi;
+#else
+ static struct ltchars ltchold, ltchnoi;
+#endif /* MINIX */
+ static int ltcharf;
+#endif /* TIOCGLTC */
+ int lmodef = 0; /* Local modes */
+ int lmode = 0;
+#endif /* POSIX */
+#endif /* ATTSV */
+
+#ifdef PROVX1
+ static struct sgttyb ttbuf;
+#endif /* PROVX1 */
+
+#ifdef ultrix
+/* do we really need this? */
+ static struct sgttyb vanilla;
+#endif /* ultrix */
+
+#ifdef ATT7300
+static int attmodem = 0; /* ATT7300 internal-modem status */
+struct updata dialer = {0}; /* Condition dialer for data call */
+#endif /* ATT7300 */
+
+char flfnam[80]; /* uucp lock file path name */
+#ifdef RTAIX
+char lkflfn[80]; /* and possible link to it */
+#endif /* RTAIX */
+int haslock = 0; /* =1 if this kermit locked uucp */
+
+#ifdef SVORPOSIX
+static int conesc = 0; /* set to 1 if esc char (^\) typed */
+#else
+#ifdef V7
+static int conesc = 0;
+#else
+#ifdef C70
+static int conesc = 0;
+#endif /* C70 */
+#endif /* V7 */
+#endif /* ATTSV */
+
+static char ttnmsv[DEVNAMLEN]; /* Copy of open path for tthang */
+
+#ifdef aegis
+static status_$t st; /* error status return value */
+static short concrp = 0; /* true if console is CRP pad */
+#define CONBUFSIZ 10
+static char conbuf[CONBUFSIZ]; /* console readahead buffer */
+static int conbufn = 0; /* # chars in readahead buffer */
+static char *conbufp; /* next char in readahead buffer */
+static uid_$t ttyuid; /* tty type uid */
+static uid_$t conuid; /* stdout type uid */
+
+/* APOLLO Aegis main()
+ * establish acl usage and cleanup handling
+ * this makes sure that CRP pads
+ * get restored to a usable mode
+ */
+main(argc,argv) int argc; char **argv; {
+ status_$t status;
+ pfm_$cleanup_rec dirty;
+
+ PID_T pid = getpid();
+
+ /* acl usage according to invoking environment */
+ default_acl(USE_DEFENV);
+
+ /* establish a cleanup continuation */
+ status = pfm_$cleanup(dirty);
+ if (status.all != pfm_$cleanup_set) {
+ /* only handle faults for the original process */
+ if (pid == getpid() && status.all > pgm_$max_severity) {
+ /* blew up in main process */
+ status_$t quo;
+ pfm_$cleanup_rec clean;
+
+ /* restore the console in any case */
+ conres();
+
+ /* attempt a clean exit */
+ debug(F101, "cleanup fault status", "", status.all);
+
+ /* doexit(), then send status to continuation */
+ quo = pfm_$cleanup(clean);
+ if (quo.all == pfm_$cleanup_set)
+ doexit(pgm_$program_faulted,-1);
+ else if (quo.all > pgm_$max_severity)
+ pfm_$signal(quo); /* blew up in doexit() */
+ }
+ /* send to the original continuation */
+ pfm_$signal(status);
+ /*NOTREACHED*/
+ }
+ return(ckcmai(argc, argv));
+}
+#endif /* aegis */
+\f
+/* ANSI-style prototypes for internal functions. */
+/* Functions used outside this module are prototyped in ckcker.h. */
+
+#ifdef apollo
+_PROTOTYP( SIGTYP timerh, () );
+_PROTOTYP( SIGTYP cctrap, () );
+_PROTOTYP( SIGTYP esctrp, () );
+_PROTOTYP( SIGTYP sig_ign, () );
+#else
+_PROTOTYP( SIGTYP timerh, (int) );
+_PROTOTYP( SIGTYP cctrap, (int) );
+_PROTOTYP( SIGTYP esctrp, (int) );
+#endif /* apollo */
+_PROTOTYP( int do_open, (char *) );
+_PROTOTYP( int ttrpid, (char *) );
+_PROTOTYP( static int ttlock, (char *) );
+_PROTOTYP( static int ttunlck, (void) );
+_PROTOTYP( int mygetbuf, (void) );
+_PROTOTYP( int myfillbuf, (void) );
+_PROTOTYP( VOID conbgt, (void) );
+#ifdef ACUCNTRL
+_PROTOTYP( VOID acucntrl, (char *, char *) );
+#endif /* ACUCNTRL */
+
+#ifdef ATTSV
+_PROTOTYP( int carrctl, (struct termio *, int) );
+#else
+#ifdef POSIX
+_PROTOTYP( int carrctl, (struct termios *, int) );
+#else
+_PROTOTYP( int carrctl, (struct sgttyb *, int) );
+#endif /* POSIX */
+#endif /* ATTSV */
+
+#ifdef ATT7300
+_PROTOTYP( int attdial, (char *, long, char *) );
+_PROTOTYP( int offgetty, (char *) );
+_PROTOTYP( int ongetty, (char *) );
+#endif /* ATT7300 */
+
+#ifdef CK_ANSIC
+static char *
+xxlast(char *s, char c)
+#else
+static char *
+xxlast(s,c) char *s; char c;
+#endif /* CK_ANSIC */
+/* xxlast */ { /* Last occurrence of character c in string s. */
+ int i;
+ for (i = (int)strlen(s); i > 0; i--)
+ if ( s[i-1] == c ) return( s + (i - 1) );
+ return(NULL);
+}
+
+/* Timeout handler for communication line input functions */
+
+SIGTYP
+timerh(foo) int foo; {
+ ttimoff();
+ longjmp(sjbuf,1);
+}
+
+/* Control-C trap for communication line input functions */
+
+int cc_int; /* Flag */
+SIGTYP (* occt)(); /* For saving old SIGINT handler */
+
+SIGTYP
+cctrap(foo) int foo; { /* Needs arg for ANSI C */
+ cc_int = 1; /* signal() prototype. */
+ return;
+}
+
+/* S Y S I N I T -- System-dependent program initialization. */
+
+int
+sysinit() {
+ int x;
+
+ conbgt(); /* See if we're in the background */
+ congm(); /* Get console modes */
+
+#ifdef ultrix
+ gtty(0,&vanilla); /* Get sgtty info */
+ iniflags = fcntl(0,F_GETFL,0); /* Get flags */
+#else
+#ifdef AUX
+ set42sig(); /* Don't ask! (hakanson@cs.orst.edu) */
+#endif /* AUX */
+#endif /* ultrix */
+
+/* Initialize the setuid package. */
+/* Change to the user's real user and group id. */
+/* If this can't be done, don't run at all. */
+
+ if (x = priv_ini()) {
+ if (x | 1) fprintf(stderr,"Fatal: setuid failure.\n");
+ if (x | 2) fprintf(stderr,"Fatal: setgid failure.\n");
+ if (x | 4) fprintf(stderr,"Fatal: C-Kermit setuid to root!\n");
+ exit(1);
+ }
+ return(0);
+}
+
+/* S Y S C L E A N U P -- System-dependent program cleanup. */
+
+int
+syscleanup() {
+#ifdef ultrix
+ stty(0,&vanilla); /* Get sgtty info */
+ fcntl(0,F_SETFL,iniflags); /* Restore flags */
+#endif /* ultrix */
+ /* No need to call anything in the suid package here, right? */
+ return(0);
+}
+\f
+/*
+ Major change in 5A(174). We used to use LPASS8, if it was defined, in all
+ versions except ENCORE, to allow 8-bit data and Xon/Xoff flow control at the
+ same time. But this LPASS8 business seems to have been causing trouble for
+ everybody but me! For example, Annex terminal servers, commonly used with
+ Encore computers, do not support LPASS8 even though the Encore itself does.
+ Now, reportedly, even vanilla 4.3 BSD systems can't do this. So let's turn
+ it off for everybody. That means we goes back to using
+ raw mode, with no flow control. Phooey.
+*/
+#ifdef LPASS8
+#undef LPASS8
+#endif /* LPASS8 */
+
+/* T T O P E N -- Open a tty for exclusive access. */
+
+/*
+ Call with:
+ ttname: character string - device name or network host name.
+ lcl:
+ If called with lcl < 0, sets value of lcl as follows:
+ 0: the terminal named by ttname is the job's controlling terminal.
+ 1: the terminal named by ttname is not the job's controlling terminal.
+ But watch out: if a line is already open, or if requested line can't
+ be opened, then lcl remains (and is returned as) -1.
+ modem:
+ Less than zero: ttname is a network host name.
+ Zero or greater: ttname is a terminal device name.
+ Zero means a local connection (don't use modem signals).
+ Positive means use modem signals.
+ timo:
+ 0 = no timer.
+ nonzero = number of seconds to wait for open() to return before timing out.
+
+ Returns:
+ 0 on success
+ -5 if device is in use
+ -4 if access to device is denied
+ -3 if access to lock directory denied
+ -2 upon timeout waiting for device to open
+ -1 on other error
+*/
+static int ttotmo = 0; /* Timeout flag */
+/* Flag kept here to avoid being clobbered by longjmp. */
+
+int
+ttopen(ttname,lcl,modem,timo) char *ttname; int *lcl, modem, timo; {
+
+#ifdef SVORPOSIX
+#ifndef CIE
+ extern char *ctermid(); /* Wish they all had this! */
+#else /* CIE Regulus */
+#define ctermid(x) strcpy(x,"")
+#endif /* CIE */
+#endif /* SVORPOSIX */
+
+ char *x; /* what's this ? */
+
+#ifndef MINIX
+ extern char* ttyname();
+#endif /* MINIX */
+ char cname[DEVNAMLEN+4];
+
+#define NAMEFD /* Feature to allow name to be an open file descriptor */
+
+#ifdef NAMEFD
+ int i, j; char *p;
+
+ debug(F101,"ttopen telnetfd","",telnetfd);
+#endif /* NAMEFD */
+
+ debug(F111,"ttopen entry modem",ttname,modem);
+ debug(F101," ttyfd","",ttyfd);
+ debug(F101," lcl","",*lcl);
+
+ if (ttyfd > -1) { /* if device already opened */
+ if (strncmp(ttname,ttnmsv,DEVNAMLEN)) /* are new & old names equal? */
+ ttclos(ttyfd); /* no, close old ttname, open new */
+ else /* else same, ignore this call, */
+ return(0); /* and return. */
+ }
+
+#ifdef NETCONN
+ if (modem < 0) { /* modem < 0 = special code for net */
+ int x;
+ ttmdm = modem;
+ modem = -modem; /* Positive network type number */
+ fdflag = 0; /* Stdio not redirected. */
+ netconn = 1; /* And it's a network connection */
+ debug(F111,"ttopen net",ttname,modem);
+#ifdef NAMEFD
+ for (p = ttname; isdigit(*p); p++) ; /* Check for all digits */
+ if (*p == '\0') {
+ ttyfd = atoi(ttname); /* Is there a way to test it's open? */
+ ttfdflg = 1; /* We got an open file descriptor */
+ debug(F111,"ttopen got open network fd",ttname,ttyfd);
+ strncpy(ttnmsv,ttname,DEVNAMLEN); /* Remember the "name". */
+ x = 1; /* Return code is "good". */
+ if (telnetfd) {
+ ttnet = NET_TCPB;
+ ttnproto = NP_TELNET;
+#ifdef SUNX25
+ } else if (x25fd) {
+ ttnet = NET_SX25;
+ ttnproto = NP_NONE;
+#endif /* SUNX25 */
+ }
+ } else { /* Host name or address given */
+#endif /* NAMEFD */
+ x = netopen(ttname, lcl, modem); /* (see ckcnet.h) */
+ if (x > -1) {
+ strncpy(ttnmsv,ttname,DEVNAMLEN);
+ } else netconn = 0;
+#ifdef NAMEFD
+ }
+#endif /* NAMEFD */
+
+#ifdef sony_news /* Sony NEWS */
+ if (ioctl(ttyfd,TIOCKGET,&km_ext) < 0) { /* Get Kanji mode */
+ perror("ttopen error getting Kanji mode (network)");
+ debug(F111,"ttopen error getting Kanji mode","network",0);
+ km_ext = -1; /* Make sure this stays undefined. */
+ }
+#endif /* sony_news */
+
+ xlocal = *lcl = 1; /* Network connections are local. */
+ debug(F101,"ttopen net x","",x);
+
+ if (x > -1 && !x25fd)
+ x = tn_ini(); /* Initialize TELNET protocol */
+ return(x);
+ } else { /* Terminal device */
+#endif /* NETCONN */
+
+#ifdef NAMEFD
+/*
+ This code lets you give Kermit an open file descriptor for a serial
+ communication device, rather than a device name. Kermit assumes that the
+ line is already open, locked, conditioned with the right parameters, etc.
+*/
+ for (p = ttname; isdigit(*p); p++) ; /* Check for all digits */
+ if (*p == '\0') {
+ ttyfd = atoi(ttname); /* Is there a way to test it's open? */
+ debug(F111,"ttopen got open fd",ttname,ttyfd);
+ strncpy(ttnmsv,ttname,DEVNAMLEN); /* Remember the "name". */
+ xlocal = *lcl = 1; /* Assume it's local. */
+ netconn = 0; /* Assume it's not a network. */
+ tvtflg = 0; /* Might need to initialize modes. */
+ ttmdm = modem; /* Remember modem type. */
+ fdflag = 0; /* Stdio not redirected. */
+ ttfdflg = 1; /* Flag we were opened this way. */
+
+#ifdef sony_news /* Sony NEWS */
+ /* Get device Kanji mode */
+ if (ioctl(ttyfd,TIOCKGET,&km_ext) < 0) {
+ perror("ttopen error getting Kanji mode");
+ debug(F101,"ttopen error getting Kanji mode","",0);
+ km_ext = -1; /* Make sure this stays undefined. */
+ }
+#endif /* sony_news */
+ return(0); /* Return success */
+ }
+#endif /* NAMEFD */
+#ifdef NETCONN
+ }
+#endif /* NETCONN */
+
+/* Here we have to open a serial device of the given name. */
+
+ occt = signal(SIGINT, cctrap); /* Set Control-C trap, save old one */
+
+ tvtflg = 0; /* Flag for use by ttvt(). */
+ /* 0 = ttvt not called yet for this device */
+
+ fdflag = (!isatty(0) || !isatty(1)); /* Flag for stdio redirected */
+ debug(F101,"ttopen fdflag","",fdflag);
+
+ ttmdm = modem; /* Make this available to other fns */
+ xlocal = *lcl; /* Make this available to other fns */
+
+/* Code for handling bidirectional tty lines goes here. */
+/* Use specified method for turning off logins and suppressing getty. */
+
+#ifdef ACUCNTRL
+ /* Should put call to priv_on() here, but that would be very risky! */
+ acucntrl("disable",ttname); /* acucntrl() program. */
+ /* and priv_off() here... */
+#else
+#ifdef ATT7300
+ if ((attmodem & DOGETY) == 0) /* offgetty() program. */
+ attmodem |= offgetty(ttname); /* Remember response. */
+#endif /* ATT7300 */
+#endif /* ACUCNTRL */
+
+/*
+ In the following section, we open the tty device for read/write.
+ If a modem has been specified via "set modem" prior to "set line"
+ then the O_NDELAY parameter is used in the open, provided this symbol
+ is defined (e.g. in fcntl.h), so that the program does not hang waiting
+ for carrier (which in most cases won't be present because a connection
+ has not been dialed yet). O_NDELAY is removed later on in ttopen(). It
+ would make more sense to first determine if the line is local before
+ doing this, but because ttyname() requires a file descriptor, we have
+ to open it first. See do_open().
+
+ Now open the device using the desired treatment of carrier.
+ If carrier is REQUIRED, then open could hang forever, so an optional
+ timer is provided. If carrier is not required, the timer should never
+ go off, and should do no harm...
+*/
+ ttotmo = 0; /* Flag no timeout */
+ saval = signal(SIGALRM,timerh); /* Timed, set up timer. */
+ if (timo > 0) {
+ alarm(timo); /* Timed open() */
+ if (setjmp(sjbuf)) {
+ ttotmo = 1; /* Flag timeout. */
+ } else ttyfd = do_open(ttname);
+ ttimoff();
+ debug(F111,"ttopen","modem",modem);
+ debug(F101," ttyfd","",ttyfd);
+ debug(F101," alarm return","",ttotmo);
+ } else ttyfd = do_open(ttname);
+ debug(F111,"ttopen ttyfd",ttname,ttyfd);
+ if (ttyfd < 0) { /* If couldn't open, fail. */
+#ifdef ATT7300
+ if (attmodem & DOGETY) /* was getty(1m) running before us? */
+ ongetty(ttnmsv); /* yes, restart on tty line */
+ attmodem &= ~DOGETY; /* no phone in use, getty restored */
+#else
+#if ACUCNTRL
+ /* Should put call to priv_on() here, but that would be risky! */
+ acucntrl("enable",ttname); /* acucntrl() program. */
+ /* and priv_off() here... */
+#endif /* ACUNTRL */
+#endif /* ATT7300 */
+
+ signal(SIGINT,occt); /* Put old Ctrl-C trap back. */
+ if (errno == EACCES) { /* Device is protected against user */
+ perror(ttname); /* Print message */
+ debug(F111,"ttopen tty access denied",ttname,errno);
+ return(-4);
+ } else return(ttotmo ? -2 : -1);
+ }
+
+ /* Make sure it's a real tty. */
+ if (!isatty(ttyfd)) {
+ fprintf(stderr,"%s is not a tty!\n",ttname);
+ debug(F110,"ttopen not a tty",ttname,0);
+ close(ttyfd);
+ ttyfd = -1;
+ signal(SIGINT,occt);
+ return(-1);
+ }
+
+#ifdef aegis
+ /* Apollo C runtime claims that console pads are tty devices, which
+ * is reasonable, but they aren't any good for packet transfer. */
+ ios_$inq_type_uid((short)ttyfd, ttyuid, st);
+ if (st.all != status_$ok) {
+ fprintf(stderr, "problem getting tty object type: ");
+ error_$print(st);
+ } else if (ttyuid != sio_$uid) { /* reject non-SIO lines */
+ close(ttyfd); ttyfd = -1;
+ errno = ENOTTY; perror(ttname);
+ signal(SIGINT,occt);
+ return(-1);
+ }
+#endif /* aegis */
+
+ strncpy(ttnmsv,ttname,DEVNAMLEN); /* Keep copy of name locally. */
+
+/* Caller wants us to figure out if line is controlling tty */
+
+ if (*lcl < 0) {
+ int x0 = 0, x1 = 0;
+ if (strcmp(ttname,CTTNAM) == 0) { /* "/dev/tty" always remote */
+ xlocal = 0;
+ debug(F111," ttname=CTTNAM",ttname,xlocal);
+
+ /* If any of 0, 1, or 2 not redirected, we can use ttyname() to get */
+ /* the name of the controlling terminal... */
+
+ } else if ((x0 = isatty(0)) || (x1 = isatty(1)) || isatty(2)) {
+#ifndef MINIX
+ if (x0)
+ x = ttyname(0); /* and compare it with the */
+ else if (x1) /* tty device name. */
+ x = ttyname(1);
+ else x = ttyname(2);
+ strncpy(cname,x,DEVNAMLEN); /* (copy from internal static buf) */
+ debug(F110," cname",x,0);
+ x = ttyname(ttyfd); /* Gat real name of ttname. */
+ xlocal = (strncmp(x,cname,DEVNAMLEN) == 0) ? 0 : 1; /* Compare. */
+ debug(F111," ttyname",x,xlocal);
+#else
+ xlocal = 1; /* Can't do this test in MINIX */
+#endif /* MINIX */
+ } else { /* Else, if stdin redirected... */
+#ifdef SVORPOSIX
+/* System V provides nice ctermid() function to get name of controlling tty */
+ ctermid(cname); /* Get name of controlling terminal */
+ debug(F110," ctermid",cname,0);
+ x = ttyname(ttyfd); /* Compare with name of comm line. */
+ xlocal = (strncmp(x,cname,DEVNAMLEN) == 0) ? 0 : 1;
+ debug(F111," ttyname",x,xlocal);
+#else
+/* Just assume local */
+ xlocal = 1;
+#endif /* SVORPOSIX */
+ debug(F101," redirected stdin","",xlocal);
+ }
+ }
+
+#ifndef NOFDZERO
+/* Note, the following code was added so that Unix "idle-line" snoopers */
+/* would not think Kermit was idle when it was transferring files, and */
+/* maybe log people out. */
+ if (xlocal == 0) { /* Remote mode */
+ if (fdflag == 0) { /* Standard i/o is not redirected */
+ debug(F100,"ttopen setting ttyfd = 0","",0);
+ close(ttyfd); /* Use file descriptor 0 */
+ ttyfd = 0;
+ } else { /* Standard i/o is redirected */
+ debug(F101,"ttopen stdio redirected","",ttyfd);
+ }
+ }
+#endif /* NOFDZERO */
+
+/* Now check if line is locked -- if so fail, else lock for ourselves */
+/* Note: After having done this, don't forget to delete the lock if you */
+/* leave ttopen() with an error condition. */
+
+ lkf = 0; /* Check lock */
+ if (xlocal > 0) {
+ int xx; int xpid;
+ if ((xx = ttlock(ttname)) < 0) { /* Can't lock it. */
+ debug(F111,"ttopen ttlock fails",ttname,xx);
+ close(ttyfd); /* Close the device. */
+ ttyfd = -1; /* Erase its file descriptor. */
+ signal(SIGINT,occt); /* Put old SIGINT back. */
+ if (xx == -2) { /* If lockfile says tty is in use, */
+ char *p = malloc(200); /* print an ls -l listing */
+ if (p) { /* if we can get space... */
+ sprintf(p,"/bin/ls -l %s",flfnam);
+ zsyscmd(p); /* Get listing. */
+ free(p); /* free the space */
+ xpid = ttrpid(flfnam); /* Try to read pid from lockfile */
+ priv_off(); /* Turn privs back off. */
+ if (xpid > -1) printf("pid = %d\n",xpid); /* show pid */
+ }
+ return(-5); /* Code for device in use */
+ } else return(-3); /* Access denied */
+ } else lkf = 1;
+ }
+
+/* Got the line, now set the desired value for local. */
+
+ if (*lcl != 0) *lcl = xlocal;
+
+/* Some special stuff for v7... */
+
+#ifdef V7
+#ifndef MINIX
+ if (kmem[TTY] < 0) { /* If open, then skip this. */
+ qaddr[TTY] = initrawq(ttyfd); /* Init the queue. */
+ if ((kmem[TTY] = open("/dev/kmem", 0)) < 0) {
+ fprintf(stderr, "Can't read /dev/kmem in ttopen.\n");
+ perror("/dev/kmem");
+ exit(1);
+ }
+ }
+#endif /* !MINIX */
+#endif /* V7 */
+
+/* No failure returns after this point */
+
+#ifdef ultrix
+#ifdef TIOCSINUSE
+ if (xlocal && ioctl(ttyfd, TIOCSINUSE, NULL) < 0) {
+ fprintf(stderr, "Can't set in-use flag on modem.\n");
+ perror("TIOCSINUSE");
+ }
+#endif /* TIOCSINUSE */
+#endif /* ultrix */
+
+/* Get tty device settings */
+
+#ifdef ATTSV /* AT&T UNIX */
+ ioctl(ttyfd,TCGETA,&ttold);
+ debug(F101,"ttopen ioctl TCGETA ttold.c_lflag","",ttold.c_lflag);
+ ioctl(ttyfd,TCGETA,&ttraw);
+ ioctl(ttyfd,TCGETA,&tttvt);
+#else
+#ifdef POSIX /* POSIX */
+ tcgetattr(ttyfd,&ttold);
+ debug(F101,"ttopen tcgetattr ttold.c_lflag","",ttold.c_lflag);
+ tcgetattr(ttyfd,&ttraw);
+ tcgetattr(ttyfd,&tttvt);
+#else /* BSD, V7, and all others */
+ gtty(ttyfd,&ttold);
+ debug(F101,"ttopen gtty ttold.sg_flags","",ttold.sg_flags);
+
+#ifdef sony_news /* Sony NEWS */
+ if (ioctl(ttyfd,TIOCKGET,&km_ext) < 0) { /* Get console Kanji mode */
+ perror("ttopen error getting Kanji mode");
+ debug(F101,"ttopen error getting Kanji mode","",0);
+ km_ext = -1; /* Make sure this stays undefined. */
+ }
+#endif /* sony_news */
+
+#ifdef TIOCGETC
+ tcharf = 0; /* In remote mode, also get */
+ if (xlocal == 0) { /* special characters */
+ if (ioctl(ttyfd,TIOCGETC,&tchold) < 0) {
+ debug(F100,"ttopen TIOCGETC failed","",0);
+ } else {
+ tcharf = 1; /* It worked. */
+ ioctl(ttyfd,TIOCGETC,&tchnoi); /* Get another copy */
+ debug(F100,"ttopen TIOCGETC ok","",0);
+ }
+ }
+#else
+ debug(F100,"ttopen TIOCGETC not defined","",0);
+#endif /* TIOCGETC */
+
+#ifdef TIOCGLTC
+ ltcharf = 0; /* In remote mode, also get */
+ if (xlocal == 0) { /* local special characters */
+ if (ioctl(ttyfd,TIOCGLTC,<chold) < 0) {
+ debug(F100,"ttopen TIOCGLTC failed","",0);
+ } else {
+ ltcharf = 1; /* It worked. */
+ ioctl(ttyfd,TIOCGLTC,<chnoi); /* Get another copy */
+ debug(F100,"ttopen TIOCGLTC ok","",0);
+ }
+ }
+#else
+ debug(F100,"ttopen TIOCGLTC not defined","",0);
+#endif /* TIOCGLTC */
+
+#ifdef TIOCLGET
+ lmodef = 0;
+ if (ioctl(ttyfd,TIOCLGET,&lmode) < 0) {
+ debug(F100,"ttopen TIOCLGET failed","",0);
+ } else {
+ lmodef = 1;
+ debug(F100,"ttopen TIOCLGET ok","",0);
+ }
+#endif /* TIOCLGET */
+
+ gtty(ttyfd,&ttraw); /* And a copy of it for packets*/
+ gtty(ttyfd,&tttvt); /* And one for virtual tty service */
+
+#endif /* POSIX */
+#endif /* ATTSV */
+
+/* Section for changing line discipline. It's restored in ttres(). */
+
+#ifdef BSD41
+/* For 4.1BSD only, force "old" tty driver, new one botches TANDEM. */
+ { int k;
+ ioctl(ttyfd, TIOCGETD, &ttld); /* Get and save line discipline */
+ debug(F101,"4.1bsd line discipline","",ttld);
+ k = OTTYDISC; /* Switch to "old" discipline */
+ k = ioctl(ttyfd, TIOCSETD, &k);
+ debug(F101,"4.1bsd tiocsetd","",k);
+ }
+#endif /* BSD41 */
+
+#ifdef aegis
+ /* This was previously done before the last two TCGETA or gtty above,
+ * in both the ATTSV and not-ATTSV case. If it is not okay to have only
+ * one copy if it here instead, give us a shout!
+ */
+ sio_$control((short)ttyfd, sio_$raw_nl, false, st);
+ if (xlocal) { /* ignore breaks from local line */
+ sio_$control((short)ttyfd, sio_$int_enable, false, st);
+ sio_$control((short)ttyfd, sio_$quit_enable, false, st);
+ }
+#endif /* aegis */
+
+#ifdef VXVE
+ ttraw.c_line = 0; /* STTY line 0 for VX/VE */
+ tttvt.c_line = 0; /* STTY line 0 for VX/VE */
+ ioctl(ttyfd,TCSETA,&ttraw);
+#endif /* vxve */
+
+/* If O_NDELAY was used during open(), then remove it now. */
+
+#ifdef O_NDELAY
+ if (fcntl(ttyfd, F_GETFL, 0) & O_NDELAY) {
+
+#ifndef aegis
+ if (fcntl(ttyfd,F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NDELAY) < 0 )
+ perror("Can't unset O_NDELAY");
+#endif /* aegis */
+ /* Some systems, notably Xenix (don't know how common this is in
+ * other systems), need special treatment to get rid of the O_NDELAY
+ * behaviour on read() with respect to carrier presence (i.e. read()
+ * returning 0 when carrier absent), even though the above fcntl()
+ * is enough to make read() wait for input when carrier is present.
+ * This magic, in turn, requires CLOCAL for working when the carrier
+ * is absent. But if xlocal == 0, presumably you already have CLOCAL
+ * or you have a carrier, otherwise you wouldn't be running this.
+ */
+#ifdef ATTSV
+ if (xlocal) {
+ ttraw.c_cflag |= CLOCAL;
+ ioctl(ttyfd, TCSETA, &ttraw);
+ }
+#endif /* ATTSV */
+#ifndef SCO3R2
+#ifndef OXOS
+/* Reportedly lets uugetty grab the device in SCO UNIX 3.2 / XENIX 2.3 */
+ close( priv_opn(ttname, O_RDWR) ); /* Magic to force change. */
+#endif /* OXOS */
+#endif /* SCO3R2 */
+ }
+#endif /* O_NDELAY */
+
+/* Instruct the system how to treat the carrier, and set a few other tty
+ * parameters.
+ *
+ * This also undoes the temporary setting of CLOCAL that may have been done
+ * for the close(open()) above (except in Xenix). Also throw in ~ECHO, to
+ * prevent the other end of the line from sitting there talking to itself,
+ * producing garbage when the user performs a connect.
+ *
+ * SCO Xenix unfortunately seems to ignore the actual state of CLOCAL.
+ * Now it thinks CLOCAL is always on. It seems the only real solution for
+ * Xenix is to switch between the lower and upper case device names.
+ *
+ * This section may at some future time expand into setting a complete
+ * collection of tty parameters, or call a function shared with ttpkt()/
+ * ttvt() that does so. On the other hand, the initial parameters are not
+ * that important, since ttpkt() or ttvt() should always fix that before
+ * any communication is done. Well, we'll see...
+ */
+ if (xlocal) {
+ curcarr = -2;
+ carrctl(&ttraw, ttcarr == CAR_ON);
+
+#ifdef SVORPOSIX
+ ttraw.c_lflag &= ~ECHO;
+ ttold.c_lflag &= ~ECHO;
+#ifdef ATTSV
+ ioctl(ttyfd, TCSETA, &ttraw);
+#else
+ tcsetattr(ttyfd, TCSADRAIN, &ttraw);
+#endif /* ATTSV */
+
+#else /* BSD, etc */
+ ttraw.sg_flags &= ~ECHO;
+ ttold.sg_flags &= ~ECHO;
+ stty(ttyfd,&ttraw);
+#endif /* SVORPOSIX */
+/* ttflui(); This fails some reason */
+ }
+
+ /* Get current speed */
+
+ ttspeed = ttgspd();
+ debug(F101,"ttopen ttspeed","",ttspeed);
+
+ /* Done, make entries in debug log, restore Ctrl-C trap, and return. */
+
+ debug(F101,"ttopen, ttyfd","",ttyfd);
+ debug(F101," lcl","",*lcl);
+ debug(F111," lock file",flfnam,lkf);
+ signal(SIGINT,occt);
+ return(0);
+}
+
+
+/* D O _ O P E N -- Do the right kind of open() call for the tty. */
+
+int
+do_open(ttname) char *ttname; {
+
+#ifndef O_NDELAY /* O_NDELAY not defined */
+ return(priv_opn(ttname,2));
+#else /* O_NDELAY defined */
+
+#ifdef ATT7300
+/*
+ Open comms line without waiting for carrier so initial call does not hang
+ because state of "modem" is likely unknown at the initial call -jrd.
+ If this is needed for the getty stuff to work, and the open would not work
+ without O_NDELAY when getty is still on, then this special case is ok.
+ Otherwise, get rid of it. -ske
+*/
+ return(priv_opn(ttname, O_RDWR | O_NDELAY));
+
+#else /* !ATT7300 */
+
+ /* Normal case. Use O_NDELAY according to SET CARRIER. See ttscarr(). */
+
+ return(priv_opn(ttname, O_RDWR | ((ttcarr != CAR_ON) ? O_NDELAY : 0) ));
+
+#endif /* !ATT7300 */
+#endif /* O_NDELAY */
+}
+\f
+/* T T C L O S -- Close the TTY, releasing any lock. */
+
+int
+ttclos(foo) int foo; { /* Arg req'd for signal() prototype */
+ int x = 0;
+
+ debug(F101,"ttclos ttyfd","",ttyfd);
+ if (ttyfd < 0) return(0); /* Wasn't open. */
+
+ if (ttfdflg) return(0); /* If we got ttyfd from another */
+ /* process, don't close it. */
+ tvtflg = 0;
+#ifdef NETCONN
+ if (netconn) { /* Network connection. */
+ debug(F100,"ttclos closing net","",0);
+ netclos(); /* Close it. */
+ netconn = 0;
+ return(0);
+ }
+#endif /* NETCONN */
+#ifdef FT21
+ if (xlocal) ioctl(ttyfd,TIOCHPCL, NULL);
+#endif /* FT21 */
+#ifdef ultrix
+ if (xlocal) ioctl(ttyfd, TIOCNCAR, NULL);
+#endif /* ultrix */
+ if (xlocal) {
+ debug(F100,"ttclos about to call ttunlck","",0);
+ if (ttunlck()) /* Release uucp-style lock */
+ fprintf(stderr,"Warning, problem releasing lock\r\n");
+ debug(F100,"ttclos about to call ttres","",0);
+ }
+ if (ttyfd > 0) {
+ saval = signal(SIGALRM,timerh); /* Enable timer interrupt. */
+ alarm(5); /* Allow 5 seconds for this. */
+ if (setjmp(sjbuf)) { /* Timer went off? */
+ x = -1;
+ } else { /* What we're really trying to do */
+ if (xlocal) {
+ tthang(); /* Hang up first, then... */
+ ttres(); /* reset device modes. */
+ }
+ debug(F101,"ttclos about to call close","",ttyfd);
+ close(ttyfd); /* Close the device. */
+ x = 1;
+ }
+ ttimoff(); /* Turn off timer. */
+ if (x < 0) {
+ fprintf(stderr,"?Timed out closing device: %s\n",ttnmsv);
+ debug(F100,"ttclos timed out","",0);
+ }
+ }
+ ttyfd = -1; /* Invalidate the file descriptor. */
+#ifdef sony_news
+ km_ext = -1; /* Invalidate device's Kanji-mode */
+#endif /* sony_news */
+
+/* For bidirectional lines, restore getty if it was there before. */
+
+#ifdef ACUCNTRL /* 4.3BSD acucntrl() method. */
+ acucntrl("enable",ttnmsv); /* Enable getty on the device. */
+#else
+#ifdef ATT7300 /* ATT UNIX PC (3B1, 7300) method. */
+ if (attmodem & DOGETY) /* Was getty(1m) running before us? */
+ ongetty(ttnmsv); /* Yes, restart getty on tty line */
+ attmodem &= ~DOGETY; /* No phone in use, getty restored */
+#endif /* ATT7300 */
+#endif /* System-dependent getty-restoring methods */
+
+ debug(F100,"ttclos done","",0);
+ return(0);
+}
+
+/* T T H A N G -- Hangup phone line or network connection. */
+/*
+ Returns:
+ 0 if it does nothing.
+ 1 if it believes that it hung up successfully.
+ -1 if it believes that the hangup attempt failed.
+*/
+
+#define HUPTIME 500 /* Milliseconds for hangup */
+
+int
+tthang() {
+ int x = 0; /* Sometimes used as return code. */
+#ifndef POSIX
+ int z; /* worker */
+#endif /* POSIX */
+
+#ifdef SVORPOSIX /* AT&T, POSIX, HPUX declarations. */
+ int spdsav; /* for saving speed */
+#ifdef HPUX
+ mflag dtr_down = 00000000000,
+ modem_rtn;
+ mflag modem_sav;
+ char modem_state[64];
+#endif /* HPUX */
+ int flags; /* fcntl flags */
+ unsigned short ttc_save;
+#endif /* SVORPOSIX */
+
+ if (ttyfd < 0) return(0); /* Don't do this if not open */
+ if (xlocal < 1) return(0); /* Don't do this if not local */
+
+#ifdef NETCONN
+ if (netconn) { /* Network connection. */
+ if (netclos() < 0) return(-1); /* Close it */
+ tvtflg = 0;
+ ttyfd = -1;
+ x = 1;
+ if (netopen(ttnmsv, &x, ttmdm) < 0) /* Open it again */
+ return(-1);
+ else
+ return(1);
+ }
+#endif /* NETCONN */
+
+/* From here down, we handle real tty devices. */
+
+#ifdef POSIX
+/* Should add some error checking here... */
+ spdsav = cfgetospeed(&ttcur); /* Get current speed */
+ cfsetospeed(&ttcur,B0); /* Replace by 0 */
+ tcsetattr(ttyfd,TCSADRAIN,&ttcur); /* Set it */
+ msleep(HUPTIME); /* Sleep */
+ cfsetospeed(&ttcur,spdsav); /* Restore previous speed */
+ tcsetattr(ttyfd,TCSADRAIN,&ttcur);
+ return(1);
+#endif /* POSIX */
+
+#ifdef aegis /* Apollo Aegis */
+ sio_$control((short)ttyfd, sio_$dtr, false, st); /* DTR down */
+ msleep(HUPTIME); /* pause */
+ sio_$control((short)ttyfd, sio_$dtr, true, st); /* DTR up */
+ return(1);
+#endif /* aegis */
+
+#ifdef ANYBSD /* Any BSD version. */
+ debug(F100,"tthang BSD style","",0);
+ if (ioctl(ttyfd,TIOCCDTR,0) < 0) { /* Clear DTR. */
+ debug(F101,"tthang TIOCCDTR fails","",errno);
+ return(-1);
+ }
+ msleep(HUPTIME); /* For about 1/2 sec */
+ errno = 0;
+ x = ioctl(ttyfd,TIOCSDTR,0); /* Restore DTR */
+ if (x < 0) {
+ /*
+ For some reason, this tends to fail with "no such device or address"
+ but the operation still works, probably because of the close/open
+ later on. So let's not scare the user unnecessarily here.
+ */
+ debug(F101,"tthang TIOCSDTR errno","",errno); /* Log the error */
+ x = 1; /* Pretend we succeeded */
+ } else if (x == 0) x = 1; /* Success */
+#ifdef COMMENT
+#ifdef FT21
+ ioctl(ttyfd, TIOCSAVEMODES, 0);
+ ioctl(ttyfd, TIOCHPCL, 0);
+ close(ttyfd); /* Yes, must do this twice */
+ if ((ttyfd = open(ttnmsv,2)) < 0) /* on Fortune computers... */
+ return(-1); /* (but why?) */
+ else x = 1;
+#endif /* FT21 */
+#endif /* COMMENT */
+ close(do_open(ttnmsv)); /* Clear i/o error condition */
+ errno = 0;
+#ifdef COMMENT
+/* This is definitely dangerous. Why was it here? */
+ z = ttvt(ttspeed,ttflow); /* Restore modes. */
+ debug(F101,"tthang ttvt returns","",z);
+ return(z < 0 ? -1 : 1);
+#else
+ return(x);
+#endif /* COMMENT */
+#endif /* ANYBSD */
+
+#ifdef ATTSV
+/* AT&T UNIX section, includes HP-UX and generic AT&T System III/V... */
+
+#ifdef HPUX
+/* Hewlett Packard allows explicit manipulation of modem signals. */
+
+#ifdef COMMENT
+/* Old way... */
+ debug(F100,"tthang HP-UX style","",0);
+ if (ioctl(ttyfd,MCSETAF,&dtr_down) < 0) /* lower DTR */
+ return(-1); /* oops, can't. */
+ msleep(HUPTIME); /* Pause half a second. */
+ x = 1; /* Set return code */
+ if (ioctl(ttyfd,MCGETA,&modem_rtn) > -1) { /* Get line status. */
+ if ((modem_rtn & MDCD) != 0) /* Check if CD is low. */
+ x = -1; /* CD didn't drop, fail. */
+ } else x = -1;
+
+ /* Even if above calls fail, RTS & DTR should be turned back on. */
+ modem_rtn = MRTS | MDTR;
+ if (ioctl(ttyfd,MCSETAF,&modem_rtn) < 0) x = -1;
+ return(x);
+#else
+/* New way, from Hellmuth Michaelis */
+ debug(F100,"tthang HP-UX style, HPUXDEBUG","",0);
+ if (ioctl(ttyfd,MCGETA,&modem_rtn) == -1) { /* Get current status. */
+ debug(F100,"tthang HP-UX: can't get modem lines, NO HANGUP!","",0);
+ return(-1);
+ }
+ sprintf(modem_state,"%#lx",modem_rtn);
+ debug(F110,"tthang HP-UX: modem lines got = ",modem_state,0);
+ modem_sav = modem_rtn; /* save line status */
+ modem_rtn &= ~MDTR; /* DTR bit down */
+ sprintf(modem_state,"%#lx",modem_rtn);
+ debug(F110,"tthang HP-UX: modem lines lowerd DTR = ",modem_state,0);
+ if (ioctl(ttyfd,MCSETAF,&modem_rtn) < 0) { /* lower DTR */
+ debug(F100,"tthang HP-UX: can't lower DTR!","",0);
+ return(-1); /* oops, can't. */
+ }
+ msleep(HUPTIME); /* Pause half a second. */
+ x = 1; /* Set return code */
+ if (ioctl(ttyfd,MCGETA,&modem_rtn) > -1) { /* Get line status. */
+ sprintf(modem_state,"%#lx",modem_rtn);
+ debug(F110,"tthang HP-UX: modem lines got = ",modem_state,0);
+ if ((modem_rtn & MDCD) != 0) { /* Check if CD is low. */
+ debug(F100,"tthang HP-UX: DCD didn't get down!","",0);
+ x = -1; /* CD didn't drop, fail. */
+ } else {
+ debug(F100,"tthang HP-UX: DCD down!","",0);
+ }
+ } else {
+ x = -1;
+ debug(F100,"tthang HP-UX: can't get DCD status !","",0);
+ }
+
+ /* Even if above calls fail, RTS & DTR should be turned back on. */
+
+ modem_sav |= (MRTS | MDTR);
+ if (ioctl(ttyfd,MCSETAF,&modem_sav) < 0) {
+ x = -1;
+ debug(F100,"tthang HP-UX: can't set saved state |=(RTS | DTR)","",0);
+ } else {
+ sprintf(modem_state,"%#lx",modem_sav);
+ debug(F110,"tthang HP-UX: final modem lines = ",modem_state,0);
+ }
+ return(x);
+#endif /* COMMENT */
+
+#else /* AT&T but not HP-UX */
+
+/* SVID for AT&T System V R3 defines ioctl's for handling modem signals. */
+/* It is not known how many, if any, systems actually implement them, */
+/* so we include them here in ifdef's. */
+
+#ifdef TIOCMBIS /* Bit Set */
+#ifdef TIOCMBIC /* Bit Clear */
+#ifdef TIOCM_DTR /* DTR */
+
+/* Clear DTR, sleep 300 msec, turn it back on. */
+/* If any of the ioctl's return failure, go on to the next section. */
+
+ z = TIOCM_DTR; /* Code for DTR. */
+#ifdef TIOCM_RTS /* Lower RTS too if symbol is known. */
+#ifndef _IBMR2 /* Don't do this on RS/6000... */
+ z |= TIOCM_RTS;
+#endif /* _IBMR2 */ /* or DTR will never come up again. */
+#endif /* TIOCM_RTS */
+
+ debug(F101,"tthang TIOCM signal mask","",z);
+#ifndef _IBMR2
+/* SVID says the argument must be a pointer to an int. */
+ if (ioctl(ttyfd,TIOCMBIC,&z) > -1)
+#else
+/* Reportedly, the RS/6000 requires an int, not a pointer. */
+ if (ioctl(ttyfd,TIOCMBIC,z) > -1)
+#endif /* _IBMR2 */
+ { /* Try to lower DTR. */
+ debug(F100,"tthang TIOCMBIC ok","",0);
+ msleep(HUPTIME); /* Pause half a second. */
+#ifndef _IBMR2
+ if (ioctl(ttyfd,TIOCMBIS,&z) > -1) /* Try to turn it back on. */
+#else
+ if (ioctl(ttyfd,TIOCMBIS,z) > -1)
+#endif /* _IBMR2 */
+ {
+ debug(F100,"tthang TIOCMBIS ok","",0);
+ return(1); /* Success, done. */
+ } else { /* Couldn't raise, continue. */
+ debug(F101,"tthang TIOCMBIS errno","",errno);
+ }
+ } else { /* Couldn't lower, continue. */
+ debug(F101,"tthang TIOCMBIC errno","",errno);
+ }
+#endif /* TIOCM_DTR */
+#endif /* TIOCMBIC */
+#endif /* TIOCMBIS */
+
+/*
+ General AT&T UNIX case, not HPUX. The following code is highly suspect. No
+ two AT&T-based systems seem to do this the same way. The object is simply
+ to turn off DTR and then turn it back on. SVID says the universal method
+ for turning off DTR is to set the speed to zero, and this does seem to do
+ the trick in all cases. But neither SVID nor any known man pages say how to
+ turn DTR back on again. Some variants, like most Xenix implementations,
+ raise DTR again when the speed is restored to a nonzero value. Others
+ require the device to be closed and opened again, but this is risky because
+ getty could seize the device during the instant it is closed.
+*/
+
+/* Return code for ioctl failures... */
+#ifdef ATT6300
+ x = 1; /* ATT6300 doesn't want to fail... */
+#else
+ x = -1;
+#endif /* ATT6300 */
+
+ debug(F100,"tthang get settings","",0);
+ if (ioctl(ttyfd,TCGETA,&ttcur) < 0) /* Get current settings. */
+ return(x); /* Fail if this doesn't work. */
+ if ((flags = fcntl(ttyfd,F_GETFL,0)) < 0) /* Get device flags. */
+ return(x);
+ ttc_save = ttcur.c_cflag; /* Remember current speed. */
+ spdsav = ttc_save & CBAUD;
+ debug(F101,"tthang speed","",spdsav);
+
+#ifdef O_NDELAY
+ debug(F100,"tthang O_NDELAY on","",0);
+ fcntl(ttyfd, F_SETFL, flags | O_NDELAY); /* Activate O_NDELAY */
+#endif /* O_NDELAY */
+
+#ifdef ATT7300 /* This is the way it is SUPPOSED to work */
+ ttcur.c_cflag &= ~CBAUD; /* Change the speed to zero. */
+#else
+#ifdef RTAIX
+ ttcur.c_cflag &= ~CBAUD; /* Change the speed to zero. */
+#else /* This way really works but may be dangerous */
+#ifdef u3b2
+ ttcur.c_cflag = ~(CBAUD|CLOCAL); /* Special for AT&T 3B2s */
+ /* (CLOCAL must be OFF) */
+#else
+#ifdef SCO3R2 /* SCO UNIX 3.2 */
+/*
+ This is complete nonsense, but an SCO user claimed this change made
+ hanging up work. Comments from other SCO UNIX 3.2 users would be
+ appreciated.
+*/
+ ttcur.c_cflag = CBAUD|B0;
+#else /* None of the above. */
+ ttcur.c_cflag = CLOCAL|HUPCL; /* Change all but these to zero */
+ /* (CLOCAL must be ON) */
+#endif /* SCO3R2 */
+#endif /* u3b2 */
+#endif /* RTAIX */
+#endif /* ATT7300 */
+
+#ifdef COMMENT
+ /* and if none of those work, try one of these... */
+ ttcur.c_cflag = 0;
+ ttcur.c_cflag = CLOCAL;
+ ttcur.c_cflag &= ~(CBAUD|HUPCL);
+ ttcur.c_cflag &= ~(CBAUD|CREAD);
+ ttcur.c_cflag &= ~(CBAUD|CREAD|HUPCL);
+ /* or other combinations */
+#endif /* COMMENT */
+
+#ifdef TCXONC
+ debug(F100,"tthang TCXONC","",0);
+ ioctl(ttyfd, TCXONC, 1);
+#endif /* TCXONC */
+
+#ifdef TIOCSTART
+ debug(F100,"tthang TIOCSTART","",0);
+ ioctl(ttyfd, TIOCSTART, 0);
+#endif /* TIOCSTART */
+
+ if (ioctl(ttyfd,TCSETAF,&ttcur) < 0) { /* Fail if we can't. */
+ fcntl(ttyfd, F_SETFL, flags); /* Restore flags */
+ return(-1); /* before returning. */
+ }
+ msleep(300); /* Give modem time to notice. */
+
+/* Now, even though it doesn't say this in SVID or any man page, we have */
+/* to close and reopen the device. This is not necessary for all systems, */
+/* but it's impossible to predict which ones need it and which ones don't. */
+
+#ifdef ATT7300
+/*
+ Special handling for ATT 7300 UNIX PC and 3B1, which have "phone"
+ related ioctl's for their internal modems. attmodem has getty status and
+ modem-in-use bit. Reportedly the ATT7300/3B1 PIOCDISC call is necessary,
+ but also ruins the file descriptor, and no other phone(7) ioctl call can fix
+ it. Whateverit does, it seems to escape detection with PIOCGETA and TCGETA.
+ The only way to undo the damage is to close the fd and then reopen it.
+*/
+ if (attmodem & ISMODEM) {
+ debug(F100,"tthang attmodem close/open","",0);
+ ioctl(ttyfd,PIOCUNHOLD,&dialer); /* Return call to handset. */
+ ioctl(ttyfd,PIOCDISC,&dialer); /* Disconnect phone. */
+ close(ttyfd); /* Close and reopen the fd. */
+ ttyfd = priv_opn(ttnmsv, O_RDWR | O_NDELAY);
+ attmodem &= ~ISMODEM; /* Phone no longer in use. */
+ }
+#else /* !ATT7300 */
+/* It seems we have to close and open the device for other AT&T systems */
+/* too, and this is the place to do it. The following code does the */
+/* famous close(open(...)) magic by default. If that doesn't work for you, */
+/* then try uncommenting the following statement or putting -DCLSOPN in */
+/* the makefile CFLAGS. */
+
+/* #define CLSOPN */
+
+#ifndef SCO32 /* Not needed by, and harmful to, SCO UNIX 3.2 / Xenix 2.3 */
+
+#ifdef RTAIX /* RT AIX needs this too... */
+#define CLSOPN
+#endif /* RTAIX */
+
+#ifdef O_NDELAY
+#define OPENFLGS O_RDWR | O_NDELAY
+#else
+#define OPENFLGS O_RDWR
+#endif
+
+#ifndef CLSOPN
+/* This method is used by default. */
+/* It is thought to be safer because there is no window where getty */
+/* can seize control of the device. The drawback is that it might not work. */
+
+ debug(F101,"tthang close(open()), OPENFLGS","",OPENFLGS);
+ close(priv_opn(ttnmsv, OPENFLGS));
+
+#else
+/* This method is used if you #define CLSOPN. It is more likely to work */
+/* than the previous method, but it's also more dangerous. */
+
+ debug(F101,"tthang close/open, OPENFLGS","",OPENFLGS);
+ close(ttyfd);
+ msleep(10);
+#ifdef COMMENT
+/* Why did we return? */
+ if (ttyfd = priv_opn(ttnmsv, OPENFLGS)) return(1);
+#else
+/* We still need to restore the flags, etc. */
+ ttyfd = priv_opn(ttnmsv, OPENFLGS);
+#endif /* COMMENT */
+
+#endif /* CLSOPN */
+#undef OPENFLGS
+
+#endif /* SCO32 */
+#endif /* ATT7300 */
+
+/* Now put all flags & modes back the way we found them. */
+/* (Does the order of ioctl & fcntl matter ? ) */
+
+ debug(F100,"tthang restore settings","",0);
+ ttcur.c_cflag = ttc_save; /* Get old speed back. */
+ if (ioctl(ttyfd,TCSETAF,&ttcur) < 0) /* ioctl parameters. */
+ return(-1);
+ if (fcntl(ttyfd,F_SETFL,flags) < 0) /* fcntl parameters */
+ return(-1);
+
+ return(1);
+#endif /* not HPUX */
+#endif /* ATTSV */
+}
+
+/* T T R E S -- Restore terminal to "normal" mode. */
+
+/* ske@pkmab.se: There are two choices for what this function should do.
+ * (1) Restore the tty to current "normal" mode, with carrier treatment
+ * according to ttcarr, to be used after every kermit command. (2) Restore
+ * the tty to the state it was in before kermit opened it. These choices
+ * conflict, since ttold can't hold both choices of tty parameters. ttres()
+ * is currently being called as in choice (1), but ttold basically holds
+ * the initial parameters, as in (2), and the description at the beginning
+ * of this file says (2).
+ *
+ * I don't think restoring tty parameters after all kermit commands makes
+ * much of a difference. Restoring them upon exit from kermit may be of
+ * some use in some cases (when the line is not restored automatically on
+ * close, by the operating system).
+ *
+ * I can't choose which one it should be, so I haven't changed it. It
+ * probably works as it is, too. It would probably even work even with
+ * ttres() entirely deleted...
+ *
+ * (from fdc: Actually, this function operates in remote mode too, so
+ * it restores the console (command) terminal to whatever mode it was
+ * in before packet operations began, so that commands work right again.)
+ */
+int
+ttres() { /* Restore the tty to normal. */
+ int x;
+
+ if (ttyfd < 0) return(-1); /* Not open. */
+
+ if (ttfdflg) return(0); /* Don't mess with terminal modes if */
+ /* we got ttyfd from another process */
+#ifdef NETCONN
+ if (netconn) return (0); /* Network connection, do nothing */
+#endif /* NETCONN */
+
+/* Real terminal device, so restore its original modes */
+
+#ifdef ATTSV /* For AT&T versions... */
+ x = ioctl(ttyfd,TCSETAW,&ttold); /* Restore tty modes this way. */
+#else
+#ifdef POSIX /* For POSIX like this */
+ x = tcsetattr(ttyfd,TCSADRAIN,&ttold);
+#else /* For all others... */
+ msleep(HUPTIME); /* This replaces sleep(1)... */
+ /* Put back sleep(1) if tty is */
+ /* messed up after close. */
+
+/* Here we restore the modes for BSD */
+
+#ifdef LPASS8 /* Undo "pass8" if it were done */
+ if (lmodef) {
+ if (ioctl(ttyfd,TIOCLSET,&lmode) < 0)
+ debug(F100,"ttres TIOCLSET failed","",0);
+ else
+ debug(F100,"ttres TIOCLSET ok","",0);
+ }
+#endif /* LPASS8 */
+
+#ifdef UTEK /* Undo hardware flow if it were done */
+ if (lmodef) {
+ if (ioctl(ttyfd,TIOCLSET,&lmode) < 0)
+ debug(F100,"ttres TIOCLSET failed","",0);
+ else
+ debug(F100,"ttres TIOCLSET ok","",0);
+ }
+#endif /* UTEK */
+
+#ifdef TIOCGETC /* Put back special characters */
+ if (tcharf && (xlocal == 0)) {
+ if (ioctl(ttyfd,TIOCSETC,&tchold) < 0)
+ debug(F100,"ttres TIOCSETC failed","",0);
+ else
+ debug(F100,"ttres TIOCSETC ok","",0);
+ }
+#endif /* TIOCGETC */
+
+#ifdef TIOCGLTC /* Put back local special characters */
+ if (ltcharf && (xlocal == 0)) {
+ if (ioctl(ttyfd,TIOCSLTC,<chold) < 0)
+ debug(F100,"ttres TIOCSLTC failed","",0);
+ else
+ debug(F100,"ttres TIOCSLTC ok","",0);
+ }
+#endif /* TIOCGLTC */
+
+ x = stty(ttyfd,&ttold); /* restore tty modes the old way. */
+
+#endif /* POSIX */
+#endif /* ATTSV */
+
+ debug(F101,"ttres tty modes restore","",x);
+ if (x < 0) debug(F101,"ttres errno","",errno);
+
+#ifdef BSD41
+ if (ttld > -1) { /* Put back line discipline */
+ x = ioctl(ttyfd, TIOCSETD, &ttld);
+ debug(F101,"ttres line discipline restore","",x);
+ if (x < 0) debug(F101,"...ioctl errno","",errno);
+ ttld = -1;
+ }
+#endif /* BSD41 */
+
+#ifdef sony_news
+ x = xlocal ? km_ext : km_con; /* Restore Kanji mode. */
+ if (x != -1) { /* Make sure we know original modes. */
+ if (ioctl(ttyfd,TIOCKSET, &x) < 0) {
+ perror("ttres can't set Kanji mode");
+ debug(F101,"ttres error setting Kanji mode","",x);
+ return(-1);
+ }
+ }
+ debug(F100,"ttres set Kanji mode ok","",0);
+#endif /* sony_news */
+
+ tvtflg = 0; /* Invalidate terminal mode settings */
+ return(x);
+}
+\f
+/* T T R P I D -- Read pid from lockfile "name" (used by ttlock) */
+
+int
+ttrpid(name) char *name; {
+ int x, fd, pid;
+
+ fd = open(name,O_RDONLY); /* Try to open lockfile. */
+ if (fd > 0) {
+
+#ifdef PIDSTRING
+ char buf[12];
+ x = read(fd, buf, 11); /* For HDP UUCP, read pid string */
+ if (x < 0) return(-1);
+ buf[11] = '\0';
+ if (x == 11)
+ x = sscanf(buf,"%d",&pid); /* Get the integer pid from it. */
+#else
+ x = read(fd, (char *)&pid, sizeof(pid)); /* Else read integer pid */
+#endif /* PIDSTRING */
+
+ if (x < 0) pid = -1; /* Return any errors. */
+ close(fd); /* Close the lockfile. */
+ } else pid = -1;
+ return(pid);
+}
+
+/* T T L O C K */
+
+/*
+ This function attempts to coordinate use of the communication device with
+ other copies of Kermit and any other program that follows the UUCP
+ device-locking conventions, which, unfortunately, vary among different UNIX
+ implementations. The idea is to look for a file of a certain name, the
+ "lockfile", in a certain directory. If such a file is found, then the line
+ is presumed to be in use, and Kermit should not use it. If no such file is
+ found, Kermit attempts to create one so that other programs will not use the
+ same line at the same time. Because the lockfile and/or the directory it's
+ in might lack write permission for the person running Kermit, Kermit could
+ find itself running setuid to uucp or other user that does have the
+ necessary permissions. At startup, Kermit has changed its effective uid to
+ the user's real uid, and so ttlock() must switch back to the original
+ effective uid in order to create the lockfile, and then back again to the
+ real uid to prevent unauthorized access to other directories or files owned
+ by the user the program is setuid to.
+
+ Totally rewritten for C-Kermit 5A to eliminate windows of vulnerability,
+ based on suggestions from Warren Tucker. Call with pointer to name of
+ tty device. Returns:
+
+ 0 on success
+ -1 on failure
+
+ Note: Once privileges are turned on using priv_on(), it is essential that
+ they are turned off again before this function returns.
+*/
+#ifdef SVR4 /* Lockfile uses device numbers. */
+#ifndef LFDEVNO /* Define this for SVR4 */
+#define LFDEVNO /* If anybody else needs it, */
+#endif /* LFDEVNO */ /* define it here or on CC */
+#endif /* SVR4 */ /* command line. */
+
+#ifdef LFDEVNO
+#include <sys/stat.h> /* For major() & minor() macros. */
+ /* Should be in <sys/types.h>. */
+#ifndef major /* If we didn't find it */
+#ifdef SVR4 /* then for Sys V R4 */
+#include <sys/mkdev.h> /* look here */
+#else /* or for Sunos versions */
+#ifdef SUNOS4 /* ... */
+#include <sys/sysmacros.h> /* look here */
+#else /* Otherwise take a chance: */
+#define major(dev) ( (int) ( ((unsigned)(dev) >> 8) & 0xff))
+#define minor(dev) ( (int) ( (dev) & 0xff))
+#endif /* SUNOS4 */
+#endif /* SVR4 */
+#endif /* major */
+#endif /* LFDEVNO */
+/*
+ Note for RS/6000: routines ttylock(devicename), ttyunlock(devicename),
+ and ttylocked(devicename) from the standard library (libc.a) could be
+ used here instead. It's not clear whether there is any advantage in this.
+*/
+static int
+ttlock(ttdev) char *ttdev; {
+ int x;
+
+#ifdef MINIX
+ char *buf;
+#endif /* MINIX */
+
+#ifdef NOUUCP
+ strcpy(flfnam,"NOLOCK");
+ haslock = 1;
+ return(0);
+#else /* !NOUUCP */
+ int lockfd; /* File descriptor for lock file. */
+ PID_T pid; /* Process id of this process. */
+ int fpid; /* pid found in existing lockfile. */
+ int tries; /* How many times we've tried... */
+#ifdef LFDEVNO
+ struct stat devbuf; /* For device numbers (SVR4). */
+#endif /* LFDEVNO */
+
+#ifdef PIDSTRING
+ char pid_str[12]; /* My pid in string format. */
+#endif /* PIDSTRING */
+
+ char *device, *devname;
+
+#define LFNAML 50 /* Max length for lock file name. */
+ char lockfil[LFNAML]; /* Lock file name */
+#ifdef RTAIX
+ char lklockf[LFNAML]; /* Name for link to lock file */
+#endif
+ char tmpnam[LFNAML+30]; /* Temporary lockfile name. */
+ char *lockdir = LOCK_DIR; /* Defined near top of this file, */
+ /* or on cc command line. */
+
+ haslock = 0; /* Not locked yet. */
+ *flfnam = '\0'; /* Lockfile name is empty. */
+ pid = getpid(); /* Get id of this process. */
+
+/* Construct name of lockfile and temporary file */
+
+/* device = name of tty device without the path, e.g. "ttyh8" */
+/* lockfil = name of lock file, without path, e.g. "LCK..ttyh8" */
+
+ device = ((devname=xxlast(ttdev,'/')) != NULL ? devname+1 : ttdev);
+
+#ifdef ISIII /* Interactive System III, PC/IX */
+ strcpy(lockfil, device);
+#else
+#ifdef LFDEVNO /* Lockfilename has device numbers. */
+ if (stat(ttdev,&devbuf) < 0)
+ return(-1);
+ sprintf(lockfil,"LK.%03d.%03d.%03d",
+ major(devbuf.st_dev), /* inode */
+ major(devbuf.st_rdev), /* major device number */
+ minor(devbuf.st_rdev)); /* minor device number */
+
+#else /* Others... */
+#ifdef PTX /* Dynix PTX */
+ if (device != &ttdev[5] && strncmp(ttdev,"/dev/",5)==0) {
+ sprintf(lockfil,"LCK..%.3s%s", &ttdev[5], device);
+ } else
+#endif /* PTX */
+ sprintf(lockfil,"LCK..%s", device);
+#ifdef M_XENIX /* SCO Xenix */
+ { int x; char c;
+ x = (int)strlen(lockfil) - 1; /* Get last letter of device name. */
+ if (x > 0) { /* If it's uppercase, lower it. */
+ c = lockfil[x];
+ if (c >= 'A' && c <= 'Z') lockfil[x] += ('a' - 'A');
+ }
+ }
+#endif /* M_XENIX */
+#ifdef RTAIX
+ strcpy(lklockf,device);
+#endif /* RTAIX */
+#endif /* LFDEVNO */
+#endif /* ISIII */
+
+/* flfnam = full lockfile pathname, e.g. "/usr/spool/uucp/LCK..ttyh8" */
+/* tmpnam = temporary unique, e.g. "/usr/spool/uucp/LTMP..pid" */
+
+ sprintf(flfnam,"%s/%s",lockdir,lockfil);
+#ifdef RTAIX
+ sprintf(lkflfn,"%s/%s",lockdir,lklockf);
+#endif
+ sprintf(tmpnam,"%s/LTMP.%05d",lockdir,(int) pid);
+ debug(F110,"ttlock flfnam",flfnam,0);
+ debug(F110,"ttlock tmpnam",tmpnam,0);
+
+ priv_on(); /* Turn on privileges if possible. */
+ lockfd = creat(tmpnam, 0444); /* Try to create temp lock file. */
+ if (lockfd < 0) { /* Create failed. */
+ debug(F111,"ttlock creat failed",tmpnam,errno);
+ if (errno == ENOENT) {
+ perror(lockdir);
+ printf("UUCP not installed or Kermit misconfigured\n");
+ } else {
+ perror(lockdir);
+ unlink(tmpnam); /* Get rid of the temporary file. */
+ }
+ priv_off(); /* Turn off privileges!!! */
+ return(-1); /* Return failure code. */
+ }
+/* Now write the pid into the temp lockfile in the appropriate format */
+
+#ifdef PIDSTRING /* For Honey DanBer UUCP, */
+ sprintf(pid_str,"%10d\n", (int) pid); /* write pid as decimal string. */
+ write(lockfd, pid_str, 11);
+ debug(F111,"ttlock hdb pid string",pid_str,(int) pid);
+#else /* Others use integer pid */
+ write(lockfd, (char *)&pid, sizeof(pid) );
+ debug(F111,"ttlock pid","",(int) pid);
+#endif /* PIDSTRING */
+
+/* Now try to rename the temp file to the real lock file name. */
+/* This will fail if a lock file of that name already exists. */
+
+ close(lockfd); /* Close the temp lockfile. */
+ chmod(tmpnam,0444); /* Permission for a valid lock. */
+ tries = 0;
+ while (!haslock && tries++ < 2) {
+ haslock = (link(tmpnam,flfnam) == 0); /* Create a link to it. */
+ if (haslock) { /* If we got the lockfile */
+#ifdef RTAIX
+ link(flfnam,lkflfn);
+#endif
+ break; /* we're done. */
+ } else { /* Otherwise, */
+ if ((fpid = ttrpid(flfnam)) > -1) { /* Read pid from lockfile */
+ if (fpid > 0) {
+ debug(F101,"ttlock fpid","",fpid);
+ errno = 0; /* See if process still exists. */
+ x = kill((PID_T) fpid,0);
+ debug(F101,"ttlock kill","",x);
+ debug(F101,"ttlock kill errno","",errno);
+ if (x < 0 && errno == ESRCH) { /* pid is invalid */
+ debug(F111,"ttlock removing stale lock",flfnam,
+ fpid);
+ if (!backgrd)
+ printf(
+ "Removing stale lock %s (pid %d terminated)\n",
+ flfnam,fpid);
+ x = unlink(flfnam); /* remove the lockfile. */
+ debug(F111,"ttlock unlink",flfnam,x);
+ continue; /* and go back and try again. */
+ } else if (x < 0 && errno == EPERM || x == 0) {
+ unlink(tmpnam); /* Delete the tempfile */
+ debug(F101,"ttlock found tty locked","",fpid);
+ priv_off(); /* Turn off privs */
+ return(-2); /* Code for device is in use. */
+ }
+ } else {
+ debug(F101,"ttlock can't get fpid","",fpid);
+ break;
+ }
+ } else break;
+ }
+ }
+ unlink(tmpnam); /* Unlink (remove) the temp file. */
+ priv_off(); /* Turn off privs */
+ return(haslock ? 0 : -1); /* Return link's return code. */
+#endif /* !NOUUCP */
+}
+
+/* T T U N L O C K */
+
+static int
+ttunlck() { /* Remove UUCP lockfile. */
+#ifndef NOUUCP
+ if (haslock && *flfnam) {
+ priv_on(); /* Turn privileges on. */
+ unlink(flfnam); /* Remove the lockfile. */
+#ifdef RTAIX
+ unlink(lkflfn);
+#endif
+ *flfnam = '\0'; /* Erase the name. */
+ priv_off(); /* Turn privileges off. */
+ }
+#endif /* !NOUUCP */
+ return(0);
+}
+
+/* 4.3BSD-style UUCP line direction control (Stan Barber, Rice U) */
+
+#ifdef ACUCNTRL
+VOID
+acucntrl(flag,ttname) char *flag, *ttname; {
+ char x[DEVNAMLEN+32], *device, *devname;
+
+ if (strcmp(ttname,CTTNAM) == 0 || xlocal == 0) /* If not local, */
+ return; /* just return. */
+ device = ((devname = xxlast(ttname,'/')) != NULL ? devname+1 : ttname);
+ if (strncmp(device,"LCK..",4) == 0) device += 5;
+ sprintf(x,"/usr/lib/uucp/acucntrl %s %s",flag,device);
+ debug(F110,"called ",x,0);
+ zsyscmd(x);
+}
+#endif /* ACUCNTRL */
+\f
+/* T T P K T -- Condition the communication line for packets. */
+/* or for modem dialing */
+
+#define DIALING 4 /* flags (via flow) for modem handling */
+#define CONNECT 5
+
+/* If called with speed > -1, also set the speed. */
+
+/* Returns 0 on success, -1 on failure. */
+
+int
+ttpkt(speed,flow,parity) long speed; int flow, parity; {
+ int s2;
+ int s = -1;
+#ifndef SVORPOSIX
+ int x;
+#endif /* SVORPOSIX */
+
+ if (ttyfd < 0) return(-1); /* Not open. */
+
+ debug(F101,"ttpkt parity","",parity);
+ debug(F101,"ttpkt flow","",flow);
+ debug(F101,"ttpkt speed","",(int) speed);
+
+ ttprty = parity; /* Let other tt functions see these. */
+ ttpmsk = ttprty ? 0177 : 0377; /* Parity stripping mask */
+ ttspeed = speed;
+ ttflow = flow; /* Now make this available too. */
+
+#ifdef NETCONN /* Nothing to do for telnet */
+ if (netconn) return (0);
+#endif /* NETCONN */
+
+ if (ttfdflg && !isatty(ttyfd)) return(0);
+
+#ifndef SVORPOSIX /* Berkeley, V7, etc. */
+#ifdef LPASS8
+/*
+ For some reason, with BSD terminal drivers, you can't set FLOW to XON/XOFF
+ after having previously set it to NONE without closing and reopening the
+ device. Unless there's something I overlooked below...
+*/
+ if (ttflow == FLO_NONE && flow == FLO_XONX && xlocal == 0) {
+ debug(F101,"ttpkt executing horrible flow kludge","",0);
+ ttclos(0); /* Close it */
+ x = 0;
+ ttopen(ttnmsv,&x,ttmdm,0); /* Open it again */
+ }
+#endif /* LPASS8 */
+#endif /* SVORPOSIX */
+
+ if (xlocal) {
+ s2 = (int) (speed / 10L); /* Convert bps to cps */
+ s = ttsspd(s2); /* Check and set the speed */
+ debug(F101,"ttpkt carrier","",flow);
+ carrctl(&ttraw, flow != DIALING /* Carrier control */
+ && (ttcarr == CAR_ON));
+ tvtflg = 0; /* So ttvt() will work next time */
+ }
+
+#ifndef SVORPOSIX /* BSD section */
+#ifdef UTEK
+ hardflow(flow);
+ if (flow == FLO_NONE || flow == FLO_HARD) /* Something besides Xon/Xoff */
+#else
+ if (flow == FLO_NONE) /* No Xon/Xoff flow control */
+#endif /* UTEK */
+ {
+ debug(F100,"ttpkt turning TANDEM off, RAW on","",0);
+ ttraw.sg_flags &= ~TANDEM; /* Don't ask for it. */
+ ttraw.sg_flags |= RAW;
+ } else if (flow == FLO_XONX) { /* Xon/Xoff requested. */
+ debug(F100,"ttpkt turning on TANDEM","",0);
+ ttraw.sg_flags |= TANDEM; /* So ask for it. */
+
+#ifdef LPASS8 /* Can pass 8-bit data through? */
+/* If the LPASS8 local mode is available, then flow control can always */
+/* be used, even if parity is none and we are transferring 8-bit data. */
+/* But we only need to do all this if Xon/Xoff is requested. */
+/* BUT... this tends not to work through IP or LAT connections, terminal */
+/* servers, telnet, rlogin, etc, so it is not generally used. */
+ x = LPASS8; /* If LPASS8 defined, then */
+ debug(F100,"ttpkt executing LPASS8 code","",0);
+ if (lmodef) { /* TIOCLBIS must be too. */
+ x = ioctl(ttyfd,TIOCLBIS,&x); /* Try to set LPASS8. */
+ if (x < 0) {
+ debug(F100,"ttpkt TIOCLBIS error","",0);
+ } else {
+ lmodef++;
+ debug(F100,"ttpkt TIOCLBIS ok","",0);
+ }
+ }
+/*
+ But if we use LPASS8 mode, we must explicitly turn off
+ terminal interrupts of all kinds.
+*/
+#ifdef TIOCGETC /* Not rawmode, */
+ if (tcharf && (xlocal == 0)) { /* must turn off */
+ tchnoi.t_intrc = -1; /* interrupt character */
+ tchnoi.t_quitc = -1; /* and quit character. */
+ tchnoi.t_startc = 17; /* Make sure xon */
+ tchnoi.t_stopc = 19; /* and xoff not ignored. */
+ if (ioctl(ttyfd,TIOCSETC,&tchnoi) < 0) {
+ debug(F100,"ttpkt TIOCSETC failed","",0);
+ } else {
+ tcharf = 1;
+ debug(F100,"ttpkt TIOCSETC ok","",0);
+ }
+#ifdef COMMENT
+/* only for paranoid debugging */
+ if (tcharf) {
+ struct tchars foo;
+ char tchbuf[100];
+ ioctl(0,TIOCGETC,&foo);
+ sprintf(tchbuf,
+ "intr=%d,quit=%d, start=%d, stop=%d, eof=%d, brk=%d",
+ foo.t_intrc, foo.t_quitc, foo.t_startc,
+ foo.t_stopc, foo.t_eofc, foo.t_brkc);
+ debug(F110,"ttpkt chars",tchbuf,0);
+ }
+#endif /* COMMENT */
+ }
+ ttraw.sg_flags |= CBREAK; /* Needed for unknown reason */
+#endif /* TIOCGETC */
+
+/* Prevent suspend during packet mode */
+#ifdef TIOCGLTC /* Not rawmode, */
+ if (ltcharf && (xlocal == 0)) { /* must turn off */
+ ltchnoi.t_suspc = -1; /* suspend character */
+ ltchnoi.t_dsuspc = -1; /* and delayed suspend character */
+ if (ioctl(ttyfd,TIOCSLTC,&tchnoi) < 0) {
+ debug(F100,"ttpkt TIOCSLTC failed","",0);
+ } else {
+ ltcharf = 1;
+ debug(F100,"ttpkt TIOCSLTC ok","",0);
+ }
+ }
+#endif /* TIOCGLTC */
+
+#else /* LPASS8 not defined */
+
+/* Previously, BSD-based implementations always */
+/* used rawmode for packets. Now, we use rawmode only if parity is NONE. */
+/* This allows the flow control requested above to actually work, but only */
+/* if the user asks for parity (which also means they get 8th-bit quoting). */
+
+ if (parity) { /* If parity, */
+ ttraw.sg_flags &= ~RAW; /* use cooked mode */
+#ifdef COMMENT
+/* WHY??? */
+ if (xlocal)
+#endif /* COMMENT */
+ ttraw.sg_flags |= CBREAK;
+ debug(F101,"ttpkt cooked, cbreak, parity","",parity);
+#ifdef TIOCGETC /* Not rawmode, */
+ if (tcharf && (xlocal == 0)) { /* must turn off */
+ tchnoi.t_intrc = -1; /* interrupt character */
+ tchnoi.t_quitc = -1; /* and quit character. */
+ tchnoi.t_startc = 17; /* Make sure xon */
+ tchnoi.t_stopc = 19; /* and xoff not ignored. */
+ if (ioctl(ttyfd,TIOCSETC,&tchnoi) < 0) {
+ debug(F100,"ttpkt TIOCSETC failed","",0);
+ } else {
+ tcharf = 1;
+ debug(F100,"ttpkt TIOCSETC ok","",0);
+ }
+ }
+#endif /* TIOCGETC */
+#ifdef TIOCGLTC /* Not rawmode, */
+/* Prevent suspend during packet mode */
+ if (ltcharf && (xlocal == 0)) { /* must turn off */
+ ltchnoi.t_suspc = -1; /* suspend character */
+ ltchnoi.t_dsuspc = -1; /* and delayed suspend character */
+ if (ioctl(ttyfd,TIOCSLTC,&tchnoi) < 0) {
+ debug(F100,"ttpkt TIOCSLTC failed","",0);
+ } else {
+ ltcharf = 1;
+ debug(F100,"ttpkt TIOCSLTC ok","",0);
+ }
+ }
+#endif /* TIOCGLTC */
+ } else { /* If no parity, */
+ ttraw.sg_flags |= RAW; /* must use 8-bit raw mode. */
+ debug(F101,"ttpkt setting rawmode, parity","",parity);
+ }
+ } else { /* No Xon/Xoff */
+ debug(F101,"ttpkt setting rawmode, flow","",flow);
+ ttraw.sg_flags |= RAW; /* Can use 8-bit raw mode. */
+#endif /* LPASS8 */
+ }
+
+ /* Don't echo, don't map CR to CRLF on output, don't fool with case */
+#ifdef LCASE
+ ttraw.sg_flags &= ~(ECHO|CRMOD|LCASE);
+#else
+ ttraw.sg_flags &= ~(ECHO|CRMOD);
+#endif /* LCASE */
+
+#ifdef TOWER1
+ ttraw.sg_flags &= ~ANYP; /* Must tell Tower no parity */
+#endif /* TOWER1 */
+
+ if (stty(ttyfd,&ttraw) < 0) return(-1); /* Set the new modes. */
+ debug(F100,"ttpkt stty ok","",0);
+
+#ifdef sony_news
+ x = xlocal ? km_ext : km_con; /* Put line in ASCII mode. */
+ if (x != -1) { /* Make sure we know original modes. */
+ x &= ~KM_TTYPE;
+ x |= KM_ASCII;
+ if (ioctl(ttyfd,TIOCKSET, &x) < 0) {
+ perror("ttpkt can't set ASCII mode");
+ debug(F101,"ttpkt error setting ASCII mode","",x);
+ return(-1);
+ }
+ }
+ debug(F100,"ttpkt set ASCII mode ok","",0);
+#endif /* sony_news */
+
+ if (xlocal == 0) /* Turn this off so we can read */
+ signal(SIGINT,SIG_IGN); /* Ctrl-C chars typed at console */
+
+ tvtflg = 0; /* So ttvt() will work next time */
+ return(0);
+
+#endif /* Not ATTSV or POSIX */
+
+/* AT&T UNIX and POSIX */
+
+#ifdef SVORPOSIX
+ if (flow == FLO_XONX) ttraw.c_iflag |= (IXON|IXOFF);
+ if (flow == FLO_NONE) ttraw.c_iflag &= ~(IXON|IXOFF);
+ if (flow == FLO_RTSC) {
+ /* RTS/CTS Flow control... */
+#ifdef RTSXOFF
+ /* This is the preferred way, according to SVID R4 */
+ if (ioctl(ttyfd,TCGETX,&rctsx) > -1) {
+ rctsx.x_hflag |= RTSXOFF | CTSXON;
+ ioctl(ttyfd,TCSETX,&rctsx); /* Ignore errors for now. */
+ }
+#else
+#ifdef CRTSCTS
+ /* Found this one in <termios.h> */
+ ttraw.c_iflag |= CRTSCTS;
+#endif /* CRTSCRS */
+#endif /* RTSXOFF */
+ }
+ if (flow == FLO_DTRC) {
+ /* DTR/CD Flow control... */
+#ifdef DTRXOFF
+ /* This is straight out of SVID R3 */
+ if (ioctl(ttyfd,TCGETX,&rctsx) > -1) {
+ rctsx.x_hflag |= DTRXOFF | CDXON;
+ ioctl(ttyfd,TCSETX,&rctsx); /* Ignore errors. */
+ }
+#endif /* DTRXOFF */
+ }
+
+ ttraw.c_lflag &= ~(ICANON|ECHO);
+ ttraw.c_lflag &= ~ISIG; /* Do NOT check for interrupt chars */
+ ttraw.c_lflag |= NOFLSH; /* Don't flush */
+ ttraw.c_iflag |= (BRKINT|IGNPAR);
+#ifdef ATTSV
+ ttraw.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|IUCLC|INPCK|ISTRIP|IXANY);
+#else /* POSIX */
+ ttraw.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|INPCK|ISTRIP);
+#endif /* ATTSV */
+ ttraw.c_oflag &= ~OPOST;
+ ttraw.c_cflag &= ~(CSIZE|PARENB);
+ ttraw.c_cflag |= (CS8|CREAD|HUPCL);
+#ifdef IX370
+ ttraw.c_cc[4] = 48; /* So Series/1 doesn't interrupt on every char */
+ ttraw.c_cc[5] = 1;
+#else
+ ttraw.c_cc[4] = 1; /* [VMIN] return max of this many characters or */
+ ttraw.c_cc[5] = 0; /* [VTIME] when this many secs/10 expire w/no input */
+#endif /* IX370 */
+
+#ifdef VINTR /* Turn off interrupt character */
+ if (xlocal == 0) /* so ^C^C can break us out of */
+ ttraw.c_cc[VINTR] = 0; /* packet mode. */
+#endif /* VINTR */
+
+#ifdef ATTSV
+ if (xlocal && (s > 0)) { /* set speed */
+ ttraw.c_cflag &= ~CBAUD;
+ ttraw.c_cflag |= s;
+ }
+ if (ioctl(ttyfd,TCSETAW,&ttraw) < 0) return(-1); /* set new modes . */
+#else /* POSIX */
+ if (xlocal && (s > 0)) {
+ cfsetispeed(&ttraw,s);
+ cfsetospeed(&ttraw,s);
+ }
+ if (tcsetattr(ttyfd,TCSADRAIN,&ttraw) < 0) return(-1);
+#endif /* ATTSV */
+ tvtflg = 0;
+ return(0);
+#endif /* ATTSV */
+}
+\f
+/* T T V T -- Condition communication line for use as virtual terminal */
+
+int
+ttvt(speed,flow) long speed; int flow; {
+ int s, s2;
+
+ debug(F101,"ttvt ttyfd","",ttyfd);
+ debug(F101,"ttvt tvtflg","",tvtflg);
+ debug(F101,"ttvt speed","",speed);
+ if (ttyfd < 0) return(-1); /* Not open. */
+#ifdef NETCONN
+ if (netconn) {
+ tvtflg = 1; /* Network connections */
+ return(0); /* require no special setup */
+ }
+#endif /* NETCONN */
+
+ if (tvtflg != 0 && speed == ttspeed && flow == ttflow && ttcarr == curcarr)
+ return(0); /* Already been called. */
+
+ if (ttfdflg && !isatty(ttyfd)) return(0);
+
+ if (xlocal) { /* For external lines... */
+ s2 = (int) (speed / 10L);
+ s = ttsspd(s2); /* Check/set the speed */
+ carrctl(&tttvt, flow != DIALING /* Do carrier control */
+ && (ttcarr == CAR_ON || ttcarr == CAR_AUT && ttmdm != 0));
+ } else s = s2 = -1;
+
+#ifndef SVORPOSIX
+ /* Berkeley, V7, etc */
+ if (flow == FLO_XONX) tttvt.sg_flags |= TANDEM; /* XON/XOFF if selected */
+#ifdef UTEK
+ hardflow(flow);
+ if (flow == FLO_NONE || flow == FLO_HARD) tttvt.sg_flags &= ~TANDEM;
+#else
+ if (flow == FLO_NONE) tttvt.sg_flags &= ~TANDEM;
+#endif /* UTEK */
+ tttvt.sg_flags |= RAW; /* Raw mode */
+#ifdef TOWER1
+ tttvt.sg_flags &= ~(ECHO|ANYP); /* No echo or parity */
+#else
+ tttvt.sg_flags &= ~ECHO; /* No echo */
+#endif /* TOWER1 */
+
+ if (stty(ttyfd,&tttvt) < 0) return(-1);
+
+#else /* It is ATTSV or POSIX */
+
+ if (flow == FLO_XONX) tttvt.c_iflag |= (IXON|IXOFF);
+ if (flow == FLO_NONE) tttvt.c_iflag &= ~(IXON|IXOFF);
+
+ tttvt.c_lflag &= ~(ISIG|ICANON|ECHO);
+ tttvt.c_iflag |= (IGNBRK|IGNPAR);
+#ifdef ATTSV
+ tttvt.c_iflag &= ~(INLCR|IGNCR|ICRNL|IUCLC|BRKINT|INPCK|ISTRIP|IXANY);
+#else /* POSIX */
+ tttvt.c_iflag &= ~(INLCR|IGNCR|ICRNL|BRKINT|INPCK|ISTRIP);
+#endif /* ATTSV */
+ tttvt.c_oflag &= ~OPOST;
+ tttvt.c_cflag &= ~(CSIZE|PARENB);
+ tttvt.c_cflag |= (CS8|CREAD|HUPCL);
+ tttvt.c_cc[4] = 1;
+ tttvt.c_cc[5] = 0;
+
+#ifdef ATTSV
+ if (s > 0) { /* Set speed */
+ tttvt.c_cflag &= ~CBAUD;
+ tttvt.c_cflag |= s;
+ }
+ if (ioctl(ttyfd,TCSETAW,&tttvt) < 0) return(-1); /* set new modes . */
+#else /* POSIX */
+ if (xlocal && (s > 0)) {
+ cfsetispeed(&tttvt,s);
+ cfsetospeed(&tttvt,s);
+ }
+ if (tcsetattr(ttyfd,TCSADRAIN,&tttvt) < 0) return(-1);
+#endif /* ATTSV */
+#endif /* SVORPOSIX */
+
+ ttspeed = speed; /* Done, remember how we were */
+ ttflow = flow; /* called, so we can decide how to */
+ tvtflg = 1; /* respond next time. */
+
+ debug(F101,"ttvt done","",tvtflg);
+ return(0);
+}
+\f
+/* T T S S P D -- Checks and sets transmission rate. */
+
+/* Call with speed in characters (not bits!) per second. */
+/* Returns internal speed code if successful, -1 otherwise. */
+
+int
+ttsspd(cps) int cps; {
+ int s, s2;
+
+ debug(F101,"ttsspd","",cps);
+
+#ifdef NETCONN
+ if (netconn) return (0);
+#endif /* NETCONN */
+
+ if (cps < 0) return(-1);
+ s = s2 = -1;
+
+ /* First check that the given speed is valid. */
+
+ switch (cps) {
+#ifndef MINIX
+ case 0: s = B0; break;
+ case 5: s = B50; break;
+ case 7: s = B75; break;
+#endif
+ case 11: s = B110; break;
+#ifndef MINIX
+ case 15: s = B150; break;
+ case 20: s = B200; break;
+#endif
+ case 30: s = B300; break;
+#ifndef MINIX
+ case 60: s = B600; break;
+#endif
+ case 120: s = B1200; break;
+#ifndef MINIX
+ case 180: s = B1800; break;
+#endif
+ case 240: s = B2400; break;
+ case 480: s = B4800; break;
+#ifndef MINIX
+ case 888: s = B75; s2 = B1200; break; /* 888 = 75/1200 split speed */
+#endif
+ case 960: s = B9600; break;
+#ifdef B19200
+ case 1920: s = B19200; break;
+#else
+#ifdef EXTA
+ case 1920: s = EXTA; break;
+#endif
+#endif
+#ifdef B38400
+ case 3840: s = B38400; break;
+#else
+#ifdef EXTB
+ case 3840: s = EXTB; break;
+#endif
+#endif
+ default:
+ return(-1);
+ }
+ /* Actually set the speed */
+
+ if (ttyfd > -1 && s > -1 && xlocal != 0) {
+ if (s2 == -1) s2 = s;
+
+#ifdef ATTSV
+ if (cps == 888) return(-1); /* No split speeds, sorry. */
+ if (ioctl(ttyfd,TCGETA,&ttcur) < 0) return(-1);
+ ttcur.c_cflag &= ~CBAUD;
+ ttcur.c_cflag |= s;
+ tttvt.c_cflag &= ~CBAUD;
+ tttvt.c_cflag |= s;
+ ttraw.c_cflag &= ~CBAUD;
+ ttraw.c_cflag |= s;
+ ttold.c_cflag &= ~CBAUD;
+ ttold.c_cflag |= s;
+ if (ioctl(ttyfd,TCSETAW,&ttcur) < 0) return(-1);
+#else
+#ifdef POSIX
+ if (tcgetattr(ttyfd,&ttcur) < 0) return(-1);
+ cfsetospeed(&ttcur,s);
+ cfsetispeed(&ttcur,s2);
+ cfsetospeed(&ttraw,s);
+ cfsetispeed(&ttraw,s2);
+ cfsetospeed(&tttvt,s);
+ cfsetispeed(&tttvt,s2);
+ cfsetospeed(&ttold,s);
+ cfsetispeed(&ttold,s2);
+ if (tcsetattr(ttyfd,TCSADRAIN,&ttcur) < 0) return(-1);
+#else
+ if (gtty(ttyfd,&ttcur) < 0) return(-1);
+ ttcur.sg_ospeed = s; ttcur.sg_ispeed = s2;
+ tttvt.sg_ospeed = s; tttvt.sg_ispeed = s2;
+ ttraw.sg_ospeed = s; ttraw.sg_ispeed = s2;
+ ttold.sg_ospeed = s; ttold.sg_ispeed = s2;
+ if (stty(ttyfd,&ttcur) < 0) return(-1);
+#endif /* POSIX */
+#endif /* ATTSV */
+ }
+ return(s);
+}
+
+/* T T G S P D - Get speed of currently selected tty line */
+
+/*
+ Unreliable. After SET LINE, it returns an actual speed, but not the real
+ speed. Apparently it always returns the line's nominal speed, from
+ /etc/ttytab. Even if you SET SPEED to something else, this function might
+ not notice.
+*/
+long
+ttgspd() { /* Get current tty speed */
+ int s; long ss;
+ char temp[12];
+
+#ifdef NETCONN
+ if (netconn) return(-1); /* -1 if network connection */
+#endif /* NETCONN */
+
+ if (ttyfd < 0) {
+#ifdef ATTSV
+ s = ccold.c_cflag & CBAUD;
+#else
+#ifdef POSIX
+ s = cfgetospeed(&ccold);
+#else
+ s = ccold.sg_ospeed; /* (obtained by congm()) */
+#endif /* POSIX */
+#endif /* ATTSV */
+ } else {
+#ifdef ATTSV
+ if (ioctl(ttyfd,TCGETA,&ttcur) < 0) return(-1);
+ s = ttcur.c_cflag & CBAUD;
+#else
+#ifdef POSIX
+ if (tcgetattr(ttyfd,&ttcur) < 0) return(-1);
+ s = cfgetospeed(&ttcur);
+#else
+ if (gtty(ttyfd,&ttcur) < 0) return(-1);
+ s = ttcur.sg_ospeed;
+#endif /* POSIX */
+#endif /* ATTSV */
+ }
+ debug(F101,"ttgspd ttyfd","",ttyfd);
+ debug(F101,"ttgspd code","",s);
+ switch (s) {
+#ifdef B0
+ case B0: ss = 0L; break;
+#endif
+
+#ifndef MINIX
+/*
+ MINIX defines the Bxx symbols to be bps/100, so B50==B75, B110==B134==B150,
+ etc, making for many "duplicate case in switch" errors, which are fatal.
+*/
+#ifdef B50
+ case B50: ss = 50L; break;
+#endif
+#ifdef B75
+ case B75: ss = 75L; break;
+#endif
+#endif /* MINIX */
+
+#ifdef B110
+ case B110: ss = 110L; break;
+#endif
+
+#ifndef MINIX
+#ifdef B134
+ case B134: ss = 134L; break;
+#endif
+#ifdef B150
+ case B150: ss = 150L; break;
+#endif
+#endif /* MINIX */
+
+#ifdef B200
+ case B200: ss = 200L; break;
+#endif
+
+#ifdef B300
+ case B300: ss = 300L; break;
+#endif
+#ifdef B600
+ case B600: ss = 600L; break;
+#endif
+#ifdef B1200
+ case B1200: ss = 1200L; break;
+#endif
+#ifdef B1800
+ case B1800: ss = 1800L; break;
+#endif
+#ifdef B2400
+ case B2400: ss = 2400L; break;
+#endif
+#ifdef B4800
+ case B4800: ss = 4800L; break;
+#endif
+#ifdef B9600
+ case B9600: ss = 9600L; break;
+#endif
+#ifdef B19200
+ case B19200: ss = 19200L; break;
+#else
+#ifdef EXTA
+ case EXTA: ss = 19200L; break;
+#endif
+#endif
+
+#ifndef MINIX
+#ifdef B38400
+ case B38400: ss = 38400L; break;
+#else
+#ifdef EXTB
+ case EXTB: ss = 38400L; break;
+#endif
+#endif
+#endif /* MINIX */
+
+ default:
+ ss = -1; break;
+ }
+ sprintf(temp,"%l",ss);
+ debug(F110,"speed",temp,0);
+ return(ss);
+}
+\f
+/* ckumyr.c by Kristoffer Eriksson, ske@pkmab.se, 15 Mar 1990. */
+
+#ifdef COHERENT
+#ifdef FIONREAD
+#undef FIONREAD
+#endif
+#define FIONREAD TIOCQUERY
+#define PEEKTYPE int
+#else
+#define PEEKTYPE long
+#endif /* COHERENT */
+
+#ifdef MYREAD
+
+/* Private buffer for myread() and its companions. Not for use by anything
+ * else. ttflui() is allowed to reset them to initial values. ttchk() is
+ * allowed to read my_count.
+ *
+ * my_item is an index into mybuf[]. Increment it *before* reading mybuf[].
+ *
+ * A global parity mask variable could be useful too. We could use it to
+ * let myread() strip the parity on its own, instead of stripping sign
+ * bits as it does now.
+ */
+
+#define MYBUFLEN 256
+static CHAR mybuf[MYBUFLEN]; /* Buffer, including push back */
+static int my_count = 0; /* Number of chars still in mybuf */
+static int my_item = -1; /* Last index read from mybuf[] */
+
+/* myread() -- Efficient read of one character from communications line.
+ *
+ * Uses a private buffer to minimize the number of expensive read() system
+ * calls. Essentially performs the equivalent of read() of 1 character, which
+ * is then returned. By reading all available input from the system buffers
+ * to the private buffer in one chunk, and then working from this buffer, the
+ * number of system calls is reduced in any case where more than one character
+ * arrives during the processing of the previous chunk, for instance high
+ * baud rates or network type connections where input arrives in packets.
+ * If the time needed for a read() system call approaches the time for more
+ * than one character to arrive, then this mechanism automatically compensates
+ * for that by performing bigger read()s less frequently. If the system load
+ * is high, the same mechanism compensates for that too.
+ *
+ * myread() is a macro that returns the next character from the buffer. If the
+ * buffer is empty, mygetbuf() is called. See mygetbuf() for possible error
+ * returns.
+ *
+ * This should be efficient enough for any one-character-at-a-time loops.
+ * For even better efficiency you might use memcpy()/bcopy() or such between
+ * buffers (since they are often better optimized for copying), but it may not
+ * be worth it if you have to take an extra pass over the buffer to strip
+ * parity and check for CTRL-C anyway.
+ *
+ * Note that if you have been using myread() from another program module, you
+ * may have some trouble accessing this macro version and the private variables
+ * it uses. In that case, just add a function in this module, that invokes the
+ * macro.
+ */
+#define myread() (--my_count < 0 ? mygetbuf() : 255 & (int)mybuf[++my_item])
+
+/* Specification: Push back up to one character onto myread()'s queue.
+ *
+ * This implementation: Push back characters into mybuf. At least one character
+ * must have been read through myread() before myunrd() may be used. After
+ * EOF or read error, again, myunrd() can not be used. Sometimes more than
+ * one character can be pushed back, but only one character is guaranteed.
+ * Since a previous myread() must have read its character out of mybuf[],
+ * that guarantees that there is space for at least one character. If push
+ * back was really needed after EOF, a small addition could provide that.
+ *
+ * myunrd() is currently not called from anywhere inside kermit...
+ */
+#ifdef NOTUSED
+myunrd(ch) CHAR ch; {
+ if (my_item >= 0) {
+ mybuf[my_item--] = ch;
+ ++my_count;
+ }
+}
+#endif
+
+/* mygetbuf() -- Fill buffer for myread() and return first character.
+ *
+ * This function is what myread() uses when it can't get the next character
+ * directly from its buffer. First, it calls a system dependent myfillbuf()
+ * to read at least one new character into the buffer, and then it returns
+ * the first character just as myread() would have done. This function also
+ * is responsible for all error conditions that myread() can indicate.
+ *
+ * Returns: When OK => a positive character, 0 or greater.
+ * When EOF => -2.
+ * When error => -3, error code in errno.
+ *
+ * Older myread()s additionally returned -1 to indicate that there was nothing
+ * to read, upon which the caller would call myread() again until it got
+ * something. The new myread()/mygetbuf() always gets something. If it
+ * doesn't, then make it do so! Any program that actually depends on the old
+ * behaviour will break.
+ *
+ * The older version also used to return -2 both for EOF and other errors,
+ * and used to set errno to 9999 on EOF. The errno stuff is gone, EOF and
+ * other errors now return different results, although Kermit currently never
+ * checks to see which it was. It just disconnects in both cases.
+ *
+ * Kermit lets the user use the quit key to perform some special commands
+ * during file transfer. This causes read(), and thus also mygetbuf(), to
+ * finish without reading anything and return the EINTR error. This should
+ * be checked by the caller. Mygetbuf() could retry the read() on EINTR,
+ * but if there is nothing to read, this could delay Kermit's reaction to
+ * the command, and make Kermit appear unresponsive.
+ *
+ * The debug() call should be removed for optimum performance.
+ */
+int
+mygetbuf() {
+ my_count = myfillbuf();
+ /* debug(F101, "myfillbuf read", "", my_count); */
+ if (my_count <= 0)
+ return(my_count < 0 ? -3 : -2);
+ --my_count;
+ return(255 & (int)mybuf[my_item = 0]);
+}
+
+/* myfillbuf():
+ * System-dependent read() into mybuf[], as many characters as possible.
+ *
+ * Returns: OK => number of characters read, always more than zero.
+ * EOF => 0
+ * Error => -1, error code in errno.
+ *
+ * If there is input available in the system's buffers, all of it should be
+ * read into mybuf[] and the function return immediately. If no input is
+ * available, it should wait for a character to arrive, and return with that
+ * one in mybuf[] as soon as possible. It may wait somewhat past the first
+ * character, but be aware that any such delay lengthens the packet turnaround
+ * time during kermit file transfers. Should never return with zero characters
+ * unless EOF or irrecoverable read error.
+ *
+ * Correct functioning depends on the correct tty parameters being used.
+ * Better control of current parameters is required than may have been the
+ * case in older Kermit releases. For instance, O_NDELAY (or equivalent) can
+ * no longer be sometimes off and sometimes on like it used to, unless a
+ * special myfillbuf() is written to handle that. Otherwise the ordinary
+ * myfillbuf()s may think they have come to EOF.
+ *
+ * If your system has a facility to directly perform the functioning of
+ * myfillbuf(), then use it. If the system can tell you how many characters
+ * are available in its buffers, then read that amount (but not less than 1).
+ * If the system can return a special indication when you try to read without
+ * anything to read, while allowing you to read all there is when there is
+ * something, you may loop until there is something to read, but probably that
+ * is not good for the system load.
+ */
+
+#ifdef SVORPOSIX
+ /* This is for System III/V with VMIN>0, VTIME=0 and O_NDELAY off,
+ * and CLOCAL set any way you like. This way, read() will do exactly
+ * what is required by myfillbuf(): If there is data in the buffers
+ * of the O.S., all available data is read into mybuf, up to the size
+ * of mybuf. If there is none, the first character to arrive is
+ * awaited and returned.
+ */
+int
+myfillbuf() {
+#ifdef sxaE50
+ /* From S. Dezawa at Fujifilm in Japan. I don't know why this is */
+ /* necessary for the sxa E50, but it is. */
+ return read(ttyfd, mybuf, 255);
+#else
+ /* sizeof(mybuf) should be MYBUFL == 256 */
+ return read(ttyfd, mybuf, sizeof(mybuf));
+#endif /* sxaE50 */
+}
+
+#else /* not AT&T or POSIX */
+
+#ifdef aegis
+ /* This is quoted from the old myread(). The semantics seem to be
+ * alright, but maybe errno would not need to be set even when
+ * there is no error? I don't know aegis.
+ */
+int
+myfillbuf() {
+ int count;
+
+ count = ios_$get((short)ttyfd, ios_$cond_opt, mybuf, 256L, st);
+ errno = EIO;
+ if (st.all == ios_$get_conditional_failed) /* get at least one */
+ inbufc = ios_$get((short)ttyfd, 0, mybuf, 1L, st);
+ if (st.all == ios_$end_of_file)
+ return(0);
+ else if (st.all != status_$ok) {
+ errno = EIO;
+ return(-1);
+ }
+ return(count);
+}
+#else /* !aegis */
+
+#ifdef FIONREAD
+ /* This is for systems with FIONREAD. FIONREAD returns the number
+ * of characters available for reading. If none are available, wait
+ * until something arrives, otherwise return all there is.
+ */
+int
+myfillbuf() {
+ PEEKTYPE avail;
+
+ if ((ioctl(ttyfd, FIONREAD, &avail) < 0) || (avail == 0))
+ avail = 1;
+
+ if (avail > MYBUFLEN)
+ avail = MYBUFLEN;
+
+ return(read(ttyfd, mybuf, (int) avail));
+}
+
+#else /* !FIONREAD */
+/* Add other systems here, between #ifdef and #else, e.g. NETCONN. */
+/* When there is no other possibility, read 1 character at a time. */
+int
+myfillbuf() {
+ return read(ttyfd, mybuf, 1);
+}
+
+#endif /* !FIONREAD */
+#endif /* !aegis */
+#endif /* !ATTSV */
+
+#endif /* MYREAD */
+\f
+/* T T F L U I -- Flush tty input buffer */
+
+int
+ttflui() {
+ int ffd;
+
+#ifndef SVORPOSIX
+ int n;
+#endif /* SVORPOSIX */
+#ifdef NETCONN
+ if (netconn) return(netflui());
+#endif /* NETCONN */
+
+ ffd = xlocal ? ttyfd : 0; /* If not local, use stdin */
+ debug(F101,"ttflui xlocal","",xlocal);
+ debug(F101,"ttflui ttyfd","",ttyfd);
+ debug(F101,"ttflui ffd","",ffd);
+ if (ffd < 0) return(-1);
+
+#ifdef MYREAD
+ my_count = 0; /* Initialize myread() stuff */
+ my_item = -1;
+#endif /* MYREAD */
+
+#ifdef aegis
+ sio_$control((short)ffd, sio_$flush_in, true, st);
+ if (st.all != status_$ok) {
+ fprintf(stderr, "flush failed: "); error_$print(st);
+ } else { /* sometimes the flush doesn't work */
+ for (;;) {
+ char buf[256];
+ /* eat all the characters that shouldn't be available */
+ ios_$get((short)ffd, ios_$cond_opt, buf, 256L, st); /* (void) */
+ if (st.all == ios_$get_conditional_failed) break;
+ fprintf(stderr, "flush failed(2): "); error_$print(st);
+ }
+ }
+#else
+#ifdef ATTSV
+#ifndef VXVE
+ ioctl(ffd,TCFLSH,0);
+#endif /* VXVE */
+#else /* POSIX */
+#ifdef POSIX
+ tcflush(ttyfd,TCIFLUSH);
+#else /* Berkeley and others */
+#ifdef TIOCFLUSH
+#ifdef ANYBSD
+ n = FREAD; /* Specify read queue */
+ debug(F101,"ttflui anybsd flush","",ffd);
+ ioctl(ffd,TIOCFLUSH,&n);
+#else
+ ioctl(ffd,TIOCFLUSH,0);
+#endif /* ANYBSD */
+#endif /* TIOCFLUSH */
+#endif /* POSIX */
+#endif /* aegis */
+#endif
+ return(0);
+}
+
+int
+ttfluo() { /* Flush output buffer */
+#ifdef POSIX
+ return(tcflush(ttyfd,TCOFLUSH));
+#else
+ return(0); /* (dummy for now) */
+#endif /* POSIX */
+
+}
+\f
+/* Interrupt Functions */
+
+/* Set up terminal interrupts on console terminal */
+
+#ifdef SVORPOSIX
+SIGTYP
+esctrp(foo) int foo; { /* trap console escapes (^\) */
+ conesc = 1;
+ signal(SIGQUIT,SIG_IGN); /* ignore until trapped */
+}
+#endif /* SVORPOSIX */
+
+#ifdef V7
+SIGTYP
+esctrp(foo) int foo; { /* trap console escapes (^\) */
+ conesc = 1;
+ signal(SIGQUIT,SIG_IGN); /* ignore until trapped */
+}
+#endif
+
+#ifdef C70
+SIGTYP
+esctrp(foo) int foo; { /* trap console escapes (^\) */
+ conesc = 1;
+ signal(SIGQUIT,SIG_IGN); /* ignore until trapped */
+}
+#endif
+
+/* C O N B G T -- Background Test */
+
+static int jc; /* 0 = no job control */
+
+VOID
+conbgt() {
+ int x = -1, /* process group or SIGINT test */
+ y = 0; /* isatty() test */
+
+/*
+ Check for background operation, even if not running on real tty, so that
+ background flag can be set correctly. If background status is detected,
+ then Kermit will not issue its interactive prompt or most messages.
+ If your prompt goes away, you can blame (and fix?) this function.
+*/
+
+/* Use process-group test if possible. */
+
+#ifdef POSIX /* We can do it in POSIX */
+#define PGROUP_T
+#else
+#ifdef BSD4 /* and in BSD 4.x. */
+#define PGROUP_T
+#else
+#ifdef HPUXJOBCTL /* and in most HP-UX's */
+#define PGROUP_T
+#else
+#ifdef TIOCGPGRP /* and anyplace that has this ioctl. */
+#define PGROUP_T
+#endif /* TIOCGPGRP */
+#endif /* HPUXJOBCTL */
+#endif /* BSD4 */
+#endif /* POSIX */
+
+#ifdef MIPS /* Except if it doesn't work... */
+#undef PGROUP_T
+#endif /* MIPS */
+
+#ifdef PGROUP_T
+/*
+ Semi-reliable process-group test. Check whether this process's group is
+ the same as the controlling terminal's process group. This works if the
+ getpgrp() call doesn't lie (as it does in the SUNOS System V environment).
+*/
+ PID_T mypgrp; /* Kermit's process group */
+ PID_T ctpgrp; /* The terminal's process group */
+#ifndef _POSIX_SOURCE
+/*
+ The getpgrp() prototype is obtained from system header files for POSIX
+ and Sys V R4 compilations. Other systems, who knows. Some complain about
+ a duplicate declaration here, others don't, so it's safer to leave it in
+ if we don't know for certain.
+*/
+#ifndef SVR4
+#ifndef PS2AIX10
+ extern PID_T getpgrp();
+#endif /* PS2AIX10 */
+#endif /* SVR4 */
+#endif /* _POSIX_SOURCE */
+
+/* Get my process group. */
+
+#ifdef SVR3 /* Maybe this should be ATTSV? */
+/* This function is not described in SVID R2 */
+ mypgrp = getpgrp();
+ debug(F101,"ATTSV conbgt process group","",(int) mypgrp);
+#else
+#ifdef POSIX
+ mypgrp = getpgrp();
+ debug(F101,"POSIX conbgt process group","",(int) mypgrp);
+#else /* BSD, V7, etc */
+ mypgrp = getpgrp(0);
+ debug(F101,"BSD conbgt process group","",(int) mypgrp);
+#endif /* POSIX */
+#endif /* SVR3 */
+
+/* Now get controlling tty's process group */
+#ifdef POSIX
+ ctpgrp = tcgetpgrp(1); /* The POSIX way */
+ debug(F101,"POSIX conbgt terminal process group","",(int) ctpgrp);
+#else
+ ioctl(1, TIOCGPGRP, &ctpgrp); /* Or the BSD way */
+ debug(F101,"non-POSIX conbgt terminal process group","",(int) ctpgrp);
+#endif /* POSIX */
+
+ if ((mypgrp > (PID_T) 0) && (ctpgrp > (PID_T) 0))
+ x = (mypgrp == ctpgrp) ? 0 : 1; /* If they differ, then background. */
+ else x = -1; /* If error, remember. */
+ debug(F101,"conbgt process group test","",x);
+#endif /* PGROUP_T */
+
+/* Try to see if job control is available */
+
+#ifdef NOJC /* User override */
+ jc = 0; /* No job control allowed */
+ debug(F111,"NOJC","jc",jc);
+#else
+#ifdef SVR4ORPOSIX /* POSIX actually tells us */
+ debug(F100,"SVR4ORPOSIX jc test...","",0);
+#ifdef _SC_JOB_CONTROL
+/* jc = sysconf(_SC_JOB_CONTROL); /* Whatever system says */
+ jc = 1;
+ debug(F111,"sysconf(_SC_JOB_CONTROL)","jc",jc);
+#else
+#ifdef _POSIX_JOB_CONTROL
+ jc = 1; /* By definition */
+ debug(F111,"_POSIX_JOB_CONTROL is defined","jc",jc);
+#else
+ jc = 0; /* Assume job control allowed */
+ debug(F111,"SVR4ORPOSIX _SC/POSIX_JOB_CONTROL not defined","jc",jc);
+#endif /* _POSIX_JOB_CONTROL */
+#endif /* _SC_JOB_CONTROL */
+#else
+#ifdef BSD4
+ jc = 1; /* Job control allowed */
+ debug(F111,"BSD job control","jc",jc);
+#else
+ jc = 0;
+ debug(F111,"job control catch-all","jc",jc);
+#endif /* BSD4 */
+#endif /* SVR4ORPOSIX */
+#endif /* NOJC */
+ debug(F101,"conbgt jc","",jc);
+
+/*
+ Another background test.
+ Test if SIGINT (terminal interrupt) is set to SIG_IGN (ignore),
+ which is done by the shell (sh) if the program is started with '&'.
+ Unfortunately, this is NOT done by csh or ksh so watch out!
+ Note, it's safe to set SIGINT to SIG_IGN here, because further down
+ we always set it to something else.
+*/
+ if (x < 0) { /* Didn't get good results above... */
+
+ SIGTYP (*osigint)();
+
+ osigint = signal(SIGINT,SIG_IGN); /* What is SIGINT set to? */
+ x = (osigint == SIG_IGN) ? 1 : 0; /* SIG_IGN? */
+ debug(F101,"conbgt osigint","",(int) osigint);
+ debug(F101,"conbgt signal test","",x);
+ }
+
+/* Also check to see if we're running with redirected stdio. */
+/* This is not really background operation, but we want to act as though */
+/* it were. */
+
+ y = (isatty(0) && isatty(1)) ? 1 : 0;
+ debug(F101,"conbgt isatty test","",y);
+
+#ifdef BSD29
+/* The process group and/or signal test doesn't work under these... */
+ backgrd = !y;
+#else
+#ifdef sxaE50
+ backgrd = !y;
+#else
+#ifdef MINIX
+ backgrd = !y;
+#else
+ backgrd = (x || !y) ? 1 : 0;
+#endif /* BSD29 */
+#endif /* sxaE50 */
+#endif /* MINIX */
+ debug(F101,"conbgt backgrd","",backgrd);
+}
+
+/* C O N I N T -- Console Interrupt setter */
+
+/*
+ First arg is pointer to function to handle SIGTERM & SIGINT (like Ctrl-C).
+ Second arg is pointer to function to handle SIGTSTP (suspend).
+*/
+
+VOID /* Set terminal interrupt traps. */
+#ifdef CK_ANSIC
+#ifdef apollo
+conint(f,s) SIGTYP (*f)(), (*s)();
+#else
+conint(SIGTYP (*f)(int), SIGTYP (*s)(int))
+#endif /* apollo */
+#else
+conint(f,s) SIGTYP (*f)(), (*s)();
+#endif /* CK_ANSIC */
+/* conint */ {
+
+ conbgt(); /* Do background test. */
+
+/* Set the desired handlers for hangup and software termination. */
+
+ signal(SIGHUP,f); /* Hangup */
+ signal(SIGTERM,f); /* Software termination */
+
+/* Now handle keyboard stop, quit, and interrupt signals. */
+/* Check if invoked in background -- if so signals set to be ignored. */
+/* However, if running under a job control shell, don't ignore them. */
+/* We won't be getting any, as we aren't in the terminal's process group. */
+
+ debug(F101,"conint backgrd","",backgrd);
+ debug(F101,"conint jc","",jc);
+
+ if (backgrd && !jc) { /* In background, ignore signals */
+ debug(F101,"conint background ignoring signals, jc","",jc);
+#ifdef SIGTSTP
+ signal(SIGTSTP,SIG_IGN); /* Keyboard stop */
+#endif /* SIGTSTP */
+ signal(SIGQUIT,SIG_IGN); /* Keyboard quit */
+ signal(SIGINT,SIG_IGN); /* Keyboard interrupt */
+ } else { /* Else in foreground or suspended */
+ debug(F101,"conint foreground catching signals, jc","",jc);
+ signal(SIGINT,f); /* Catch terminal interrupt */
+
+#ifdef SIGTSTP /* Keyboard stop (suspend) */
+ debug(F101,"conint SIGSTSTP","",(int) s);
+ if (s == NULL) s = SIG_DFL;
+#ifdef NOJC /* No job control allowed. */
+ signal(SIGTSTP,SIG_IGN);
+#else /* Job control allowed */
+ if (jc) /* if available. */
+ signal(SIGTSTP,s);
+ else
+ signal(SIGTSTP,SIG_IGN);
+#endif /* NOJC */
+#endif /* SIGTSTP */
+
+#ifdef SVORPOSIX
+ signal(SIGQUIT,esctrp); /* Quit signal, Sys III/V. */
+ if (conesc) conesc = 0; /* Clear out pending escapes */
+#else
+#ifdef V7
+ signal(SIGQUIT,esctrp); /* V7 like Sys III/V */
+ if (conesc) conesc = 0;
+#else
+#ifdef aegis
+ signal(SIGQUIT,f); /* Apollo, catch it like others. */
+#else
+ signal(SIGQUIT,SIG_IGN); /* Others, ignore like 4D & earlier. */
+#endif /* aegis */
+#endif /* V7 */
+#endif /* SVORPOSIX */
+ }
+}
+
+
+/* C O N N O I -- Reset console terminal interrupts */
+
+SIGTYP /* Dummy function to ignore signals */
+#ifdef CK_ANSIC
+sig_ign(int foo)
+#else
+sig_ign(foo) int foo;
+#endif /* CK_ANSIC */
+/* sig_IGN */ { /* Just like the real one, but has */
+} /* different address. */
+
+VOID
+connoi() { /* Console-no-interrupts */
+
+ debug(F100,"connoi","",0);
+#ifdef SIGTSTP
+ signal(SIGTSTP,SIG_DFL);
+#endif /* SIGTSTP */
+ /* Note the locally defined replacement for SIG_IGN that is used here */
+ /* for the SIGINT setting. This is done so that the Sys V background */
+ /* test -- (signal(SIGINT,SIG_IGN) == SIG_IGN) -- can work. If we use */
+ /* the real SIG_IGN here, then conint will always decide that this */
+ /* program is running in the background! */
+
+ signal(SIGINT,sig_ign); /* <--- note! */
+
+ signal(SIGHUP,SIG_DFL);
+ signal(SIGQUIT,SIG_IGN);
+ signal(SIGTERM,SIG_IGN);
+}
+\f
+/* I N I T R A W Q -- Set up to read /dev/kmem for character count. */
+
+#ifdef V7
+/*
+ Used in Version 7 to simulate Berkeley's FIONREAD ioctl call. This
+ eliminates blocking on a read, because we can read /dev/kmem to get the
+ number of characters available for raw input. If your system can't
+ or you won't let the world read /dev/kmem then you must figure out a
+ different way to do the counting of characters available, or else replace
+ this by a dummy function that always returns 0.
+*/
+/*
+ * Call this routine as: initrawq(tty)
+ * where tty is the file descriptor of a terminal. It will return
+ * (as a char *) the kernel-mode memory address of the rawq character
+ * count, which may then be read. It has the side-effect of flushing
+ * input on the terminal.
+ */
+/*
+ * John Mackin, Physiology Dept., University of Sydney (Australia)
+ * ...!decvax!mulga!physiol.su.oz!john
+ *
+ * Permission is hereby granted to do anything with this code, as
+ * long as this comment is retained unmodified and no commercial
+ * advantage is gained.
+ */
+#ifndef MINIX
+#ifndef COHERENT
+#include <a.out.h>
+#include <sys/proc.h>
+#endif /* COHERENT */
+#endif /* MINIX */
+
+#ifdef COHERENT
+#include <l.out.h>
+#include <sys/proc.h>
+#endif /* COHERENT */
+
+char *
+initrawq(tty) int tty; {
+#ifdef MINIX
+ return(0);
+#else
+#ifdef UTS24
+ return(0);
+#else
+#ifdef BSD29
+ return(0);
+#else
+ long lseek();
+ static struct nlist nl[] = {
+ {PROCNAME},
+ {NPROCNAME},
+ {""}
+ };
+ static struct proc *pp;
+ char *qaddr, *p, c;
+ int m;
+ PID_T pid, me;
+ NPTYPE xproc; /* Its type is defined in makefile. */
+ int catch();
+
+ me = getpid();
+ if ((m = open("/dev/kmem", 0)) < 0) err("kmem");
+ nlist(BOOTNAME, nl);
+ if (nl[0].n_type == 0) err("proc array");
+
+ if (nl[1].n_type == 0) err("nproc");
+
+ lseek(m, (long)(nl[1].n_value), 0);
+ read (m, &xproc, sizeof(xproc));
+ saval = signal(SIGALRM, catch);
+ if ((pid = fork()) == 0) {
+ while(1)
+ read(tty, &c, 1);
+ }
+ alarm(2);
+
+ if(setjmp(jjbuf) == 0) {
+ while(1)
+ read(tty, &c, 1);
+ }
+ signal(SIGALRM, SIG_DFL);
+
+#ifdef DIRECT
+ pp = (struct proc *) nl[0].n_value;
+#else
+ if (lseek(m, (long)(nl[0].n_value), 0) < 0L) err("seek");
+ if (read(m, &pp, sizeof(pp)) != sizeof(pp)) err("no read of proc ptr");
+#endif
+ lseek(m, (long)(nl[1].n_value), 0);
+ read(m, &xproc, sizeof(xproc));
+
+ if (lseek(m, (long)pp, 0) < 0L) err("Can't seek to proc");
+ if ((p = malloc(xproc * sizeof(struct proc))) == NULL) err("malloc");
+ if (read(m,p,xproc * sizeof(struct proc)) != xproc*sizeof(struct proc))
+ err("read proc table");
+ for (pp = (struct proc *)p; xproc > 0; --xproc, ++pp) {
+ if (pp -> p_pid == (short) pid) goto iout;
+ }
+ err("no such proc");
+
+iout:
+ close(m);
+ qaddr = (char *)(pp -> p_wchan);
+ free (p);
+ kill(pid, SIGKILL);
+ wait((WAIT_T)0); /* Destroy the ZOMBIEs! */
+ return (qaddr);
+#endif
+#endif
+#endif
+}
+
+/* More V7-support functions... */
+
+static VOID
+err(s) char *s; {
+ char buf[200];
+
+ sprintf(buf, "fatal error in initrawq: %s", s);
+ perror(buf);
+ doexit(1,-1);
+}
+
+static VOID
+catch(foo) int foo; {
+ longjmp(jjbuf, -1);
+}
+
+
+/* G E N B R K -- Simulate a modem break. */
+
+#ifdef MINIX
+#define BSPEED B110
+#else
+#define BSPEED B150
+#endif /* MINIX */
+
+VOID
+genbrk(fn,msec) int fn, msec; {
+ struct sgttyb ttbuf;
+ int ret, sospeed, x, y;
+
+ ret = ioctl(fn, TIOCGETP, &ttbuf);
+ sospeed = ttbuf.sg_ospeed;
+ ttbuf.sg_ospeed = BSPEED;
+ ret = ioctl(fn, TIOCSETP, &ttbuf);
+ y = (int)strlen(brnuls);
+ x = ( BSPEED * 100 ) / msec;
+ if (x > y) x = y;
+ ret = write(fn, brnuls, (( BSPEED * 100 ) / msec ));
+ ttbuf.sg_ospeed = sospeed;
+ ret = ioctl(fn, TIOCSETP, &ttbuf);
+ ret = write(fn, "@", 1);
+ return;
+}
+#endif /* V7 */
+\f
+/* T T C H K -- Tell how many characters are waiting in tty input buffer */
+
+/* Some callers of this want to know whether there is something to read
+ * either in the system buffers or in our private buffers (and mostly don't
+ * care how many characters, just whether it's more than zero or not), while
+ * some others would be better off with the number of characters in our
+ * private buffers only.
+ *
+ * Some systems can say how many characters there are in the system buffers.
+ * Others can not. For those that can't, the number in the private buffers
+ * will have to do (or should we put the tty into O_NDELAY-mode and try to
+ * read one character?). If the system can tell whether there is zero or
+ * more than zero characters, we can return 1 in the latter case even if the
+ * private buffer is empty. (That is good for sliding windows.)
+ */
+int
+ttchk() {
+ int x;
+ PEEKTYPE n = 0;
+
+#ifdef NETCONN
+ if (netconn) return(nettchk());
+#endif /* NETCONN */
+
+#ifdef FIONREAD
+ x = ioctl(ttyfd, FIONREAD, &n); /* Berkeley and maybe some others */
+ debug(F101,"ttchk","",n);
+ if (x < 0) n = 0;
+#else
+#ifdef V7
+#ifdef MINIX
+ return(0);
+#else
+ lseek(kmem[TTY], (long) qaddr[TTY], 0); /* 7th Edition Unix */
+ x = read(kmem[TTY], &n, sizeof(int));
+ if (x != sizeof(int)) n = 0;
+#endif /* MINIX */
+#else
+#ifdef PROVX1
+ x = ioctl(ttyfd, TIOCQCNT, &ttbuf); /* Pro/3xx Venix V.1 */
+ n = ttbuf.sg_ispeed & 0377;
+ if (x < 0) n = 0;
+#else
+#ifdef RDCHK
+ /* Last resort for systems without FIONREAD or equivalent, but with
+ * something like rdchk(), like XENIX.
+ */
+ if (my_count == 0 && rdchk(ttyfd) > 0) n = 1;
+#endif /* RDCHK */
+#endif /* PROVX1 */
+#endif /* V7 */
+#endif /* FIONREAD */
+
+#ifdef MYREAD
+ /* For myread() users, add the contents of myread()'s private buffer.
+ * Sometimes, this is all there is to construct a result of ttchk() on.
+ */
+ if (my_count > 0) /* Sys III, Sys V, Apollo Aegis, etc */
+ n += my_count;
+#endif /* MYREAD */
+
+ debug(F101,"ttchk","",n);
+ return(n);
+}
+
+/* T T X I N -- Get n characters from tty input buffer */
+
+/* Returns number of characters actually gotten, or -1 on failure */
+
+/* Intended for use only when it is known that n characters are actually */
+/* Available in the input buffer. */
+
+int
+ttxin(n,buf) int n; CHAR *buf; {
+ register int x, c;
+
+ ttpmsk = (ttprty) ? 0177 : 0377; /* Parity stripping mask. */
+ debug(F101,"ttxin n","",n);
+#ifdef SUNX25
+ if (netconn && (ttnet == NET_SX25)) /* X.25 connection */
+ return(x25xin(n,buf));
+#endif /* SUNX25 */
+
+#ifdef MYREAD
+ for( x = 0; (x > -1) && (x < n) && (c = myread()) >= 0; )
+ buf[x++] = c & ttpmsk;
+#else
+ x = read(ttyfd,buf,n);
+ for (c = 0; c < n; c++) buf[c] &= ttpmsk;
+ debug(F101," x","",x);
+#endif /* MYREAD */
+ if (x > 0) buf[x] = '\0';
+ if (x < 0) x = -1;
+ return(x);
+}
+\f
+/* T T O L -- Write string s, length n, to communication device. */
+/*
+ Returns:
+ >= 0 on success, number of characters actually written.
+ -1 on failure.
+*/
+#define TTOLMAXT 5
+int
+ttol(s,n) int n; CHAR *s; {
+ int x, len, tries;
+
+ if (ttyfd < 0) return(-1); /* Not open? */
+ debug(F101,"ttol n","",n);
+ tries = TTOLMAXT; /* Allow up to this many tries */
+ len = n; /* Remember original length */
+
+ while (n > 0 && tries-- > 0) { /* Be persistent */
+ debug(F101,"ttol try","",TTOLMAXT - tries);
+ x = write(ttyfd,s,n); /* Write string to device */
+ if (x == n) { /* Worked? */
+ debug(F101,"ttol ok","",x); /* OK */
+ return(len); /* Done */
+ } else if (x < 0) { /* No, got error? */
+ debug(F101,"ttol failed","",errno);
+ return(-1);
+ } else { /* No error, so partial success */
+ debug(F101,"ttol partial","",x);
+ s += x; /* Point to part not written yet */
+ n -= x; /* Adjust length */
+ if (x > 0) msleep(100); /* Wait 100 msec */
+ } /* Go back and try again */
+ }
+ return(n < 1 ? len : -1); /* Too many tries */
+}
+
+
+
+/* T T O C -- Output a character to the communication line */
+
+/*
+ This function should only be used for interactive, character-mode operations,
+ like terminal connection, script execution, dialer i/o, where the overhead
+ of the signals and alarms does not create a bottleneck.
+*/
+int
+#ifdef CK_ANSIC
+ttoc(char c)
+#else
+ttoc(c) char c;
+#endif /* CK_ANSIC */
+/* ttoc */ {
+ c &= 0xff;
+ /* debug(F101,"ttoc","",(CHAR) c); */
+ if (ttyfd < 0) return(-1); /* Check for not open. */
+ saval = signal(SIGALRM,timerh); /* Enable timer interrupt */
+ alarm(15); /* for 15 seconds. */
+ if (setjmp(sjbuf)) { /* Timer went off? */
+ ttimoff(); /* Yes, turn off the alarm. */
+ if (!backgrd) conoc('\07'); /* Ring the bell. */
+ debug(F100,"ttoc timed out","",0); /* Log a message. */
+ if (ttflow == FLO_XONX) {
+ debug(F101,"ttoc flow","",ttflow); /* Maybe we're xoff'd */
+#ifdef POSIX
+ debug(F100,"ttoc tcflow","",0); /* POSIX way to unstick. */
+ tcflow(ttyfd,TCOON);
+#else
+#ifdef BSD4 /* Berkeley way to do it. */
+#ifdef TIOCSTART
+ debug(F100,"ttoc TIOCSTART","",0);
+ ioctl(ttyfd, TIOCSTART, 0);
+#endif /* TIOCSTART */
+#endif /* BSD4 */
+#endif /* POSIX */
+ }
+ return(-1); /* Return failure code. */
+ } else {
+ if (write(ttyfd,&c,1) < 1) { /* Try to write the character. */
+ ttimoff(); /* If error, turn off timer, */
+ if (!backgrd) conoc('\07'); /* ring the bell, */
+ debug(F101,"ttoc error","",errno); /* log the error, */
+ return(-1); /* and return the error code. */
+ }
+ }
+ ttimoff(); /* Success, turn off timer. */
+ return(0);
+}
+\f
+/* T T I N L -- Read a record (up to break character) from comm line. */
+/*
+ Reads up to "max" characters from the communication line, terminating on:
+
+ (a) the packet length field if the "turn" argument is zero, or
+ (b) on the packet-end character (eol) if the "turn" argument is nonzero
+
+ and returns the number of characters read upon success, or if "max" was
+ exceeded or the timeout interval expired before (a) or (b), returns -1.
+
+ The characters that were input are copied into "dest" with their parity bits
+ stripped if parity was selected. Returns the number of characters read.
+ Characters after the eol are available upon the next call to this function.
+
+ The idea is to minimize the number of system calls per packet, and also to
+ minimize timeouts. This function is the inner loop of the program and must
+ be as efficient as possible. The current strategy is to use myread().
+
+ WARNING: this function calls parchk(), which is defined in another module.
+ Normally, ckutio.c does not depend on code from any other module, but there
+ is an exception in this case because all the other ck?tio.c modules also
+ need to call parchk(), so it's better to have it defined in a common place.
+*/
+#ifdef CTRLC
+#undef CTRLC
+#endif /* CTRLC */
+#define CTRLC '\03'
+/*
+ We have four different declarations here because:
+ (a) to allow Kermit to be built without the automatic parity sensing feature
+ (b) one of each type for ANSI C, one for non-ANSI.
+*/
+int
+#ifdef PARSENSE
+#ifdef CK_ANSIC
+ttinl(CHAR *dest, int max,int timo, CHAR eol, CHAR start, int turn)
+#else
+ttinl(dest,max,timo,eol,start,turn) int max,timo,turn; CHAR *dest, eol, start;
+#endif /* CK_ANSIC */
+#else /* not PARSENSE */
+#ifdef CK_ANSIC
+ttinl(CHAR *dest, int max,int timo, CHAR eol)
+#else
+ttinl(dest,max,timo,eol) int max,timo; CHAR *dest, eol;
+#endif /* __SDTC__ */
+#endif /* PARSENSE */
+/* ttinl */ {
+
+#ifndef MYREAD
+ CHAR ch;
+#endif /* MYREAD */
+#ifdef PARSENSE
+ int pktlen = -1;
+ int lplen = 0;
+ int havelen = 0;
+#endif /* PARSENSE */
+
+ if (ttyfd < 0) return(-1); /* Not open. */
+
+ debug(F101,"ttinl max","",max);
+ debug(F101,"ttinl timo","",timo);
+
+ *dest = '\0'; /* Clear destination buffer */
+ if (timo < 0) timo = 0; /* Safety */
+ if (timo) { /* Don't time out if timo == 0 */
+ saval = signal(SIGALRM,timerh); /* Enable timer interrupt */
+ alarm(timo); /* Set it. */
+ }
+ if (setjmp(sjbuf)) { /* Timer went off? */
+ debug(F100,"ttinl timout","",0); /* Get here on timeout. */
+ debug(F110," with",(char *) dest,0);
+ ttimoff(); /* Turn off timer */
+ return(-1); /* and return error code. */
+ } else {
+ register int i, m, n; /* local variables */
+ int ccn = 0;
+#ifdef PARSENSE
+ int flag = 0;
+
+ debug(F000,"ttinl start","",start);
+ flag = 0; /* Start of packet flag */
+#endif /* PARSENSE */
+
+ ttpmsk = m = (ttprty) ? 0177 : 0377; /* Set parity stripping mask. */
+
+#ifdef SUNX25
+ if (netconn && (ttnet == NET_SX25))
+#ifdef PARSENSE
+ return(x25inl(dest,max,timo,eol,start));
+#else
+ return(x25inl(dest,max,timo,eol));
+#endif /* PARSENSE */
+#endif /* SUNX25 */
+
+/* Now read into destination, stripping parity and looking for the */
+/* the packet terminator, and also for two Ctrl-C's typed in a row. */
+
+ i = 0; /* Destination index */
+ debug(F101,"ttinl eol","",eol);
+
+#ifdef MYREAD
+ while (i < max-1) {
+ /* debug(F101,"ttinl i","",i); */
+ if ((n = myread()) < 0) {
+ debug(F101,"ttinl myread failure, n","",n);
+ debug(F101,"ttinl myread errno,","",errno);
+ /* Don't let EINTR break packets. */
+ if (n == -3 && errno == EINTR && i > 0) {
+ debug(F101,"ttinl myread i","",i);
+ continue;
+ }
+ break;
+ }
+#else
+ while ((i < max-1) && (n = read(ttyfd, &ch, 1)) > 0) {
+ n = ch;
+#endif /* MYREAD */
+
+ /* debug(F101,"ttinl char","", (n & ttpmsk)); */
+
+#ifdef PARSENSE
+/*
+ Figure out what the length is supposed to be in case the packet
+ has no terminator (as with Honeywell GCOS-8 Kermit).
+*/
+#ifndef xunchar
+#define xunchar(ch) (((ch) - 32 ) & 0xFF ) /* Character to number */
+#endif /* xunchar */
+ if ((flag == 0) && ((n & 0x7f) == start)) flag = 1;
+ if (flag) dest[i++] = n & ttpmsk;
+/*
+ If we have not been instructed to wait for a turnaround character, we
+ can go by the packet length field. If turn != 0, we must wait for the
+ end of line (eol) character before returning.
+*/
+ if (i == 2) {
+ pktlen = xunchar(dest[1]);
+ havelen = (pktlen > 1);
+ debug(F101,"ttinl length","",pktlen);
+ } else if (i == 5 && pktlen == 0) {
+ lplen = xunchar(dest[4]);
+ } else if (i == 6 && pktlen == 0) {
+ pktlen = lplen * 95 + xunchar(dest[5]) + 5;
+ havelen = 1;
+ debug(F101,"ttinl length","",pktlen);
+ }
+#else
+ dest[i++] = n & ttpmsk;
+#endif /* PARSENSE */
+ if ((n & 0x7f) == CTRLC) { /* Check for ^C^C */
+ if (++ccn > 1) { /* If we got 2 in a row, bail out. */
+ if (timo) { /* Clear timer. */
+ ttimoff();
+ }
+ fprintf(stderr,"^C...\r\n"); /* Echo Ctrl-C */
+ return(-2);
+ }
+ } else ccn = 0; /* Not ^C, so reset ^C counter, */
+
+#ifdef PARSENSE
+ if (flag == 0) {
+ debug(F101,"ttinl skipping","",n);
+ continue;
+ }
+#endif /* PARSENSE */
+
+ /* Check for end of packet */
+
+ if (((n & 0x7f) == eol)
+#ifdef PARSENSE
+ || (!turn && havelen && (i > pktlen+1))
+#endif /* PARSENSE */
+ ) {
+#ifndef PARSENSE
+ debug(F101,"ttinl got eol","",eol);
+ dest[i] = '\0'; /* Yes, terminate the string, */
+ /* debug(F101,"ttinl i","",i); */
+#else
+ if ((n & 0x7f) != eol) {
+ debug(F101,"ttinl EOP length","",pktlen);
+ debug(F101,"ttinl i","",i);
+ } else debug(F101,"ttinl got eol","",eol);
+ dest[i] = '\0'; /* Terminate the string, */
+
+/* Here's where we actually check and adjust the parity. */
+/* The major flaw here is if parity is NONE (ttprty = 0) and the packets */
+/* really do have no parity, then parchk() is called for every packet. */
+/* In practice, this doesn't really harm efficiency noticably, but it would */
+/* be better if ttinl() had a way of knowing to stop doing this once a */
+/* particular file transfer had been started and checked. */
+ if (ttprty == 0) {
+ if ((ttprty = parchk(dest,start,i)) > 0) {
+ int j;
+ debug(F101,"ttinl senses parity","",ttprty);
+ debug(F110,"ttinl packet before",dest,0);
+ ttpmsk = 0x7f;
+ for (j = 0; j < i; j++)
+ dest[j] &= 0x7f; /* Strip parity from packet */
+ debug(F110,"ttinl packet after ",dest,0);
+ } else debug(F101,"parchk","",ttprty);
+ }
+#endif /* PARSENSE */
+ if (timo) { /* Turn off timer. */
+ ttimoff();
+ }
+ debug(F111,"ttinl got", dest,i);
+ return(i);
+ }
+ } /* end of while() */
+ ttimoff();
+ return(-1);
+ }
+}
+\f
+/* T T I N C -- Read a character from the communication line */
+/*
+ On success, returns the character that was read, >= 0.
+ On failure, returns -1 or other negative myread error code.
+*/
+int
+ttinc(timo) int timo; {
+
+ int n = 0;
+#ifndef MYREAD
+ CHAR ch = 0;
+#endif /* MYREAD */
+
+ if (ttyfd < 0) return(-1); /* Not open. */
+ if (timo <= 0) { /* Untimed. */
+#ifdef MYREAD
+ /* comm line failure returns -1 thru myread, so no &= 0377 */
+ n = myread(); /* Wait for a character... */
+ /* debug(F101,"ttinc n","",n); */
+ return(n < 0 ? n : n & ttpmsk);
+#else
+ while ((n = read(ttyfd,&ch,1)) == 0) /* Wait for a character. */
+ /* Shouldn't have to loop in ver 5A. */
+#ifdef NETCONN
+ if (netconn) { /* Special handling for net */
+ netclos();
+ netconn = 0;
+ errno = ENOTCONN;
+ return(-2);
+ }
+#endif /* NETCONN */
+ ;
+/* debug(F000,"ttinc","",ch); */
+ return( (n < 1) ? -3 : (ch & ttpmsk) );
+#endif /* MYREAD */
+ } else {
+ saval = signal(SIGALRM,timerh); /* Timed, set up timer. */
+ alarm(timo);
+ if (setjmp(sjbuf)) {
+ n = -1;
+ } else {
+#ifdef MYREAD
+ n = myread(); /* If managing own buffer... */
+ debug(F101,"ttinc myread","",n);
+#else
+ n = read(ttyfd,&ch,1); /* Otherwise call the system. */
+ debug(F101,"ttinc read","",n);
+ if (n > 0)
+ n = ch & 255;
+ else
+ n = (n < 0) ? -3 : -2; /* Special return codes */
+#endif /* MYREAD */
+ }
+ ttimoff(); /* Turn off timer */
+#ifdef NETCONN
+ if (netconn) {
+ if (n == -2) { /* read() returns 0 */
+ netclos(); /* on network read failure */
+ netconn = 0;
+ errno = ENOTCONN;
+ }
+ }
+#endif /* NETCONN */
+ return( (n < 0) ? n : (n & ttpmsk) ); /* Return masked char or neg. */
+ }
+}
+\f
+/* S N D B R K -- Send a BREAK signal of the given duration */
+
+static int
+#ifdef CK_ANSIC
+sndbrk(int msec) { /* Argument is milliseconds */
+#else
+sndbrk(msec) int msec; {
+#endif /* CK_ANSIC */
+#ifndef POSIX
+ int x, n;
+#endif /* POSIX */
+
+#ifdef ANYBSD
+#define BSDBREAK
+#endif /* ANYBSD */
+
+#ifdef COHERENT
+#define BSDBREAK
+#endif /* COHERENT */
+
+#ifdef PROVX1
+ char spd;
+#endif /* PROVX1 */
+
+ debug(F101,"ttsndb ttyfd","",ttyfd);
+ if (ttyfd < 0) return(-1); /* Not open. */
+
+#ifdef NETCONN
+ if (netconn) /* Send network BREAK */
+ return(netbreak());
+#endif /* NETCONN */
+
+ if (msec < 1 || msec > 5000) return(-1); /* Bad argument */
+
+#ifdef POSIX /* Easy in POSIX */
+ return(tcsendbreak(ttyfd,msec / 375));
+#else
+#ifdef PROVX1
+ gtty(ttyfd,&ttbuf); /* Get current tty flags */
+ spd = ttbuf.sg_ospeed; /* Save speed */
+ ttbuf.sg_ospeed = B50; /* Change to 50 baud */
+ stty(ttyfd,&ttbuf); /* ... */
+ n = (int)strlen(brnuls); /* Send the right number of nulls */
+ x = msec / 91;
+ if (x > n) x = n;
+ write(ttyfd,brnuls,n);
+ ttbuf.sg_ospeed = spd; /* Restore speed */
+ stty(ttyfd,&ttbuf); /* ... */
+ return(0);
+#else
+#ifdef aegis
+ sio_$control((short)ttyfd, sio_$send_break, msec, st);
+ return(0);
+#else
+#ifdef ATTSV
+/*
+ No way to send a long BREAK in Sys V, so send a bunch of regular ones.
+ (Actually, Sys V R4 is *supposed* to have the POSIX tcsendbreak() function,
+ but there's no way for this code to know for sure.)
+*/
+ x = msec / 275;
+ for (n = 0; n < x; n++) {
+ if (ioctl(ttyfd,TCSBRK,(char *)0) < 0) {
+ perror("Can't send BREAK");
+ return(-1);
+ }
+ }
+ return(0);
+#else
+#ifdef BSDBREAK
+ n = FWRITE; /* Flush output queue. */
+/* Watch out for int vs long problems in &n arg! */
+ ioctl(ttyfd,TIOCFLUSH,&n); /* Ignore any errors.. */
+ if (ioctl(ttyfd,TIOCSBRK,(char *)0) < 0) { /* Turn on BREAK */
+ perror("Can't send BREAK");
+ return(-1);
+ }
+ x = msleep(msec); /* Sleep for so many milliseconds */
+ if (ioctl(ttyfd,TIOCCBRK,(char *)0) < 0) { /* Turn off BREAK */
+ perror("BREAK stuck!!!");
+ doexit(BAD_EXIT,-1); /* Get out, closing the line. */
+ /* with bad exit status */
+ }
+ return(x);
+#else
+#ifdef V7
+ return(genbrk(ttyfd,250)); /* Simulate a BREAK */
+#endif /* V7 */
+#endif /* BSDBREAK */
+#endif /* ATTSV */
+#endif /* aegis */
+#endif /* PROVX1 */
+#endif /* POSIX */
+}
+
+/* T T S N D B -- Send a BREAK signal */
+
+int
+ttsndb() {
+ return(sndbrk(275));
+}
+
+/* T T S N D L B -- Send a Long BREAK signal */
+
+int
+ttsndlb() {
+ return(sndbrk(1500));
+}
+\f
+/* M S L E E P -- Millisecond version of sleep(). */
+
+/*
+ Call with number of milliseconds (thousandths of seconds) to sleep.
+ Intended only for small intervals. For big ones, just use sleep().
+ Highly system-dependent.
+ Returns 0 always, even if it didn't work.
+*/
+
+/* Define MSLFTIME for systems that must use an ftime() loop. */
+#ifdef ANYBSD /* For pre-4.2 BSD versions */
+#ifndef BSD42
+#ifndef __386BSD__
+#define MSLFTIME
+#endif /* __386BSD__ */
+#endif /* BSD42 */
+#endif /* ANYBSD */
+#ifdef TOWER1 /* NCR Tower OS 1.0 */
+#define MSLFTIME
+#endif /* TOWER1 */
+#ifdef COHERENT /* Coherent */
+#define MSLFTIME
+#endif /* COHERENT */
+
+int
+msleep(m) int m; {
+
+#ifndef USLEEP
+#ifdef SUNOS4 /* Systems that have usleep() */
+#define USLEEP
+#endif /* SUNOS4 */
+#ifdef SUN4S5
+#define USLEEP
+#endif /* SUN4S5 */
+#ifdef NEXT
+#define USLEEP
+#endif /* NEXT*/
+#endif /* USLEEP */
+
+#ifdef AIXRS
+/* RS/6000 can do select() */
+#define BSD4
+#endif /* AIXRS */
+
+#ifdef USLEEP
+/*
+ "This routine is implemented using setitimer(2); it requires eight
+ system calls...". In other words, it might take 5 minutes to sleep
+ for 100 milliseconds...
+*/
+ if (m >= 1000) { /* Catch big arguments. */
+ sleep(m/1000);
+ m = m % 1000;
+ if (m < 10) return(0);
+ }
+ usleep((unsigned int)(m * 1000));
+ return(0);
+#else
+#ifdef aegis
+ time_$clock_t dur;
+
+ dur.c2.high16 = 0;
+ dur.c2.low32 = 250 * m; /* one millisecond = 250 four microsecond ticks */
+ time_$wait(time_$relative, dur, st);
+ return(0);
+#else
+#ifdef PROVX1
+ if (m <= 0) return(0);
+ sleep(-((m * 60 + 500) / 1000));
+ return(0);
+#else
+#ifdef NAP
+ nap((long)m);
+ return(0);
+#else
+#ifdef BSD42
+/* BSD 4.2 & above can do it with select()... */
+ int t1;
+ if (m <= 0) return(0);
+ if (m >= 1000) { /* Catch big arguments. */
+ sleep(m/1000);
+ m = m % 1000;
+ if (m < 10) return(0);
+ }
+ if (gettimeofday(&tv, &tz) < 0) return(-1); /* Get current time. */
+ t1 = tv.tv_sec; /* Seconds */
+ tv.tv_sec = 0; /* Use select() */
+ tv.tv_usec = m * 1000L;
+ select( 0, (int *)0, (int *)0, (int *)0, &tv );
+ return(0);
+#ifdef AIXRS
+/* Don't pretend we're BSD any more! */
+#undef BSD4
+#endif /* AIXRS */
+
+#else
+#ifdef ATTSV
+ extern long times(); /* Or #include <times.h> ? */
+ long t1, t2, tarray[4];
+ int t3;
+
+#ifdef COMMENT
+/* This better be picked up in ckcdeb.h... */
+ char *getenv();
+#endif /* COMMENT */
+ char *cp = getenv("HZ");
+ int CLOCK_TICK;
+ int hertz;
+
+ if (cp && (hertz = atoi(cp))) {
+ CLOCK_TICK = 1000 / hertz;
+ } else { /* probably single user mode */
+#ifdef HZ
+ CLOCK_TICK = 1000 / HZ;
+#else
+ static warned = 0;
+ /* HZ always exists in, for instance, SCO Xenix, so you don't have to
+ * make special #ifdefs for XENIX here, like in ver 4F. Also, if you
+ * have Xenix, you have should have nap(), so the best is to use -DNAP
+ * in the makefile. Most systems have HZ.
+ */
+ CLOCK_TICK = 17; /* 1/60 sec */
+ if (!warned) {
+ printf("warning: environment variable HZ bad... using HZ=%d\r\n",
+ 1000 / CLOCK_TICK);
+ warned = 1;
+ }
+#endif /* !HZ */
+ }
+
+ if (m <= 0) return(0);
+ if (m >= 1000) { /* Catch big arguments. */
+ sleep(m/1000);
+ m = m % 1000;
+ if (m < 10) return(0);
+ }
+ if ((t1 = times(tarray)) < 0) return(-1);
+ while (1) {
+ if ((t2 = times(tarray)) < 0) return(-1);
+ t3 = ((int)(t2 - t1)) * CLOCK_TICK;
+ if (t3 > m) return(t3);
+ }
+#else /* Not ATTSV */
+#ifdef MSLFTIME /* Use ftime() loop... */
+ int t1, t3 = 0;
+ if (m <= 0) return(0);
+ if (m >= 1000) { /* Catch big arguments. */
+ sleep(m/1000);
+ m = m % 1000;
+ if (m < 10) return(0);
+ }
+ if (ftime(&ftp) < 0) return(-1); /* Get base time. */
+ t1 = ((ftp.time & 0xff) * 1000) + ftp.millitm;
+ while (1) {
+ ftime(&ftp); /* Get current time and compare. */
+ t3 = (((ftp.time & 0xff) * 1000) + ftp.millitm) - t1;
+ if (t3 > m) return(0);
+ }
+#else
+/* This includes true POSIX, which has no way to do this. */
+ if (m >= 1000) { /* Catch big arguments. */
+ sleep(m/1000);
+ m = m % 1000;
+ if (m < 10) return(0);
+ }
+ if (m > 0) while (m > 0) m--; /* Just a dumb busy loop */
+ return(0);
+#endif /* MLSFTIME */
+#endif /* ATTSV */
+#endif /* BSD42 */
+#endif /* NAP */
+#endif /* PROVX1 */
+#endif /* aegis */
+#endif /* USLEEP */
+}
+\f
+/* R T I M E R -- Reset elapsed time counter */
+
+VOID
+rtimer() {
+ tcount = time( (time_t *) 0 );
+}
+
+
+/* G T I M E R -- Get current value of elapsed time counter in seconds */
+
+int
+gtimer() {
+ int x;
+ x = (int) (time( (time_t *) 0 ) - tcount);
+ debug(F101,"gtimer","",x);
+ return( (x < 0) ? 0 : x );
+}
+
+
+/* Z T I M E -- Return date/time string */
+
+VOID
+ztime(s) char **s; {
+
+#undef ZTIMEV7 /* Which systems need to use */
+#ifdef COHERENT /* old UNIX Version 7 way... */
+#define ZTIMEV7
+#endif /* COHERENT */
+#ifdef TOWER1
+#define ZTIMEV7
+#endif /* TOWER1 */
+#ifdef ANYBSD
+#ifndef BSD42
+#define ZTIMEV7
+#endif /* BSD42 */
+#endif /* ANYBSD */
+#ifdef V7
+#ifndef MINIX
+#define ZTIMEV7
+#endif /* MINIX */
+#endif /* V7 */
+#ifdef POSIX
+#define ZTIMEV7
+#endif /* POSIX */
+
+#ifdef ATTSV /* AT&T way */
+/* extern long time(); */ /* Theoretically these should */
+ char *ctime(); /* already been dcl'd in <time.h> */
+ long clock_storage;
+ clock_storage = time( (long *) 0 );
+ *s = ctime( &clock_storage );
+#else
+#ifdef PROVX1 /* Venix 1.0 way */
+ int utime[2];
+ time(utime);
+ *s = ctime(utime);
+#else
+#ifdef BSD42 /* 4.2BSD way */
+ char *asctime();
+ struct tm *localtime();
+ struct tm *tp;
+ gettimeofday(&tv, &tz);
+ time(&tv.tv_sec);
+ tp = localtime(&tv.tv_sec);
+ *s = asctime(tp);
+#else
+#ifdef MINIX /* MINIX way */
+#ifdef COMMENT
+ extern long time(); /* Already got these from <time.h> */
+ extern char *ctime();
+#endif /* COMMENT */
+ time_t utime[2];
+ time(utime);
+ *s = ctime(utime);
+#else
+#ifdef ZTIMEV7 /* The regular way */
+ char *asctime();
+ struct tm *localtime();
+ struct tm *tp;
+ long xclock;
+ time(&xclock);
+ tp = localtime(&xclock);
+ *s = asctime(tp);
+#else /* Catch-all for others... */
+ *s = "Ddd Mmm 00 00:00:00 0000\n" /* Return dummy in asctime() format */
+#endif /* ZTIMEV7 */
+#endif /* MINIX */
+#endif /* BSD42 */
+#endif /* PROVX1 */
+#endif /* SVORPOSIX */
+}
+\f
+/* C O N G M -- Get console terminal modes. */
+
+/*
+ Saves initial console mode, and establishes variables for switching
+ between current (presumably normal) mode and other modes.
+ Should be called when program starts, but only after establishing
+ whether program is in the foreground or background.
+ Returns 1 if it got the modes OK, 0 if it did nothing, -1 on error.
+*/
+int
+congm() {
+ int fd;
+ if (backgrd || !isatty(0)) { /* If in background. */
+ cgmf = -1; /* Don't bother, modes are garbage. */
+ return(-1);
+ }
+ if (cgmf > 0) return(0); /* Already did this. */
+ debug(F100,"congm getting modes","",0); /* Need to do it. */
+#ifdef aegis
+ ios_$inq_type_uid(ios_$stdin, conuid, st);
+ if (st.all != status_$ok) {
+ fprintf(stderr, "problem getting stdin objtype: ");
+ error_$print(st);
+ }
+ concrp = (conuid == mbx_$uid);
+ conbufn = 0;
+#endif /* aegis */
+
+ if ((fd = open(CTTNAM,2)) < 0) { /* Open controlling terminal */
+ fprintf(stderr,"Error opening %s\n", CTTNAM);
+ perror("congm");
+ return(-1);
+ }
+#ifdef ATTSV
+ if (ioctl(fd,TCGETA,&ccold) < 0) return(-1);
+ if (ioctl(fd,TCGETA,&cccbrk) < 0) return(-1);
+ if (ioctl(fd,TCGETA,&ccraw) < 0) return(-1);
+#ifdef VXVE
+ cccbrk.c_line = 0; /* STTY line 0 for CDC VX/VE */
+ if (ioctl(fd,TCSETA,&cccbrk) < 0) return(-1);
+ ccraw.c_line = 0; /* STTY line 0 for CDC VX/VE */
+ if (ioctl(fd,TCSETA,&ccraw) < 0) return(-1);
+#endif /* VXVE */
+#else
+#ifdef POSIX
+ if (tcgetattr(fd,&ccold) < 0) return(-1);
+ if (tcgetattr(fd,&cccbrk) < 0) return(-1);
+ if (tcgetattr(fd,&ccraw) < 0) return(-1);
+#else
+ if (gtty(fd,&ccold) < 0) return(-1);
+ if (gtty(fd,&cccbrk) < 0) return(-1);
+ if (gtty(fd,&ccraw) < 0) return(-1);
+#endif /* POSIX */
+#endif /* ATTSV */
+#ifdef sony_news /* Sony NEWS */
+ if (ioctl(fd,TIOCKGET,&km_con) < 0) { /* Get console Kanji mode */
+ perror("congm error getting Kanji mode");
+ debug(F101,"congm error getting Kanji mode","",0);
+ km_con = -1; /* Make sure this stays undefined. */
+ return(-1);
+ }
+#endif /* sony_news */
+ close(fd);
+ cgmf = 1; /* Flag that we got them. */
+ return(1);
+}
+
+
+/* C O N C B -- Put console in cbreak mode. */
+
+/* Returns 0 if ok, -1 if not */
+
+int
+#ifdef CK_ANSIC
+concb(char esc)
+#else
+concb(esc) char esc;
+#endif /* CK_ANSIC */
+/* concb */ {
+ int x;
+ debug(F101,"concb backgrd","",backgrd);
+ if (!isatty(0)) return(0); /* Only for real ttys */
+ debug(F100,"concb isatty","",0);
+ if (backgrd) return(0); /* Do nothing if in background. */
+ escchr = esc; /* Make this available to other fns */
+ ckxech = 1; /* Program can echo characters */
+#ifdef aegis
+ conbufn = 0;
+ if (concrp) return(write(1, "\035\002", 2));
+ if (conuid == input_pad_$uid) {pad_$raw(ios_$stdin, st); return(0);}
+#endif
+#ifndef SVORPOSIX /* BSD, V7, etc */
+ cccbrk.sg_flags |= CBREAK; /* Set to character wakeup, */
+ cccbrk.sg_flags &= ~ECHO; /* no echo. */
+ x = stty(0,&cccbrk);
+#else /* Sys V and POSIX */
+ cccbrk.c_lflag &= ~(ICANON|ECHO);
+ cccbrk.c_cc[0] = 003; /* interrupt char is control-c */
+ cccbrk.c_cc[1] = escchr; /* escape during packet modes */
+ cccbrk.c_cc[4] = 1;
+#ifdef ZILOG
+ cccbrk.c_cc[5] = 0;
+#else
+ cccbrk.c_cc[5] = 1;
+#endif /* ZILOG */
+#ifdef ATTSV /* Set new modes */
+ x = ioctl(0,TCSETAW,&cccbrk); /* the Sys V way */
+#else /* POSIX */ /* or the POSIX way */
+ x = tcsetattr(0,TCSADRAIN,&cccbrk);
+#endif /* ATTSV */
+#endif /* SVORPOSIX */
+
+#ifndef aegis
+#ifndef NOSETBUF
+ if (x > -1) setbuf(stdout,NULL); /* Make console unbuffered. */
+#endif /* NOSETBUF */
+#endif /* aegis */
+
+#ifdef V7
+#ifndef MINIX
+ if (kmem[CON] < 0) {
+ qaddr[CON] = initrawq(0);
+ if((kmem[CON] = open("/dev/kmem", 0)) < 0) {
+ fprintf(stderr, "Can't read /dev/kmem in concb.\n");
+ perror("/dev/kmem");
+ exit(1);
+ }
+ }
+#endif /* MINIX */
+#endif /* V7 */
+ debug(F101,"concb returns","",x);
+ return(x);
+}
+\f
+/* C O N B I N -- Put console in binary mode */
+
+/* Returns 0 if ok, -1 if not */
+
+int
+#ifdef CK_ANSIC
+conbin(char esc)
+#else
+conbin(esc) char esc;
+#endif /* CK_ANSIC */
+/* conbin */ {
+ if (!isatty(0)) return(0); /* only for real ttys */
+ congm(); /* Get modes if necessary. */
+ debug(F100,"conbin","",0);
+ escchr = esc; /* Make this available to other fns */
+ ckxech = 1; /* Program can echo characters */
+#ifdef aegis
+ conbufn = 0;
+ if (concrp) return(write(1, "\035\002", 2));
+ if (conuid == input_pad_$uid)
+ pad_$raw(ios_$stdin, st);
+ return(0)
+#endif /* aegis */
+
+#ifdef SVORPOSIX
+ ccraw.c_lflag &= ~(ISIG|ICANON|ECHO);
+ ccraw.c_iflag |= (BRKINT|IGNPAR);
+#ifdef ATTSV
+ ccraw.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|IUCLC|IXON|IXANY|IXOFF
+ |INPCK|ISTRIP);
+#else /* POSIX */
+ ccraw.c_iflag &= ~(IGNBRK|INLCR|IGNCR|ICRNL|IXON|IXOFF|INPCK|ISTRIP);
+#ifdef __386BSD__
+ ccraw.c_lflag &= ~IEXTEN; /* pass CTRL-V though while connected */
+#endif /* __386BSD__ */
+#endif /* ATTSV */
+ ccraw.c_oflag &= ~OPOST;
+#ifdef ATT7300
+ ccraw.c_cflag = CLOCAL | B9600 | CS8 | CREAD | HUPCL;
+#endif /* ATT7300 */
+/*** Kermit used to put the console in 8-bit raw mode, but some users have
+ *** pointed out that this should not be done, since some sites actually
+ *** use terminals with parity settings on their Unix systems, and if we
+ *** override the current settings and stop doing parity, then their terminals
+ *** will display blotches for characters whose parity is wrong. Therefore,
+ *** the following two lines are commented out (Larry Afrin, Clemson U):
+ ***
+ *** ccraw.c_cflag &= ~(PARENB|CSIZE);
+ *** ccraw.c_cflag |= (CS8|CREAD);
+ ***
+ *** Sys III/V sites that have trouble with this can restore these lines.
+ ***/
+ ccraw.c_cc[0] = 003; /* Interrupt char is Ctrl-C */
+ ccraw.c_cc[1] = escchr; /* Escape during packet mode */
+ ccraw.c_cc[4] = 1;
+
+#ifdef ZILOG
+ ccraw.c_cc[5] = 0;
+#else
+ ccraw.c_cc[5] = 1;
+#endif /* ZILOG */
+
+#ifdef ATTSV
+ return(ioctl(0,TCSETAW,&ccraw)); /* Set new modes. */
+#else
+ return(tcsetattr(0,TCSADRAIN,&ccraw));
+#endif /* ATTSV */
+
+#else /* Berkeley, etc. */
+ ccraw.sg_flags |= (RAW|TANDEM); /* Set rawmode, XON/XOFF (ha) */
+ ccraw.sg_flags &= ~(ECHO|CRMOD); /* Set char wakeup, no echo */
+ return(stty(0,&ccraw));
+#endif /* SVORPOSIX */
+}
+
+
+/* C O N R E S -- Restore the console terminal */
+
+int
+conres() {
+ debug(F101,"conres cgmf","",cgmf);
+ if (cgmf < 1) return(0); /* Do nothing if modes unchanged */
+ if (!isatty(0)) return(0); /* only for real ttys */
+ debug(F100,"conres isatty ok","",0);
+ ckxech = 0; /* System should echo chars */
+
+#ifdef aegis
+ conbufn = 0;
+ if (concrp) return(write(1, "\035\001", 2));
+ if (conuid == input_pad_$uid)
+ pad_$cooked(ios_$stdin, st);
+ return(0);
+#endif /* aegis */
+
+#ifdef ATTSV
+ debug(F100,"conres restoring ioctl","",0);
+ return(ioctl(0,TCSETAW,&ccold));
+#else
+#ifdef POSIX
+ debug(F100,"conres restoring tcsetattr","",0);
+ return(tcsetattr(0,TCSADRAIN,&ccold));
+#else
+#ifdef sony_news /* Sony NEWS */
+ return(ioctl(0,TIOCKSET,&km_con)); /* Restore console Kanji mode */
+#else /* BSD, V7, and friends */
+ msleep(300);
+ debug(F100,"conres restoring stty","",0);
+ return(stty(0,&ccold));
+#endif /* sony_news */
+#endif /* POSIX */
+#endif /* ATTSV */
+}
+\f
+/* C O N O C -- Output a character to the console terminal */
+
+int
+#ifdef CK_ANSIC
+conoc(char c)
+#else
+conoc(c) char c;
+#endif /* CK_ANSIC */
+/* conoc */ {
+ return(write(1,&c,1));
+}
+
+/* C O N X O -- Write x characters to the console terminal */
+
+int
+conxo(x,s) int x; char *s; {
+ return(write(1,s,x));
+}
+
+/* C O N O L -- Write a line to the console terminal */
+
+int
+conol(s) char *s; {
+ int len;
+ len = (int)strlen(s);
+ return(write(1,s,len));
+}
+
+/* C O N O L A -- Write an array of lines to the console terminal */
+
+int
+conola(s) char *s[]; {
+ int i;
+ for (i=0 ; *s[i] ; i++) if (conol(s[i]) < 0) return(-1);;
+ return(0);
+}
+
+/* C O N O L L -- Output a string followed by CRLF */
+
+int
+conoll(s) char *s; {
+ conol(s);
+ return(write(1,"\r\n",2));
+}
+\f
+/* C O N C H K -- Return how many characters available at console */
+
+int
+conchk() {
+ int x; PEEKTYPE n;
+
+ if (!isatty(0)) return(0);
+#ifdef PROVX1
+ x = ioctl(0, TIOCQCNT, &ttbuf);
+ n = ttbuf.sg_ispeed & 0377;
+ return((x < 0) ? 0 : n);
+#else
+#ifdef aegis
+ if (conbufn > 0) return(conbufn); /* use old count if nonzero */
+
+ /* read in more characters */
+ conbufn = ios_$get(ios_$stdin,
+ ios_$cond_opt, conbuf, (long)sizeof(conbuf), st);
+ if (st.all != status_$ok) conbufn = 0;
+ conbufp = conbuf;
+ return(conbufn);
+#else
+#ifdef V7
+#ifdef MINIX
+ return(0);
+#else
+ lseek(kmem[CON], (long) qaddr[CON], 0);
+ x = read(kmem[CON], &n, sizeof(int));
+ return((x == sizeof(int))? n: 0);
+#endif /* MINIX */
+#else
+#ifdef SVORPOSIX
+ if (conesc) { /* Escape typed */
+ conesc = 0;
+ signal(SIGQUIT,esctrp); /* Restore escape */
+ return(1);
+ }
+ return(0);
+#else
+#ifdef C70
+ if (conesc) { /* Escape typed */
+ conesc = 0;
+ signal(SIGQUIT,esctrp); /* Restore escape */
+ return(1);
+ }
+ return(0);
+#else
+#ifdef FIONREAD
+ x = ioctl(0, FIONREAD, &n); /* BSD and maybe some others */
+ debug(F101,"conchk","",n);
+ return((x < 0) ? 0 : n);
+#else
+ return(0); /* Others can't do. */
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+}
+\f
+/* C O N I N C -- Get a character from the console */
+
+int
+coninc(timo) int timo; {
+ int n = 0; CHAR ch;
+#ifdef aegis
+ fflush(stdout);
+ if (conchk() > 0) {
+ --conbufn;
+ return(*conbufp++ & 0377);
+ }
+#endif /* aegis */
+
+ if (timo <= 0 ) { /* Untimed. */
+ n = read(0, &ch, 1); /* Read a character. */
+ ch &= 0377;
+ if (n > 0) /* Return the character if read ok */
+ return(ch);
+ else /* otherwise... */
+#ifdef SVORPOSIX
+#ifndef CIE /* CIE Regulus has no such symbol */
+ if (n < 0 && errno == EINTR) /* If read was interrupted by QUIT, */
+ return(escchr); /* user entered escape character. */
+ else /* Can't be ^c, sigint never returns */
+#endif /* CIE */
+#endif /* SVORPOSIX */
+ {
+ debug(F101, "coninc(0) errno","",errno);
+ return(-1); /* Return -1 as error indication */
+ }
+ }
+ saval = signal(SIGALRM,timerh); /* Timed read, so set up timer */
+ alarm(timo);
+ if (setjmp(sjbuf)) n = -2;
+ else {
+ n = read(0, &ch, 1);
+ ch &= 0377;
+ }
+ ttimoff(); /* Turn off timer */
+ if (n > 0) return(ch);
+ else
+#ifdef SVORPOSIX
+#ifndef CIE /* CIE Regulus has no such symbol */
+ if (n == -1 && errno == EINTR) /* If read interrupted by QUIT, */
+ return(escchr); /* user entered escape character, */
+ else /* can't be ^c, sigint never returns */
+#endif /* CIE */
+#endif /* SVORPOSIX */
+ return(-1);
+}
+
+/* C O N G K S -- Console Get Keyboard Scancode */
+
+#ifndef congks
+/*
+ This function needs to be filled in with the various system-dependent
+ system calls used by SUNOS, NeXT OS, Xenix, Aviion, etc, to read a full
+ keyboard scan code. For now, it's a dummy.
+*/
+int
+congks(timo) int timo; {
+ return(coninc(timo));
+}
+#endif /* congks */
+\f
+#ifdef ATT7300
+
+/* A T T D I A L -- Dial up the remote system using internal modem
+ * Purpose: to open and dial a number on the internal modem available on the
+ * ATT7300 UNIX PC. Written by Joe Doupnik. Superceeds version written by
+ * Richard E. Hill, Dickinson, TX. which employed dial(3c).
+ * Uses information in <sys/phone.h> and our status int attmodem.
+ */
+attdial(ttname,speed,telnbr) char *ttname,*telnbr; long speed; {
+ char *telnum;
+ int ttclos();
+
+ attmodem &= ~ISMODEM; /* modem not in use yet */
+ /* Ensure O_NDELAY is set, else i/o traffic hangs */
+ /* We turn this flag off once the dial is complete */
+ fcntl(ttyfd, F_SETFL, fcntl(ttyfd, F_GETFL, 0) | O_NDELAY);
+
+ /* Condition line, check availability & DATA mode, turn on speaker */
+ if (ioctl(ttyfd,PIOCOFFHOOK, &dialer) == -1) {
+ printf("cannot access phone\n");
+ ttclos(0);
+ return (-2);
+ }
+ ioctl(ttyfd,PIOCGETP,&dialer); /* get phone dialer parameters */
+
+ if (dialer.c_lineparam & VOICE) { /* phone must be in DATA mode */
+ printf(" Should not dial with modem in VOICE mode.\n");
+ printf(" Exit Kermit, switch to DATA and retry call.\n");
+ ttclos(0);
+ return (-2);
+ }
+#ifdef ATTTONED /* Old way, tone dialing only. */
+ dialer.c_lineparam = DATA | DTMF; /* Dial with tones, */
+ dialer.c_lineparam &= ~PULSE; /* not with pulses. */
+#else
+ /* Leave current pulse/tone state alone. */
+ /* But what about DATA? Add it back if you have trouble. */
+ /* sys/phone says you get DATA automatically by opening device RDWR */
+#endif
+ dialer.c_waitdialtone = 5; /* wait 5 sec for dialtone */
+#ifdef COMMENT
+ dialer.c_feedback = SPEAKERON|NORMSPK|RINGON; /* control speaker */
+#else
+ /* sys/phone says RINGON used only for incoming voice calls */
+ dialer.c_feedback &= ~(SOFTSPK|LOUDSPK);
+ dialer.c_feedback |= SPEAKERON|NORMSPK;
+#endif
+ dialer.c_waitflash = 500; /* 0.5 sec flash hook */
+ if(ioctl(ttyfd,PIOCSETP,&dialer) == -1) { /* set phone parameters */
+ printf("Cannot set modem characteristics\n");
+ ttclos(0);
+ return (-2);
+ }
+ ioctl(ttyfd,PIOCRECONN,0); /* Turns on speaker for pulse */
+
+#ifdef COMMENT
+ fprintf(stderr,"Phone line status. line_par:%o dialtone_wait:%o \
+line_status:%o feedback:%o\n",
+ dialer.c_lineparam, dialer.c_waitdialtone,
+ dialer.c_linestatus, dialer.c_feedback);
+#endif
+
+ attmodem |= ISMODEM; /* modem is now in-use */
+ sleep(1);
+ for (telnum = telnbr; *telnum != '\0'; telnum++) /* dial number */
+#ifdef ATTTONED
+ /* Tone dialing only */
+ if (ioctl(ttyfd,PIOCDIAL,telnum) != 0) {
+ perror("Error in dialing");
+ ttclos(0);
+ return(-2);
+ }
+#else /* Allow Pulse or Tone dialing */
+ switch (*telnum) {
+ case 't': case 'T': case '%': /* Tone dialing requested */
+ dialer.c_lineparam |= DTMF;
+ dialer.c_lineparam &= ~PULSE;
+ if (ioctl(ttyfd,PIOCSETP,&dialer) == -1) {
+ printf("Cannot set modem to tone dialing\n");
+ ttclos(0);
+ return(-2);
+ }
+ break;
+ case 'd': case 'D': case 'p': case 'P': case '^':
+ dialer.c_lineparam |= PULSE;
+ dialer.c_lineparam &= ~DTMF;
+ if (ioctl(ttyfd,PIOCSETP,&dialer) == -1) {
+ printf("Cannot set modem to pulse dialing\n");
+ ttclos(0);
+ return(-2);
+ }
+ break;
+ default:
+ if (ioctl(ttyfd,PIOCDIAL,telnum) != 0) {
+ perror("Dialing error");
+ ttclos(0);
+ return(-2);
+ }
+ break;
+ }
+#endif
+
+ ioctl(ttyfd,PIOCDIAL,"@"); /* terminator for data call */
+ do { /* wait for modems to Connect */
+ if (ioctl(ttyfd,PIOCGETP,&dialer) != 0) { /* get params */
+ perror("Cannot get modems to connect");
+ ttclos(0);
+ return(-2);
+ }
+ } while ((dialer.c_linestatus & MODEMCONNECTED) == 0);
+ /* Turn off O_NDELAY flag now. */
+ fcntl(ttyfd, F_SETFL, fcntl(ttyfd, F_GETFL, 0) & ~O_NDELAY);
+ signal(SIGHUP, ttclos); /* hangup on loss of carrier */
+ return(0); /* return success */
+}
+
+/*
+ Offgetty, ongetty functions. These function get the 'getty(1m)' off
+ and restore it to the indicated line. Shell's return codes are:
+ 0: Can't do it. Probably a user logged on.
+ 1: No need. No getty on that line.
+ 2: Done, you should restore the getty when you're done.
+ DOGETY System(3), however, returns them as 0, 256, 512, respectively.
+ Thanks to Kevin O'Gorman, Anarm Software Systems.
+
+ getoff.sh looks like: geton.sh looks like:
+ setgetty $1 0 setgetty $1 1
+ err=$? exit $?
+ sleep 2
+ exit $err
+*/
+
+/* O F F G E T T Y -- Turn off getty(1m) for the communications tty line
+ * and get status so it can be restarted after the line is hung up.
+ */
+int
+offgetty(ttname) char *ttname; {
+ char temp[30];
+ while (*ttname != '\0') ttname++; /* seek terminator of path */
+ ttname -= 3; /* get last 3 chars of name */
+ sprintf(temp,"/usr/bin/getoff.sh %s",ttname);
+ return(zsyscmd(temp));
+}
+
+/* O N G E T T Y -- Turn on getty(1m) for the communications tty line */
+
+int
+ongetty(ttname) char *ttname; {
+ char temp[30];
+ while (*ttname != '\0') ttname++; /* comms tty path name */
+ ttname -= 3;
+ sprintf(temp,"/usr/bin/geton.sh %s",ttname);
+ return(zsyscmd(temp));
+}
+#endif /* ATT7300 */
+\f
+/* T T S C A R R -- Set ttcarr variable, controlling carrier handling.
+ *
+ * 0 = Off: Always ignore carrier. E.g. you can connect without carrier.
+ * 1 = On: Heed carrier, except during dialing. Carrier loss gives disconnect.
+ * 2 = Auto: For "modem direct": The same as "Off".
+ * For real modem types: Heed carrier during connect, but ignore
+ * it anytime else. Compatible with pre-5A C-Kermit versions.
+ *
+ * As you can see, this setting does not affect dialing, which always ignores
+ * carrier (unless there is some special exception for some modem type). It
+ * does affect ttopen() if it is set before ttopen() is used. This setting
+ * takes effect on the next call to ttopen()/ttpkt()/ttvt(). And they are
+ * (or should be) always called before any communications is tried, which
+ * means that, practically speaking, the effect is immediate.
+ *
+ * Of course, nothing of this applies to remote mode (xlocal = 0).
+ *
+ * Someone has yet to uncover how to manipulate the carrier in the BSD
+ * environment (or any non-termio using environment). Until that time, this
+ * will simply be a no-op for BSD.
+ *
+ * Note that in previous versions, the carrier was most often left unchanged
+ * in ttpkt()/ttvt() unless they were called with DIALING or CONNECT. This
+ * has changed. Now it is controlled by ttcarr in conjunction with these
+ * modes.
+ */
+int
+ttscarr(carrier) int carrier; {
+ ttcarr = carrier;
+ debug(F101, "ttscarr","",ttcarr);
+ return(ttcarr);
+}
+
+/* C A R R C T L -- Set tty modes for carrier treatment.
+ *
+ * Sets the appropriate bits in a termio or sgttyb struct for carrier control
+ * (actually, there are no bits in sgttyb for that), or performs any other
+ * operations needed to control this on the current system. The function does
+ * not do the actual TCSETA or stty, since often we want to set other bits too
+ * first. Don't call this function when xlocal is 0, or the tty is not opened.
+ *
+ * We don't know how to do anything like carrier control on non-ATTSV systems,
+ * except, apparently, ultrix. See above. It is also known that this doesn't
+ * have much effect on a Xenix system. For Xenix, one should switch back and
+ * forth between the upper and lower case device files. Maybe later.
+ * Presently, Xenix will stick to the mode it was opened with.
+ *
+ * carrier: 0 = ignore carrier, 1 = require carrier.
+ * The current state is saved in curcarr, and checked to save labour.
+ */
+#ifdef SVORPOSIX
+int
+#ifdef ATTSV
+carrctl(ttpar, carrier) struct termio *ttpar; int carrier;
+#else /* POSIX */
+carrctl(ttpar, carrier) struct termios *ttpar; int carrier;
+#endif /* ATTSV */
+/* carrctl */ {
+ debug(F101, "carrctl","",carrier);
+ if (carrier)
+ ttpar->c_cflag &= ~CLOCAL;
+ else
+ ttpar->c_cflag |= CLOCAL;
+ return(0);
+}
+#else /* Berkeley, V7, et al... */
+int
+carrctl(ttpar, carrier) struct sgttyb *ttpar; int carrier; {
+#ifdef ultrix
+ int temp = 0;
+#endif /* ultrix */
+#ifdef OXOS
+ int modem_status, lnohang = LNOHANG;
+#endif /* OXOS */
+ debug(F101, "carrctl","",carrier);
+ if (carrier == curcarr)
+ return(0);
+ curcarr = carrier;
+#ifdef ultrix
+ if (carrier) {
+ ioctl(ttyfd, TIOCMODEM, &temp);
+ ioctl(ttyfd, TIOCHPCL, 0);
+ } else {
+ /* (According to the manuals, TIOCNCAR should be preferred */
+ /* over TIOCNMODEM...) */
+ ioctl(ttyfd, TIOCNMODEM, &temp);
+ }
+#endif /* ultrix */
+#ifdef OXOS
+/*
+ From Fulvio Marino at Olivetti. This code allows CONNECT to work even
+ if DCD/RTS are down, if "carrier" is set appropriately.
+*/
+ if (ioctl(ttyfd, TIOCMODG, &modem_status) == 0) {
+ if (carrier) {
+ /* enable carrier detect */
+ modem_status |= TIOCM_CAR;
+ } else {
+ /* disable carrier detect */
+ modem_status &= ~TIOCM_CAR;
+ }
+ (void)ioctl(ttyfd, TIOCMODS, &modem_status);
+ }
+ if (carrier) {
+ /* Send hangup when carrier drops */
+ (void)ioctl(ttyfd, TIOCLBIC, &lnohang);
+ /* hang up the phone */
+ (void)ioctl(ttyfd, TIOCHPCL, NULL);
+ } else {
+ /* Don't send hangup when carrier drops */
+ (void)ioctl(ttyfd, TIOCLBIS, &lnohang);
+ }
+#endif /* OXOS */
+ return(0);
+}
+#endif /* SVORPOSIX */
+\f
+
+/* T T G M D M -- Get modem signals */
+/*
+ Looks for RS-232 modem signals, and returns those that are on in as its
+ return value, in a bit mask composed of the BM_xxx values defined in ckcdeb.h.
+ Returns:
+ -3 Not implemented
+ -2 if the communication device does not have modem control (e.g. telnet)
+ -1 on error.
+ >= 0 on success, with a bit mask containing the modem signals that are on.
+*/
+
+/*
+ Define the symbol K_MDMCTL if we have Sys V R3 / 4.3 BSD style
+ modem control, namely the TIOCMGET ioctl.
+*/
+
+#ifdef BSD43
+#define K_MDMCTL
+#endif
+
+#ifdef SUNOS4
+#define K_MDMCTL
+#endif
+
+#ifdef TIOCMGET
+#define K_MDMCTL
+#endif
+
+int
+ttgmdm() {
+
+#ifdef HPUX /* HPUX has its own way */
+
+/*
+ NOTE: I don't have an HPUX man page, and so I'm only guessing at the
+ right names for these symbols. Somebody with HPUX please let me know
+ what corrections are needed.
+*/
+
+ int x, y, z;
+
+ if (netconn) return(-2); /* No modem signals for network */
+ if (xlocal) /* Get modem signals */
+ x = ioctl(ttyfd,MCGETA,&y);
+ else
+ x = ioctl(0,MCGETA,&y);
+ if (x < 0) return(-1);
+ debug(F101,"ttgmdm","",y);
+
+ z = 0; /* Initialize return value */
+
+/* Now set bits for each modem signal that is reported to be on. */
+
+#ifdef MCTS
+ /* Clear To Send */
+ if (y & MCTS) z |= BM_CTS;
+#endif
+#ifdef MDSR
+ /* Data Set Ready */
+ if (y & MDSR) z |= BM_DSR;
+#endif
+#ifdef MDCD
+ /* Carrier */
+ if (y & MDCD) z |= BM_DCD;
+#endif
+#ifdef MRNG
+ /* Ring Indicate */
+ if (y & MRNG) z |= BM_RNG;
+#endif
+#ifdef MDTR
+ /* Data Terminal Ready */
+ if (y & MDTR) z |= BM_DTR;
+#endif
+#ifdef MRTS
+ /* Request To Send */
+ if (y & MRTS) z |= BM_RTS;
+#endif
+ return(z);
+
+#else /* ! HPUX */
+
+#ifdef K_MDMCTL
+/*
+ Note, <sys/ttycom> might have already been included by by <sys/ioctl.h>.
+ Hence the following ifndef on a symbol which is defined there.
+*/
+#ifndef TIOCMGET
+#ifdef __386BSD__
+#include <sys/ioctl.h>
+#else
+#include <sys/ttycom.h>
+#endif /* __386BSD__ */
+#endif /* TIOCMGET */
+
+ int x, y, z;
+
+ if (netconn) return(-2); /* Network, no modem signals. */
+ if (xlocal)
+ x = ioctl(ttyfd,TIOCMGET,&y); /* Get modem signals. */
+ else
+ x = ioctl(0,TIOCMGET,&y);
+ if (x < 0) return(-1);
+ debug(F101,"ttgmdm","",y);
+
+ z = 0; /* Initialize return value. */
+#ifdef TIOCM_CTS
+ /* Clear To Send */
+ if (y & TIOCM_CTS) z |= BM_CTS;
+#endif
+#ifdef TIOCM_DSR
+ /* Data Set Ready */
+ if (y & TIOCM_DSR) z |= BM_DSR;
+#endif
+#ifdef TIOCM_CAR
+ /* Carrier */
+ if (y & TIOCM_CAR) z |= BM_DCD;
+#endif
+#ifdef TIOCM_RNG
+ /* Ring Indicate */
+ if (y & TIOCM_RNG) z |= BM_RNG;
+#endif
+#ifdef TIOCM_DTR
+ /* Data Terminal Ready */
+ if (y & TIOCM_DTR) z |= BM_DTR;
+#endif
+#ifdef TIOCM_RTS
+ /* Request To Send */
+ if (y & TIOCM_RTS) z |= BM_RTS;
+#endif
+ return(z);
+#else
+ if (netconn) return(-2);
+ return(-3);
+
+#endif /* K_MDMCTL */
+#endif /* HPUX */
+}
+
+/* P S U S P E N D -- Put this process in the background. */
+
+/*
+ Call with flag nonzero if suspending is allowed, zero if not allowed.
+ Returns 0 on apparent success, -1 on failure (flag was zero, or
+ kill() returned an error code.
+*/
+int
+psuspend(flag) int flag; {
+
+#ifdef RTU
+ extern int rtu_bug;
+#endif /* RTU */
+
+ if (flag == 0) return(-1);
+
+#ifdef NOJC
+ return(-1);
+#else
+#ifdef SIGTSTP
+/*
+ The big question here is whether job control is *really* supported.
+ There's no way Kermit can know for sure. The fact that SIGTSTP is
+ defined does not guarantee the Unix kernel supports it, and the fact
+ that the Unix kernel supports it doesn't guarantee that the user's
+ shell (or other process that invoked Kermit) supports it.
+*/
+#ifdef RTU
+ rtu_bug = 1;
+#endif /* RTU */
+ if (kill(0,SIGSTOP) < 0
+#ifdef OXOS
+/*
+ Because "kill(myself,SIGSTOP)" can't be caught, blocked, or ignored..."
+*/
+ && kill(getpid(),SIGSTOP) < 0
+#else
+#ifdef MIPS
+/* Let's try this for MIPS too. */
+ && kill(getpid(),SIGSTOP) < 0
+#endif /* MIPS */
+#endif /* OXOS */
+ ) { /* If job control, suspend the job */
+ perror("suspend");
+ debug(F101,"psuspend error","",errno);
+ return(-1);
+ }
+ debug(F100,"psuspend ok","",0);
+ return(0);
+#else
+ return(-1);
+#endif /* SIGTSTP */
+#endif /* NOJC */
+}
+\f
+/*
+ setuid package, by Kristoffer Eriksson, with contributions from Dean
+ Long and fdc.
+*/
+
+#ifndef _POSIX_SOURCE
+#ifndef SUNOS4
+#ifndef NEXT
+#ifndef PS2AIX10
+extern UID_T getuid(), geteuid(), getreuid();
+extern GID_T getgid(), getegid(), getregid();
+#endif /* PS2AIX10 */
+#endif /* NEXT */
+#endif /* SUNOS4 */
+#endif /* _POSIX_SOURCE */
+
+/*
+Subject: Set-user-id
+To: fdc@watsun.cc.columbia.edu (Frank da Cruz)
+Date: Sat, 21 Apr 90 4:48:25 MES
+From: Kristoffer Eriksson <ske@pkmab.se>
+
+This is a set of functions to be used in programs that may be run set-user-id
+and/or set-group-id. They handle both the case where the program is not run
+with such privileges (nothing special happens then), and the case where one
+or both of these set-id modes are used. The program is made to run with the
+user's real user and group ids most of the time, except for when more
+privileges are needed. Don't set-user-id to "root".
+
+This works on System V and POSIX. In BSD, it depends on the
+"saved-set-user-id" feature.
+*/
+
+#define UID_ROOT 0 /* Root user and group ids */
+#define GID_ROOT 0
+
+/*
+ The following construction automatically defines the symbol SETREUID for
+ Unix versions based on Berkeley Unix 4.2 or later. If this symbol is
+ defined, then this program will use getreuid() and getregid() calls in
+ preference to getuid() and getgid(), which in Berkeley-based Unixes do
+ not allow arbitrary switching back and forth of real & effective uid.
+ This construction also allows -DSETREUID to be put on the cc command line
+ for any system that has and wants to use setre[ug]id(). It also prevents
+ automatic definition of SETREUID if -DNOSETREU is included on the cc
+ command line (or otherwise defined).
+*/
+#ifdef FT18 /* None of this for Fortune. */
+#define NOSETREU
+#endif /* FT18 */
+
+#ifdef ANYBSD
+#ifndef BSD29
+#ifndef BSD41
+#ifndef SETREUID
+#ifndef NOSETREU
+#define SETREUID
+#endif /* NOSETREU */
+#endif /* SETREUID */
+#endif /* !BSD41 */
+#endif /* !BSD29 */
+#endif /* ANYBSD */
+
+/* Variables for user and group IDs. */
+
+static UID_T realuid = (UID_T) -1, privuid = (UID_T) -1;
+static GID_T realgid = (GID_T) -1, privgid = (GID_T) -1;
+
+
+/* P R I V _ I N I -- Initialize privileges package */
+
+/* Called as early as possible in a set-uid or set-gid program to store the
+ * set-to uid and/or gid and step down to the users real uid and gid. The
+ * stored id's can be temporarily restored (allowed in System V) during
+ * operations that require the privilege. Most of the time, the program
+ * should execute in unpriviliged state, to not impose any security threat.
+ *
+ * Note: Don't forget that access() always uses the real id:s to determine
+ * file access, even with privileges restored.
+ *
+ * Returns an error mask, with error values or:ed together:
+ * 1 if setuid() fails,
+ * 2 if setgid() fails, and
+ * 4 if the program is set-user-id to "root", which can't be handled.
+ *
+ * Only the return value 0 indicates real success. In case of failure,
+ * those privileges that could be reduced have been, at least, but the
+ * program should be aborted none-the-less.
+ *
+ * Also note that these functions do not expect the uid or gid to change
+ * without their knowing. It may work if it is only done temporarily, but
+ * you're on your own.
+ */
+int
+priv_ini() {
+ int err = 0;
+
+ /* Save real ID:s. */
+ realuid = getuid();
+ realgid = getgid();
+
+ /* Save current effective ID:s, those set to at program exec. */
+ privuid = geteuid();
+ privgid = getegid();
+
+ /* If running set-uid, go down to real uid, otherwise remember that
+ * no privileged uid is available.
+ *
+ * Exceptions:
+ *
+ * 1) If the real uid is already "root" and the set-uid uid (the
+ * initial effective uid) is not "root", then we would have trouble
+ * if we went "down" to "root" here, and then temporarily back to the
+ * set-uid uid (not "root") and then again tried to become "root". I
+ * think the "saved set-uid" is lost when changing uid from effective
+ * uid "root", which changes all uid, not only the effective uid. But
+ * in this situation, we can simply go to "root" and stay there all
+ * the time. That should give sufficient privilege (understatement!),
+ * and give the right uids for subprocesses.
+ *
+ * 2) If the set-uid (the initial effective uid) is "root", and we
+ * change uid to the real uid, we can't change it back to "root" when
+ * we need the privilege, for the same reason as in 1). Thus, we can't
+ * handle programs that are set-user-id to "root" at all. The program
+ * should be aborted. Use some other uid. "root" is probably to
+ * privileged for such things, anyway. (The uid is reverted to the
+ * real uid until abortion.)
+ *
+ * These two exceptions have the effect that the "root" uid will never
+ * be one of the two uids that are being switched between, which also
+ * means we don't have to check for such cases in the switching
+ * functions.
+ *
+ * Note that exception 1) is handled by these routines (by constantly
+ * running with uid "root", while exception 2) is a serious error, and
+ * is not provided for at all in the switching functions.
+ */
+ if (realuid == privuid)
+ privuid = (UID_T) -1; /* Not running set-user-id. */
+
+ /* If running set-gid, go down to real gid, otherwise remember that
+ * no privileged gid is available.
+ *
+ * There are no exception like there is for the user id, since there
+ * is no group id that is privileged in the manner of uid "root".
+ * There could be equivalent problems for group changing if the
+ * program sometimes ran with uid "root" and sometimes not, but
+ * that is already avoided as explained above.
+ *
+ * Thus we can expect always to be able to switch to the "saved set-
+ * gid" when we want, and back to the real gid again. You may also
+ * draw the conclusion that set-gid provides for fewer hassles than
+ * set-uid.
+ */
+
+ if (realgid == privgid) /* If not running set-user-id, */
+ privgid = (GID_T) -1; /* remember it this way. */
+
+ err = priv_off(); /* Turn off setuid privilege. */
+
+ if (privuid == UID_ROOT) /* If setuid to root, */
+ err |= 4; /* return this error. */
+
+ if (realuid == UID_ROOT) /* If real id is root, */
+ privuid = (UID_T) -1; /* stay root at all times. */
+
+ return(err);
+}
+
+
+/* Macros for hiding the differences in UID/GID setting between various Unix
+ * systems. These macros should always be called with both the privileged ID
+ * and the non-privileged ID. The one in the second argument, will become the
+ * effective ID. The one in the first argument will be retained for later
+ * retrieval.
+ */
+#ifdef SETREUID
+#ifdef SAVEDUID
+/* On BSD systems with the saved-UID feature, we just juggle the effective
+ * UID back and forth, and leave the real UID at its true value. The kernel
+ * allows switching to both the current real UID, the effective UID, and the
+ * UID which the program is set-UID to. The saved set-UID always holds the
+ * privileged UID for us, and the real UID will always be the non-privileged,
+ * and we can freely choose one of them for the effective UID at any time.
+ */
+#define switchuid(hidden,active) setreuid( (UID_T) -1, active)
+#define switchgid(hidden,active) setregid( (GID_T) -1, active)
+
+#else /* SETREUID,!SAVEDUID */
+
+/* On systems with setreXid() but without the saved-UID feature, notably
+ * BSD 4.2, we swap the real and effective UIDs each time. It's
+ * the effective UID that we are interrested in, but we have to retain the
+ * unused UID somewhere to enable us to restore it later, and that we do this
+ * in the real UID. The kernel only allows switching to either the current
+ * real or the effective UID, unless you're "root".
+ */
+#define switchuid(hidden,active) setreuid(hidden,active)
+#define switchgid(hidden,active) setregid(hidden,active)
+#endif
+
+#else /* !SETREUID, !SAVEDUID */
+
+/* On System V and POSIX, the only thing we can change is the effective UID
+ * (unless the current effective UID is "root", but initsuid() avoids that for
+ * us). The kernel allows switching to the current real UID or to the saved
+ * set-UID. These are always set to the non-privileged UID and the privileged
+ * UID, respectively, and we only change the effective UID. This breaks if
+ * the current effective UID is "root", though, because for "root" setuid/gid
+ * becomes more powerful, which is why initsuid() treats "root" specially.
+ * Note: That special treatment maybe could be ignored for BSD? Note: For
+ * systems that don't fit any of these three cases, we simply can't support
+ * set-UID.
+ */
+#define switchuid(hidden,active) setuid(active)
+#define switchgid(hidden,active) setgid(active)
+#endif /* SETREUID */
+
+
+/* P R I V _ O N -- Turn on the setuid and/or setgid */
+
+/* Go to the privileged uid (gid) that the program is set-user-id
+ * (set-group-id) to, unless the program is running unprivileged.
+ * If setuid() fails, return value will be 1. If getuid() fails it
+ * will be 2. Return immediately after first failure, and the function
+ * tries to restore any partial work done. Returns 0 on success.
+ * Group id is changed first, since it is less serious than user id.
+ */
+int
+priv_on() {
+ if (privgid != (GID_T) -1)
+ if (switchgid(realgid,privgid))
+ return(2);
+
+ if (privuid != (UID_T) -1)
+ if (switchuid(realuid,privuid)) {
+ if (privgid != (GID_T) -1)
+ switchgid(privgid,realgid);
+ return(1);
+ }
+ return(0);
+}
+
+/* P R I V _ O F F -- Turn on the real uid and gid */
+
+/* Return to the unprivileged uid (gid) after an temporary visit to
+ * privileged status, unless the program is running without set-user-id
+ * (set-group-id). Returns 1 for failure in setuid() and 2 for failure
+ * in setgid() or:ed together. The functions tries to return both uid
+ * and gid to unprivileged state, regardless of errors. Returns 0 on
+ * success.
+ */
+int
+priv_off() {
+ int err = 0;
+
+ if (privuid != (UID_T) -1)
+ if (switchuid(privuid,realuid))
+ err |= 1;
+
+ if (privgid != (GID_T) -1)
+ if (switchgid(privgid,realgid))
+ err |= 2;
+
+ return(err);
+}
+
+/* Turn off privilege permanently. No going back. This is necessary before
+ * a fork() on BSD43 machines that don't save the setUID or setGID, because
+ * we swap the real and effective ids, and we don't want to let the forked
+ * process swap them again and get the privilege back. It will work on other
+ * machines too, such that you can rely on its effect always being the same,
+ * for instance, even when you're in priv_on() state when this is called.
+ * (Well, that part about "permanent" is on System V only true if you follow
+ * this with a call to exec(), but that's what we want it for anyway.)
+ * Added by Dean Long -- dlong@midgard.ucsc.edu
+ */
+int
+priv_can() {
+
+#ifdef SETREUID
+ int err = 0;
+ if (privuid != (UID_T) -1)
+ if (setreuid(realuid,realuid))
+ err |= 1;
+
+ if (privgid != (GID_T) -1)
+ if (setregid(realgid,realgid))
+ err |= 2;
+
+ return(err);
+
+#else
+ /* Easy way of using setuid()/setgid() instead of setreuid()/setregid().*/
+ return(priv_off());
+
+#endif /* SETREUID */
+}
+
+/* P R I V _ O P N -- For opening protected files or devices. */
+
+int
+priv_opn(name, modes) char *name; int modes; {
+ int x;
+ priv_on(); /* Turn privileges on */
+ x = open(name, modes); /* Try to open the device */
+ priv_off(); /* Turn privileges off */
+ return(x); /* Return open's return code */
+}
+
+/* P R I V _ C H K -- Check privileges. */
+
+/* Try to turn them off. If turning them off did not succeed, cancel them */
+
+int
+priv_chk() {
+ int x, y = 0;
+ x = priv_off(); /* Turn off privs. */
+ if (x != 0 || getuid() == privuid || geteuid() == privuid)
+ y = priv_can();
+ if (x != 0 || getgid() == privgid || getegid() == privgid)
+ y = y | priv_can();
+ return(y);
+}
+
+UID_T
+real_uid() {
+ return(realuid);
+}
+
+VOID
+ttimoff() { /* Turn off any timer interrupts */
+ alarm(0);
+ if (saval)
+ signal(SIGALRM,saval);
+ else
+ signal(SIGALRM,SIG_DFL);
+ saval = NULL;
+}
+
+#ifdef UTEK
+/* Turn on or off hardware flow control under UTek */
+VOID
+hardflow(flow) int flow; {
+ int x;
+ x = LDODTR | LDOCTS;
+ if (flow == FLO_HARD) { /* Use hardware flow control */
+ if (lmodef) {
+ x = ioctl(ttyfd,TIOCLBIS,&x);
+ if (x < 0) {
+ debug(F100,"hardflow TIOCLBIS error","",0);
+ } else {
+ lmodef++;
+ debug(F100,"hardflow TIOCLBIS ok","",0);
+ }
+ }
+ } else {
+ if (lmodef) {
+ x = ioctl(ttyfd,TIOCLBIC,&x);
+ if (x < 0) {
+ debug(F100,"hardflow TIOCLBIC error","",0);
+ } else {
+ lmodef++;
+ debug(F100,"hardflow TIOCLBIC ok","",0);
+ }
+ }
+ }
+}
+#endif /* UTEK */