[mod.sources] v09i013: ELM Mail System, Part13/19

sources-request@mirror.TMC.COM (03/10/87)

Submitted by: Dave Taylor <hplabs!taylor>
Mod.sources: Volume 9, Issue 13
Archive-name: elm2/Part13

#! /bin/sh
# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
# If this archive is complete, you will see the message:
#		"End of archive 13 (of 19)."
# Contents:  test/test.notes utils/arepdaemon.c utils/newalias.c
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
echo shar: Extracting \"test/test.notes\" \(13582 characters\)
if test -f test/test.notes ; then 
  echo shar: Will not over-write existing file \"test/test.notes\"
else
sed "s/^X//" >test/test.notes <<'END_OF_test/test.notes'
X/***** hpfloat:net.micro.68K / barrett /  2:39 pm  Dec 16, 1985*/
XDoes anyone here at this site know anything about hang-gliding? 
X
XI am thinking of learning to hang-glide, but am afraid of heights.  I
Xdo fly light planes and love the hairiest roller coaster rides available
Xhowever.  My main question is "is there a way to learn to hang-glide 
Xgradually?"  I have seen some films of people learning on big sand dunes
Xan such before leaping off of cliffs with the things.  
X
XDave Barrett
Xhpfcla!barrett
X/* ---------- */
X/***** hpcnof:fsd.rec / hpfcla!ajs /  5:57 pm  Dec 16, 1985*/
X> Does anyone here at this site know anything about hang-gliding? 
X
XYeah.  Don't waste your time, don't waste your money, don't risk your life.
X
X> I am thinking of learning to hang-glide, but am afraid of heights.
X
XI wasn't, but it still got me a broken arm.
X
X> My main question is "is there a way to learn to hang-glide gradually?"
X
XProbably not (yet).  Five years ago, simulators were in practice non-
Xexistent.  We got twenty seconds hanging in a triangular control bar
Xwith a person pushing.  Next stop, rocky slopes, real gliders, and cheap
Xwalkie-talkies.
X
XYou'd be amazed how easy it is to injure yourself.  It's the nature of
Xthe hobby.  People with plenty of experience die doing it every day,
Xdue to circumstances often beyond their control.  There are better ways
Xto get thrills.
X
XAlan
X/* ---------- */
X/***** hpcnof:fsd.rec / hpfcms!mpm /  8:58 pm  Dec 16, 1985*/
X
X>You'd be amazed how easy it is to injure yourself.  It's the nature of
X>the hobby.  People with plenty of experience die doing it every day,
X>due to circumstances often beyond their control.  There are better ways
X>to get thrills.
X>Alan
X
X     I haven't done any hang-gliding myself, but I would like to try it
Xsome day.  (I have a moderate fear of heights; it depends on the altitude
Xand the apparent stability of my "perch".)
X
X     I read (or heard) that MOST hang-gliding accidents fall into two
Xcategories:
X
X     1) novices attempt something beyond their experience (like jumping
X	off a tall building after one lesson on a gently sloped hill),
X
X     2) experts attempt VERY dramatic stuff (like jumping off El
X	Capitan in unpredictable thermal up- and down- drafts).
X
X     Please note:  Alan Silverstein doesn't fall in EITHER category.  I
Xtook some sport parachuting lessons a few years ago.  It turned out to be
Xquite safe GIVEN ADEQUATE TRAINING as I had at the time.  I suspect the
Xsame would hold true for hang-gliding (or rapelling, or ice climbing, or
X...).  The best way to find out if you can overcome your fears is by con-
Xfronting them in a safe and supportive environment.
X
X     My recommendation:  check out any "school" before you sign up.  Ask
Xabout their safety record, the terrain where they offer lessons, amount of
X"ground school" training before first "flight", etc.  Above all, make sure
Xthat you TRUST any prospective teacher.  Even if you have no logical reason
Xto distrust someone, don't try something like this unless you trust them.
X(This is where your rational mind needs to work with your intuition.)
XOtherwise you could easily get hurt.
X
X     This is likely to be unknown territory for you, so be prepared and
Xyou will likely have a more enjoyable (and safe) experience.  Of course,
Xthere is ALWAYS the chance for an accident.
X
X	-- Mike "no I wasn't crazy at the time; I WANTED to do it" McCarthy
X	   hpfcla!mpm
X/* ---------- */
X/***** hpcnof:fsd.rec / dat / 12:12 pm  Dec 19, 1985*/
X>> Does anyone here at this site know anything about hang-gliding? 
X>Yeah.  Don't waste your time, don't waste your money, don't risk your life.
X
X	Strong stuff!  I think you're out on a limb this time, Alan.
XI've known lots of people who've hang-glided and never gotten hurt.
X(and we're talking the La Jolla cliffs in San Diego!!) (they also
Xthink it's the best 'high' in the world (and they've tried some
Xpretty strange things to compare!))
X
X>> I am thinking of learning to hang-glide, but am afraid of heights.
X>I wasn't, but it still got me a broken arm.
X
X	Fine.  So I broke my arm a long time ago jumping off a bunk
Xbed.  Does this mean that bunk beds are too dangerous and that I 
Xshouldn't ever sleep in one???
X
X	The point is that anything you do is dangerous and that the
Xway to minimize the danger is to take things gradually and only 
Xprogress when you feel comfortable with your current level of learning.
XAt the same time realize that even sitting in a chair in a warm room
Xcould be dangerous, so don't be so foolishly optimistic to think that
Xyou cannot get seriously hurt hang-gliding.
X
X	On the other hand - if you want to go for it - GO FOR IT!!!
X
X			-- Dave "Cheap Thrills, Inc." Taylor
X/* ---------- */
X/***** hpcnof:fsd.rec / hpfcmp!rjn / 11:33 pm  Dec 16, 1985*/
Xre: hang gliding
X
XI am a licensed [so what] pilot in powered  aircraft and  sailplanes.  I was
Xtaking  hang  gliding  (HG)  instruction  four years ago (prior to moving to
XColorado).  I gave it up when I moved here.  My impressions:
X
X* If your  introduction  to piloting flying machines is via HG, you will not
X  have enough  understanding of aerodynamics to safely operate your craft in
X  calm or steady-wind conditions.
X
X* HGs which are controlled by weight  shifting do not have adequate  control
X  authority   for   normal   conditions,   unless   you  have  lots  of  the
X  aforementioned  understanding and fly only in ideal  conditions.  HGs with
X  3-axis control offer a little more margin.
X
X* HGs are typically operated close to the ground.  No HG designs have enough
X  control  authority  to handle  gusty  conditions.  You can  safely  land a
X  parachute in conditions  which are dangerous for HG  operation.  Flying in
X  gusty  conditions  is the most  popular  way to crash a HG.  If you  think
X  jumping is dangerous, don't take up HG.  (I used to room with a jumpmaster
X  and have made one jump myself.  I think jumping is safer.)
X
X* HGs operated at higher altitudes (away from ground  reference) suffer from
X  lack of  instrumentation.  It is easy to enter a spiral dive, spin or deep
X  stall (luff the sail on Rogallo machines)  without  instruments or lots of
X  experience.  Spiral dives usually  overstress  the airframe; the resulting
X  collection of parts crashes.
X
XIf you  insist on K-Mart  aviating,  I suggest a 2-place  ultra-light  (with
Xparachute), a good instructor and a calm day.  At least the ground is level.
XBring earplugs.
X
XBob Niland  TN-226-4014   HP-UX: hpfcla!rjn    DESK: rjn (hpfcla) /HP4000/UX
X/* ---------- */
X/***** hpcnof:fsd.rec / hpfloat!jim /  9:10 am  Dec 17, 1985*/
XTry flying across the waves on a windsurfer!  I once met a guy from
XDenver who said he had tried them all--hang gliding, sky diving.  Windsurfing
Xoffered just as much thrill with almost no risk.
X
XThe crash landings are rather painless.  I've gotten 5 feet of air right
Xhere in Colorado.
X
X		"Jumping Jim" Tear
X
X/* ---------- */
X/***** hpcnof:fsd.rec / hpfcmt!ron /  7:56 am  Dec 17, 1985*/
X
X
XI also am a "regular" aircraft (and sailplane) pilot.
X
XI have not tried hang gliding however I have a fairly close friend who 
Xwas into it before he totally demolished his craft. He was only bruised
Xby the impact but came away considerably more careful about his sports.
X
XBesides the previously mentioned drawbacks I would like to mention the 
Xfollowing:
X
XA perfect landing consists of
X
X   (a) Correct airspeed
X   (b) Level wings ( tolerance to prevent wingtip dragging)
X   (c) Correct yaw alignment   (within tolerance of landing gear)
X   (d) Correct pitch
X   (e) Correct rate of descent  (within tolerance of landing gear)
X   (f) Correct altitude
X   (g) Correct groundspeed (within tolerance of landing gear) 
X
XConsider that the landing gear on an HG is your legs and gear collapse
Xis fairly common due to the low maximum speed for the gear and the 
Xairspeed being right in that range at touchdown.
XConsider also that even calm air has some "breezes" going. 
XAdd to the "breezes" the fact that your control authority relative to the
Xvelocity of the breezes is poor and you can wind up with all the ingredients
Xfor a face plant.
X
XNow to moderate the above, the idea of simple flight appeals greatly to
Xme. Unfortunately my personal risk-taking threshold is below the minimum
Xrisk for this sport.  
XI agree with Bob, try ultra-lights if you MUST . At least they have wheels.
X
X
XRon Miller
X
X
X"Show me a country where the newspapers are filled with good news
Xand I'll show you a country where the jails are filled with good people."
X					-<I forgot>
X
XService Engineering  (Hardware Support)
XHewlett-Packard Co.
XFt. Collins Systems Div. Home of the HP 9000 Series 200,300 & 500
XFt. Collins Colorado
X303-226-3800
X
Xat: {ihnp4}hpfcla!ron
X/* ---------- */
X/***** hpcnof:fsd.rec / hpfcla!ajs /  6:36 pm  Dec 19, 1985*/
X> Strong stuff!  I think you're out on a limb this time, Alan.
X> I've known lots of people who've hang-glided and never gotten hurt.
X
XYes, but, --
X
X> Fine.  So I broke my arm a long time ago jumping off a bunk
X> bed.  Does this mean that bunk beds are too dangerous and that I 
X> shouldn't ever sleep in one???
X
XI'll be more explicit (and just as strong).  Let's say sleeping is a
Xzero (epsilon?) on the risk scale, and flying in a commercial aircraft
Xis 1, and driving a car, oh, I'd guess about a 4, and parachuting maybe
Xa 6, and SCUBA diving must be maybe a 7 or 8 then, comparable (?) with
Xclimbing Fourteeners.  Based on my experience with it, I'd rank hang
Xgliding at around a 12 or 15.  Don't risk your life.
X
XOne thing I discovered is that being under a "kite" feels very different
Xfrom how you might guess while watching someone fly.  Not nearly as
Xcomfortable (until airborne); very exposed.  Some people are naturals at
Xit; some (like me) are not.  If you are the former, and you are lucky,
Xand it appeals to you, you'll go do it anyway, no matter what I or Dave
Xsay about it; good luck to you.
X
XBut, if you are the latter, you'll likely injure yourself seriously
Xtrying to learn, because there isn't much margin for error outside a
Xsimulator.  Look, I was gung-ho, being trained by a "professional"
Xtraining school, determined to overcome inexperience, ignored warnings
Xfrom concerned friends, was certain I could do it safely, paid close
Xattention to instructions, studied the subject intensely, and when I
Xcrashed, I'd been in the air about five seconds, was about ten feet off
Xthe ground, and was amazed that I'd actually broken anything.  A very
Xsobering experience.
X
XOn the way to the hospital, the trainer doing the driving informed me
Xthat someone was seriously injured in their classes about once a month.
X
XGee, Dave, I guess I must be "out on a limb", incapable of giving advice
Xon the subject, because I survived the crash.  :-)
X
XAlan
X/* ---------- */
X/***** hpcnof:fsd.rec / hpfcde!anny /  2:28 pm  Dec 31, 1985*/
XWARNING:  Severe Base Note  D  r   i    f      t
X
X<. . . and driving a car, oh, I'd guess about a 4, and parachuting maybe
X<a 6, and SCUBA diving must be maybe a 7 or 8 then, . . .
X
XCome on Alan!  SCUBA diving more dangerous than parachuting?  Maybe if your
Xparachuting off a jump tower versus SCUBA diving alone on the Great Barrier
XReef at night carring shark bait making wounded fish sounds. . . ;-)
X
XAfter all, the FIRST time you parachute, you have to jump out of a PLANE! (/.\)
XIn the SKY!  You can SCUBA dive in a pool or a shallow lake or inlet.
XIf something goes wrong in the water, your buddy's there to help.  If 
Xsomething goes wrong in the sky, so long . . .
X
XJust defending what I consider to be a fun and safe sport!
X
XAnny (low altitude (4' or less) sports for me!) Randel
X/* ---------- */
X/***** hpcnof:fsd.rec / hpfcla!ajs /  9:27 am  Jan  2, 1986*/
X> Come on Alan!  SCUBA diving more dangerous than parachuting?
X
XForgive me, I was just guessing, to make a point.  I don't know the
Xactual statistics, but you're probably right -- if you measure accidents
Xper hour.  On the other hand, if you measure accidents per jump or dive,
Xit wouldn't surprise me if the rates were similar.  Lotsa people go
Xdiving without enough training, but skydiving requires decent training
Xand the accident rate is surprisingly low.
X
XAlan "pick your poison" Silverstein
X/* ---------- */
X/***** hpcnof:fsd.rec / hpfcdc!donn /  9:32 am  Jan  3, 1986*/
XThe problem with SCUBA diving is the fact that "fly by nites" can
Xafford to get into the business.  A reputable dive shop will not
Xlet you rent a tank unless you have a NAUI card (or ==) (and they'll hold
Xit while you have the tanks).  However there are always some who
Xwill not do this, and some clown tries diving without essentially
Xany training ("Gee, I can swim, so I can dive.") and gets into
Xtrouble.  It's much tougher to be a "fly by night" (or anytime)
Xwhen you need an airplane and a pilot's license.  Actually, the
Xaccident rate for people with any significant training *and* who
Xare doing something more-or-less reasonable is not bad.  (Diving
Xbelow 150ft (or maybe less) is like starting a jump at 50000 feet:
Xit might work, but good luck unless you know what you're doing. 
XThe problem is that there isn't much reason to start at 50000 feet,
Xbut there's a lot of interesting and valuable stuff below 150.)
X
XI like to dive (tropical saltwater only, so I don't do it much),
Xand since one of the graduation exercises is diving while someone
Xis *trying* to make you screw up (albeit in a pool where there's
Xsomeone to fish you out), you learn to handle problems.  If you're
Xgutsy, try the NAUI *instructors* class: the graduation from that
Xis a open-water dive with known defective equipment!
X
XDonn
X/* ---------- */
END_OF_test/test.notes
if test 13582 -ne `wc -c <test/test.notes`; then
    echo shar: \"test/test.notes\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"utils/arepdaemon.c\" \(13412 characters\)
if test -f utils/arepdaemon.c ; then 
  echo shar: Will not over-write existing file \"utils/arepdaemon.c\"
else
sed "s/^X//" >utils/arepdaemon.c <<'END_OF_utils/arepdaemon.c'
X/**			arepdaemon.c			**/
X
X/** (C) Copyright 1986 Dave Taylor			**/
X
X/** Keep track of mail as it arrives, and respond by sending a 'recording'
X    file to the sender as new mail is received.
X
X    Note: the user program that interacts with this program is the
X    'autoreply' program and that should be consulted for further
X    usage information.
X
X    This program is part of the 'autoreply' system, and is designed
X    to run every hour and check all mailboxes listed in the file 
X    "/etc/autoreply.data", where the data is in the form:
X
X	username	replyfile	current-mailfile-size
X
X    To avoid a flood of autoreplies, this program will NOT reply to mail 
X    that contains header "X-Mailer: fastmail".  Further, each time the 
X    program responds to mail, the 'mailfile size' entry is updated in
X    the file /etc/autoreply.data to allow the system to be brought 
X    down and rebooted without any loss of data or duplicate messages.
X
X    This daemon also uses a lock semaphore file, /usr/spool/uucp/LCK..arep,
X    to ensure that more than one copy of itself is never running.  For this
X    reason, it is recommended that this daemon be started up each morning
X    from cron, since it will either start since it's needed or simply see
X    that the file is there and disappear.
X
X    Since this particular program is the main daemon answering any
X    number of different users, it must be run with uid root.
X
X    (C) 1985, Dave Taylor, HP Colorado Networks Operation
X**/
X
X#include <stdio.h>
X
X#ifdef BSD
X# include <sys/time.h>
X#else
X# include <time.h>
X#endif
X
X#include <sys/types.h>
X#include <sys/stat.h>
X
X#include "defs.h"
X
Xstatic char ident[] = { WHAT_STRING };
X
X#define arep_lock_file	"/usr/spool/uucp/LCK..arep"
X
X#define autoreply_file	"/etc/autoreply.data"
X#define fastmail	"/usr/local/bin/fastmail"
X
X#define logfile		"/etc/autoreply.log"	/* first choice   */
X#define logfile2	"/tmp/autoreply.log"	/* second choice  */
X
X#define BEGINNING	0		/* see fseek(3S) for info */
X#define SLEEP_TIME	3600		/* run once an hour       */
X#define MAX_PEOPLE	20		/* max number in program  */
X
X#define EXISTS		00		/* lock file exists??     */
X#define MODE		0777		/* lockfile creation mode */
X
X#define remove_return(s)	if (strlen(s) > 0) { \
X			          if (s[strlen(s)-1] == '\n') \
X				    s[strlen(s)-1] = '\0'; \
X		                }
X
Xstruct replyrec {
X	char 	username[NLEN];		/* login name of user */
X	char	mailfile[SLEN];		/* name of mail file  */
X	char    replyfile[SLEN];	/* name of reply file */
X	long    mailsize;		/* mail file size     */
X	int     in_list;		/* for new replies    */
X      } reply_table[MAX_PEOPLE];
X
XFILE  *logfd;				/* logfile (log action)   */
Xlong  autoreply_size = 0L;		/* size of autoreply file */
Xint   active = 0;			/* # of people 'enrolled' */
X
XFILE  *open_logfile();			/* forward declaration    */
X
Xlong  bytes();				/*       ditto 		  */
X
Xmain()
X{
X	long size;
X	int  person, data_changed;
X
X	if (! lock())
X	  exit(0);	/* already running! */
X
X	while (1) {
X
X	  logfd = open_logfile();	/* open the log */
X
X	  /* 1. check to see if autoreply table has changed.. */
X
X	  if ((size = bytes(autoreply_file)) != autoreply_size) {
X	    read_autoreply_file(); 
X	    autoreply_size = size;
X	  }
X
X	  /* 2. now for each active person... */
X	
X	  data_changed = 0;
X
X	  for (person = 0; person < active; person++) {
X	    if ((size = bytes(reply_table[person].mailfile)) != 
X		reply_table[person].mailsize) {
X	      if (size > reply_table[person].mailsize)
X	        read_newmail(person);
X	      /* else mail removed - resync */
X	      reply_table[person].mailsize = size;
X	      data_changed++;
X	    }
X	  }
X
X	  /* 3. if data changed, update autoreply file */
X
X	  if (data_changed)
X	    update_autoreply_file();
X
X	  close_logfile();		/* close the logfile again */
X
X	  /* 4. Go to sleep...  */
X
X	  sleep(SLEEP_TIME);
X	}
X}
X
Xint
Xread_autoreply_file()
X{
X	/** We're here because the autoreply file has changed size!!  It
X	    could either be because someone has been added or because
X	    someone has been removed...since the list will always be in
X	    order (nice, eh?) we should have a pretty easy time of it...
X	**/
X
X	FILE *file;
X	char username[SLEN], 	replyfile[SLEN];
X	int  person;
X 	long size;
X	
X	log("Autoreply data file has changed!  Reading...");
X
X	if ((file = fopen(autoreply_file,"r")) == NULL) {
X	  log("No-one is using autoreply...");
X	  return(0);
X	}
X	
X	for (person = 0; person < active; person++)
X	  reply_table[person].in_list = 0;
X	
X	while (fscanf(file, "%s %s %dl", username, replyfile, &size) != EOF) {
X	  /* check to see if this person is already in the list */
X	  if ((person = in_list(username)) != -1) {
X	    reply_table[person].in_list = 1;
X	    reply_table[person].mailsize = size;	 /* sync */
X	  }
X	  else { 	/* if not, add them */
X	    if (active == MAX_PEOPLE) {
X	      unlock();
X	      exit(log("Couldn't add %s - already at max people!", 
X		         username));
X	    }
X	    log("adding %s to the active list", username);
X	    strcpy(reply_table[active].username, username);
X	    sprintf(reply_table[active].mailfile, "/usr/mail/%s", username);
X	    strcpy(reply_table[active].replyfile, replyfile);
X	    reply_table[active].mailsize = size;
X	    reply_table[active].in_list = 1;	/* obviously! */
X	    active++;
X	  }
X	}
X
X	/** now check to see if anyone has been removed... **/
X
X	for (person = 0; person < active; person++)
X	  if (reply_table[person].in_list == 0) {
X	     log("removing %s from the active list", 
X		  reply_table[person].username);
X	    strcpy(reply_table[person].username, 
X		   reply_table[active-1].username);
X	    strcpy(reply_table[person].mailfile, 
X		   reply_table[active-1].mailfile);
X	    strcpy(reply_table[person].replyfile, 
X		   reply_table[active-1].replyfile);
X	    reply_table[person].mailsize = reply_table[active-1].mailsize;
X	    active--;
X	  }
X}
X
Xupdate_autoreply_file()
X{
X	/** update the entries in the autoreply file... **/
X
X	FILE *file;
X	register int person;
X
X	if ((file = fopen(autoreply_file,"w")) == NULL) {
X          log("Couldn't update autoreply file!");
X	  return;
X	}
X
X	for (person = 0; person < active; person++)
X	  fprintf(file, "%s %s %ld\n",
X		  reply_table[person].username,
X		  reply_table[person].replyfile,
X		  reply_table[person].mailsize);
X
X	fclose(file);
X
X	printf("updated autoreply file\n");
X	autoreply_size = bytes(autoreply_file);
X}
X
Xint
Xin_list(name)
Xchar *name;
X{
X	/** search the current active reply list for the specified username.
X	    return the index if found, or '-1' if not. **/
X
X	register int index;
X
X	for (index = 0; index < active; index++)
X	  if (strcmp(name, reply_table[index].username) == 0)
X	    return(index);
X	
X	return(-1);
X}
X
Xread_newmail(person)
Xint person;
X{
X	/** Read the new mail for the specified person. **/
X
X	
X	FILE *mailfile;
X	char from_whom[LONG_SLEN], subject[SLEN];
X	int  sendit;
X
X	log("New mail for %s", reply_table[person].username);
X
X        if ((mailfile = fopen(reply_table[person].mailfile,"r")) == NULL)
X           return(log("can't open mailfile for user %s", 
X		    reply_table[person].username));
X
X        if (fseek(mailfile, reply_table[person].mailsize, BEGINNING) == -1)
X           return(log("couldn't seek to %ld in mail file!", 
X	               reply_table[person].mailsize));
X
X	while (get_return(mailfile, person, from_whom, subject, &sendit) != -1)
X	  if (sendit)
X	    reply_to_mail(person, from_whom, subject);
X
X	return;
X}
X
Xint
Xget_return(file, person, from, subject, sendit)
XFILE *file;
Xint  person, *sendit;
Xchar *from, *subject;
X{
X	/** Reads the new message and return the from and subject lines.
X	    sendit is set to true iff it isn't a machine generated msg
X	**/
X	
X    char name1[SLEN], name2[SLEN], lastname[SLEN];
X    char buffer[LONG_SLEN], hold_return[NLEN];
X    int done = 0, in_header = 0;
X
X    from[0] = '\0';
X    *sendit = 1;
X
X    while (! done) {
X
X      if (fgets(buffer, LONG_SLEN, file) == NULL)
X	return(-1);
X
X      if (first_word(buffer, "From ")) {
X	in_header++;
X	sscanf(buffer, "%*s %s", hold_return);
X      }
X      else if (in_header) {
X        if (first_word(buffer, ">From")) {
X	  sscanf(buffer,"%*s %s %*s %*s %*s %*s %*s %*s %*s %s", name1, name2);
X	  add_site(from, name2, lastname);
X        }
X        else if (first_word(buffer,"Subject:")) {
X	  remove_return(buffer);
X	  strcpy(subject, (char *) (buffer + 8));
X        }
X        else if (first_word(buffer,"X-Mailer: fastmail"))
X	  *sendit = 0;
X        else if (strlen(buffer) == 1)
X	  done = 1;
X      }
X    }
X
X    if (from[0] == '\0')
X      strcpy(from, hold_return); /* default address! */
X    else
X      add_site(from, name1, lastname);	/* get the user name too! */
X
X    return(0);
X}
X
Xadd_site(buffer, site, lastsite)
Xchar *buffer, *site, *lastsite;
X{
X	/** add site to buffer, unless site is 'uucp', or the same as 
X	    lastsite.   If not, set lastsite to site.
X	**/
X
X	char local_buffer[LONG_SLEN], *strip_parens();
X
X	if (strcmp(site, "uucp") != 0)
X	  if (strcmp(site, lastsite) != 0) {
X	      if (buffer[0] == '\0')
X	        strcpy(buffer, strip_parens(site));         /* first in list! */
X	      else {
X	        sprintf(local_buffer,"%s!%s", buffer, strip_parens(site));
X	        strcpy(buffer, local_buffer);
X	      }
X	      strcpy(lastsite, strip_parens(site)); /* don't want THIS twice! */
X	   }
X}
X
Xremove_first_word(string)
Xchar *string;
X{	/** removes first word of string, ie up to first non-white space
X	    following a white space! **/
X
X	register int loc;
X
X	for (loc = 0; string[loc] != ' ' && string[loc] != '\0'; loc++) 
X	    ;
X
X	while (string[loc] == ' ' || string[loc] == '\t')
X	  loc++;
X	
X	move_left(string, loc);
X}
X
Xmove_left(string, chars)
Xchar string[];
Xint  chars;
X{
X	/** moves string chars characters to the left DESTRUCTIVELY **/
X
X	register int i;
X
X	chars--; /* index starting at zero! */
X
X	for (i=chars; string[i] != '\0' && string[i] != '\n'; i++)
X	  string[i-chars] = string[i];
X
X	string[i-chars] = '\0';
X}
X
Xreply_to_mail(person, from, subject)
Xint   person;
Xchar *from, *subject;
X{
X	/** Respond to the message from the specified person with the
X	    specified subject... **/
X	
X	char buffer[SLEN];
X
X	if (strlen(subject) == 0)
X	  strcpy(subject, "Auto-reply Mail");
X	else if (! first_word(subject,"Auto-reply")) {
X	  sprintf(buffer, "Auto-reply to:%s", subject);
X	  strcpy(subject, buffer);
X	}
X
X	log("auto-replying to '%s'", from);
X
X	mail(from, subject, reply_table[person].replyfile, person);
X}	
X
Xreverse(string)
Xchar *string;
X{
X	/** reverse string... pretty trivial routine, actually! **/
X
X	char buffer[SLEN];
X	register int i, j = 0;
X
X	for (i = strlen(string)-1; i >= 0; i--)
X	  buffer[j++] = string[i];
X
X	buffer[j] = '\0';
X
X	strcpy(string, buffer);
X}
X
Xlong
Xbytes(name)
Xchar *name;
X{
X	/** return the number of bytes in the specified file.  This
X	    is to check to see if new mail has arrived....  **/
X
X	int ok = 1;
X	extern int errno;	/* system error number! */
X	struct stat buffer;
X
X	if (stat(name, &buffer) != 0)
X	  if (errno != 2) {
X	   unlock();
X	   exit(fprintf(stderr,"Error %d attempting fstat on %s", errno, name));
X	  }
X	  else
X	    ok = 0;
X	
X	return(ok ? buffer.st_size : 0);
X}
X
Xmail(to, subject, filename, person)
Xchar *to, *subject, *filename;
Xint   person;
X{
X	/** Mail 'file' to the user from person... **/
X	
X	char buffer[VERY_LONG_STRING];
X
X	sprintf(buffer, "%s -f '%s [autoreply]' -s '%s' %s %s",
X		fastmail, reply_table[person].username,
X	        subject, filename, to);
X	
X	system(buffer);
X}
X
Xlog(message, arg)
Xchar *message;
Xchar *arg;
X{
X	/** Put log entry into log file.  Use the format:
X	      date-time: <message>
X	**/
X
X	struct tm *localtime(), *thetime;
X	long      time(), clock;
X	char      buffer[SLEN];
X
X	/** first off, get the time and date **/
X
X	clock = time((long *) 0);       /* seconds since ???   */
X	thetime = localtime(&clock);	/* and NOW the time... */
X
X	/** then put the message out! **/
X
X	sprintf(buffer, message, arg);
X
X	fprintf(logfd,"%d/%d-%d:%02d: %s\n", 
X		thetime->tm_mon+1, thetime->tm_mday,
X	        thetime->tm_hour,  thetime->tm_min,
X	        buffer);
X}
X
XFILE *open_logfile()
X{
X	/** open the logfile.  returns a valid file descriptor **/
X
X	FILE *fd;
X
X	if ((fd = fopen(logfile, "a")) == 0)
X	  if ((fd = fopen(logfile2, "a")) == 0) {
X	    unlock();
X	    exit(1);	/* give up! */
X	  }
X
X	return( (FILE *) fd);
X}
X
Xclose_logfile()
X{
X	/** Close the logfile until needed again. **/
X
X	fclose(logfd);
X}
X
Xchar *strip_parens(string)
Xchar *string;
X{
X	/** Return string with all parenthesized information removed.
X	    This is a non-destructive algorithm... **/
X
X	static char  buffer[LONG_SLEN];
X	register int i, depth = 0, buffer_index = 0;
X
X	for (i=0; i < strlen(string); i++) {
X	  if (string[i] == '(')
X	    depth++;
X	  else if (string[i] == ')') 
X	    depth--;
X	  else if (depth == 0)
X	    buffer[buffer_index++] = string[i];
X	}
X	
X	buffer[buffer_index] = '\0';
X
X	return( (char *) buffer);
X}
X
X/*** LOCK and UNLOCK - ensure only one copy of this daemon running at any
X     given time by using a file existance semaphore (wonderful stuff!) ***/
X
Xlock()
X{
X	/** Try to create the lock file.  If it's there, or we can't
X	    create it for some stupid reason, return zero, otherwise,
X	    a non-zero return code indicates success in locking this
X	    process in. **/
X
X	if (access(arep_lock_file, EXISTS) == 0)
X	  return(0); 	/* file already exists!! */
X
X	if (creat(arep_lock_file, MODE) == -1)
X	  return(0);	/* can't create file!!   */
X
X	return(1);
X}
X
Xunlock()
X{
X	/** remove lock file if it's there! **/
X
X	(void) unlink(arep_lock_file);
X}
END_OF_utils/arepdaemon.c
if test 13412 -ne `wc -c <utils/arepdaemon.c`; then
    echo shar: \"utils/arepdaemon.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: Extracting \"utils/newalias.c\" \(13123 characters\)
if test -f utils/newalias.c ; then 
  echo shar: Will not over-write existing file \"utils/newalias.c\"
else
sed "s/^X//" >utils/newalias.c <<'END_OF_utils/newalias.c'
X/**		newalias.c		**/
X
X/** (C) Copyright 1986 Dave Taylor      **/
X
X/** Install a new set of aliases for the 'Elm' mailer. 
X
X	If invoked with a specific filename, it assumes that
X  it is working with an individual users alias tables, and
X  generates the .alias.hash and .alias.data files in their
X  home directory.
X	If, however, it is invoked with no arguments, then
X  it assumes that the user is updating the system alias
X  file and uses the defaults for everything.
X
X  The format for the input file is;
X    alias1, alias2, ... : username : address
Xor  alias1, alias2, ... : groupname: member, member, member, ...
X                                     member, member, member, ...
X
X  "-q" flag added: 6/17/86
X**/
X
X#ifdef BSD
X#  include <sys/file.h>
X#else
X#  include <fcntl.h>
X#endif
X
X#include <stdio.h>
X#include "defs.h"		/* ELM system definitions */
X
Xstatic char ident[] = { WHAT_STRING };
X
X#ifndef TAB
X# define TAB	 	'\t'	/* TAB character!         */
X#endif
X	
X#define alias_hash	".alias_hash"
X#define alias_data	".alias_data"
X#define alias_text	".alias_text"
X
X#define group(string)		(strpbrk(string,", ") != NULL)
X
Xstruct alias_rec
Xshash_table[MAX_SALIASES];	/* the actual hash table     */
X
Xstruct alias_rec
Xuhash_table[MAX_UALIASES];	/* the actual hash table     */
X
Xint  hash_table_loaded=0;	/* is system table actually loaded? */
X
Xint  buff_loaded;		/* for file input overlap... */
Xint  error= 0;			/* if errors, don't save!    */
Xint  system=0;			/* system file updating?     */
Xint  count=0;			/* how many aliases so far?  */
Xlong offset = 0L;		/* data file line offset!    */
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X	FILE *in, *data;
X	char inputname[SLEN], hashname[SLEN], dataname[SLEN];
X	char home[SLEN], buffer[LONG_STRING];
X	int  hash, count = 0, owner, quiet = 0;
X
X	if (argc != 1)
X	  if (strcmp(argv[1], "-q") == 0)
X	    quiet++;
X	  else
X	    exit(printf("Usage: %s\n", argv[0]));
X
X	owner = getuid();
X
X	if (owner == 0 && ! quiet) {	/* being run by root! */
X	  printf("Would you like to update the system aliases? (y/n)");
X	  gets(buffer, 2);
X	  if (buffer[0] == 'y' || buffer[0] == 'Y') {
X	    printf("Updating the system alias file...\n");
X
X	    sprintf(inputname, "%s/%s", mailhome, alias_text);
X	    sprintf(hashname, "%s/%s", mailhome, alias_hash);
X	    sprintf(dataname, "%s/%s", mailhome, alias_data);
X	    system++;
X	    init_table(shash_table, MAX_SALIASES); 
X	  }
X	  else 
X	    printf("Updating your personal alias file...\n");
X	}
X	
X	if (! system) {
X	  if (strcpy(home, getenv("HOME")) == NULL)
X	    exit(printf("Confused: No HOME variable in environment!\n"));
X
X	  sprintf(inputname, "%s/%s", home, alias_text);
X	  sprintf(hashname,  "%s/%s", home, alias_hash); 
X	  sprintf(dataname,  "%s/%s", home, alias_data); 
X
X	  init_table(uhash_table, MAX_UALIASES); 
X
X	  read_in_system(shash_table, sizeof shash_table);
X	}
X
X	if ((in = fopen(inputname,"r")) == NULL)
X	  exit(printf("Couldn't open %s for input!\n", inputname));
X
X	if ((hash = open(hashname, O_WRONLY | O_CREAT, 0644)) == -1)
X	  exit(printf("Couldn't open %s for output!\n", hashname));
X
X	if ((data = fopen(dataname,"w")) == NULL)
X	  exit(printf("Couldn't open %s for output!\n", dataname));
X
X	buff_loaded = 0; 	/* file buffer empty right now! */
X
X	while (get_alias(in, buffer) != -1) {
X	  if (system)
X	    put_alias(data, buffer, shash_table, MAX_SALIASES);	
X	  else
X	    put_alias(data, buffer, uhash_table, MAX_UALIASES);	
X	  count++;
X	}
X
X	if (error) {
X	  printf("\n** Not saving tables!  Please fix and re-run %s!\n",
X		 argv[0]);
X	  exit(1);
X	}
X	else {
X	  if (system)
X	    write(hash, shash_table, sizeof shash_table);
X	  else
X	    write(hash, uhash_table, sizeof uhash_table);
X
X	  close(hash);
X	  fclose(data);
X	  close(in);
X	
X	  printf("Processed %d aliases\n", count);
X	  exit(0);
X	}
X}
X
Xint
Xget_alias(file, buffer)
XFILE *file;
Xchar *buffer;
X{
X	/* load buffer with the next complete alias from the file.
X	   (this can include reading in multiple lines and appending
X	   them all together!)  Returns EOF after last entry in file.
X	
X	Lines that start with '#' are assumed to be comments and are
X 	ignored.  White space as the first field of a line is taken
X	to indicate that this line is a continuation of the previous. */
X
X	static char mybuffer[SLEN];
X	int    done = 0, first_read = 1;
X
X	/** get the first line of the entry... **/
X
X	buffer[0] = '\0';			/* zero out line */
X
X	do {
X	  if (get_line(file, mybuffer, first_read) == -1) 
X	    return(-1);
X	  first_read = 0;
X	  if (mybuffer[0] != '#')
X	    strcpy(buffer, mybuffer);
X	} while (strlen(buffer) == 0);	
X
X	/** now read in the rest (if there is any!) **/
X
X	do {
X	  if (get_line(file, mybuffer, first_read) == -1) {
X	    buff_loaded = 0;	/* force a read next pass! */
X	    return(0);	/* okay. let's just hand 'buffer' back! */
X	  }
X	  done = (mybuffer[0] != ' ' && mybuffer[0] != TAB);
X	  if (mybuffer[0] != '#' && ! done)
X	    strcat(buffer, mybuffer);
X	  done = (done && mybuffer[0] != '#');
X	} while (! done);
X	
X	return(0);	/* no sweat! */
X}
X
Xput_alias(data, buffer, table, size)
XFILE *data;
Xchar *buffer;
Xstruct alias_rec table[];
Xint  size;
X{
X	/** break buffer down into three pieces: aliases, comment, and address.
X	    Make the appropriate entries in the table (size) 
X	**/
X
X	char aliases[LONG_STRING], address[LONG_STRING];
X	char comment[LONG_STRING];
X	int  first, last, i = 0, j = 0;
X
X	remove_all(' ', TAB, buffer);
X
X	for (i=0; buffer[i] != ':' && i < LONG_STRING; i++)
X	  aliases[i] = buffer[i];
X	aliases[i] = '\0';
X
X	for (i=strlen(buffer)-1; buffer[i] != ':' && i > 0; i--)
X	  address[j++] = buffer[i];
X	address[j] = '\0';
X
X	comment[0] = '\0';	/* default to nothing at all... */
X
X	if ((first=strlen(aliases)+1) < (last=(strlen(buffer) - j))) {
X	  extract_comment(comment, buffer, first, last); 
X	}
X
X	reverse(address);
X
X	add_to_table(data, aliases, comment, address, table, size);
X}
X
Xint
Xget_line(file, buffer, first_line)
XFILE *file;
Xchar *buffer;
Xint  first_line;
X{
X	/** read line from file.  If first_line and buff_loaded, 
X	    then just return! **/
X	int stat;
X
X	if (first_line && buff_loaded) {
X	  buff_loaded = 1;
X	  return;
X	}
X
X	buff_loaded = 1;	/* we're going to get SOMETHING in the buffer */
X
X	stat = fgets(buffer, SLEN, file) == NULL ? -1 : 0;
X
X	if (stat != -1)
X	  no_ret(buffer);
X
X	return(stat);
X}
X
Xreverse(string)
Xchar *string;
X{
X	/** reverse the order of the characters in string... 
X	    uses a bubble-sort type of algorithm!                 **/
X	
X	register int f, l;
X	char     c;
X	
X	f = 0;
X	l = strlen(string) - 1;
X	
X	while (f < l) {
X	  c = string[f];
X 	  string[f] = string[l];
X	  string[l] = c;
X	  f++;
X	  l--;
X	}
X}
X
Xadd_to_table(data, aliases, comment, address, table, size)
XFILE *data;
Xchar *aliases, *comment, *address;
Xstruct alias_rec table[];
Xint  size;
X{
X	/** add address + comment to datafile, incrementing offset count 
X	    (bytes), then for each alias in the aliases string, add to the
X	    hash table, with the associated pointer value! **/
X
X	static char buf[SLEN], *word;
X	long additive = 1L;
X
X	word = buf;	/* use the allocated space! */
X
X	if (group(address)) {
X	  check_group(address, aliases);
X	  if (error) return;	/* don't do work if we aren't to save it! */
X	  fprintf(data, "!%s\n", address);
X	  additive = 2L;
X	}
X	else {
X	  if (error) return;	/* don't do work if we aren't to save it! */
X	  if (strlen(comment) > 0) {
X	    fprintf(data, "%s (%s)\n", address, comment);
X	    additive = (long) (strlen(comment) + 4);
X	  }
X	  else
X	    fprintf(data, "%s\n", address, comment);
X	}
X
X	while ((word = (char *) strtok(aliases,", ")) != NULL) {
X	  add_to_hash_table(word, offset, table, size);
X	  aliases = NULL;	/* let's get ALL entries via 'strtok' */
X	  count++;
X	}
X
X	if ( system ? count > MAX_SALIASES-35 : count > MAX_UALIASES-21) {
X	  printf("** Too many aliases in file! **\n");
X	  error++;
X	}
X
X	offset = (offset + (long) strlen(address) + additive);
X}	
X
Xremove_all(c1, c2, string)
Xchar c1, c2, *string;
X{
X	/* Remove all occurances of character 'c1' or 'c2' from the string.
X	   Hacked (literally) to NOT remove ANY characters from within the
X	   colon fields.  This will only be used if the line contains TWO
X	   colons (and comments with colons in them are the kiss of death!)
X	 */
X
X	char buffer[LONG_STRING];
X	register int i = 0, j = 0, first_colon = -1, last_colon = -1;
X	
X	for (i = 0; string[i] != '\0' && i < LONG_STRING; i++) {
X	  if (string[i] != c1 && string[i] != c2)
X	    buffer[j++] = string[i];
X
X	  if (first_colon == -1 && string[i] == ':') {
X	    first_colon = i;
X	    for (last_colon=strlen(string);string[last_colon] != ':'; 
X		last_colon--) ;
X	  }
X	  else if (i > first_colon && i < last_colon)
X	   if (string[i] == c1 || string[i] == c2)
X	     buffer[j++] = string[i];
X	}
X	
X	buffer[j] = '\0';
X	strcpy(string, buffer);
X}
X
Xadd_to_hash_table(word, offset, table, size)
Xchar *word;
Xlong  offset;
Xstruct alias_rec table[];
Xint   size;
X{
X	/** add word and offset to current hash table. **/
X	register int loc;
X	
X	if (strlen(word) > 20)
X	  exit(printf("Bad alias name: %s.  Too long.\n", word));
X
X	loc = hash_it(word, size);
X
X	while (table[loc].name[0] != '\0' && strcmp(table[loc].name, word) != 0)
X	  loc = loc + 1 % size; 
X
X	if (table[loc].name[0] == '\0') {
X	  strcpy(table[loc].name, word);
X	  table[loc].byte = offset;
X	}
X	else 
X	  printf("** Duplicate alias '%s' in file.  Multiples ignored.\n",
X	         word);
X}
X
Xint
Xhash_it(string, table_size)
Xchar *string;
X{
X	/** compute the hash function of the string, returning
X	    it (mod table_size) **/
X
X	register int i, sum = 0;
X	
X	for (i=0; string[i] != '\0'; i++)
X	  sum += (int) string[i];
X
X	return(sum % table_size);
X}
X
Xinit_table(table, size)
Xstruct alias_rec table[];
Xint size;
X{
X	/** initialize hash table! **/
X
X	register int i;
X
X	for (i=0; i < size; i++)
X	  table[i].name[0] = '\0';
X}
X
Xread_in_system(table, size)
Xstruct alias_rec table[];
Xint size;
X{
X	/** read in the system hash table...to check for group aliases
X	    from the user alias file (to ensure that there are no names
X	    in the user group files that are not purely contained within
X	    either alias table) **/
X	
X	int  fd;
X	char fname[SLEN];
X
X	sprintf(fname, "%s/%s", mailhome, alias_hash);
X
X	if ((fd = open(fname, O_RDONLY)) == -1)
X	  return;	/* no sweat: flag 'hash_table_loaded' not set! */
X
X	(void) read(fd, table, size);
X	close(fd);
X	hash_table_loaded++;
X}
X	
Xcheck_group(names, groupname)
Xchar *names, *groupname;
X{
X	/** one by one make sure each name in the group is defined
X	    in either the system alias file or the user alias file.
X	    This search is linearly dependent, so all group aliases
X	    in the source file should appear LAST, after all the user
X	    aliases! **/
X
X	char *word, *bufptr, buffer[LONG_STRING];
X
X	strcpy(buffer, names);
X	bufptr = (char *) buffer;
X
X	while ((word = (char *) strtok(bufptr,", ")) != NULL) {
X	  if (! can_find(word)) 
X	    if (! valid_name(word)) {
X	      error++;
X	      printf("** Alias %s in group %s is bad!\n", word, groupname);
X	    }
X	  bufptr = NULL;
X	}
X}
X
Xint
Xcan_find(name)
Xchar *name;
X{	
X	/** find name in either hash table...use 'system' variable to
X	    determine if we should look in both or just system....    **/
X
X	register int loc;
X	
X	if (strlen(name) > 20) {
X	  error++;
X	  printf("** Bad alias name: %s.  Too long.\n", name);
X	  return(1);	/* fake out: don't want 2 error messages! */
X	}
X
X	/** system alias table... **/
X	if (hash_table_loaded || system) {
X	  loc = hash_it(name, MAX_SALIASES);
X
X	  while (strcmp(name, shash_table[loc].name) != 0 && 
X                 shash_table[loc].name[0] != '\0')
X	    loc = (loc + 1) % MAX_SALIASES; 
X  
X	  if (strcmp(name, shash_table[loc].name) == 0)
X	    return(1);	/* found it! */
X	}
X
X	if (! system) {	/* okay! Let's check the user alias file! */
X	  loc = hash_it(name, MAX_UALIASES);
X
X	  while (strcmp(name, uhash_table[loc].name) != 0 && 
X                 uhash_table[loc].name[0] != '\0')
X	    loc = (loc + 1) % MAX_UALIASES; 
X
X	  if (strcmp(name, uhash_table[loc].name) == 0)
X	    return(1);	/* found it! */
X	}
X
X	return(0);
X}
X
Xextract_comment(comment, buffer, first, last)
Xchar *comment, *buffer;
Xint first, last;
X{
X	/** Buffer contains a comment, located between the first and last
X	    values.  Copy that into 'comment', but remove leading and
X	    trailing white space.  Note also that it doesn't copy past
X	    a comma, so `unpublishable' comments can be of the form;
X		dave: Dave Taylor, HP Labs : taylor@hplabs
X	    and the output will be "taylor@hplabs (Dave Taylor)".
X	**/
X
X	register int loc = 0; 
X
X	/** first off, skip the LEADING white space... **/
X
X	while (buffer[first] == ' ') first++;
X	
X	/** now let's backup the 'last' value until we hit a non-space **/
X
X	last -= 2;	/* starts at ch AFTER colon.. */
X	while (buffer[last] == ' ') last--;
X
X	/** now a final check to make sure we're still talking about a 
X	    reasonable string (rather than a "joe :: joe@dec" type string) **/
X
X	if (first < last) {
X	  /* one more check - let's find the comma, if present... */
X	  for (loc=first; loc < last; loc++)
X	    if (buffer[loc] == ',') {
X	      last = loc-1;
X	      break;
X	  }
X	  loc = 0;
X	  while (first <= last)
X	    comment[loc++] = buffer[first++];
X	  comment[loc] = '\0';
X	}
X}
END_OF_utils/newalias.c
if test 13123 -ne `wc -c <utils/newalias.c`; then
    echo shar: \"utils/newalias.c\" unpacked with wrong size!?
fi
# end of overwriting check
fi
echo shar: End of archive 13 \(of 19\).
cp /dev/null ark13isdone
DONE=true
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do
    if test ! -f ark${I}isdone ; then
	echo shar: You still need to run archive ${I}.
	DONE=false
    fi
done
if test "$DONE" = "true" ; then
	echo You have unpacked all 19 archives.
	echo "See the Instructions file"
	rm -f ark[1-9]isdone ark[1-9][0-9]isdone
fi
##  End of shell archive.
exit 0