Ultrix-3.1/src/cmd/ted.c

Compare this file to the similar file:
Show the results in this format:


/**********************************************************************
 *   Copyright (c) Digital Equipment Corporation 1984, 1985, 1986.    *
 *   All Rights Reserved. 					      *
 *   Reference "/usr/src/COPYRIGHT" for applicable restrictions.      *
 **********************************************************************/

/*
 * ULTRIX-11 tty edit program.
 *
 * John Dustin	2/16/84
 *
 * This program allows modification of the /etc/ttys file
 * without the use of an editor.  It is recommended that 
 * this program be run single-user to avoid disabling any
 * active terminals.
 *
 */

#define JUMP	/* include setjmp/longjmp code to trap <CTRL/C> */

static char Sccsid[] =	"@(#)ted.c	3.0	4/22/86";

#include <stdio.h>
#include <ctype.h>
#include <setjmp.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>

#define LINELEN		132		/* length of an input line */
#define TRANSAC		20		/* longest transaction type */
#define ENTRYLEN	128		/* ttyfile entry length */
#define TTYXXLEN	16		/* "ttyxx" string (remember console) */
#define XXLEN		4		/* "nn" mode string */
#define BADDEV		9999		/* ?/? device number not in /dev */
#define STARTNUM    	0		/* start count for page one */		
#define SCREENFUL	12		/* lines to display per screen */

#define FAILED		-1
#define NOTFOUND	-2
#define BADCMD		-3
#define NOGO		-4

/*
 * Some of the following are defined twice
 * to allow for single characters getting
 * interpreted correctly.  Note the lack of
 * ambiguity since the command line is taken
 * in context.
 */

#define NO	0
#define	NOLOGIN	0
#define NAME	0
#define YES	1
#define	HELP	2
#define	CREATE	3
#define	REMOVE	4
#define	REMOTE	4
#define	WRITE	5
#define	PRINT	6
#define	LOCAL	7
#define	DISABLE	8
#define	DISCARD	8
#define	CTRLD	9
#define	NOREPLY	10			/* carriage return */
#define DIDPW	11			/* did print or write only */
#define HELPTED 12
#define H_CTRLC 13
#define H_CTRLD 14
#define MODIFY	15
#define MODE	15
#define SPEED	16

#define	EXIT	20

#define VALID		sizeof(valid)
#define DEVSIZ		(sizeof(dev) - 1)	/* else lose one char */

char	valid[50];				/* valid gettytab chars */
char	tmode[XXLEN];				/* "xx" mode characters */
char	newmode[XXLEN];				/* new "xx" mode characters */
char	line[LINELEN];				/* input line */
char	fline[LINELEN];				/* line read from ttyfile */
char	termname[TTYXXLEN];			/* name of tty read */
char	repterm[TTYXXLEN];			/* name of replacement tty */
char	ourtty[TTYXXLEN];			/* the user's tty */
char	progname[] =	"ted";			/* for perror */
char	dev[] =	"/dev/";			/* initial path to device */
char	testend[TTYXXLEN+DEVSIZ];		/* device pathname */
char	gettytab[] =	"/etc/gettytab";	/* getty table */
char	ttyfile[] =	"/etc/ttys";		/* the ttyfile */
char	tmp0file[] =	"/etc/tty.tmpXXXXXX";	/* temporary ttys file */
char	midfile[] =	"/etc/tty.midXXXXXX";	/* temporary link file */
char	lockfile[] =	"/tmp/TED.EDIT_LOCK";
char	added[] =	"added";
char	removed[] =	"removed";
char	changed[] =	"changed to ";
char	youarehere[] =	" [ * you are here ]";
char	dnp[] =		"device not present in /dev";
char	dot[] =		"";
char	dis[] = 	"disabled";
char	rem[] = 	"remote";
char	loc[] = 	"local";
char	locdis[] = 	"local (no logins)";
char	unk[] = 	"terminal mode unknown";
char	h_ctrlc[] =	"\nTo generate <CTRL/C>, you hold down the CTRL key and press C\n";
char	h_ctrld[] =	"\nTo generate <CTRL/D>, you hold down the CTRL key and press D\n";
char	badent[] =	"\n'%s' is not a valid command\n";
char	badtty[] =	"\n'%s' is not a valid terminal name\n";
char	notexist[] =	"\n%s does not exist.\n\nYou must enter a terminal name which currently exists in\nthe /etc/ttys file.";
char	exists[] =	"\n%s already exists.\n\nYou must enter a terminal name which is not currently in\nthe /etc/ttys file.";
char	isalready[] =	"\n%s is already -> ";
char	sayprint[] =	"\n\n[ enter 'print' to obtain a list of current terminals ]\n";
char	pretfm[] =	"\n\nPress <RETURN> for more: ";
char	h_pretfm[] =	"\nPress <RETURN> to see more terminals,\nPress <CTRL/D> to back up to the previous question.";
char	getround[] =	"\n[ To back up to the previous question press <CTRL/D> ]\n[ To return to the main menu press <CTRL/C> ]\n";
char	filenotfnd[] =	"\nFile must exist in /dev before you may access it.\n";
char	heading[] =	"\nMajor\tMinor\tName\t  Type\n---------------------------------------";
char	tty1help[] =	"\nTerminal names consist of the letters 'tty' followed by 2 unique\ncharacters.";
char	tty2help[] =	"  Valid terminal names are of the form: 'tty07', 'ttyd2',\n'ttypa'.  Enter only one terminal name at a time.";
int	length;			/* real length of the input line */
int	keepupper;		/* don't convert to lower case */
int	modify;			/* modifying a tty entry */
int	create;			/* create a new entry */
int	disable;		/* disabling a tty */
int	removing;		/* removing a tty entry from the ttyfile */
int	changing;		/* changing the name or speed of tty */
int	without;		/* enabling a local (no logins) */
int	quiting;		/* quiting the program */
int	sendhup;		/* remind about sending 'kill -1 1' at end */
int	nowriteallowed=0;	/* disallow multiple writes to pile up */
FILE	*tmpstrm;		/* for tmp0file */
FILE	*ttystrm;		/* for ttyfile */
FILE	*gtabstrm;		/* for gettytab file */
FILE	*lockstrm;		/* for lockfile */

/* 
 * These are kept alphabetical so that we don't get
 * two with the same significance.  They are taken
 * in context, hence the lack of ambiguity.
 */
