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