home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.bin / tip / cmds.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-18  |  17.2 KB  |  889 lines

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