struct	cmdtyp {
	char	*cmd_name;	/* name of command */
	int	cmd_id;		/* command ID */
} cmdtyp[] = {
	"?",		HELP,
	"bye",		EXIT,
	"create",	CREATE,
	"disabled",	DISABLE,
	"discard",	DISCARD,
	"exit",		EXIT,
	"help",		HELP,
	"help ted",	HELPTED,
	"local",	LOCAL,
	"mode",		MODE,
	"modify",	MODIFY,
	"name",		NAME,
	"no",		NO,
	"nologins",	NOLOGIN,
	"print",	PRINT,
	"quit",		EXIT,
	"remove",	REMOVE,
	"remote",	REMOTE,
	"speed",	SPEED,
	"write",	WRITE,
	"yes",		YES,
	"0",		DISABLE,
	"1",		REMOTE,
	"2",		LOCAL,
	"3",		NOLOGIN,
	"ctrl/c",	H_CTRLC,
	"^c",		H_CTRLC,
	"ctrl/d",	H_CTRLD,
	"^d",		H_CTRLD,
	0,		BADCMD,
};

struct llist {			/* linked list of tty entrys */
	char tmode[XXLEN];	/* tty mode */
	char tname[TTYXXLEN];	/* tty name */
	char ttype[TRANSAC];	/* transaction type */
	int  maj;		/* major device num */
	int  min;		/* minor device num */
	char *comment;		/* comment, if any (gets malloc'd) */
	struct llist *next;	/* next entry in list */
};

struct llist *head;		/* head of ttys list */
struct llist *deadhead;		/* head of new changes list */

struct stat statbuf;

#ifdef JUMP
struct jmp_buf *jmpbuf;
struct jmp_buf *savbuf;
#endif

jmp_buf	printbuf;
jmp_buf	mainbuf;

int	(*savsig)();

#ifdef JUMP
interr()
{
	signal(SIGINT, interr);
	printf("\n");
	longjmp(jmpbuf, 1);
}
#endif

/*
 * various help messages 
 */

char	*h_general[] =
{
	"Ted allows you to modify the /etc/ttys file without the use",
	"of an editor.",
	"",
	"To print the contents of the /etc/ttys file, enter 'print'.",
	"To enable or disable existing terminals, enter 'modify'.",
	"To create new terminal entries in the /etc/ttys file, enter",
	"'create'.",
	"",
	"The < command list > prompt means ted is asking you to select",
	"one of the commands from the command list.  You can enter just",
	"the first letter of the command name to select the command.",
	"",
	"Ted keeps track of the changes that you have made to the",
	"/etc/ttys file.  Your new changes are not permanently saved",
	"in the /etc/ttys file until you use the 'write' command.  Ted",
	"will ask if you try to quit without saving the latest changes.",
	"",
	"Enter 'help' or '?' at any time for more help with the current",
	"command.",
	"",
	0
};
char	*h_opts1[] =
{
	"Summary of TED commands:",
	"",
	"(c) create - allows you to create a new terminal entry in the",
	"	     /etc/ttys file.",
	"(m) modify - allows you to modify the name, speed, or mode",
	"	     of any existing terminal entry.  You use the",
	"	     'modify' command to enable and disable terminals.",
	"(r) remove - removes an entry from the /etc/ttys file.",
	"(p) print  - prints the mode of all the terminals currently",
	"	     in the ttys file.  The major and minor device",
	"	     numbers are listed along with the name and mode",
	"	     of the terminal.  The 'print' command shows the",
	"	     current status of the /etc/ttys file.",
	"(w) write  - writes out your latest changes to the /etc/ttys",
	"	     file.  The changes made since the last 'write'",
	"	     are included in the /etc/ttys file permanently.",
	"(e) exit   - exits the program.  'quit' and 'bye' also exit.",
	"",
	"The 'print' or 'write' options may be entered at any time.",
	"",
	0
};
char	*h_opts2[] =
{
	"The first character of the /etc/ttys entry reflects the",
	"mode of the terminal.  For example, the first character",
	"is '2' for a local terminal line.",
	"",
	"Choose from the following terminal modes:\n",
	"(d) disabled  - '0', disables a terminal.  The terminal is",
	"                     ignored by the system.",
	"(r) remote    - '1', enables a remote (dial-up) terminal.",
	"(l) local     - '2', enables a local terminal.",
	"(n) nologins  - '3', no logins allowed on the line.",
	"                     Useful for outgoing TIP/UUCP lines.",
	"",
	"Hard wired terminals are usually 'local'.  Terminals",
	"which are not being used, or are not connected, are usually",
	"'disabled'.",
	"",
	0
};
char	*h_modify[] =
{
	"For any given entry, there are three characteristics that",
	"may be modified.  These are:",
	"",
	"(m) mode -  this selects the mode of the terminal.  The",
	"	    possible modes are disabled, remote, local, and",
	"	    nologins.  The first character of the /etc/ttys",
	"	    entry selects the terminal mode.",
	"(s) speed - this selects the sequence of line speeds which",
	"	    getty cycles through when first logging in.  The",
	"	    second character of the /etc/ttys entry selects",
	"	    the line speed.",
	"(n) name  - this is the actual name of the terminal.  The",
	"	    name can be changed to anything you wish, but",
	"	    the new name must already exist in the /dev",
	"	    directory.",
	"",
	0
};

char	*h_getch[] =
{
	"The second character of the /etc/ttys entry is used to index",
	"the /etc/gettytab table. The referenced gettytab entry",
	"determines what sequence of line speeds getty cycles through",
	"when logging in.  Each time the <BREAK> key is depressed,",
	"getty changes the line speed to the next value specified in",
	"the table.  Sometimes speed recognition is unnecessary, in",
	"which case the 'login: ' message is just reprinted.",
	"",
	"Refer to Getty(8) in the ULTRIX-11 Programmers Manual, Vol. 1",
	"for a complete description of this procedure.",
	"",
	"	 0   - Cycles through 300-1200-150-110 baud.",
	"	       Useful for default dialup lines.",
	"	 1   - Optimized for a 150-baud teletype model 37.",
	"	 2   - Intended for an on-line 9600-baud terminal.",
	"	 3   - Starts at 1200-baud, cycles to 300 and back.",
	"	 4   - Useful for on-line console DECwriter (LA36).",
	"    others   - See Getty(8) in the ULTRIX-11 Programmers Manual.",
	"",
	0
};

char	*h_decide[] =
{
	"Choose from one of the following:",
	"",
	"(y) yes     - include these changes in the /etc/ttys file.",
	"	      Newly enabled or disabled terminals are written",
	"	      to the /etc/ttys file permanently.",
	"(n) no      - do not update the /etc/ttys file yet, but",
	"	      reserve these changes internally for later.",
	"(d) discard - discard all of these changes and start over.",
	"",
	0
};
char	*h_sendhup[] =
{
	"After modifying the /etc/ttys file, it is necessary to notify",
	"init of the changes that have been made.  To do this, the",
	"hangup signal (-1) is sent to init.  The command 'kill -1 1'",
	"causes init to reread the /etc/ttys file, activating any",
	"terminals which have been altered since the last reboot.",
	"",
	"If this signal is not sent, the altered terminals will not be",
	"recognized until the next reboot, or until the next time init",
	"receives a hangup signal.",
	"",
	0
};

