1
0
Files
irix-657m-src/eoe/cmd/cu/cu.c
2022-09-29 17:59:04 +03:00

1391 lines
34 KiB
C

/* Copyright (c) 1984 AT&T */
/* All Rights Reserved */
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
/* The copyright notice above does not evidence any */
/* actual or intended publication of such source code. */
#ident "$Revision: 1.19 $"
/********************************************************************
*cu [-sspeed] [-lline] [-h] [-t] [-d] [-n] [-o|-e] telno | systemname
*
* legal baud rates: 300, 1200, 2400, 4800, 9600.
*
* -l is for specifying a line unit from the file whose
* name is defined in /usr/lib/uucp/Devices.
* -h is for half-duplex (local echoing).
* -t is for adding CR to LF on output to remote (for terminals).
* -d can be used to get some tracing & diagnostics.
* -o or -e is for odd or even parity on transmission to remote.
* -n will request the phone number from the user.
* Telno is a telephone number with `=' for secondary dial-tone.
* If "-l dev" is used, speed is taken from /usr/lib/uucp/Devices.
* Only systemnames that are included in /usr/lib/uucp/Systems may
* be used.
*
* Escape with `~' at beginning of line.
* Silent output diversions are ~>:filename and ~>>:filename.
* Terminate output diversion with ~> alone.
* ~. is quit, and ~![cmd] gives local shell [or command].
* Also ~$ for local procedure with stdout to remote.
* Both ~%put from [to] and ~%take from [to] invoke built-ins.
* Also, ~%break or just ~%b will transmit a BREAK to remote.
* ~%nostop toggles on/off the DC3/DC1 input control from remote,
* (certain remote systems cannot cope with DC3 or DC1).
* ~%debug(~%b) toggles on/off the program tracing and diagnostics output;
* cu should no longer be compiled with #ifdef ddt.
*
* Cu no longer uses dial.c to reach the remote. Instead, cu places
* a telephone call to a remote system through the uucp conn() routine
* when the user picks the systemname option or through altconn()--
* which bypasses /usr/lib/uucp/Systems -- if a telno or direct
* line is chosen. The line termio attributes are set in fixline(),
* before the remote connection is made. As a device-lockout semaphore
* mechanism, uucp creates an entry in /var/spool/locks whose name is
* LCK..dev where dev is the device name from the Devices file.
* When cu terminates, for whatever reason, cleanup() must be
* called to "release" the device, and clean up entries from
* the locks directory. Cu runs with uucp ownership, and thus provides
* extra insurance that lock files will not be left around.
# ******************************************************************/
#include "uucp.h"
#include <string.h>
#include <sys/param.h>
#define MID BUFSIZ/2 /* mnemonic */
#define RUB '\177' /* mnemonic */
#define XON '\21' /* mnemonic */
#define XOFF '\23' /* mnemonic */
#define TTYIN 0 /* mnemonic */
#define TTYOUT 1 /* mnemonic */
#define TTYERR 2 /* mnemonic */
#define YES 1 /* mnemonic */
#define NO 0 /* mnemonic */
#define IOERR 4 /* exit code */
#define MAXPATH 100
#define NPL 50
int Sflag=0;
int Cn; /*fd for remote comm line */
static char *_Cnname; /*to save associated ttyname */
jmp_buf Sjbuf; /*needed by uucp routines*/
/* io buffering */
/* Wiobuf contains, in effect, 3 write buffers (to remote, to tty */
/* stdout, and to tty stderr) and Riobuf contains 2 read buffers */
/* (from remote, from tty). [WR]IOFD decides which one to use. */
/* [RW]iop holds current position in each. */
#define WIOFD(fd) (fd == TTYOUT ? 0 : (fd == Cn ? 1 : 2))
#define RIOFD(fd) (fd == TTYIN ? 0 : 1)
#define WRIOBSZ 256
static char Riobuf[2*WRIOBSZ];
static char Wiobuf[3*WRIOBSZ];
static int Riocnt[2]={0,0};
static char *Riop[2];
static char *Wiop[3];
extern int
errno, /* supplied by system interface */
optind; /* variable in getopt() */
extern char
*optarg;
static struct call { /*NOTE-also included in altconn.c--> */
/*any changes must be made in both places*/
char *speed; /* transmission baud rate */
char *line; /* device name for outgoing line */
char *telno; /* ptr to tel-no digit string */
char *class; /* call class */
}Cucall;
static int Saved_tty; /* was TCGETAW of _Tv0 successful? */
static struct termio _Tv, _Tv0; /* for saving, changing TTY atributes */
static struct termio _Lv; /* attributes for the line to remote */
static char
_Cxc, /* place into which we do character io*/
_Tintr, /* current input INTR */
_Tquit, /* current input QUIT */
_Terase, /* current input ERASE */
_Tkill, /* current input KILL */
_Teol, /* current secondary input EOL */
_Myeof, /* current input EOF */
_Tswtch; /* current input SWTCH */
int
Terminal=0, /* flag; remote is a terminal */
Oddflag = 0, /*flag- odd parity option*/
Evenflag = 0, /*flag- even parity option*/
Echoe, /* save users ECHOE bit */
Echok, /* save users ECHOK bit */
Child, /* pid for receive process */
Intrupt=NO, /* interrupt indicator */
Duplex=YES, /* Unix= full duplex=YES; half = NO */
Sstop=YES, /* NO means remote can't XON/XOFF */
Rtn_code=0, /* default return code */
Takeflag=NO, /* indicates a ~%take is in progress */
input_eof_file=0, /* if set, input side was not a tty, and got eof */
w_char(int), /* local io routine */
r_char(int); /* local io routine */
static void
_onintrpt(void), /* interrupt routines */
_rcvdead(int),
_quit(int),
_bye(int);
extern void tdmp(int);
static void
_flush(void),
_shell(char *),
_dopercen(char *),
_receive(void),
_mode(int),
_w_str(char *);
static void recfork(void);
static int dofork(void);
int transmit(void);
static int wioflsh(int);
static void blckcnt(long);
int tilda(char *);
extern int altconn(struct call *);
char *Myline = NULL; /* flag to force the requested line to be used */
/* by rddev() in uucp conn.c */
char *P_USAGE= "USAGE:%s [-s speed] [-l line] [-c class] [-h] [-n] [-t] [-d] [-o|-e] telno | systemname\n";
char *P_CON_FAILED = "Connect failed: %s\r\n";
char *P_Ct_OPEN = "Cannot open: %s\r\n";
char *P_LINE_GONE = "Remote line gone\r\n";
char *P_Ct_EXSH = "Can't execute shell\r\n";
char *P_Ct_DIVERT = "Can't divert %s\r\n";
char *P_STARTWITH = "Use `~~' to start line with `~'\r\n";
char *P_CNTAFTER = "after %ld bytes\r\n";
char *P_CNTLINES = "%d lines/";
char *P_CNTCHAR = "%ld characters\r\n";
char *P_FILEINTR = "File transmission interrupted\r\n";
char *P_Ct_FK = "Can't fork -- try later\r\n";
char *P_Ct_SPECIAL = "r\nCan't transmit special character `%#o'\r\n";
char *P_TOOLONG = "\nLine too long\r\n";
char *P_IOERR = "r\nIO error\r\n";
char *P_USECMD = "Use `~$'cmd \r\n";
char *P_USEPLUSCMD ="Use `~+'cmd \r\n";
char *P_NOTERMSTAT = "Can't get terminal status\r\n";
char *P_3BCONSOLE = "Sorry, you can't cu from a 3B console\r\n";
char *P_PARITY = "Parity option error\r\n";
char *P_TELLENGTH = "Telno cannot exceed 58 digits!\r\n";
/***************************************************************
* main: get command line args, establish connection, and fork.
* Child invokes "receive" to read from remote & write to TTY.
* Main line invokes "transmit" to read TTY & write to remote.
***************************************************************/
void
main(argc, argv)
char *argv[];
{
char s[MAXPH];
char *string;
char *p;
int i;
int errflag=0;
int lflag=0;
int nflag=0;
int systemname = 0;
Riop[0] = &Riobuf[0];
Riop[1] = &Riobuf[WRIOBSZ];
Wiop[0] = &Wiobuf[0];
Wiop[1] = &Wiobuf[WRIOBSZ];
Wiop[2] = &Wiobuf[2*WRIOBSZ];
Verbose = 1; /*for uucp callers, dialers feedback*/
strcpy(Progname,"cu");
setservice(Progname);
if ( sysaccess(EACCESS_SYSTEMS) != 0 ) {
(void)fprintf(stderr, "cu: cannot read Systems files\n");
exit(1);
}
if ( sysaccess(EACCESS_DEVICES) != 0 ) {
(void)fprintf(stderr, "cu: cannot read Devices files\n");
exit(1);
}
if ( sysaccess(EACCESS_DIALERS) != 0 ) {
(void)fprintf(stderr, "cu: cannot read Dialers files\n");
exit(1);
}
Cucall.speed = "Any"; /*default speed*/
Cucall.line = NULL;
Cucall.telno = NULL;
Cucall.class = NULL;
/*Flags for -h, -t, -e, and -o options set here; corresponding line attributes*/
/*are set in fixline() in culine.c before remote connection is made */
while((i = getopt(argc, argv, "dhteons:l:c:")) != EOF)
switch(i) {
case 'd':
Debug = 9; /*turns on uucp debugging-level 9*/
break;
case 'h':
Duplex = NO;
Sstop = NO;
break;
case 't':
Terminal = YES;
break;
case 'e':
if ( Oddflag ) {
(void)fprintf(stderr, "%s: cannot have both even and odd parity\n", argv[0]);
exit(1);
}
Evenflag = 1;
break;
case 'o':
if ( Evenflag ) {
(void)fprintf(stderr, "%s: cannot have both even and odd parity\n", argv[0]);
exit(1);
}
Oddflag = 1;
break;
case 's':
Sflag++;
Cucall.speed = optarg;
break;
case 'l':
lflag++;
Cucall.line = optarg;
break;
/* -c specifies the class-selecting an entry type from the Devices file */
case 'c':
Cucall.class = optarg;
break;
case 'n':
nflag++;
printf("Please enter the number: ");
fgets(s, MAXPH, stdin);
break;
case '?':
++errflag;
}
#ifdef u3b
if(fstat(TTYIN, &buff) < 0) {
VERBOSE(P_NOTERMSTAT,"");
exit(1);
} else if ( (buff.st_mode & S_IFMT) == S_IFCHR && buff.st_rdev == 0 ) {
VERBOSE(P_3BCONSOLE,"");
exit(1);
}
#endif
if((optind < argc && optind > 0) || (nflag && optind > 0)) {
if(nflag)
string=s;
else
string = argv[optind];
if(p = strchr(string, '\n'))
*p = '\0';
if((strlen(string) == strspn(string, "0123456789=-*#")) ||
(Cucall.class != NULL))
Cucall.telno = string;
else { /*if it's not a legitimate telno, */
/*then it should be a systemname */
if ( nflag ) {
(void)fprintf(stderr,
"%s: bad phone number %s\nPhone numbers may contain only the digits 0 through 9 and the special\ncharacters =, -, * and #.\n",
argv[0], string );
exit(1);
}
systemname++;
}
} else
if(Cucall.line == NULL) /*if none of above, must be direct */
++errflag;
if(errflag) {
VERBOSE(P_USAGE, argv[0]);
exit(1);
}
if(Cucall.telno != NULL && strlen(Cucall.telno) >= (MAXPH - 1)) {
VERBOSE(P_TELLENGTH,"");
exit(0);
}
/* save initial tty state */
Saved_tty = ( ioctl(TTYIN, TCGETA, &_Tv0) == 0 );
_Tintr = _Tv0.c_cc[VINTR]? _Tv0.c_cc[VINTR]: '\377';
_Tquit = _Tv0.c_cc[VQUIT]? _Tv0.c_cc[VQUIT]: '\377';
_Terase = _Tv0.c_cc[VERASE]? _Tv0.c_cc[VERASE]: '\377';
_Tkill = _Tv0.c_cc[VKILL]? _Tv0.c_cc[VKILL]: '\377';
_Teol = _Tv0.c_cc[VEOL]? _Tv0.c_cc[VEOL]: '\377';
_Myeof = _Tv0.c_cc[VEOF]? _Tv0.c_cc[VEOF]: '\04';
/* Make sure the line discipline supports job control */
_Tswtch = (_Tv0.c_line == LDISC1) ? _Tv0.c_cc[VSUSP] : CNSWTCH;
Echoe = (_Tv0.c_lflag & ECHOE) != 0;
Echok = (_Tv0.c_lflag & ECHOK) != 0;
(void)signal(SIGHUP, cleanup);
(void)signal(SIGQUIT, cleanup);
(void)signal(SIGINT, cleanup);
/* place call to system; if "cu systemname", use conn() from uucp
directly. Otherwise, use altconn() which dummies in the
Systems file line.
*/
if(systemname) {
if ( lflag )
(void)fprintf(stderr,
"%s: warning: -l flag ignored when system name used\n",
argv[0]);
if ( Sflag )
(void)fprintf(stderr,
"%s: warning: -s flag ignored when system name used\n",
argv[0]);
Cn = conn(string);
} else
Cn = altconn(&Cucall);
_Cnname = ttyname(Cn);
if(_Cnname != NULL) {
struct stat Cnsbuf;
if ( fstat(Cn, &Cnsbuf) == 0 )
Dev_mode = Cnsbuf.st_mode;
else
Dev_mode = R_DEVICEMODE;
chmod(_Cnname, M_DEVICEMODE);
}
Euid = geteuid();
if(setuid(getuid()) && setgid(getgid()) < 0) {
VERBOSE("Unable to setuid/gid\n","");
cleanup(-1);
}
if(Cn < 0) {
VERBOSE(P_CON_FAILED,UERRORTEXT);
cleanup(-Cn);
}
if(Debug) tdmp(Cn);
/* At this point succeeded in getting an open communication line */
/* Conn() takes care of closing the Systems file */
(void)signal(SIGINT,_onintrpt);
_mode(1); /* put terminal in `raw' mode */
VERBOSE("Connected\007\r\n",""); /*bell!*/
/* must catch signals before fork. if not and if _receive() */
/* fails in just the right (wrong?) way, _rcvdead() can be */
/* called and do "kill(getppid(),SIGUSR1);" before parent */
/* has done calls to signal() after recfork(). */
(void)signal(SIGUSR1, _bye);
(void)signal(SIGHUP, cleanup);
(void)signal(SIGQUIT, _onintrpt);
recfork(); /* checks for child == 0 */
if(Child > 0) {
Rtn_code = transmit();
_quit(Rtn_code);
} else {
cleanup(Cn);
}
}
/*
* Kill the present child, if it exists, then fork a new one.
*/
static void
recfork(void)
{
if (Child)
kill(Child, SIGKILL);
Child = dofork();
if(Child == 0) {
(void)signal(SIGUSR1, SIG_DFL);
(void)signal(SIGHUP, _rcvdead);
(void)signal(SIGQUIT, SIG_IGN);
(void)signal(SIGINT, SIG_IGN);
_receive(); /* This should run until killed */
/*NOTREACHED*/
}
}
/***************************************************************
* transmit: copy stdin to remote fd, except:
* ~. terminate
* ~! local login-style shell
* ~!cmd execute cmd locally
* ~$proc execute proc locally, send output to line
* ~%cmd execute builtin cmd (put, take, or break)
****************************************************************/
#ifdef forfutureuse
/*****************************************************************
* ~+proc execute locally, with stdout to and stdin from line.
******************************************************************/
#endif
int
transmit(void)
{
char b[BUFSIZ];
char prompt[MAXHOSTNAMELEN];
register char *p;
register int escape;
register int id = 0; /*flag for systemname prompt on tilda escape*/
CDEBUG(4,"transmit started\n\r","");
if (gethostname(prompt, sizeof prompt) < 0) {
strcpy(prompt, "Local");
}
/* In main loop, always waiting to read characters from */
/* keyboard; writes characters to remote, or to TTYOUT */
/* on a tilda escape */
while(TRUE && !input_eof_file) {
p = b;
while(r_char(TTYIN) == YES) {
if(p == b) /* Escape on leading ~ */
escape = (_Cxc == '~');
if(p == b+1) /* But not on leading ~~ */
escape &= (_Cxc != '~');
if(escape) {
if(_Cxc == '\n' || _Cxc == '\r' || _Cxc == _Teol) {
*p = '\0';
if(tilda(b+1) == YES)
return(0);
id = 0;
break;
}
if(_Cxc == _Tintr || _Cxc == _Tkill || _Cxc
== _Tquit ||
(Intrupt && _Cxc == '\0')) {
if(_Cxc == _Tkill) {
if(Echok)
VERBOSE("\r\n","");
}
else {
_Cxc = '\r';
if( w_char(Cn) == NO) {
VERBOSE(P_LINE_GONE,"");
return(IOERR);
}
id=0;
}
break;
}
if((p == b+1) && (_Cxc != _Terase) && (!id)) {
id = 1;
VERBOSE("[%s]", prompt);
}
if(_Cxc == _Terase) {
p = (--p < b)? b:p;
if(p > b)
if(Echoe)
VERBOSE("\b \b", "");
else
(void)w_char(TTYOUT);
} else {
(void)w_char(TTYOUT);
if(p-b < BUFSIZ)
*p++ = _Cxc;
else {
VERBOSE(P_TOOLONG,"");
break;
}
}
/*not a tilda escape command*/
} else {
if(Intrupt && _Cxc == '\0') {
CDEBUG(4,"got break in transmit\n\r","");
Intrupt = NO;
(*genbrk)(Cn);
_flush();
break;
}
if(w_char(Cn) == NO) {
VERBOSE(P_LINE_GONE,"");
return(IOERR);
}
if(Duplex == NO) {
if((w_char(TTYERR) == NO) ||
(wioflsh(TTYERR) == NO))
return(IOERR);
}
if ((_Cxc == _Tintr) || (_Cxc == _Tquit) ||
( (p==b) && (_Cxc == _Myeof) ) ) {
CDEBUG(4,"got a tintr\n\r","");
_flush();
break;
}
if(_Cxc == '\n' || _Cxc == '\r' ||
_Cxc == _Teol || _Cxc == _Tkill) {
id=0;
Takeflag = NO;
break;
}
p = (char*)0;
}
}
}
/* The nap is because some people may have counted on the old
* behavior of cu looping 'forever' on reaching end of file when
* stdin is a script, to allow the remote side to have seen the
* earlier stuff passed from a script, and do something about it,
* before the line drops. 15 seconds seems reasonable. */
sginap(15*HZ);
CDEBUG(0,"return from transmit after eof from non-tty input\r\n", 0);
return 0; /* must have gotten EOF and not from a tty */
}
/***************************************************************
* routine to halt input from remote and flush buffers
***************************************************************/
static void
_flush(void)
{
(void)ioctl(TTYOUT, TCXONC, 0); /* stop tty output */
(void)ioctl(Cn, TCFLSH, 0); /* flush remote input */
(void)ioctl(TTYOUT, TCFLSH, 1); /* flush tty output */
(void)ioctl(TTYOUT, TCXONC, 1); /* restart tty output */
if(Takeflag == NO) {
return; /* didn't interupt file transmission */
}
VERBOSE(P_FILEINTR,"");
(void)sleep(3);
_w_str("echo '\n~>\n';mesg y;stty echo\n");
Takeflag = NO;
}
/**************************************************************
* command interpreter for escape lines
**************************************************************/
int
tilda(char *cmd)
{
VERBOSE("\r\n","");
CDEBUG(4,"call tilda(%s)\r\n", cmd);
switch(cmd[0]) {
case '.':
if(Cucall.telno == NULL)
if(cmd[1] != '.') {
_w_str("\04\04\04\04\04");
if (Child)
kill(Child, SIGKILL);
(void) ioctl (Cn, TCGETA, &_Lv);
/* speed to zero for hangup */
_Lv.c_ospeed = 0;
(void) ioctl (Cn, TCSETAW, &_Lv);
(void) sleep (2);
}
return(YES);
case '!':
_shell(cmd); /* local shell */
VERBOSE("\r%c\r\n", *cmd);
VERBOSE("(continue)","");
break;
case '$':
if(cmd[1] == '\0') {
VERBOSE(P_USECMD,"");
VERBOSE("(continue)","");
}
else {
_shell(cmd); /*Local shell */
VERBOSE("\r%c\r\n", *cmd);
}
break;
#ifdef forfutureuse
case '+':
if(cmd[1] == '\0') {
VERBOSE(P_USEPLUSCMD, "");
VERBOSE("(continue)","");
}
else {
if (*cmd == '+')
/* must suspend receive to give*/
/*remote out to stdin of cmd */
kill(Child, SIGKILL);
_shell(cmd); /* Local shell */
if (*cmd == '+')
recfork();
VERBOSE("\r%c\r\n", *cmd);
}
break;
#endif
case '%':
_dopercen(++cmd);
break;
case 't':
tdmp(TTYIN);
VERBOSE("(continue)","");
break;
case 'l':
tdmp(Cn);
VERBOSE("(continue)","");
break;
default:
if (_Tswtch != CNSWTCH && cmd[0] == _Tswtch) {
_mode(0);
kill(0, SIGTSTP);
_mode(1);
VERBOSE("(continue)","");
break;
}
VERBOSE(P_STARTWITH,"");
VERBOSE("(continue)","");
break;
}
return(NO);
}
/***************************************************************
* The routine "shell" takes an argument starting with
* either "!" or "$", and terminated with '\0'.
* If $arg, arg is the name of a local shell file which
* is executed and its output is passed to the remote.
* If !arg, we escape to a local shell to execute arg
* with output to TTY, and if arg is null, escape to
* a local shell and blind the remote line. In either
* case, '^D' will kill the escape status.
**************************************************************/
#ifdef forfutureuse
/***************************************************************
* Another argument to the routine "shell" may be +. If +arg,
* arg is the name of a local shell file which is executed with
* stdin from and stdout to the remote.
**************************************************************/
#endif
static void
_shell(char *str)
{
int fk;
void (*xx)(), (*yy)();
CDEBUG(4,"call _shell(%s)\r\n", str);
fk = dofork();
if(fk < 0)
return;
_mode(0); /* restore normal tty attributes */
xx = signal(SIGINT, SIG_IGN);
yy = signal(SIGQUIT, SIG_IGN);
if(fk == 0) {
char *shell;
shell = getenv("SHELL");
if(shell == NULL)
shell = "/bin/sh";
/*user's shell is set if*/
/*different from default*/
(void)close(TTYOUT);
/***********************************************
* Hook-up our "standard output"
* to either the tty for '!' or the line
* for '$' as appropriate
***********************************************/
#ifdef forfutureuse
/************************************************
* Or to the line for '+'.
**********************************************/
#endif
(void)fcntl((*str == '!')? TTYERR:Cn,F_DUPFD,TTYOUT);
#ifdef forfutureuse
/*************************************************
* Hook-up "standard input" to the line for '+'.
* **********************************************/
if (*str == '+')
{
(void)close(TTYIN);
(void)fcntl(Cn,F_DUPFD,TTYIN);
}
#endif
/***********************************************
* Hook-up our "standard input"
* to the tty for '!' and '$'.
***********************************************/
(void)close(Cn); /*parent still has Cn*/
(void)signal(SIGINT, SIG_DFL);
(void)signal(SIGHUP, SIG_DFL);
(void)signal(SIGQUIT, SIG_DFL);
(void)signal(SIGUSR1, SIG_DFL);
if(*++str == '\0')
(void)execl(shell,shell,(char*)0,(char*)0,0);
else
(void)execl(shell,"sh","-c",str,0);
VERBOSE(P_Ct_EXSH,"");
exit(0);
}
while(wait((int*)0) != fk);
(void)signal(SIGINT, xx);
(void)signal(SIGQUIT, yy);
_mode(1);
}
/***************************************************************
* This function implements the 'put', 'take', 'break', and
* 'nostop' commands which are internal to cu.
***************************************************************/
static void
_dopercen(char *cmd)
{
char *arg[5];
char *getpath;
char mypath[MAXPATH];
int narg;
blckcnt((long)(-1));
CDEBUG(4,"call _dopercen(\"%s\")\r\n", cmd);
arg[narg=0] = strtok(cmd, " \t\n");
/* following loop breaks out the command and args */
while((arg[++narg] = strtok((char*) NULL, " \t\n")) != NULL) {
if(narg < 4)
continue;
else
break;
}
if (arg[0] == NULL) {
VERBOSE("(continue)","");
return;
}
/* ~%take file option */
if(EQUALS(arg[0], "take")) {
if(narg < 2 || narg > 3) {
VERBOSE("usage: ~%%take from [to]\r\n","");
VERBOSE("(continue)","");
return;
}
if(narg == 2)
arg[2] = arg[1];
/*
* be sure that the remote file (arg[1]) exists before
* you try to take it. otherwise, the error message from
* cat will wind up in the local file (arg[2])
*
* what we're doing is:
* stty -echo; \
* if test -r arg1
* then (echo '~'>:arg2; cat arg1; echo '~'>)
* else echo can't open: arg1
* fi; \
* stty echo
*
*/
_w_str("stty -echo;if test -r ");
_w_str(arg[1]);
_w_str("; then (echo '~>':");
_w_str(arg[2]);
_w_str(";cat ");
_w_str(arg[1]);
_w_str(";echo '~>'); else echo cant\\'t open: ");
_w_str(arg[1]);
_w_str("; fi;stty echo\n");
Takeflag = YES;
return;
}
/* ~%put file option*/
if(EQUALS(arg[0], "put")) {
FILE *file;
char ch, buf[BUFSIZ], spec[NCC+1], *b, *p, *q;
int i, j, len, tc=0, lines=0;
long chars=0L;
if(narg < 2 || narg > 3) {
VERBOSE("usage: ~%%put from [to]\r\n","");
VERBOSE("(continue)","");
return;
}
if(narg == 2)
arg[2] = arg[1];
if((file = fopen(arg[1], "r")) == NULL) {
VERBOSE(P_Ct_OPEN, arg[1]);
VERBOSE("(continue)","");
return;
}
/*
* if cannot write into file on remote machine, write into
* /dev/null
*
* what we're doing is:
* stty -echo
* (cat - > arg2) || cat - > /dev/null
* stty echo
*/
_w_str("stty -echo;(cat - >");
_w_str(arg[2]);
_w_str(")||cat - >/dev/null;stty echo\n");
Intrupt = NO;
for(i=0,j=0; i < NCC; ++i)
if((ch=_Tv0.c_cc[i]) != '\0')
spec[j++] = ch;
spec[j] = '\0';
_mode(2); /*accept interrupts from keyboard*/
(void)sleep(5); /*hope that w_str info digested*/
/* Read characters line by line into buf to write to remote with character*/
/*and line count for blckcnt */
while(Intrupt == NO &&
fgets(b= &buf[MID],MID,file) != NULL) {
/*worse case= each*/
/*char must be escaped*/
len = strlen(b);
chars += len; /* character count */
p = b;
while(q = strpbrk(p, spec)) {
if(*q == _Tintr || *q == _Tquit ||
(*q == _Teol && _Teol != '\n')) {
VERBOSE(P_Ct_SPECIAL, *q);
(void)strcpy(q, q+1);
Intrupt = YES;
}
else {
b = strncpy(b-1, b, q-b);
*(q-1) = '\\';
}
p = q+1;
}
if((tc += len) >= MID) {
(void)sleep(1);
tc = len;
}
if(write(Cn, b, (unsigned)strlen(b)) < 0) {
VERBOSE(P_IOERR,"");
Intrupt = YES;
break;
}
++lines; /* line count */
blckcnt((long)chars);
}
_mode(1);
blckcnt((long)(-2)); /* close */
(void)fclose(file);
if(Intrupt == YES) {
Intrupt = NO;
VERBOSE(P_FILEINTR,"");
_w_str("\n");
VERBOSE(P_CNTAFTER, ++chars);
} else
VERBOSE(P_CNTLINES, lines);
VERBOSE(P_CNTCHAR,chars);
_w_str("\04");
(void)sleep(3);
return;
}
/* ~%b or ~%break */
if(EQUALS(arg[0], "b") || EQUALS(arg[0], "break")) {
(*genbrk)(Cn);
return;
}
/* ~%d or ~%debug toggle */
if(EQUALS(arg[0], "d") || EQUALS(arg[0], "debug")) {
if(Debug == 0)
Debug = 9;
else
Debug = 0;
VERBOSE("(continue)","");
return;
}
/* ~%nostop toggles start/stop input control */
if(EQUALS(arg[0], "nostop")) {
(void)ioctl(Cn, TCGETA, &_Tv);
if(Sstop == NO)
_Tv.c_iflag |= IXOFF;
else
_Tv.c_iflag &= ~IXOFF;
(void)ioctl(Cn, TCSETAW, &_Tv);
Sstop = !Sstop;
_mode(1);
VERBOSE("(continue)","");
return;
}
/* Change local current directory */
if(EQUALS(arg[0], "cd")) {
if (narg < 2) {
getpath = getenv("HOME");
strcpy(mypath, getpath);
if(chdir(mypath) < 0) {
VERBOSE("Cannot change to %s\r\n", mypath);
VERBOSE("(continue)","");
return;
}
}
else if (chdir(arg[1]) < 0) {
VERBOSE("Cannot change to %s\r\n", arg[1]);
VERBOSE("(continue)","");
return;
}
recfork(); /* fork a new child so it know about change */
VERBOSE("(continue)","");
return;
}
VERBOSE("~%%%s unknown to cu\r\n", arg[0]);
VERBOSE("(continue)","");
}
/***************************************************************
* receive: read from remote line, write to fd=1 (TTYOUT)
* catch:
* ~>[>]:file
* .
* . stuff for file
* .
* ~> (ends diversion)
***************************************************************/
static void
_receive(void)
{
register silent=NO, file;
register char *p;
int tic;
char b[BUFSIZ];
long count;
CDEBUG(4,"_receive started\r\n","");
b[0] = '\0';
file = -1;
p = b;
while(r_char(Cn) == YES) {
if(silent == NO) /* ie., if not redirecting to file*/
if(w_char(TTYOUT) == NO)
_rcvdead(IOERR); /* this will exit */
/* remove CR's and fill inserted by remote */
if(_Cxc == '\0' || _Cxc == RUB || _Cxc == '\r')
continue;
*p++ = _Cxc;
if(_Cxc != '\n' && (p-b) < BUFSIZ)
continue;
/***********************************************
* The rest of this code is to deal with what
* happens at the beginning, middle or end of
* a diversion to a file.
************************************************/
if(b[0] == '~' && b[1] == '>') {
/****************************************
* The line is the beginning or
* end of a diversion to a file.
****************************************/
if((file < 0) && (b[2] == ':' || b[2] == '>')) {
/**********************************
* Beginning of a diversion
*********************************/
int append;
*(p-1) = NULL; /* terminate file name */
append = (b[2] == '>')? 1:0;
p = b + 3 + append;
if(append && (file=open(p,O_WRONLY))>0)
(void)lseek(file, 0L, 2);
else
file = creat(p, 0666);
if(file < 0) {
VERBOSE(P_Ct_DIVERT, p);
perror("cu: open|creat failed");
(void)sleep(5); /* 10 seemed too long*/
} else {
silent = YES;
count = tic = 0;
}
} else {
/*******************************
* End of a diversion (or queer data)
*******************************/
if(b[2] != '\n')
goto D; /* queer data */
if(silent = close(file)) {
VERBOSE(P_Ct_DIVERT, b);
perror("cu: close failed");
silent = NO;
}
blckcnt((long)(-2));
VERBOSE("~>\r\n","");
VERBOSE(P_CNTLINES, tic);
VERBOSE(P_CNTCHAR, count);
file = -1;
}
} else {
/***************************************
* This line is not an escape line.
* Either no diversion; or else yes, and
* we've got to divert the line to the file.
***************************************/
D:
if(file > 0) {
(void)write(file, b, (unsigned)(p-b));
count += p-b; /* tally char count */
++tic; /* tally lines */
blckcnt((long)count);
}
}
p = b;
}
VERBOSE("\r\nLost Carrier\r\n","");
_rcvdead(IOERR);
}
/***************************************************************
* change the TTY attributes of the users terminal:
* 0 means restore attributes to pre-cu status.
* 1 means set `raw' mode for use during cu session.
* 2 means like 1 but accept interrupts from the keyboard.
***************************************************************/
static void
_mode(int arg)
{
CDEBUG(4,"call _mode(%d)\r\n", arg);
if(arg == 0) {
if ( Saved_tty )
(void)ioctl(TTYIN, TCSETAW, &_Tv0);
} else {
(void)ioctl(TTYIN, TCGETA, &_Tv);
if(arg == 1) {
_Tv.c_iflag &= ~(INLCR | ICRNL | IGNCR |
IXOFF | IUCLC);
_Tv.c_iflag |= ISTRIP;
_Tv.c_oflag |= OPOST;
_Tv.c_oflag &= ~(OLCUC | ONLCR | OCRNL | ONOCR
| ONLRET);
_Tv.c_lflag &= ~(ICANON | ISIG | ECHO);
if(Sstop == NO)
_Tv.c_iflag &= ~IXON;
else
_Tv.c_iflag |= IXON;
if(Terminal) {
_Tv.c_oflag |= ONLCR;
_Tv.c_iflag |= ICRNL;
}
_Tv.c_cc[VEOF] = '\01';
_Tv.c_cc[VEOL] = '\0';
}
if(arg == 2) {
_Tv.c_iflag |= IXON;
_Tv.c_lflag |= ISIG;
}
(void)ioctl(TTYIN, TCSETAW, &_Tv);
}
}
static int
dofork(void)
{
register int x,i;
for(i = 0; i < 6; ++i) {
if((x = fork()) >= 0) {
return(x);
}
}
if(Debug) perror("dofork");
VERBOSE(P_Ct_FK,"");
return(x);
}
int
r_char(int fd)
{
int rtn = 1, rfd;
char *riobuf;
/* find starting pos in correct buffer in Riobuf */
rfd = RIOFD(fd);
riobuf = &Riobuf[rfd*WRIOBSZ];
if (Riop[rfd] >= &riobuf[Riocnt[rfd]]) {
/* empty read buffer - refill it */
/* flush any waiting output */
if ( (wioflsh(Cn) == NO ) || (wioflsh(TTYOUT) == NO) )
return(NO);
while((rtn = read(fd, riobuf, WRIOBSZ)) < 0){
if(errno == EINTR) {
/* onintrpt() called asynchronously before this line */
if(Intrupt == YES) {
/* got a BREAK */
_Cxc = '\0';
return(YES);
} else {
/*a signal other than interrupt*/
/*received during read*/
continue;
}
} else {
CDEBUG(4,"got read error, not EINTR\n\r","");
break; /* something wrong */
}
}
if (rtn > 0) {
/* reset current position in buffer */
/* and count of available chars */
Riop[rfd] = riobuf;
Riocnt[rfd] = rtn;
}
else if(!isatty(rfd)) {
/* We hit end of file, and input was from a pipe or file. cu
* used to loop 'forever' in this case, until the line dropped.
* and if it never did for some reason, we'd just eat up the
* whole cpu... */
input_eof_file = 1;
return NO;
}
}
if ( rtn > 0 ) {
_Cxc = *(Riop[rfd]++) & 0177; /*must mask off parity bit*/
return(YES);
} else {
_Cxc = '\0';
return(NO);
}
}
int
w_char(int fd)
{
int wfd;
char *wiobuf;
/* find starting pos in correct buffer in Wiobuf */
wfd = WIOFD(fd);
wiobuf = &Wiobuf[wfd*WRIOBSZ];
if (Wiop[wfd] >= &wiobuf[WRIOBSZ]) {
/* full output buffer - flush it */
if ( wioflsh(fd) == NO )
return(NO);
}
*(Wiop[wfd]++) = _Cxc;
return(YES);
}
/* wioflsh flush output buffer */
static int
wioflsh(int fd)
{
int wfd;
char *wiobuf;
/* find starting pos in correct buffer in Wiobuf */
wfd = WIOFD(fd);
wiobuf = &Wiobuf[wfd*WRIOBSZ];
if (Wiop[wfd] > wiobuf) {
/* there's something in the buffer */
while(write(fd, wiobuf, (Wiop[wfd] - wiobuf)) < 0) {
if(errno == EINTR)
if(Intrupt == YES) {
VERBOSE("\ncu: Output blocked\r\n","");
_quit(IOERR);
} else
continue; /* alarm went off */
else {
Wiop[wfd] = wiobuf;
return(NO); /* bad news */
}
}
}
Wiop[wfd] = wiobuf;
return(YES);
}
static void
_w_str(char *string)
{
int len;
len = strlen(string);
if ( write(Cn, string, (unsigned)len) != len )
VERBOSE(P_LINE_GONE,"");
}
static void
_onintrpt(void)
{
(void)signal(SIGINT, _onintrpt);
(void)signal(SIGQUIT, _onintrpt);
Intrupt = YES;
}
static void
_rcvdead(int arg) /* this is executed only in the receive process */
{
CDEBUG(4,"call _rcvdead(%d)\r\n", arg);
(void)kill(getppid(), SIGUSR1);
exit((arg == SIGHUP)? SIGHUP: arg);
/*NOTREACHED*/
}
static void
_quit(int arg) /* this is executed only in the parent process */
{
CDEBUG(4,"call _quit(%d)\r\n", arg);
(void)kill(Child, SIGKILL);
_bye(arg);
/*NOTREACHED*/
}
static void
_bye(int arg) /* this is executed only in the parent proccess */
{
int status;
CDEBUG(4,"call _bye(%d)\r\n", arg);
(void)signal(SIGINT, SIG_IGN);
(void)signal(SIGQUIT, SIG_IGN);
(void)wait(&status);
VERBOSE("\r\nDisconnected\007\r\n","");
cleanup((arg == SIGUSR1)? (status >>= 8): arg);
/*NOTREACHED*/
}
void /*this is executed only in the parent process*/
cleanup(int code) /*Closes device; removes lock files */
{
CDEBUG(4,"call cleanup(%d)\r\n", code);
(void) setuid(Euid);
if(Cn > 0) {
chmod(_Cnname, Dev_mode);
(void)close(Cn);
}
rmlock((char*) NULL); /*uucp routine in ulockf.c*/
_mode(0); /*which removes lock files*/
exit(code); /* code=negative for signal causing disconnect*/
}
void
tdmp(int arg)
{
struct termio xv;
int i;
VERBOSE("\rdevice status for fd=%d\n", arg);
VERBOSE("F_GETFL=%o,", fcntl(arg, F_GETFL,1));
if(ioctl(arg, TCGETA, &xv) < 0) {
char buf[100];
i = errno;
(void)sprintf(buf, "\rtdmp for fd=%d", arg);
errno = i;
perror(buf);
return;
}
VERBOSE("iflag=`%o',", xv.c_iflag);
VERBOSE("oflag=`%o',", xv.c_oflag);
VERBOSE("cflag=`%o',", xv.c_cflag);
VERBOSE("lflag=`%o',", xv.c_lflag);
VERBOSE("ospeed=`%d',", xv.c_ospeed);
VERBOSE("line=`%o'\r\n", xv.c_line);
VERBOSE("cc[0]=`%o',", xv.c_cc[0]);
for(i=1; i<8; ++i) {
VERBOSE("[%d]=", i);
VERBOSE("`%o' , ",xv.c_cc[i]);
}
VERBOSE("\r\n","");
}
static void
blckcnt(long count)
{
static long lcharcnt = 0;
register long c1, c2;
register int i;
char c;
if(count == (long) (-1)) { /* initialization call */
lcharcnt = 0;
return;
}
c1 = lcharcnt/BUFSIZ;
if(count != (long)(-2)) { /* regular call */
c2 = count/BUFSIZ;
for(i = c1; i++ < c2;) {
c = '0' + i%10;
write(2, &c, 1);
if(i%NPL == 0)
write(2, "\n\r", 2);
}
lcharcnt = count;
}
else {
c2 = (lcharcnt + BUFSIZ -1)/BUFSIZ;
if(c1 != c2)
write(2, "+\n\r", 3);
else if(c2%NPL != 0)
write(2, "\n\r", 2);
lcharcnt = 0;
}
}
/* ARGSUSED */
void assert(char *s1, char *s2, int i1, char *file, int line){
/* for ASSERT in gnamef.c */
}
/* ARGSUSED */
void logent(char *text, char *status){} /* so we can load ulockf() */