ast@cs.vu.nl (Andy Tanenbaum) (10/23/89)
: This is a shar archive. Extract with sh, not csh. : This archive ends with exit, so do not worry about trailing junk. : --------------------------- cut here -------------------------- PATH=/bin:/usr/bin:/usr/ucb echo Extracting 'Makefile' sed 's/^X//' > 'Makefile' << '+ END-OF-FILE ''Makefile' X# X# Makefile for UMAIL Mail Router (MINIX) X# X# Author: Fred van Kempen, MicroWalt Corporation X# X# Define WMAILER if the local mailer is W-MAIL. X# This mailer understands the "-i filename" option, X# and this works much faster than stdIO redirecting. X# XCFLAGS = -DMINIX -DWMAILER X XBIN = /usr/bin X XOBJS = ummain.s umtime.s umheader.s umconvert.s \ X umroute.s umsend.s umscanner.s XSRCS = ummain.c umtime.c umheader.c umconvert.c \ X umroute.c umsend.c umscanner.c XOTHERS = README umail.doc umail.cf umail Makefile uucp.h umail.h X Xumail: Makefile $(OBJS) X cc -i -o umail $(OBJS) X @chmem =16000 umail >/dev/null X Xinstall: umail X @rm -f $(BIN)/umail $(BIN)/rmail X @echo 'Copying files...' X @cp umail $(BIN)/umail X @echo 'Setting up links...' X @ln $(BIN)/umail $(BIN)/rmail X @echo 'Setting up permissions:' X chown root.root $(BIN)/umail X chmod 6555 $(BIN)/umail X Xshar: X @shar -v -o umail.shar $(OTHERS) $(SRCS) X Xtar: X @tar c umail.tar $(OTHERS) $(SRCS) X Xummain.s: umail.h ummain.c X cc $(CFLAGS) -S ummain.c X Xumtime.s: umail.h umtime.c X cc $(CFLAGS) -S umtime.c X Xumheader.s: umail.h umheader.c X cc $(CFLAGS) -S umheader.c X Xumconvert.s: umail.h umconvert.c X cc $(CFLAGS) -S umconvert.c X Xumroute.s: umail.h umroute.c X cc $(CFLAGS) -S umroute.c X Xumsend.s: umail.h uucp.h umsend.c X cc $(CFLAGS) -S umsend.c X Xumscanner.s: umscanner.c X cc $(CFLAGS) -S umscanner.c X + END-OF-FILE Makefile chmod 'u=rw,g=r,o=r' 'Makefile' set `wc -c 'Makefile'` count=$1 case $count in 1429) :;; *) echo 'Bad character count in ''Makefile' >&2 echo 'Count should be 1429' >&2 esac echo Extracting 'README' sed 's/^X//' > 'README' << '+ END-OF-FILE ''README' X U-MAIL Remote Mail Transport Agent (MINIX) X X MicroWalt U-MAIL V2.5 X ===================== X X XThis archive contains the sources of the UMAIL Network Mailer Xfor the MINIX operating system. It runs on most UNIXes as well. X XThe file 'umail' is the executable program, and 'umail.doc' is Xthe (preliminary) documentation of the program. XSee 'umail.cf' for configuration information. X XOf course, this program needs the presence of either UUCP or XRSMTP to run properly... X XEnjoy! X+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ X| MINIX User Group (Holland) UUCP: hp4nl!kyber!minixug!waltje | X| c/o Fred van Kempen, or: minixug!waltje@kyber.UUCP | X| Hoefbladhof 27 | X| 2215 DV VOORHOUT | X| The Netherlands "A good programmer knows his Sources" | X+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-+ X + END-OF-FILE README chmod 'u=rw,g=r,o=r' 'README' set `wc -c 'README'` count=$1 case $count in 852) :;; *) echo 'Bad character count in ''README' >&2 echo 'Count should be 852' >&2 esac echo Extracting 'umail.cf' sed 's/^X//' > 'umail.cf' << '+ END-OF-FILE ''umail.cf' X# X# MicroWalt Corporation -- UMAIL Configuration File X# X# System: MINIX User Group Holland, minixug.nluug.nl X# OS: MicroWalt Advanced MINIX 1.4B X# X# Author: F. van Kempen, MicroWalt Corporation X# X X# These are some configuration variables for the X# mailer. They define the working environment. XBEGIN X Organization := "MINIX User Group Holland, Netherlands" X System := "minixug" X Domain := "nluug.nl" X TimeZone := "MET +0100" X OldMailer := TRUE # We want V6 "From <user> <date>" lines X Escape := TRUE # We can send all undeliverable mail X # to another host. XEND X X# The next table defines all the names under which X# our system could possibly be adressed. It is used X# to map domain adresses back to local adresses. XNAMES BEGIN X minixug.nluug.nl # Our official name X minixug.nl # other possible names X minixug.uucp # old-style UUCP name X minixug # local name XEND X X# This table defines the systems that we know of, and X# what networking software to use to get to it. X# 'TRUE' means, that the specified host understands X# route-adressing (and can thus do things itself). X# FALSE means that we have to give that host a ready- X# to-run UUCP adress in bang-style. XHOSTS BEGIN X minixug := TRUE , $$ , "@" # Just local mail ! X kyber := FALSE , $$ , "@" # My UUCP host site X archvax := FALSE , "rsmtp" , "<%s archvax!" # SMTP via Ethernet XEND X X# This is the actual routing table. X# It defines where mail for the various domains X# should be sent to. Note, that domain "." is X# the "default domain", which is used if no X# matching domain can be found. X# Format: X# X# DOMAIN := HOST , ROUTE X# XDOMAINS BEGIN X aha.nl := kyber , htsa # Algemene Hogeschool Amsterdam X edu := kyber , hp4nl # Educational Institutes Network X nl := kyber , hp4nl # UUCP-Netherlands X nluug := kyber , hp4nl # Dutch UNIX User Group Network X nluug.nl := kyber , hp4nl X uu.net := kyber , hp4nl # USA UUCP Network X uucp := kyber , hp4nl # Old-style UUCP Network X uunet := kyber , hp4nl # USA UUCP Network X . := kyber , hp4nl # Default host XEND + END-OF-FILE umail.cf chmod 'u=rw,g=r,o=r' 'umail.cf' set `wc -c 'umail.cf'` count=$1 case $count in 2282) :;; *) echo 'Bad character count in ''umail.cf' >&2 echo 'Count should be 2282' >&2 esac echo Extracting 'umail.doc' sed 's/^X//' > 'umail.doc' << '+ END-OF-FILE ''umail.doc' XUMAIL(1) MINIX User's Manual UMAIL(1) X X XNAME X umail - electronic mail delivery agent. X XSYNOPSIS X umail [-c <config> [-d] [-i <inputfile> [-n] <user> X XDESCRIPTION X Umail is a semi-intelligent mail transporting program X which knows something about Internet adresses and mail X adress routing to other systems. X It is used as a back-end to the Mailx program; which X is used to read or create messages. Umail only takes X care of the transportation of the message to other X MINIX (or UNIX) systems through the available network X software like UUCP and RSMTP. X X In restricted mode ('rmail') it functions as a message X delivery program for incoming network mail. X Networking programs like uucico(1) and rsmtp(8) execute X the rmail(1) command to deliver a received message to X the adressed user, or to forward the message to another X system. X X The program has some command-line options, which are X used to alter the "standard" behaviour of the program. X The options are: X X -c <config> Debug; use 'config' as the configura- X tion file instead of the standard X one. This is sometimes useful when X debugging the package. X X -d Debug; generate some useful output X while working. X X -i <infile> Input; use file 'infile' as the mes- X sage file instead of standard input. X X -n Now; call the networking delivery X software immediately after queueing X the message. Not recommended! X X The 'user' string identifies the network-adress of the X user who is to receive the message. It can be any legal X UUCP or Internet mail-adress. X XDELIVERY X The Mailx program can serve as both a user agent and a X local delivery agent. If Mailx sees that the adressed X user is on the local system, it will do the delivery X itself. No header (except for From, To and Subject) is X added, and delivery errors will cause the message to X be returned to the user as a file 'dead.letter'. X X Otherwise, if the specified adress is not local, the X message is sent to the Remote Mailer on the system. X X X X -- 1 -- X X X XUMAIL(1) MINIX User's Manual UMAIL(1) X X X X Usually, this is the rmail(1) command; programs like X sendmail(8) or smail(1) (or links to them) are used as X well. On MINIX systems, rmail(1) is a separate program. X X This small remote mailer knows something about UUCP, and X how to interpret old-style UUCP (host!user) adresses. X This works fine for most users, but it is sometimes nice X to have a somewhat more intelligent mailer. X Therefore, one could install Umail as a link to rmail(1) X so that the mail(1) or Mailx(1) programs will never see X the difference. X X These (more or less) intelligent mailers add a standard X (RFC-822) header to the message, and then send it to the X networking software. On MINIX systems, messages will be X transported using either the UUCP networking software X (serial lines) or the Amoeba RSMTP (Ethernet) software. X X Because it would be too difficult to program all sorts X of networks, system names and the lot in the mailer X software, a file exists which contains the Configuration X parameters of Umail: the Config File. This is a plain X ASCII text file, containing a mixture of comments, data X and instructions. X XAUTHOR X Steve R. Sampson (first draft of rmail program) X Fred van Kempen, waltje@kyber.UUCP X XFILES X /usr/spool/mail/user maildrop file X /usr/spool/mail/user.lock lock for maildrop files X /tmp/um* temporary file X /usr/spool/uucp/* UUCP networking files X /usr/spool/rsmtp/* SMTP networking files X /usr/lib/uucp/umail.cf configuration file X XSEE ALSO X wmail.doc X XBUGS X The routing algorithm could be improved a lot. X Also, the Config scanner should be more robust. X X X X X X X X X X X X X X X X -- 2 -- X X + END-OF-FILE umail.doc chmod 'u=rw,g=r,o=r' 'umail.doc' set `wc -c 'umail.doc'` count=$1 case $count in 4082) :;; *) echo 'Bad character count in ''umail.doc' >&2 echo 'Count should be 4082' >&2 esac echo Extracting 'umail.h' sed 's/^X//' > 'umail.h' << '+ END-OF-FILE ''umail.h' X/* X * UMAIL - MINIX Remote Domain-adressing Mail Router X * X * This version of RMAIL handles message-headers in a much X * more "standard" way. It can handle bang-adresses, plus X * the new-style Internet adressing (user@host.domain). X * It is called by programs as "Mail" and "Uuxqt". X * X * D E F I N I T I O N S X * X * Author: F. van Kempen, Jul-Oct '89 (waltje@minixug.nluug.nl) X */ X X#ifdef MINIX X# define VERSION "2.5/MINIX" /* 10/19/89 */ X# define CONFIG "/usr/lib/uucp/umail.cf" X#endif X X#ifdef UNIX X# define VERSION "2.5/UNIX" /* 10/19/89 */ X# define CONFIG "/usr/lib/uucp/umail.cf" X#endif X X#ifndef TRUE X# define FALSE 0 X# define TRUE 1 X#endif X Xtypedef struct __name { X struct __name *next; X char *name; X} NAME; X Xtypedef struct __var { X struct __var *next; X char *name; X char *value; X} VAR; X Xtypedef struct __host { X struct __host *next; X char *name; X int smart; X char *command; X char *opts; X} HOST; X Xtypedef struct __routemap { X struct __routemap *next; X char *domain; X char *host; X char *route; X} ROUTE; X Xtypedef struct { X char user[256]; /* user name of adress */ X char host[256]; /* host name of adress */ X char domain[256]; /* domain name of adress */ X} BOX; X X#define NILNAME ((NAME *)NULL) X#define NILVAR ((VAR *)NULL) X#define NILHOST ((HOST *)NULL) X#define NILROUTE ((ROUTE *)NULL) X#define NILBOX ((BOX *)NULL) X X X/* globals in ummain.c */ Xextern char *Version; /* UMAIL version ID */ Xextern int immediate, debug; /* commandline option flags */ Xextern int restrict; /* restricted (UUCP) use only */ Xextern int aremote; /* is adressee REMOTE or LOCAL ? */ Xextern char dfile[], infile[]; /* names of message temp-files */ Xextern char errmsg[]; /* global error message */ Xextern char mailsender[]; /* who sent the message? */ Xextern char mailaddr[]; /* final routed adress to use. */ Xextern char mailhost[]; /* which host to send to */ Xextern char mailcmd[]; /* command to use to send the mail */ Xextern char mailopts[]; /* which options the mailer uses */ Xextern NAME *namelist; /* list of my network names */ Xextern VAR *varlist; /* list of configuration variables */ Xextern HOST *hostlist; /* list of reacheable host names */ Xextern ROUTE *routemap; /* list of domain routes */ X X/* configuration settings */ Xextern char *myname; /* my UUCP site name */ Xextern char *mydomain; /* my UUCP domain name */ Xextern char *myorg; /* Name of my organization */ Xextern int oldmailer; /* mailer uses old From-lines? */ Xextern int escape; /* routing ESCAPE enable */ X X/* external routines */ Xextern char *xtime(/* time_t *salt */); Xextern BOX *convert(/* char *adr */); Xextern int route(/* BOX *box */); Xextern int header(/* FILE *infp, FILE *outfp */); Xextern char *strupr(/* char *s */); Xextern char *strlwr(/* char *s */); Xextern void add_name(/* char *name */); Xextern void add_host(/* char *name, int smart, char *cmd, char *opts */); Xextern void add_route(/* char *domain, char *host, char *route */); Xextern void add_var(/* char *name, char *val */); Xextern char *lookup(/* char *what */); Xextern int boolean(/* char *ascii */); Xextern HOST *gethost(/* char *host */); Xextern ROUTE *getdomain(/* char *domain */); Xextern char *mfgets(/* char *s, int n, FILE *iop */); Xextern char *whoami(/* void */); Xextern char *full_id(/* char *user */); Xextern char *realname(/* char *who */); Xextern char *maketime(/* long *salt */); Xextern void fcopy(/* FILE *inf, FILE *outf */); Xextern int scanner(/* char *fname */); Xextern int KnowHost(/* char *name */); Xextern int islocal(/* char *name */); Xextern char *genname(/* int prefix, int grade, char *sysname */); Xextern int send_local(/* char *user, char *data */); Xextern int send_remote(/* char *rmtname, char *rmtuser, char *data */); Xextern void errmail(/* char *str, int mgronly */); Xextern int sendit(/* char *who, char *host, char *cmd, char *opts, char *data */); + END-OF-FILE umail.h chmod 'u=rw,g=r,o=r' 'umail.h' set `wc -c 'umail.h'` count=$1 case $count in 3965) :;; *) echo 'Bad character count in ''umail.h' >&2 echo 'Count should be 3965' >&2 esac echo Extracting 'umconvert.c' sed 's/^X//' > 'umconvert.c' << '+ END-OF-FILE ''umconvert.c' X/* X * UMAIL - MINIX Remote Domain-adressing Mail Router X * X * This version of RMAIL handles message-headers in a much X * more "standard" way. It can handle bang-adresses, plus X * the new-style Internet adressing (user@host.domain). X * It is called by programs as "Mail" and "Uuxqt". X * X * A D R E S S C O N V E R S I O N M O D U L E X * X * Convert adresses into manageable chunks. X * X * We understand the following notations: X * X * 1. user@host.domain --> waltje@minixug.UUCP X * X * 2. host!user --> minixug!waltje X * X * 3. user --> waltje X * X * Return TRUE (1) if OK, or FALSE (0) if an error was encountered. X * X * Author: F. van Kempen, Jul-Oct '89 (waltje@minixug.nluug.nl) X */ X#include <stdio.h> X#include <alloc.h> X#include <string.h> X#include "umail.h" X X X/* X * Convert adress 'adr' into more manageable chunks. X * Stuff the output into 'mailaddr' (the final user) X * and 'mailcmd' (the mailer command to use). X * Return NILBOX if an error ocurred. X */ XBOX *convert(adr) Xchar *adr; X{ X static BOX box; X char temp[1024]; X register char *bp, *sp, *cp; X register ROUTE *rp; X register HOST *hp; X X strcpy(mailaddr, adr); X box.domain[0] = box.host[0] = '\0'; X box.user[0] = temp[0] = '\0'; X X /* X * Rule 1: Check for user@host.domain X */ X sp = strrchr(mailaddr, '@'); X if (sp != NULL) { X *sp++ = '\0'; X strcpy(box.user, mailaddr); X strcpy(temp, sp); X strlwr(temp); /* convert domain to lower case: RFC822 */ X X /* X * Rule 1A: Now check for "." in the domain part. X * This indicates that a host name was given. X */ X sp = strchr(temp, '.'); /* syntax host.domain ?? */ X if (sp == NULL) { /* no, onlky 'host' part */ X hp = gethost(temp); /* is this a local host? */ X if (hp != NILHOST) { /* yes! */ X strcpy(box.host, temp); X box.domain[0] = '\0'; X return(&box); X } else { /* no, must be a domain.... */ X strcpy(box.domain, temp); X box.host[0] = '\0'; X return(&box); X } X } else { /* domain and host given! */ X *sp++ = '\0'; X strcpy(box.host, temp); X strcpy(box.domain, sp); X return(&box); X } X } X X /* X * Rule 2: Check for host!user X */ X sp = strchr(mailaddr, '!'); X if (sp != NULL) { X *sp++ = '\0'; X strcpy(box.host, mailaddr); X strcpy(box.user, sp); X return(&box); X } X X /* X * Rule 3: Must be local user. X */ X strcpy(box.user, mailaddr); X box.host[0] = '\0'; X box.domain[0] = '\0'; X return(&box); X} X + END-OF-FILE umconvert.c chmod 'u=rw,g=r,o=r' 'umconvert.c' set `wc -c 'umconvert.c'` count=$1 case $count in 2388) :;; *) echo 'Bad character count in ''umconvert.c' >&2 echo 'Count should be 2388' >&2 esac echo Extracting 'umroute.c' sed 's/^X//' > 'umroute.c' << '+ END-OF-FILE ''umroute.c' X/* X * UMAIL - MINIX Remote Domain-adressing Mail Router X * X * This version of RMAIL handles message-headers in a much X * more "standard" way. It can handle bang-adresses, plus X * the new-style Internet adressing (user@host.domain). X * It is called by programs as "Mail" and "Uuxqt". X * X * M A I L R O U T E R M O D U L E X * X * Some rules exist for routing to other machines. X * X * 1. Check for local system names. X * X * 2. If we have a domain, check it for validity. X * X * 3. If we have a hostname, check it for validity. X * X * 4. Local user: check existence. X * X * Examples are: X * X * user@host.domain@newdomain - X * -> ast@cs.vu@nluug.nl X * host!user@domain X * --> cs.vu!ast@nluug.nl X * X * If we cannot find a matching domain or system name, X * then we get stuck. Fortunately, we can define the X * ESCAPE parameter, which allows us to send all mail X * for unknown hosts or domains to a "default" domain. X * This is the domain called "." in the tables. X * If the ESCAPE parameter is FALSE, we cannot deliver X * the message, so return it to the sender! X * X * Return TRUE (1) if OK, or FALSE (0) if an error was encountered. X * X * Author: F. van Kempen, Jul-Oct '89 (waltje@minixug.nluug.nl) X */ X#include <stdio.h> X#include <alloc.h> X#include <pwd.h> X#include <string.h> X#include "umail.h" X X X/* X * Route the adress in BOX into a ready-to-run X * UUCP adress. X */ Xint route(b) XBOX *b; X{ X char temp[1024]; X register char *bp, *sp; X register ROUTE *rp; X register HOST *hp; X struct passwd *pw; X X /* X * Rule 1: check for local system names. X * convert to LOCAL if possible. X */ X if (b->domain[0] == '\0') strcpy(temp, b->host); X else sprintf(temp, "%s.%s", b->host, b->domain); X if (islocal(temp)) { X b->host[0] = '\0'; X b->domain[0] = '\0'; X } X X /* X * Rule 2: Do we have a domain? X */ X if (b->domain[0] != '\0') { /* domain given? */ X if (b->host[0]) sprintf(temp, "%s.%s", b->host, b->domain); X else strcpy(temp, b->domain); X strcpy(b->host, temp); X bp = temp; X rp = NILROUTE; X while (TRUE) { /* iterate on domain fragments */ X sp = bp; X rp = getdomain(bp); X if (rp != NILROUTE) { /* a matching domain! */ X strcpy(b->domain, bp); X break; X } X bp = strchr(sp, '.'); X if (bp == NULL) break; X else bp++; X } X X /* X * We now have checked the DOMAIN table for a matching domain. X * If we failed to find a match, there is a problem. X * If the mailer was defined with the ESCAPE parameter, we can X * try to route it to the default (".") domain. X * Otherwise, we cannot deliver the message... X */ X X /* check if we found a matching domain */ X if (rp == NILROUTE) { /* we did not. try to re-route it */ X if (escape == FALSE) { X sprintf(errmsg, "%s ... domain unknown", b->domain); X return(FALSE); X } X rp = getdomain("."); /* get default domain! */ X if (rp == NILROUTE) { X sprintf(errmsg, "%s ... domain unknown", b->domain); X strcat(errmsg, "\n\nESCAPE domain not found."); X return(FALSE); X } X } X X /* X * At this point we have all the information we X * need to build an UUCP-adress. X * We have a HOST as well. X * Check if we can indeed reach that host. X */ X hp = gethost(rp->host); X if (hp == NILHOST) { X sprintf(errmsg, "%s ... host unreacheble", rp->host); X return(FALSE); X } X X /* is the host smart enough to get "@"-adresses?? */ X if (hp->smart == TRUE) { /* yes, it is! */ X if (*(rp->route) == '@') { X if (b->host[0] == '\0') sprintf(mailaddr,"%s@%s", X b->user, b->domain); X else sprintf(mailaddr, "%s@%s", b->user, b->host); X } else { X if (b->host[0] == '\0') sprintf(mailaddr,"%s!%s@%s", X rp->route, b->user, b->domain); X else sprintf(mailaddr, "%s!%s@%s.%s", X rp->route, b->user, b->host, b->domain); X } X } else { X if (b->host[0] == '\0') sprintf(mailaddr,"%s!%s", X rp->route, b->user); X else sprintf(mailaddr, "%s!%s!%s", X rp->route, b->host, b->user); X } X strcpy(b->host, rp->host); X X /* X * We now have a HOST and an ADRESS. X */ X strcpy(mailcmd, hp->command); X strcpy(mailopts, hp->opts); X strcpy(mailhost, b->host); X aremote = TRUE; X return(TRUE); X } X X /* X * Rule 3: Do we have a host name ? X */ X if (b->host[0] != '\0') { /* host name given? */ X b->domain[0] = '\0'; /* signal 'no routing' */ X hp = gethost(b->host); X if (hp == NILHOST) { X if (escape == FALSE) { X sprintf(errmsg, "%s ... host unknown", b->host); X return(FALSE); X } X rp = getdomain("."); /* get default domain! */ X if (rp == NILROUTE) { X sprintf(errmsg, "%s ... host unknown", b->host); X strcat(errmsg, "\n\nESCAPE domain not found."); X return(FALSE); X } X X /* X * We now have a HOST as well. X * Check if we can indeed reach that host. X */ X strcpy(b->domain, rp->host); X hp = gethost(rp->host); X if (hp == NILHOST) { X sprintf(errmsg, "%s ... host unreacheble", rp->host); X return(FALSE); X } X } X X /* X * USER contains the user-part X * HOST contains the (old) host-part X * DOMAIN now contains the new hostname X */ X X /* is the host smart enough to get "@"-adresses?? */ X if (b->domain[0] != '\0') { /* are we routing? */ X if (hp->smart == TRUE) sprintf(mailaddr, "%s@%s", X b->user, b->host); X else sprintf(mailaddr, "%s!%s", b->host, b->user); X strcpy(b->host, b->domain); X } else { /* no, ordinary case */ X strcpy(mailaddr, b->user); X } X X strcpy(mailhost, b->host); X strcpy(mailcmd, hp->command); X strcpy(mailopts, hp->opts); X aremote = TRUE; X return(TRUE); X } X X /* X * Rule 4: Check for local user. X */ X if ((pw = getpwnam(b->user)) == (struct passwd *)NULL) { X sprintf(errmsg, "%s ... user unknown", b->user); X return(FALSE); X } X hp = gethost(myname); X strcpy(mailaddr, b->user); X mailhost[0] = '\0'; X strcpy(mailcmd, hp->command); X strcpy(mailopts, hp->opts); X aremote = FALSE; X return(TRUE); X} + END-OF-FILE umroute.c chmod 'u=rw,g=r,o=r' 'umroute.c' set `wc -c 'umroute.c'` count=$1 case $count in 5809) :;; *) echo 'Bad character count in ''umroute.c' >&2 echo 'Count should be 5809' >&2 esac echo Extracting 'umscanner.c' sed 's/^X//' > 'umscanner.c' << '+ END-OF-FILE ''umscanner.c' X/* X * UMAIL - MINIX Remote Domain-adressing Mail Router X * X * This version of RMAIL handles message-headers in a much X * more "standard" way. It can handle bang-adresses, plus X * the new-style Internet adressing (user@host.domain). X * It is called by programs as "Mail" and "Uuxqt". X * X * C O N F I G U R A T I O N F I L E S C A N N E R X * X * Author: F. van Kempen, Jul-Oct '89 (waltje@minixug.nluug.nl) X */ X#include <stdio.h> X X Xtypedef struct { X char *name; X int opcode; X} WORD; X X X/* operator opcodes */ X#define O_ASSIGN 1 /* ":=" */ X#define O_COMMA 2 /* "," */ X#define O_BEGIN 3 /* "BEGIN" */ X#define O_END 4 /* "END" */ X#define O_DATA 5 /* "DATA" */ X#define O_NAMES 6 /* "NAMES" */ X#define O_DOMAIN 7 /* "DOMAINS" */ X#define O_HOST 8 /* "HOST" */ X X X/* main scanner states */ X#define S_LOOK 1 /* looking for a keyword */ X#define S_BEGIN 2 /* got O_BEGIN */ X#define S_END 3 /* got O_END */ X X/* block classes */ X#define SC_DATA 1 /* DATA (variable) class */ X#define SC_NAMES 2 /* NAMES (my domains) class */ X#define SC_DOMAIN 3 /* DOMAIN TABLE class */ X#define SC_HOST 4 /* HOSTS class */ X X#define SS_LOOK 1 /* looking for keyword or variable name */ X#define SS_IDENT 2 /* looking for identifier */ X#define SS_ASSIGN 3 /* got varname, looking for O_COMMA */ X#define SS_COMMA 4 /* got varname, looking for O_ASSIGN */ X#define SS_VALUE 5 /* got O_ASSIGN, looking for value */ X#define SS_DOMAIN1 6 /* got domain, looking for hostname */ X#define SS_DOMAIN2 7 /* got boolean, looking for domain descr. */ X#define SS_HOST1 8 /* got host, looking for boolean */ X#define SS_HOST2 9 /* got host, looking for host mailer name */ X#define SS_HOST3 10 /* got host, looking for host mailer opts */ X X Xstatic FILE *infp; /* input file pointer */ Xstatic char scbuf[1024]; /* input line buffer */ Xstatic char *scptr = NULL; /* input line pointer */ Xstatic char *errptr = NULL; /* error pointer */ Xstatic char sctemp1[128]; /* current identifier */ Xstatic char sctemp2[128]; /* current identifier */ Xstatic int scbool; /* temp. boolean value */ Xstatic int lineno = 1; /* current line number */ Xstatic int sclass = SC_DATA; /* temporary state */ Xstatic int state = S_LOOK; /* state of scanner */ Xstatic int sstate = SS_LOOK; /* secondairy state */ Xstatic int tstate = SS_LOOK; /* triple state */ Xstatic WORD table[] = { /* language table */ X { ":=" , O_ASSIGN }, X { "," , O_COMMA }, X { "BEGIN" , O_BEGIN }, X { "END" , O_END }, X { "DATA" , O_DATA }, X { "NAMES" , O_NAMES }, X { "DOMAINS" , O_DOMAIN }, X { "HOSTS" , O_HOST }, X}; X X Xextern int debug; X X X/* X * Return next character of input file; X * also do some bookkeeping for error-recovery. X */ Xstatic int nextch(void) X{ X register int ch; X X ch = fgetc(infp); X if (ch == '\n') { X lineno++; X *scptr = '\0'; X scptr = scbuf; X } else *scptr++ = ch; X return(ch); X} X X X/* X * Handle a syntax error. X * Also, perform some error recovery. X */ Xstatic void syntax(s) Xchar *s; X{ X register char *bp, *ep; X register int ch; X X ep = errptr; X do { X ch = nextch(); /* read up to end of line */ X } while (ch!='\n' && ch!=EOF); /* and start over on next line */ X X sstate = SS_LOOK; /* reset state machine #2 */ X X fprintf(stderr, "%05.5d %s\n ", lineno, scbuf); X bp = scbuf; X ep--; X while (bp < ep) { X fprintf(stderr, " "); X bp++; X } X fprintf(stderr, "^ %s\n", s); X} X X X/* X * Check the text of a keyword. X */ Xstatic int crunch(text) Xchar *text; X{ X register WORD *wp; X X wp = &table[0]; X while (wp->opcode != 0) { X if (!strcmp(wp->name, text)) return(wp->opcode); X wp++; X } X return(0); X} X X X/* X * Decode a word, and perform some action if necessary. X * This routine holds all the syntax grammar. X * It is far from perfect, but it works... X */ Xstatic void do_word(name) Xchar *name; X{ X int op; /* decoded keyword code */ X char *s = "expected END"; X X op = crunch(name); X if (sstate == SS_LOOK) switch(op) { X case O_DATA: X if (state==S_LOOK || state==S_END) sclass = SC_DATA; X else syntax(s); X break; X case O_NAMES: X if (state==S_LOOK || state==S_END) sclass = SC_NAMES; X else syntax(s); X break; X case O_DOMAIN: X if (state==S_LOOK || state==S_END) sclass = SC_DOMAIN; X else syntax(s); X break; X case O_HOST: X if (state==S_LOOK || state==S_END) sclass = SC_HOST; X else syntax(s); X break; X case O_BEGIN: X switch(sclass) { X case SC_DATA: X case SC_DOMAIN: X case SC_HOST: X case SC_NAMES: X sstate = SS_LOOK; X state = S_BEGIN; X break; X default: X syntax("expected class prefix"); X break; X } X break; X case O_END: X if (state == S_BEGIN) { X state = S_LOOK; X sclass = 0; X } else syntax("expected BEGIN"); X break; X default: X sstate = SS_IDENT; X break; X } X switch(sstate) { X case SS_LOOK: /* propagated from the above switch() */ X break; X case SS_IDENT: /* looking for identifier */ X switch(sclass) { X case SC_DATA: X case SC_HOST: X case SC_DOMAIN: X strcpy(sctemp1, name); X sstate = SS_ASSIGN; X break; X case SC_NAMES: X add_name(name); X sstate = SS_LOOK; X break; X default: X syntax("expected BEGIN or CLASS"); X } X break; X case SS_ASSIGN: /* looking for O_ASSIGN */ X op = crunch(name); X if (op == O_ASSIGN) switch(sclass) { /* found O_ASSIGN */ X case SC_DATA: X sstate = SS_VALUE; X break; X case SC_DOMAIN: X sstate = SS_DOMAIN1; X break; X case SC_HOST: X sstate = SS_HOST1; X break; X default: X break; X } else syntax("expected ASSIGN"); X break; X case SS_COMMA: /* field separator */ X op = crunch(name); X if (op == O_COMMA) switch(sclass) { X case SC_DOMAIN: X sstate = SS_DOMAIN2; X break; X case SC_HOST: X switch(tstate) { X case SS_HOST1: X sstate = SS_HOST2; X break; X case SS_HOST2: X sstate = SS_HOST3; X break; X default: X syntax("no comma here"); X break; X } X break; X default: X syntax("no comma here"); X break; X } else syntax("expected COMMA"); X break; X case SS_VALUE: /* looking for value */ X add_var(sctemp1, name); X sstate = SS_LOOK; X break; X case SS_DOMAIN1: /* looking for route hostname */ X strcpy(sctemp2, name); X sstate = SS_COMMA; X break; X case SS_DOMAIN2: /* looking for host route */ X add_route(sctemp1, sctemp2, name); X sstate = SS_LOOK; X break; X case SS_HOST1: /* looking for host 'smart' boolean */ X scbool = boolean(name); X sstate = SS_COMMA; X tstate = SS_HOST1; X break; X case SS_HOST2: /* looking for host mailer name */ X strcpy(sctemp2, name); /* mailer name */ X sstate = SS_COMMA; /* look for mailer opts */ X tstate = SS_HOST2; X break; X case SS_HOST3: /* looking for host mailer opts */ X add_host(sctemp1, scbool, sctemp2, name); X sstate = SS_LOOK; X break; X default: /* this can't be for real! */ X syntax("Huh? You must be joking!"); X break; X } X} X X X/* X * This is the Configuration File Scanner. X * It has *some* level of intelligence, but is must X * be improved a lot. X */ Xint scanner(fname) Xchar *fname; X{ X char wordbuf[512]; X register char *bp = wordbuf; X register int ch; X register int quote = 0; X register int stopped = 0; X X infp = fopen(fname, "r"); X if (infp == (FILE *)NULL) return(-1); X X scptr = scbuf; X ch = nextch(); X do { X switch(ch) { X case '#': /* comment, skip rest of line */ X do { X ch = nextch(); X } while (ch != '\n'); X break; X case ' ': /* SPACE */ X case '\t': X if (quote == 0) { X errptr = scptr; X do { /* skip rest of leading space */ X ch = nextch(); X } while (ch==' ' || ch=='\t'); X *bp = '\0'; X if (bp != wordbuf) { X do_word(wordbuf); X bp = wordbuf; X } X } else { X *bp++ = ch; X ch = nextch(); X } X break; X case EOF: /* EOF, done! */ X stopped = 1; X case '\n': /* end-of-word marker, handle word */ X *bp = '\0'; X if (wordbuf[0]) { X do_word(wordbuf); X bp = wordbuf; X } X if (ch != EOF) ch = nextch(); X break; X case '"': /* quote, set/reset quoting flag */ X quote = 1 - quote; X ch = nextch(); X break; X default: /* anything else: text */ X *bp++ = ch; X ch = nextch(); X break; X } X } while (!stopped); X X fclose(infp); X X return(0); X} + END-OF-FILE umscanner.c chmod 'u=rw,g=r,o=r' 'umscanner.c' set `wc -c 'umscanner.c'` count=$1 case $count in 9752) :;; *) echo 'Bad character count in ''umscanner.c' >&2 echo 'Count should be 9752' >&2 esac echo Extracting 'umsend.c' sed 's/^X//' > 'umsend.c' << '+ END-OF-FILE ''umsend.c' X/* X * UMAIL - MINIX Remote Domain-adressing Mail Router X * X * This version of RMAIL handles message-headers in a much X * more "standard" way. It can handle bang-adresses, plus X * the new-style Internet adressing (user@host.domain). X * It is called by programs as "Mail" and "Uuxqt". X * X * M A I L T R A N S P O R T M O D U L E X * X * Author: F. van Kempen, Jul-Oct '89 (waltje@minixug.nluug.nl) X */ X#include <stdio.h> X#include <sys/types.h> X#include <dirent.h> X#include <errno.h> X#include <string.h> X#include <time.h> X#include <uucp.h> X#include "umail.h" X X X/* X * Check the host machine name. X * X * returns FALSE if not found, else TRUE X */ Xint KnowHost(name) Xchar *name; X{ X register HOST *hp; X X hp = gethost(name); X return((hp==NILHOST) ? FALSE : TRUE); X} X X X/* X * Check if this is one of our local names. X * Return 1 if TRUE, or 0 if FALSE. X */ Xint islocal(name) Xchar *name; X{ X register NAME *np; X X np = namelist; X while (np != NILNAME) { X if (!strcmp(np->name, name)) return(TRUE); X np = np->next; X } X return(FALSE); X} X X X/* X * Creates a unique UUCP file name, and returns a pointer X * to it. The filename is a combination of prefix, grade, system name X * and a sequential number taken from SPOOLSEQ. X */ Xchar *genname(prefix, grade, sysname) Xint prefix, grade; Xchar *sysname; X{ X static char _gen_buf[128]; X int i = 0; X char seqline[10]; X char *seqname = SPOOLSEQ; /* to save some string space */ X char *lseqname = LSPOOLSEQ; /* to save some string space */ X FILE *spoolseq; X X if (access(seqname, 0) != 0) close(creat(seqname, 0600)); X X while(link(seqname, lseqname) != 0) { X sleep(5); X if (++i > 5) return(NULL); X } X X spoolseq = fopen(seqname, "r"); X fgets(seqline, sizeof(seqline), spoolseq); X fclose(spoolseq); X unlink(lseqname); X X i = (atoi(seqline) + 1); X X if ((spoolseq = fopen(seqname, "w")) == (FILE *)NULL) return(NULL); X fprintf(spoolseq, "%d\n", i); X fclose(spoolseq); X X if (grade == 0) sprintf(_gen_buf, "%c.%.7s%04.4x", prefix, sysname, i); X else sprintf(_gen_buf, "%c.%.7s%c%04.4x", prefix, sysname, grade, i); X X return(_gen_buf); X} X X X/* X * Deliver this message to a local user. X * We do this by calling "LMAIL" (which is actually X * a link to "Mail"; the Local Mail Agent. X */ Xint send_local(user, data) Xchar *user; Xchar *data; X{ X struct passwd *pw; X char tmpbuf[128]; X X /* See if destination user name exists on this machine */ X pw = (struct passwd *) getpwnam(user); X if (pw == (struct passwd *)NULL) { X sprintf(tmpbuf, "%s ... unknown user at %s", user, myname); X errmail(tmpbuf, FALSE); X } X X#ifdef WMAILER /* faster than redirecting! */ X sprintf(tmpbuf, "exec %s -i%s %s", LMAIL, data, user); X#else X sprintf(tmpbuf, "exec %s <%s %s", LMAIL, data, user); X#endif WMAILER X X return(system(tmpbuf)); X} X X X/* X * Deliver this message to a remote user. X * We do this by creating the spoolfiles needed by UUCICO. X * Then we call that program daemon to do the real work. X */ Xint send_remote(rmtname, rmtuser, data) Xchar *rmtname; Xchar *rmtuser; Xchar *data; X{ X char tmpbuf[128]; X char Bfile[128], Cfile[128], Dfile[128], Xfile[128]; X FILE *fcfile, *fbfile, *fdfile, *fp; X X if (KnowHost(rmtname) == FALSE) { X sprintf(tmpbuf, "%s ... unknown host machine", rmtname); X errmail(tmpbuf, FALSE); X } X X /* make the spool files for uucico */ X strcpy(Bfile, genname('B', 0, rmtname)); X strcpy(Cfile, genname('C', 'N', rmtname)); X strcpy(Dfile, genname('D', 0, myname)); X strcpy(Xfile, genname('X', 'N', rmtname)); X X /* Copy the temp-file to the UUCP data file (D.???) */ X if ((fdfile = fopen(Dfile, "w")) == (FILE *)NULL) { X perror("rmail 4"); X exit(1); X } else { X fp = fopen(data, "r"); /* open temp-file */ X fcopy(fp, fdfile); X fclose(fdfile); X fclose(fp); X } X X if ((fbfile = fopen(Bfile, "w")) == (FILE *)NULL) { X perror("rmail 4"); X exit(1); X } else { X fprintf(fbfile, "U %s %s\nF %s\nI %s\nC rmail %s\n", X UUCPUSER, myname, Dfile, Dfile, rmtuser); X fclose(fbfile); X } X X if ((fcfile = fopen(Cfile, "w")) == (FILE *)NULL) { X perror("rmail 5"); X exit(1); X } else { X fprintf(fcfile,"S %s %s %s - %s 0666\nS %s %s %s - %s 0666\n", X Dfile, Dfile, UUCPUSER, Dfile, Bfile, Xfile, UUCPUSER, Bfile); X fclose(fcfile); X } X X /* RMAIL is setUID root... UUCP cannot read these files! */ X chown(Bfile, UUCPUID, UUCPGID); X chown(Cfile, UUCPUID, UUCPGID); X chown(Dfile, UUCPUID, UUCPGID); X chown(Xfile, UUCPUID, UUCPGID); X X if (immediate == TRUE) { /* call uucico now! */ X strcpy(tmpbuf, UUCICO); X sprintf(tmpbuf, "exec %s -s%s -x1 >/dev/null &", UUCICO, rmtname); X system(tmpbuf); X } X X return(FALSE); X} X X X/* X * Perform the mail-transport. X * We do this by calling the appropriate mailer. X * If the name of the mailer is "$$" then we can use X * this program to deliver. This saves a lot of memory. X */ Xint sendit(who, host, cmd, opts, data) Xchar *who; /* who is the adressee? */ Xchar *host; /* on which machine? */ Xchar *cmd; /* what command should we use? */ Xchar *opts; /* which options? */ Xchar *data; /* name of data (message) file */ X{ X char cmdline[512]; X char tmpbuff[512]; X X chdir(SPOOLDIR); /* Change to UUCP directory */ X X if (!strcmp(cmd, "$$")) { /* run our own mail routines */ X if (*host == '\0') send_local(who, data); X else send_remote(host, who, data); X } else { X sprintf(tmpbuff, "exec %s %s ", cmd, opts); X sprintf(cmdline, tmpbuff, data); /* create commandline */ X strcat(cmdline, who); /* add user adress */ X system(cmdline); /* execute command (mailer) */ X } X} X X X/* X * Send mail to system manager upon errors X * X * Mail is contained in a file referenced X * by Global variable 'dfile' X */ Xvoid errmail(str, mgronly) Xchar *str; Xint mgronly; X{ X FILE *fp, *tp; X long now; X char fname[32]; X char tmp[128]; X X strcpy(fname, "/tmp/umeXXXXXX"); X mktemp(fname); X X tp = fopen(fname, "w"); X fp = fopen(dfile, "r"); X X time(&now); X X /* create header of the report-message */ X fprintf(tp, "From %s %s\n", ERRUSER, xtime(&now)); X if (mailsender != NULL) fprintf(tp, "To: %s\n", mailsender); X fprintf(tp, "Subject: Returned mail\n\n"); X X /* create an error transcript */ X fprintf(tp, " ---- Transcript of session follows ----\n\n"); X fprintf(tp, "%s\n", str); X fprintf(tp, "\n ---- Unsent message follows ----\n"); X X /* copy the message */ X while (mfgets(tmp, sizeof(tmp), fp) != (char *)NULL) X fprintf(tp, "> %s\n", tmp); X X fclose(tp); /* flush and close message file */ X fclose(fp); /* flush and close orig. file */ X X /* Return mail to system manager (and sender if mgronly == FALSE) */ X if (mgronly == FALSE) sendit(mailsender, "", RMAIL, " <%s", fname); X X /* send mail to UUCP administrator */ X sendit(ERRUSER, "", "$$", "", fname); X X unlink(fname); /* remove data files */ X unlink(dfile); X X exit(1); /* and exit! */ X} + END-OF-FILE umsend.c chmod 'u=rw,g=r,o=r' 'umsend.c' set `wc -c 'umsend.c'` count=$1 case $count in 6827) :;; *) echo 'Bad character count in ''umsend.c' >&2 echo 'Count should be 6827' >&2 esac echo Extracting 'umtime.c' sed 's/^X//' > 'umtime.c' << '+ END-OF-FILE ''umtime.c' X/* X * XTIME - Create ASCII string of the given time. X * This file contains a modified version X * of the ctime(3) function from the MINIX X * C library. The format of the string is: X * X * Sat, Oct 14 89 20:26:00\0 X * X */ X#include <time.h> X X Xstatic int days_per_month[] = { X 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 X}; Xstatic char *months[] = { X "Jan", "Feb", "Mar", "Apr", "May", "Jun", X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" X}; Xstatic char *days[] = { X "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" X}; X X#define MIN 60L /* # seconds in a minute */ X#define HOUR (60 * MIN) /* # seconds in an hour */ X#define DAY (24 * HOUR) /* # seconds in a day */ X#define YEAR (365 * DAY) /* # seconds in a year */ X X Xchar *xtime(pt) Xlong *pt; X{ X static struct tm tm; X static char xtmbuf[30]; X X register long t = *pt; X long year; X X tm.tm_year = 0; X tm.tm_mon = 0; X tm.tm_mday = 1; X tm.tm_hour = 0; X tm.tm_min = 0; X tm.tm_sec = 0; X X /* t is elapsed time in seconds since Jan 1, 1970. */ X tm.tm_wday = (int) (t/DAY + 4L) % 7; /* Jan 1, 1970 is 4th wday */ X while (t >= (year=((tm.tm_year%4)==2) ? YEAR+DAY : YEAR)) { X tm.tm_year += 1; X t -= year; X } X tm.tm_year += 1970; X X /* t is now the offset into the current year, in seconds. */ X tm.tm_yday = (t/DAY); /* day # of the year, Jan 1 = 0 */ X X days_per_month[1] = 28; X if ((tm.tm_year % 4) == 0) /* check for leap year */ X days_per_month[1]++; X X /* Compute month. */ X while (t >= (days_per_month[tm.tm_mon] * DAY)) X t -= days_per_month[tm.tm_mon++] * DAY; X X /* Month established, now compute day of the month */ X while (t >= DAY) { X t -= DAY; X tm.tm_mday++; X } X X /* Day established, now do hour. */ X while (t >= HOUR) { X t -= HOUR; X tm.tm_hour++; X } X X /* Hour established, now do minute. */ X while (t >= MIN) { X t -= MIN; X tm.tm_min++; X } X X /* Residual time is # seconds. */ X tm.tm_sec = (int) t; X X /* Generate output in ASCII in _buf_. */ X sprintf(xtmbuf, "%s, %2.2d %s %2.2d %02d:%02d:%02d", X days[tm.tm_wday], tm.tm_mday, months[tm.tm_mon], X tm.tm_year - 1900, tm.tm_hour, tm.tm_min, tm.tm_sec); X return(xtmbuf); X} + END-OF-FILE umtime.c chmod 'u=rw,g=r,o=r' 'umtime.c' set `wc -c 'umtime.c'` count=$1 case $count in 2113) :;; *) echo 'Bad character count in ''umtime.c' >&2 echo 'Count should be 2113' >&2 esac echo Extracting 'uucp.h' sed 's/^X//' > 'uucp.h' << '+ END-OF-FILE ''uucp.h' X/* X * UUCP.H- DCP: A UUCP clone. X * Definitions for the UUCP package X * X * Copyright Richard H. Lamb 1985,1986,1987 X * Copyright S. R. Sampson, August 1989 X * Copyright F. N. G. van Kempen Jul-Oct '89 X */ X X#ifndef TRUE X# define FALSE 0 X# define TRUE 1 X#endif X X#define LSYS "/usr/lib/uucp/L.sys" X#define LDEVICE "/usr/lib/uucp/L-devices" X#define UUCICO "/usr/lib/uucp/uucico" X#define UUXQT "/usr/lib/uucp/uuxqt" X#define RMAIL "rmail" /* Remote Mailer */ X#define SMAIL "smail" /* Internet Mailer */ X#define LMAIL "lmail" /* Local Mailer */ X#define SYSLOG "/usr/lib/uucp/Log/uucico.log" X#define XQTLOG "/usr/lib/uucp/Log/uuxqt.log" X#define PUBDIR "/usr/spool/uucppublic" X#define SPOOLDIR "/usr/spool/uucp" X#define SPOOLSEQ "/usr/lib/uucp/SPOOLSEQ" X#define LSPOOLSEQ "/usr/lib/uucp/SPOOLSEQ.LCK" X#define LOCKFILE "/usr/spool/locks/LCK..%s" /* terminal LOCKfile */ X#define GLOCKFILE "/usr/spool/locks/GLOCK..%s" /* terminal LOCKfile */ X#define NODENAME "/etc/uucpname" X#define CALLFILE "C.%s" X#define XQTFILE "X.%s" X#define MAILFILE "B.%s" X X#define UUCPUSER "uucp" X#define ERRUSER "postmaster" X X#define UUCPUID 40 /* RMAIL needs these */ X#define UUCPGID 40 X#define POSTUID 41 /* RMAIL needs these */ X#define POSTGID 40 X X#define SITENAMELEN 32 X#define PATHLEN 256 X X#define MSGTIME 20 X#define MAXPACK 256 X X/* L.sys field defines */ X#define FLD_REMOTE 0 /* remote system name */ X#define FLD_CCTIME 1 /* legal call times */ X#define FLD_DEVICE 2 /* device, or ACU for modem */ X#define FLD_SPEED 3 /* bit rate */ X#define FLD_PHONE 4 /* phone number */ X#define FLD_EXPECT 5 /* first login "expect" field */ X#define FLD_SEND 6 /* first login "send" field */ X + END-OF-FILE uucp.h chmod 'u=rw,g=r,o=r' 'uucp.h' set `wc -c 'uucp.h'` count=$1 case $count in 1882) :;; *) echo 'Bad character count in ''uucp.h' >&2 echo 'Count should be 1882' >&2 esac exit 0