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