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