main()
{
	int	i;

	signal(SIGTERM, SIG_IGN); 	/* Ignore signal 15 so we don't get */
					/* killed by someone else trying to */
					/* run ted. Ignore ^C's at start */
	signal(SIGINT, SIG_IGN);

	printf("\nULTRIX-11 System tty edit program\n");
	if (getuid() != 0) {
		fprintf(stderr, "\nted: must be super-user!\n\n");
		exit(1);
	}
	if (lockup() == FAILED) {			/* secure channels */
		fprintf(stderr, "Could not lock %s file.\n",ttyfile);
		exit(1);
	}
	if (whome() == FAILED) {		/* find user's terminal */
		fprintf(stderr, "Could not find user's terminal!\n");
		exit(1);
	}
	if (readttys() == FAILED) {		/* get ttys into list */
		fprintf(stderr, "Could not get information from %s file!\n", ttyfile);
		exit(1);
	}

	printf("\nFor instructions type 'help ted', then press <RETURN>\n");
#ifdef JUMP
	setjmp(mainbuf);
	jmpbuf = &mainbuf;
	signal(SIGINT, interr);
#endif

	newmode[2]=NULL;
prompt:
	clrregs();
	printf("\nCommand < help create modify remove print write exit >: ");

	switch (getcmd()) {
	case HELPTED:
		for(i=0;h_general[i];i++)
			printf("\n%s",h_general[i]);
		break;
	case HELP:
		for(i=0;h_opts1[i];i++)
			printf("\n%s",h_opts1[i]);
		break;
	case CREATE:
		crmenu();
		break;
	case MODIFY:
		modify++;
		do {
			printf("\nEnter the name of a terminal you wish to modify:");
			printf("\n\nModify < tty## >: ");
		} while (doable() >= 0);
		break;
	case REMOVE:
		removing++;
		do {
			printf("\nEnter the name of a terminal you wish to remove:");
			printf("\n\nRemove < tty## >: ");
		} while (doable() >= 0);
		break;
	case CTRLD:
	case EXIT:
		reallyquit();			/* ask if sure or not */
		break;
	case NOREPLY:
	case DIDPW:
		break;
	case BADCMD:
	default:
		printf(badent,line);
		break;
	}
		goto prompt;
}

/*
 * This creates new entrys.
 */
crmenu()
{
	int	i;

	for (;;) {
		clrregs();
		printf("\nCreate which type of terminal?\n");
		printf("\nCreate < disabled remote local nologins help >: ");

		switch(getcmd()) {
		case CTRLD:
			return;
		case DISABLE:
			do {
				strcpy(newmode,"00");
				clrregs();
				create++;
				disable++;
				printf("\nEnter the name of a disabled terminal you wish to create:");
				printf("\n\nCreate disabled terminal < tty## >: ");
			} while (doable() >= 0);
			break;
		case REMOTE:
			do {
				newmode[0]='1';
				clrregs();
				create++;
				printf("\nEnter the name of a remote terminal to create:");
				printf("\n\nCreate remote terminal < tty## > ");
			} while (doable() >= 0);
			break;
		case LOCAL:
			do {
				newmode[0]='2';
				clrregs();
				create++;
				printf("\nEnter the name of a local terminal to create:");
				printf("\n\nCreate local terminal < tty## > ");
			} while (doable() >= 0);
			break;
		case NOLOGIN:
			do {
				strcpy(newmode,"30");
				clrregs();
				create++;
				without++;
				printf("\nEnter the name of a local (no logins) terminal to create:");
				printf("\n\nCreate local (no logins) terminal  < tty## >: ");
			} while (doable() >= 0);
			break;
		case HELP:
			for(i=0;h_opts2[i];i++)
				printf("\n%s",h_opts2[i]);
			break;
		case NOREPLY:
			printf(getround);
		case DIDPW:
			break;
		case BADCMD:
		default:
			printf(badent,line);
			break;
		}
	}
}

doable()
{
	int	cmd;

	cmd = getcmd();
	if (cmd == CTRLD)
		return(-1);			/* quit the doable loop */
	else if (cmd == NOREPLY) {
		printf(getround);
		return(0);
	}
	else if (cmd == DIDPW)
		return(0);
	else if (checkok(line) == BADCMD)		/* error or something */
		return(0);
	strcpy(termname,line);
	if (findtty(termname) == NOTFOUND) {
		if (create) {
			if (disable || without)
				cent();
			else {
				do {
					printf("\nCreating entry: %s\n", termname);
					printf("\n\tEnter a single gettytab character:\n");
				} while(cent() == DIDPW);
			}
		}
		else {
			printf(notexist,termname);
			printf(sayprint);
		}
	}
	else {
		if (create) {
			printf("\nError:  %s%s already exists.",tmode,termname);
			printf(sayprint);
		}
		else if (removing) {
			if (deadlog(tmode,termname,removed) == FAILED)
				return(0);
			if (rement(termname) == YES) {
				printf("\n*** removed: ");
				pline(tmode,termname);
				printf("\n");
			}
		}
		else if (modify) {
			modifyit(termname);
		}
		else
			printf("\nImpossible flag set!\n");
	}
	return(0);
}

/*
 * Modify an entry - termname (tt)
 */
modifyit(tt)
char	*tt;
{
	int	i;

	for (;;) {
		printf("\nWhat would you like to modify about %s?",tt);
		printf("\n\nModify %s < mode speed name help >: ",tt);

		switch(getcmd()) {
		case CTRLD:
			return;
		case NAME:
			newname();
			break;
		case SPEED:
			newspeed();
			break;
		case MODE:
			newmod();
			break;
		case HELP:
			for(i=0;h_modify[i];i++)
				printf("\n%s",h_modify[i]);
			break;
		case NOREPLY:
			printf(getround);
		case DIDPW:
			break;
		case BADCMD:
		default:
			printf(badent,line);
			break;
		}
	}
}

/*
 * find a tty entry if it exists
 */
findtty(lf)
char	*lf;
{
	register struct llist *ptr;

	for(ptr = head; ptr != NULL; ptr = ptr->next) {
		if (strcmp(lf, ptr->tname) == 0) {		/* match! */
			strcpy(tmode,ptr->tmode);		/* save mode */
			return(1);
		}
	}
	if (ptr == NULL)
		return(NOTFOUND);	
}

/*
 * Read a line, decode the command, return command id
 */
