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