root/usr.bin/tip/cmds.c
/*
 * Copyright (c) 1983, 1993
 *      The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * @(#)cmds.c   8.1 (Berkeley) 6/6/93
 * $FreeBSD: src/usr.bin/tip/tip/cmds.c,v 1.11.2.2 2000/07/01 12:24:23 ps Exp $
 */

#include "tip.h"
#include "pathnames.h"

#include <sys/types.h>
#include <sys/wait.h>
#include <err.h>
#include <libutil.h>
#include <stdio.h>
#include <unistd.h>

/*
 * tip
 *
 * miscellaneous commands
 */

int     quant[] = { 60, 60, 24 };

char    null = '\0';
char    *sep[] = { "second", "minute", "hour" };
static char *argv[10];          /* argument vector for take and put */

static void     stopsnd(int);           /* SIGINT handler during file transfers */
static void     intcopy(int);           /* interrupt routine for file transfers */

static int anyof(char *, char *);
static void tandem(char *);
static void prtime(char *, time_t);
static int args(char *, char **, int);
static void execute(char *);
static void send(char);
static void transmit(FILE *, char *, char *);
static void transfer(char *, int, char *);
static void xfer(char *, int, char *);

static void
usedefchars(void)
{
        int cnt;
        struct termios ttermios;
        ttermios = ctermios;
        for (cnt = 0; cnt < NCCS; cnt++)
                ttermios.c_cc [cnt] = otermios.c_cc [cnt];
        tcsetattr (0, TCSANOW, &ttermios);
}

static void
usetchars(void)
{
        tcsetattr (0, TCSANOW, &ctermios);
}

static void
flush_remote(void)
{
        int cmd = 0;
        ioctl (FD, TIOCFLUSH, &cmd);
}

/*
 * FTP - remote ==> local
 *  get a file from the remote host
 */
void
getfl(int c)
{
        char buf[256], *cp;

        putchar(c);
        /*
         * get the UNIX receiving file's name
         */
        if (prompt("Local file name? ", copyname, sizeof(copyname)))
                return;
        cp = expand(copyname);
        if ((sfd = creat(cp, 0666)) < 0) {
                printf("\r\n%s: cannot creat\r\n", copyname);
                return;
        }

        /*
         * collect parameters
         */
        if (prompt("List command for remote system? ", buf, sizeof(buf))) {
                unlink(copyname);
                return;
        }
        transfer(buf, sfd, value(EOFREAD));
}

/*
 * Cu-like take command
 */
void
cu_take(int c)
{
        int fd, argc;
        char line[BUFSIZ], *cp;

        if (prompt("[take] ", copyname, sizeof(copyname)))
                return;
        if ((argc = args(copyname, argv, sizeof(argv)/sizeof(argv[0]))) < 1 || argc > 2) {
                printf("usage: <take> from [to]\r\n");
                return;
        }
        if (argc == 1)
                argv[1] = argv[0];
        cp = expand(argv[1]);
        if ((fd = creat(cp, 0666)) < 0) {
                printf("\r\n%s: cannot create\r\n", argv[1]);
                return;
        }
        (void)snprintf(line, sizeof(line), "cat %s ; echo \"\" ; echo ___tip_end_of_file_marker___", argv[0]);
        xfer(line, fd, "\n___tip_end_of_file_marker___\n");
}

static jmp_buf intbuf;

