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