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