root/usr/src/ucbcmd/stty/sttyparse.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 1997 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <termio.h>
#include <sys/stermio.h>
#include <sys/termiox.h>
#include "stty.h"

static char     *s_arg;                 /* s_arg: ptr to mode to be set */
static int      match;
static int gct(), eq(), encode();
static int eqarg(char *, int);

/* set terminal modes for supplied options */
char *
sttyparse(argc, argv, term, ocb, cb, termiox, winsize)
int     argc;
char    *argv[];
int     term; /* type of tty device, -1 means allow all options,
               * no sanity check
               */
struct  termio  *ocb;
struct  termios *cb;
struct  termiox *termiox;
struct  winsize *winsize;
{
        int i;
        extern  const struct    speeds  speeds[];
        extern  const struct    mds     lmodes[];
        extern  const struct    mds     nlmodes[];
        extern  const struct    mds     cmodes[];
        extern  const struct    mds     ncmodes[];
        extern  const struct    mds     imodes[];
        extern  const struct    mds     nimodes[];
        extern  const struct    mds     omodes[];
        extern  const struct    mds     hmodes[];
        extern  const struct    mds     clkmodes[];

        while(--argc > 0) {

                s_arg = *++argv;
                match = 0;
                if ((term & ASYNC) || term == -1) {
                        if (eqarg("erase", argc) && --argc)
                                cb->c_cc[VERASE] = gct(*++argv, term);
                        else if (eqarg("intr", argc) && --argc)
                                cb->c_cc[VINTR] = gct(*++argv, term);
                        else if (eqarg("quit", argc) && --argc)
                                cb->c_cc[VQUIT] = gct(*++argv, term);
                        else if (eqarg("eof", argc) && --argc)
                                cb->c_cc[VEOF] = gct(*++argv, term);
                        else if (eqarg("min", argc) && --argc)
                                cb->c_cc[VMIN] = atoi(*++argv);
                        else if (eqarg("eol", argc) && --argc)
                                cb->c_cc[VEOL] = gct(*++argv, term);
                        else if (eqarg("brk", argc) && --argc)
                                cb->c_cc[VEOL] = gct(*++argv, term);
                        else if (eqarg("eol2", argc) && --argc)
                                cb->c_cc[VEOL2] = gct(*++argv, term);
                        else if (eqarg("time", argc) && --argc)
                                cb->c_cc[VTIME] = atoi(*++argv);
                        else if (eqarg("kill", argc) && --argc)
                                cb->c_cc[VKILL] = gct(*++argv, term);
                        else if (eqarg("swtch", argc) && --argc)
                                cb->c_cc[VSWTCH] = gct(*++argv, term);
                        if(match)
                                continue;
                        if((term & TERMIOS) || term == -1) {
                                if (eqarg("start", argc) && --argc)
                                        cb->c_cc[VSTART] = gct(*++argv, term);
                                else if (eqarg("stop", argc) && --argc)
                                        cb->c_cc[VSTOP] = gct(*++argv, term);
                                else if (eqarg("susp", argc) && --argc)
                                        cb->c_cc[VSUSP] = gct(*++argv, term);
                                else if (eqarg("dsusp", argc) && --argc)
                                        cb->c_cc[VDSUSP] = gct(*++argv, term);
                                else if (eqarg("rprnt", argc) && --argc)
                                        cb->c_cc[VREPRINT] = gct(*++argv, term);
                                else if (eqarg("flush", argc) && --argc)
                                        cb->c_cc[VDISCARD] = gct(*++argv, term);
                                else if (eqarg("werase", argc) && --argc)
                                        cb->c_cc[VWERASE] = gct(*++argv, term);
                                else if (eqarg("lnext", argc) && --argc)
                                        cb->c_cc[VLNEXT] = gct(*++argv, term);
                        }
                        if(match)
                                continue;
                        if (eq("ek")) {
                                cb->c_cc[VERASE] = CERASE;
                                cb->c_cc[VKILL] = CKILL;
                        }
                        else if (eq("crt") || eq("newcrt")) {
                                cb->c_lflag &= ~ECHOPRT;
                                cb->c_lflag |= ECHOE|ECHOCTL;
                                if (cfgetospeed(cb) >= B1200)
                                        cb->c_lflag |= ECHOKE;
                        }
                        else if (eq("dec")) {
                                cb->c_cc[VERASE] = 0177;
                                cb->c_cc[VKILL] = CTRL('u');
                                cb->c_cc[VINTR] = CTRL('c');
                                cb->c_lflag &= ~ECHOPRT;
                                cb->c_lflag |= ECHOE|ECHOCTL|IEXTEN;
                                if (cfgetospeed(cb) >= B1200)
                                        cb->c_lflag |= ECHOKE;
                        }
                        else if (eqarg("line", argc) && (!(term & TERMIOS) || term == -1) && --argc) {
                                ocb->c_line = atoi(*++argv);
                                continue;
                        }
                        else if (eq("raw") || eq("cbreak")) {
                                cb->c_cc[VMIN] = 1;
                                cb->c_cc[VTIME] = 0;
                        }
                        else if (eq("-raw") || eq("-cbreak") || eq("cooked")) {
                                cb->c_cc[VEOF] = CEOF;
                                cb->c_cc[VEOL] = CNUL;
                        }
                        else if(eq("sane")) {
                                cb->c_cc[VERASE] = CERASE;
                                cb->c_cc[VKILL] = CKILL;
                                cb->c_cc[VQUIT] = CQUIT;
                                cb->c_cc[VINTR] = CINTR;
                                cb->c_cc[VEOF] = CEOF;
                                cb->c_cc[VEOL] = CNUL;
                                                           /* SWTCH purposely not set */
                        }
                        else if((term & TERMIOS) && eqarg("ospeed", argc) && --argc) {
                                s_arg = *++argv;
                                match = 0;
                                for(i=0; speeds[i].string; i++)
                                        if(eq(speeds[i].string))
                                            cfsetospeed(cb, speeds[i].speed);
                                if(!match)
                                        return s_arg;
                                continue;
                        }
                        else if((term & TERMIOS) && eqarg("ispeed", argc) && --argc) {
                                s_arg = *++argv;
                                match = 0;
                                for(i=0; speeds[i].string; i++)
                                        if(eq(speeds[i].string))
                                            cfsetispeed(cb, speeds[i].speed);
                                if(!match)
                                        return s_arg;
                                continue;
                        }
                        else if (argc == 0) {
                                (void) fprintf(stderr, "stty: No argument for \"%s\"\n", s_arg);
                                exit(1);
                        }
                        for(i=0; speeds[i].string; i++)
                                if(eq(speeds[i].string)) {
                                        cfsetospeed(cb, B0);
                                        cfsetispeed(cb, B0);
                                        cfsetospeed(cb, speeds[i].speed);
                                }
                }
                if ((!(term & ASYNC) || term == -1) && eqarg("ctab", argc) && --argc) {
                        cb->c_cc[7] = gct(*++argv, term);
                        continue;
                }
                else if (argc == 0) {
                        (void) fprintf(stderr, "stty: No argument for \"%s\"\n", s_arg);
                        exit(1);
                }

                for(i=0; imodes[i].string; i++)
                        if(eq(imodes[i].string)) {
                                cb->c_iflag &= ~imodes[i].reset;
                                cb->c_iflag |= imodes[i].set;
                        }
                if((term & TERMIOS) || term == -1) {
                        for(i=0; nimodes[i].string; i++)
                                if(eq(nimodes[i].string)) {
                                        cb->c_iflag &= ~nimodes[i].reset;
                                        cb->c_iflag |= nimodes[i].set;
                                }
                }

                for(i=0; omodes[i].string; i++)
                        if(eq(omodes[i].string)) {
                                cb->c_oflag &= ~omodes[i].reset;
                                cb->c_oflag |= omodes[i].set;
                        }
                if((!(term & ASYNC) || term == -1) && eq("sane")) {
                        cb->c_oflag |= TAB3;
                        continue;
                }
                for(i=0; cmodes[i].string; i++)
                        if(eq(cmodes[i].string)) {
                                cb->c_cflag &= ~cmodes[i].reset;
                                cb->c_cflag |= cmodes[i].set;
                        }
                if((term & TERMIOS) || term == -1)
                        for(i=0; ncmodes[i].string; i++)
                                if(eq(ncmodes[i].string)) {
                                        cb->c_cflag &= ~ncmodes[i].reset;
                                        cb->c_cflag |= ncmodes[i].set;
                                }
                for(i=0; lmodes[i].string; i++)
                        if(eq(lmodes[i].string)) {
                                cb->c_lflag &= ~lmodes[i].reset;
                                cb->c_lflag |= lmodes[i].set;
                        }
                if((term & TERMIOS) || term == -1)
                        for(i=0; nlmodes[i].string; i++)
                                if(eq(nlmodes[i].string)) {
                                        cb->c_lflag &= ~nlmodes[i].reset;
                                        cb->c_lflag |= nlmodes[i].set;
                                }
                if((term & FLOW) || term == -1) {
                        for(i=0; hmodes[i].string; i++)
                                if(eq(hmodes[i].string)) {
                                        termiox->x_hflag &= ~hmodes[i].reset;
                                        termiox->x_hflag |= hmodes[i].set;
                                }
                        for(i=0; clkmodes[i].string; i++)
                                if(eq(clkmodes[i].string)) {
                                        termiox->x_cflag &= ~clkmodes[i].reset;
                                        termiox->x_cflag |= clkmodes[i].set;
                                }

                }
                if(eqarg("rows", argc) && --argc)
                        winsize->ws_row = atoi(*++argv);
                else if((eqarg("columns", argc) || eqarg("cols", argc)) && --argc)
                        winsize->ws_col = atoi(*++argv);
                else if(eqarg("xpixels", argc) && --argc)
                        winsize->ws_xpixel = atoi(*++argv);
                else if(eqarg("ypixels", argc) && --argc)
                        winsize->ws_ypixel = atoi(*++argv);
                else if (argc == 0) {
                        (void) fprintf(stderr, "stty: No argument for \"%s\"\n", s_arg);
                        exit(1);
                }
                if(!match)
                        if(!encode(cb, term)) {
                                return(s_arg); /* parsing failed */
                        }
        }
        return((char *)0);
}

