static const char copyr[] = "@(#)gettydefs.c	1.6 94/03/13 Copyright(c) 1993 Gert Doering/Chris Lewis"
;
/* gettydefs.c
 *
 * Read /etc/gettydefs file, and permit retrieval of individual entries.
 *
 * Code in this module by Chris Lewis
 */

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <malloc.h>

#include "gettydefs.h"

int gettycheck = 0;	/* set to 1 for getty -c */
int lineno;		/* when getty -c runs */
int gettyerrors = 0;	/* # of errors encountered processing gettytab */

#define BAD(fmt, arg)	(gettycheck && (((lineno) && printf("%d: ", lineno)),\
					printf(fmt, arg),\
					putchar('\n'),\
					gettyerrors++\
				       ))

struct modeword {
    char *name;
    int turnon;
    int turnoff;
    int metaon;
    int metaoff;
};

/*	Meta tokens */
#define SANE	0x0001
#define	ODDP	0x0002

#define	PARITY	0x0004
#define	NPARITY	0x0008

#define RAW	0x0010
#define	COOKED	0x0020

#define NL	0x0040
#define NNL	0x0080

#define LCASE	0x0100
#define NLCASE	0x0200

#define TABS	0x0400
#define	NTABS	0x0800

/* input modes */

static struct modeword iflags[] = {
    { "ignbrk", IGNBRK, IGNBRK },
    { "brkint", BRKINT, BRKINT, SANE },
    { "ignpar", IGNPAR, IGNPAR, SANE },
    { "parmrk", PARMRK, PARMRK },
    { "inpck", INPCK, INPCK },
    { "istrip", ISTRIP, ISTRIP, SANE },
    { "inlcr", INLCR, INLCR, 0, NNL },
    { "igncr", IGNCR, IGNCR, 0, NNL },
    { "icrnl", ICRNL, ICRNL,(SANE|NL), NNL },
    { "iuclc", IUCLC, IUCLC, LCASE, NLCASE },
    { "ixon", IXON, IXON, SANE },
    { "ixany", IXANY, IXANY },
    { "ixoff", IXOFF, IXOFF },
    { NULL }
};

/* output modes */

static struct modeword oflags[] = {
    { "opost", OPOST, OPOST,(SANE|COOKED), RAW },
    { "olcuc", OLCUC, OLCUC, LCASE, NLCASE },
    { "onlcr", ONLCR, ONLCR, (SANE|NL), NNL },
    { "ocrnl", OCRNL, OCRNL, 0, NNL },
    { "onocr", ONOCR, ONOCR },
    { "onlret", ONLRET, ONLRET, NNL },
    { "ofill", OFILL, OFILL },
    { "ofdel", OFDEL, OFDEL },
    { "nldly", NLDLY, NLDLY },
    { "nl0", NL0, NLDLY },
    { "nl1", NL1, NLDLY },
    { "cr0", CR0, CRDLY },
    { "cr1", CR1, CRDLY },
    { "cr2", CR2, CRDLY },
    { "cr3", CR3, CRDLY },
    { "tab0", TAB0, TABDLY, TABS },
    { "tab1", TAB1, TABDLY },
    { "tab2", TAB2, TABDLY },
    { "Tab3", TAB3, TABDLY, NTABS },
    { "bs0", BS0, BSDLY },
    { "bs1", BS1, BSDLY },
    { "vt0", VT0, VTDLY },
    { "vt1", VT1, VTDLY },
    { "ff0", FF0, FFDLY },
    { "ff1", FF1, FFDLY },
    { NULL }
};

/* control modes */

