[alt.sources] Enhanced SYSV Getty, v1.1, part 1 of 4

paul@devon.lns.pa.us (Paul Sutcliffe Jr.) (01/17/90)

This has evolved from a private project to get a SysV-like getty running
on my Tandy 6000 (Xenix System III).  I'm releasing it here in the hopes
that you alt.sources-types will find any bugs and report them to me so
they can be fixed before I submit this to comp.sources.{misc,unix}.

If you want to know more details, the README file is the first thing in
the shell archive below.

- paul

#! /bin/sh

# Make a new directory for the getty sources, cd to it, and run kits 1
# thru 4 through sh.  When all 4 kits have been run, read README.

echo "This is getty 1.1 kit 1 (of 4).  If kit 1 is complete, the line"
echo '"'"End of kit 1 (of 4)"'" will echo at the end.'
echo ""
export PATH || (echo "You didn't use sh, you clunch." ; kill $$)
mkdir man 2>/dev/null
echo Extracting README
sed >README <<'!STUFFY!FUNK!' -e 's/X//'
X
X			   Getty Kit, Version 1.1
X
X		  Copyright (c) 1989,1990, Paul Sutcliffe Jr.
X
X-------------------------------------------------------------------------------
X
X	Permission is hereby granted to copy, reproduce, redistribute,
X	or otherwise use this software as long as: there is no monetary
X	profit gained specifically from the use or reproduction or this
X	software, it is not sold, rented, traded or otherwise marketed,
X	and this copyright notice is included prominently in any copy
X	made.
X
X	The author make no claims as to the fitness or correctness of
X	this software for any use whatsoever, and it is provided as is. 
X	Any use of this software is at the user's own risk.
X
X-------------------------------------------------------------------------------
X
X
XWHY THIS GETTY:
X
XAs most people have seen, the stock getty provided on Unix/Xenix
Xsystems lacks many features that can be useful.  The getty included in
Xthis distribution adds several features that I needed on my own system,
Xplus includes several "Wouldn't it be nice if ..." features I've heard
Xmentioned around UseNet.
X
XGetty 1.1 trys to emulate a "standard" System V getty in every way it
Xcan.  For instance, it uses the SysV /etc/gettydefs file (although you
Xmay give it a different name).  It also uses an /etc/issue file, if one
Xis present.
X
XAdded features include:
X
X    +	Can be used as a normal getty, or as "uugetty" to allow
X	bi-directional usage of modem lines.
X
X    +	Reads a "defaults" file at runtime, so that a single binary
X	can be configured differently on individual lines.  This also
X	allows you to change getty's behavior without recompiling.
X
X    +	Let's you specify default erase and kill characters, instead
X	of the ancient '#' and '@' convention still used in some
X	"modern" gettys.
X
X    +	Extensive debugging (to a log file) can be enabled at compile-
X	time.  The command line argument to envoke debugging is an
X	octal number -- the bit pattern determines which aspects of
X	getty's behavior are logged.
X
X    +	Let's you specify a program other than "login" to be executed
X	after the user name is entered.
X
X    +	(and the best for last:)  The line can be "initialized"
X	before sending the login banner (/etc/issue) and prompt with
X	the use of an expect/send sequence not unlike that used by the
X	UUCP L.sys (or Systems) file.
X
X
XREQUIREMENTS:
X
XGetty 1.1 should drop right in to any AT&T (System III or V) Unix
Xor derivitive.  It has already been successfully installed on:
X
X	Tandy 6000		Tandy Xenix 3.2  (Microsoft Xenix 3.0)
X	NCR Tower 32/400	Unix SVR[23]
X	80386 clone		SCO Xenix V/386 2.2
X
X
XINSTALLATION:
X
XPlease read all the directions below before you proceed any further, and
Xthen follow them carefully.  Failure to do so may void your warranty. :-)
X
XAfter you have unpacked your kit(s), you should have all the files listed
Xin MANIFEST.
X
X1)  Run Configure.  This will figure out various things about your system.
X    Some things Configure will figure out for itself, other things it will
X    ask you about.  It will then proceed to make config.h, config.sh, and
X    Makefile.
X
X    You might possibly have to trim # comments from the front of Configure
X    if your sh doesn't handle them, but all other # comments will be taken
X    care of.
X
X2)  Glance through config.h to make sure system dependencies are correct.
X    Most of them should have been taken care of by running the Configure
X    script.
X
X    If you have any additional changes to make to the C definitions, they
X    can be done in the Makefile, or in config.h.  Bear in mind that they
X    will get undone next time you run Configure.
X
X3)  Copy the sample file tune.H to tune.h and edit tune.h to reflect the
X    special needs of your system and your desired features.  Use the
X    following as a guide:
X
X    boolean	If your compiler supports a (boolean) type, you may
X		remove this definition.
X
X    DEF_CFL	Define this to the <termio.h> parameters that will
X		identify your system's normal word length and parity.
X		Possible values are:
X
X			(CS8)			/* 8-bit, no parity */
X			(CS7|PARENB)		/* 7-bit, even parity */
X			(CS7|PARENB|PARODD)	/* 7-bit, odd parity */
X
X		Be sure to use only symbols defined in <termio.h> on
X		your system.
X
X    DEBUG	Define this if you want the runtime debugging code
X		included in the executables.  See the section on
X		DEBUGGING in this readme for instructions in usage.
X
X    LOGUTMP	Define this if your utmp file (/etc/utmp) records
X		getty processes as a LOGIN_PROCESS.  This is true
X		for all SYS V sites that I'm aware of.
X
X    MY_CANON	Define this if you want to define your own ERASE and
X		KILL characters.  You may wish to do this if you are a
X		SYS V site whose defaults are the ancient '#' and '@'
X		characters.  See MY_ERASE and MY_KILL below.
X
X    RCSID	Define this if you want RCS version strings compiled
X		into the executables.  These can later be found with
X		the what (SCCS) or ident (RCS) commands.
X
X    SETTERM	Define this if you want getty to export the TERM
X		environment variable before calling login.  This will
X		only be done if getty knows what kind of terminal is
X		attached to the line it's running on.
X
X    TRYMAIL	Define this if you want getty to send email if it has
X		and error and cannot access the CONSOLE device.
X
X    WARNCASE	Define this to allow getty to warn a user if he/she has
X		used only upper-case letters in their login id.  If the
X		user re-enters his/her id a second time in upper-case,
X		that value is accepted and process accordingly.
X
X    MY_ERASE	If you've defined MY_CANON, use these to define the values
X    MY_KILL	you want for erase and kill characters.
X
X    NOTIFY	If you've defined TRYMAIL, use this to define the account
X		to which the email will be sent.
X
X    CONSOLE	Define this to the name of the console device.
X
X    DEFAULTS	Define this to the name of the defaults file.  The %s is
X		necessary, and will be replaced (via an sprintf()) by the
X		name of the executable--either getty or uugetty.
X
X    ISSUE	Define this to the name of your issue file.  If this
X		is undefined, no issue file will be displayed during
X		getty's startup.
X
X    LOGIN	Define this to the name of the login program to be called.
X
X    TRS16	Define this only if you're compiling getty on a Tandy 6000.
X		This define handles the different command line required due
X		to the V7 based /etc/init and also the dain-bramaged
X		/etc/inittab used on Tandy Xenix-68000 3.2
X
X4)  make depend
X
X    This will look for all the includes and modify Makefile accordingly.
X    Configure will offer to do this for you.
X
X5)  make
X
X    This will attempt to make getty and uugetty in the current directory.
X    It will also go to the man sub-directory and use m4 to create
X    nroff-able man pages.  It will then run nroff on the m4 output.
X
X6)  make install
X
X    This will put getty/uugetty into a public directory (normally /etc).
X    It will also make sure the man pages have been created.  It will not
X    install them.  You may need to be root to do this.  If you are not
X    root, you must own the directories in question and you should ignore
X    any messages about chown not working.
X
X    Also, if you don't already have an /etc/gettydefs file, you'll need
X    to create one.  This goes for the /etc/default files (if you are
X    using them) and the /etc/issue file.  There are examples of these
X    in the `sample.files' file.
X
X7)  Read the manual entries before running getty/uugetty.
X
X8)  IMPORTANT!  Help save the world!  Communicate any problems and suggested
X    patches to me, paul@devon.lns.pa.us (Paul Sutcliffe Jr.), so we can
X    keep the world in sync.  If you have a problem, there's someone else
X    out there who either has had or will have the same problem.
X
X    If possible, send in patches such that the patch program will apply them.
X    Context diffs are the best, then normal diffs.  Don't send ed scripts--
X    I've probably changed my copy since the version you have.
X
X    Watch for getty patches in comp.sources.bugs.  Patches will generally be
X    in a form usable by the patch program.  If you are just now bringing up
X    getty and aren't sure how many patches there are, write to me and I'll
X    send any you don't have.  Your current patch level is shown in patchlevel.h.
X
X
XDEBUGGING:
X
XTo use debugging, you must define DEBUG (in config.h) before compiling.
X
XTo envoke debugging, use the "-D onum" command line argument.  Onum is
Xan octal number.  To turn on all levels of debugging, use "-D 0377".
XTo pick specific areas to be watched, look at the defines in the "debug
Xlevels" section of getty.h.  The value for onum will be the result of
XOR-ing the values you want.  For instance, to debug the defaults file
Xand gettytab file processing, use "-D 022".
X
X
XHave fun.
X
X- paul
X
XINTERNET:  paul@devon.LNS.PA.US         |   How many whales do you have to
XUUCP:      ...!rutgers!devon!paul       |       save to get a toaster?
!STUFFY!FUNK!
echo Extracting man/README
sed >man/README <<'!STUFFY!FUNK!' -e 's/X//'
X
X			Getty 1.1 manual page files.
X
XThis README describes the files used and created by the Makefile.
X
XIn general, a file with a `.m4' extension is nroff source intersperced
Xwith m4 macros.  These files are processed by m4 to yield nroff-able
Xfiles, and use the macros to create man pages whose content matches the
Xway you've configured your getty/uugetty executables.  Running m4 on
Xthe .m4 files produce ready-for-nroff files with either a `.1m', `.3'
Xor `.4' extension, depending upon the chapter to which the man page
Xbelongs.  The makefile will also run nroff on these files, producing
Xfiles with a `.man' extension.
X
XAfter running make in this directory, you will have the following files:
X
X	getty.1m	nroff-ready man page for getty
X	getty.man	nroff'd getty man page
X	gettytab.4 *	nroff-ready man page for the gettytab file
X	gettytab.man *	nroff'd gettytab man page
X	issue.4		nroff-ready man page for the issue file
X	issue.man	nroff'd issue man page
X
X* Note: the name `gettytab' will be replaced by the actual name you
X  assigned for that file -- usually `gettydefs'.
X
XAlso, you may have some (or all) of the following files, depending upon
Xthe configuration of your Unix/Xenix.  These files describe library
Xroutines used by getty that were not found in your libc.a (and thus
Xhomegrown versions were used):
X
X	getutent.3	nroff-ready man page for getutent(3)
X	getutent.man	nroff'd getutent man page
X	strdup.3	nroff-ready man page for strdup(3)
X	strdup.man	nroff'd strdup man page
X	putenv.3	nroff-ready man page for putenv(3)
X	putenv.man	nroff'd putenv man page
X
XYou will need to manually install the man pages.  Use either the .{1m,3,4}
X(pre-nroff) or .man (post-nroff) files, according to the needs of your
Xsystem.
X
X- paul
X
XINTERNET:  paul@devon.LNS.PA.US         |   How many whales do you have to
XUUCP:      ...!rutgers!devon!paul       |       save to get a toaster?
!STUFFY!FUNK!
echo Extracting main.c
sed >main.c <<'!STUFFY!FUNK!' -e 's/X//'
X/*
X**	$Id: main.c,v 1.1 90/01/16 16:16:10 paul Exp Locker: paul $
X**
X**	Main body of program.
X*/
X
X/*
X**	Copyright 1989,1990 by Paul Sutcliffe Jr.
X**
X**	Permission is hereby granted to copy, reproduce, redistribute,
X**	or otherwise use this software as long as: there is no monetary
X**	profit gained specifically from the use or reproduction or this
X**	software, it is not sold, rented, traded or otherwise marketed,
X**	and this copyright notice is included prominently in any copy
X**	made.
X**
X**	The author make no claims as to the fitness or correctness of
X**	this software for any use whatsoever, and it is provided as is. 
X**	Any use of this software is at the user's own risk.
X*/
X
X/*
X**	$Log:	main.c,v $
X**	Revision 1.1  90/01/16  16:16:10  paul
X**	Initial revision
X**	
X*/
X
X
X#define	MAIN
X
X#include "getty.h"
X#include "defaults.h"
X#include "table.h"
X#include <signal.h>
X#include <fcntl.h>
X#include <sys/stat.h>
X#include <pwd.h>
X
X#if defined(RCSID) && !defined(lint)
Xstatic char *RcsId =
X"@(#)$Id: main.c,v 1.1 90/01/16 16:16:10 paul Exp Locker: paul $";
X#endif
X
X#if !defined(lint)
X#include "patchlevel.h"
Xstatic char *Release = RELEASE;
Xstatic char *RelDate = DATE;
X#endif
X
X/* how does this thing work
X */
X#define	USAGE	"Usage:\t%s %s%s\n\t%s -c checkfile\n"
X#ifdef	TRS16
X#define	UREQ	" speed [defaultfile]"		/* Tandy Xenix/68k 3.2 */
X#define	UOPT	""
X#else	/* TRS16 */
X#define	UREQ	" line [speed [type [lined]]]"	/* real System V */
X#define	UOPT	"[-d defaultfile] [-h] [-r delay] [-t timeout]"
X#endif	/* TRS16 */
X
X#define	VALUE(cptr)	((cptr == (char *) NULL) ? "NULL" : cptr)
X
Xstruct	speedtab {
X	ushort	cbaud;		/* baud rate */
X	char	*speed;		/* speed in display format */
X} speedtab[] = {
X	{ B50,	 "50"	},
X	{ B75,	 "75"	},
X	{ B110,	 "110"	},
X	{ B134,	 "134"	},
X	{ B150,	 "150"	},
X	{ B200,	 "200"	},
X	{ B300,	 "300"	},
X	{ B600,	 "600"	},
X	{ B1200, "1200"	},
X	{ B1800, "1800"	},
X	{ B2400, "2400"	},
X	{ B4800, "4800"	},
X	{ B9600, "9600"	},
X#ifdef	B19200
X	{ B19200,"19200"},
X#endif	/*B19200*/
X	{ EXTA,	 "EXTA"	},
X	{ EXTB,	 "EXTB"	},
X	{ 0,	 ""	}
X};
X
Xsig_t		timeout();
Xint		tputc();
Xvoid		exit_usage();
Xstruct	passwd	*getpwuid();
Xstruct	utmp	*getutent();
X#ifdef	DEBUG
Xchar		*ctime();
X#endif	/* DEBUG */
X
X
X#ifdef	UUGETTY
X
X#include <errno.h>
X
Xextern	int	errno;
X
Xchar	*lock, *altlock;
X
Xint	makelock(), readlock();
Xboolean	checklock();
Xsig_t	rmlocks();
X
X#endif	/* UUGETTY */
X
X
X#ifdef	WARNCASE
Xchar	*bad_case[] = {
X	"\r\n",
X	"If your terminal supports lower case letters, please\r\n",
X	"use them.  Login again, using lower case if possible.\r\n",
X	(char *) NULL
X};
X#endif	/* WARNCASE */
X
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X	Reg1 int c;
X	Reg2 int i;
X	Reg3 int fd;
X	int cbaud, flags;
X	char ch, *p, devname[MAXLINE+1];
X	STDCHAR buf[MAXLINE+1];
X	char termcap[1024], tbuf[64];
X	char *tgetstr();
X	DEF **def;
X	TERMIO termio;
X	GTAB *gtab;
X	FILE *fp;
X	struct utmp *utmp;
X
X#if defined(DEBUG) || defined(LOGUTMP)
X	time_t clock;
X#endif	/* DEBUG || LOGUTMP */
X
X#ifndef	TRS16
X	char lined[MAXLINE+1];
X#endif	/* TRS16 */
X
X#ifdef	UUGETTY
X	struct passwd *pwd;
X	UIDTYPE uucpuid = 0;
X	GIDTYPE uucpgid = 0;
X#else	/* UUGETTY */
X	struct stat st;
X#endif	/* UUGETTY */
X
X#ifdef	TTYTYPE
X	char name[16], line[16];
X#endif	/* TTYTYPE */
X
X#ifdef	LOGUTMP
X	int pid;
X#endif	/* LOGUTMP */
X
X#ifdef	ISSUE
X	char *issue = ISSUE;			/* default issue file */
X#endif	/* ISSUE */
X
X	boolean clear = TRUE;			/* clear screen flag */
X	char *login = LOGIN;			/* default login program */
X	char *clrscr = (char *) NULL;		/* string to clear screen */
X	char *defname = (char *) NULL;		/* defaults file name */
X	char *init = (char *) NULL;		/* value of INIT */
X	char term[16];				/* terminal type */
X	boolean waitchar = FALSE;		/* wait for char flag */
X	unsigned int delay = 0;			/* #sec's delay before prompt */
X
X	extern int optind;
X	extern char *optarg;
X
X	/* startup
X	 */
X	(void) signal(SIGINT, SIG_IGN);
X	(void) signal(SIGQUIT, SIG_DFL);
X	(void) signal(SIGTERM, SIG_DFL);
X
X	(void) strcpy(term, "unknown");
X	NoHangUp = FALSE;
X	Check = FALSE;
X	CheckFile = (char *) NULL;
X#ifdef	DEBUG
X	Debug = 0;
X#endif	/* DEBUG */
X	Device = "unknown";
X	GtabId = (char *) NULL;
X	LineD = (char *) NULL;
X	TimeOut = 0;
X#ifdef	WARNCASE
X	WarnCase = TRUE;
X#endif	/* WARNCASE */
X
X	/* who am I?
X	 */
X#ifdef	UUGETTY
X	MyName = "uugetty";
X#else
X	MyName = "getty";
X#endif	/* UUGETTY */
X
X	/* process the command line
X	 */
X
X	while ((c = getopt(argc, argv, "D:c:d:ht:")) != EOF) {
X		switch (c) {
X#ifdef	DEBUG
X		case 'D':
X			(void) sscanf(optarg, "%o", &Debug);
X			Dfp = stderr;
X			break;
X#endif	/* DEBUG */
X		case 'c':
X			Check = TRUE;
X			CheckFile = optarg;
X			break;
X		case 'd':
X			defname = optarg;
X			break;
X		case 'h':
X			NoHangUp = TRUE;
X			break;
X		case 'r':
X			waitchar = TRUE;
X			delay = (unsigned) atoi(optarg);
X			break;
X		case 't':
X			TimeOut = atoi(optarg);
X			break;
X		case '?':
X			exit_usage(2);
X		}
X	}
X
X	/* just checking?
X	 */
X	if (Check) {
X		(void) signal(SIGINT, SIG_DFL);
X		(void) gtabvalue((char *) NULL);
X		exit(0);
X	}
X
X#ifdef	TRS16
X	
X	/* special handling for v7-based init
X	 */
X
X	if (optind < argc)
X		GtabId = argv[optind++];
X	else {
X		logerr("no speed given");
X		exit_usage(2);
X	}
X
X	/* Tandy Xenix/68k 3.2 /etc/inittab allows one optional argument
X	 * after the speed flag.  The best use I could do with it here is
X	 * to assume that it's the name of the defaults file to be used.
X	 *
X	 * Sigh.  Actually, it's not optional -- if none is given in
X	 * /etc/inittab, then it appears here as a 0-length string.
X	 */
X	if (optind < argc)
X		if (strlen(argv[optind]) > 0)
X			defname = argv[optind++];
X
X	if ((p = ttyname(STDIN)) != (char *) NULL)
X		Device = p+5;		/* strip off "/dev/" */
X	else {
X		logerr("cannot determine line");
X		exit_usage(2);
X	}
X
X#else	/* TRS16 */
X
X	/* normal System V handling
X	 */
X
X	if (optind < argc)
X		Device = argv[optind++];
X	else {
X		logerr("no line given");
X		exit_usage(2);
X	}
X	if (optind < argc)
X		GtabId = argv[optind++];
X	if (optind < argc)
X		(void) strncpy(term, argv[optind++], sizeof(term));
X	if (optind < argc) {
X		(void) strncpy(lined, argv[optind++], sizeof(lined));
X		LineD = lined;
X	}
X
X#endif	/* TRS16 */
X
X#ifdef	TTYTYPE
X
X	if (strequal(term, "unknown")) {
X		if ((fp = fopen(TTYTYPE, "r")) == (FILE *) NULL) {
X			(void) sprintf(MsgBuf, "cannot open %s", TTYTYPE);
X			logerr(MsgBuf);
X		} else {
X			while ((fscanf(fp, "%s %s", name, line)) != EOF) {
X				if (strequal(line, Device)) {
X					(void) strncpy(term,name,sizeof(term));
X					break;
X				}
X			}
X			(void) fclose(fp);
X		}
X	}
X
X#endif	/* TTYTYPE */
X
X	/* need full name of the device
X	 */
X	(void) sprintf(devname, "/dev/%s", Device);
X
X	/* command line parsed, now build the list of
X	 * runtime defaults; this may override things set above.
X	 */
X	def = defbuild(defname);
X
X#ifdef	DEBUG
X
X	/* debugging on?
X	 */
X	if ((p = defvalue(def, "DEBUG")) != (char *) NULL)
X		(void) sscanf(p, "%o", &Debug);
X
X	if (Debug) {
X		(void) sprintf(buf, "/tmp/getty:%s", Device);
X		if ((Dfp = fopen(buf, "a+")) == (FILE *) NULL) {
X			logerr("cannot open debug file");
X			exit(FAIL);
X		} else {
X			if (fileno(Dfp) < 3) {
X				if ((fd = fcntl(fileno(Dfp), F_DUPFD, 3)) > 2) {
X					(void) fclose(Dfp);
X					Dfp = fdopen(fd, "a+");
X				}
X			}
X			(void) time(&clock);
X			(void) fprintf(Dfp, "%s Started: %s",
X					MyName, ctime(&clock));
X		}
X	}
X
X	debug(D_OPT, "command line values:\n");
X	debug(D_OPT, " [-d] defname  = (%s)\n", VALUE(defname));
X	debug(D_OPT, " [-h] NoHangUp = (%s)\n", (NoHangUp) ? "TRUE" : "FALSE");
X	debug(D_OPT, " [-r] waitchar = (%s)\n", (waitchar) ? "TRUE" : "FALSE");
X	debug(D_OPT, "      delay    = (%u)\n", delay);
X	debug(D_OPT, " [-t] TimeOut  = (%d)\n", TimeOut);
X	debug(D_OPT, " line  = (%s)\n", VALUE(Device));
X	debug(D_OPT, " speed = (%s)\n", VALUE(GtabId));
X	debug(D_OPT, " type  = (%s)\n", term);
X	debug(D_OPT, " lined = (%s)\n", VALUE(LineD));
X	debug(D_RUN, "loading defaults\n");
X
X#endif	/* DEBUG */
X
X	/* setup all runtime values
X	 */
X
X	if ((SysName = defvalue(def, "SYSTEM")) == (char *) NULL)
X		SysName = getuname();
X
X	if ((Version = defvalue(def, "VERSION")) != (char *) NULL)
X		if (*Version == '/') {
X			if ((fp = fopen(Version, "r")) != (FILE *) NULL) {
X				(void) fgets(buf, sizeof(buf), fp);
X				(void) fclose(fp);
X				buf[strlen(buf)-1] = '\0';
X				Version = strdup(buf);
X			}
X		}
X
X	if ((p = defvalue(def, "LOGIN")) != (char *) NULL)
X		login = p;
X	if ((p = defvalue(def, "INIT")) != (char *) NULL)
X		init = p;
X#ifdef	ISSUE
X	if ((p = defvalue(def, "ISSUE")) != (char *) NULL)
X		issue = p;
X#endif	/* ISSUE */
X	if ((p = defvalue(def, "CLEAR")) != (char *) NULL)
X		if (strequal(p, "NO"))
X			clear = FALSE;
X	if ((p = defvalue(def, "HANGUP")) != (char *) NULL)
X		if (strequal(p, "NO"))
X			NoHangUp = TRUE;
X	if ((p = defvalue(def, "WAITCHAR")) != (char *) NULL)
X		if (strequal(p, "YES"))
X			waitchar = TRUE;
X	if ((p = defvalue(def, "DELAY")) != (char *) NULL)
X		delay = (unsigned) atoi(p);
X	if ((p = defvalue(def, "TIMEOUT")) != (char *) NULL)
X		TimeOut = atoi(p);
X
X	/* find out how to clear the screen
X	 */
X	if (!strequal(term, "unknown")) {
X		p = tbuf;
X		if ((tgetent(termcap, term)) == 1)
X			if ((clrscr = tgetstr("cl", &p)) == (char *) NULL)
X				clrscr = "";
X	}
X
X#ifdef	UUGETTY
X
X	debug2(D_RUN, "check for lockfiles\n");
X
X	/* deal with the lockfiles; we don't want to charge
X	 * ahead if uucp, kermit or something is already
X	 * using the line.
X	 */
X
X	/* name the lock file(s)
X	 */
X	(void) sprintf(buf, LOCK, Device);
X	lock = strdup(buf);
X	altlock = defvalue(def, "ALTLOCK");
X	if (altlock != (char *) NULL) {
X		(void) sprintf(buf, LOCK, altlock);
X		altlock = strdup(buf);
X	}
X	debug3(D_LOCK, "lock = (%s)\n", lock);
X	debug3(D_LOCK, "altlock = (%s)\n", VALUE(altlock));
X
X	/* check for existing lock file(s)
X	 */
X	if (checklock(lock) == TRUE) {
X		while (checklock(lock) == TRUE)
X			(void) sleep(60);
X		exit(0);
X	}
X
X	/* there's a race condition just asking for trouble here  :-(
X	 */
X	if (altlock != (char *) NULL && checklock(altlock) == TRUE) {
X		while (checklock(altlock) == TRUE)
X			(void) sleep(60);
X		exit(0);
X	}
X
X	/* allow uucp to access the device
X	 */
X	(void) chmod(devname, 0666);
X	if ((pwd = getpwuid(UUCPID)) != (struct passwd *) NULL) {
X		uucpuid = pwd->pw_uid;
X		uucpgid = pwd->pw_gid;
X	}
X	(void) chown(devname, uucpuid, uucpgid);
X
X#else	/* UUGETTY */
X
X	(void) chmod(devname, 0622);
X	if (stat(devname, &st) == 0)
X		(void) chown(devname, 0, st.st_gid);
X	else
X		(void) chown(devname, 0, 0);
X
X#endif	/* UUGETTY */
X
X	/* the line is mine now ...
X	 */
X
X	debug2(D_RUN, "open stdin, stdout and stderr\n");
X
X	/* get a new fd
X	 */
X	if ((fd = open(devname, O_RDWR)) < 0) {	/* open the device */
X		logerr("cannot open line");
X		exit(FAIL);
X	}
X
X	/* make new fd == stdin if it isn't already
X	 */
X	if (fd > 0) {
X		(void) close(0);
X		if (dup(fd) != 0) {
X			logerr("cannot open stdin");
X			exit(FAIL);
X		}
X	}
X
X	/* make stdout and stderr, too
X	 */
X	(void) close(1);
X	(void) close(2);
X	if (dup(0) != 1) {
X		logerr("cannot open stdout");
X		exit(FAIL);
X	}
X	if (dup(0) != 2) {
X		logerr("cannot open stderr");
X		exit(FAIL);
X	}
X
X	if (fd > 0)
X		(void) close(fd);
X
X	/* no buffering
X	 */
X	setbuf(stdin, (char *) NULL);
X	setbuf(stdout, (char *) NULL);
X	setbuf(stderr, (char *) NULL);
X
X	debug2(D_RUN, "setup terminal\n");
X
X	/* get the required info from the gettytab file
X	 */
X	gtab = gtabvalue(GtabId);
X
X	/* setup terminal
X	 */
X	if (!NoHangUp) {
X		(void) ioctl(STDIN, TCGETA, &termio);
X		termio.c_cflag &= ~CBAUD;	/* keep all but CBAUD bits */
X		termio.c_cflag |= B0;		/* set speed == 0 */
X		(void) ioctl(STDIN, TCSETAF, &termio);
X	}
X	settermio(&(gtab->itermio), INITIAL);
X
X	/* handle init sequence if requested
X	 */
X	if (init != (char *) NULL) {
X		debug2(D_RUN, "perform line initialization\n");
X		if (initialize(init) == FAIL)
X			logerr("warning: INIT sequence failed");
X	}
X
X	/* do we need to wait ?
X	 */
X	if (waitchar) {
X
X		/* clear O_NDELAY flag
X		 */
X		flags = fcntl(STDIN, F_GETFL, 0);
X		(void) fcntl(STDIN, F_SETFL, flags & ~(O_NDELAY));
X
X		debug2(D_RUN, "waiting for any char ...\n");
X
X		(void) read(STDIN, &ch, 1);		/* this will block */
X
X		debug2(D_RUN, "... got one!\n");
X
X		if (delay) {
X			(void) sleep(delay);
X			/* eat up any garbage from the line (modem) */
X			(void) fcntl(STDIN, F_SETFL, flags | O_NDELAY);
X			while (read(STDIN, &ch, 1) == 1)
X				;
X		}
X
X		/* put back original flags
X		 */
X		(void) fcntl(STDIN, F_SETFL, flags);
X
X	}
X
X#ifdef	UUGETTY
X
X	debug2(D_RUN, "locking the line\n");
X
X	/* try to lock the line
X	 */
X	if (makelock(lock) == FAIL) {
X		while (checklock(lock) == TRUE)
X			(void) sleep(60);
X		exit(0);
X	}
X	if (altlock != (char *) NULL && makelock(altlock) == FAIL) {
X		while (checklock(altlock) == TRUE)
X			(void) sleep(60);
X		exit(0);
X	}
X
X	/* set to remove lockfile(s) on certain signals
X	 */
X	(void) signal(SIGINT, rmlocks);
X	(void) signal(SIGQUIT, rmlocks);
X	(void) signal(SIGTERM, rmlocks);
X
X#endif	/* UUGETTY */
X
X#ifdef	LOGUTMP
X
X	debug2(D_RUN, "update utmp/wtmp files\n");
X
X	pid = getpid();
X	while ((utmp = getutent()) != (struct utmp *) NULL) {
X		if (utmp->ut_type == INIT_PROCESS && utmp->ut_pid == pid) {
X
X			debug2(D_UTMP, "logutmp entry made\n");
X			/* show login process in utmp
X			 */
X			strncopy(utmp->ut_line, Device);
X			strncopy(utmp->ut_user, "LOGIN");
X			utmp->ut_pid = pid;
X			utmp->ut_type = LOGIN_PROCESS;
X			(void) time(&clock);
X			utmp->ut_time = clock;
X			pututline(utmp);
X
X			/* write same record to end of wtmp
X			 */
X			if ((fp = fopen(WTMP_FILE, "w+")) != (FILE *) NULL) {
X				(void) fseek(fp, 0L, 2);
X				(void) fwrite((char *)utmp,sizeof(*utmp),1,fp);
X				(void) fclose(fp);
X			}
X		}
X	}
X	endutent();
X
X#endif	/* LOGUTMP */
X
X	debug2(D_RUN, "entering login loop\n");
X
X	/* loop until a successful login is made
X	 */
X	for (;;) {
X
X		/* set Nusers value
X		 */
X		Nusers = 0;
X		setutent();
X		while ((utmp = getutent()) != (struct utmp *) NULL) {
X#ifdef	USER_PROCESS
X			if (utmp->ut_type == USER_PROCESS)
X#endif	/* USER_PROCESS */
X			{
X				Nusers++;
X				debug3(D_UTMP, "utmp entry (%s)\n",
X						utmp->ut_name);
X			}
X		}
X		endutent();
X		debug3(D_UTMP, "Nusers=%d\n", Nusers);
X
X		/* set Speed value
X		 */
X		cbaud = gtab->itermio.c_cflag & CBAUD;
X		for (i=0; speedtab[i].cbaud != cbaud; i++)
X			;
X		Speed = speedtab[i].speed;
X
X#ifdef	ISSUE
X		if (clear && *clrscr)		/* clear screen */
X			(void) tputs(clrscr, 1, tputc);
X
X		(void) fputc('\r', stdout);	/* just in case */
X
X		if (def == (DEF **) NULL)	/* no defaults file */
X			(void) Fputs("@S\n", stdout);
X
X		/* display ISSUE, if present
X		 */
X		if (*issue != '/') {
X			(void) Fputs(issue, stdout);
X			(void) fputs("\r\n", stdout);
X		} else if ((fp = fopen(issue, "r")) != (FILE *) NULL) {
X			while (fgets(buf, sizeof(buf), fp) != (char *) NULL)
X				(void) Fputs(buf, stdout);
X			(void) fclose(fp);
X		}
X#endif	/* ISSUE */
X
X	login_prompt:
X		/* display login prompt
X		 */
X		(void) Fputs(gtab->login, stdout);
X
X		/* eat any chars from line noise
X		 */
X		(void) ioctl(STDIN, TCFLSH, 0);
X
X		/* start timer, if required
X		 */
X		if (TimeOut > 0) {
X			(void) signal(SIGALRM, timeout);
X			(void) alarm((unsigned) TimeOut);
X		}
X
X		/* handle the login name
X		 */
X		switch (getlogname(&termio, buf, MAXLINE)) {
X		case SUCCESS:
X			/* stop alarm clock
X			 */
X			if (TimeOut > 0)
X				(void) alarm((unsigned) 0);
X
X			/* setup terminal
X			 */
X			termio.c_iflag |= gtab->ftermio.c_iflag;
X			termio.c_oflag |= gtab->ftermio.c_oflag;
X			termio.c_cflag |= gtab->ftermio.c_cflag;
X			termio.c_lflag |= gtab->ftermio.c_lflag;
X			termio.c_line  |= gtab->ftermio.c_line;
X			settermio(&termio, FINAL);
X#ifdef	DEBUG
X			if (Debug)
X				(void) fclose(Dfp);
X#endif	/* DEBUG */
X
X#ifdef	SETTERM
X			(void) sprintf(MsgBuf, "TERM=%s", term);
X			(void) putenv(strdup(MsgBuf));
X#endif	/* SETTERM */
X
X			/* hand off to login
X			 */
X			(void) execl(login, "login", buf, (char *) NULL);
X			(void) execl("/bin/sh", "sh", "-c",
X					login, buf, (char *) NULL);
X			(void) sprintf(MsgBuf, "cannot execute %s", login);
X			logerr(MsgBuf);
X			exit(FAIL);
X
X		case BADSPEED:
X			/* go to next entry
X			 */
X			GtabId = gtab->next_id;
X			gtab = gtabvalue(GtabId);
X			settermio(&(gtab->itermio), INITIAL);
X			break;
X
X#ifdef	WARNCASE
X		case BADCASE:
X			/* first try was all uppercase
X			 */
X			for (i=0; bad_case[i] != (char *) NULL; i++)
X				(void) fputs(bad_case[i], stdout);
X			goto login_prompt;
X#endif	/* WARNCASE */
X
X		case NONAME:
X			/* no login name entered
X			 */
X			break;
X
X		}
X	}
X}
X
X
X/*
X**	timeout() - handles SIGALRM
X*/
X
Xsig_t
Xtimeout()
X{
X	TERMIO termio;
X
X	/* say bye-bye
X	 */
X	(void) sprintf(MsgBuf, "\nTimed out after %d seconds.\n", TimeOut);
X	(void) Fputs(MsgBuf, stdout);
X	(void) Fputs("Bye Bye.\n", stdout);
X
X	/* force a hangup
X	 */
X	(void) ioctl(STDIN, TCGETA, &termio);
X	termio.c_cflag &= ~CBAUD;
X	termio.c_cflag |= B0;
X	(void) ioctl(STDIN, TCSETAF, &termio);
X
X	exit(1);
X}
X
X
X/*
X**	tputc() - output a character for tputs()
X*/
X
Xint
Xtputc(c)
Xchar c;
X{
X	fputc(c, stdout);
X}
X
X
X/*
X**	exit_usage() - exit with usage display
X*/
X
Xvoid
Xexit_usage(code)
Xint code;
X{
X	FILE *fp;
X
X	if ((fp = fopen(CONSOLE, "w")) != (FILE *) NULL) {
X		(void) fprintf(fp, USAGE, MyName, UOPT, UREQ, MyName);
X		(void) fclose(fp);
X	}
X	exit(code);
X}
X
X
X#ifdef	UUGETTY
X
X/*
X**	makelock() - attempt to create a lockfile
X**
X**	Returns FAIL if lock could not be made (line in use).
X*/
X
Xint
Xmakelock(name)
Xchar *name;
X{
X	int fd, pid;
X	char *temp, buf[MAXLINE+1];
X#ifdef	ASCIIPID
X	char apid[16];
X#endif	/* ASCIIPID */
X	int getpid();
X	char *mktemp();
X
X	debug3(D_LOCK, "makelock(%s) called\n", name);
X
X	/* first make a temp file
X	 */
X	(void) sprintf(buf, LOCK, "TM.XXXXXX");
X	if ((fd = creat((temp=mktemp(buf)), 0444)) == FAIL) {
X		(void) sprintf(MsgBuf, "cannot create tempfile (%s)", temp);
X		logerr(MsgBuf);
X		return(FAIL);
X	}
X	debug3(D_LOCK, "temp = (%s)\n", temp);
X
X	/* put my pid in it
X	 */
X#ifdef	ASCIIPID
X	(void) sprintf(apid, "%09d", getpid());
X	(void) write(fd, apid, strlen(apid));
X#else
X	pid = getpid();
X	(void) write(fd, (char *)&pid, sizeof(pid));
X#endif	/* ASCIIPID */
X	(void) close(fd);
X
X	/* link it to the lock file
X	 */
X	while (link(temp, name) == FAIL) {
X		debug3(D_LOCK, "link(temp,name) failed, errno=%d\n", errno);
X		if (errno == EEXIST) {		/* lock file already there */
X			if ((pid = readlock(name)) == FAIL)
X				continue;
X			if ((kill(pid, 0) == FAIL) && errno == ESRCH) {
X				/* pid that created lockfile is gone */
X				(void) unlink(name);
X				continue;
X			}
X		}
X		debug2(D_LOCK, "lock NOT made\n");
X		(void) unlink(temp);
X		return(FAIL);
X	}
X	debug2(D_LOCK, "lock made\n");
X	(void) unlink(temp);
X	return(SUCCESS);
X}
X
X/*
X**	checklock() - test for presense of valid lock file
X**
X**	Returns TRUE if lockfile found, FALSE if not.
X*/
X
Xboolean
Xchecklock(name)
Xchar *name;
X{
X	int pid;
X	struct stat st;
X
X	debug3(D_LOCK, "checklock(%s) called\n", name);
X
X	if ((stat(name, &st) == FAIL) && errno == ENOENT) {
X		debug2(D_LOCK, "stat failed, no file\n");
X		return(FALSE);
X	}
X
X	if ((pid = readlock(name)) == FAIL) {
X		debug2(D_LOCK, "couldn't read lockfile\n");
X		return(FALSE);
X	}
X
X	if ((kill(pid, 0) == FAIL) && errno == ESRCH) {
X		debug2(D_LOCK, "no active process has lock, will remove\n");
X		(void) unlink(name);
X		return(FALSE);
X	}
X
X	debug2(D_LOCK, "active process has lock, return(TRUE)\n");
X	return(TRUE);
X}
X
X/*
X**	readlock() - read contents of lockfile
X**
X**	Returns pid read or FAIL on error.
X*/
X
Xint
Xreadlock(name)
Xchar *name;
X{
X	int fd, pid;
X#ifdef	ASCIIPID
X	char apid[16];
X#endif	/* ASCIIPID */
X
X	if ((fd = open(name, O_RDONLY)) == FAIL)
X		return(FAIL);
X
X#ifdef	ASCIIPID
X	(void) read(fd, apid, sizeof(apid));
X	(void) sscanf(apid, "%d", &pid);
X#else
X	(void) read(fd, (char *)&pid, sizeof(pid));
X#endif	/* ASCIIPID */
X
X	(void) close(fd);
X	return(pid);
X}
X
X/*
X**	rmlocks() - remove lockfile(s)
X*/
X
Xsig_t
Xrmlocks()
X{
X	if (altlock != (char *) NULL)
X		(void) unlink(altlock);
X
X	(void) unlink(lock);
X}
X
X#endif	/* UUGETTY */
X
X
X/* end of main.c */
!STUFFY!FUNK!
echo Extracting funcs.c
sed >funcs.c <<'!STUFFY!FUNK!' -e 's/X//'
X/*
X**	$Id: funcs.c,v 1.1 90/01/16 16:14:28 paul Exp Locker: paul $
X**
X**	Miscellaneous routines.
X*/
X
X/*
X**	Copyright 1989,1990 by Paul Sutcliffe Jr.
X**
X**	Permission is hereby granted to copy, reproduce, redistribute,
X**	or otherwise use this software as long as: there is no monetary
X**	profit gained specifically from the use or reproduction or this
X**	software, it is not sold, rented, traded or otherwise marketed,
X**	and this copyright notice is included prominently in any copy
X**	made.
X**
X**	The author make no claims as to the fitness or correctness of
X**	this software for any use whatsoever, and it is provided as is. 
X**	Any use of this software is at the user's own risk.
X*/
X
X/*
X**	$Log:	funcs.c,v $
X**	Revision 1.1  90/01/16  16:14:28  paul
X**	Initial revision
X**	
X*/
X
X
X#include "getty.h"
X#include "table.h"
X#include <ctype.h>
X#include <time.h>
X#include <sys/utsname.h>
X#include <setjmp.h>
X#include <signal.h>
X#ifdef	DEBUG
X#include <varargs.h>
X#endif	/* DEBUG */
X
X#if defined(RCSID) && !defined(lint)
Xstatic char *RcsId =
X"@(#)$Id: funcs.c,v 1.1 90/01/16 16:14:28 paul Exp Locker: paul $";
X#endif
X
X#ifndef	MAXBUF
X#define	MAXBUF	512	/* buffer size */
X#endif	/* MAXBUF */
X
X#ifndef	EXPFAIL
X#define	EXPFAIL	30	/* num seconds to wait for expected input */
X#endif	/* EXPFAIL */
X
X#define	SEND	0	/* states for initialize() */
X#define EXPECT	1
X
Xchar	*unquote();
Xint	expect(), send();
Xsig_t	expalarm();
X
X
X/*
X**	Fputs() - does fputs() with '\' and '@' expansion
X**
X**	Returns EOF if an error occurs.
X*/
X
Xint
XFputs(s, stream)
XReg1 char *s;
XReg2 FILE *stream;
X{
X	char c, n, tbuf[9], ubuf[32];
X	time_t clock;
X	struct tm *lt, *localtime();
X
X	while (c = *s++) {
X		if ((c == '@') && (n = *s++)) {
X			switch (n) {
X			case 'B':	/* baud rate */
X				if (*Speed && Fputs(Speed, stream) == EOF)
X					return(EOF);
X				break;
X			case 'D':	/* date */
X				(void) time(&clock);
X				lt = localtime(&clock);
X				(void) sprintf(tbuf, "%02d/%02d/%02d",
X						++(lt->tm_mon),
X						lt->tm_mday, lt->tm_year);
X				if (Fputs(tbuf, stream) == EOF)
X					return(EOF);
X				break;
X			case 'L':	/* line */
X				if (*Device && Fputs(Device, stream) == EOF)
X					return(EOF);
X				break;
X			case 'S':	/* system node name */
X				if (*SysName && Fputs(SysName, stream) == EOF)
X					return(EOF);
X				break;
X#ifdef	M_XENIX
X			/* Special case applys here: SCO XENIX's
X			 * /etc/gettydefs file has "\r\n@!login: " as
X			 * the login field value, and replaces the "@"
X			 * with the system node name.  This will do
X			 * the same thing.
X			 */
X			case '!':
X				if (*SysName && Fputs(SysName, stream) == EOF)
X					return(EOF);
X				(void) fputc(n, stream);
X				break;
X#endif	/* M_XENIX */
X			case 'T':	/* time */
X				(void) time(&clock);
X				lt = localtime(&clock);
X				(void) sprintf(tbuf, "%02d:%02d:%02d",
X						lt->tm_hour,
X						lt->tm_min, lt->tm_sec);
X				if (Fputs(tbuf, stream) == EOF)
X					return(EOF);
X				break;
X			case 'U':	/* number of active users */
X				(void) sprintf(ubuf, "%d", Nusers);
X				if (Fputs(ubuf, stream) == EOF)
X					return(EOF);
X				break;
X			case 'V':	/* version */
X				if (*Version && Fputs(Version, stream) == EOF)
X					return(EOF);
X				break;
X			case '@':	/* in case '@@' was used */
X				if (fputc(n, stream) == EOF)
X					return(EOF);
X				break;
X			}
X		} else {
X			if (c == '\\')
X				s = unquote(s, &c);
X			/* we're in raw mode: send CR before every LF
X			 */
X			if (c == '\n' && (fputc('\r', stream) == EOF))
X				return(EOF);
X			if (c && fputc(c, stream) == EOF)
X				return(EOF);
X		}
X	}
X	return(SUCCESS);
X}
X
X
X/*
X**	getuname() - retrieve the system's node name
X**
X**	Returns pointer to name or a zero-length string if not found.
X*/
X
Xchar *
Xgetuname()
X{
X#ifdef	HOSTNAME			/* hardwire the name */
X
X	static char name[] = HOSTNAME;
X
X	return(name);
X
X#else	/* HOSTNAME */
X
X#ifdef	M_XENIX
X#define	SYSTEMID "/etc/systemid"
X	static FILE *fp;
X#endif	/* M_XENIX */
X
X	struct utsname uts;
X	static char name[80];
X
X	name[0] = '\0';
X
X#ifdef	DOUNAME				/* dig it out of the kernel */
X
X	if (uname(&uts) != FAIL)
X		(void) strcpy(name, uts.nodename);
X
X#endif	/* DOUNAME */
X
X#ifdef	M_XENIX				/* if Xenix's uts.nodename is empty */
X	if (strlen(name) == 0) {
X		if ((fp = fopen(SYSTEMID, "r")) != (FILE *) NULL) {
X			(void) fgets(name, sizeof(name), fp);
X			(void) fclose(fp);
X			name[strlen(name)-1] = '\0';
X		}
X	}
X#endif	/* M_XENIX */
X
X#ifdef	PHOSTNAME			/* get it from the shell */
X
X	if (strlen(name) == 0) {
X		FILE *cmd;
X		if ((cmd = popen(PHOSTNAME, "r")) != (FILE *) NULL) {
X			(void) fgets(name, sizeof(name), cmd);
X			(void) pclose(cmd);
X			name[strlen(name)-1] = '\0';
X		}
X	}
X
X#endif	/* PHOSTNAME */
X
X	return(name);
X
X#endif	/* HOSTNAME */
X}
X
X
X/*
X**	settermio() - setup tty according to termio values
X*/
X
Xvoid
Xsettermio(termio, state)
XReg2 TERMIO *termio;
Xint state;
X{
X	Reg1 int i;
X	static TERMIO setterm;
X
X#ifdef	TRS16
X	/* Tandy 16/6000 console's BREAK key sends ^C
X	 */
X	char Cintr = (strequal(Device, "console")) ? '\003' : CINTR;
X#else
X	char Cintr = CINTR;
X#endif	/* TRS16 */
X
X#ifdef	MY_ERASE
X	char Cerase = MY_ERASE;
X#else
X	char Cerase = CERASE;
X#endif	/* MY_ERASE */
X
X#ifdef	MY_KILL
X	char Ckill = MY_KILL;
X#else
X	char Ckill = CKILL;
X#endif	/* MY_KILL */
X
X	(void) ioctl(STDIN, TCGETA, &setterm);
X
X	switch (state) {
X	case INITIAL:
X		setterm.c_iflag = termio->c_iflag;
X		setterm.c_oflag = termio->c_oflag;
X		setterm.c_cflag = termio->c_cflag;
X		setterm.c_lflag = termio->c_lflag;
X		setterm.c_line  = termio->c_line;
X
X		/* single character processing
X		 */
X		setterm.c_lflag &= ~(ICANON);
X		setterm.c_cc[VMIN] = 1;
X		setterm.c_cc[VTIME] = 0;
X
X		/* sanity check
X		 */
X		if ((setterm.c_cflag & CBAUD) == 0)
X			setterm.c_cflag |= B9600;
X		if ((setterm.c_cflag & CSIZE) == 0)
X			setterm.c_cflag |= DEF_CFL;
X		setterm.c_cflag |= (CREAD | HUPCL);
X		setterm.c_lflag |= ECHO;
X
X		(void) ioctl(STDIN, TCSETAF, &setterm);
X		break;
X
X	case FINAL:
X		setterm.c_iflag = termio->c_iflag;
X		setterm.c_oflag = termio->c_oflag;
X		setterm.c_cflag = termio->c_cflag;
X		setterm.c_lflag = termio->c_lflag;
X		setterm.c_line  = termio->c_line;
X
X		/* sanity check
X		 */
X		if ((setterm.c_cflag & CBAUD) == 0)
X			setterm.c_cflag |= B9600;
X		if ((setterm.c_cflag & CSIZE) == 0)
X			setterm.c_cflag |= DEF_CFL;
X		setterm.c_cflag |= CREAD;
X		setterm.c_lflag |= ECHO;
X
X		/* set c_cc[] chars to reasonable values
X		 */
X		for (i=0; i < NCC; i++)
X			setterm.c_cc[i] = CNUL;
X		setterm.c_cc[VINTR] = Cintr;
X		setterm.c_cc[VQUIT] = CQUIT;
X		setterm.c_cc[VERASE] = Cerase;
X		setterm.c_cc[VKILL] = Ckill;
X		setterm.c_cc[VEOF] = CEOF;
X#ifdef	CEOL
X		setterm.c_cc[VEOL] = CEOL;
X#endif	/* CEOL */
X
X		(void) ioctl(STDIN, TCSETAW, &setterm);
X		break;
X
X	}
X}
X
X
X/*
X**	initialize() - handle expect/send init sequence to Device
X**
X**	Returns FAIL if an error occurs.
X*/
X
Xint
Xinitialize(s)
Xchar *s;
X{
X	Reg1 int state = SEND;
X	boolean finished = FALSE, if_fail = FALSE;
X	char c, *p;
X	char word[MAXLINE+1];		/* buffer for next word */
X
X	debug3(D_INIT, "initialize(%s) called\n", s);
X
X	while (!finished) {
X		p = word;
X		while (((c = (*s++ & 0177)) != '\0') && c != ' ' && c != '-')
X			*p++ = (c) ? c : '\177';
X		if (c == '\0')
X			finished = TRUE;
X		if (c == '-')
X			if_fail = (if_fail == FALSE) ? TRUE : FALSE;
X		*p = '\0';
X		switch (state) {
X		case SEND:
X			if (send(word) == FAIL)
X				return(FAIL);
X			state = EXPECT;
X			break;
X		case EXPECT:
X			if (expect(word) == FAIL) {
X				if (if_fail == FALSE)
X					return(FAIL);	/* no if-fail seq */
X			} else {
X				/* eat up rest of current sequence
X				 */
X				if (if_fail == TRUE) {
X					while ((c = (*s++ & 0177)) != '\0' &&
X						c != ' ')
X						;
X					if (c == '\0')
X						finished = TRUE;
X				}
X			}
X			state = SEND;
X			break;
X		}
X		continue;
X	}
X	debug2(D_INIT, "initialize() successful\n");
X	return (SUCCESS);
X}
X
X
X/*
X**	unquote() - decode char(s) after a '\' is found.
X**
X**	Returns the pointer s; decoded char in *c.
X*/
X
Xchar *
Xunquote(s, c)
Xchar *s, *c;
X{
X	int octal;
X	char n;
X
X	n = *s++;
X	switch (n) {
X	case 'b':
X		*c = '\b';	break;
X	case 'c':
X		if ((n = *s++) == '\n')
X			*c = '\0';
X		else
X			*c = n;
X		break;
X	case 'f':
X		*c = '\f';	break;
X	case 'n':
X		*c = '\n';	break;
X	case 'r':
X		*c = '\r';	break;
X	case 's':
X		*c = ' ';	break;
X	case 't':
X		*c = '\t';	break;
X	case '\n':
X		*c = '\0';	break;	/* ignore NL which follows a '\' */
X	case '\\':
X		*c = '\\';	break;	/* '\\' will give a single '\' */
X	default:
X		if (isdigit(n)) {
X			octal = 0;
X			do {
X				octal = (octal * 8) + (int) n;
X				n = *s++;
X			} while (isdigit(n));
X			*c = (char) (octal & 0177);
X		} else {
X			*c = n;
X		}
X		break;
X	}
X	return(s);
X}
X
X
X/*
X**	send() - send a string to stdout
X*/
X
Xint
Xsend(s)
XReg1 char *s;
X{
X	Reg2 int retval = SUCCESS;
X	char ch;
X
X	debug2(D_INIT, "SEND: (");
X	while (ch = *s++) {
X		if (ch == '\\') {
X			switch (*s) {
X			case 'p':		/* '\p' == pause */
X				debug2(D_INIT, "[pause]");
X				(void) sleep(1);
X				s++;		/* skip past 'p' */
X				continue;
X			case 'd':		/* '\d' == delay */
X				debug2(D_INIT, "[delay]");
X				(void) sleep(2);
X				s++;
X				continue;
X			default:
X				s = unquote(s, &ch);
X				break;
X			}
X		}
X		debug3(D_INIT, ((ch < ' ') ? "^%c" : "%c"),
X			       ((ch < ' ') ? ch | 0100 : ch));
X		if (write(STDOUT, &ch, 1) == FAIL) {
X			retval = FAIL;
X			break;
X		}
X	}
X	debug3(D_INIT, ") -- %s\n", (retval == SUCCESS) ? "OK" : "Failed");
X	return(retval);
X}
X
X
X/*
X**	expect() - look for a specific string on stdin
X*/
X
Xjmp_buf env;	/* here so expalarm() sees it */
X
Xint
Xexpect(s)
XReg1 char *s;
X{
X	Reg2 int i;
X	Reg3 retval = FAIL;
X	char ch, *p, word[MAXLINE+1], buf[MAXBUF];
X	sig_t (*oldalarm)();
X
X#ifdef	lint
X	/* shut lint up about 'warning: oldalarm may be used before set' */
X	oldalarm = signal(SIGALRM, SIG_DFL);
X#endif	/* lint */
X
X	if (setjmp(env)) {	/* expalarm returns non-zero here */
X		debug3(D_INIT, "[timed out after %d seconds]\n", EXPFAIL);
X		(void) signal(SIGALRM, oldalarm);
X		return(FAIL);
X	}
X
X	/* look for escape chars in expected word
X	 */
X	for (p = word; ch = (*s++ & 0177);) {
X		if (ch == '\\')
X			s = unquote(s, &ch);
X		*p++ = (ch) ? ch : '\177';
X	}
X	*p = '\0';
X
X	oldalarm = signal(SIGALRM, expalarm);
X	(void) alarm((unsigned) EXPFAIL);
X
X	debug2(D_INIT, "EXPECT: (");
X	debug1(D_INIT, word);
X	debug2(D_INIT, ")\n");
X	p = buf;
X	while (read(STDIN, &ch, 1) == 1) {
X		debug3(D_INIT, ((ch < ' ') ? "^%c" : "%c"),
X			       ((ch < ' ') ? ch | 0100 : ch));
X		*p++ = (char) ((int) ch & 0177);
X		*p = '\0';
X		if (strlen(buf) >= strlen(word)) {
X			for (i=0; buf[i]; i++)
X				if (strnequal(&buf[i], word, strlen(word))) {
X					retval = SUCCESS;
X					break;
X				}
X		}
X		if (retval == SUCCESS)
X			break;
X	}
X	(void) alarm((unsigned) 0);
X	(void) signal(SIGALRM, oldalarm);
X	debug3(D_INIT, " -- %s\n", (retval == SUCCESS) ? "got it" : "Failed");
X	return(retval);
X}
X
X
X/*
X**	expalarm() - called when expect()'s SIGALRM goes off
X*/
X
Xsig_t
Xexpalarm()
X{
X	longjmp(env, 1);
X}
X
X
X/*
X**	getlogname() - get the users login response
X**
X**	Returns int value indicating success.
X*/
X
Xint
Xgetlogname(termio, name, size)
XTERMIO *termio;
XReg1 char *name;
Xint size;
X{
X	Reg2 int count;
X	Reg3 int lower = 0;
X	Reg4 int upper = 0;
X	char ch, *p;
X	ushort lflag;
X
X#ifdef	MY_ERASE
X	char Erase = MY_ERASE;
X#else
X	char Erase = CERASE;
X#endif	/* MY_ERASE */
X#ifdef	MY_KILL
X	char Kill = MY_KILL;
X#else
X	char Kill = CKILL;
X#endif	/* MY_KILL */
X
X	debug2(D_GETL, "getlogname() called\n");
X
X	(void) ioctl(STDIN, TCGETA, termio);
X	lflag = termio->c_lflag;
X
X	termio->c_iflag = 0;
X	termio->c_oflag = 0;
X	termio->c_cflag = 0;
X	termio->c_lflag = 0;
X
X	p = name;	/* point to beginning of buffer */
X	count = 0;	/* nothing entered yet */
X
X	do {
X		if (read(STDIN, &ch, 1) != 1)	/* nobody home */
X			exit(0);
X		if ((ch = (char) ((int) ch & 0177)) == CEOF)
X			if (p == name)		/* ctrl-d was first char */
X				exit(0);
X		if (ch == CQUIT)		/* user wanted out, i guess */
X			exit(0);
X		if (ch == '\0') {
X			debug2(D_GETL, "returned (BADSPEED)\n");
X			return(BADSPEED);
X		}
X		if ((lflag & ECHO) == 0) {
X			(void) putc(ch, stdout);
X			(void) fflush(stdout);
X		}
X		if (ch == Erase) {
X			if (count) {
X				if ((lflag & ECHOE) == 0) {
X					(void) fputs(" \b", stdout);
X					(void) fflush(stdout);
X				}
X				--p;
X				--count;
X			}
X		} else if (ch == Kill) {
X			if ((lflag & ECHOK) == 0) {
X				(void) fputs("\r\n", stdout);
X				(void) fflush(stdout);
X			}
X			p = name;
X			count = 0;
X		} else {
X			*p++ = ch;
X			count++;
X			if (islower(ch))
X				lower++;
X			if (isupper(ch))
X				upper++;
X		}
X	} while ((ch != '\n') && (ch != '\r') && (count < size));
X
X	*(--p) = '\0';	/* terminate buffer */
X
X	if (ch == '\r') {
X		(void) putc('\n', stdout);
X		(void) fflush(stdout);
X		termio->c_iflag |= ICRNL;	/* turn on cr/nl xlate */
X		termio->c_oflag |= ONLCR;
X	} else if (ch == '\n') {
X		(void) putc('\r', stdout);
X		(void) fflush(stdout);
X	}
X
X	if (strlen(name) == 0) {
X		debug2(D_GETL, "returned (NONAME)\n");
X		return(NONAME);
X	}
X
X	if (upper && !lower) {
X#ifdef	WARNCASE
X		if (WarnCase) {
X			WarnCase = FALSE;
X			debug2(D_GETL, "returned (BADCASE)\n");
X			return(BADCASE);
X		}
X#endif	/* WARNCASE */
X		for (p=name; *p; p++)		/* make all chars UC */
X			*p = toupper(*p);
X		termio->c_iflag |= IUCLC;
X		termio->c_oflag |= OLCUC;
X		termio->c_lflag |= XCASE;
X	}
X
X	debug3(D_GETL, "returned (SUCCESS), name=(%s)\n", *name);
X	return(SUCCESS);
X}
X
X
X/*
X**	logerr() - display an error message
X*/
X
Xvoid
Xlogerr(msg)
XReg1 char *msg;
X{
X	Reg2 FILE *co;
X	char *errdev;
X
X	errdev = (Check) ? "/dev/tty" : CONSOLE;
X
X	if ((co = fopen(errdev, "w")) != (FILE *) NULL) {
X		(void) fprintf(co, "\r\n%s (%s): %s\r\n", MyName, Device, msg);
X		(void) fclose(co);
X	}
X
X#ifdef	TRYMAIL
X	else {
X		char buf[MAXLINE];
X		FILE *popen();
X
X		(void) sprintf(buf, "%s %s", MAILER, NOTIFY);
X		if ((co = popen(buf, "w")) != (FILE *) NULL) {
X			(void) fprintf(co, "To: %s\n", NOTIFY);
X			(void) fprintf(co, "Subject: %s problem\n\n", MyName);
X			(void) fprintf(co, "%s: %s\n", Device, msg);
X			(void) pclose(co);
X		}
X	}
X#endif	/* TRYMAIL */
X
X}
X
X
X#ifdef	DEBUG
X
X/*
X**	debug() - an fprintf to the debug file
X**
X**	Only does the output if the requested level is "set."
X*/
X
X/*VARARGS2*/
Xvoid
Xdebug(lvl, fmt, va_alist)
Xint lvl;
Xchar *fmt;
Xva_dcl
X{
X	va_list args;
X
X	va_start(args);
X	if (Debug & lvl) {
X		(void) vfprintf(Dfp, fmt, args);
X		(void) fflush(Dfp);
X	}
X	va_end(args);
X}
X
X/*
X**	dprint() - like debug(), but shows control chars
X*/
X
Xvoid
Xdprint(lvl, word)
Xint lvl;
Xchar *word;
X{
X	char *p, *fmt, ch;
X
X	if (Debug & lvl) {
X		p = word;
X		while (ch = *p++) {
X			if (ch < ' ') {
X				fmt = "^%c";
X				ch = ch | 0100;
X			} else {
X				fmt = "%c";
X			}
X			(void) fprintf(Dfp, fmt, ch);
X		}
X		(void) fflush(Dfp);
X	}
X}
X
X#endif	/* DEBUG */
X
X
X/* end of funcs.c */
!STUFFY!FUNK!
echo Extracting man/strdup.m4
sed >man/strdup.m4 <<'!STUFFY!FUNK!' -e 's/X//'
X.\" +----------
X.\" |	$Id: strdup.m4,v 1.1 90/01/16 16:04:12 paul Exp Locker: paul $
X.\" |
X.\" |	STRDUP man page.
X.\" |
X.\" |	Copyright 1989,1990 by Paul Sutcliffe Jr.
X.\" |
X.\" |	Permission is hereby granted to copy, reproduce, redistribute,
X.\" |	or otherwise use this software as long as: there is no monetary
X.\" |	profit gained specifically from the use or reproduction or this
X.\" |	software, it is not sold, rented, traded or otherwise marketed,
X.\" |	and this copyright notice is included prominently in any copy
X.\" |	made.
X.\" |
X.\" |	The author make no claims as to the fitness or correctness of
X.\" |	this software for any use whatsoever, and it is provided as is. 
X.\" |	Any use of this software is at the user's own risk.
X.\" |
X.\"
X.\" +----------
X.\" |	$Log:	strdup.m4,v $
X.\" |	Revision 1.1  90/01/16  16:04:12  paul
X.\" |	Initial revision
X.\" |	
X.\" |	
X.\" 
X.\" +----------
X.\" | M4 configuration
X.\"
Xinclude(config.m4).\"
X.\"
X.\" +----------
X.\" | Manpage source follows:
X.\"
X.TH STRDUP _library_section_
X.SH NAME
Xstrdup \- duplicate a string in memory
X.SH SYNOPSIS
X.B char *strdup(\fIstring\fB)\fR;
X.br
X.B char *\fIstring\fR;
X.SH DESCRIPTION
X.I Strdup
Xallocates storage space (with a call to
X.I malloc(_library_section_))
Xfor a copy of
X.I string
Xand returns a pointer to the storage space containing the copied
Xstring.
X.SH "RETURN VALUE"
X.I Strdup
Xreturns NULL if storage cannot be allocated.  Otherwise, a valid
Xpointer is returned.
X.SH "SEE ALSO"
Xmalloc(_library_section_),
Xstring(_library_section_)
X.SH AUTHOR
X.nf
XPaul Sutcliffe, Jr.  <paul@devon.lns.pa.us>
XUUCP: ...!rutgers!devon!paul
!STUFFY!FUNK!
echo ""
echo "End of kit 1 (of 4)"
cat /dev/null >kit1isdone
run=''
config=''
for iskit in 1 2 3 4; do
    if test -f kit${iskit}isdone; then
	run="$run $iskit"
    else
	todo="$todo $iskit"
    fi
done
case $todo in
    '')
	echo "You have run all your kits.  Please read README and then type Configure."
	chmod 755 Configure
	;;
    *)  echo "You have run$run."
	echo "You still need to run$todo."
	;;
esac
: Someone might mail this, so...
exit

INTERNET:  paul@devon.lns.pa.us        |      If life's a bitch, then
UUCP:      ...!rutgers!devon!paul      |      we must be her puppies.