static int eq(string)
char *string;
{
        int i;

        if(!s_arg)
                return(0);
        i = 0;
loop:
        if(s_arg[i] != string[i])
                return(0);
        if(s_arg[i++] != '\0')
                goto loop;
        match++;
        return(1);
}

/* Checks for options that require an argument */
static int
eqarg(char *string, int argc)
{
        int status;

        if ((status = eq(string)) == 1) {
                if (argc <= 1) {
                        (void) fprintf(stderr, "stty: No argument for \"%s\"\n",
                                                s_arg);
                        exit(1);
                }
        }
        return(status);
}

/* get pseudo control characters from terminal */
/* and convert to internal representation      */
static int gct(cp, term)
char *cp;
int term;
{
        int c;

        c = *cp++;
        if (c == '^') {
                c = *cp;
                if (c == '?')
                        c = 0177;               /* map '^?' to DEL */
                else if (c == '-')
                        c = (term & TERMIOS) ? _POSIX_VDISABLE : 0200;          /* map '^-' to undefined */
                else
                        c &= 037;
        }
        return(c);
}

/* get modes of tty device and fill in applicable structures */
int
get_ttymode(fd, termio, termios, stermio, termiox, winsize)
int fd;
struct termio *termio;
struct termios *termios;
struct stio *stermio;
struct termiox *termiox;
struct winsize *winsize;
{
        int i;
        int term = 0;
        if(ioctl(fd, STGET, stermio) == -1) {
                term |= ASYNC;
                if(ioctl(fd, TCGETS, termios) == -1) {
                        if(ioctl(fd, TCGETA, termio) == -1)
                                return -1;
                        termios->c_lflag = termio->c_lflag;
                        termios->c_oflag = termio->c_oflag;
                        termios->c_iflag = termio->c_iflag;
                        termios->c_cflag = termio->c_cflag;
                        for(i = 0; i < NCC; i++)
                                termios->c_cc[i] = termio->c_cc[i];
                } else
                        term |= TERMIOS;
        }
        else {
                termios->c_cc[7] = (unsigned)stermio->tab;
                termios->c_lflag = stermio->lmode;
                termios->c_oflag = stermio->omode;
                termios->c_iflag = stermio->imode;
        }