getcmd()
{
	int	cc, cmd;
	register char *q,*s;

	if (fgets(line, LINELEN, stdin) == NULL) {
		printf("\n");
		return(CTRLD);
	}
	if (line[0] == '\n')
		return(NOREPLY);

	if (line[strlen(line) - 1] == '\n')
		line[strlen(line) - 1] = NULL;

	for(q=line; isspace(*q); q++)		/* strip leading blanks */
		;
	if (keepupper) {			/* preserving uppercase */
		length = strlen(q);
		strcpy(line, q);
		keepupper = 0;
	}
	else {
		for(s=line; *s = *q; q++) {	/* put to lowercase */
			if (isascii(*s) && isupper(*s))
				*s = tolower(*s);
			s++;
		}
		length = strlen(line);
	}
	cmd = parcmd();		/* return cmd_id */
	switch(cmd) {
		case H_CTRLC:
			printf(h_ctrlc);
			cmd = DIDPW;
			break;
		case H_CTRLD:
			printf(h_ctrld);
			cmd = DIDPW;
			break;
		default:
			break;
	}
	return(cmd);
}

/*
 * parse commands, returning cmd_id.
 */
parcmd()
{
	int	i;

	for (i=0; cmdtyp[i].cmd_name; i++) {
		if (strncmp(cmdtyp[i].cmd_name, line, length) == 0) {
			switch (cmdtyp[i].cmd_id) {
			case PRINT:
				printall();			/* print ttys */
				return(DIDPW);
				break;
			case WRITE:				/* do write */
				if (nowriteallowed) /* avoid multiple writes */
					return(DIDPW);
				if (deadhead == NULL)
					printf("\nNo new changes have been entered.\n");
				else {
					printf("\nThe following changes have been entered:\n");
					report();
				}
				return(DIDPW);			/* both cases */
			default:
				return(cmdtyp[i].cmd_id);	/* command id */
			}
		}
	}

	return(BADCMD);				/* reached end - no match */
}

/*
 * change the mode of a terminal.
 */
chmode(tt)
char	*tt;
{
	if (newmode[0] == tmode[0]) {
		printf(isalready,tt);
		pline(tmode,tt);
		printf("\n");
	}
	else {
		savsig = signal(SIGINT,SIG_IGN);
		strcpy(repterm,tt);
		if (repterm[0] != NULL)
			rep();
		else
			cre();
		signal(SIGINT,savsig);
		strcpy(tmode,newmode);
	}
}

/*
 * create an entry
 */
cent()
{
	int	cmd, retcode, i;

	/*
	 * Don't ask for gettytab char if withoutflag (No logins) (30)
	 * or disabledflag (00).
	 */

	if (without || disable) {
		savsig = signal(SIGINT,SIG_IGN);
		if (repterm[0] != NULL)
			rep();
		else
			cre();
		signal(SIGINT,savsig);
		return;
	}

	/*
	 * create entry with appropriate character
	 */
	else {
		for (;;) {	/* while NOREPLY, do 'till the cows come home */
			printf("\n\t< ? >: ");
			keepupper++;
			cmd = getcmd();
			keepupper=0;
			if (cmd == CTRLD) {
				printf("\nRequest Canceled.\n");
				return;
			} else if (cmd == DIDPW) {
				return(DIDPW);
			} else if (cmd == NOREPLY) {
				printf(getround);
				printf("\n\tEnter a single ");
				printf("gettytab character:\n");
			} else
				break;
		}

		/*
		 * Decode possible valid commands first 
		 */

		if ((retcode = whichchar(line)) == YES) {/* it is a valid char*/
			savsig = signal(SIGINT,SIG_IGN);
			if (repterm[0] != NULL) {
				if (newmode[1] == tmode[1]) {
					printf(isalready, termname);
					pline(tmode, termname);
					printf("\n");
				} else
					rep();
			} else
				cre();
			/* restore signal here before returning */
			signal(SIGINT,savsig);
			return;
		}

		/*
		 * decode cmd after checking gettytab character
		 * since single letters like 'n' or 'h' could be 
		 * valid gettytab entries.
		 */

		else if (cmd == HELP) {
			for(i=0;h_getch[i];i++)
				printf("\n%s",h_getch[i]);
			return(DIDPW);
		}
		else if (retcode == BADCMD) {
			printf("\nEntry must be a single character.\n",line);
			return(DIDPW);
		}
		else if (retcode == NOTFOUND) {
			printf("\n'%s' is not a valid gettytab character.\n",line);
			return(DIDPW);
		}
		else	/* (FAILED) - no /etc/gettytab or cannot open */
			return(FAILED);
	}
}

/*
 * replace a terminal, actually REPLACE entry, don't just remove
 * the old one, and create a new one like before. (else the order changes)
 */
rep()
{
	register struct llist *ptr, *prev;

	ptr = head;
	for (;;) {
		/* look for the old entry */
		if ((strcmp(ptr->tname, repterm)) == 0) {
			break;	
		}
		/* didn't find it yet, get the next one */
		prev = ptr;
		ptr = ptr->next;
		if (ptr == NULL) {
			printf("\nFatal: %s not found!\n",repterm);
			printf("Attempt to replace %s failed.\n",repterm);
			return(FAILED);
		}
	}
/*** The variables:
	old term: (tmode, repterm)
	new term: (newmode, termname)
***/
	/* replace the changed field(s), leaving the comment as is */

	/* if current mode in entry structure is same as 'old' mode,
	   AND if the new mode is different than existing one... */
	if ((strcmp(ptr->tmode, tmode) == 0)
	 && (strcmp(ptr->tmode, newmode) != 0)) {
		strcpy(ptr->tmode, newmode);
	}

	/* check same thing for name */
	if ((strcmp(ptr->tname, repterm) == 0)
		&& (strcmp(ptr->tname, termname) != 0)) {
		strcpy(ptr->tname, termname);
	}

	/* record entry in logfile */
	if (deadlog(tmode,repterm,changed) == FAILED)
		return;
	if (deadlog(newmode,termname,dot) == FAILED)
		return;

	printf("\n*** changed entry to: ");
	pline(newmode,termname);
	printf("\n");

/******************* The OLD way: (remove it and create a new one, since they used to be sorted in major/minor device order...)

	if (rement(repterm) == YES) {
		if (cement(newmode,termname, (char *) NULL) < 0)
			return;
		if (deadlog(tmode,repterm,changed) == FAILED)
			return;
		if (deadlog(newmode,termname,dot) == FAILED)
			return;
		printf("\n*** changed entry to: ");
		pline(newmode,termname);
		printf("\n");
	}
 *******************/
}

/*
 * create terminal
 */