static void
xfer(char *buf, int fd, char *eofchars)
{
        int ct;
        char c, *match;
        int cnt, eof, v;
        time_t start;
        sig_t f;
        char r;
        FILE *ff;

        v = boolean(value(VERBOSE));

        if ((ff = fdopen (fd, "w")) == NULL) {
                warn("file open");
                return;
        }
        if ((cnt = number(value(FRAMESIZE))) != BUFSIZ)
                if (setvbuf(ff, NULL, _IOFBF, cnt) != 0) {
                        warn("file allocation");
                        (void)fclose(ff);
                        return;
                }

        xpwrite(FD, buf, size(buf));
        quit = 0;
        kill(pid, SIGIOT);
        read(repdes[0], (char *)&ccc, 1);  /* Wait until read process stops */

        /*
         * finish command
         */
        r = '\r';
        xpwrite(FD, &r, 1);
        do
                read(FD, &c, 1);
        while ((c&0177) != '\n');

        usedefchars ();

        (void) setjmp(intbuf);
        f = signal(SIGINT, intcopy);
        start = time(0);
        match = eofchars;
        for (ct = 0; !quit;) {
                eof = read(FD, &c, 1) <= 0;
                c &= 0177;
                if (quit)
                        continue;
                if (eof)
                        break;
                if (c == 0)
                        continue;       /* ignore nulls */
                if (c == '\r')
                        continue;
                if (c != *match && match > eofchars) {
                        char *p = eofchars;
                        while (p < match) {
                                if (*p == '\n'&& v)
                                        (void)printf("\r%d", ++ct);
                                fputc(*p++, ff);
                        }
                        match = eofchars;
                }
                if (c == *match) {
                        if (*++match == '\0')
                                break;
                } else {
                        if (c == '\n' && v)
                                (void)printf("\r%d", ++ct);
                        fputc(c, ff);
                }
        }
        if (v)
                prtime(" lines transferred in ", time(0)-start);
        usetchars ();
        write(fildes[1], (char *)&ccc, 1);
        signal(SIGINT, f);
        (void)fclose(ff);
}

/*
 * Bulk transfer routine --
 *  used by getfl(), cu_take(), and pipefile()
 */
static void
transfer(char *buf, int fd, char *eofchars)
{
        int ct;
        char c;
        int cnt, eof, v;
        time_t start;
        sig_t f;
        char r;
        FILE *ff;

        v = boolean(value(VERBOSE));

        if ((ff = fdopen (fd, "w")) == NULL) {
                warn("file open");
                return;
        }
        if ((cnt = number(value(FRAMESIZE))) != BUFSIZ)
                if (setvbuf(ff, NULL, _IOFBF, cnt) != 0) {
                        warn("file allocation");
                        (void)fclose(ff);
                        return;
                }

        xpwrite(FD, buf, size(buf));
        quit = 0;
        kill(pid, SIGIOT);
        read(repdes[0], (char *)&ccc, 1);  /* Wait until read process stops */

        /*
         * finish command
         */
        r = '\r';
        xpwrite(FD, &r, 1);
        do
                read(FD, &c, 1);
        while ((c&0177) != '\n');
        usedefchars ();
        (void) setjmp(intbuf);
        f = signal(SIGINT, intcopy);
        start = time(0);
        for (ct = 0; !quit;) {
                eof = read(FD, &c, 1) <= 0;
                c &= 0177;
                if (quit)
                        continue;
                if (eof || any(c, eofchars))
                        break;
                if (c == 0)
                        continue;       /* ignore nulls */
                if (c == '\r')
                        continue;
                if (c == '\n' && v)
                        printf("\r%d", ++ct);
                fputc(c, ff);
        }
        if (v)
                prtime(" lines transferred in ", time(0)-start);
        usetchars ();
        write(fildes[1], (char *)&ccc, 1);
        signal(SIGINT, f);
        (void)fclose(ff);
}

/*
 * FTP - remote ==> local process
 *   send remote input to local process via pipe
 */
void
pipefile(int c)
{
        int cpid, pdes[2];
        char buf[256];
        int status, p;

        if (prompt("Local command? ", buf, sizeof(buf)))
                return;

        if (pipe(pdes)) {
                printf("can't establish pipe\r\n");
                return;
        }

        if ((cpid = fork()) < 0) {
                printf("can't fork!\r\n");
                return;
        } else if (cpid) {
                if (prompt("List command for remote system? ", buf, sizeof(buf))) {
                        close(pdes[0]), close(pdes[1]);
                        kill (cpid, SIGKILL);
                } else {
                        close(pdes[0]);
                        signal(SIGPIPE, intcopy);
                        transfer(buf, pdes[1], value(EOFREAD));
                        signal(SIGPIPE, SIG_DFL);
                        while ((p = wait(&status)) > 0 && p != cpid)
                                ;
                }
        } else {
                int f;

                dup2(pdes[0], 0);
                close(pdes[0]);
                for (f = 3; f < 20; f++)
                        close(f);
                execute(buf);
                printf("can't execl!\r\n");
                exit(0);
        }
}

/*
 * Interrupt service routine for FTP
 */
void
stopsnd(int __dummy)
{

        stop = 1;
        signal(SIGINT, SIG_IGN);
}

/*
 * FTP - local ==> remote
 *  send local file to remote host
 *  terminate transmission with pseudo EOF sequence
 */
