xref: /illumos-gate/usr/src/cmd/tip/cmds.c (revision 94e1761e)
1 /*
2  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright (c) 1983 Regents of the University of California.
8  * All rights reserved.  The Berkeley software License Agreement
9  * specifies the terms and conditions for redistribution.
10  */
11 
12 #pragma ident	"%Z%%M%	%I%	%E% SMI"
13 
14 #include "tip.h"
15 #include <limits.h>
16 #ifdef USG
17 #include <unistd.h>
18 #else
19 #include <vfork.h>
20 #endif
21 
22 /*
23  * tip
24  *
25  * miscellaneous commands
26  */
27 
28 int	quant[] = { 60, 60, 24 };
29 
30 char	null = '\0';
31 char	*sep[] = { "second", "minute", "hour" };
32 static	char *argv[10];		/* argument vector for take and put */
33 
34 sigjmp_buf intbuf;		/* for interrupts and timeouts */
35 
36 void	timeout(void);		/* timeout function called on alarm */
37 void	intcopy(void);		/* interrupt routine for file transfers */
38 void	transfer(char *, int, char *);
39 void	transmit(FILE *, char *, char *);
40 void	send(char);
41 void	execute(char *);
42 void	prtime(char *, time_t);
43 void	hardwareflow(char *);
44 void	intr(char *);
45 int	args(char *, char *[], size_t);
46 int	anyof(char *, char *);
47 
48 /*
49  * FTP - remote ==> local
50  *  get a file from the remote host
51  */
52 void
53 getfl(int c)
54 {
55 	char buf[256], *cp;
56 
57 	(void) putchar(c);
58 	/*
59 	 * get the UNIX receiving file's name
60 	 */
61 	if (prompt("Local file name? ", copyname, sizeof (copyname)))
62 		return;
63 	cp = expand(copyname);
64 	if (cp == NOSTR)
65 		return;
66 	if ((sfd = creat(cp, 0666)) < 0) {
67 		(void) printf("\r\n%s: cannot creat\r\n", copyname);
68 		return;
69 	}
70 
71 	/*
72 	 * collect parameters
73 	 */
74 	if (prompt("List command for remote system? ", buf, sizeof (buf))) {
75 		(void) unlink(copyname);
76 		return;
77 	}
78 	transfer(buf, sfd, value(EOFREAD));
79 }
80 
81 /*
82  * Cu-like take command
83  */
84 /* ARGSUSED */
85 void
86 cu_take(int cc)
87 {
88 	int fd, argc;
89 	char line[BUFSIZ], *cp;
90 
91 	if (prompt("[take] ", copyname, sizeof (copyname)))
92 		return;
93 	argc = args(copyname, argv, sizeof (argv)/sizeof (char *));
94 	if (argc < 1 || argc > 2) {
95 		(void) printf("usage: <take> from [to]\r\n");
96 		return;
97 	}
98 	if (argc == 1)
99 		argv[1] = argv[0];
100 	cp = expand(argv[1]);
101 	if (cp == NOSTR)
102 		return;
103 	if ((fd = creat(cp, 0666)) < 0) {
104 		(void) printf("\r\n%s: cannot create\r\n", argv[1]);
105 		return;
106 	}
107 	(void) snprintf(line, sizeof (line), "cat %s; echo \01", argv[0]);
108 	transfer(line, fd, "\01");
109 }
110 
111 /*
112  * Bulk transfer routine --
113  *  used by getfl(), cu_take(), and pipefile()
114  */
115 void
116 transfer(char *buf, int fd, char *eofchars)
117 {
118 	int ct;
119 	char c, buffer[BUFSIZ];
120 	char *p = buffer;	/* can't be register because of longjmp */
121 	int cnt, eof, bol;
122 	time_t start;
123 	sig_handler_t	f;
124 
125 	parwrite(FD, (unsigned char *)buf, strlen(buf));
126 	(void) kill(pid, SIGIOT);
127 	/* Wait until read process stops */
128 	(void) read(repdes[0], (char *)&ccc, 1);
129 
130 	/*
131 	 * finish command
132 	 */
133 	parwrite(FD, (unsigned char *)"\r", 1);
134 	do
135 		(void) read(FD, &c, 1);
136 	while ((c&0177) != '\n')
137 		;
138 
139 	if (sigsetjmp(intbuf, 1))
140 		goto out;
141 	f = signal(SIGINT, (sig_handler_t)intcopy);
142 	intr("on");
143 
144 	start = time(0);
145 	bol = 1;
146 	ct = 0;
147 	for (;;) {
148 		eof = read(FD, &c, 1) <= 0;
149 		if (noparity)
150 			c &= 0377;
151 		else
152 			c &= 0177;
153 		if (eof || (bol && any(c, eofchars)))
154 			break;
155 		if (c == 0)
156 			continue;	/* ignore nulls */
157 		if (c == '\r')
158 			continue;
159 		*p++ = c;
160 
161 		if (c == '\n') {
162 			bol = 1;
163 			if (boolean(value(VERBOSE)))
164 				(void) printf("\r%d", ++ct);
165 		} else
166 			bol = 0;
167 		if ((cnt = (p-buffer)) == number(value(FRAMESIZE))) {
168 			if (write(fd, buffer, cnt) != cnt) {
169 				(void) printf("\r\nwrite error\r\n");
170 				goto out;
171 			}
172 			p = buffer;
173 		}
174 	}
175 out:
176 	if ((cnt = (p-buffer)) != 0)
177 		if (write(fd, buffer, cnt) != cnt)
178 			(void) printf("\r\nwrite error\r\n");
179 
180 	if (boolean(value(VERBOSE)))
181 		prtime(" lines transferred in ", time(0)-start);
182 	intr("off");
183 	(void) write(fildes[1], (char *)&ccc, 1);
184 	(void) signal(SIGINT, f);
185 	(void) close(fd);
186 }
187 
188 /*
189  * FTP - remote ==> local process
190  *   send remote input to local process via pipe
191  */
192 /* ARGSUSED */
193 void
194 pipefile(int cc)
195 {
196 	int cpid, pdes[2];
197 	char buf[256];
198 	int status, p;
199 
200 	if (prompt("Local command? ", buf, sizeof (buf)))
201 		return;
202 
203 	if (pipe(pdes)) {
204 		(void) printf("can't establish pipe\r\n");
205 		return;
206 	}
207 
208 	if ((cpid = fork()) < 0) {
209 		(void) printf("can't fork!\r\n");
210 		return;
211 	} else if (cpid) {
212 		if (prompt("List command for remote system? ", buf,
213 		    sizeof (buf))) {
214 			(void) close(pdes[0]), (void) close(pdes[1]);
215 			(void) kill(cpid, SIGKILL);
216 		} else {
217 			(void) close(pdes[0]);
218 			(void) signal(SIGPIPE, (sig_handler_t)intcopy);
219 			transfer(buf, pdes[1], value(EOFREAD));
220 			(void) signal(SIGPIPE, SIG_DFL);
221 			while ((p = wait(&status)) > 0 && p != cpid)
222 				;
223 		}
224 	} else {
225 		int f;
226 
227 		userperm();
228 		(void) dup2(pdes[0], 0);
229 		(void) close(pdes[0]);
230 		for (f = 3; f < 20; f++)
231 			(void) close(f);
232 		execute(buf);
233 		(void) printf("can't execl!\r\n");
234 		exit(0);
235 	}
236 }
237 
238 /*
239  * FTP - local ==> remote
240  *  send local file to remote host
241  *  terminate transmission with pseudo EOF sequence
242  */
243 void
244 tip_sendfile(int cc)
245 {
246 	FILE *fd;
247 	char *fnamex;
248 
249 	(void) putchar(cc);
250 	/*
251 	 * get file name
252 	 */
253 	if (prompt("Local file name? ", fname, sizeof (fname)))
254 		return;
255 
256 	/*
257 	 * look up file
258 	 */
259 	fnamex = expand(fname);
260 	if (fnamex == NOSTR)
261 		return;
262 	if ((fd = fopen(fnamex, "r")) == NULL) {
263 		(void) printf("%s: cannot open\r\n", fname);
264 		return;
265 	}
266 	transmit(fd, value(EOFWRITE), NULL);
267 	if (!boolean(value(ECHOCHECK))) {
268 		struct termios buf;
269 
270 		(void) ioctl(FD, TCGETS, (char *)&buf);	/* this does a */
271 		(void) ioctl(FD, TCSETSF, (char *)&buf);	/* wflushtty */
272 	}
273 }
274 
275 /*
276  * Bulk transfer routine to remote host --
277  *   used by tip_sendfile() and cu_put()
278  */
279 void
280 transmit(FILE *fd, char *eofchars, char *command)
281 {
282 	sig_handler_t	ointr;
283 	char *pc, lastc, rc;
284 	int c, ccount, lcount;
285 	time_t start_t, stop_t;
286 
287 	(void) kill(pid, SIGIOT);	/* put TIPOUT into a wait state */
288 	timedout = 0;
289 	if (sigsetjmp(intbuf, 1)) {
290 		if (timedout)
291 			(void) printf("\r\ntimed out at eol\r\n");
292 		(void) alarm(0);
293 		goto out;
294 	}
295 	ointr = signal(SIGINT, (sig_handler_t)intcopy);
296 	intr("on");
297 	(void) read(repdes[0], (char *)&ccc, 1);
298 	if (command != NULL) {
299 		for (pc = command; *pc; pc++)
300 			send(*pc);
301 		if (boolean(value(ECHOCHECK)))
302 			(void) read(FD, (char *)&c, 1);	/* trailing \n */
303 		else {
304 			struct termios buf;
305 			/* wait for remote stty to take effect */
306 			(void) sleep(5);
307 			/* this does a */
308 			(void) ioctl(FD, TCGETS, (char *)&buf);
309 			/* wflushtty */
310 			(void) ioctl(FD, TCSETSF, (char *)&buf);
311 		}
312 	}
313 	lcount = 0;
314 	lastc = '\0';
315 	start_t = time(0);
316 	if (boolean(value(RAWFTP))) {
317 		while ((c = getc(fd)) != EOF) {
318 			lcount++;
319 			send(c);
320 			if (boolean(value(VERBOSE)) && lcount%100 == 0)
321 				(void) printf("\r%d", lcount);
322 		}
323 		if (boolean(value(VERBOSE)))
324 			(void) printf("\r%d", lcount);
325 		goto out;
326 	}
327 	for (;;) {
328 		ccount = 0;
329 		do {
330 			c = getc(fd);
331 			if (c == EOF)
332 				goto out;
333 			if (c == 0177)
334 				continue;
335 			lastc = c;
336 			if (c < 040) {
337 				if (c == '\n') {
338 					c = '\r';
339 				} else if (c == '\t') {
340 					if (boolean(value(TABEXPAND))) {
341 						send(' ');
342 						while ((++ccount % 8) != 0)
343 							send(' ');
344 						continue;
345 					}
346 				} else
347 					continue;
348 			}
349 			send(c);
350 		} while (c != '\r');
351 		if (boolean(value(VERBOSE)))
352 			(void) printf("\r%d", ++lcount);
353 		if (boolean(value(ECHOCHECK))) {
354 			(void) alarm(number(value(ETIMEOUT)));
355 			do {	/* wait for prompt */
356 				(void) read(FD, &rc, 1);
357 			} while ((rc&0177) != character(value(PROMPT)));
358 			(void) alarm(0);
359 		}
360 	}
361 out:
362 	if (lastc != '\n' && !boolean(value(RAWFTP)))
363 		send('\r');
364 	if (eofchars)
365 		for (pc = eofchars; *pc; pc++)
366 			send(*pc);
367 	stop_t = time(0);
368 	(void) fclose(fd);
369 	if (boolean(value(VERBOSE)))
370 		if (boolean(value(RAWFTP)))
371 			prtime(" chars transferred in ", stop_t-start_t);
372 		else
373 			prtime(" lines transferred in ", stop_t-start_t);
374 	(void) write(fildes[1], (char *)&ccc, 1);
375 	intr("off");
376 	(void) signal(SIGINT, ointr);
377 }
378 
379 /*
380  * Cu-like put command
381  */
382 /* ARGSUSED */
383 void
384 cu_put(int cc)
385 {
386 	FILE *fd;
387 	char line[BUFSIZ];
388 	int argc;
389 	char *copynamex;
390 
391 	if (prompt("[put] ", copyname, sizeof (copyname)))
392 		return;
393 	argc = args(copyname, argv, sizeof (argv)/sizeof (char *));
394 	if (argc < 1 || argc > 2) {
395 		(void) printf("usage: <put> from [to]\r\n");
396 		return;
397 	}
398 	if (argc == 1)
399 		argv[1] = argv[0];
400 	copynamex = expand(argv[0]);
401 	if (copynamex == NOSTR)
402 		return;
403 	if ((fd = fopen(copynamex, "r")) == NULL) {
404 		(void) printf("%s: cannot open\r\n", copynamex);
405 		return;
406 	}
407 	if (boolean(value(ECHOCHECK)))
408 		(void) snprintf(line, sizeof (line), "cat>%s\r", argv[1]);
409 	else
410 		(void) snprintf(line, sizeof (line),
411 		    "stty -echo; cat>%s; stty echo\r", argv[1]);
412 	transmit(fd, "\04", line);
413 }
414 
415 /*
416  * FTP - send single character
417  *  wait for echo & handle timeout
418  */
419 void
420 send(char c)
421 {
422 	char cc;
423 	int retry = 0;
424 
425 	cc = c;
426 	parwrite(FD, (unsigned char *)&cc, 1);
427 #ifdef notdef
428 	if (number(value(CDELAY)) > 0 && c != '\r')
429 		nap(number(value(CDELAY)));
430 #endif
431 	if (!boolean(value(ECHOCHECK))) {
432 #ifdef notdef
433 		if (number(value(LDELAY)) > 0 && c == '\r')
434 			nap(number(value(LDELAY)));
435 #endif
436 		return;
437 	}
438 tryagain:
439 	timedout = 0;
440 	if (sigsetjmp(intbuf, 1) && timedout) {
441 		(void) printf("\r\ntimeout error (%s)\r\n", ctrl(c));
442 		if (retry++ > 3)
443 			return;
444 		parwrite(FD, (unsigned char *)&null, 1); /* poke it */
445 		goto tryagain;
446 	}
447 	(void) alarm(number(value(ETIMEOUT)));
448 	(void) read(FD, &cc, 1);
449 	(void) alarm(0);
450 }
451 
452 void
453 timeout(void)
454 {
455 	(void) signal(SIGALRM, (sig_handler_t)timeout);
456 	timedout = 1;
457 	siglongjmp(intbuf, 1);
458 }
459 
460 /*
461  * Stolen from consh() -- puts a remote file on the output of a local command.
462  *	Identical to consh() except for where stdout goes.
463  */
464 void
465 pipeout(int c)
466 {
467 	char buf[256];
468 	int cpid, status, p;
469 	time_t start;
470 
471 	(void) putchar(c);
472 	if (prompt("Local command? ", buf, sizeof (buf)))
473 		return;
474 	(void) kill(pid, SIGIOT);	/* put TIPOUT into a wait state */
475 	(void) signal(SIGINT, SIG_IGN);
476 	(void) signal(SIGQUIT, SIG_IGN);
477 	intr("on");
478 	(void) read(repdes[0], (char *)&ccc, 1);
479 	/*
480 	 * Set up file descriptors in the child and
481 	 *  let it go...
482 	 */
483 	if ((cpid = fork()) < 0)
484 		(void) printf("can't fork!\r\n");
485 	else if (cpid) {
486 		start = time(0);
487 		while ((p = wait(&status)) > 0 && p != cpid)
488 			;
489 	} else {
490 		int i;
491 
492 		userperm();
493 		(void) dup2(FD, 1);
494 		for (i = 3; i < 20; i++)
495 			(void) close(i);
496 		(void) signal(SIGINT, SIG_DFL);
497 		(void) signal(SIGQUIT, SIG_DFL);
498 		execute(buf);
499 		(void) printf("can't find `%s'\r\n", buf);
500 		exit(0);
501 	}
502 	if (boolean(value(VERBOSE)))
503 		prtime("away for ", time(0)-start);
504 	(void) write(fildes[1], (char *)&ccc, 1);
505 	intr("off");
506 	(void) signal(SIGINT, SIG_DFL);
507 	(void) signal(SIGQUIT, SIG_DFL);
508 }
509 
510 /*
511  * Fork a program with:
512  *  0 <-> remote tty in
513  *  1 <-> remote tty out
514  *  2 <-> local tty stderr out
515  */
516 void
517 consh(int c)
518 {
519 	char buf[256];
520 	int cpid, status, p;
521 	sig_handler_t	ointr, oquit;
522 	time_t start;
523 
524 	(void) putchar(c);
525 	if (prompt("Local command? ", buf, sizeof (buf)))
526 		return;
527 	(void) kill(pid, SIGIOT);	/* put TIPOUT into a wait state */
528 	(void) read(repdes[0], (char *)&ccc, 1);
529 	ointr = signal(SIGINT, SIG_IGN);
530 	oquit = signal(SIGQUIT, SIG_IGN);
531 	unraw();
532 	/*
533 	 * Set up file descriptors in the child and
534 	 *  let it go...
535 	 */
536 	if ((cpid = fork()) < 0)
537 		(void) printf("can't fork!\r\n");
538 	else if (cpid) {
539 		start = time(0);
540 		while ((p = wait(&status)) > 0 && p != cpid)
541 			;
542 		raw();
543 		(void) signal(SIGINT, ointr);
544 		(void) signal(SIGQUIT, oquit);
545 	} else {
546 		int i;
547 
548 		userperm();
549 		(void) dup2(FD, 0);
550 		(void) dup2(0, 1);
551 		for (i = 3; i < 20; i++)
552 			(void) close(i);
553 		(void) signal(SIGINT, SIG_DFL);
554 		(void) signal(SIGQUIT, SIG_DFL);
555 		execute(buf);
556 		(void) printf("can't find `%s'\r\n", buf);
557 		exit(0);
558 	}
559 	if (boolean(value(VERBOSE)))
560 		prtime("\r\naway for ", time(0)-start);
561 	(void) write(fildes[1], (char *)&ccc, 1);
562 }
563 
564 /*
565  * Escape to local shell
566  */
567 /* ARGSUSED */
568 void
569 shell(int cc)
570 {
571 	int shpid, status;
572 	sig_handler_t	ointr, oquit;
573 	char *cp;
574 
575 	(void) printf("[sh]\r\n");
576 	ointr = signal(SIGINT, SIG_IGN);
577 	oquit = signal(SIGQUIT, SIG_IGN);
578 	unraw();
579 	if (shpid = fork()) {
580 		while (shpid != wait(&status))
581 			;
582 		raw();
583 		(void) printf("\r\n!\r\n");
584 		(void) signal(SIGINT, ointr);
585 		(void) signal(SIGQUIT, oquit);
586 	} else {
587 		userperm();
588 		(void) signal(SIGQUIT, SIG_DFL);
589 		(void) signal(SIGINT, SIG_DFL);
590 		if ((cp = strrchr(value(SHELL), '/')) == NULL)
591 			cp = value(SHELL);
592 		else
593 			cp++;
594 		(void) execl(value(SHELL), cp, 0);
595 		(void) printf("\r\ncan't execl!\r\n");
596 		exit(1);
597 	}
598 }
599 
600 /*
601  * TIPIN portion of scripting
602  *   initiate the conversation with TIPOUT
603  */
604 void
605 setscript(void)
606 {
607 	char c;
608 
609 	if (strlen(value(RECORD)) >= PATH_MAX-1) {
610 		(void) fprintf(stderr, "tip: record file name too long\r\n");
611 		return;
612 	}
613 	/*
614 	 * enable TIPOUT side for dialogue
615 	 */
616 	(void) kill(pid, SIGEMT);
617 	if (boolean(value(SCRIPT)))
618 		(void) write(fildes[1], value(RECORD), strlen(value(RECORD)));
619 	(void) write(fildes[1], "\n", 1);
620 	/*
621 	 * wait for TIPOUT to finish
622 	 */
623 	(void) read(repdes[0], &c, 1);
624 	if (c == 'n')
625 		(void) fprintf(stderr, "tip: can't create record file %s\r\n",
626 		    value(RECORD));
627 }
628 
629 /*
630  * Change current working directory of
631  *   local portion of tip
632  */
633 /* ARGSUSED */
634 void
635 chdirectory(int cc)
636 {
637 	char dirname[80];
638 	char *cp = dirname;
639 
640 	if (prompt("[cd] ", dirname, sizeof (dirname))) {
641 		if (stoprompt)
642 			return;
643 		cp = value(HOME);
644 	}
645 	if (chdir(cp) < 0)
646 		(void) printf("%s: bad directory\r\n", cp);
647 	(void) printf("!\r\n");
648 }
649 
650 void
651 tip_abort(char *msg)
652 {
653 	/* don't want to hear about our child */
654 	(void) signal(SIGCHLD, SIG_DFL);
655 	(void) kill(pid, SIGTERM);
656 	myperm();
657 	disconnect(msg);
658 	if (msg != NOSTR)
659 		(void) printf("\r\n%s", msg);
660 	(void) printf("\r\n[EOT]\r\n");
661 	delock(uucplock);
662 	unraw();
663 	exit(0);
664 }
665 
666 /* ARGSUSED */
667 void
668 finish(int cc)
669 {
670 	char *dismsg;
671 
672 	if ((dismsg = value(DISCONNECT)) != NOSTR) {
673 		(void) write(FD, dismsg, strlen(dismsg));
674 		(void) sleep(5);
675 	}
676 	tip_abort(NOSTR);
677 }
678 
679 void
680 intcopy(void)
681 {
682 
683 	(void) signal(SIGINT, SIG_IGN);
684 	siglongjmp(intbuf, 1);
685 }
686 
687 void
688 execute(char *s)
689 {
690 	char *cp;
691 
692 	if ((cp = strrchr(value(SHELL), '/')) == NULL)
693 		cp = value(SHELL);
694 	else
695 		cp++;
696 	(void) execl(value(SHELL), cp, "-c", s, 0);
697 }
698 
699 int
700 args(char *buf, char *a[], size_t na)
701 {
702 	char *p = buf, *start;
703 	char **parg = a;
704 	int n = 0;
705 
706 	do {
707 		while (*p && (*p == ' ' || *p == '\t'))
708 			p++;
709 		start = p;
710 		if (*p)
711 			*parg = p;
712 		while (*p && (*p != ' ' && *p != '\t'))
713 			p++;
714 		if (p != start)
715 			parg++, n++;
716 		if (*p)
717 			*p++ = '\0';
718 	} while (*p && n < na);
719 
720 	return (n);
721 }
722 
723 void
724 prtime(char *s, time_t a)
725 {
726 	int i;
727 	int nums[3];
728 
729 	for (i = 0; i < 3; i++) {
730 		nums[i] = (int)(a % quant[i]);
731 		a /= quant[i];
732 	}
733 	(void) printf("%s", s);
734 	while (--i >= 0)
735 		if (nums[i] || i == 0 && nums[1] == 0 && nums[2] == 0)
736 			(void) printf("%d %s%c ", nums[i], sep[i],
737 			    nums[i] == 1 ? '\0' : 's');
738 	(void) printf("\r\n!\r\n");
739 }
740 
741 /* ARGSUSED */
742 void
743 variable(int cc)
744 {
745 	char	buf[256];
746 
747 	if (prompt("[set] ", buf, sizeof (buf)))
748 		return;
749 	vlex(buf);
750 	if (vtable[BEAUTIFY].v_access&CHANGED) {
751 		vtable[BEAUTIFY].v_access &= ~CHANGED;
752 		(void) kill(pid, SIGSYS);
753 	}
754 	if (vtable[SCRIPT].v_access&CHANGED) {
755 		vtable[SCRIPT].v_access &= ~CHANGED;
756 		setscript();
757 		/*
758 		 * So that "set record=blah script" doesn't
759 		 *  cause two transactions to occur.
760 		 */
761 		if (vtable[RECORD].v_access&CHANGED)
762 			vtable[RECORD].v_access &= ~CHANGED;
763 	}
764 	if (vtable[RECORD].v_access&CHANGED) {
765 		vtable[RECORD].v_access &= ~CHANGED;
766 		if (boolean(value(SCRIPT)))
767 			setscript();
768 	}
769 	if (vtable[TAND].v_access&CHANGED) {
770 		vtable[TAND].v_access &= ~CHANGED;
771 		if (boolean(value(TAND)))
772 			tandem("on");
773 		else
774 			tandem("off");
775 	}
776 	if (vtable[LECHO].v_access&CHANGED) {
777 		vtable[LECHO].v_access &= ~CHANGED;
778 		boolean(value(HALFDUPLEX)) = boolean(value(LECHO));
779 	}
780 	if (vtable[PARITY].v_access&CHANGED) {
781 		vtable[PARITY].v_access &= ~CHANGED;
782 		setparity(NULL);
783 	}
784 	if (vtable[BAUDRATE].v_access&CHANGED) {
785 		vtable[BAUDRATE].v_access &= ~CHANGED;
786 		ttysetup(speed(number(value(BAUDRATE))));
787 	}
788 	if (vtable[HARDWAREFLOW].v_access & CHANGED) {
789 		vtable[HARDWAREFLOW].v_access &= ~CHANGED;
790 		if (boolean(value(HARDWAREFLOW)))
791 			hardwareflow("on");
792 		else
793 			hardwareflow("off");
794 	}
795 }
796 
797 /*
798  * Turn tandem mode on or off for remote tty.
799  */
800 void
801 tandem(char *option)
802 {
803 	struct termios rmtty;
804 
805 	(void) ioctl(FD, TCGETS, (char *)&rmtty);
806 	if (equal(option, "on")) {
807 		rmtty.c_iflag |= IXOFF|IXON;
808 		arg.c_iflag |= IXOFF|IXON;
809 		rmtty.c_cc[VSTART] = defarg.c_cc[VSTART];
810 		rmtty.c_cc[VSTOP] = defarg.c_cc[VSTOP];
811 	} else {
812 		rmtty.c_iflag &= ~(IXOFF|IXON);
813 		arg.c_iflag &= ~(IXOFF|IXON);
814 	}
815 	(void) ioctl(FD, TCSETSF, (char *)&rmtty);
816 	(void) ioctl(0, TCSETSF, (char *)&arg);
817 }
818 
819 /*
820  * Turn hardwareflow mode on or off for remote tty.
821  */
822 void
823 hardwareflow(char *option)
824 {
825 	struct termios rmtty;
826 
827 	(void) ioctl(FD, TCGETS, (char *)&rmtty);
828 	if (equal(option, "on")) {
829 		rmtty.c_cflag |= (CRTSCTS|CRTSXOFF);
830 	} else {
831 		rmtty.c_cflag &= ~(CRTSCTS|CRTSXOFF);
832 	}
833 	(void) ioctl(FD, TCSETSF, (char *)&rmtty);
834 }
835 
836 /*
837  * Turn interrupts from local tty on or off.
838  */
839 void
840 intr(char *option)
841 {
842 
843 	if (equal(option, "on"))
844 		arg.c_lflag |= ISIG;
845 	else
846 		arg.c_lflag &= ~ISIG;
847 	(void) ioctl(0, TCSETSF, (char *)&arg);
848 }
849 
850 /*
851  * Send a break.
852  */
853 /* ARGSUSED */
854 void
855 genbrk(int cc)
856 {
857 
858 	(void) ioctl(FD, TCSBRK, 0);
859 }
860 
861 /*
862  * Suspend tip
863  */
864 void
865 suspend(int c)
866 {
867 
868 	unraw();
869 	(void) kill(c == _CTRL('y') ? getpid() : 0, SIGTSTP);
870 	raw();
871 }
872 
873 /*
874  *	expand a file name if it includes shell meta characters
875  */
876 
877 char *
878 expand(char name[])
879 {
880 	static char xname[BUFSIZ];
881 	char cmdbuf[BUFSIZ];
882 	int pid, l;
883 	char *cp, *Shell;
884 	int s, pivec[2];
885 
886 	if (!anyof(name, "~{[*?$`'\"\\"))
887 		return (name);
888 	if (pipe(pivec) < 0) {
889 		perror("pipe");
890 		return (name);
891 	}
892 	(void) snprintf(cmdbuf, sizeof (cmdbuf), "echo %s", name);
893 	if ((pid = vfork()) == 0) {
894 		userperm();
895 		Shell = value(SHELL);
896 		if (Shell == NOSTR)
897 			Shell = "/bin/sh";
898 		(void) close(pivec[0]);
899 		(void) close(1);
900 		(void) dup(pivec[1]);
901 		(void) close(pivec[1]);
902 		(void) close(2);
903 		(void) execl(Shell, Shell, "-c", cmdbuf, 0);
904 		_exit(1);
905 	}
906 	if (pid == -1) {
907 		perror("fork");
908 		(void) close(pivec[0]);
909 		(void) close(pivec[1]);
910 		return (NOSTR);
911 	}
912 	(void) close(pivec[1]);
913 	l = read(pivec[0], xname, BUFSIZ);
914 	(void) close(pivec[0]);
915 	while (wait(&s) != pid)
916 		;
917 	s &= 0377;
918 	if (s != 0 && s != SIGPIPE) {
919 		(void) fprintf(stderr, "\"Echo\" failed\n");
920 		return (NOSTR);
921 	}
922 	if (l < 0) {
923 		perror("read");
924 		return (NOSTR);
925 	}
926 	if (l == 0) {
927 		(void) fprintf(stderr, "\"%s\": No match\n", name);
928 		return (NOSTR);
929 	}
930 	if (l == BUFSIZ) {
931 		(void) fprintf(stderr, "Buffer overflow expanding \"%s\"\n",
932 		    name);
933 		return (NOSTR);
934 	}
935 	xname[l] = 0;
936 	for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
937 		;
938 	*++cp = '\0';
939 	return (xname);
940 }
941 
942 /*
943  * Are any of the characters in the two strings the same?
944  */
945 
946 int
947 anyof(char *s1, char *s2)
948 {
949 	int c;
950 
951 	while ((c = *s1++) != 0)
952 		if (any(c, s2))
953 			return (1);
954 	return (0);
955 }
956