cre()
{
	if (cement(newmode,termname, (char *) NULL) == FAILED)
		return;
	if (deadlog(newmode,termname,added) == FAILED)
		return;
	printf("\n*** new entry created: ");
	pline(newmode,termname);
	printf("\n");
}

/*
 * change existing tty name
 */
newname()
{
	int	cmd;

	for(;;) {
		printf("\nChanging the name of %s:",termname);
		printf("\n\nNew name < tty## >: ");

		switch(getcmd()) {
		case CTRLD:
			return;
		case NOREPLY:
			printf(getround);
			continue;
		case DIDPW:
			continue;
		default:
			if (checkok(line) == BADCMD)	/* error or 'help' */
				continue;
			else {
				if (findtty(line) == NOTFOUND) {
					if (t_stat(line) == NOTFOUND) {
						continue;
					}
					else
						break;
				}
				else {		/* can't use existing name */
					printf(exists, line);
					printf(sayprint);
					continue;
				}
			}
		}				/* end switch */
	break;
	}
	/* get here if valid name was entered */

	strcpy(repterm,termname);	/* termname is the present one */
	strcpy(termname,line);
	strcpy(newmode,tmode);
	savsig = signal(SIGINT,SIG_IGN);
	if (repterm[0] != NULL)
		rep();
	else
		cre();
	strcpy(tmode,newmode);
	signal(SIGINT,savsig);
	return;
}

/*
 * change existing tty speed
 */
newspeed()
{
	if ((tmode[0] == '0') || (tmode[0] == '3')) {
		printf("\nThe terminal is disabled.  You may change the speed");
		printf("\nof the terminal only after changing the mode to");
		printf("\nsomething other than 'disabled'.  Local (no logins)");
		printf("\nmeans the same as 'disabled' in this case.\n");
	}
	else {
		strcpy(newmode,tmode);
		strcpy(repterm,termname);
		do {
			printf("\nChanging entry: %s%s", tmode,termname);
			printf("\n\n\tEnter new gettytab character?\n");
		} while (cent() == DIDPW);
		strcpy(tmode,newmode);		/* copy new mode back to tmode*/
	}
}

/*
 * change existing tty mode
 */
newmod()
{
	int	i;

	for(;;) {
		strcpy(newmode,tmode);
		printf("\nEnter new terminal mode for %s?",termname);
		printf("\n\nNew mode < disabled remote local nologins help >: ");
		switch(getcmd()) {
		case CTRLD:
			return;
		case LOCAL:
			newmode[0]='2';
			chmode(termname);
			return;
		case DISABLE:
			newmode[0]='0';
			newmode[1]='0';
			chmode(termname);
			return;
		case REMOTE:
			newmode[0]='1';
			chmode(termname);
			return;
		case NOLOGIN:
			newmode[0]='3';
			newmode[1]='0';
			chmode(termname);
			return;
		case HELP:
			for(i=0;h_opts2[i];i++)
				printf("\n%s",h_opts2[i]);
			continue;
		case NOREPLY:
			printf(getround);
		case DIDPW:
			continue;
		case BADCMD:
		default:
			printf(badent,line);
			continue;
		}

	}
}

/*
 * read ttyfile into linked list.
 */
readttys()
{
	int	savflg, entlen;
	char tm1[XXLEN];	/* mode */
	char tm2[TTYXXLEN];	/* ttyxx part */
	char localbuf[LINELEN];	/* local buffer for comment, if any */
	char dummy[32];		/* dummy buffer, plenty big enough */
	char *p;		/* pointer */

	savflg = create;
	create = 0;
	if ((ttystrm = fopen(ttyfile, "r")) == NULL) {
		printf("\n");
		perror(ttyfile);
		return(FAILED);	
	}
	if (fgets(fline, LINELEN, ttystrm) == NULL) {
		fprintf(stderr, "\n%s file is null!\n", ttyfile);
		return(FAILED);		  /* should at least find console! */
	}
	do {
	/* sscanf stops on white-space, thus tm2 will not contain a comment. */
		sscanf(fline,"%2s%s",tm1,tm2);
		if (tm1[0] == '#') {	  /* entire line is a comment */
			tm1[0] = tm2[0] = '\0'; /* null the entry pointers */
			fline[strlen(fline)-1] = '\0'; /* get rid of '\n' */
			strcpy(localbuf, fline);
		} else {
		/*
		 * Check if something like space or tab follows the entry,
		 * in which case it is a comment.
		 */
		if (strncmp(tm2, "console", 7) == 0)
		    entlen=7;	/* length of "console" */
		else
		    entlen=5;	/* length of "ttyxx" */

		if (strlen(fline) > entlen+3) {
	/* next won't work if there is white space comments */
	/*	    sscanf(fline, "%*s", entlen+2, dummy);	 */
		    p = fline;
		    p = p + entlen + 2;	/* add 2 for leading 00 */
		    if (strlen(p) < 1)
			localbuf[0] = '\0';
		    else {
			strcpy(localbuf, p);
			localbuf[strlen(localbuf)-1] = '\0'; /* no <CR> */
		    }
		} else
		    localbuf[0] = '\0';

		}  /* end the else #comment */

		if (cement(tm1,tm2,localbuf) == FAILED)
			return(FAILED);		/* malloc error */

	} while (fgets(fline, LINELEN, ttystrm) != NULL);

	if (fclose(ttystrm) == EOF)
		printf("\nwarning: could not close %s.\n",ttyfile);
	create = savflg;
	return(0);
}

/*
 * append transaction to deadlist. 
 * return YES normal, return FAILED if malloc error.
 */
deadlog(tm,tn,ttype)
char *tm,*tn,*ttype;
{	
	register struct llist *ptr, *prev;

	if (deadhead != NULL)
	for (prev=deadhead;prev->next!=NULL;prev=prev->next);
	if ((ptr = malloc(sizeof(struct llist))) == NULL) {
		printf("\n\7Memory allocation error (1027: struct llist).\nCannot create another entry.\n");
		return(FAILED);
	}
	strcpy(ptr->tmode, tm);
	strcpy(ptr->tname, tn);
	strcpy(ptr->ttype, ttype);
	if (deadhead == NULL) {
		deadhead = ptr;
		deadhead->next = NULL;
	}
	else {
		ptr->next = prev->next;
		prev->next = ptr;
	}
	return(YES);
}

/*
 * clear dead list
 * if n=0, just clear the list;
 * if n=1, restore ttyslist by re-reading /etc/ttys;
 * also want to re-get the tmode for the current terminal.
 */
