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