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