deadclr(n)
int n;
{
	register struct llist *ptr, *prev;

	if (deadhead == NULL)
		return;
	ptr = prev = deadhead;
	deadhead = NULL;
	do {
		prev = ptr;
		ptr = ptr->next;
		free(prev);
	} while (ptr != NULL);
	if (n == 1) {			   		/* put things back! */
		if (head == NULL)
			printf("\ninternal warning: head is null!\n");
		ptr = prev = head;
		head = NULL;
		do {					/* free the ttys list */
			prev = ptr;
			ptr = ptr->next;
			free(prev);
		} while (ptr != NULL);
		if (readttys() < 0) {			/* reread ttyfile */
			printf("Fatal: couldn't read %s file!\n",ttyfile);
		}
		/* restore tmode in case we were in the middle of something. */
		findtty(termname);	/* it has to be there! */
					/* tmode is filled in globally */
	}
}

/*
 * say which changes have been made so far.
 */
saywhich()
{
	register struct llist *ptr;

	printf("\n\t");
	for (ptr = deadhead; ptr != NULL; ptr = ptr->next) {
		printf("%s%s %s",ptr->tmode, ptr->tname, ptr->ttype);
		if ((strcmp(ptr->ttype, added) == 0) ||
		    (strcmp(ptr->ttype, removed) == 0) ||
		    (strcmp(ptr->ttype, dot) == 0))
			printf("\n\t");
	}
}

/*
 * Tell about existing terminals becoming disabled, or names changed...
 */
fersure()
{
	register struct llist *ptr;

	printf("\n\t");
	for (ptr = deadhead; ptr != NULL; ptr = ptr->next) {
		if (strcmp(ptr->ttype, added) == 0)
			printf("added:   ");
	 	else if (strcmp(ptr->ttype, removed) == 0)
			printf("removed: ");
		else if (strcmp(ptr->ttype, changed) == 0)
			printf("changed: ");
		else if (strcmp(ptr->ttype, dot) == 0)
			printf("     to: ");
		else
			printf("	");	/* should never happen */

		pline(ptr->tmode, ptr->tname);
		printf("\n\t");
	}
	deadclr(0);				/* clear deadhead list */
}

/*
 * write list of ttys to temp file and update the ttyfile
 * return 1 successful
 */
update()
{
	register struct llist *ptr;

	if ((tmpstrm = fopen(tmp0file, "w")) == NULL) {
		printf("\nError: cannot open %s\n", tmp0file);
		printf("\nChanges not saved!!!\n\n");
		return;	
	}
	printf("\nUpdating...\n");
	for (ptr = head; ptr != NULL; ptr = ptr->next) {
		if ((ptr->tmode != NULL) && (ptr->tname != NULL)) {
			fputs(ptr->tmode, tmpstrm);
			fputs(ptr->tname, tmpstrm);
		}
		if (ptr->comment != NULL)
			fputs(ptr->comment, tmpstrm);
		fputs("\n", tmpstrm);
	}
	if (fclose(tmpstrm) == EOF)
		printf("\nwarning: couldn't close %s\n",tmp0file);
	/*
	 * If a link/unlink fails, leave ttyfile as is,
	 * and announce a new file: /etc/tty.newXXXXXX
	 */
	if (link(ttyfile,midfile) < 0) {
		printf("\n");
		perror(ttyfile);
		cleanup(1);
		unlink(midfile);
		exit(0);
	}
	if (unlink(ttyfile) < 0) {
		printf("\n");
		perror(ttyfile);
		cleanup(1);
		unlink(midfile);
		exit(0);
	}
	if (link(tmp0file, ttyfile) < 0) {
		printf("\n");
		perror(tmp0file);
		cleanup(1);
		link(midfile,ttyfile);		/* restore ttys file */
		unlink(midfile);
		exit(0);
	}
	if (unlink(tmp0file) < 0) {
		printf("\n");
		perror(tmp0file);
		printf("\n\7warning: couldn't unlink %s\n",tmp0file);	
		return(1);
	}
	if (unlink(midfile) < 0) {
		printf("\n");
		perror(midfile);
		printf("\n\7warning: couldn't unlink %s\n",midfile);	
		return(1);
	}
	printf("\nNew changes saved in %s\n",ttyfile);
	return(1);
}

/*
 * print tty and mode
 */
pline(tmod,ttynum)
char *tmod, *ttynum;
{
	int	i;

	    printf("%s%s - ",tmod,ttynum);
	    sscanf(tmod,"%1d",&i);
	    switch(i) {
	    case 0:
		printf(dis);
		break;
	    case 1:
		printf(rem);
		break;
	    case 2:
		printf(loc);
		break;
	    case 3:
		printf(locdis);
		break;
	    default:
		printf(unk);
		break;
	    }
}

/*
 * removes an entry from the ttys file.
 */
rement(n)
char *n;
{
	register struct llist *ptr, *prev;

	ptr = head;
	for (;;) {
		if ((strcmp(ptr->tname, n)) == 0) {
			break;	
		}
		prev = ptr;
		ptr = ptr->next;
		if (ptr == NULL) {
			printf("\nFatal: %s not found!\n",n);
			printf("Attempt to remove %s failed.\n",n);
			return(FAILED);
		}
	}
	prev->next = ptr->next;		/* skip that one */
	if (strlen(ptr->comment) > 1)
		free(ptr->comment);	/* free the comment, if any */
	free(ptr);
	return(YES);
}
/*
 * test for device present in /dev
 * return NOTFOUND if not in /dev. (p points to ttyname)
 */
t_stat(p)
char *p;
{
	strcpy(testend,dev);
	strcat(testend,p);
	if (stat(testend, &statbuf) < 0) {
		printf("\n");
		perror(testend);
		printf(filenotfnd);
		return(NOTFOUND);
	}
	else
		return;
}

/*
 * put entry into ttys list in major/minor device order.
 * 	p is first 2 chars of ttys file, *p is NULL if only a comment.
 *	q is tty name (console, or ttyxx, *q is NULL if only a comment)
 *	s is ptr to comment, and *s is NULL if no comment.
 * return FAILED on malloc error.
 * return NOTFOUND if tty not in /dev
 */