static struct modeword cflags[] = {
    { "b0", B0, CBAUD },
    { "b50", B50, CBAUD },
    { "b75", B75, CBAUD },
    { "b110", B110, CBAUD },
    { "b134", B134, CBAUD },
    { "b150", B150, CBAUD },
    { "b200", B200, CBAUD },
    { "b300", B300, CBAUD },
    { "b600", B600, CBAUD },
#ifdef B900
    { "b900", B900, CBAUD },
#endif
    { "b1200", B1200, CBAUD },
    { "b1800", B1800, CBAUD },
    { "b2400", B2400, CBAUD },
#ifdef B3600
    { "b3600", B3600, CBAUD },
#endif
    { "b4800", B4800, CBAUD },
#ifdef B7200
    { "b7200", B7200, CBAUD },
#endif
    { "b9600", B9600, CBAUD },
#ifdef B19200
    { "b19200", B19200, CBAUD },
#endif
#ifdef B38400
    { "b38400", B38400, CBAUD },
#endif
#ifdef B57600
    { "b57600", B57600, CBAUD },
#endif
#ifdef B115200
    { "b115200", B115200, CBAUD },
#endif
#ifdef B230400
    { "b230400", B230400, CBAUD },
#endif
#ifdef B230400
    { "b230400", B230400, CBAUD },
#endif
#ifdef B460800
    { "b460800", B460800, CBAUD },
#endif
    { "exta", EXTA, CBAUD },
    { "extb", EXTB, CBAUD },
    { "cs5", CS5, CSIZE },
    { "cs6", CS6, CSIZE },
    { "cs7", CS7, CSIZE,(ODDP|PARITY) },
    { "cs8", CS8, CSIZE,(SANE|NPARITY) },
    { "cstopb", CSTOPB, CSTOPB },
    { "cread", CREAD, CREAD, SANE },
    { "parenb", PARENB, PARENB,(ODDP|PARITY),(NPARITY) },
    { "parodd", PARODD, PARODD, ODDP },
    { "hupcl", HUPCL, HUPCL },
    { "clocal", CLOCAL, CLOCAL },
/* Various handshaking defines */
#ifdef CTSCD
    { "ctscd", CTSCD, CTSCD },
#endif
#ifdef CRTSCTS
    { "crtscts", CRTSCTS, CRTSCTS },
#endif
#ifdef CRTSFL
    { "crtsfl", CRTSFL, CRTSFL },
#endif
#ifdef RTSFLOW
    { "rtsflow", RTSFLOW, RTSFLOW },
    { "ctsflow", CTSFLOW, CTSFLOW },
#endif
#ifdef HDX
    { "hdx", HDX, HDX },
#endif
    { NULL }
};

/* line discipline */
static struct modeword lflags[] =  {
    { "isig", ISIG, ISIG, SANE },
    { "icanon", ICANON, ICANON,(SANE|COOKED), RAW },
    { "xcase", XCASE, XCASE, LCASE, NLCASE },
    { "echo", ECHO, ECHO, SANE },
    { "echoe", ECHOE, ECHOE, SANE },
    { "echok", ECHOK, ECHOK, SANE },
    { "echonl", ECHONL, ECHONL },
    { "echoprt", ECHOPRT, ECHOPRT },
    { "noflsh", NOFLSH, NOFLSH },
    { NULL }
};

#define CINTR	003	/* ^C */
#define	CQUIT	034	/* ^\ */
#define CERASE	010	/* ^H */
#define	CKILL	025	/* ^U */
#define CEOF	004	/* ^D */
#define CEOL	015	/* ^M */
#define CSUSP	032	/* ^Z */
#define CSTART	021	/* ^Q */
#define CSTOP	023	/* ^S */
#define	CRPRNT	022	/* ^R */
#define CFLUSH	017	/* ^O */
#define	CWERASE	027	/* ^W */
#define CLNEXT	026	/* ^V */

/* c_cc special characters */
static struct modeword ccchars[] = {
    {"vintr", VINTR, CINTR},
    {"vquit", VQUIT, CQUIT},
    {"verase", VERASE, CERASE},
    {"vkill", VKILL, CKILL},
    {"veof", VEOF, CEOF},
#if defined(VEOL) && defined(TIONCC) && VEOL < TIONCC
    {"veol", VEOL, CEOL},
#endif
#if defined(CEOL2) && defined(VEOL2) && VEOL2 < TIONCC
    {"veol2", VEOL2, CEOL2},
#endif
#if defined(VSUSP) && VSUSP < TIONCC
    {"vsusp", VSUSP, CSUSP},
#endif
#if defined(VSTART) && VSTART < TIONCC
    {"vstart", VSTART, CSTART},
#endif
#if defined(VSTOP) && VSTOP < TIONCC
    {"vstop", VSTOP, CSTOP},
#endif
#if defined(VSWTCH) && VSWTCH < TIONCC
    {"vswtch", VSWTCH, CSWTCH},
#endif
/* SVR4.2 */
#if defined(VDSUSP) && VDSUSP < TIONCC
   {"vdsusp", VDSUSP, CDSUSP},
#endif
#if defined(VREPRINT) && VREPRINT < TIONCC
   {"vreprint", VREPRINT, CRPRNT},
#endif
#if defined(VDISCARD) && VDISCARD < TIONCC
   {"vdiscard", VDISCARD, CFLUSH},
#endif
#if defined(VWERASE) && VWERASE < TIONCC
   {"vwerase", VWERASE, CWERASE},
#endif
#if defined(VLNEXT) && VLNEXT < TIONCC
   {"vlnext", VLNEXT, CLNEXT},
#endif
    {"vmin", VMIN, 0},
    {"vtime", VTIME, 0},
    { NULL }
};