        if(ioctl(fd, TCGETX, termiox) == 0)
                term |= FLOW;

        if(ioctl(fd, TIOCGWINSZ, winsize) == 0)
                term |= WINDOW;
        return term;
}

/* set tty modes */
int
set_ttymode(fd, term, termio, termios, stermio, termiox, winsize, owinsize)
int fd, term;
struct termio *termio;
struct termios *termios;
struct stio *stermio;
struct termiox *termiox;
struct winsize *winsize, *owinsize;
{
        int i;
        if (term & ASYNC) {
                if(term & TERMIOS) {
                        if(ioctl(fd, TCSETSW, termios) == -1)
                                return -1;
                } else {
                        termio->c_lflag = termios->c_lflag;
                        termio->c_oflag = termios->c_oflag;
                        termio->c_iflag = termios->c_iflag;
                        termio->c_cflag = termios->c_cflag;
                        for(i = 0; i < NCC; i++)
                                termio->c_cc[i] = termios->c_cc[i];
                        if(ioctl(fd, TCSETAW, termio) == -1)
                                return -1;
                }

        } else {
                stermio->imode = termios->c_iflag;
                stermio->omode = termios->c_oflag;
                stermio->lmode = termios->c_lflag;
                stermio->tab = termios->c_cc[7];
                if (ioctl(fd, STSET, stermio) == -1)
                        return -1;
        }
        if(term & FLOW) {
                if(ioctl(fd, TCSETXW, termiox) == -1)
                        return -1;
        }
        if((owinsize->ws_col != winsize->ws_col
           || owinsize->ws_row != winsize->ws_row)
           && ioctl(0, TIOCSWINSZ, winsize) != 0)
                return -1;
        return 0;
}

static int encode(cb, term)
struct  termios *cb;
int term;
{
        unsigned long grab[20], i;
        int last;
        i = sscanf(s_arg,
        "%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx:%lx",
        &grab[0],&grab[1],&grab[2],&grab[3],&grab[4],&grab[5],&grab[6],
        &grab[7],&grab[8],&grab[9],&grab[10],&grab[11],
        &grab[12], &grab[13], &grab[14], &grab[15],
        &grab[16], &grab[17], &grab[18], &grab[19]);

        if((term & TERMIOS) && i < 20 && term != -1 || i < 12)
                return(0);
        cb->c_iflag = grab[0];
        cb->c_oflag = grab[1];
        cb->c_cflag = grab[2];
        cb->c_lflag = grab[3];

        if(term & TERMIOS)
                last = NCCS - 1;
        else
                last = NCC;
        for(i=0; i<last; i++)
                cb->c_cc[i] = (unsigned char) grab[i+4];
        return(1);
}