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