struct modeword metatokens[] = {
    { "sane", SANE },
    { "oddp", ODDP },

    { "parity", PARITY },
    { "evenp", PARITY },
    { "-oddp", NPARITY },
    { "-parity", NPARITY },
    { "-evenp", NPARITY },

    { "raw", RAW },
    { "-raw", COOKED },
    { "cooked", COOKED },

    { "nl", NL, NNL },

    { "lcase", LCASE, NLCASE },

    { "tabs", TABS, NTABS },
    { "tab3", NTABS },

    { NULL }
};

#define GDCHUNK	5

GDE *gdep =(GDE *) NULL;
GDE *cur =(GDE *) NULL;
static int cntalloc = 0;

static struct modeword *
findmode(struct modeword *modes, register char *tok)
{
    for ( ; modes->name; modes++)
	if (strcasecmp(modes->name, tok) == 0)
	    return(modes);
    return((struct modeword *) NULL);
}

static void
metaset(tcflag_t *tc, struct modeword *modes, int key)
{
    for (; modes->name; modes++) {
	if (modes->metaon&key)
	    *tc =(*tc & ~ modes->turnoff) | modes->turnon;
	if (modes->metaoff&key)
	    *tc =(*tc & ~ modes->turnoff);
    }
}

static void
tio_default_cc(struct termios *t)
{
    t->c_cc[VQUIT]  = CQUIT;
    t->c_cc[VKILL]  = CKILL;
    t->c_cc[VEOF]   = CEOF;
#if defined(VEOL) && VEOL < TIONCC
    t->c_cc[VEOL] = CEOL;
#endif
#if defined(VSTART) && VSTART < TIONCC
    t->c_cc[VSTART] = CSTART;
#endif
#if defined(VSTOP) && VSTOP < TIONCC
    t->c_cc[VSTOP] = CSTOP;
#endif
#if defined(VSUSP) && VSUSP < TIONCC
    t->c_cc[VSUSP] = CSUSP;
#endif
#if defined(VSWTCH) && VSWTCH < TIONCC
    t->c_cc[VSWTCH] = CSWTCH;
#endif
    /* the following are for SVR4.2 (and higher) */
#if defined(VDSUSP) && VDSUSP < TIONCC
    t->c_cc[VDSUSP] = CDSUSP;
#endif
#if defined(VREPRINT) && VREPRINT < TIONCC
    t->c_cc[VREPRINT] = CRPRNT;
#endif
#if defined(VDISCARD) && VDISCARD < TIONCC
    t->c_cc[VDISCARD] = CFLUSH;
#endif
#if defined(VWERASE) && VWERASE < TIONCC
    t->c_cc[VWERASE] = CWERASE;
#endif
#if defined(VLNEXT) && VLNEXT < TIONCC
    t->c_cc[VLNEXT] = CLNEXT;
#endif
}


