xref: /titanic_41/usr/src/cmd/bnu/cu.c (revision 70025d765b044c6d8594bb965a2247a61e991a99)
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