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