Enhanced SYSV Getty, v1.1, part 1 of 4
Paul Sutcliffe Jr.
paul at devon.lns.pa.us
Wed Jan 17 18:44:09 AEST 1990
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 at 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 at 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 at 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 at 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 at devon.lns.pa.us | If life's a bitch, then
UUCP: ...!rutgers!devon!paul | we must be her puppies.
More information about the Alt.sources
mailing list