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