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