]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.bin/tip/tip/cmds.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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         unexcl();
670         exit(0);
671 }
672
673 /*ARGSUSED*/
674 void
675 finish(int c)
676 {
677         char *dismsg;
678
679         if ((dismsg = value(DISCONNECT)) != NOSTR) {
680                 write(FD, dismsg, strlen(dismsg));
681                 sleep(5);
682         }
683         tipabort(NOSTR);
684 }
685
686 /*ARGSUSED*/
687 static void
688 intcopy(int signo)
689 {
690         raw();
691         quit = 1;
692         longjmp(intbuf, 1);
693 }
694
695 static void
696 execute(char *s)
697 {
698         char *cp;
699
700         if ((cp = strrchr(value(SHELL), '/')) == NULL)
701                 cp = value(SHELL);
702         else
703                 cp++;
704         shell_uid();
705         execl(value(SHELL), cp, "-c", s, (char *)NULL);
706 }
707
708 static int
709 args(char *buf, char *a[], int num)
710 {
711         char *p = buf, *start;
712         char **parg = a;
713         int n = 0;
714
715         do {
716                 while (*p && (*p == ' ' || *p == '\t'))
717                         p++;
718                 start = p;
719                 if (*p)
720                         *parg = p;
721                 while (*p && (*p != ' ' && *p != '\t'))
722                         p++;
723                 if (p != start)
724                         parg++, n++;
725                 if (*p)
726                         *p++ = '\0';
727         } while (*p && n < num);
728
729         return(n);
730 }
731
732 static void
733 prtime(char *s, time_t a)
734 {
735         int i;
736         int nums[3];
737
738         for (i = 0; i < 3; i++) {
739                 nums[i] = (int)(a % quant[i]);
740                 a /= quant[i];
741         }
742         printf("%s", s);
743         while (--i >= 0)
744                 if (nums[i] || (i == 0 && nums[1] == 0 && nums[2] == 0))
745                         printf("%d %s%c ", nums[i], sep[i],
746                                 nums[i] == 1 ? '\0' : 's');
747         printf("\r\n!\r\n");
748 }
749
750 /*ARGSUSED*/
751 void
752 variable(int c)
753 {
754         char    buf[256];
755
756         if (prompt("[set] ", buf, sizeof(buf)))
757                 return;
758         vlex(buf);
759         if (vtable[BEAUTIFY].v_access&CHANGED) {
760                 vtable[BEAUTIFY].v_access &= ~CHANGED;
761                 kill(tipout_pid, SIGSYS);
762         }
763         if (vtable[SCRIPT].v_access&CHANGED) {
764                 vtable[SCRIPT].v_access &= ~CHANGED;
765                 setscript();
766                 /*
767                  * So that "set record=blah script" doesn't
768                  *  cause two transactions to occur.
769                  */
770                 if (vtable[RECORD].v_access&CHANGED)
771                         vtable[RECORD].v_access &= ~CHANGED;
772         }
773         if (vtable[RECORD].v_access&CHANGED) {
774                 vtable[RECORD].v_access &= ~CHANGED;
775                 if (boolean(value(SCRIPT)))
776                         setscript();
777         }
778         if (vtable[TAND].v_access&CHANGED) {
779                 vtable[TAND].v_access &= ~CHANGED;
780                 if (boolean(value(TAND)))
781                         tandem("on");
782                 else
783                         tandem("off");
784         }
785         if (vtable[LECHO].v_access&CHANGED) {
786                 vtable[LECHO].v_access &= ~CHANGED;
787                 HD = boolean(value(LECHO));
788         }
789         if (vtable[PARITY].v_access&CHANGED) {
790                 vtable[PARITY].v_access &= ~CHANGED;
791                 setparity(NOSTR);
792         }
793         if (vtable[HARDWAREFLOW].v_access&CHANGED) {
794                 vtable[HARDWAREFLOW].v_access &= ~CHANGED;
795                 if (boolean(value(HARDWAREFLOW)))
796                         hardwareflow("on");
797                 else
798                         hardwareflow("off");
799         }
800         if (vtable[LINEDISC].v_access&CHANGED) {
801                 vtable[LINEDISC].v_access &= ~CHANGED;
802                 linedisc(NOSTR);
803         }
804 }
805
806 /*ARGSUSED*/
807 void
808 listvariables(int c)
809 {
810         value_t *p;
811         char *buf;
812         char charbuf[5];        /* for vis(3), 4 chars for encoding, plus nul */
813
814         puts("v\r");
815         for (p = vtable; p->v_name; p++) {
816                 fputs(p->v_name, stdout);
817                 switch (p->v_type&TMASK) {
818                 case STRING:
819                         if (p->v_value) {
820                                 buf = malloc(4*strlen(p->v_value) + 1);
821                                 if (buf == NULL) {
822                                         fprintf(stderr, "Unable to malloc()\n");
823                                         abort();
824                                 }
825                                 strvis(buf, p->v_value, VIS_WHITE);
826                                 printf(" %s", buf);
827                                 free(buf);
828                         }
829                         putchar('\r');
830                         putchar('\n');
831                         break;
832                 case NUMBER:
833                         printf(" %ld\r\n", number(p->v_value));
834                         break;
835                 case BOOL:
836                         printf(" %s\r\n",
837                             !boolean(p->v_value) ? "false" : "true");
838                         break;
839                 case CHAR:
840                         vis(charbuf, character(p->v_value), VIS_WHITE, 0);
841                         printf(" %s\r\n", charbuf);
842                         break;
843                 }
844         }
845 }
846
847 /*
848  * Turn tandem mode on or off for remote tty.
849  */
850 static void
851 tandem(char *option)
852 {
853         struct termios  rmtty;
854
855         tcgetattr(FD, &rmtty);
856         if (strcmp(option, "on") == 0) {
857                 rmtty.c_iflag |= IXOFF;
858                 term.c_iflag |= IXOFF;
859         } else {
860                 rmtty.c_iflag &= ~IXOFF;
861                 term.c_iflag &= ~IXOFF;
862         }
863         tcsetattr(FD, TCSADRAIN, &rmtty);
864         tcsetattr(0, TCSADRAIN, &term);
865 }
866
867 /*
868  * Turn hardware flow control on or off for remote tty.
869  */
870 static void
871 hardwareflow(char *option)
872 {
873         struct termios  rmtty;
874
875         tcgetattr(FD, &rmtty);
876         if (strcmp(option, "on") == 0)
877                 rmtty.c_iflag |= CRTSCTS;
878         else
879                 rmtty.c_iflag &= ~CRTSCTS;
880         tcsetattr(FD, TCSADRAIN, &rmtty);
881 }
882
883 /*
884  * Change line discipline to the specified one.
885  */
886 void
887 linedisc(char *option)
888 {
889         int ld = (int)(intptr_t)value(LINEDISC);
890
891         ioctl(FD, TIOCSETD, &ld);
892 }
893
894 /*
895  * Send a break.
896  */
897 /*ARGSUSED*/
898 void
899 genbrk(int c)
900 {
901         ioctl(FD, TIOCSBRK, NULL);
902         sleep(1);
903         ioctl(FD, TIOCCBRK, NULL);
904 }
905
906 /*
907  * Suspend tip
908  */
909 void
910 suspend(int c)
911 {
912         unraw();
913         kill(c == CTRL('y') ? getpid() : 0, SIGTSTP);
914         raw();
915 }
916
917 /*
918  *      expand a file name if it includes shell meta characters
919  */
920 char *
921 expand(char name[])
922 {
923         static char xname[BUFSIZ];
924         char cmdbuf[BUFSIZ];
925         int l;
926         char *cp, *Shell;
927         int s, pivec[2];
928         pid_t pid;
929
930         if (!anyof(name, "~{[*?$`'\"\\"))
931                 return(name);
932         /* sigint = signal(SIGINT, SIG_IGN); */
933         if (pipe(pivec) < 0) {
934                 perror("pipe");
935                 /* signal(SIGINT, sigint) */
936                 return(name);
937         }
938         (void)snprintf(cmdbuf, sizeof(cmdbuf), "echo %s", name);
939         if ((pid = vfork()) == 0) {
940                 Shell = value(SHELL);
941                 if (Shell == NOSTR)
942                         Shell = _PATH_BSHELL;
943                 close(pivec[0]);
944                 close(1);
945                 dup(pivec[1]);
946                 close(pivec[1]);
947                 close(2);
948                 shell_uid();
949                 execl(Shell, Shell, "-c", cmdbuf, (char *)NULL);
950                 _exit(1);
951         }
952         if (pid == -1) {
953                 perror("fork");
954                 close(pivec[0]);
955                 close(pivec[1]);
956                 return(NOSTR);
957         }
958         close(pivec[1]);
959         l = read(pivec[0], xname, BUFSIZ);
960         close(pivec[0]);
961         while (wait(&s) != pid);
962                 ;
963         s &= 0377;
964         if (s != 0 && s != SIGPIPE) {
965                 fprintf(stderr, "\"Echo\" failed\n");
966                 return(NOSTR);
967         }
968         if (l < 0) {
969                 perror("read");
970                 return(NOSTR);
971         }
972         if (l == 0) {
973                 fprintf(stderr, "\"%s\": No match\n", name);
974                 return(NOSTR);
975         }
976         if (l == BUFSIZ) {
977                 fprintf(stderr, "Buffer overflow expanding \"%s\"\n", name);
978                 return(NOSTR);
979         }
980         xname[l] = 0;
981         for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
982                 ;
983         *++cp = '\0';
984         return(xname);
985 }
986
987 /*
988  * Are any of the characters in the two strings the same?
989  */
990 static int
991 anyof(char *s1, char *s2)
992 {
993         int c;
994
995         while ((c = *s1++))
996                 if (any(c, s2))
997                         return(1);
998         return(0);
999 }