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