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