void
sendfile(int c)
{
        FILE *fd;
        char *fnamex;

        putchar(c);
        /*
         * get file name
         */
        if (prompt("Local file name? ", fname, sizeof(fname)))
                return;

        /*
         * look up file
         */
        fnamex = expand(fname);
        if ((fd = fopen(fnamex, "r")) == NULL) {
                printf("%s: cannot open\r\n", fname);
                return;
        }
        transmit(fd, value(EOFWRITE), NULL);
        if (!boolean(value(ECHOCHECK))) {
                flush_remote ();
        }
}

/*
 * Bulk transfer routine to remote host --
 *   used by sendfile() and cu_put()
 */
static void
transmit(FILE *fd, char *eofchars, char *command)
{
        char *pc, lastc;
        int c, ccount, lcount;
        time_t start_t, stop_t;
        sig_t f;

        kill(pid, SIGIOT);      /* put TIPOUT into a wait state */
        stop = 0;
        f = signal(SIGINT, stopsnd);
        usedefchars ();
        read(repdes[0], (char *)&ccc, 1);
        if (command != NULL) {
                for (pc = command; *pc; pc++)
                        send(*pc);
                if (boolean(value(ECHOCHECK)))
                        read(FD, (char *)&c, 1);        /* trailing \n */
                else {
                        flush_remote ();
                        sleep(5); /* wait for remote stty to take effect */
                }
        }
        lcount = 0;
        lastc = '\0';
        start_t = time(0);
        while (1) {
                ccount = 0;
                do {
                        c = getc(fd);
                        if (stop)
                                goto out;
                        if (c == EOF)
                                goto out;
                        if (c == 0177 && !boolean(value(RAWFTP)))
                                continue;
                        lastc = c;
                        if (c < 040) {
                                if (c == '\n') {
                                        if (!boolean(value(RAWFTP)))
                                                c = '\r';
                                }
                                else if (c == '\t') {
                                        if (!boolean(value(RAWFTP))) {
                                                if (boolean(value(TABEXPAND))) {
                                                        send(' ');
                                                        while ((++ccount % 8) != 0)
                                                                send(' ');
                                                        continue;
                                                }
                                        }
                                } else
                                        if (!boolean(value(RAWFTP)))
                                                continue;
                        }
                        send(c);
                } while (c != '\r' && !boolean(value(RAWFTP)));
                if (boolean(value(VERBOSE)))
                        printf("\r%d", ++lcount);
                if (boolean(value(ECHOCHECK))) {
                        timedout = 0;
                        alarm(number(value(ETIMEOUT)));
                        do {    /* wait for prompt */
                                read(FD, (char *)&c, 1);
                                if (timedout || stop) {
                                        if (timedout)
                                                printf("\r\ntimed out at eol\r\n");
                                        alarm(0);
                                        goto out;
                                }
                        } while ((c&0177) != character(value(PROMPT)));
                        alarm(0);
                }
        }
out:
        if (lastc != '\n' && !boolean(value(RAWFTP)))
                send('\r');
        for (pc = eofchars; pc && *pc; pc++)
                send(*pc);
        stop_t = time(0);
        fclose(fd);
        signal(SIGINT, f);
        if (boolean(value(VERBOSE))) {
                if (boolean(value(RAWFTP)))
                        prtime(" chars transferred in ", stop_t-start_t);
                else
                        prtime(" lines transferred in ", stop_t-start_t);
        }
        write(fildes[1], (char *)&ccc, 1);
        usetchars ();
}

/*
 * Cu-like put command
 */
void
cu_put(int c)
{
        FILE *fd;
        char line[BUFSIZ];
        int argc;
        char *copynamex;

        if (prompt("[put] ", copyname, sizeof(copyname)))
                return;
        if ((argc = args(copyname, argv, sizeof(argv)/sizeof(argv[0]))) < 1 || argc > 2) {
                printf("usage: <put> from [to]\r\n");
                return;
        }
        if (argc == 1)
                argv[1] = argv[0];
        copynamex = expand(argv[0]);
        if ((fd = fopen(copynamex, "r")) == NULL) {
                printf("%s: cannot open\r\n", copynamex);
                return;
        }
        if (boolean(value(ECHOCHECK)))
                snprintf(line, sizeof(line), "cat>%s\r", argv[1]);
        else
                snprintf(line, sizeof(line), "stty -echo;cat>%s;stty echo\r", argv[1]);
        transmit(fd, "\04", line);
}


