[comp.sources.misc] v15i006: Enhanced SYSV Getty/Uugetty, Ver 2.0, part 3 of 5

paul@devon.lns.pa.us (Paul Sutcliffe Jr.) (10/06/90)

Posting-number: Volume 15, Issue 6
Submitted-by: paul@devon.lns.pa.us (Paul Sutcliffe Jr.)
Archive-name: getty_ps/part03

#! /bin/sh

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

echo "This is getty 2.0 kit 3 (of 5).  If kit 3 is complete, the line"
echo '"'"End of kit 3 (of 5)"'" will echo at the end.'
echo ""
export PATH || (echo "You didn't use sh, you clunch." ; kill $$)
mkdir man 2>/dev/null
echo Extracting man/getty.m4
sed >man/getty.m4 <<'!STUFFY!FUNK!' -e 's/X//'
X.\" +----------
X.\" |	$Id: getty.m4,v 2.0 90/09/19 20:11:33 paul Rel $
X.\" |
X.\" |	GETTY/UUGETTY 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:	getty.m4,v $
X.\" |	Revision 2.0  90/09/19  20:11:33  paul
X.\" |	Initial 2.0 release
X.\" |	
X.\" |	
X.\" 
X.\" +----------
X.\" | M4 configuration
X.\"
Xinclude(config.m4).\"
X.\"
X.\" define(`_gtab_file_', _gtab_`_file')
X.\"
X.\" +----------
X.\" | Manpage source follows:
X.\"
X.TH GETTY _mcmd_section_ "DATE" "Release RELEASE"
X.SH NAME
Xgetty \- sets terminal mode, speed, and line discipline
X.SH SYNOPSIS
X.B /etc/getty
X.\" +----------
X.\" | M4_start (trs16)
Xifdef(`trs16',
X.B speed
X.I [defaults_file],
X.\" | else (trs16)
X[\-d
X.I defaults_file]
X[\-a] [\-h] [\-r
X.I delay]
X[\-t
X.I timeout]
X[\-w
X.I waitfor]
X.B line
X.I [speed [type [lined]]])
X.\" | M4_end (trs16)
X.\" +----------
X.br
X.B /etc/getty \-c
X.I _gtab_file_
X.SH DESCRIPTION
X.I Getty
Xis the second of the three programs
X.IR (init (_mcmd_section_),
X.IR getty (_mcmd_section_),
Xand
X.IR login (_mcmd_section_)),
Xused by the
X.\" +----------
X.\" | M4_start
Xifdef(`M_XENIX', XENIX, .\")
Xifdef(`XENIX', XENIX, .\")
Xifdef(`UNIX', UNIX, .\")
X.\" | M4_end
X.\" +----------
Xsystem to allow users to login.
X.I Getty
Xis invoked by
X.IR init (_mcmd_section_)
Xto:
X.br
X.TP 3
X1.
XOpen tty lines and set their modes.
X.TP
X2.
XPrint the login prompt, and get the user's name.
X.TP
X3.
XInitiate a login process for the user.
X.P
XThe actual procedure that
X.I getty
Xfollows is described below:  Initially,
X.I getty
Xparses its command line.  If no errors are found,
X.I getty
Xscans the defaults file (normally
X.BR _defaults_`/getty' )
Xto determine certain runtime values.  The values in the defaults file
X(whose compiled\-in name can be altered with the optional
X.\" +----------
X.\" | M4_start (trs16)
Xifdef(`trs16', .\", .B \-d)
X.\" | M4_end (trs16)
X.\" +----------
X.I defaults_file
Xargument) take precedence to those on the command line.
X.I Getty
Xthen opens the
X.I line
Xfor reading and writing, and disables stdio buffering.
X.\" +----------
X.\" | M4_start (logutmp)
Xifdef(`logutmp',
XThe
X.IR utmp (_file_section_)
Xand
X.IR wtmp (_file_section_)
X(if it exists) files are updated to indicate a LOGIN_PROCESS.,
X.\" | else (logutmp)
X.\")
X.\" | M4_end (logutmp)
X.\" +----------
XIf an initialization was specified, it is performed (see LINE
XINITIALIZATION).
X.PP
XNext,
X.I getty
Xtypes the
X.\" +----------
X.\" | M4_start (issue)
Xifdef(`_issue_',
Xissue `(or login banner,' usually from
X.BR _issue_ `)'
Xand,
X.\" | else (issue)
X.\")
X.\" | M4_end (issue)
X.\" +----------
Xlogin prompt.  Finally,
X.I getty
Xreads the user's login name and invokes
X.IR login (_mcmd_section_)
Xwith the user's name as an argument.  While reading the name,
X.I getty
Xattempts to adapt the system to the speed of the terminal being used,
Xand also sets certain terminal parameters (see
X.\" +----------
X.\" | M4_start (trs16)
Xifdef(`trs16',
X.IR tty `('_misc_section_`))',
X.\" | else (trs16)
X.IR termio `('_misc_section_`))')
X.\" | M4_end (trs16)
X.\" +----------
Xto conform with the user's login procedure.
X.PP
XThe tty device used by
X.I getty
Xis determined by
X.\" +----------
X.\" | M4_start (trs16)
Xifdef(`trs16',
Xa call to
X.IR ttyname (S)`,'
Xwhich sets the
X.I line
Xvalue.,
X.\" | else (trs16)
Xthe
X.I line
Xargument.)
X.\" | M4_end (trs16)
X.\" +----------
X.I Getty
Xuses the string
X.BI /dev/ line
Xas the name of the device to attach itself to.  Unless
X.I getty
Xis invoked with the
X.B \-h
Xflag (or
X.B HANGUP=NO
Xis specified in the defaults file), it will force a hangup on the line
Xby setting the speed to zero.  Giving
X.B \-r
X.I delay
Xon the command line (or using
X.B WAITCHAR=YES
Xand
X.BI DELAY= delay
Xin the defaults file) will cause
X.I getty
Xto wait for a single character from the line, and then to wait
X.I delay
Xseconds before continuing.  If no delay is desired, use
X.BR \-r0 .
XGiving
X.B \-w
X.I waitfor
Xon the command line (or using
X.BI WAITFOR= waitfor
Xin the defaults file) will cause
X.I getty
Xto wait for the specified string of characters from the line
Xbefore continuing.  Giving
X.B \-t
X.I timeout
Xon the command line (or using
X.BR TIMEOUT= timeout
Xin the defaults file) will cause
X.I getty
Xto exit if no user name is accepted within
X.I timeout
Xseconds after the login prompt is typed.
X.PP
XThe
X.I speed
Xargument is a label to a entry in the
X.B _gettytab_
Xfile (see
X.IR _gtab_ `('_file_section_)).
XThis entry defines to
X.I getty
Xthe initial speed (baud rate) and tty settings, the login prompt to be
Xused, the final speed and tty settings, and a pointer to another entry
Xto try should the user indicate that the speed is not correct.  This
Xis done by sending a
X.I <break>
Xcharacter (actually sequence).  Under certain conditions, a
Xcarriage\-return will perform the same function.  This is usually the
Xcase when getty is set to a higher speed than the modem or terminal.
X.I Getty
Xscans the _gtab_ file sequentially looking for a matching entry.
X.\" +----------
X.\" | M4_start (trs16)
Xifdef(`trs16',
XIf an entry for the given
X.I speed,
X.\" | else (trs16)
XIf no
X.I speed
Xwas given or the entry)
X.\" | M4_end (trs16)
X.\" +----------
Xcannot be found, the first entry in the
X.B _gettytab_
Xfile is used as a default.  In the event that the _gtab_ file cannot be
Xaccessed`,' there is a compiled\-in default entry that is used.
X.PP
X.\" +----------
X.\" | M4_start (trs16)
Xifdef(`trs16',
XThe terminal type is determined by examining the
X.I ttytype
Xfile`,' using the value associated with
X.I line.
X.I Getty
Xuses this value to determine how to clear the video display.,
X.\" | else (trs16)
XThe
X.I type
Xargument is a string which names the type of terminal attached to the
Xline.  The
X.I type
Xshould be a valid terminal name listed in the
X.\" +----------
X.\" | M4_start (termcap)
Xifdef(`termcap',
X.BR termcap (_misc_section_),
X.\" | else (termcap)
X.BR terminfo (_misc_section_))
X.\" | M4_end (termcap)
X.\" +----------
Xdatabase.
X.\" +----------
X.\" | M4_start (ttytype)
Xifdef(`ttytype',
XIf no
X.I type
Xis given on the command line`,' one is deduced from the
X.B ttytype
Xfile.,
X.\" | else (ttytype)
X.\")
X.\" | M4_end (ttytype)
X.\" +----------
X.I Getty
Xuses this value to determine how to clear the video display.
X.PP
XThe
X.I lined
Xargument is a string describing the line discipline to use on the
Xline.  The default is
X.BR LDISC0 .)
X.\" | M4_end (trs16)
X.\" +----------
X.PP
XAs mentioned,
X.I getty
Xtypes the login prompt and then reads the user's login name.  If a
Xnull character is received, it is assumed to be the result of the user
Xpressing the
X.I <break>
Xkey or the carriage\-return key to indicate the speed is wrong.  This
Xcauses
X.I getty
Xto locate the next
X.I speed
Xin the series (defined in _gettytab_).
X.PP
XThe user's name is terminated by a new\-line or carriage\-return
Xcharacter.  A carriage\-return results in the system being set to map
Xthose to new\-lines (see
X.IR ioctl (_system_section_)).
X.PP
XThe user's name is scanned to see if it contains only upper\-case
Xcharacters.  If so,
X.\" +----------
X.\" | M4_start (warncase)
Xifdef(`warncase',
Xa warning is issued to the user to retry his login`,' using lower\-case
Xif possible.  If the second attempt is still all upper\-case`,',
X.\" | else (warncase)
X.\")
X.\" | M4_end (warncase)
X.\" +----------
Xthe system is set to map any future upper\-case characters into
Xlower\-case.
X.PP
XA check option is provided for testing the _gtab_ file.  When
X.I getty
Xis invoked with the
X.BI \-c _gtab_
Xoption, it scans the named
X.I _gtab_
Xfile and prints out (to the standard output) the values it sees.  If
Xany parsing errors occur (due to errors in the syntax of the _gtab_
Xfile), they are reported.
X.SH "DEFAULTS FILE"
XDuring its startup,
X.I getty
Xlooks for the file
X.BI _defaults_`/getty' .line,
X(or, if it cannot find that file, then
X.BR _defaults_`/getty' ),
Xand if found, reads the contents for lines of the form
X
X.in +.5i
XNAME=\fIvalue\fR
X.in -.5i
X
XThis allows getty to have certain features configurable at runtime,
Xwithout recompiling.  The recognized NAME strings, and their
Xcorresponding values, follows:
X.TP 6
XSYSTEM=\fIname\fR
XSets the nodename value (displayed by
X.B @S
X\-\- see PROMPT SUBSTITUTIONS) to
X.IR name .
XThe default is the
X.I nodename
Xvalue returned by the
X.IR uname (_library_section_)
Xcall.  On XENIX systems, if the value of nodename is a zero\-length
Xstring, the file
X.B /etc/systemid
Xis examined to get the nodename.
XNote that some sites may have elected to compile the nodename value
Xinto
X.IR getty .
X.TP
XVERSION=\fIstring\fR
XSets the value that is displayed by the
X.B @V
Xparameter (see PROMPT SUBSTITUTIONS) to
X.I string.
XThere is no default value.  If
X.I string
Xbegins with a '/' character, it is assumed to be the full pathname of a
Xfile, and
X.B @V
Xis set to be the contents of that file.
X.TP
XLOGIN=\fIname\fR
XSets the name of the login program to
X.I name.
XThe default is
X.B _login_
X(see
X.IR login (_mcmd_section_)).
XIf used,
X.I name
Xmust be the full pathname of the program that
X.I getty
Xwill execute instead of
X.BR _login_ .
XNote that this program is called, as is
X.BR _login_ ,
Xthe with the user's name as its only argument.
X.TP
XINIT=\fIstring\fR
XIf defined,
X.I string
Xis an expect/send sequence that is used to initialize the line before
X.I getty
Xattempts to use it.  This string is in a form resembling that used in
Xthe
X.I _systems_
Xfile of
X.IR uucp (_cmd_section_).
XFor more details, see LINE INITIALIZATION.  By default, no
Xinitialization is done.
X.\" +----------
X.\" | M4_start (issue)
Xifdef(`_issue_',
X.TP
XISSUE=\fIstring\fR
XDuring startup`,'
X.I getty
Xdefaults to displaying`,' as an issue or login banner`,' the contents of
Xthe
X.B _issue_
Xfile.  If ISSUE is defined to a
X.I string`,'
Xthat string is typed instead.  If
X.I string
Xbegins with a '/' character`,' it is assumed to be the full pathname of
Xa file`,' and that file is used instead of
X.BR _issue_ .,
X.\" | else (issue)
X.\")
X.\" | M4_end (issue)
X.\" +----------
X.TP
XCLEAR=\fIvalue\fR
XIf
X.I value
Xis
X.BR NO ,
Xthen 
X.I getty
Xwill not attempt to clear the video screen before typing the
X.\" +----------
X.\" < M4_start (issue)
Xifdef(`_issue_',
Xissue or login prompts.,
X.\" | else (issue)
Xlogin prompt.)
X.\" | M4_end (issue)
X.\" +----------
XThe default is to clear the screen.
X.TP
XHANGUP=\fIvalue\fR
XIf
X.I value
Xis
X.BR NO ,
Xthen
X.I getty
Xwill NOT hangup the line during its startup.  This is analogus to
Xgiving the
X.B \-h
Xargument on the command line.
X.TP
XWAITCHAR=\fIvalue\fR
XIf
X.I value
Xis
X.BR YES ,
Xthen
X.I getty
Xwill wait for a single character from it's line before continuing.
XThis is useful for modem connections where the modem has CD forced
Xhigh at all times, to keep getty from endlessly chatting with the
Xmodem.
X.TP
XDELAY=\fIseconds\fR
XUsed in conjunction with
X.BR WAITCHAR ,
Xthis adds a time delay of
X.I seconds
Xafter the character is accepted before allowing
X.I getty
Xto continue.  Both
X.B WAITCHAR
Xand
X.B DELAY
Xhave the same effect as specifying
X.BI \-r delay
Xon the command line.
XIf
X.B WAITCHAR
Xis given without a
X.BR DELAY ,
Xthe result is equal to having said
X.B \-r0
Xon the command line.
XThe default is to not wait for a character.
X.TP
XTIMEOUT=\fInumber\fR
XAs with the
X.B \-t
X.I timeout
Xcommand line argument, tells
X.I getty
Xto exit if no user name is accepted before the
X.I number
Xof seconds elapse after the login prompt is typed.
XThe default is to wait indefinetly for the user name.
X.TP
XCONNECT=\fIstring\fR
XIf defined,
X.I string
Xshould be an expect/send sequence (like that for INIT) to direct
X.I getty
Xin establishing the connection.
X.I String
Xmay be defined as
X.BR DEFAULT ,
Xwhich will substitute the built\-in string:
X
X.in +.5i
X.nf
X_connect_
X.fi
X.in -.5i
X
XThe \\A escape marks the place where the digits showing the speed
Xwill be seen.  See CONNECTION AND AUTOBAUDING for more details.
XThe default is to not perform a connection chat sequence.
X.TP
XWAITFOR=\fIstring\fR
XThis parameter is similar to WAITCHAR, but defines a string of
Xcharacters to be waited for.
X.I Getty
Xwill wait until
X.I string
Xis received before issuing the login prompt.
XThis parameter is best used when combined with CONNECT, as in
Xthis example:
X
X.in +.5i
X.nf
XWAITFOR=RING
XCONNECT="" ATA\\r CONNECT\\s\\A
X.fi
X.in -.5i
X
XThis would cause
X.I getty
Xto wait for the string
X.BR RING ,
Xthen expect nothing, send
X.B ATA
Xfollowed by a carriage\-return, and then wait for a string such as
X.BR "CONNECT 2400" ,
Xin which case,
X.I getty
Xwould set itself to 2400 baud.
XThe default is not to wait for any string of characters.
X.TP
XALTLOCK=\fIline\fR
X.I Uugetty
Xuses this parameter to lock an alternate device, in addition to the
Xone it is attached to.  This is for those systems that have two
Xdifferent device names that refer to the same physical port; e.g.
X/dev/tty1A vs. /dev/tty1a, where one uses modem control and the
Xother doesn't.  See the section on UUGETTY for more details.
XThe default is to have no alternate lockfile.
X.P
XThe name of the defaults file can be changed by specifying
X.\" +----------
X.\" | M4_start (trs16)
Xifdef(`trs16',
X.\" Nothing,
X.\" | else (trs16)
X.B \-d)
X.\" | M4_end (trs16)
X.\" +----------
X.I defaults_file
Xon the command line.  If
X.I defaults_file
Xbegins with a slash, it is assumed to be a complete pathname of the
Xdefaults file to be used.  Otherwise, it is assumed to be a regular
Xfilename, causing
X.I getty
Xto use the pathname
X.BI _defaults_ `/defaults_file.'
X.SH "PROMPT SUBSTITUTIONS"
XWhen
X.I getty
Xis typing
X.\" +----------
X.\" | M4_start (issue)
Xifdef(`_issue_',
Xthe issue or login banner (ususally
X.BR _issue_ )`,'
Xor,
X.\" | else (issue)
X.\")
X.\" | M4_end (issue)
X.\" +----------
Xthe
X.I login\-prompt,
Xit recognizes several escape (quoted) characters.  When one of these
Xquoted characters is found, its value is substituted in the output
Xproduced by
X.I getty.
XRecognized escape characters are:
X.br
X.TP 6
X\\\\\\\\\\\\\\\\
XBackslash (\\).
X.TP
X\\\\\\\\b
XBackspace (^H).
X.TP
X\\\\\\\\c
XPlaced at the end of a string, this prevents a new\-line from
Xbeing typed after the string.
X.TP
X\\\\\\\\f
XFormfeed (^L).
X.TP
X\\\\\\\\n
XNew\-line (^J).
X.TP
X\\\\\\\\r
XCarriage\-return (^M).
X.TP
X\\\\\\\\s
XA single space (' ').
X.TP
X\\\\\\\\t
XHorizontal tab (^I).
X.TP
X\\\\\\\\\fInnn\fR
XOutputs the ASCII character whose decimal value is
X.IR nnn .
XIf
X.I nnn
Xbegins with 0, the value is taken to be in octal.  If it begins
Xwith 0x, the value is taken to be in hexidecimal.
X.P
XIn addition, a single backslash at the end of a line causes the
Ximmediately following new\-line to be ignored, allowing continuation
Xlines.
X.PP
XAlso, certain
X.BI "@" char
Xparameters are recognized.  Those parameters, and the value that is
Xsubstituted for them are:
X.TP 6
X@B
XThe current (evaluated at the time the
X.B @B
Xis seen) baud rate.
X.TP
X@D
XThe current date, in MM/DD/YY format.
X.TP
X@L
XThe
X.I line
Xto which
X.I getty
Xis attached.
X.TP
X@S
XThe system node name.
X.TP
X@T
XThe current time, in HH:MM:SS (24-hour) format.
X.TP
X@U
XThe number of currently signed\-on users.  This is a count of the
Xnumber of entries in the
X.I _utmp_
Xfile
X.\" +----------
X.\" | M4_start (logutmp)
Xifdef(`logutmp',
Xwhose ut_type field is USER_PROCESS.,
X.\" | else (logutmp)
Xthat have a non\-null ut_name field.)
X.\" | M4_end (logutmp)
X.\" +----------
X.TP
X@V
XThe value of
X.BR VERSION ,
Xas given in the defaults file.
X.P
XTo display a single '@' character, use either '\\@' or '@@'.
X.SH "LINE INITIALIZATION"
XOne of the greatest benefits (in the author's opinion, at least) is
Xthe ability of
X.I getty
Xto initialize its line before use.  This will most likely be done on
Xlines with modems, not terminals, although initializing terminals is
Xnot out of the question.
X.PP
XLine initialization is performed just after the
X.I line
Xis opened and prior to handling the WAITCHAR and/or WAITFOR options.
XInitialization is accomplished by placing an
X
X.in +.5i
XINIT=\fIstring\fR
X.in -.5i
X
Xline in the defaults file.
X.I String
Xis a series of one or more fields in the form
X
X.in +.5i
Xexpect [ send [ expect [ send ] ] ... ]
X.in -.5i
X
XThis format resembles the expect/send sequences used in the UUCP
X.I _systems_
Xfile, with the following exception:
XA carriage return is NOT appended automatically to sequences that
Xare 'sent.'  If you want a carriage\-return sent, you must explicitly
Xshow it, with '\\r'.
X.PP
X.I Getty
Xsupports subfields in the expect field of the form
X
X.in +.5i
Xexpect[\-send\-expect]...
X.in -.5i
X
Xas with UUCP.  All the escape characters (those beginning with a '\\'
Xcharacter) listed in the PROMPT SUBSTITUTIONS section are valid in
Xthe send and expect fields.
XIn addition, the following escape characters are recognized:
X.br
X.TP 6
X\\\\\\\\p
XInserts a 1\-second delay.
X.TP
X\\\\\\\\d
XInserts a 2\-second delay.
X.TP
X\\\\\\\\K
XSends a .25\-second Break.
X.TP
X\\\\\\\\T\fInnn\fR
XModifies the default timeout (usually 30 seconds) to
Xthe value indicated by
X.IR nnn .
XThe value
X.I nnn
Xmay be decimal, octal, or hexidecimal; see the usage of
X\fB\\\fInnn\fR in PROMPT SUBSTITUTIONS.
X.P
XNote that for these additional escape characters, no actual
Xcharacter is sent.
X.SH "CONNECTION AND AUTOBAUDING"
X.I Getty
Xwill perform a chat sequence establish a proper connection.
XThe best use of this feature is to look for the
X.B CONNECT
Xmessage sent by a modem and set the line speed to the number given
Xin that message (e.g. CONNECT 2400).  
X.PP
XThe format for the connect chat script is exactly the same as that
Xfor the INIT script (see LINE INITIALIZATION), with the following
Xaddition:
X.br
X.TP 6
X\\\\\\\\A
XMarks the spot where the baud rate will be seen.  This mark will
Xmatch any and all digits 0\-9 at that location in the script, and
Xset it's speed to that value, if possible.
X.P
XAutobauding, therefore, is enabled by placing the
X.B \\\\A
Xmark in the chat script.  For example, the definition:
X
X.in+.5i
XCONNECT=CONNECT\\s\\A
X.in-.5i
X
Xwould match the string
X.B "CONNECT 1200"
Xand cause
X.I getty
Xto set it's baud rate to 1200, using the following steps:
X.TP 3
X1.
XHaving matched the value 1200,
X.I getty
Xwill attempt to find an entry with the label
X.B 1200
Xin the
X.B _gtab_
Xfile.  If a matching _gtab_ entry is found, those values are
Xused.  If there is no match, then
X.TP
X2.
XThe _gtab_ values currently in use are modified to use the
Xmatched speed (e.g. 1200).  However, if the matched speed
Xis invalid, then
X.TP
X3.
X.I Getty
Xlogs a warning message and resumes normal operation.  This
Xallows the practice of toggling through linked entries in the
X_gtab_ file to behave as expected.
X.P
X.SH UUGETTY
X.I Uugetty
Xhas identical behavior to
X.I getty,
Xexcept that
X.I uugetty
Xis designed to create and use the lock files maintained by the UUCP
Xfamily
X.IR (uucp (_cmd_section_),
X.IR cu (_cmd_section_)
Xand others).  This prevents two or more processes from having conficting
Xuse of a tty line.
X.PP
XWhen
X.I uugetty
Xstarts up, if it sees a lock file on the line it intends to use,
Xit will use the pid in the lock file to see if there is an active
Xprocess holding the lock.  If not,
X.I uugetty
Xwill remove the lock file and continue.  If a valid process is found,
X.I uugetty
Xwill sleep until that process releases the lock and then it will exit,
Xforcing
X.IR init (_mcmd_section_)
Xto spawn a new
X.I uugetty.
XOnce no conflicting process is found,
X.I uugetty
Xgrabs the
X.I line
Xby creating the lock file itself before issuing the login prompt.
XThis prevents other processes from using the line.
X.PP
X.I Uugetty
Xwill normally only lock the name of the line it is running on.  On
Xsystems where there are two device names referring to the same port
X(as is the case where one device uses modem control while the other
Xdoesn't), place a line of the form
X
X.in +.5i
XALTLOCK=\fIline\fR
X.in -.5i
X
Xline in the defaults file.  For instance, if
X.I uugetty
Xis on
X.I /dev/tty1a,
Xand you want to have it lock
X.I /dev/tty1A
Xalso, use the line
X.B ALTLOCK=tty1A
Xin the defaults file.
X.SH FILES
X.TP 16
X_console_
XThe device to which errors are reported.
X.\" +----------
X.\" | M4_start (trymail)
Xifdef(`trymail',
XIf the device is for some reason unavailable (cannot be written to)`,'
Xa mail message containing the error is sent to the user
X.BR _notify_ .,
X.\" | else (trymail)
X.\")
X.\" | M4_end (trymail)
X.\" +----------
X.TP
X_defaults_`/getty[\fI.line\fR]'
XContains the runtime configuration.  Note that
X.I uugetty
Xuses _defaults_`/uugetty[\fI.line\fR].'
X.TP
X_gettytab_
XContains speed and tty settings to be used by
X.I getty.
X.\" +----------
X.\" | M4_start (issue)
Xifdef(`_issue_',
X.TP
X_issue_
XThe default issue (or login banner).
X.\" | else (issue)
X.\")
X.\" | M4_end (issue)
X.\" +----------
X.TP
X_login_
XThe default login program called after the user's name is entered.
X.\" +----------
X.\" | M4_start (ttytype)
Xifdef(`ttytype',
X.TP
X_ttytype_
XContains the terminal types for each line in the system.,
X.\" | else (ttytype)
X.\")
X.\" | M4_end (ttytype)
X.\" +----------
X.P
X.SH "SEE ALSO"
Xinit(_mcmd_section_),
Xlogin(_mcmd_section_),
Xuucp(_cmd_section_),
Xioctl(_system_section_),
Xuname(_library_section_),
X.\" +----------
X.\" | M4_start (issue)
Xifdef(`issue',
Xissue`('_file_section_`)',
X.\" | else (issue)
X.\")
X.\" | M4_end (issue)
X.\" +----------
X_gtab_`('_file_section_),
Xutmp(_file_section_),
X.\" +----------
X.\" | M4_start (trs16)
Xifdef(`trs16',
Xtty`('_misc_section_`)',
X.\" | else (trs16)
Xtermio`('_misc_section_`)')
X.\" | M4_end (trs16)
X.\" +----------
X.SH AUTHOR
X.nf
XPaul Sutcliffe, Jr.  <paul@devon.lns.pa.us>
XUUCP: ...!rutgers!devon!paul
X.br
X
XAutobauding routines adapted from code submitted by
XMark Keating <...!utzoo!censor!markk>
!STUFFY!FUNK!
echo Extracting funcs.c
sed >funcs.c <<'!STUFFY!FUNK!' -e 's/X//'
X/*
X**	$Id: funcs.c,v 2.0 90/09/19 19:53:19 paul Rel $
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 2.0  90/09/19  19:53:19  paul
X**	Initial 2.0 release
X**	
X*/
X
X
X#include "getty.h"
X#include "table.h"
X#include <ctype.h>
X#ifdef	I_TIME
X#include <time.h>
X#endif	/* I_TIME */
X#ifdef	I_SYSTIME
X#include <sys/time.h>
X#endif	/* I_SYSTIME */
X#ifdef	DOUNAME
X#include <sys/utsname.h>
X#endif	/* DOUNAME */
X#include <setjmp.h>
X#include <signal.h>
X
X#if defined(RCSID) && !defined(lint)
Xstatic char *RcsId =
X"@(#)$Id: funcs.c,v 2.0 90/09/19 19:53:19 paul Rel $";
X#endif
X
X#ifndef	MAXBUF
X#define	MAXBUF	512	/* buffer size */
X#endif	/* MAXBUF */
X
X#ifndef	EXPFAIL
X#define	EXPFAIL	30	/* default num seconds to wait for expected input */
X#endif	/* EXPFAIL */
X
X#define	EXPECT	0	/* states for chat() */
X#define	SEND	1
X
X#define	AUTOBD	0376	/* marker for AutoBaud digits */
X
Xchar	*unquote();
Xint	expect(), send();
Xboolean	expmatch();
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)
Xregister char *s;
Xregister 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':	/* speed (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)
Xregister TERMIO *termio;
Xint state;
X{
X	register 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
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
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**	chat() - handle expect/send sequence to Device
X**
X**	Returns FAIL if an error occurs.
X*/
X
Xint
Xchat(s)
Xchar *s;
X{
X	register int state = EXPECT;
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, "chat(%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 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		case SEND:
X			if (send(word) == FAIL)
X				return(FAIL);
X			state = EXPECT;
X			break;
X		}
X		continue;
X	}
X	debug2(D_INIT, "chat() 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	valid_oct[] = "01234567";
Xchar	valid_dec[] = "0123456789";
Xchar	valid_hex[] = "0123456789aAbBcCdDeEfF";
X
Xchar *
Xunquote(s, c)
Xchar *s, *c;
X{
X	int value, base;
X	char n, *valid;
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			value = 0;
X			if (n == '0') {
X				if (*s == 'x') {
X					valid = valid_hex;
X					base = 16;
X					s++;
X				} else {
X					valid = valid_oct;
X					base = 8;
X				}
X			} else {
X				valid = valid_dec;
X				base = 10;
X				s--;
X			}
X			while (strpbrk(s, valid) == s) {
X				value = (value * base) + (int) (n - '0');
X				s++;
X			}
X			*c = (char) (value & 0377);
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)
Xregister char *s;
X{
X	register int retval = SUCCESS;
X	char ch;
X
X	debug2(D_INIT, "SEND: (");
X
X	if (strequal(s, "\"\"")) {	/* ("") used as a place holder */
X		debug2(D_INIT, "[nothing])\n");
X		return(retval);
X	}
X
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++;
X				continue;
X			case 'd':		/* '\d' == delay */
X				debug2(D_INIT, "[delay]");
X				(void) sleep(2);
X				s++;
X				continue;
X			case 'K':		/* '\K' == BREAK */
X				debug2(D_INIT, "[break]");
X				(void) ioctl(STDOUT, TCSBRK, 0);
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)
Xregister char *s;
X{
X	register int i;
X	register int expfail = EXPFAIL;
X	register retval = FAIL;
X	char ch, *p, word[MAXLINE+1], buf[MAXBUF];
X	sig_t (*oldalarm)();
X
X	if (strequal(s, "\"\"")) {	/* ("") used as a place holder */
X		debug2(D_INIT, "EXPECT: ([nothing])\n");
X		return(SUCCESS);
X	}
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	/* look for escape chars in expected word
X	 */
X	for (p = word; ch = (*s++ & 0177);) {
X		if (ch == '\\') {
X			if (*s == 'A') {	/* spot for AutoBaud digits */
X				*p++ = AUTOBD;
X				s++;
X				continue;
X			} else if (*s == 'T') {	/* change expfail timeout */
X				if (isdigit(*++s)) {
X					s = unquote(s, &ch);
X					/* allow 3 - 255 second timeout */
X					if ((expfail = (int) ch) < 3)
X						expfail = 3;
X				}
X				continue;
X			} else
X				s = unquote(s, &ch);
X		}
X		*p++ = (ch) ? ch : '\177';
X	}
X	*p = '\0';
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	oldalarm = signal(SIGALRM, expalarm);
X	(void) alarm((unsigned) expfail);
X
X	debug3(D_INIT, "EXPECT: <%d> (", expfail);
X	debug1(D_INIT, word);
X	debug2(D_INIT, "), GOT:\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 (expmatch(&buf[i], 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**	expmatch() - compares expected string with the one gotten
X*/
X
X#ifdef	TELEBIT
Xchar	valid[] = "0123456789FAST";
X#else	/* TELEBIT */
Xchar	valid[] = "0123456789";
X#endif	/* TELEBIT */
X
Xboolean
Xexpmatch(got, exp)
Xregister char *got;
Xregister char *exp;
X{
X	register int ptr = 0;
X
X	while (*exp) {
X		if (*exp == AUTOBD) {	/* substitute real digits gotten */
X			while (*got && strpbrk(got, valid) == got) {
X				AutoBaud = TRUE;
X				if (ptr < (sizeof(AutoRate) - 2))
X					AutoRate[ptr++] = *got;
X				got++;
X			}
X			if (*got == '\0')
X				return(FALSE);	/* didn't get it all yet */
X			AutoRate[ptr] = '\0';
X			exp++;
X			continue;
X		}
X		if (*got++ != *exp++)
X			return(FALSE);		/* no match */
X	}
X	return(TRUE);
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;
Xregister char *name;
Xint size;
X{
X	register int count;
X	register int lower = 0;
X	register 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)) {
X			(void) putc(ch, stdout);
X			(void) fflush(stdout);
X		}
X		if (ch == Erase) {
X			if (count) {
X				if (!(lflag & ECHOE)) {
X					(void) fputs(" \b", stdout);
X					(void) fflush(stdout);
X				}
X				--p;
X				--count;
X			}
X		} else if (ch == Kill) {
X			if (!(lflag & ECHOK)) {
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)
Xregister char *msg;
X{
X	register 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#ifdef	VARARGS
X
X#include <varargs.h>
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#else	/* VARARGS */
X
X/*VARARGS2*/
Xvoid
Xdebug(lvl, fmt, arg1, arg2, arg3, arg4)
Xint lvl;
Xchar *fmt;
X{
X	if (Debug & lvl) {
X		(void) fprintf(Dfp, fmt, arg1, arg2, arg3, arg4);
X		(void) fflush(Dfp);
X	}
X}
X
X#endif	/* VARARGS */
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 == AUTOBD) {
X				(void) fputs("[speed]", Dfp);
X				(void) fflush(Dfp);
X				continue;
X			} else 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 defaults.c
sed >defaults.c <<'!STUFFY!FUNK!' -e 's/X//'
X/*
X**	$Id: defaults.c,v 2.0 90/09/19 19:42:09 paul Rel $
X**
X**	Routines to access runtime defaults file.
X**	This is to allow program features to be configured
X**	without the need to recompile.
X**
X**	XENIX has defopen(S) and defread(S), but I think this is better,
X**	since it reads the file only once, storing the values in core.
X**	It is certainly more portable.
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:	defaults.c,v $
X**	Revision 2.0  90/09/19  19:42:09  paul
X**	Initial 2.0 release
X**	
X*/
X
X
X#include "getty.h"
X#include "defaults.h"
X#include <sys/stat.h>
X#include <errno.h>
X
X#if defined(RCSID) && !defined(lint)
Xstatic char *RcsId =
X"@(#)$Id: defaults.c,v 2.0 90/09/19 19:42:09 paul Rel $";
X#endif
X
X#ifndef DEFAULTS
X#define DEFAULTS  "/etc/default/%s"	/* location of defaults file */
X#endif	/* DEFAULTS */
X
X#ifndef	MAXLINE
X#define	MAXLINE	256	/* maximum # chars in a line */
X#endif	/* MAXLINE */
X
X#ifndef MAXDEF
X#define MAXDEF  100	/* maximum # of lines in defaults file */
X#endif	/* MAXDEF */
X
X
X/*
X**	defbuild() - create in-core list of defaults
X**
X**	Returns (DEF**)NULL if no defaults file found or an error occurs.
X*/
X
XDEF **
Xdefbuild(filename)
Xchar *filename;
X{
X	register int i;
X	register DEF *dp;
X	register DEF *next;
X	FILE *fp;
X	char *fname, defname[MAXLINE+1], buf[MAXLINE+1];
X	static DEF *deflist[MAXDEF+1];		/* in-core list */
X	struct stat st;
X	extern int errno;
X
X	debug3(D_DEF, "defbuild(%s) called\n",
X			((filename == (char *) NULL) ? "NULL" : filename));
X
X	/* look to see if there's a DEFAULTS/MyName.Device file
X	 */
X	(void) sprintf(buf, "%s", DEFAULTS);
X	(void) strcat(buf, ".%s");
X	(void) sprintf(defname, buf, MyName, Device);
X	debug3(D_DEF, "looking for %s\n", defname);
X	if ((stat(defname, &st) == FAIL) && errno == ENOENT) {	/* Nope */
X		debug2(D_DEF, "stat failed, no file\n");
X		(void) sprintf(defname, DEFAULTS, MyName);
X	}
X
X	fname = (filename != (char *) NULL) ? filename : defname;
X
X	/* if fname doesn't begin with a '/', assume it's a
X	 * filename to be made "DEFAULTS/fname"
X	 */
X	if (*fname != '/') {
X		(void) sprintf(defname, DEFAULTS, fname);
X		fname = defname;
X	}
X
X	debug3(D_DEF, "fname = (%s)\n", fname);
X
X	if ((fp = defopen(fname)) == (FILE *) NULL) {
X		debug2(D_DEF, "defopen() failed\n");
X		return((DEF **) NULL);		/* couldn't open file */
X	}
X
X	for (i=0; i < MAXDEF; i++) {
X		if ((dp = defread(fp)) == (DEF *) NULL)
X			break;
X		if ((next = (DEF *) malloc((unsigned) sizeof(DEF))) ==
X		    (DEF *) NULL) {
X			logerr("malloc() failed: defaults list truncated");
X			break;
X		}
X		next->name = dp->name;
X		next->value = dp->value;
X		deflist[i] = next;
X		debug5(D_DEF, "deflist[%d]: name=(%s), value=(%s)\n",
X				i, deflist[i]->name, deflist[i]->value);
X	}
X	deflist[i] = (DEF *) NULL;	/* terminate list */
X	(void) defclose(fp);
X	debug2(D_DEF, "defbuild() successful\n");
X	return(deflist);
X}
X
X
X/*
X**	defvalue() - locate the value in "deflist" that matches "name"
X**
X**	Returns (char*)NULL if no match is made.
X*/
X
Xchar *
Xdefvalue(deflist, name)
Xregister DEF **deflist;
Xregister char *name;
X{
X	debug3(D_DEF, "defvalue(%s) called\n", name);
X
X	if (deflist != (DEF **) NULL)
X		for (; *deflist != (DEF *) NULL; *deflist++)
X			if (strequal(name, (*deflist)->name)) {
X				debug3(D_DEF, "defvalue returns (%s)\n",
X						(*deflist)->value);
X				return((*deflist)->value);  /* normal exit */
X			}
X
X	debug2(D_DEF, "defvalue returns NULL\n");
X	return((char *) NULL);
X}
X
X
X/*
X**	defopen() - open the defaults file
X**
X**	Returns (FILE*)NULL if file not found or an error occurs.
X*/
X
XFILE *
Xdefopen(filename)
Xregister char *filename;
X{
X	if (filename != (char *) NULL)
X		return(fopen(filename, "r"));
X
X	return((FILE *) NULL);
X}
X
X
X/*
X**	defread() - read a line from the defaults file
X**
X**	Returns (DEF*)NULL if an error occurs.
X*/
X
XDEF *
Xdefread(fp)
Xregister FILE *fp;
X{
X	register char *p;
X	STDCHAR buf[MAXLINE+1];	/* buffer large enough for 1 line */
X	static DEF def;
X
X	do {
X		if (fgets(buf, sizeof(buf), fp) == (char *) NULL)
X			return((DEF *) NULL);	/* no more lines */
X
X	} while (buf[0] == '#');		/* ignore comment lines */
X
X	buf[strlen(buf)-1] = '\0';		/* rm trailing \n */
X
X	/* lines should be in the form "NAME=value"
X	 */
X	if ((p = index(buf, '=')) == (char *) NULL) {
X		(void) sprintf(MsgBuf, "bad defaults line: %s", buf);
X		logerr(MsgBuf);
X		return((DEF *) NULL);
X	}
X	*p++ = '\0';		/* split into two fields, name and value */
X	def.name = strdup(buf);
X	def.value = strdup(p);
X
X	return(&def);
X}
X
X
X/*
X**	defclose() - closes the defaults file
X**
X**	Returns EOF if an error occurs.
X*/
X
Xint
Xdefclose(fp)
Xregister FILE *fp;
X{
X	return(fclose(fp));
X}
X
X
X/* end of defaults.c */
!STUFFY!FUNK!
echo Extracting extern.h
sed >extern.h <<'!STUFFY!FUNK!' -e 's/X//'
X/*
X**	$Id: extern.h,v 2.0 90/09/19 19:48:33 paul Rel $
X**
X**	Defines all external values.
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:	extern.h,v $
X**	Revision 2.0  90/09/19  19:48:33  paul
X**	Initial 2.0 release
X**	
X*/
X
X
X#ifdef	MAIN			/* define as "extern", except for MAIN,	*/
X#define EXTERN			/* which is defined only in main.c	*/
X#else
X#define EXTERN	extern
X#endif	/* MAIN */
X
X
X/*	Global variables
X */
X
X#ifdef	MAIN
XEXTERN	STDCHAR	MsgBuf[80];	/* message buffer */
X#else
XEXTERN	STDCHAR	MsgBuf[];
X#endif	/* MAIN */
X
XEXTERN	boolean	AutoBaud;	/* autobauding requested? */
XEXTERN	char	AutoRate[16];	/* AutoBaud digits buffer */
XEXTERN	boolean	Check;		/* check a gettytab file? */
XEXTERN	char	*CheckFile;	/* gettytab-like file to check */
XEXTERN	char	*Device;	/* controlling line (minus "/dev/") */
XEXTERN	char	*GtabId;	/* current gettytab id */
XEXTERN	boolean	NoHangUp;	/* don't hangup line before setting speed */
XEXTERN	char	*LineD;		/* line discipline */
XEXTERN	char	*MyName;	/* this program name */
XEXTERN	int	Nusers;		/* number of users currently logged in */
XEXTERN	char	*Speed;		/* current baud rate (string literal) */
XEXTERN	char	*SysName;	/* nodename of system */
XEXTERN	int	TimeOut;	/* timeout value from command line */
XEXTERN	char	*Version;	/* value of VERSION */
X
X#ifdef	WARNCASE
XEXTERN	boolean	WarnCase;	/* controls display of bad case message */
X#endif	/* WARNCASE */
X
X#ifdef	DEBUG
XEXTERN	int	Debug;		/* debug value from command line */
XEXTERN	FILE	*Dfp;		/* debug output file pointer */
X#endif	/* DEBUG */
X
X
X/*	System routines
X */
X
Xextern	int	fputc();
Xextern	char	*malloc(), *ttyname();
Xextern	unsigned alarm(), sleep();
Xextern	time_t	time();
X
X#ifndef	STRDUP			/* Is There In Truth No Strdup() ? */
Xextern	char	*strdup();
X#endif	/* STRDUP */
X
X#ifndef	GETUTENT		/* How about getutent() ? */
Xextern	struct utmp	*getutent();
Xextern	void		setutent(), endutent();
X#endif	/* GETUTENT */
X
X#ifndef	PUTENV			/* putenv() ? */
Xextern	int	putenv();
X#endif	/* PUTENV */
X
X
X/* end of extern.h */
!STUFFY!FUNK!
echo Extracting patchlevel.h
sed >patchlevel.h <<'!STUFFY!FUNK!' -e 's/X//'
X#define PATCHLEVEL 0
!STUFFY!FUNK!
echo ""
echo "End of kit 3 (of 5)"
cat /dev/null >kit3isdone
run=''
config=''
for iskit in 1 2 3 4 5; 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.