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