Home | History | Annotate | Download | only in bnu
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
     28 /*	  All Rights Reserved  	*/
     29 
     30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     31 
     32 
     33 /*
     34  * cu [-cdevice] [-sspeed] [-lline] [-bbits] [-h] [-t] [-d] [-n]
     35  *		[-o|-e] [-L] [-C] telno | systemname [local-cmd]
     36  *
     37  *	legal baud rates: 300, 1200, 2400, 4800, 9600, 19200, 38400.
     38  *
     39  *	-c is used to specify which device will be used for making the
     40  *		call.  The device argument is compared to the Type (first)
     41  *		field in the Devices file, and only those records that
     42  *		match will be used to make the call.  Either -d or -t
     43  *		would be more intuitive options designations, but they
     44  *		are already in use.
     45  *	-l is for specifying a line unit from the file whose
     46  *		name is defined in /etc/uucp/Devices.
     47  *	-b is for forcing the number of bits per character processed on
     48  *		the connection. Valid values are '7' or '8'.
     49  *	-h is for half-duplex (local echoing).
     50  *	-t is for adding CR to LF on output to remote (for terminals).
     51  *	-d can be used  to get some tracing & diagnostics.
     52  *	-o or -e is for odd or even parity on transmission to remote.
     53  *	-n will request the phone number from the user.
     54  *	-L will cause cu to go through the login chat sequence in the
     55  *		Systems file.
     56  *	-C will cause cu to run the local command specified at the end
     57  *		of the command line, instead of entering interactive mode.
     58  *	Telno is a telephone number with `=' for secondary dial-tone.
     59  *	If "-l dev" is used, speed is taken from /etc/uucp/Devices.
     60  *	Only systemnames that are included in /etc/uucp/Systems may
     61  *	be used.
     62  *
     63  *	Escape with `~' at beginning of line:
     64  *
     65  *	~.	quit,
     66  *
     67  *	~![cmd]			execute shell (or 'cmd') locally,
     68  *
     69  *	~$cmd			execute 'cmd' locally, stdout to remote,
     70  *
     71  *	~%break	(alias ~%b)	transmit BREAK to remote,
     72  *	~%cd [dir]		change directory to $HOME (or 'dir'),
     73  *	~%debug (alias ~%d)	toggles on/off the program debug trace,
     74  *	~%divert		allow unsolicited diversions to files,
     75  *	~%ifc (alias ~%nostop)	toggles on/off the DC3/DC1 input control,
     76  *	~%ofc (alias ~%noostop)	toggles on/off the DC3/DC1 output control,
     77  *		(certain remote systems cannot cope with DC3 or DC1).
     78  *	~%old			recognize old style silent diversions,
     79  *	~%put from [to]		put file from local to remote,
     80  *	~%take from [to]	take file from remote to local,
     81  *
     82  *	~l			dump communication line ioctl settings,
     83  *	~t			dump terminal ioctl settings.
     84  *
     85  *	Silent diversions are enabled only for use with the ~%take
     86  *	command by default for security reasons. Unsolicited diversions
     87  *	may be enabled using the ~%divert toggle. The 'new-style'
     88  *	diversion syntax is "~[local]>:filename", and is terminaled
     89  *	by "~[local]>", where 'local' is the nodename of the local
     90  *	system. This enables ~%take to operate properly when cu
     91  *	is used over multiple hops. 'old-style' diversion syntax may
     92  *	be enabled using the ~%old toggle. ('old-style' diversion
     93  *	should be avoided!)
     94  *
     95  *	Cu no longer uses dial.c to reach the remote.  Instead, cu places
     96  *	a telephone call to a remote system through the uucp conn() routine
     97  *	when the user picks the systemname option or through altconn()--
     98  *	which bypasses /etc/uucp/Systems -- if a telno or direct
     99  *	line is chosen. The line termio attributes are set in fixline(),
    100  *	before the remote connection is made.  As a device-lockout semaphore
    101  *	mechanism, uucp creates an entry in /var/spool/locks whose name is
    102  *	LK.<MAJ>.<maj>.<min> where MAJ is the major device of the
    103  *	filesystem containing the device, and <maj> and <min> are the
    104  *	major and minor of the device.
    105  *	When cu terminates, for whatever reason, cleanup() must be
    106  *	called to "release" the device, and clean up entries from
    107  *	the locks directory.  Cu runs with uucp ownership, and thus provides
    108  *	extra insurance that lock files will not be left around.
    109  */
    110 
    111 #include "uucp.h"
    112 #include <locale.h>
    113 #include <stropts.h>
    114 
    115 #define	MID	BUFSIZ/2	/* mnemonic */
    116 #define	RUB	'\177'		/* mnemonic */
    117 #define	XON	'\21'		/* mnemonic */
    118 #define	XOFF	'\23'		/* mnemonic */
    119 #define	TTYIN	0		/* mnemonic */
    120 #define	TTYOUT	1		/* mnemonic */
    121 #define	TTYERR	2		/* mnemonic */
    122 #define	HUNGUP  2
    123 #define	YES	1		/* mnemonic */
    124 #define	NO	0		/* mnemonic */
    125 #define	IOERR	4		/* exit code */
    126 #define	MAXPATH	100
    127 #define	NPL	50
    128 
    129 int Sflag=0;
    130 int Cn;				/*fd for remote comm line */
    131 jmp_buf Sjbuf;			/*needed by uucp routines*/
    132 
    133 /*	io buffering	*/
    134 /*	Wiobuf contains, in effect, 3 write buffers (to remote, to tty	*/
    135 /*	stdout, and to tty stderr) and Riobuf contains 2 read buffers	*/
    136 /*	(from remote, from tty).  [WR]IOFD decides which one to use.	*/
    137 /*	[RW]iop holds current position in each.				*/
    138 #define	WIOFD(fd)	(fd == TTYOUT ? 0 : (fd == Cn ? 1 : 2))
    139 #define	RIOFD(fd)	(fd == TTYIN ? 0 : 1)
    140 #define	WMASK(fd)	(fd == Cn ? line_mask : term_mask)
    141 #define	RMASK(fd)	(fd == Cn ? line_mask : term_mask)
    142 #define	WRIOBSZ 256
    143 static char Riobuf[2*WRIOBSZ];
    144 static char Wiobuf[3*WRIOBSZ];
    145 static int Riocnt[2] = {0, 0};
    146 static char *Riop[2];
    147 static char *Wiop[3];
    148 
    149 extern int optind;		/* variable in getopt() */
    150 
    151 extern char
    152 	*optarg;
    153 
    154 static struct call Cucall;	/* call structure for altconn()	*/
    155 
    156 static int Saved_tty;		/* was TCGETAW of _Tv0 successful?	*/
    157 static int Saved_termios;	/* was TCGETSW of _Tv0 successful?	*/
    158 static struct termio _Tv, _Tv0;	/* for saving, changing TTY atributes */
    159 static struct termios _Tv0s;	/* for saving, changing TTY atributes */
    160 static struct termio _Lv;	/* attributes for the line to remote */
    161 static struct termios _Lvs;	/* attributes for the line to remote */
    162 static char prompt[BUFSIZ]= "[";
    163 static struct utsname utsn;
    164 static int command_line_hups = 0;
    165 
    166 static char filename[BUFSIZ] = "/dev/null";
    167 
    168 static char
    169 	_Cxc,			/* place into which we do character io*/
    170 	_Tintr,			/* current input INTR */
    171 	_Tquit,			/* current input QUIT */
    172 	_Terase,		/* current input ERASE */
    173 	_Tkill,			/* current input KILL */
    174 	_Teol,			/* current secondary input EOL */
    175 	_Myeof,			/* current input EOF */
    176 	term_mask,		/* mask value for local terminal */
    177 	line_mask;		/* mask value for remote line */
    178 				/* either '0177' or '0377' */
    179 
    180 int
    181 	Echoe,			/* save users ECHOE bit */
    182 	Echok,			/* save users ECHOK bit */
    183 	Intrupt=NO,		/* interrupt indicator */
    184 	Ifc=YES,		/* NO means remote can't XON/XOFF */
    185 	Ofc=YES,		/* NO means local can't XON/XOFF */
    186 	Rtn_code=0,		/* default return code */
    187 	Divert=NO,		/* don't allow unsolicited redirection */
    188 	OldStyle=NO,		/* don't handle old '~>:filename' syntax */
    189 				/* this will be mandatory in SVR4.1 */
    190 	Takeflag=NO,		/* indicates a ~%take is in progress */
    191 	Dologin=NO,		/* go through the login chat sequence */
    192 	Docmd=NO;		/* execute command instead of interactive cu */
    193 
    194 EXTERN int			/* These are initialized in line.c */
    195 	Terminal,		/* flag; remote is a terminal */
    196 	Oddflag,		/* flag- odd parity option*/
    197 	Evenflag,		/* flag- even parity option*/
    198 	Duplex,			/* Unix= full duplex=YES; half = NO */
    199 	term_8bit,		/* is terminal set for 8 bit processing */
    200 	line_8bit;		/* is line set for 8 bit processing */
    201 
    202 EXTERN int clear_hup();
    203 
    204 pid_t
    205 	Child,			/* pid for receive process */
    206 	Shell;			/* pid for escape process */
    207 
    208 static pid_t
    209 	dofork();		/* fork and return pid */
    210 
    211 static int
    212 	r_char(),		/* local io routine */
    213 	w_char(),		/* local io routine */
    214 	wioflsh();
    215 
    216 static void
    217 	_onintrpt(),		/* interrupt routines */
    218 	_rcvdead(),
    219 	_quit(),
    220 	_bye();
    221 
    222 extern void	cleanup();
    223 extern void	tdmp();
    224 extern int conn(), altconn(), transmit(), tilda();
    225 
    226 static void
    227 	recfork(),
    228 	sysname(),
    229 	blckcnt(),
    230 	_flush(),
    231 	_shell(),
    232 	_dopercen(),
    233 	_receive(),
    234 	_mode(),
    235 	_w_str();
    236 
    237 extern char *Myline;	/* flag to force the requested line to be used  */
    238 extern char *Mytype;	/* flag to force requested line type to be used
    239 			 * rddev() will compare the string to the D_TYPE
    240 			 * (first) field of the Devices record and skip any
    241 			 * records where they are not equal. Mytype is set
    242 			 * to point to the argument of the -c option from
    243 			 * the command line. */
    244 static char *P_USAGE= "Usage: %s [-dhtnLC] [-c device] [-s speed] [-l line] [-b 7|8]\n\t[-o | -e] telno | systemname [local-cmd]\n";
    245 static char *P_CON_FAILED = "Connect failed: %s\r\n";
    246 static char *P_Ct_OPEN = "Cannot open: %s\r\n";
    247 static char *P_LINE_GONE = "Remote line gone\r\n";
    248 static char *P_Ct_EXSH = "Can't execute shell\r\n";
    249 static char *P_Ct_DIVERT = "Can't divert to %s\r\n";
    250 static char *P_Ct_UNDIVERT = "Can't end diversion to %s\r\n";
    251 static char *P_Bad_DIVERT = "Won't divert to %s. Unsolicited.\r\n";
    252 static char *P_STARTWITH = "Use `~~' to start line with `~'\r\n";
    253 static char *P_CNTAFTER = "File transmission interrupted after %ld bytes.\r\n";
    254 static char *P_CNTLINES = "%d lines/";
    255 static char *P_CNTCHAR = "%ld characters\r\n";
    256 static char *P_FILEINTR = "File transmission interrupted\r\n";
    257 static char *P_Ct_FK = "Can't fork -- try later\r\n";
    258 static char *P_Ct_SPECIAL = "r\nCan't transmit special character `%#o'\r\n";
    259 static char *P_TOOLONG = "\nLine too long\r\n";
    260 static char *P_IOERR = "r\nIO error\r\n";
    261 static char *P_USECMD = "Use `~$'cmd \r\n";
    262 #ifdef forfutureuse
    263 static char *P_USEPLUSCMD ="Use `~+'cmd \r\n";
    264 #endif
    265 #ifdef u3b
    266 static char *P_NOTERMSTAT = "Can't get terminal status\r\n";
    267 static char *P_3BCONSOLE = "Sorry, you can't cu from a 3B console\r\n";
    268 #endif
    269 static char *P_TELLENGTH = "Telno cannot exceed 58 digits!\r\n";
    270 
    271 /***************************************************************
    272  *	main: get command line args, establish connection, and fork.
    273  *	Child invokes "receive" to read from remote & write to TTY.
    274  *	Main line invokes "transmit" to read TTY & write to remote.
    275  ***************************************************************/
    276 
    277 int
    278 main(argc, argv)
    279 int argc;
    280 char *argv[];
    281 {
    282     extern void setservice();
    283     extern int sysaccess();
    284     char s[MAXPH];
    285     char *string;
    286     int i;
    287     int errflag=0;
    288     int lflag=0;
    289     int nflag=0;
    290     int systemname = 0;
    291     char vdisable;
    292 
    293     /* Set locale environment variables local definitions */
    294     (void) setlocale(LC_ALL, "");
    295 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
    296 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it wasn't */
    297 #endif
    298     (void) textdomain(TEXT_DOMAIN);
    299 
    300     Riop[0] = &Riobuf[0];
    301     Riop[1] = &Riobuf[WRIOBSZ];
    302     Wiop[0] = &Wiobuf[0];
    303     Wiop[1] = &Wiobuf[WRIOBSZ];
    304     Wiop[2] = &Wiobuf[2*WRIOBSZ];
    305 
    306     Verbose = 1;		/*for uucp callers,  dialers feedback*/
    307     if ((string = strrchr(argv[0], '/')) != NULL)
    308 	string++;
    309     else
    310 	string = argv[0];
    311     if (strlcpy(Progname, string, NAMESIZE) >= NAMESIZE) {
    312 	errno = ENAMETOOLONG;
    313 	perror("cu");
    314 	exit(1);
    315     }
    316     setservice(Progname);
    317     if ( sysaccess(EACCESS_SYSTEMS) != 0 ) {
    318 	(void)fprintf(stderr,
    319 	     gettext("%s: Cannot read Systems files\n"), Progname);
    320 	exit(1);
    321     }
    322     if ( sysaccess(EACCESS_DEVICES) != 0 ) {
    323 	(void)fprintf(stderr,
    324 	     gettext("%s: Cannot read Devices files\n"), Progname);
    325 	exit(1);
    326     }
    327     if ( sysaccess(EACCESS_DIALERS) != 0 ) {
    328 	(void)fprintf(stderr,
    329 	    gettext("%s: Cannot read Dialers files\n"), Progname);
    330 	exit(1);
    331     }
    332 
    333     Cucall.speed = "Any";	/*default speed*/
    334     Cucall.line = CNULL;
    335     Cucall.telno = CNULL;
    336     Cucall.type = CNULL;
    337 
    338 /*Flags for -h, -t, -e, and -o options set here; corresponding line attributes*/
    339 /*are set in fixline() in culine.c before remote connection is made	   */
    340 
    341     while((i = getopt(argc, argv, "dhteons:l:c:b:LCH")) != EOF)
    342 	switch(i) {
    343 	    case 'd':
    344 		Debug = 9; /*turns on uucp debugging-level 9*/
    345 		break;
    346 	    case 'h':
    347 		Duplex  = NO;
    348 		Ifc = NO;
    349 		Ofc = NO;
    350 		break;
    351 	    case 't':
    352 		Terminal = YES;
    353 		break;
    354 	    case 'e':
    355 		if ( Oddflag ) {
    356 		    (void)fprintf(stderr,
    357 			gettext("%s: Cannot have both even and odd parity\n"),
    358 			argv[0]);
    359 		    exit(1);
    360 		}
    361 		Evenflag = 1;
    362 		break;
    363 	    case 'o':
    364 		if ( Evenflag ) {
    365 		    (void)fprintf(stderr,
    366 			gettext("%s: Cannot have both even and odd parity\n"),
    367 			argv[0]);
    368 		    exit(1);
    369 		}
    370 		Oddflag = 1;
    371 		break;
    372 	    case 'n':
    373 		nflag++;
    374 		printf(gettext("Please enter the number: "));
    375 		/* Read line from stdin, remove trailing newline, if any */
    376 		if (fgets(s, sizeof(s), stdin) != NULL &&
    377 			strchr(s, '\n') != NULL)
    378 		   s[strlen(s)-1] = '\0';
    379 		break;
    380 	    case 's':
    381 		Sflag++;
    382 		Cucall.speed = optarg;
    383 		break;
    384 	    case 'l':
    385 		lflag++;
    386 		Cucall.line = optarg;
    387 		break;
    388 	    case 'c':
    389 		Cucall.type = optarg;
    390 		Mytype = optarg;
    391 		break;
    392 	    case 'b':
    393 		line_8bit = ((*optarg=='7') ? NO : ((*optarg=='8') ? YES : -1));
    394 		if ( line_8bit == -1 ) {
    395 		    (void) fprintf(stderr,
    396 			gettext("%s: b option value must be '7' or '8'\n"),
    397 			argv[0]);
    398 		    exit(1);
    399 		}
    400 		break;
    401 	    case 'L':
    402 		Dologin++;
    403 		break;
    404 	    case 'C':
    405 		Docmd++;
    406 		break;
    407 	    case 'H':
    408 		command_line_hups++;
    409 		break;
    410 	    case '?':
    411 		++errflag;
    412 	}
    413 
    414 #ifdef  u3b
    415     {
    416     struct stat buff;
    417     if(fstat(TTYIN, &buff) < 0) {
    418 	VERBOSE(gettext(P_NOTERMSTAT),"");
    419 	exit(1);
    420     } else if ( (buff.st_mode & S_IFMT) == S_IFCHR && buff.st_rdev == 0 ) {
    421 	VERBOSE(gettext(P_3BCONSOLE),"");
    422 	exit(1);
    423 	}
    424     }
    425 #endif
    426 
    427     if((optind < argc && optind > 0) || (nflag && optind > 0)) {
    428 	if(nflag)
    429 	    string=s;
    430 	else
    431 	    string = strdup(argv[optind++]);
    432 	Cucall.telno = string;
    433 	if ( strlen(string) != strspn(string, "0123456789=-*#") ) {
    434 	    /* if it's not a legitimate telno, then it should be a systemname */
    435 	    if ( nflag ) {
    436 		(void)fprintf(stderr, gettext("%s: Bad phone number %s\n"),
    437 				argv[0], string);
    438 		(void) fprintf(stderr, gettext("Phone numbers may contain "
    439 		    "only the digits 0 through 9 and the special\n"
    440 		    "characters =, -, * and #.\n"));
    441 		exit(1);
    442 	    }
    443 	    systemname++;
    444 	}
    445     } else
    446 	if(Cucall.line == CNULL)   /*if none of above, must be direct */
    447 	    ++errflag;
    448 
    449     if(errflag) {
    450 	VERBOSE(gettext(P_USAGE), argv[0]);
    451 	exit(1);
    452     }
    453 
    454     if ((Cucall.telno != CNULL) &&
    455 		(strlen(Cucall.telno) >= (size_t)(MAXPH - 1))) {
    456 	VERBOSE(gettext(P_TELLENGTH),"");
    457 	exit(0);
    458     }
    459 
    460     /* save initial tty state */
    461     if (!(Saved_termios = ( ioctl(TTYIN, TCGETS, &_Tv0s) >= 0 ))) {
    462 	Saved_tty = ( ioctl(TTYIN, TCGETA, &_Tv0) == 0 );
    463 	_Tv0s.c_lflag = _Tv0.c_lflag;
    464 	_Tv0s.c_oflag = _Tv0.c_oflag;
    465 	_Tv0s.c_iflag = _Tv0.c_iflag;
    466 	_Tv0s.c_cflag = _Tv0.c_cflag;
    467 	for(i = 0; i < NCC; i++)
    468 		_Tv0s.c_cc[i] = _Tv0.c_cc[i];
    469     }
    470 
    471     if (Saved_termios || Saved_tty) {
    472 	char *p;
    473 
    474 	/*
    475 	 * We consider the terminal to be in 8 bit mode only if cs8 is set,
    476 	 * istrip is not set, and we're not in the "C" locale.  The "C"
    477 	 * locale is by definition 7 bit only.  This provides reasonable
    478 	 * compatibility when running in the "C" locale (currently the default)
    479 	 * and connecting to other systems, which are most often 7 bit systems.
    480 	 */
    481 	term_8bit = ( (_Tv0s.c_cflag & CS8) && !(_Tv0s.c_iflag & ISTRIP) &&
    482 	  ((p = setlocale(LC_CTYPE, NULL)) != NULL) && (strcmp(p, "C") != 0) );
    483 	if ( !Oddflag && !Evenflag )
    484 	    if (_Tv0s.c_cflag & PARENB)
    485 		if (_Tv0s.c_cflag & PARODD)
    486 		    Oddflag = 1;
    487 		else
    488 		    Evenflag = 1;
    489     }
    490 
    491     if (line_8bit == -1)
    492 	line_8bit = term_8bit;
    493 
    494     term_mask = ( term_8bit ? 0377 : 0177 );
    495     line_mask = ( line_8bit ? 0377 : 0177 );
    496 
    497     /* if not set, use the POSIX disabled designation */
    498 #ifdef _POSIX_VDISABLE
    499     vdisable = _POSIX_VDISABLE;
    500 #else
    501     vdisable = fpathconf(TTYIN, _PC_VDISABLE);
    502 #endif
    503     _Tintr = _Tv0s.c_cc[VINTR] ? _Tv0s.c_cc[VINTR] : vdisable;
    504     _Tquit = _Tv0s.c_cc[VQUIT] ? _Tv0s.c_cc[VQUIT] : vdisable;
    505     _Terase = _Tv0s.c_cc[VERASE] ? _Tv0s.c_cc[VERASE] : vdisable;
    506     _Tkill = _Tv0s.c_cc[VKILL] ? _Tv0s.c_cc[VKILL] : vdisable;
    507     _Teol = _Tv0s.c_cc[VEOL] ? _Tv0s.c_cc[VEOL] : vdisable;
    508     _Myeof = _Tv0s.c_cc[VEOF] ? _Tv0s.c_cc[VEOF] : '\04';
    509     Echoe = _Tv0s.c_lflag & ECHOE;
    510     Echok = _Tv0s.c_lflag & ECHOK;
    511 
    512     (void)signal(SIGHUP, cleanup);
    513     (void)signal(SIGQUIT, cleanup);
    514     (void)signal(SIGINT, cleanup);
    515 
    516 /* place call to system; if "cu systemname", use conn() from uucp
    517    directly.  Otherwise, use altconn() which dummies in the
    518    Systems file line.
    519 */
    520 
    521     if(systemname) {
    522 	if ( lflag )
    523 	    (void)fprintf(stderr,
    524 	        gettext("%s: Warning: -l flag ignored when system name used\n"),
    525 	        argv[0]);
    526 	if ( Sflag )
    527 	    (void)fprintf(stderr,
    528 	        gettext("%s: Warning: -s flag ignored when system name used\n"),
    529 	        argv[0]);
    530 	Cn = conn(string);
    531 	if ( (Cn < 0) && (Cucall.type != CNULL) )
    532 	    Cn = altconn(&Cucall);
    533     } else
    534 	Cn = altconn(&Cucall);
    535 
    536     if(Cn < 0) {
    537 	VERBOSE(gettext(P_CON_FAILED),UERRORTEXT);
    538 	cleanup(-Cn);
    539     } else {
    540 	struct stat Cnsbuf;
    541 	if ( fstat(Cn, &Cnsbuf) == 0 )
    542 	    Dev_mode = Cnsbuf.st_mode;
    543 	else
    544 	    Dev_mode = R_DEVICEMODE;
    545 	fchmod(Cn, M_DEVICEMODE);
    546     }
    547 
    548     if ((Docmd) && (argv[optind] == NULL)) {
    549         (void) fprintf(stderr,gettext("cu: local cmd is required, -C is ignored.\n"));
    550         VERBOSE(gettext(P_USAGE), argv[0]);
    551         Docmd=NO;
    552     }
    553 
    554     if (!Docmd) {
    555 	Euid = geteuid();
    556 	if((setuid(getuid()) < 0) || (setgid(getgid()) < 0)) {
    557 	    VERBOSE("Unable to setuid/gid\n%s", "");
    558 	    cleanup(101);
    559 	}
    560     }
    561 
    562     if(Debug)
    563 	tdmp(Cn);
    564 
    565     /* At this point succeeded in getting an open communication line	*/
    566     /* Conn() takes care of closing the Systems file			*/
    567 
    568     if (!Docmd) {
    569 	(void)signal(SIGINT,_onintrpt);
    570 	_mode(1);			/* put terminal in `raw' mode */
    571 	VERBOSE("Connected\007\r\n%s", "");	/*bell!*/
    572 
    573 	/* must catch signals before fork.  if not and if _receive()	*/
    574 	/* fails in just the right (wrong?) way, _rcvdead() can be	*/
    575 	/* called and do "kill(getppid(),SIGUSR1);" before parent	*/
    576 	/* has done calls to signal() after recfork().			*/
    577 	(void)signal(SIGUSR1, _bye);
    578 	(void)signal(SIGHUP, cleanup);
    579 	(void)signal(SIGQUIT, _onintrpt);
    580 
    581 	sysname(&prompt[1]);	/* set up system name prompt */
    582 	(void) strcat(prompt, "]");
    583 
    584 	recfork();		/* checks for child == 0 */
    585 
    586 	if(Child > 0) {
    587 	    /*
    588 	     * Because the child counts hangups for the -H flag,
    589 	     * and because we fork a new child when doing (e.g.)
    590 	     * ~%take, we assume the first child we fork has
    591 	     * processed all the hangups and we reset the count here.
    592 	     * We really should pass the remaining count back from
    593 	     * the child to the parent when we kill the child.
    594 	     */
    595 	    command_line_hups = 0;
    596 	    Rtn_code = transmit();
    597 	    _quit(Rtn_code);
    598 	    /*NOTREACHED*/
    599 	}
    600     } else {
    601 	/*
    602 	 * Fork a child to run the specified command,
    603 	 * wait for it to finish, and clean up.
    604 	 */
    605 	Child = dofork();
    606 	if (Child == 0) {
    607 	    close(0);
    608 	    close(1);
    609 	    dup(Cn);
    610 	    dup(Cn);
    611 	    close(Cn);
    612 	    setgid(getgid());
    613 	    setuid(getuid());
    614 	    execvp(argv[optind], &argv[optind]);
    615 	    exit(-1);
    616 	    /* NOTREACHED */
    617 	}
    618 	wait(0);
    619 	/* XXX - should return wait status as our exit code */
    620     }
    621     cleanup(Cn);
    622     /*NOTREACHED*/
    623 	return (0);
    624 }
    625 
    626 /*
    627  *	Kill the present child, if it exists, then fork a new one.
    628  */
    629 
    630 static void
    631 recfork()
    632 {
    633     int ret, status;
    634     if (Child) {
    635 	kill(Child, SIGKILL);
    636 	while ( (ret = wait(&status)) != Child )
    637 	    if (ret == -1 && errno != EINTR)
    638 		break;
    639     }
    640     Child = dofork();
    641     if(Child == 0) {
    642 	(void)signal(SIGUSR1, SIG_DFL);
    643 	(void)signal(SIGHUP, _rcvdead);
    644 	(void)signal(SIGQUIT, SIG_IGN);
    645 	(void)signal(SIGINT, SIG_IGN);
    646 
    647 	_receive();	/* This should run until killed */
    648 	/*NOTREACHED*/
    649     }
    650     return;
    651 }
    652 
    653 /***************************************************************
    654  *	transmit: copy stdin to remote fd, except:
    655  *	~.	terminate
    656  *	~!	local login-style shell
    657  *	~!cmd	execute cmd locally
    658  *	~$proc	execute proc locally, send output to line
    659  *	~%cmd	execute builtin cmd (put, take, or break)
    660  ****************************************************************/
    661 #ifdef forfutureuse
    662  /*****************************************************************
    663   *	~+proc	execute locally, with stdout to and stdin from line.
    664   ******************************************************************/
    665 #endif
    666 
    667 int
    668 transmit()
    669 {
    670     char b[BUFSIZ];
    671     char *p;
    672     int escape;
    673     int id = 0;  /* flag for systemname prompt on tilda escape */
    674 
    675     CDEBUG(4,"transmit started\n\r%s", "");
    676 
    677     /* In main loop, always waiting to read characters from	*/
    678     /* keyboard; writes characters to remote, or to TTYOUT	*/
    679     /* on a tilda escape					*/
    680 
    681     for (;;) {
    682 	p = b;
    683 	while(r_char(TTYIN) == YES) {
    684 	    if(p == b)  	/* Escape on leading  ~    */
    685 		escape = (_Cxc == '~');
    686 	    if(p == b+1)   	/* But not on leading ~~   */
    687 		escape &= (_Cxc != '~');
    688 	    if(escape) {
    689 		 if(_Cxc == '\n' || _Cxc == '\r' || _Cxc == _Teol) {
    690 		    *p = '\0';
    691 		    if(tilda(b+1) == YES)
    692 			return(0);
    693 		    id = 0;
    694 		    break;
    695 		}
    696 		if(_Cxc == _Tintr || _Cxc == _Tkill || _Cxc == _Tquit ||
    697 			(Intrupt && _Cxc == '\0')) {
    698 		    if(_Cxc == _Tkill) {
    699 			if(Echok)
    700 			    VERBOSE("\r\n%s", "");
    701 		    } else {
    702 			_Cxc = '\r';
    703 			if( w_char(Cn) == NO) {
    704 			    VERBOSE(gettext(P_LINE_GONE),"");
    705 			    return(IOERR);
    706 			}
    707 			id=0;
    708 		    }
    709 		    break;
    710 		}
    711 		if((p == b+1) && (_Cxc != _Terase) && (!id)) {
    712 		    id = 1;
    713 		    VERBOSE("%s", prompt);
    714 		}
    715 		if(_Cxc == _Terase) {
    716 		    p = (--p < b)? b:p;
    717 		    if(p > b)
    718 			if(Echoe) {
    719 			    VERBOSE("\b \b%s", "");
    720 			} else
    721 			    (void)w_char(TTYOUT);
    722 		} else {
    723 		    (void)w_char(TTYOUT);
    724 		    if(p-b < BUFSIZ)
    725 			*p++ = _Cxc;
    726 		    else {
    727 			VERBOSE(gettext(P_TOOLONG),"");
    728 			break;
    729 		    }
    730 		}
    731     /*not a tilda escape command*/
    732 	    } else {
    733 		if(Intrupt && _Cxc == '\0') {
    734 		    CDEBUG(4,"got break in transmit\n\r%s", "");
    735 		    Intrupt = NO;
    736 		    (*genbrk)(Cn);
    737 		    _flush();
    738 		    break;
    739 		}
    740 		if(w_char(Cn) == NO) {
    741 		    VERBOSE(gettext(P_LINE_GONE),"");
    742 		    return(IOERR);
    743 		}
    744 		if(Duplex == NO) {
    745 		    if((w_char(TTYERR) == NO) || (wioflsh(TTYERR) == NO))
    746 			return(IOERR);
    747 		}
    748 		if ((_Cxc == _Tintr) || (_Cxc == _Tquit) ||
    749 		     ( (p==b) && (_Cxc == _Myeof) ) ) {
    750 		    CDEBUG(4,"got a tintr\n\r%s", "");
    751 		    _flush();
    752 		    break;
    753 		}
    754 		if(_Cxc == '\n' || _Cxc == '\r' ||
    755 		    _Cxc == _Teol || _Cxc == _Tkill) {
    756 		    id=0;
    757 		    Takeflag = NO;
    758 		    break;
    759 		}
    760 		p = (char*)0;
    761 	    }
    762 	}
    763     }
    764 }
    765 
    766 /***************************************************************
    767  *	routine to halt input from remote and flush buffers
    768  ***************************************************************/
    769 static void
    770 _flush()
    771 {
    772     (void)ioctl(TTYOUT, TCXONC, 0);	/* stop tty output */
    773     (void)ioctl(Cn, TCFLSH, 0);		/* flush remote input */
    774     (void)ioctl(TTYOUT, TCFLSH, 1);	/* flush tty output */
    775     (void)ioctl(TTYOUT, TCXONC, 1);	/* restart tty output */
    776     if(Takeflag == NO) {
    777 	return;		/* didn't interupt file transmission */
    778     }
    779     VERBOSE(gettext(P_FILEINTR),"");
    780     (void)sleep(3);
    781     _w_str("echo '\n~>\n';mesg y;stty echo\n");
    782     Takeflag = NO;
    783     return;
    784 }
    785 
    786 /**************************************************************
    787  *	command interpreter for escape lines
    788  **************************************************************/
    789 int
    790 tilda(cmd)
    791 char	*cmd;
    792 {
    793 
    794     VERBOSE("\r\n%s", "");
    795     CDEBUG(4,"call tilda(%s)\r\n", cmd);
    796 
    797     switch(cmd[0]) {
    798 	case CSUSP:
    799 	case CDSUSP:
    800 	    _mode(0);
    801 	    kill(cmd[0] == CDSUSP ? getpid() : (pid_t) 0, SIGTSTP);
    802 	    _mode(1);
    803 	    break;
    804 	case '.':
    805 	    if(Cucall.telno == CNULL)
    806 		if(cmd[1] != '.') {
    807 		    _w_str("\04\04\04\04\04");
    808 		    if (Child)
    809 			kill(Child, SIGKILL);
    810 		    if (ioctl (Cn, TCGETS, &_Lvs) < 0) {
    811 		    	(void) ioctl (Cn, TCGETA, &_Lv);
    812 		    	/* speed to zero for hangup */
    813 		    	_Lv.c_cflag = 0;
    814 		    	(void) ioctl (Cn, TCSETAW, &_Lv);
    815 		    } else {
    816 		    	/* speed to zero for hangup */
    817 			_Lvs.c_cflag &= 0xffff0000;
    818 			cfsetospeed(&_Lvs, B0);
    819 		    	(void) ioctl (Cn, TCSETSW, &_Lvs);
    820 		    }
    821 		    (void) sleep (2);
    822 		}
    823 	    return(YES);
    824 	case '!':
    825 	    _shell(cmd);	/* local shell */
    826 	    VERBOSE("\r%c\r\n", *cmd);
    827 	    VERBOSE("(continue)%s", "");
    828 	    break;
    829 	case '$':
    830 	    if(cmd[1] == '\0') {
    831 		VERBOSE(gettext(P_USECMD),"");
    832 		VERBOSE("(continue)%s", "");
    833 	    } else {
    834 		_shell(cmd);	/*Local shell  */
    835 		VERBOSE("\r%c\r\n", *cmd);
    836 	    }
    837 	    break;
    838 
    839 #ifdef forfutureuse
    840 	case '+':
    841 	    if(cmd[1] == '\0') {
    842 		VERBOSE(gettext(P_USEPLUSCMD), "");
    843 		VERBOSE("(continue)%s", "");
    844 	    } else {
    845 		if (*cmd == '+')
    846 			  /* must suspend receive to give*/
    847 			  /*remote out to stdin of cmd */
    848 		    kill(Child, SIGKILL);
    849 		    _shell(cmd);	/* Local shell */
    850 		if (*cmd == '+')
    851 		    recfork();
    852 		VERBOSE("\r%c\r\n", *cmd);
    853 	    }
    854 	    break;
    855 #endif
    856 	case '%':
    857 	    _dopercen(++cmd);
    858 	    break;
    859 
    860 	case 't':
    861 	    tdmp(TTYIN);
    862 	    VERBOSE("(continue)%s", "");
    863 	    break;
    864 	case 'l':
    865 	    tdmp(Cn);
    866 	    VERBOSE("(continue)%s", "");
    867 	    break;
    868 
    869 	default:
    870 	    VERBOSE(gettext(P_STARTWITH),"");
    871 	    VERBOSE("(continue)%s", "");
    872 	    break;
    873     }
    874     return(NO);
    875 }
    876 
    877 /***************************************************************
    878  *	The routine "shell" takes an argument starting with
    879  *	either "!" or "$", and terminated with '\0'.
    880  *	If $arg, arg is the name of a local shell file which
    881  *	is executed and its output is passed to the remote.
    882  *	If !arg, we escape to a local shell to execute arg
    883  *	with output to TTY, and if arg is null, escape to
    884  *	a local shell and blind the remote line.  In either
    885  *	case, '^D' will kill the escape status.
    886  **************************************************************/
    887 
    888 #ifdef forfutureuse
    889 /***************************************************************
    890  *	Another argument to the routine "shell" may be +.  If +arg,
    891  *	arg is the name of a local shell file which is executed with
    892  *	stdin from and stdout to the remote.
    893  **************************************************************/
    894 #endif
    895 
    896 static void
    897 _shell(str)
    898 char	*str;
    899 {
    900     pid_t	fk, w_ret;
    901     void	(*xx)(), (*yy)();
    902 
    903     CDEBUG(4,"call _shell(%s)\r\n", str);
    904     fk = dofork();
    905     if(fk < 0)
    906 	return;
    907     Shell = fk;
    908     _mode(0);	/* restore normal tty attributes */
    909     xx = signal(SIGINT, SIG_IGN);
    910     yy = signal(SIGQUIT, SIG_IGN);
    911     if(fk == 0) {
    912 	char *shell;
    913 
    914 	if( (shell = getenv("SHELL")) == NULL)
    915 	    /* use default if user's shell is not set */
    916 	    shell = SHELL;
    917 	(void)close(TTYOUT);
    918 
    919 	/***********************************************
    920 	 * Hook-up our "standard output"
    921 	 * to either the tty for '!' or the line
    922 	 * for '$'  as appropriate
    923 	 ***********************************************/
    924 #ifdef forfutureuse
    925 
    926 	/************************************************
    927 	 * Or to the line for '+'.
    928 	 **********************************************/
    929 #endif
    930 
    931 	(void)fcntl((*str == '!')? TTYERR:Cn,F_DUPFD,TTYOUT);
    932 
    933 #ifdef forfutureuse
    934 	/*************************************************
    935 	 * Hook-up "standard input" to the line for '+'.
    936 	 * **********************************************/
    937 	if (*str == '+') {
    938 	    (void)close(TTYIN);
    939 	    (void)fcntl(Cn,F_DUPFD,TTYIN);
    940 	    }
    941 #endif
    942 
    943 	/***********************************************
    944 	 * Hook-up our "standard input"
    945 	 * to the tty for '!' and '$'.
    946 	 ***********************************************/
    947 
    948 	(void)close(Cn);   	/*parent still has Cn*/
    949 	(void)signal(SIGINT, SIG_DFL);
    950 	(void)signal(SIGHUP, SIG_DFL);
    951 	(void)signal(SIGQUIT, SIG_DFL);
    952 	(void)signal(SIGUSR1, SIG_DFL);
    953 	if(*++str == '\0')
    954 	    (void)execl(shell,shell,(char*) 0,(char*) 0,(char *) 0);
    955 	else
    956 	    (void)execl(shell,"sh","-c",str,(char *) 0);
    957 	VERBOSE(gettext(P_Ct_EXSH),"");
    958 	exit(0);
    959     }
    960     while ((w_ret = wait((int*)0)) != fk)
    961 	if (w_ret == -1 && errno != EINTR)
    962 	    break;
    963     Shell = 0;
    964     (void)signal(SIGINT, xx);
    965     (void)signal(SIGQUIT, yy);
    966     _mode(1);
    967     return;
    968 }
    969 
    970 
    971 /***************************************************************
    972  *	This function implements the 'put', 'take', 'break',
    973  *	'ifc' (aliased to nostop) and 'ofc' (aliased to noostop)
    974  *	commands which are internal to cu.
    975  ***************************************************************/
    976 
    977 static void
    978 _dopercen(cmd)
    979 char *cmd;
    980 {
    981     char	*arg[5];
    982     char	*getpath;
    983     char	mypath[MAXPATH];
    984     int	narg;
    985 
    986     blckcnt((long)(-1));
    987 
    988     CDEBUG(4,"call _dopercen(\"%s\")\r\n", cmd);
    989 
    990     arg[narg=0] = strtok(cmd, " \t\n");
    991 
    992     /* following loop breaks out the command and args */
    993     while((arg[++narg] = strtok((char*) NULL, " \t\n")) != NULL) {
    994 	if(narg < 4)
    995 	    continue;
    996 	else
    997 	    break;
    998     }
    999 
   1000     /* ~%take file option */
   1001     if(EQUALS(arg[0], "take")) {
   1002 	if(narg < 2 || narg > 3) {
   1003 	    VERBOSE("usage: ~%%take from [to]\r\n%s", "");
   1004 	    VERBOSE("(continue)%s", "");
   1005 	    return;
   1006 	}
   1007 	if(narg == 2)
   1008 	    arg[2] = arg[1];
   1009 	(void) strcpy(filename, arg[2]);
   1010 	recfork();	/* fork so child (receive) knows filename */
   1011 
   1012 	/*
   1013 	 * be sure that the remote file (arg[1]) exists before
   1014 	 * you try to take it.   otherwise, the error message from
   1015 	 * cat will wind up in the local file (arg[2])
   1016 	 *
   1017 	 * what we're doing is:
   1018 	 *	stty -echo; \
   1019 	 *	if test -r arg1
   1020 	 *	then (echo '~[local]'>arg2; cat arg1; echo '~[local]'>)
   1021 	 *	else echo can't open: arg1
   1022 	 *	fi; \
   1023 	 *	stty echo
   1024 	 *
   1025 	 */
   1026 	_w_str("stty -echo;if test -r ");
   1027 	_w_str(arg[1]);
   1028 	_w_str("; then (echo '~");
   1029 	_w_str(prompt);
   1030 	_w_str(">'");
   1031 	_w_str(arg[2]);
   1032 	_w_str(";cat ");
   1033 	_w_str(arg[1]);
   1034 	_w_str(";echo '~");
   1035 	_w_str(prompt);
   1036 	_w_str(">'); else echo cant\\'t open: ");
   1037 	_w_str(arg[1]);
   1038 	_w_str("; fi;stty echo\n");
   1039 	Takeflag = YES;
   1040 	return;
   1041     }
   1042     /* ~%put file option*/
   1043     if(EQUALS(arg[0], "put")) {
   1044 	FILE	*file;
   1045 	char	ch, buf[BUFSIZ], spec[NCC+1], *b, *p, *q;
   1046 	int	i, j, len, tc=0, lines=0;
   1047 	long	chars=0L;
   1048 
   1049 	if(narg < 2 || narg > 3) {
   1050 	    VERBOSE("usage: ~%%put from [to]\r\n%s", "");
   1051 	    VERBOSE("(continue)%s", "");
   1052 	    return;
   1053 	}
   1054 	if(narg == 2)
   1055 	    arg[2] = arg[1];
   1056 
   1057 	if((file = fopen(arg[1], "r")) == NULL) {
   1058 	    VERBOSE(gettext(P_Ct_OPEN), arg[1]);
   1059 	    VERBOSE("(continue)%s", "");
   1060 	    return;
   1061 	}
   1062 	/*
   1063 	 * if cannot write into file on remote machine, write into
   1064 	 * /dev/null
   1065 	 *
   1066 	 * what we're doing is:
   1067 	 *	stty -echo
   1068 	 *	(cat - > arg2) || cat - > /dev/null
   1069 	 *	stty echo
   1070 	 */
   1071 	_w_str("stty -echo;(cat - >");
   1072 	_w_str(arg[2]);
   1073 	_w_str(")||cat - >/dev/null;stty echo\n");
   1074 	Intrupt = NO;
   1075 	for(i=0,j=0; i < NCC; ++i)
   1076 	    if((ch=_Tv0s.c_cc[i]) != '\0')
   1077 		spec[j++] = ch;
   1078 	spec[j] = '\0';
   1079 	_mode(2);	/*accept interrupts from keyboard*/
   1080 	(void)sleep(5);	/*hope that w_str info digested*/
   1081 
   1082 	/* Read characters line by line into buf to write to	*/
   1083 	/* remote with character and line count for blckcnt	*/
   1084 	while(Intrupt == NO &&
   1085 		fgets(b= &buf[MID],MID,file) != NULL) {
   1086 	    /* worse case is each char must be escaped*/
   1087 	    len = strlen(b);
   1088 	    chars += len;		/* character count */
   1089 	    p = b;
   1090 	    while(q = strpbrk(p, spec)) {
   1091 		if(*q == _Tintr || *q == _Tquit || *q == _Teol) {
   1092 		    VERBOSE(gettext(P_Ct_SPECIAL), *q);
   1093 		    (void)strcpy(q, q+1);
   1094 		    Intrupt = YES;
   1095 		} else {
   1096 		    b = strncpy(b-1, b, q-b);
   1097 		    *(q-1) = '\\';
   1098 		}
   1099 		p = q+1;
   1100 	    }
   1101 	    if((tc += len) >= MID) {
   1102 		(void)sleep(1);
   1103 		tc = len;
   1104 	    }
   1105 	    if(write(Cn, b, (unsigned)strlen(b)) < 0) {
   1106 		VERBOSE(gettext(P_IOERR),"");
   1107 		Intrupt = YES;
   1108 		break;
   1109 	    }
   1110 	    ++lines;		/* line count */
   1111 	    blckcnt((long)chars);
   1112 	}
   1113 	_mode(1);
   1114 	blckcnt((long)(-2));		/* close */
   1115 	(void)fclose(file);
   1116 	if(Intrupt == YES) {
   1117 	    Intrupt = NO;
   1118 	    _w_str("\n");
   1119 	    VERBOSE(gettext(P_CNTAFTER), ++chars);
   1120 	} else {
   1121 	    VERBOSE(gettext(P_CNTLINES), lines);
   1122 	    VERBOSE(gettext(P_CNTCHAR),chars);
   1123 	}
   1124 	(void)sleep(3);
   1125 	_w_str("\04");
   1126 	return;
   1127     }
   1128 
   1129 	/*  ~%b or ~%break  */
   1130     if(EQUALS(arg[0], "b") || EQUALS(arg[0], "break")) {
   1131 	(*genbrk)(Cn);
   1132 	return;
   1133     }
   1134 	/*  ~%d or ~%debug toggle  */
   1135     if(EQUALS(arg[0], "d") || EQUALS(arg[0], "debug")) {
   1136 	if(Debug == 0)
   1137 	    Debug = 9;
   1138 	else
   1139 	    Debug = 0;
   1140 	VERBOSE("(continue)%s", "");
   1141 	return;
   1142     }
   1143 	/*  ~%[ifc|nostop]  toggles start/stop input control  */
   1144     if( EQUALS(arg[0], "ifc") || EQUALS(arg[0], "nostop") ) {
   1145 	(void)ioctl(Cn, TCGETA, &_Tv);
   1146 	Ifc = !Ifc;
   1147 	if(Ifc == YES)
   1148 	    _Tv.c_iflag |= IXOFF;
   1149 	else
   1150 	    _Tv.c_iflag &= ~IXOFF;
   1151 	(void)ioctl(Cn, TCSETAW, &_Tv);
   1152 	_mode(1);
   1153 	VERBOSE("(ifc %s)", (Ifc ? "enabled" : "disabled"));
   1154 	VERBOSE("(continue)%s", "");
   1155 	return;
   1156     }
   1157 	/*  ~%[ofc|noostop]  toggles start/stop output control  */
   1158     if( EQUALS(arg[0], "ofc") || EQUALS(arg[0], "noostop") ) {
   1159 	(void)ioctl(Cn, TCGETA, &_Tv);
   1160 	Ofc = !Ofc;
   1161 	if(Ofc == YES)
   1162 	    _Tv.c_iflag |= IXON;
   1163 	else
   1164 	    _Tv.c_iflag &= ~IXON;
   1165 	(void)ioctl(Cn, TCSETAW, &_Tv);
   1166 	_mode(1);
   1167 	VERBOSE("(ofc %s)", (Ofc ? "enabled" : "disabled"));
   1168 	VERBOSE("(continue)%s", "");
   1169 	return;
   1170     }
   1171 	/*  ~%divert toggles unsolicited redirection security */
   1172     if( EQUALS(arg[0], "divert") ) {
   1173 	Divert = !Divert;
   1174 	recfork();	/* fork a new child so it knows about change */
   1175 	VERBOSE("(unsolicited diversion %s)", (Divert ? "enabled" : "disabled"));
   1176 	VERBOSE("(continue)%s", "");
   1177 	return;
   1178     }
   1179 	/*  ~%old toggles recognition of old-style '~>:filename' */
   1180     if( EQUALS(arg[0], "old") ) {
   1181 	OldStyle = !OldStyle;
   1182 	recfork();	/* fork a new child so it knows about change */
   1183 	VERBOSE("(old-style diversion %s)", (OldStyle ? "enabled" : "disabled"));
   1184 	VERBOSE("(continue)%s", "");
   1185 	return;
   1186     }
   1187 	/* Change local current directory */
   1188     if(EQUALS(arg[0], "cd")) {
   1189 	if (narg < 2) {
   1190 	    getpath = getenv("HOME");
   1191 	    strlcpy(mypath, getpath, sizeof (mypath));
   1192 	    if(chdir(mypath) < 0) {
   1193 		VERBOSE("Cannot change to %s\r\n", mypath);
   1194 		VERBOSE("(continue)%s", "");
   1195 		return;
   1196 	    }
   1197 	} else if (chdir(arg[1]) < 0) {
   1198 	    VERBOSE("Cannot change to %s\r\n", arg[1]);
   1199 	    VERBOSE("(continue)%s", "");
   1200 	    return;
   1201 	}
   1202 	recfork();	/* fork a new child so it knows about change */
   1203 	VERBOSE("(continue)%s", "");
   1204 	return;
   1205     }
   1206 
   1207    if (arg[0] == (char *) NULL)
   1208        arg[0] = "";
   1209 
   1210     VERBOSE("~%%%s unknown to cu\r\n", arg[0]);
   1211     VERBOSE("(continue)%s", "");
   1212     return;
   1213 }
   1214 
   1215 /***************************************************************
   1216  *	receive: read from remote line, write to fd=1 (TTYOUT)
   1217  *	catch:
   1218  *	~>[>]:file
   1219  *	.
   1220  *	. stuff for file
   1221  *	.
   1222  *	~>	(ends diversion)
   1223  ***************************************************************/
   1224 
   1225 static void
   1226 _receive()
   1227 {
   1228     int silent = NO, file = -1;
   1229     char *p;
   1230     int	tic;
   1231     int for_me = NO;
   1232     char	b[BUFSIZ];
   1233     char	*b_p;
   1234     long	count;
   1235     int		line_ok = 1, rval;
   1236 
   1237     CDEBUG(4,"_receive started\r\n%s", "");
   1238 
   1239     b[0] = '\0';
   1240     b_p = p = b;
   1241 
   1242     while(line_ok) {
   1243 	rval = r_char(Cn);
   1244 	if (rval == NO) {
   1245 	    line_ok = 0;
   1246 	    continue;
   1247 	}
   1248 	if (rval == HUNGUP) {
   1249 	    if (command_line_hups > 0) {
   1250 		CDEBUG(4, "Ignoring device hangup\n%s", "");
   1251 		command_line_hups--;
   1252 		(void) setuid(Euid);	/* reacquire privileges */
   1253 		if (clear_hup(Cn) != SUCCESS) {
   1254 		    DEBUG(4, "Unable to clear hup on device\n%s", "");
   1255 		    line_ok = 0;
   1256 		}
   1257 		(void) setuid(getuid());  /* relinquish privileges */
   1258 	    } else
   1259 		line_ok = 0;
   1260 	    continue;
   1261 	}
   1262 
   1263 	if(silent == NO)    /* ie., if not redirecting from screen */
   1264 	    if(w_char(TTYOUT) == NO)
   1265 		_rcvdead(IOERR);    /* this will exit */
   1266 	/* remove CR's and fill inserted by remote */
   1267 	if(_Cxc == '\0' || _Cxc == RUB || _Cxc == '\r')
   1268 	    continue;
   1269 	*p++ = _Cxc;
   1270 	if(_Cxc != '\n' && (p-b) < BUFSIZ)
   1271 	    continue;
   1272 	/* ****************************************** */
   1273 	/* This code deals with ~%take file diversion */
   1274 	/* ****************************************** */
   1275 	if (b[0] == '~') {
   1276 	    int    append;
   1277 
   1278 	    if (EQUALSN(&b[1],prompt,strlen(prompt))) {
   1279 		b_p = b + 1 + strlen(prompt);
   1280 		for_me = YES;
   1281 	    } else {
   1282 		b_p = b + 1;
   1283 		for_me = NO;
   1284 	    }
   1285 	    if ( (for_me || OldStyle) && (*b_p == '>') ) {
   1286 		/* This is an acceptable '~[uname]>' line */
   1287 		b_p++;
   1288 		if ( (*b_p == '\n') && (silent == YES) ) {
   1289 		    /* end of diversion */
   1290 		    *b_p = '\0';
   1291 		    (void) strcpy(filename, "/dev/null");
   1292 		    if ( file >= 0 && close(file) ) {
   1293 			VERBOSE(gettext(P_Ct_UNDIVERT), b_p);
   1294 			perror(gettext("cu: close failed"));
   1295 			VERBOSE("%s","\r");
   1296 		    }
   1297 		    silent = NO;
   1298 		    blckcnt((long)(-2));
   1299 		    VERBOSE("%s\r\n", b);
   1300 		    VERBOSE(gettext(P_CNTLINES), tic);
   1301 		    VERBOSE(gettext(P_CNTCHAR), count);
   1302 		    file = -1;
   1303 		    p = b;
   1304 		    continue;
   1305 		} else if (*b_p != '\n') {
   1306 		    if ( *b_p == '>' ) {
   1307 			append = 1;
   1308 			b_p++;
   1309 		    }
   1310 		    if ( (for_me || (OldStyle && (*b_p == ':'))) && (silent == NO) ) {
   1311 			/* terminate filename string */
   1312 			*(p-1) = '\0';
   1313 			if ( *b_p == ':' )
   1314 			    b_p++;
   1315 			if ( !EQUALS(filename, b_p) ) {
   1316 			    if ( !Divert  || !EQUALS(filename, "/dev/null") ) {
   1317 				VERBOSE(gettext(P_Bad_DIVERT), b_p);
   1318 				(void) strcpy(filename, "/dev/null");
   1319 				append = 1;
   1320 			    } else {
   1321 				(void) strcpy(filename, b_p);
   1322 			    }
   1323 			}
   1324 			if ( append && ((file=open(filename,O_WRONLY)) >= 0) )
   1325 			    (void)lseek(file, 0L, 2);
   1326 			else
   1327 			    file = creat(filename, PUB_FILEMODE);
   1328 			if (file < 0) {
   1329 			    VERBOSE(gettext(P_Ct_DIVERT), filename);
   1330 			    perror(gettext("cu: open|creat failed"));
   1331 			    VERBOSE("%s","\r");
   1332 			    (void)sleep(5); /* 10 seemed too long*/
   1333 			}
   1334 			silent = YES;
   1335 			count = tic = 0;
   1336 			p = b;
   1337 			continue;
   1338 		    }
   1339 		}
   1340 	    }
   1341 	}
   1342 	/* Regular data, divert if appropriate */
   1343 	if ( silent == YES ) {
   1344 	    if ( file >= 0)
   1345 		(void)write(file, b, (unsigned)(p-b));
   1346 	    count += p-b;	/* tally char count */
   1347 	    ++tic;		/* tally lines */
   1348 	    blckcnt((long)count);
   1349 	}
   1350 	p = b;
   1351     }
   1352     /*
   1353      * we used to tell of lost carrier here, but now
   1354      * defer to _bye() so that escape processes are
   1355      * not interrupted.
   1356      */
   1357     _rcvdead(IOERR);
   1358     return;
   1359 }
   1360 
   1361 /***************************************************************
   1362  *	change the TTY attributes of the users terminal:
   1363  *	0 means restore attributes to pre-cu status.
   1364  *	1 means set `raw' mode for use during cu session.
   1365  *	2 means like 1 but accept interrupts from the keyboard.
   1366  ***************************************************************/
   1367 static void
   1368 _mode(arg)
   1369 {
   1370     int i;
   1371 
   1372     CDEBUG(4,"call _mode(%d)\r\n", arg);
   1373     if(arg == 0) {
   1374 	if ( Saved_termios )
   1375 		(void)ioctl(TTYIN, TCSETSW, &_Tv0s);
   1376 	else if ( Saved_tty ) {
   1377 		_Tv0.c_lflag = _Tv0s.c_lflag;
   1378 		_Tv0.c_oflag = _Tv0s.c_oflag;
   1379 		_Tv0.c_iflag = _Tv0s.c_iflag;
   1380 		_Tv0.c_cflag = _Tv0s.c_cflag;
   1381 		for(i = 0; i < NCC; i++)
   1382 			_Tv0.c_cc[i] = _Tv0s.c_cc[i];
   1383 		(void)ioctl(TTYIN, TCSETAW, &_Tv0);
   1384 	}
   1385     } else {
   1386 	(void)ioctl(TTYIN, TCGETA, &_Tv);
   1387 	if(arg == 1) {
   1388 	    _Tv.c_iflag &= ~(INLCR | ICRNL | IGNCR | IUCLC);
   1389 	    if ( !term_8bit )
   1390 		_Tv.c_iflag |= ISTRIP;
   1391 	    _Tv.c_oflag |= OPOST;
   1392 	    _Tv.c_oflag &= ~(OLCUC | ONLCR | OCRNL | ONOCR | ONLRET);
   1393 	    _Tv.c_lflag &= ~(ICANON | ISIG | ECHO);
   1394 	    if(Ifc == NO)
   1395 		_Tv.c_iflag &= ~IXON;
   1396 	    else
   1397 		_Tv.c_iflag |= IXON;
   1398 	    if(Ofc == NO)
   1399 		_Tv.c_iflag &= ~IXOFF;
   1400 	    else
   1401 		_Tv.c_iflag |= IXOFF;
   1402 	    if(Terminal) {
   1403 		_Tv.c_oflag |= ONLCR;
   1404 		_Tv.c_iflag |= ICRNL;
   1405 	    }
   1406 	    _Tv.c_cc[VEOF] = '\01';
   1407 	    _Tv.c_cc[VEOL] = '\0';
   1408 	}
   1409 	if(arg == 2) {
   1410 	    _Tv.c_iflag |= IXON;
   1411 	    _Tv.c_lflag |= ISIG;
   1412 	}
   1413 	(void)ioctl(TTYIN, TCSETAW, &_Tv);
   1414     }
   1415     return;
   1416 }
   1417 
   1418 
   1419 static pid_t
   1420 dofork()
   1421 {
   1422     int i;
   1423     pid_t x;
   1424 
   1425     for(i = 0; i < 6; ++i) {
   1426 	if((x = fork()) >= 0) {
   1427 	    return(x);
   1428 	}
   1429     }
   1430 
   1431     if(Debug) perror("dofork");
   1432 
   1433     VERBOSE(gettext(P_Ct_FK),"");
   1434     return(x);
   1435 }
   1436 
   1437 static int
   1438 r_char(fd)
   1439 {
   1440     int rtn = 1, rfd;
   1441     char *riobuf;
   1442 
   1443     /* find starting pos in correct buffer in Riobuf	*/
   1444     rfd = RIOFD(fd);
   1445     riobuf = &Riobuf[rfd*WRIOBSZ];
   1446 
   1447     if (Riop[rfd] >= &riobuf[Riocnt[rfd]]) {
   1448 	/* empty read buffer - refill it	*/
   1449 
   1450 	/*	flush any waiting output	*/
   1451 	if ( (wioflsh(Cn) == NO ) || (wioflsh(TTYOUT) == NO) )
   1452 	    return(NO);
   1453 
   1454 	while((rtn = read(fd, riobuf, WRIOBSZ)) < 0){
   1455 	    if(errno == EINTR) {
   1456 		/* onintrpt() called asynchronously before this line */
   1457 		if(Intrupt == YES) {
   1458 		    /* got a BREAK */
   1459 		    _Cxc = '\0';
   1460 		    return(YES);
   1461 		} else {
   1462 		    /*a signal other than interrupt*/
   1463 		    /*received during read*/
   1464 		    continue;
   1465 		}
   1466 	    } else {
   1467 		CDEBUG(4,"got read error, not EINTR\n\r%s", "");
   1468 		break;			/* something wrong */
   1469 	    }
   1470 	}
   1471 	if (rtn > 0) {
   1472 	    /* reset current position in buffer	*/
   1473 	    /* and count of available chars		*/
   1474 	    Riop[rfd] = riobuf;
   1475 	    Riocnt[rfd] = rtn;
   1476 	}
   1477     }
   1478 
   1479     if ( rtn > 0 ) {
   1480 	_Cxc = *(Riop[rfd]++) & RMASK(fd);	/* mask off appropriate bits */
   1481 	return(YES);
   1482     } else if (rtn == 0) {
   1483 	_Cxc = '\0';
   1484 	return (HUNGUP);
   1485     } else {
   1486 	_Cxc = '\0';
   1487 	return(NO);
   1488     }
   1489 }
   1490 
   1491 static int
   1492 w_char(fd)
   1493 {
   1494     int wfd;
   1495     char *wiobuf;
   1496 
   1497     /* find starting pos in correct buffer in Wiobuf	*/
   1498     wfd = WIOFD(fd);
   1499     wiobuf = &Wiobuf[wfd*WRIOBSZ];
   1500 
   1501     if (Wiop[wfd] >= &wiobuf[WRIOBSZ]) {
   1502 	/* full output buffer - flush it */
   1503 	if ( wioflsh(fd) == NO )
   1504 	    return(NO);
   1505     }
   1506     *(Wiop[wfd]++) = _Cxc & WMASK(fd);	/* mask off appropriate bits */
   1507     return(YES);
   1508 }
   1509 
   1510 /* wioflsh	flush output buffer	*/
   1511 static int
   1512 wioflsh(fd)
   1513 int fd;
   1514 {
   1515     int wfd;
   1516     char *wiobuf;
   1517 
   1518     /* find starting pos in correct buffer in Wiobuf	*/
   1519     wfd = WIOFD(fd);
   1520     wiobuf = &Wiobuf[wfd*WRIOBSZ];
   1521 
   1522     if (Wiop[wfd] > wiobuf) {
   1523 	/* there's something in the buffer */
   1524 	while(write(fd, wiobuf, (Wiop[wfd] - wiobuf)) < 0) {
   1525 	    if(errno == EINTR) {
   1526 		if(Intrupt == YES) {
   1527 		    VERBOSE("\ncu: Output blocked\r\n%s", "");
   1528 		    _quit(IOERR);
   1529 		} else
   1530 		    continue;	/* alarm went off */
   1531 	    } else {
   1532 		Wiop[wfd] = wiobuf;
   1533 		return(NO);			/* bad news */
   1534 	    }
   1535 	}
   1536     }
   1537     Wiop[wfd] = wiobuf;
   1538     return(YES);
   1539 }
   1540 
   1541 
   1542 static void
   1543 _w_str(string)
   1544 char *string;
   1545 {
   1546     int len;
   1547 
   1548     len = strlen(string);
   1549     if ( write(Cn, string, (unsigned)len) != len )
   1550 	VERBOSE(gettext(P_LINE_GONE),"");
   1551     return;
   1552 }
   1553 
   1554 /* ARGSUSED */
   1555 static void
   1556 _onintrpt(sig)
   1557 int sig;
   1558 {
   1559     (void)signal(SIGINT, _onintrpt);
   1560     (void)signal(SIGQUIT, _onintrpt);
   1561     Intrupt = YES;
   1562     return;
   1563 }
   1564 
   1565 static void
   1566 _rcvdead(arg)	/* this is executed only in the receive process */
   1567 int arg;
   1568 {
   1569     CDEBUG(4,"call _rcvdead(%d)\r\n", arg);
   1570     (void)kill(getppid(), SIGUSR1);
   1571     exit((arg == SIGHUP)? SIGHUP: arg);
   1572     /*NOTREACHED*/
   1573 }
   1574 
   1575 static void
   1576 _quit(arg)	/* this is executed only in the parent process */
   1577 int arg;
   1578 {
   1579     CDEBUG(4,"call _quit(%d)\r\n", arg);
   1580     (void)kill(Child, SIGKILL);
   1581     _bye(arg);
   1582     /*NOTREACHED*/
   1583 }
   1584 
   1585 static void
   1586 _bye(arg)	/* this is executed only in the parent proccess */
   1587 int arg;
   1588 {
   1589     int status;
   1590     pid_t obit;
   1591 
   1592     if ( Shell > 0 )
   1593 	while ((obit = wait(&status)) != Shell) {
   1594 	    if (obit == -1 && errno != EINTR)
   1595 		break;
   1596 	    /* _receive (Child) may have ended - check it out */
   1597 	    if (obit == Child)
   1598 		Child = 0;
   1599 	}
   1600 
   1601     /* give user customary message after escape command returns */
   1602     if (arg == SIGUSR1)
   1603 	VERBOSE("\r\nLost Carrier\r\n%s", "");
   1604 
   1605     CDEBUG(4,"call _bye(%d)\r\n", arg);
   1606 
   1607     (void)signal(SIGINT, SIG_IGN);
   1608     (void)signal(SIGQUIT, SIG_IGN);
   1609     /* if _receive() ended already, don't wait for it again */
   1610     if ( Child != 0 )
   1611 	while ((obit = wait(&status)) != Child)
   1612 	    if (obit == -1 && errno != EINTR)
   1613 		break;
   1614     VERBOSE("\r\nDisconnected\007\r\n%s", "");
   1615     cleanup((arg == SIGUSR1)? (status >>= 8): arg);
   1616     /*NOTREACHED*/
   1617 }
   1618 
   1619 
   1620 
   1621 void
   1622 cleanup(code) 	/*this is executed only in the parent process*/
   1623 int code;	/*Closes device; removes lock files	  */
   1624 {
   1625 
   1626     CDEBUG(4,"call cleanup(%d)\r\n", code);
   1627 
   1628     if (Docmd) {
   1629 	if (Child > 0)
   1630 	    (void)kill(Child, SIGTERM);
   1631     } else
   1632 	(void) setuid(Euid);
   1633     if(Cn > 0) {
   1634 	fchmod(Cn, Dev_mode);
   1635 	fd_rmlock(Cn);
   1636 	(void)close(Cn);
   1637     }
   1638 
   1639 
   1640     rmlock((char*) NULL);	/* remove all lock files for this process */
   1641     if (!Docmd)
   1642 	_mode(0);
   1643     exit(code);		/* code=negative for signal causing disconnect*/
   1644 }
   1645 
   1646 
   1647 
   1648 void
   1649 tdmp(arg)
   1650 int arg;
   1651 {
   1652 
   1653     struct termio xv;
   1654     int i;
   1655 
   1656     VERBOSE("\rdevice status for fd=%d\r\n", arg);
   1657     VERBOSE("F_GETFL=%o,", fcntl(arg, F_GETFL,1));
   1658     if(ioctl(arg, TCGETA, &xv) < 0) {
   1659 	char	buf[100];
   1660 	i = errno;
   1661 	(void)snprintf(buf, sizeof (buf), gettext("\rtdmp for fd=%d"), arg);
   1662 	errno = i;
   1663 	perror(buf);
   1664 	return;
   1665     }
   1666     VERBOSE("iflag=`%o',", xv.c_iflag);
   1667     VERBOSE("oflag=`%o',", xv.c_oflag);
   1668     VERBOSE("cflag=`%o',", xv.c_cflag);
   1669     VERBOSE("lflag=`%o',", xv.c_lflag);
   1670     VERBOSE("line=`%o'\r\n", xv.c_line);
   1671     VERBOSE("cc[0]=`%o',",  xv.c_cc[0]);
   1672     for(i=1; i<8; ++i) {
   1673 	VERBOSE("[%d]=", i);
   1674 	VERBOSE("`%o',",xv.c_cc[i]);
   1675     }
   1676     VERBOSE("\r\n%s", "");
   1677     return;
   1678 }
   1679 
   1680 
   1681 
   1682 static void
   1683 sysname(name)
   1684 char * name;
   1685 {
   1686 
   1687     char *s;
   1688 
   1689     if(uname(&utsn) < 0)
   1690 	s = "Local";
   1691     else
   1692 	s = utsn.nodename;
   1693 
   1694     strcpy(name, s);
   1695     return;
   1696 }
   1697 
   1698 
   1699 static void
   1700 blckcnt(count)
   1701 long count;
   1702 {
   1703     static long lcharcnt = 0;
   1704     long c1, c2;
   1705     int i;
   1706     char c;
   1707 
   1708     if(count == (long) (-1)) {	/* initialization call */
   1709 	lcharcnt = 0;
   1710 	return;
   1711     }
   1712     c1 = lcharcnt/BUFSIZ;
   1713     if(count != (long)(-2)) {	/* regular call */
   1714 	c2 = count/BUFSIZ;
   1715 	for(i = c1; i++ < c2;) {
   1716 	    c = '0' + i%10;
   1717 	    write(2, &c, 1);
   1718 	    if(i%NPL == 0)
   1719 		write(2, "\n\r", 2);
   1720 	}
   1721 	lcharcnt = count;
   1722     } else {
   1723 	c2 = (lcharcnt + BUFSIZ -1)/BUFSIZ;
   1724 	if(c1 != c2)
   1725 	    write(2, "+\n\r", 3);
   1726 	else if(c2%NPL != 0)
   1727 	    write(2, "\n\r", 2);
   1728 	lcharcnt = 0;
   1729     }
   1730     return;
   1731 }
   1732 
   1733 /*VARARGS*/
   1734 /*ARGSUSED*/
   1735 void
   1736 assert (s1, s2, i1, s3, i2)
   1737 char *s1, *s2, *s3;
   1738 int i1, i2;
   1739 { }		/* for ASSERT in gnamef.c */
   1740 
   1741 /*ARGSUSED*/
   1742 void
   1743 logent (s1, s2)
   1744 char *s1, *s2;
   1745 { }		/* so we can load ulockf() */
   1746