xref: /illumos-gate/usr/src/cmd/bnu/cu.c (revision d4039345c8fe6e54a31d17d91e86e393fdcf401b)
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 /*
31  * cu [-cdevice] [-sspeed] [-lline] [-bbits] [-h] [-t] [-d] [-n]
32  *		[-o|-e] [-L] [-C] telno | systemname [local-cmd]
33  *
34  *	legal baud rates: 300, 1200, 2400, 4800, 9600, 19200, 38400.
35  *
36  *	-c is used to specify which device will be used for making the
37  *		call.  The device argument is compared to the Type (first)
38  *		field in the Devices file, and only those records that
39  *		match will be used to make the call.  Either -d or -t
40  *		would be more intuitive options designations, but they
41  *		are already in use.
42  *	-l is for specifying a line unit from the file whose
43  *		name is defined in /etc/uucp/Devices.
44  *	-b is for forcing the number of bits per character processed on
45  *		the connection. Valid values are '7' or '8'.
46  *	-h is for half-duplex (local echoing).
47  *	-t is for adding CR to LF on output to remote (for terminals).
48  *	-d can be used  to get some tracing & diagnostics.
49  *	-o or -e is for odd or even parity on transmission to remote.
50  *	-n will request the phone number from the user.
51  *	-L will cause cu to go through the login chat sequence in the
52  *		Systems file.
53  *	-C will cause cu to run the local command specified at the end
54  *		of the command line, instead of entering interactive mode.
55  *	Telno is a telephone number with `=' for secondary dial-tone.
56  *	If "-l dev" is used, speed is taken from /etc/uucp/Devices.
57  *	Only systemnames that are included in /etc/uucp/Systems may
58  *	be used.
59  *
60  *	Escape with `~' at beginning of line:
61  *
62  *	~.	quit,
63  *
64  *	~![cmd]			execute shell (or 'cmd') locally,
65  *
66  *	~$cmd			execute 'cmd' locally, stdout to remote,
67  *
68  *	~%break	(alias ~%b)	transmit BREAK to remote,
69  *	~%cd [dir]		change directory to $HOME (or 'dir'),
70  *	~%debug (alias ~%d)	toggles on/off the program debug trace,
71  *	~%divert		allow unsolicited diversions to files,
72  *	~%ifc (alias ~%nostop)	toggles on/off the DC3/DC1 input control,
73  *	~%ofc (alias ~%noostop)	toggles on/off the DC3/DC1 output control,
74  *		(certain remote systems cannot cope with DC3 or DC1).
75  *	~%old			recognize old style silent diversions,
76  *	~%put from [to]		put file from local to remote,
77  *	~%take from [to]	take file from remote to local,
78  *
79  *	~l			dump communication line ioctl settings,
80  *	~t			dump terminal ioctl settings.
81  *
82  *	Silent diversions are enabled only for use with the ~%take
83  *	command by default for security reasons. Unsolicited diversions
84  *	may be enabled using the ~%divert toggle. The 'new-style'
85  *	diversion syntax is "~[local]>:filename", and is terminaled
86  *	by "~[local]>", where 'local' is the nodename of the local
87  *	system. This enables ~%take to operate properly when cu
88  *	is used over multiple hops. 'old-style' diversion syntax may
89  *	be enabled using the ~%old toggle. ('old-style' diversion
90  *	should be avoided!)
91  *
92  *	Cu no longer uses dial.c to reach the remote.  Instead, cu places
93  *	a telephone call to a remote system through the uucp conn() routine
94  *	when the user picks the systemname option or through altconn()--
95  *	which bypasses /etc/uucp/Systems -- if a telno or direct
96  *	line is chosen. The line termio attributes are set in fixline(),
97  *	before the remote connection is made.  As a device-lockout semaphore
98  *	mechanism, uucp creates an entry in /var/spool/locks whose name is
99  *	LK.<MAJ>.<maj>.<min> where MAJ is the major device of the
100  *	filesystem containing the device, and <maj> and <min> are the
101  *	major and minor of the device.
102  *	When cu terminates, for whatever reason, cleanup() must be
103  *	called to "release" the device, and clean up entries from
104  *	the locks directory.  Cu runs with uucp ownership, and thus provides
105  *	extra insurance that lock files will not be left around.
106  */
107 
108 #include "uucp.h"
109 #include <locale.h>
110 #include <stropts.h>
111 
112 #define	MID	BUFSIZ/2	/* mnemonic */
113 #define	RUB	'\177'		/* mnemonic */
114 #define	XON	'\21'		/* mnemonic */
115 #define	XOFF	'\23'		/* mnemonic */
116 #define	TTYIN	0		/* mnemonic */
117 #define	TTYOUT	1		/* mnemonic */
118 #define	TTYERR	2		/* mnemonic */
119 #define	HUNGUP  2
120 #define	YES	1		/* mnemonic */
121 #define	NO	0		/* mnemonic */
122 #define	IOERR	4		/* exit code */
123 #define	MAXPATH	100
124 #define	NPL	50
125 
126 int Sflag=0;
127 int Cn;				/*fd for remote comm line */
128 jmp_buf Sjbuf;			/*needed by uucp routines*/
129 
130 /*	io buffering	*/
131 /*	Wiobuf contains, in effect, 3 write buffers (to remote, to tty	*/
132 /*	stdout, and to tty stderr) and Riobuf contains 2 read buffers	*/
133 /*	(from remote, from tty).  [WR]IOFD decides which one to use.	*/
134 /*	[RW]iop holds current position in each.				*/
135 #define	WIOFD(fd)	(fd == TTYOUT ? 0 : (fd == Cn ? 1 : 2))
136 #define	RIOFD(fd)	(fd == TTYIN ? 0 : 1)
137 #define	WMASK(fd)	(fd == Cn ? line_mask : term_mask)
138 #define	RMASK(fd)	(fd == Cn ? line_mask : term_mask)
139 #define	WRIOBSZ 256
140 static char Riobuf[2*WRIOBSZ];
141 static char Wiobuf[3*WRIOBSZ];
142 static int Riocnt[2] = {0, 0};
143 static char *Riop[2];
144 static char *Wiop[3];
145 
146 extern int optind;		/* variable in getopt() */
147 
148 extern char
149 	*optarg;
150 
151 static struct call Cucall;	/* call structure for altconn()	*/
152 
153 static int Saved_tty;		/* was TCGETAW of _Tv0 successful?	*/
154 static int Saved_termios;	/* was TCGETSW of _Tv0 successful?	*/
155 static struct termio _Tv, _Tv0;	/* for saving, changing TTY atributes */
156 static struct termios _Tv0s;	/* for saving, changing TTY atributes */
157 static struct termio _Lv;	/* attributes for the line to remote */
158 static struct termios _Lvs;	/* attributes for the line to remote */
159 static char prompt[BUFSIZ]= "[";
160 static struct utsname utsn;
161 static int command_line_hups = 0;
162 
163 static char filename[BUFSIZ] = "/dev/null";
164 
165 static char
166 	_Cxc,			/* place into which we do character io*/
167 	_Tintr,			/* current input INTR */
168 	_Tquit,			/* current input QUIT */
169 	_Terase,		/* current input ERASE */
170 	_Tkill,			/* current input KILL */
171 	_Teol,			/* current secondary input EOL */
172 	_Myeof,			/* current input EOF */
173 	term_mask,		/* mask value for local terminal */
174 	line_mask;		/* mask value for remote line */
175 				/* either '0177' or '0377' */
176 
177 int
178 	Echoe,			/* save users ECHOE bit */
179 	Echok,			/* save users ECHOK bit */
180 	Intrupt=NO,		/* interrupt indicator */
181 	Ifc=YES,		/* NO means remote can't XON/XOFF */
182 	Ofc=YES,		/* NO means local can't XON/XOFF */
183 	Rtn_code=0,		/* default return code */
184 	Divert=NO,		/* don't allow unsolicited redirection */
185 	OldStyle=NO,		/* don't handle old '~>:filename' syntax */
186 				/* this will be mandatory in SVR4.1 */
187 	Takeflag=NO,		/* indicates a ~%take is in progress */
188 	Dologin=NO,		/* go through the login chat sequence */
189 	Docmd=NO;		/* execute command instead of interactive cu */
190 
191 EXTERN int			/* These are initialized in line.c */
192 	Terminal,		/* flag; remote is a terminal */
193 	Oddflag,		/* flag- odd parity option*/
194 	Evenflag,		/* flag- even parity option*/
195 	Duplex,			/* Unix= full duplex=YES; half = NO */
196 	term_8bit,		/* is terminal set for 8 bit processing */
197 	line_8bit;		/* is line set for 8 bit processing */
198 
199 EXTERN int clear_hup();
200 
201 pid_t
202 	Child,			/* pid for receive process */
203 	Shell;			/* pid for escape process */
204 
205 static pid_t
206 	dofork();		/* fork and return pid */
207 
208 static int
209 	r_char(),		/* local io routine */
210 	w_char(),		/* local io routine */
211 	wioflsh();
212 
213 static void
214 	_onintrpt(),		/* interrupt routines */
215 	_rcvdead(),
216 	_quit(),
217 	_bye();
218 
219 extern void	cleanup();
220 extern void	tdmp();
221 extern int conn(), altconn(), transmit(), tilda();
222 
223 static void
224 	recfork(),
225 	sysname(),
226 	blckcnt(),
227 	_flush(),
228 	_shell(),
229 	_dopercen(),
230 	_receive(),
231 	_mode(),
232 	_w_str();
233 
234 extern char *Myline;	/* flag to force the requested line to be used  */
235 extern char *Mytype;	/* flag to force requested line type to be used
236 			 * rddev() will compare the string to the D_TYPE
237 			 * (first) field of the Devices record and skip any
238 			 * records where they are not equal. Mytype is set
239 			 * to point to the argument of the -c option from
240 			 * the command line. */
241 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";
242 static char *P_CON_FAILED = "Connect failed: %s\r\n";
243 static char *P_Ct_OPEN = "Cannot open: %s\r\n";
244 static char *P_LINE_GONE = "Remote line gone\r\n";
245 static char *P_Ct_EXSH = "Can't execute shell\r\n";
246 static char *P_Ct_DIVERT = "Can't divert to %s\r\n";
247 static char *P_Ct_UNDIVERT = "Can't end diversion to %s\r\n";
248 static char *P_Bad_DIVERT = "Won't divert to %s. Unsolicited.\r\n";
249 static char *P_STARTWITH = "Use `~~' to start line with `~'\r\n";
250 static char *P_CNTAFTER = "File transmission interrupted after %ld bytes.\r\n";
251 static char *P_CNTLINES = "%d lines/";
252 static char *P_CNTCHAR = "%ld characters\r\n";
253 static char *P_FILEINTR = "File transmission interrupted\r\n";
254 static char *P_Ct_FK = "Can't fork -- try later\r\n";
255 static char *P_Ct_SPECIAL = "r\nCan't transmit special character `%#o'\r\n";
256 static char *P_TOOLONG = "\nLine too long\r\n";
257 static char *P_IOERR = "r\nIO error\r\n";
258 static char *P_USECMD = "Use `~$'cmd \r\n";
259 #ifdef forfutureuse
260 static char *P_USEPLUSCMD ="Use `~+'cmd \r\n";
261 #endif
262 #ifdef u3b
263 static char *P_NOTERMSTAT = "Can't get terminal status\r\n";
264 static char *P_3BCONSOLE = "Sorry, you can't cu from a 3B console\r\n";
265 #endif
266 static char *P_TELLENGTH = "Telno cannot exceed 58 digits!\r\n";
267 
268 /***************************************************************
269  *	main: get command line args, establish connection, and fork.
270  *	Child invokes "receive" to read from remote & write to TTY.
271  *	Main line invokes "transmit" to read TTY & write to remote.
272  ***************************************************************/
273 
274 int
275 main(argc, argv)
276 int argc;
277 char *argv[];
278 {
279     extern void setservice();
280     extern int sysaccess();
281     char s[MAXPH];
282     char *string;
283     int i;
284     int errflag=0;
285     int lflag=0;
286     int nflag=0;
287     int systemname = 0;
288     char vdisable;
289 
290     /* Set locale environment variables local definitions */
291     (void) setlocale(LC_ALL, "");
292 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
293 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it wasn't */
294 #endif
295     (void) textdomain(TEXT_DOMAIN);
296 
297     Riop[0] = &Riobuf[0];
298     Riop[1] = &Riobuf[WRIOBSZ];
299     Wiop[0] = &Wiobuf[0];
300     Wiop[1] = &Wiobuf[WRIOBSZ];
301     Wiop[2] = &Wiobuf[2*WRIOBSZ];
302 
303     Verbose = 1;		/*for uucp callers,  dialers feedback*/
304     if ((string = strrchr(argv[0], '/')) != NULL)
305 	string++;
306     else
307 	string = argv[0];
308     if (strlcpy(Progname, string, NAMESIZE) >= NAMESIZE) {
309 	errno = ENAMETOOLONG;
310 	perror("cu");
311 	exit(1);
312     }
313     setservice(Progname);
314     if ( sysaccess(EACCESS_SYSTEMS) != 0 ) {
315 	(void)fprintf(stderr,
316 	     gettext("%s: Cannot read Systems files\n"), Progname);
317 	exit(1);
318     }
319     if ( sysaccess(EACCESS_DEVICES) != 0 ) {
320 	(void)fprintf(stderr,
321 	     gettext("%s: Cannot read Devices files\n"), Progname);
322 	exit(1);
323     }
324     if ( sysaccess(EACCESS_DIALERS) != 0 ) {
325 	(void)fprintf(stderr,
326 	    gettext("%s: Cannot read Dialers files\n"), Progname);
327 	exit(1);
328     }
329 
330     Cucall.speed = "Any";	/*default speed*/
331     Cucall.line = CNULL;
332     Cucall.telno = CNULL;
333     Cucall.type = CNULL;
334 
335 /*Flags for -h, -t, -e, and -o options set here; corresponding line attributes*/
336 /*are set in fixline() in culine.c before remote connection is made	   */
337 
338     while((i = getopt(argc, argv, "dhteons:l:c:b:LCH")) != EOF)
339 	switch(i) {
340 	    case 'd':
341 		Debug = 9; /*turns on uucp debugging-level 9*/
342 		break;
343 	    case 'h':
344 		Duplex  = NO;
345 		Ifc = NO;
346 		Ofc = NO;
347 		break;
348 	    case 't':
349 		Terminal = YES;
350 		break;
351 	    case 'e':
352 		if ( Oddflag ) {
353 		    (void)fprintf(stderr,
354 			gettext("%s: Cannot have both even and odd parity\n"),
355 			argv[0]);
356 		    exit(1);
357 		}
358 		Evenflag = 1;
359 		break;
360 	    case 'o':
361 		if ( Evenflag ) {
362 		    (void)fprintf(stderr,
363 			gettext("%s: Cannot have both even and odd parity\n"),
364 			argv[0]);
365 		    exit(1);
366 		}
367 		Oddflag = 1;
368 		break;
369 	    case 'n':
370 		nflag++;
371 		printf(gettext("Please enter the number: "));
372 		/* Read line from stdin, remove trailing newline, if any */
373 		if (fgets(s, sizeof(s), stdin) != NULL &&
374 			strchr(s, '\n') != NULL)
375 		   s[strlen(s)-1] = '\0';
376 		break;
377 	    case 's':
378 		Sflag++;
379 		Cucall.speed = optarg;
380 		break;
381 	    case 'l':
382 		lflag++;
383 		Cucall.line = optarg;
384 		break;
385 	    case 'c':
386 		Cucall.type = optarg;
387 		Mytype = optarg;
388 		break;
389 	    case 'b':
390 		line_8bit = ((*optarg=='7') ? NO : ((*optarg=='8') ? YES : -1));
391 		if ( line_8bit == -1 ) {
392 		    (void) fprintf(stderr,
393 			gettext("%s: b option value must be '7' or '8'\n"),
394 			argv[0]);
395 		    exit(1);
396 		}
397 		break;
398 	    case 'L':
399 		Dologin++;
400 		break;
401 	    case 'C':
402 		Docmd++;
403 		break;
404 	    case 'H':
405 		command_line_hups++;
406 		break;
407 	    case '?':
408 		++errflag;
409 	}
410 
411 #ifdef  u3b
412     {
413     struct stat buff;
414     if(fstat(TTYIN, &buff) < 0) {
415 	VERBOSE(gettext(P_NOTERMSTAT),"");
416 	exit(1);
417     } else if ( (buff.st_mode & S_IFMT) == S_IFCHR && buff.st_rdev == 0 ) {
418 	VERBOSE(gettext(P_3BCONSOLE),"");
419 	exit(1);
420 	}
421     }
422 #endif
423 
424     if((optind < argc && optind > 0) || (nflag && optind > 0)) {
425 	if(nflag)
426 	    string=s;
427 	else
428 	    string = strdup(argv[optind++]);
429 	Cucall.telno = string;
430 	if ( strlen(string) != strspn(string, "0123456789=-*#") ) {
431 	    /* if it's not a legitimate telno, then it should be a systemname */
432 	    if ( nflag ) {
433 		(void)fprintf(stderr, gettext("%s: Bad phone number %s\n"),
434 				argv[0], string);
435 		(void) fprintf(stderr, gettext("Phone numbers may contain "
436 		    "only the digits 0 through 9 and the special\n"
437 		    "characters =, -, * and #.\n"));
438 		exit(1);
439 	    }
440 	    systemname++;
441 	}
442     } else
443 	if(Cucall.line == CNULL)   /*if none of above, must be direct */
444 	    ++errflag;
445 
446     if(errflag) {
447 	VERBOSE(gettext(P_USAGE), argv[0]);
448 	exit(1);
449     }
450 
451     if ((Cucall.telno != CNULL) &&
452 		(strlen(Cucall.telno) >= (size_t)(MAXPH - 1))) {
453 	VERBOSE(gettext(P_TELLENGTH),"");
454 	exit(0);
455     }
456 
457     /* save initial tty state */
458     if (!(Saved_termios = ( ioctl(TTYIN, TCGETS, &_Tv0s) >= 0 ))) {
459 	Saved_tty = ( ioctl(TTYIN, TCGETA, &_Tv0) == 0 );
460 	_Tv0s.c_lflag = _Tv0.c_lflag;
461 	_Tv0s.c_oflag = _Tv0.c_oflag;
462 	_Tv0s.c_iflag = _Tv0.c_iflag;
463 	_Tv0s.c_cflag = _Tv0.c_cflag;
464 	for(i = 0; i < NCC; i++)
465 		_Tv0s.c_cc[i] = _Tv0.c_cc[i];
466     }
467 
468     if (Saved_termios || Saved_tty) {
469 	char *p;
470 
471 	/*
472 	 * We consider the terminal to be in 8 bit mode only if cs8 is set,
473 	 * istrip is not set, and we're not in the "C" locale.  The "C"
474 	 * locale is by definition 7 bit only.  This provides reasonable
475 	 * compatibility when running in the "C" locale (currently the default)
476 	 * and connecting to other systems, which are most often 7 bit systems.
477 	 */
478 	term_8bit = ( (_Tv0s.c_cflag & CS8) && !(_Tv0s.c_iflag & ISTRIP) &&
479 	  ((p = setlocale(LC_CTYPE, NULL)) != NULL) && (strcmp(p, "C") != 0) );
480 	if ( !Oddflag && !Evenflag )
481 	    if (_Tv0s.c_cflag & PARENB)
482 		if (_Tv0s.c_cflag & PARODD)
483 		    Oddflag = 1;
484 		else
485 		    Evenflag = 1;
486     }
487 
488     if (line_8bit == -1)
489 	line_8bit = term_8bit;
490 
491     term_mask = ( term_8bit ? 0377 : 0177 );
492     line_mask = ( line_8bit ? 0377 : 0177 );
493 
494     /* if not set, use the POSIX disabled designation */
495 #ifdef _POSIX_VDISABLE
496     vdisable = _POSIX_VDISABLE;
497 #else
498     vdisable = fpathconf(TTYIN, _PC_VDISABLE);
499 #endif
500     _Tintr = _Tv0s.c_cc[VINTR] ? _Tv0s.c_cc[VINTR] : vdisable;
501     _Tquit = _Tv0s.c_cc[VQUIT] ? _Tv0s.c_cc[VQUIT] : vdisable;
502     _Terase = _Tv0s.c_cc[VERASE] ? _Tv0s.c_cc[VERASE] : vdisable;
503     _Tkill = _Tv0s.c_cc[VKILL] ? _Tv0s.c_cc[VKILL] : vdisable;
504     _Teol = _Tv0s.c_cc[VEOL] ? _Tv0s.c_cc[VEOL] : vdisable;
505     _Myeof = _Tv0s.c_cc[VEOF] ? _Tv0s.c_cc[VEOF] : '\04';
506     Echoe = _Tv0s.c_lflag & ECHOE;
507     Echok = _Tv0s.c_lflag & ECHOK;
508 
509     (void)signal(SIGHUP, cleanup);
510     (void)signal(SIGQUIT, cleanup);
511     (void)signal(SIGINT, cleanup);
512 
513 /* place call to system; if "cu systemname", use conn() from uucp
514    directly.  Otherwise, use altconn() which dummies in the
515    Systems file line.
516 */
517 
518     if(systemname) {
519 	if ( lflag )
520 	    (void)fprintf(stderr,
521 	        gettext("%s: Warning: -l flag ignored when system name used\n"),
522 	        argv[0]);
523 	if ( Sflag )
524 	    (void)fprintf(stderr,
525 	        gettext("%s: Warning: -s flag ignored when system name used\n"),
526 	        argv[0]);
527 	Cn = conn(string);
528 	if ( (Cn < 0) && (Cucall.type != CNULL) )
529 	    Cn = altconn(&Cucall);
530     } else
531 	Cn = altconn(&Cucall);
532 
533     if(Cn < 0) {
534 	VERBOSE(gettext(P_CON_FAILED),UERRORTEXT);
535 	cleanup(-Cn);
536     } else {
537 	struct stat Cnsbuf;
538 	if ( fstat(Cn, &Cnsbuf) == 0 )
539 	    Dev_mode = Cnsbuf.st_mode;
540 	else
541 	    Dev_mode = R_DEVICEMODE;
542 	fchmod(Cn, M_DEVICEMODE);
543     }
544 
545     if ((Docmd) && (argv[optind] == NULL)) {
546         (void) fprintf(stderr,gettext("cu: local cmd is required, -C is ignored.\n"));
547         VERBOSE(gettext(P_USAGE), argv[0]);
548         Docmd=NO;
549     }
550 
551     if (!Docmd) {
552 	Euid = geteuid();
553 	if((setuid(getuid()) < 0) || (setgid(getgid()) < 0)) {
554 	    VERBOSE("Unable to setuid/gid\n%s", "");
555 	    cleanup(101);
556 	}
557     }
558 
559     if(Debug)
560 	tdmp(Cn);
561 
562     /* At this point succeeded in getting an open communication line	*/
563     /* Conn() takes care of closing the Systems file			*/
564 
565     if (!Docmd) {
566 	(void)signal(SIGINT,_onintrpt);
567 	_mode(1);			/* put terminal in `raw' mode */
568 	VERBOSE("Connected\007\r\n%s", "");	/*bell!*/
569 
570 	/* must catch signals before fork.  if not and if _receive()	*/
571 	/* fails in just the right (wrong?) way, _rcvdead() can be	*/
572 	/* called and do "kill(getppid(),SIGUSR1);" before parent	*/
573 	/* has done calls to signal() after recfork().			*/
574 	(void)signal(SIGUSR1, _bye);
575 	(void)signal(SIGHUP, cleanup);
576 	(void)signal(SIGQUIT, _onintrpt);
577 
578 	sysname(&prompt[1]);	/* set up system name prompt */
579 	(void) strcat(prompt, "]");
580 
581 	recfork();		/* checks for child == 0 */
582 
583 	if(Child > 0) {
584 	    /*
585 	     * Because the child counts hangups for the -H flag,
586 	     * and because we fork a new child when doing (e.g.)
587 	     * ~%take, we assume the first child we fork has
588 	     * processed all the hangups and we reset the count here.
589 	     * We really should pass the remaining count back from
590 	     * the child to the parent when we kill the child.
591 	     */
592 	    command_line_hups = 0;
593 	    Rtn_code = transmit();
594 	    _quit(Rtn_code);
595 	    /*NOTREACHED*/
596 	}
597     } else {
598 	/*
599 	 * Fork a child to run the specified command,
600 	 * wait for it to finish, and clean up.
601 	 */
602 	Child = dofork();
603 	if (Child == 0) {
604 	    close(0);
605 	    close(1);
606 	    dup(Cn);
607 	    dup(Cn);
608 	    close(Cn);
609 	    setgid(getgid());
610 	    setuid(getuid());
611 	    execvp(argv[optind], &argv[optind]);
612 	    exit(-1);
613 	    /* NOTREACHED */
614 	}
615 	wait(0);
616 	/* XXX - should return wait status as our exit code */
617     }
618     cleanup(Cn);
619     /*NOTREACHED*/
620 	return (0);
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     char *p;
669     int escape;
670     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 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 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     int silent = NO, file = -1;
1226     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     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 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     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     long c1, c2;
1702     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