cement(p,q,s)
char *p,*q,*s;
{	
	register struct llist *ptr, *prev;
	int	maj, min;
	int	badflg;				/* /dev/tty?? not found */

	ptr = prev = head;
	badflg = 0;

	strcpy(testend,dev);			/* "/dev/" */
	strcat(testend,q);			/* add "ttyxx" */
	if (stat(testend, &statbuf) < 0) {	/* then file not in /dev ! */
		fprintf(stderr, "\nwarning: ");
		perror(testend);
		fflush(stdout);
		maj = min = BADDEV;
		badflg++;
	} else {
		maj = major(statbuf.st_rdev);
		min = minor(statbuf.st_rdev);
	}

	/* find the end of the list */
	if (head != NULL) {
		do {
			prev = ptr;
			ptr = ptr->next;
		} while (ptr != NULL); 	/* find the end of the list */
	}

	/*
	 * only malloc a structure if at least p or q exist, or
	 * a comment (*s) is present
	 */
	 /* NOTE: p and q always point somewhere, if only to a null. */
	if ((*p && *q) || (s && *s)) {
	    if ((ptr = malloc(sizeof(struct llist))) == NULL) {
		printf("\n\7Memory allocation error (1315: struct llist).\nCannot create another entry.\n");
		return(FAILED);
	    }
	}

	if (badflg) {
		maj = BADDEV;
		min = BADDEV;
	}

	if ((*p != '\0') && (*q != '\0')) {
		ptr->maj = maj;
		ptr->min = min;
	} else {
	/*
	 * Use 0 here, even though it is legal.
	 * (we never look at it since it is a comment (#))
	 */
		ptr->maj = 0;
		ptr->min = 0;
	}

	if (*p != '\0')
		strcpy(ptr->tmode, p);
	else
		ptr->tmode[0] = '\0';

	if (*q != '\0')
		strcpy(ptr->tname, q);
	else
		ptr->tname[0] = '\0';

	if (s && *s) {
		if ((ptr->comment = malloc(strlen(s)+1)) == NULL) {
			printf("\n\7Memory allocation error (1346: strlen(comment) = %d).\nCannot create another entry.\n",strlen(s));
				return(FAILED);
			}
		strcpy(ptr->comment, s);
	}
	else {
		if ((ptr->comment = malloc(sizeof(char))) == NULL) {
			printf("\n\7Memory allocation error (1355: sizeof(char) = %d).\nCannot create another entry.\n", sizeof(char));
			return(FAILED);
		}
		ptr->comment[0] = '\0';
	}

	if (head == NULL) {
		head = ptr;
		head->next = NULL;
	} else {
		ptr->next = prev->next;
		prev->next = ptr;
	}
	return(1);
}

/*
 * get user's ttyname
 */
whome()
{
	char	*ttynum;

	if ((ttynum = ttyname(0)) == NULL) {
		printf("\n");
		perror(ttynum);
		printf("ted: cannot find your tty.\n\n");
		return(FAILED);	
	}
	strcpy(ourtty,&ttynum[DEVSIZ]);		/* save 'ttyxx' part */
	/*
	printf("You are on %s.\n",ourtty);
	*/
	return(0);

}

/*
 * print maj/min, tty, mode
 */
printall()
{
	int	cc;
	char line[LINELEN];
	register struct llist *ptr;
	int	pcnt;
	int	beatit=0;

#ifdef JUMP
	if (setjmp(printbuf)) {
		jmpbuf = savbuf;
		printf("\n");
		return;
	}
	savbuf = jmpbuf;
	jmpbuf = &printbuf;
#endif JUMP

	pcnt = STARTNUM;
	printf("\n%s",heading);
	for (ptr = head; ptr != NULL; ptr = ptr->next) {
		if (pcnt < SCREENFUL) {
			putone(ptr);
			pcnt++;
		}
		else for (;;) {				/* screenful */
			printf(pretfm);
			if (fgets(line, LINELEN, stdin) == NULL) {
				beatit++;
				break;
			}
			if (line[0] == '\n') {
				printf(heading);
				putone(ptr);
				pcnt = STARTNUM;
				break;
			}
			/* be able to say 'q' for quit */
			else if ((line[0] == 'q') || (line[0] == 'Q')) {
				beatit++;
				break;
			}
			else {
				printf(h_pretfm);
				continue;
			}
		}
		if (beatit)		/* break on CTRL/D */
			break;
	}
	printf("\n");
#ifdef JUMP
	jmpbuf = savbuf;
#endif
}

/*
 * print a line of maj/min	tty	mode
 * maj/min become ?/? if device not present in /dev
 */
putone(ptr)
struct llist *ptr;
{
/*	printf("\n"); */
	if (ptr->maj == BADDEV)
		printf("\n?\t?\t%s%s - %s",ptr->tmode, ptr->tname,dnp);
	else {
		if ((ptr->tmode[0] != '\0') && (ptr->tname[0] != '\0')) {
			printf("\n%d\t%d", ptr->maj, ptr->min);
			printf("\t");
			pline(ptr->tmode,ptr->tname);
		}
	}
	if ((strcmp(ptr->tname, ourtty)) == 0)
		printf(youarehere);
}

/*
 * Lock things up
 * Return FAILED if can't do it
 */
lockup()
{
	int	pid;				/* old pid, if any */

	if (chmod(ttyfile,0400) < 0) {
		printf("\n");
		perror(ttyfile);
		return(FAILED);
	}
	if ((lockstrm = fopen(lockfile, "r")) == NULL) {
		if(takeover() == FAILED)
			return(FAILED);
	}
	else {					/* lock file already exits */
		if (fgets(fline,LINELEN,lockstrm) == NULL) {
			unlink(lockfile);
			printf("\nCouldn't lock %s file.  Please try again.\n\n", ttyfile);
			return(FAILED);
		} else {
			sscanf(fline,"%d",&pid);
			if (kill(pid, SIGTERM) == -1) {		/*alreadydead*/
				if (takeover() < 0)
					return(FAILED);
			} else {
				printf("\nted:  %s is running.\n",progname);
				printf("\nOnly one user may run the %s program at once.\n",progname);
				return(FAILED);
			}
		}
	}
	mktemp(tmp0file);			/* create temp ttys file */
	mktemp(midfile);			/* create a temp link file */
	return(0);
}

/*
 * No lockfile around or process is dead so move in.
 */
takeover()
{
	int	pid;				/* current pid */
	char	spid[8];			/* string form of pid */

	if ((lockstrm = fopen(lockfile, "w")) == NULL) {
		printf("\n");
		perror(lockfile);
		printf("\nNo write permission for %s.\n",lockfile);
		return(FAILED);
	} else {				/* insert our pid */
		pid = getpid();
		sprintf(spid,"%d",pid);
		fputs(spid, lockstrm);
		if (fclose(lockstrm) == EOF)
			printf("\nwarning: couldn't close %s.\n",lockfile);
		return(0);
	}
}

/*
 * close files, put things back and update as necessary
 * unlock lock file, chmod the ttyfile, etc.
 */
