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