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