static int
nap(int msec)
{
        if (usleep(msec*1000) != 0) {
                fprintf ( stderr, "warning: ldelay or cdelay interrupted, "
                          "delay time cut short: %s\n",
                          strerror(errno) );
        }

        return 0;
}


/*
 * FTP - send single character
 *  wait for echo & handle timeout
 */
static void
send(char c)
{
        char cc;
        int retry = 0;

        cc = c;
        xpwrite(FD, &cc, 1);
        if (number(value(CDELAY)) > 0 && c != '\r')
                nap(number(value(CDELAY)));
        if (!boolean(value(ECHOCHECK))) {
                if (number(value(LDELAY)) > 0 && c == '\r')
                        nap(number(value(LDELAY)));
                return;
        }
tryagain:
        timedout = 0;
        alarm(number(value(ETIMEOUT)));
        read(FD, &cc, 1);
        alarm(0);
        if (timedout) {
                printf("\r\ntimeout error (%s)\r\n", ctrl(c));
                if (retry++ > 3)
                        return;
                xpwrite(FD, &null, 1); /* poke it */
                goto tryagain;
        }
}

void
timeoutfunc(int __dummy)
{
        signal(SIGALRM, timeoutfunc);
        timedout = 1;
}

/*
 * Stolen from consh() -- puts a remote file on the output of a local command.
 *      Identical to consh() except for where stdout goes.
 */
void
pipeout(int c)
{
        char buf[256];
        int cpid, status, p;
        time_t start;

        putchar(c);
        if (prompt("Local command? ", buf, sizeof(buf)))
                return;
        kill(pid, SIGIOT);      /* put TIPOUT into a wait state */
        signal(SIGINT, SIG_IGN);
        signal(SIGQUIT, SIG_IGN);
        usedefchars ();
        read(repdes[0], (char *)&ccc, 1);
        /*
         * Set up file descriptors in the child and
         *  let it go...
         */
        if ((cpid = fork()) < 0)
                printf("can't fork!\r\n");
        else if (cpid) {
                start = time(0);
                while ((p = wait(&status)) > 0 && p != cpid)
                        ;
        } else {
                int i;

                dup2(FD, 1);
                for (i = 3; i < 20; i++)
                        close(i);
                signal(SIGINT, SIG_DFL);
                signal(SIGQUIT, SIG_DFL);
                execute(buf);
                printf("can't find `%s'\r\n", buf);
                exit(0);
        }
        if (boolean(value(VERBOSE)))
                prtime("away for ", time(0)-start);
        write(fildes[1], (char *)&ccc, 1);
        usetchars ();
        signal(SIGINT, SIG_DFL);
        signal(SIGQUIT, SIG_DFL);
}

int
tiplink (char *cmd, unsigned int flags)
{
        int cpid, status, p;
        time_t start;

        if (flags & TL_SIGNAL_TIPOUT) {
                kill(pid, SIGIOT);      /* put TIPOUT into a wait state */
                signal(SIGINT, SIG_IGN);
                signal(SIGQUIT, SIG_IGN);
                usedefchars ();
                read(repdes[0], (char *)&ccc, 1);
        }

        /*
         * Set up file descriptors in the child and
         *  let it go...
         */
        if ((cpid = fork()) < 0)
                printf("can't fork!\r\n");
        else if (cpid) {
                start = time(0);
                while ((p = wait(&status)) > 0 && p != cpid)
                        ;
        } else {
                int fd;

                dup2(FD, 0);
                dup2(3, 1);
                for (fd = 3; fd < 20; fd++)
                        close (fd);
                signal(SIGINT, SIG_DFL);
                signal(SIGQUIT, SIG_DFL);
                execute (cmd);
                printf("can't find `%s'\r\n", cmd);
                exit(0);
        }

        if (flags & TL_VERBOSE && boolean(value(VERBOSE)))
                prtime("away for ", time(0)-start);

        if (flags & TL_SIGNAL_TIPOUT) {
                write(fildes[1], (char *)&ccc, 1);
                usetchars ();
                signal(SIGINT, SIG_DFL);
                signal(SIGQUIT, SIG_DFL);
        }

        return 0;
}

/*
 * Fork a program with:
 *  0 <-> remote tty in
 *  1 <-> remote tty out
 *  2 <-> local tty out
 */