static void
parsetermio(struct termios *ti, char *str)
{
    register char *p;
    struct modeword *m;
    tcflag_t *flag;
    int metakey;

    /* initialize c_cc[] array(tio_* doesn't init INTR/ERASE!) */
    tio_default_cc(ti);
    ti->c_cc[VINTR] = CINTR;
    ti->c_cc[VERASE] = CERASE;
    ti->c_cc[VMIN] = 1;
    ti->c_cc[VTIME] = 0;

#ifndef POSIX_TERMIOS
    ti->c_line = 0;
#endif

    for (p = str; *p; p++)
	if (islower(*p))
	    *p = toupper(*p);

    while ((p = strtok(str, " \t")) != NULL ) {
	int not = 0;

	str = NULL;

	metakey = 0;

	if (strcasecmp(p, "ek") == 0) {
	    ti->c_cc[VERASE] = '#';
	    ti->c_cc[VKILL] = CKILL;
	    continue;
	}

	for (m = metatokens; m->name; m++)
	    if (strcasecmp(p, m->name) == 0) {
		metakey = m->turnon;
		break;
	    }
	
	if (metakey) {
	    metaset(&ti->c_lflag, lflags, metakey);
	    metaset(&ti->c_oflag, oflags, metakey);
	    metaset(&ti->c_iflag, iflags, metakey);
	    metaset(&ti->c_cflag, cflags, metakey);
	    continue;
	}

	if (*p == '-') {
	    not = 1;
	    p++;
	}

	if     ((m = findmode(lflags, p)) != NULL)
	    flag = &ti->c_lflag;
	else if ((m = findmode(oflags, p)) != NULL)
	    flag = &ti->c_oflag;
	else if ((m = findmode(iflags, p)) != NULL)
	    flag = &ti->c_iflag;
	else if ((m = findmode(cflags, p)) != NULL)
	    flag = &ti->c_cflag;
	if (m) {
	    if (not)
		*flag =(*flag & ~ m->turnoff);
	    else
		*flag =(*flag & ~ m->turnoff) | m->turnon;
	} else {
	    if ((m = findmode(ccchars, p)) != NULL) {
		char *p2;
		if ((p2 = strtok(str, " \t")) == 0) {
		    BAD("parse error", "");
		    return;	/* can't find field */
		}

		if (*p2 == '\\')
		    switch(*(p2+1)) {
			case 'n': *p2 = '\n'; break;
			case 'r': *p2 = '\r'; break;
			case 'b': *p2 = '\010'; break;
			case 'v': *p2 = '\013'; break;
			case 'g': *p2 = '\007'; break;
			case 'f': *p2 = '\f'; break;
			case '0': case '1': case '2': case '3':
			case '4': case '5': case '6': case '7': {
			    char tbuf[4];
			    strncpy(tbuf, p2+1, 3);
			    tbuf[3] = '\0';
			    *p2 = strtol(tbuf,(char **) NULL, 8);
			    break;
			}
			default:
			    *p2 = *(p2+1);
		    }
		else if (*p2 == '^')
		    *p2 = (*(p2+1) == '?') ? 0x7f : (*(p2+1) - '@');

		ti->c_cc[m->turnon] = *p2;
	    }
	    else
		BAD("can't parse <%s>", p);
	    /* else can't parse msg */
	}
    }
}

static char *
stripblanks(register char *s)
{
    register char *p;
    while (*s && isspace(*s)) s++;
    p = s;
    while (*p && !isspace(*p)) p++;
    *p = '\0';
    return(s);
}

#define	GETTYBUFSIZE	(10*BUFSIZ)

int
getentry(char *entry, int elen, FILE *f)
{
    char buf[BUFSIZ*2];
    register char *p;

    entry[0] = '\0';

    do {
	if (!fgets(buf, sizeof(buf), f))
	    return(0);
	lineno++;
	for (p = buf; isspace(*p); p++);
    } while (*p == '#' || *p == '\n');

    p = strchr(buf, '\n');
    if (p)
	*p = '\0';
    strcat(entry, buf);

    while (1) {
	if (!fgets(buf, sizeof(buf), f))
	    break;
	lineno++;
	p = strchr(buf, '\n');
	if (p)
	    *p = '\0';
	for (p = buf; isspace(*p); p++);
	if (!*p)
	    break;
	strcat(entry, " ");
	strcat(entry, p);
    }
    return(1);
}

/*
 * loads all of the entries from the gettydefs file
 * returns 0 if it fails.
 */
