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