void
consh(int c)
{
        char buf[256];
        putchar(c);
        if (prompt("Local command? ", buf, sizeof(buf)))
                return;
        tiplink (buf, TL_SIGNAL_TIPOUT | TL_VERBOSE);
}

/*
 * Escape to local shell
 */
void
shell(int c)
{
        int shpid, status;
        char *cp;

        printf("[sh]\r\n");
        signal(SIGINT, SIG_IGN);
        signal(SIGQUIT, SIG_IGN);
        unraw();
        if ((shpid = fork())) {
                while (shpid != wait(&status));
                raw();
                printf("\r\n!\r\n");
                signal(SIGINT, SIG_DFL);
                signal(SIGQUIT, SIG_DFL);
                return;
        } else {
                signal(SIGQUIT, SIG_DFL);
                signal(SIGINT, SIG_DFL);
                if ((cp = rindex(value(SHELL), '/')) == NULL)
                        cp = value(SHELL);
                else
                        cp++;
                shell_uid();
                execl(value(SHELL), cp, NULL);
                printf("\r\ncan't execl!\r\n");
                exit(1);
        }
}

/*
 * TIPIN portion of scripting
 *   initiate the conversation with TIPOUT
 */
void
setscript(void)
{
        char c;
        /*
         * enable TIPOUT side for dialogue
         */
        kill(pid, SIGEMT);
        if (boolean(value(SCRIPT)))
                write(fildes[1], value(RECORD), size(value(RECORD)));
        write(fildes[1], "\n", 1);
        /*
         * wait for TIPOUT to finish
         */
        read(repdes[0], &c, 1);
        if (c == 'n')
                printf("can't create %s\r\n", value(RECORD));
}

/*
 * Change current working directory of
 *   local portion of tip
 */
void
chdirectory(int c)
{
        char dirname[PATH_MAX];
        char *cp = dirname;

        if (prompt("[cd] ", dirname, sizeof(dirname))) {
                if (stoprompt)
                        return;
                cp = value(HOME);
        }
        if (chdir(cp) < 0)
                printf("%s: bad directory\r\n", cp);
        printf("!\r\n");
}

void
tipabort(char *msg)
{

        kill(pid, SIGTERM);
        disconnect(msg);
        if (msg != NULL)
                printf("\r\n%s", msg);
        printf("\r\n[EOT]\r\n");
        daemon_uid();
        (void)uu_unlock(uucplock);
        unraw();
        exit(0);
}

void
finish(int c)
{
        char *abortmsg = NULL, *dismsg;

        if (LO != NULL && tiplink (LO, TL_SIGNAL_TIPOUT) != 0) {
                abortmsg = "logout failed";
        }

        if ((dismsg = value(DISCONNECT)) != NULL) {
                write(FD, dismsg, strlen(dismsg));
                sleep (2);
        }
        tipabort(abortmsg);
}

void
intcopy(int signo)
{
        raw();
        quit = 1;
        longjmp(intbuf, 1);
}

static void
execute(char *s)
{
        char *cp;

        if ((cp = rindex(value(SHELL), '/')) == NULL)
                cp = value(SHELL);
        else
                cp++;
        shell_uid();
        execl(value(SHELL), cp, "-c", s, NULL);
}

static int
args(char *buf, char **a, int num)
{
        char *p = buf, *start;
        char **parg = a;
        int n = 0;

        while (*p && n < num) {
                while (*p && (*p == ' ' || *p == '\t'))
                        p++;
                start = p;
                if (*p)
                        *parg = p;
                while (*p && (*p != ' ' && *p != '\t'))
                        p++;
                if (p != start)
                        parg++, n++;
                if (*p)
                        *p++ = '\0';
        }
        return(n);
}

static void
prtime(char *s, time_t a)
{
        int i;
        int nums[3];

        for (i = 0; i < 3; i++) {
                nums[i] = (int)(a % quant[i]);
                a /= quant[i];
        }
        printf("%s", s);
        while (--i >= 0)
                if (nums[i] || (i == 0 && nums[1] == 0 && nums[2] == 0))
                        printf("%d %s%c ", nums[i], sep[i],
                                nums[i] == 1 ? '\0' : 's');
        printf("\r\n!\r\n");
}