cleanup(n)
int	n;
{
	signal(SIGINT, SIG_IGN);
	if (n ==1) {				/* ungraceful exit */
		printf("\7\7\7");
		printf("\nA copy of your latest changes are in %s",tmp0file);
		printf("\n\nTo save your changes, use: \n\n\t\"cp %s %s\"",tmp0file,ttyfile);
		printf("\n\nEXIT (link error)\n");
		if (chmod(ttyfile,0664) < 0) {
			printf("\n");
			perror(ttyfile);
		}
		return;
	}
	if (deadhead != NULL) {
		printf("\n\n\7The following changes are not saved:\n");
		report();
	}
	if (chmod(ttyfile,0664) < 0) {
		printf("\n");
		perror(ttyfile);
	}
	if (unlink(lockfile) < 0) {
		printf("\n");
		perror(lockfile);
	}
	return;
}

/*
 * ask about saving new changes 
 */
report()
{
	int	i;

	saywhich();
	nowriteallowed = 1;	/* disallow writes to pile up */
	for (;;) {
		printf("\nDo you wish to save these new changes?");
		printf("\n\nSave < yes no discard help >: ");

		switch(getcmd()) {
		case YES:
			if (update()) {
				sendhup++;
				fersure();
			}
			break;
		case NO:
			if (quiting)
				printf("\nNo changes saved.\n");
			else
				printf("\nOK.\n");
			break;
		case DISCARD:
			if (!quiting) {
				printf("\nChanges discarded.\n");
				printf("\nRereading %s\n",ttyfile);
				deadclr(1);
			}
			else
				printf("\nNo changes saved.\n");
			break;
		case HELP:
			for (i=0;h_decide[i];i++)
				printf("\n%s",h_decide[i]);
			saywhich();
			continue;
		case CTRLD:
			if (quiting) {			/* exit */
				printf("\7\7\nNew changes not saved !\n");
				break;
			}
		case NOREPLY:			/* else fall through */
			if (quiting)
				printf("\7");
			printf("\nPlease answer the question.\n");
		case DIDPW:
			continue;
		case BADCMD:
		default:
			printf(badent,line);
			continue;
		}	/* end switch */
		nowriteallowed=0;	/* reset flag */
		return;

	}	/* for loop */
}

clrregs()
{
	int	i;
	disable=without=removing=modify=create=changing=keepupper=quiting=0;
	for (i=0; i < TTYXXLEN; repterm[i++]=NULL);	/* zero out repterm */
}

/*
 * Checkok checks for a reasonable tty name, ie. 'tty##'.
 * return BADCMD 	if invalid entry
 * return   1		if it looks good
 */
checkok(p)
char *p;
{
	register char *q,*s;

	for(q=p; isspace(*q); q++)	/* remove leading blanks and tabs */
		;

	for(s=p; *s = *q; q++) {		/* put to lowercase */
		if (isascii(*s) && isupper(*s))
			*s = tolower(*s);
	s++;
	}

	length = strlen(p);
	strcpy(line, p);
	switch (parcmd()) {
		case HELP:
			printf(tty1help);
			printf(tty2help);
			printf(sayprint);
			return(BADCMD);
		case NOREPLY:
			return(NOREPLY);
		case DIDPW:
			return(BADCMD);
		default:			/* other normally valid ones */
			printf(badtty,line);
			return(BADCMD);
		case BADCMD:			/* but a bad command
					   	   might be a terminal name */
	/*
	 * We allow entrys like tty00, ttyd0, ttyp0
	 * Desire last two digits unique in ttyfile
	 * Hex digits are a possibility (ttyaf)
	 */

	if (strcmp(ourtty,p) == 0) {
		printf("\nYou can't change your own terminal. (%s)\n",ourtty);
		return(BADCMD);
	}
	if (length == 5) {
	 	for (q=p; q < &p[5]; q++)		/* alphanumeric test */
			if (!isalnum(*q)) {
				printf(badtty,line);
				return(BADCMD);
			}
		if (strncmp(p,"tty",3) == 0) {
			if ((create) && (t_stat(p) == NOTFOUND))
				return(BADCMD);
			return(1);
		}
	}
	else if (length > 5) {
		if (strcmp(p, "console") == 0) {
			printf("\nSorry, the console cannot be altered.\n");	
			return(BADCMD);
		}	
	}

	printf(badtty,line);		/* otherwise, bad entry */
	return(BADCMD);

	}	/* end switch */
}

/*
 * check for valid gettytab character
 * return BADCMD if too long, NOTFOUND if invalid char
 */
whichchar(q)
char *q;
{
	int	i;
	char	wline[LINELEN];

	/*
	 * Read valid getty chars from /etc/gettytab first time only.
	 * Just look for first character on a line followed by '|' .
	 */
	if (valid[0] == NULL) {	
		if ((gtabstrm = fopen(gettytab, "r")) == NULL) {
			printf("\n");
			perror(gettytab);
			return(FAILED);	
		}
		if (fgets(wline, LINELEN, gtabstrm) == NULL)
			return(FAILED);			/* file empty */
		i = 0;
		do {
			if (isascii(wline[0]) && (wline[1] == '|')) {
				valid[i++] = wline[0];
			}
		} while (fgets(wline, LINELEN, gtabstrm) != NULL);

		if (fclose(ttystrm) == EOF)
			printf("\nwarning: could not close %s.\n",gettytab);
	}

	if (q[1] == NULL) {			/* allow only 1 char entry */
		for (i=0; i<=VALID; i++) {
			if (valid[i] == q[0]) {
				newmode[1] = q[0];
				return(YES);
			}
			else if (i >= VALID) {
				return(NOTFOUND);
			}
		}
		return(NOTFOUND);		/* insurance */
	}
	else {
		return(BADCMD);
	}
}

/*
 * ask about quitting or not
 */
reallyquit()
{
	int	i, cmd;

	for(;;) {
		printf("\nDo you really want to quit? \n\n< yes no >: ");
		cmd = getcmd();
		if (cmd == HELP) {
			printf("\nIf you wish to quit, enter 'yes'.");
			printf("  Press <RETURN> or enter 'no' to continue.\n");
			continue;
		} else if ((cmd == YES) || (cmd == CTRLD)) {
			printf("\nExit");
			quiting++;
			cleanup(0);
			if (sendhup) {
				printf("\n");
				for (;;) {
					printf("\nDo you wish to notify init of the new changes to /etc/ttys?");
					printf("\n\nNotify init < yes no help >: ");
					cmd = getcmd();
					if (cmd == HELP) {
						for (i=0;h_sendhup[i];i++)
							printf("\n%s",h_sendhup[i]);
						continue;
					}
					else if (cmd == YES) {
						printf("\nkill -1 1");	
						kill(1, SIGHUP);
						break;
					}
					else if (cmd == NOREPLY)
						continue;
					else {
						printf("\7\7\n*** No hangup signal sent to init!");
						break;	/* includes CTRL/D */
					}
				}
			}
			printf("\n\n");
			exit(0);
		}
		else			/* if not YES or CTRL/D, assume NO */
			return;
	}

}