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