cmds.c revision 59217
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 59217 2000-04-14 06:39:19Z imp $";
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 */
728int
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	return 0;
737}
738#endif
739
740/*
741 * Escape to local shell
742 */
743void
744shell()
745{
746	int shpid, status;
747	char *cp;
748
749	printf("[sh]\r\n");
750	signal(SIGINT, SIG_IGN);
751	signal(SIGQUIT, SIG_IGN);
752	unraw();
753	if ((shpid = fork())) {
754		while (shpid != wait(&status));
755		raw();
756		printf("\r\n!\r\n");
757		signal(SIGINT, SIG_DFL);
758		signal(SIGQUIT, SIG_DFL);
759		return;
760	} else {
761		signal(SIGQUIT, SIG_DFL);
762		signal(SIGINT, SIG_DFL);
763		if ((cp = rindex(value(SHELL), '/')) == NULL)
764			cp = value(SHELL);
765		else
766			cp++;
767		shell_uid();
768		execl(value(SHELL), cp, 0);
769		printf("\r\ncan't execl!\r\n");
770		exit(1);
771	}
772}
773
774/*
775 * TIPIN portion of scripting
776 *   initiate the conversation with TIPOUT
777 */
778void
779setscript()
780{
781	char c;
782	/*
783	 * enable TIPOUT side for dialogue
784	 */
785	kill(pid, SIGEMT);
786	if (boolean(value(SCRIPT)))
787		write(fildes[1], value(RECORD), size(value(RECORD)));
788	write(fildes[1], "\n", 1);
789	/*
790	 * wait for TIPOUT to finish
791	 */
792	read(repdes[0], &c, 1);
793	if (c == 'n')
794		printf("can't create %s\r\n", value(RECORD));
795}
796
797/*
798 * Change current working directory of
799 *   local portion of tip
800 */
801void
802chdirectory()
803{
804	char dirname[PATH_MAX];
805	register char *cp = dirname;
806
807	if (prompt("[cd] ", dirname, sizeof(dirname))) {
808		if (stoprompt)
809			return;
810		cp = value(HOME);
811	}
812	if (chdir(cp) < 0)
813		printf("%s: bad directory\r\n", cp);
814	printf("!\r\n");
815}
816
817void
818tipabort(msg)
819	char *msg;
820{
821
822	kill(pid, SIGTERM);
823	disconnect(msg);
824	if (msg != NOSTR)
825		printf("\r\n%s", msg);
826	printf("\r\n[EOT]\r\n");
827	daemon_uid();
828	(void)uu_unlock(uucplock);
829	unraw();
830	exit(0);
831}
832
833void
834finish()
835{
836	char *abortmsg = NOSTR, *dismsg;
837
838	if (LO != NOSTR && tiplink (LO, TL_SIGNAL_TIPOUT) != 0) {
839		abortmsg = "logout failed";
840	}
841
842	if ((dismsg = value(DISCONNECT)) != NOSTR) {
843		write(FD, dismsg, strlen(dismsg));
844		sleep (2);
845	}
846	tipabort(abortmsg);
847}
848
849void
850intcopy()
851{
852	raw();
853	quit = 1;
854	longjmp(intbuf, 1);
855}
856
857static void
858execute(s)
859	char *s;
860{
861	register char *cp;
862
863	if ((cp = rindex(value(SHELL), '/')) == NULL)
864		cp = value(SHELL);
865	else
866		cp++;
867	shell_uid();
868	execl(value(SHELL), cp, "-c", s, 0);
869}
870
871static int
872args(buf, a, num)
873	char *buf, *a[];
874	int num;
875{
876	register char *p = buf, *start;
877	register char **parg = a;
878	register int n = 0;
879
880	while (*p && n < num) {
881		while (*p && (*p == ' ' || *p == '\t'))
882			p++;
883		start = p;
884		if (*p)
885			*parg = p;
886		while (*p && (*p != ' ' && *p != '\t'))
887			p++;
888		if (p != start)
889			parg++, n++;
890		if (*p)
891			*p++ = '\0';
892	}
893	return(n);
894}
895
896static void
897prtime(s, a)
898	char *s;
899	time_t a;
900{
901	register i;
902	int nums[3];
903
904	for (i = 0; i < 3; i++) {
905		nums[i] = (int)(a % quant[i]);
906		a /= quant[i];
907	}
908	printf("%s", s);
909	while (--i >= 0)
910		if (nums[i] || (i == 0 && nums[1] == 0 && nums[2] == 0))
911			printf("%d %s%c ", nums[i], sep[i],
912				nums[i] == 1 ? '\0' : 's');
913	printf("\r\n!\r\n");
914}
915
916void
917variable()
918{
919	char	buf[256];
920
921	if (prompt("[set] ", buf, sizeof(buf)))
922		return;
923	vlex(buf);
924	if (vtable[BEAUTIFY].v_access&CHANGED) {
925		vtable[BEAUTIFY].v_access &= ~CHANGED;
926		kill(pid, SIGSYS);
927	}
928	if (vtable[SCRIPT].v_access&CHANGED) {
929		vtable[SCRIPT].v_access &= ~CHANGED;
930		setscript();
931		/*
932		 * So that "set record=blah script" doesn't
933		 *  cause two transactions to occur.
934		 */
935		if (vtable[RECORD].v_access&CHANGED)
936			vtable[RECORD].v_access &= ~CHANGED;
937	}
938	if (vtable[RECORD].v_access&CHANGED) {
939		vtable[RECORD].v_access &= ~CHANGED;
940		if (boolean(value(SCRIPT)))
941			setscript();
942	}
943	if (vtable[TAND].v_access&CHANGED) {
944		vtable[TAND].v_access &= ~CHANGED;
945		if (boolean(value(TAND)))
946			tandem("on");
947		else
948			tandem("off");
949	}
950 	if (vtable[LECHO].v_access&CHANGED) {
951 		vtable[LECHO].v_access &= ~CHANGED;
952 		HD = boolean(value(LECHO));
953 	}
954	if (vtable[PARITY].v_access&CHANGED) {
955		vtable[PARITY].v_access &= ~CHANGED;
956		setparity(value(PARITY));
957	}
958}
959
960/*
961 * Turn tandem mode on or off for remote tty.
962 */
963static void
964tandem(option)
965	char *option;
966{
967#if HAVE_TERMIOS
968	struct termios ttermios;
969	tcgetattr (FD, &ttermios);
970	if (strcmp(option,"on") == 0) {
971		ttermios.c_iflag |= IXOFF;
972		ctermios.c_iflag |= IXOFF;
973	}
974	else {
975		ttermios.c_iflag &= ~IXOFF;
976		ctermios.c_iflag &= ~IXOFF;
977	}
978	tcsetattr (FD, TCSANOW, &ttermios);
979	tcsetattr (0, TCSANOW, &ctermios);
980#else /* HAVE_TERMIOS */
981	struct sgttyb rmtty;
982
983	ioctl(FD, TIOCGETP, &rmtty);
984	if (strcmp(option,"on") == 0) {
985		rmtty.sg_flags |= TANDEM;
986		arg.sg_flags |= TANDEM;
987	} else {
988		rmtty.sg_flags &= ~TANDEM;
989		arg.sg_flags &= ~TANDEM;
990	}
991	ioctl(FD, TIOCSETP, &rmtty);
992	ioctl(0,  TIOCSETP, &arg);
993#endif /* HAVE_TERMIOS */
994}
995
996/*
997 * Send a break.
998 */
999void
1000genbrk()
1001{
1002
1003	ioctl(FD, TIOCSBRK, NULL);
1004	sleep(1);
1005	ioctl(FD, TIOCCBRK, NULL);
1006}
1007
1008/*
1009 * Suspend tip
1010 */
1011void
1012suspend(c)
1013	char c;
1014{
1015
1016	unraw();
1017	kill(c == CTRL('y') ? getpid() : 0, SIGTSTP);
1018	raw();
1019}
1020
1021/*
1022 *	expand a file name if it includes shell meta characters
1023 */
1024
1025char *
1026expand(name)
1027	char name[];
1028{
1029	static char xname[BUFSIZ];
1030	char cmdbuf[BUFSIZ];
1031	register int pid, l;
1032	register char *cp, *Shell;
1033	int s, pivec[2] /*, (*sigint)()*/;
1034
1035	if (!anyof(name, "~{[*?$`'\"\\"))
1036		return(name);
1037	/* sigint = signal(SIGINT, SIG_IGN); */
1038	if (pipe(pivec) < 0) {
1039		warn("pipe");
1040		/* signal(SIGINT, sigint) */
1041		return(name);
1042	}
1043	snprintf(cmdbuf, sizeof(cmdbuf), "echo %s", name);
1044	if ((pid = vfork()) == 0) {
1045		Shell = value(SHELL);
1046		if (Shell == NOSTR)
1047			Shell = _PATH_BSHELL;
1048		close(pivec[0]);
1049		close(1);
1050		dup(pivec[1]);
1051		close(pivec[1]);
1052		close(2);
1053		shell_uid();
1054		execl(Shell, Shell, "-c", cmdbuf, 0);
1055		_exit(1);
1056	}
1057	if (pid == -1) {
1058		warn("fork");
1059		close(pivec[0]);
1060		close(pivec[1]);
1061		return(NOSTR);
1062	}
1063	close(pivec[1]);
1064	l = read(pivec[0], xname, BUFSIZ);
1065	close(pivec[0]);
1066	while (wait(&s) != pid);
1067		;
1068	s &= 0377;
1069	if (s != 0 && s != SIGPIPE) {
1070		fprintf(stderr, "\"Echo\" failed\n");
1071		return(NOSTR);
1072	}
1073	if (l < 0) {
1074		warn("read");
1075		return(NOSTR);
1076	}
1077	if (l == 0) {
1078		fprintf(stderr, "\"%s\": No match\n", name);
1079		return(NOSTR);
1080	}
1081	if (l == BUFSIZ) {
1082		fprintf(stderr, "Buffer overflow expanding \"%s\"\n", name);
1083		return(NOSTR);
1084	}
1085	xname[l] = 0;
1086	for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
1087		;
1088	*++cp = '\0';
1089	return(xname);
1090}
1091
1092/*
1093 * Are any of the characters in the two strings the same?
1094 */
1095
1096static int
1097anyof(s1, s2)
1098	register char *s1, *s2;
1099{
1100	register int c;
1101
1102	while ((c = *s1++))
1103		if (any(c, s2))
1104			return(1);
1105	return(0);
1106}
1107