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