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