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