void
variable(int c)
{
        char    buf[256];

        if (prompt("[set] ", buf, sizeof(buf)))
                return;
        vlex(buf);
        if (vtable[BEAUTIFY].v_access&CHANGED) {
                vtable[BEAUTIFY].v_access &= ~CHANGED;
                kill(pid, SIGSYS);
        }
        if (vtable[SCRIPT].v_access&CHANGED) {
                vtable[SCRIPT].v_access &= ~CHANGED;
                setscript();
                /*
                 * So that "set record=blah script" doesn't
                 *  cause two transactions to occur.
                 */
                if (vtable[RECORD].v_access&CHANGED)
                        vtable[RECORD].v_access &= ~CHANGED;
        }
        if (vtable[RECORD].v_access&CHANGED) {
                vtable[RECORD].v_access &= ~CHANGED;
                if (boolean(value(SCRIPT)))
                        setscript();
        }
        if (vtable[TAND].v_access&CHANGED) {
                vtable[TAND].v_access &= ~CHANGED;
                if (boolean(value(TAND)))
                        tandem("on");
                else
                        tandem("off");
        }
        if (vtable[LECHO].v_access&CHANGED) {
                vtable[LECHO].v_access &= ~CHANGED;
                HD = boolean(value(LECHO));
        }
        if (vtable[PARITY].v_access&CHANGED) {
                vtable[PARITY].v_access &= ~CHANGED;
                setparity(value(PARITY));
        }
}

/*
 * Turn tandem mode on or off for remote tty.
 */
static void
tandem(char *option)
{
        struct termios ttermios;
        tcgetattr (FD, &ttermios);
        if (strcmp(option,"on") == 0) {
                ttermios.c_iflag |= IXOFF;
                ctermios.c_iflag |= IXOFF;
        }
        else {
                ttermios.c_iflag &= ~IXOFF;
                ctermios.c_iflag &= ~IXOFF;
        }
        tcsetattr (FD, TCSANOW, &ttermios);
        tcsetattr (0, TCSANOW, &ctermios);
}

/*
 * Send a break.
 */
void
genbrk(int c)
{

        ioctl(FD, TIOCSBRK, NULL);
        sleep(1);
        ioctl(FD, TIOCCBRK, NULL);
}

/*
 * Suspend tip
 */
void
suspend(int c)
{

        unraw();
        kill(c == CTRL('y') ? getpid() : 0, SIGTSTP);
        raw();
}

/*
 *      expand a file name if it includes shell meta characters
 */

char *
expand(char *name)
{
        static char xname[BUFSIZ];
        char cmdbuf[BUFSIZ];
        int pid, l;
        char *cp, *Shell;
        int s, pivec[2] /*, (*sigint)()*/;

        if (!anyof(name, "~{[*?$`'\"\\"))
                return(name);
        /* sigint = signal(SIGINT, SIG_IGN); */
        if (pipe(pivec) < 0) {
                warn("pipe");
                /* signal(SIGINT, sigint) */
                return(name);
        }
        snprintf(cmdbuf, sizeof(cmdbuf), "echo %s", name);
        if ((pid = vfork()) == 0) {
                Shell = value(SHELL);
                if (Shell == NULL)
                        Shell = _PATH_BSHELL;
                close(pivec[0]);
                close(1);
                dup(pivec[1]);
                close(pivec[1]);
                close(2);
                shell_uid();
                execl(Shell, Shell, "-c", cmdbuf, NULL);
                _exit(1);
        }
        if (pid == -1) {
                warn("fork");
                close(pivec[0]);
                close(pivec[1]);
                return(NULL);
        }
        close(pivec[1]);
        l = read(pivec[0], xname, BUFSIZ);
        close(pivec[0]);
        while (wait(&s) != pid)
                ;
        s &= 0377;
        if (s != 0 && s != SIGPIPE) {
                fprintf(stderr, "\"Echo\" failed\n");
                return(NULL);
        }
        if (l < 0) {
                warn("read");
                return(NULL);
        }
        if (l == 0) {
                fprintf(stderr, "\"%s\": No match\n", name);
                return(NULL);
        }
        if (l == BUFSIZ) {
                fprintf(stderr, "Buffer overflow expanding \"%s\"\n", name);
                return(NULL);
        }
        xname[l] = 0;
        for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
                ;
        *++cp = '\0';
        return(xname);
}

/*
 * Are any of the characters in the two strings the same?
 */

static int
anyof(char *s1, char *s2)
{
        int c;

        while ((c = *s1++))
                if (any(c, s2))
                        return(1);
        return(0);
}