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