int
loadgettydefs (char *file)
{
    FILE *gd = fopen(file, "r");
    char buf[GETTYBUFSIZE];
    register char *p;
    char *tag, *prompt, *nexttag, *before, *after;

    if (!gd)
	return 0;

    while (getentry(buf, sizeof(buf), gd)) {
	
	p = buf;

	if (tag = strtok(p, "#")) {
	    tag = stripblanks(tag);
	    tag = strdup(tag);
	}
	else {
	    BAD("no tag field", "");
	    continue;
	}

	if ((before = strtok(NULL, "#")) == 0) {
	    BAD("no before field", "");
	    continue;
	}

	if ((after = strtok(NULL, "#")) == 0) {
	    BAD("no after field", "");
	    continue;
	}

	if ((prompt = strtok(NULL, "#")) == 0) {
	    BAD("no prompt field", "");
	    continue;
	}

	/* do NOT escape prompt here - it may contain \D and \T, and
	 * for that, the real time at login should be used
	 */
	prompt = strdup(prompt);

	if ((nexttag = strtok(NULL, "#")) == 0)
	    continue;

	if (p = strchr(nexttag, '\n'))
	    *p = '\0';

	nexttag = stripblanks(nexttag);
	nexttag = strdup(nexttag);

#ifdef NEVER
	printf("tag: %s\nbefore: %s\nafter: %s\nprompt: %s\nnexttag: %s\n\n",
	    tag, before, after, prompt, nexttag);
#endif

	if (cur - gdep >= cntalloc-2) {
	    GDE *sav;
	    sav = gdep;
	    if (!gdep) {
		gdep =(GDE *) malloc(sizeof(GDE) * GDCHUNK);
		cur = gdep;
	    } else {
		gdep =(GDE *) realloc(gdep, sizeof(GDE) *(GDCHUNK + cntalloc));
		cur = gdep +(cur - sav);
	    }
	    cntalloc += GDCHUNK;
	}

	memset(cur, sizeof *cur, 0);
	
	cur->tag = tag;
	cur->prompt = prompt;
	cur->nexttag = nexttag;
	parsetermio(&cur->before, before);
	parsetermio(&cur->after, after);
	cur++;
	cur->tag =(char *) NULL;
    }
    /* check and make sure that all the next tags are legitimate
     */
    if (gettycheck) {
	GDE *p;
	lineno=0;
	for (cur = gdep; cur->tag; cur++) {
	    for (p=gdep; p->tag; p++)
		if (strcmp(p->tag, cur->nexttag) == 0)
		    break;
	    if (!p->tag)
		BAD("unmatched next tag for %s", cur->tag);
	}
    }
    fclose(gd);
    return 1;
}

GDE *
getgettydef(register char *s)
{
    for (cur = gdep; cur && cur->tag; cur++)
	if (strcasecmp(cur->tag, s) == 0)
	    return(cur);
    return((GDE *) NULL);
}

void
dumpflag(char *type, struct modeword *modes, int flag)
{
    printf("%s: %08lo", type,(unsigned long) flag);
    for (; modes->name; modes++)
	if ((flag&modes->turnoff) == modes->turnon)
	    printf(" %s", modes->name);
    putchar('\n');
}

void
dump(struct termios *ti, char *s)
{
    register int i;
    register struct modeword *modes;

    printf("%s:", s);
    dumpflag("\tiflags", iflags, ti->c_iflag);
    dumpflag("\toflags", oflags, ti->c_oflag);
    dumpflag("\tcflags", cflags, ti->c_cflag);
    dumpflag("\tlflags", lflags, ti->c_lflag);
    printf("\tc_cc:\t");
    for (i = 0; i < TIONCC; i++) {
	if (i%6 == 5)
	    printf("\n\t\t");
	for (modes = ccchars; modes->name; modes++)
	    if (modes->turnon == i) {
		printf("%s(", modes->name);
		break;
	    }
	if (!modes->name)	/* skip unallocated ones */
	    continue;
	/* Yeah, I know.  But who's ever heard of getty on a EBCDIC system ;-) */
	if (ti->c_cc[i] < ' ')
	    printf("^%c", ti->c_cc[i] + '@');
	else if (ti->c_cc[i] ==(0xff & _POSIX_VDISABLE))
	    printf("disabled");
	else if (ti->c_cc[i] == 0x7f)
	    printf("^?");
	else if (ti->c_cc[i] > 0x7f)
	    printf("\\%03o", 0xff&ti->c_cc[i]);
	else
	    putchar(ti->c_cc[i]);
	printf(") ");
    }
    printf("\n\n");
}

void 
spew(GDE *gd)
{
    printf("tag: `%s'\nprompt: `%s'\nnexttag: `%s'\n",
	gd->tag, gd->prompt, gd->nexttag);
    dump(&gd->before, "before");
    dump(&gd->after, "after");
    putchar('\n');
}

void
dumpgettydefs(char *file)
{
    if ( !loadgettydefs(file) ) {
	printf("Couldn't read %s\n", file);
	exit(1);
    }
    printf("loaded entries:\n");
    for (cur = gdep; cur->tag; cur++)
	spew(cur);

}
