From: syd@dsinc.DSI.COM (Syd Weinstein)
Subject: elm 2.4 Patch #9
Followup-To: poster
Summary: This is an official patch for elm 2.4 system.  Please apply it.
Priority: HIGH
Organization: Datacomp Systems, Inc., Huntingdon Valley, PA 19006
Date: Tue, 17 Nov 1992 19:18:56 GMT

part 1 of a 2 part patch

I think that the code in 'newmbox.c' which handles bad 'Content-length'
entries is incomplete. The file-ptr for the mail file is
backed up, but the file-ptr of the temp file WAS LEFT UNMODIFIED !
From langesw.ssw.de!root Wed Nov 11 14:28:57 1992

When elm copies the temp mailbox back to the mail spool to resync or
quit, it changes to the mailgroup before attempting to diddle in the
mail spool, but when it copies the temp mailbox back to the mail spool
after editing, it forgets to change to mailgroup.  This patch appears
to work, but I haven't exhaustively checked for some path that leaves
the gid set
wrong.  From: dwolfe@pffft.sps.mot.com (Dave Wolfe)

There's an error in two messages in s_aliases.m which causes elm2.4
to core dump when resyncronize aliases. (%s/%s) should be (%c/%c).

This bug doesn't show up unless you use message cataloges, since the default
message in a_quit.c is correct.
From: Jan Djarv <Jan.Djarv@sa.erisoft.se>

Fix how nls emulation lib gencat links prev pointers
From: Jan Djarv <Jan.Djarv@sa.erisoft.se>

The alias message_count isn't set to zero if the last alias has
been deleted from the alias table. As no aliases are reread from
the aliases database the message_count is left as it was before.

Fixed that the function do_newalias() sometimes returns without freeing
the buffer allocated before. The patch adds these free calls.

When you erroneously type a number in your folder elm asks you for
a new current message number. But now if you erase this one number
and leave the string empty elm will set the new current message to
the second message on our sun4! The patch adds a check for an empty
string and returns the current number if no number was entered.
From: vogt@isa.de (Gerald Vogt)

The situation is that the .elm/aliases file is missing, but
.elm/aliases.dir and .elm/aliases.pag exist (isn't serendipity
wonderful?).  The ndbz functions tolerate this and just put a NULL
pointer in the db structure for the data file FILE pointer.  However,
get_one_alias() in listalias and elm doesn't account for the db_open()
succeeding but the dbz_basef field being NULL, so it passes the NULL
pointer to fread().  Detect null and return 0
From: dwolfe@pffft.sps.mot.com (Dave Wolfe)

Clear the screen before displaying MIME:
From: marius@rhi.hi.is (Marius Olafsson)

Add regexp processing to filter.
Add execc operator
From: Jan Djarv <Jan.Djarv@sa.erisoft.se>

Fix:	From rn, say "| patch -p -N -d DIR", where DIR is your elm source
	directory.  Outside of rn, say "cd DIR; patch -p -N <thisarticle".
	If you don't have the patch program, apply the following by hand,
	or get patch (version 2.0, latest patchlevel).

	After patching:
		Do nothing, apply patch 10 next

	If patch indicates that patchlevel is the wrong version, you may need
	to apply one or more previous patches, or the patch may already
	have been applied.  See the patchlevel.h file to find out what has or
	has not been applied.  In any event, don't continue with the patch.

	If you are missing previous patches they can be obtained from our:
	archive server.

	Syd Weinstein
	elm@DSI.COM

	The patches are available from the dsinc archive server
	Send the following message to archive-server@DSI.COM for
	a list of available patches:

	Subject: patch list
	send index elm

Index: hdrs/patchlevel.h
Prereq: "8"
*** ../elm2.4/hdrs/patchlevel.h	Tue Nov 10 15:20:28 1992
--- hdrs/patchlevel.h	Sat Nov 14 16:40:24 1992
***************
*** 1 ****
! #define PATCHLEVEL "8"
--- 1 ----
! #define PATCHLEVEL "9"

Index: Configure
Prereq: 5.6
*** ../elm2.4/Configure	Tue Oct 27 11:17:21 1992
--- Configure	Tue Nov 17 13:19:52 1992
***************
*** 8,14 ****
  # and edit it to reflect your system.  Some packages may include samples
  # of config.h for certain machines, so you might look for one of those.)
  #
! # $Header: /home/syd/elm.rel/RCS/Configure,v 5.6 1992/10/25 02:18:36 syd Exp $
  #
  # Yes, you may rip this off to use in other distribution packages.
  # (Note: this Configure script was generated automatically.  Rather than
--- 8,14 ----
  # and edit it to reflect your system.  Some packages may include samples
  # of config.h for certain machines, so you might look for one of those.)
  #
! # $Header: /home/syd/elm.rel/RCS/Configure,v 5.9 1992/11/17 18:19:47 syd Exp $
  #
  # Yes, you may rip this off to use in other distribution packages.
  # (Note: this Configure script was generated automatically.  Rather than
***************
*** 205,210 ****
--- 205,211 ----
  d_termio=''
  d_termios=''
  d_tz_min=''
+ d_tzname=''
  d_useembed=''
  d_utimbuf=''
  d_vfork=''
***************
*** 303,309 ****
  attrlist="$attrlist $mc68k __STDC__ UTS M_I8086 M_I186 M_I286 M_I386"
  attrlist="$attrlist i186 __m88k__ m88k DGUX __DGUX__ NeXT _AIX"
  pth="/bin /usr/bin /usr/ucb /sbin /usr/sbin /usr/local /usr/local/bin /usr/lbin"
! pth="$pth /usr/5bin /vol/local/bin /etc /usr/bsd /usr/lib /usr/css/lib /lib"
  pth="$pth /usr/local/lib /sys5.3/bin /sys5.3/usr/bin /bsd4.3/bin /bsd4.3/usr/bin /bsd4.3/usr/ucb"
  pth="$pth /usr/convex /usr/mmdf/bin /usr/mmdf/lib ${BSDBASE-/bsd}/usr/ucb ${BSDBASE-/bsd}/bin ${BSDBASE-/bsd}/usr/bin"
  
--- 304,310 ----
  attrlist="$attrlist $mc68k __STDC__ UTS M_I8086 M_I186 M_I286 M_I386"
  attrlist="$attrlist i186 __m88k__ m88k DGUX __DGUX__ NeXT _AIX"
  pth="/bin /usr/bin /usr/ucb /sbin /usr/sbin /usr/local /usr/local/bin /usr/lbin"
! pth="$pth /usr/5bin /vol/local/bin /etc /usr/bsd /usr/lib /usr/ccs/lib /usr/ccs/bin /lib"
  pth="$pth /usr/local/lib /sys5.3/bin /sys5.3/usr/bin /bsd4.3/bin /bsd4.3/usr/bin /bsd4.3/usr/ucb"
  pth="$pth /usr/convex /usr/mmdf/bin /usr/mmdf/lib ${BSDBASE-/bsd}/usr/ucb ${BSDBASE-/bsd}/bin ${BSDBASE-/bsd}/usr/bin"
  
***************
*** 798,804 ****
  if $test -f "$uname"; then
      uname_os=`uname -s`
      uname_rel=`uname -r`
!     uname_rel=`expr "$uname" : "\(...\).*"`
  else
      uname_os=unknown
      uname_rel=unknown
--- 799,805 ----
  if $test -f "$uname"; then
      uname_os=`uname -s`
      uname_rel=`uname -r`
!     uname_rel=`expr "$uname_rel" : "\(...\).*"`
  else
      uname_os=unknown
      uname_rel=unknown
***************
*** 810,816 ****
      echo exit 0 >usg
      echo exit 1 >v7
  elif $contains SIGTSTP foo >/dev/null 2>&1 ; then
!     if $test "$uname_os" = "SunOs" -a "$uname_rel" = "4.1" ; then
  	echo "Looks like SunOs 4.1, a USG system, but we'll see..."
  	echo exit 1 >bsd
  	echo exit 0 >usg
--- 811,817 ----
      echo exit 0 >usg
      echo exit 1 >v7
  elif $contains SIGTSTP foo >/dev/null 2>&1 ; then
!     if $test "(" $uname_os" = "SunOS" -a "$uname_rel" = "4.1")" ; then
  	echo "Looks like SunOs 4.1, a USG system, but we'll see..."
  	echo exit 1 >bsd
  	echo exit 0 >usg
***************
*** 1405,1411 ****
  
  case "$ccflags" in
  '') case "$cc" in
!     *gcc*) dflt='-fpcc-struct-return';;
      *) dflt='';;
      esac
      ;;
--- 1406,1412 ----
  
  case "$ccflags" in
  '') case "$cc" in
!     *gcc*) dflt='-fpcc-struct-return -traditional';;
      *) dflt='';;
      esac
      ;;
***************
*** 3700,3705 ****
--- 3701,3710 ----
      d_tz_min="$undef"
  fi
  $rm -f try.c
+ : see if there is a tzname
+ set tzname d_tzname
+ eval $inlibc
+ 
  : check for valid reply/to fields
  case "$d_useembed" in
  "$define")	dflt=y;;
***************
*** 4516,4521 ****
--- 4521,4527 ----
  d_termio='$d_termio'
  d_termios='$d_termios'
  d_tz_min='$d_tz_min'
+ d_tzname='$d_tzname'
  d_useembed='$d_useembed'
  d_utimbuf='$d_utimbuf'
  d_vfork='$d_vfork'

Index: config.h.SH
*** ../elm2.4/config.h.SH	Tue Oct 27 11:17:22 1992
--- config.h.SH	Sat Nov 14 21:11:40 1992
***************
*** 496,501 ****
--- 496,506 ----
   */
  #$d_tz_min	TZ_MINUTESWEST 	/**/
  
+ /* TZNAME:
+  *	This symbol, if defined, indicates that extern char *tzname[] exists.
+  */
+ #$d_tzname	TZNAME	/**/
+ 
  /* USE_EMBEDDED_ADDRESSES:
   *	This symbol, if defined, indicates that replyto: and from:
   *	headers can be trusted.

Index: Instruct
*** ../elm2.4/Instruct	Sat Oct  3 18:12:32 1992
--- Instruct	Sat Nov 14 20:07:24 1992
***************
*** 1,7 ****
  				Instructions
  				------------
  
! 		        Last Update: $Date: 1992/10/03 22:12:17 $
  
  
  	This file contains instructions on how to create and install
--- 1,7 ----
  				Instructions
  				------------
  
! 		        Last Update: $Date: 1992/11/15 01:07:19 $
  
  
  	This file contains instructions on how to create and install
***************
*** 16,22 ****
  
  	$ sh Configure
  
! 	Answer the questions of that program,, then let it create the
  localized Makefiles and system definition files for you.  When it's
  done you can double check the configuration (or customize it further)
  by reading the Configuration Guide and then rerunning Configure.
--- 16,22 ----
  
  	$ sh Configure
  
! 	Answer the questions of that program, then let it create the
  localized Makefiles and system definition files for you.  When it's
  done you can double check the configuration (or customize it further)
  by reading the Configuration Guide and then rerunning Configure.

Index: MANIFEST
*** ../elm2.4/MANIFEST	Tue Nov 10 15:20:21 1992
--- MANIFEST	Sat Nov 14 20:35:44 1992
***************
*** 43,48 ****
--- 43,49 ----
  filter/filter.c
  filter/lock.c
  filter/parse.c
+ filter/regexp.c
  filter/rules.c
  filter/summarize.c
  filter/utils.c
***************
*** 59,64 ****
--- 60,66 ----
  hdrs/ndbz.h
  hdrs/nl_types.h
  hdrs/patchlevel.h
+ hdrs/regexp.h
  hdrs/s_aliases.h
  hdrs/s_answer.h
  hdrs/s_arepdaem.h

Index: README
*** ../elm2.4/README	Tue Nov 10 15:20:21 1992
--- README	Sat Nov 14 20:06:38 1992
***************
*** 2,8 ****
  
  See the NOTICE and Instruct files for further details.
  
! It is IMPERITIVE that all users rerun newalias after installing
  Elm 2.4 when upgrading from a previous version.  Elm's behavior
  with aliases could be unpredictible if this step is not performed.
  
--- 2,8 ----
  
  See the NOTICE and Instruct files for further details.
  
! It is IMPERATIVE that all users rerun newalias after installing
  Elm 2.4 when upgrading from a previous version.  Elm's behavior
  with aliases could be unpredictible if this step is not performed.
  

Index: doc/Filter.guid
Prereq: 5.1
*** ../elm2.4/doc/Filter.guid	Sat Oct  3 16:54:23 1992
--- doc/Filter.guid	Sat Nov 14 20:41:50 1992
***************
*** 1,4 ****
! .\" @(#)$Id: Filter.guid,v 5.1 1992/10/03 20:51:50 syd Exp $
  .\"
  .\"  A guide to the Elm Filter program
  .\"  format with:
--- 1,4 ----
! .\" @(#)$Id: Filter.guid,v 5.2 1992/11/15 01:41:50 syd Exp $
  .\"
  .\"  A guide to the Elm Filter program
  .\"  format with:
***************
*** 12,17 ****
--- 12,22 ----
  .\"	Syd Weinstein		elm@DSI.COM (dsinc!elm)
  .\"
  .\"  $Log: Filter.guid,v $
+ .\" Revision 5.2  1992/11/15  01:41:50  syd
+ .\" Add regexp processing to filter.
+ .\" Add execc operator
+ .\" From: Jan Djarv <Jan.Djarv@sa.erisoft.se>
+ .\"
  .\" Revision 5.1  1992/10/03  20:51:50  syd
  .\" Initial checkin as of 2.4 Release at PL0
  .\"
***************
*** 145,150 ****
--- 150,159 ----
  The \f2value\f1 is any quoted string that is to be matched against
  or number if ``lines'' is the field being considered.
  .sp
+ The relation ``matches'' (or ``~'') takes an egrep like regular expression 
+ as the \f2value\f1. The regular expression must be enclosed in pairs of ``/''.
+ If you need a ``/'' in your expression write it as ``\\/''.
+ .sp
  Individual conditions are joined together by using the word ``and'',
  and the logic of a condition can be flipped by using ``not'' as the
  first word (e.g. `not subject "joe"').  We'll see more examples of
***************
*** 162,167 ****
--- 171,177 ----
  forward  \f2address\f1
  forwardc  \f2address\f1
  execute  \f2command\f1
+ executec  \f2command\f1
  leave
  .in 0
  .fi
***************
*** 172,190 ****
  \f3forwardc\f1 sends the message to the specified address and leaves a copy in your mailbox; 
  \f3execute\f1 feeds the message to the specified command (or complex
  sequence of commands) as standard input;
  and \f3leave\f1 leaves the message in your mailbox.
  .sp
  NOTE: The execute command assumes that you are doing something
  useful with incoming mail.  The command does \f2not\f1
! automatically append the message to your mail spool.  A
! command such as:
  .sp
!  if (from = "dave") then echo mail from dave > /dev/tty
  .sp
  will result in \f2losing\f1 your mail.  A more suitable
  use for execute would be:
  .sp
!  if (from = "boss") then vacation pat
  .sp
  Foldernames can contain any of a number of macros, too, as we'll see in
  the example ruleset below.  The macros available for the string fields are;
--- 182,201 ----
  \f3forwardc\f1 sends the message to the specified address and leaves a copy in your mailbox; 
  \f3execute\f1 feeds the message to the specified command (or complex
  sequence of commands) as standard input;
+ \f3executec\f1 is like \f3execute\f1 but also leaves a copy in your mailbox;
  and \f3leave\f1 leaves the message in your mailbox.
  .sp
  NOTE: The execute command assumes that you are doing something
  useful with incoming mail.  The command does \f2not\f1
! automatically append the message to your mail spool.  Use executec for that.
! A command such as:
  .sp
!  if (from = "dave") then execute "echo mail from dave > /dev/tty"
  .sp
  will result in \f2losing\f1 your mail.  A more suitable
  use for execute would be:
  .sp
!  if (from = "boss") then execute "vacation pat"
  .sp
  Foldernames can contain any of a number of macros, too, as we'll see in
  the example ruleset below.  The macros available for the string fields are;
***************
*** 208,216 ****
--- 219,236 ----
  .zf
  %t	current hour and minute in HH:MM format
  %y	year (last two digits)
+ %&	the string that matched the last regular expression
+ %1-%9	the i:th subexpression in the last regular expression that matched
  .TE
  .ft 1
  .sp
+ If a message has a subject of \f2This is a test\f1, and the rule is
+ .sp
+ if (subject matches /([a-z]+) a ([a-z]+)/) then "echo %& %1 %2"
+ .sp
+ then %& expands to \f2is a test\f1, %1 expands to \f2is\f1 and %2 expands to
+ \f2test\f1.
+ .sp
  Foldernames may contain a leading '~/' which will be expanded to be
  your home directory.
  .sp
***************
*** 586,599 ****
  .in .5i
  
  .ti -\n(TWu
! 1. The ability to use regular expressions in the patterns.  
! This would be a \f2very\f1 nice feature!
! 
! .ti -\n(TWu
! 2. Perhaps more \f2actions\f1 available (but what?)
  
  .ti -\n(TWu
! 3. Certainly the ability to filter based on any field or combination of
  fields.  
  .in 0
  .ne 5
--- 606,615 ----
  .in .5i
  
  .ti -\n(TWu
! 1. Perhaps more \f2actions\f1 available (but what?)
  
  .ti -\n(TWu
! 2. Certainly the ability to filter based on any field or combination of
  fields.  
  .in 0
  .ne 5

Index: doc/filter.1
*** ../elm2.4/doc/filter.1	Mon Oct 19 13:04:04 1992
--- doc/filter.1	Sat Nov 14 20:48:08 1992
***************
*** 58,63 ****
--- 58,64 ----
          \fBsave  \fIfoldername\fR
          \fBsavecopy \fIfoldername\fR
          \fBexecute  \fIcommand\fR
+         \fBexecutec  \fIcommand\fR
          \fBforward  \fIaddress\fR
          \fBleave\fR
  

Index: filter/regexp.c
*** /dev/null	Mon Nov 16 22:43:59 1992
--- filter/regexp.c	Mon Nov 16 23:10:02 1992
***************
*** 0 ****
--- 1,1214 ----
+ 
+ /* $Id: regexp.c,v 5.4 1992/11/17 18:25:42 syd Exp $ */
+ 
+ /*******************************************************************************
+  *  The Elm Mail System  -  $Revision: 5.4 $   $State: Exp $
+  *
+  * 			Copyright (c) 1992 USENET Community Trust
+  *******************************************************************************
+  * Bug reports, patches, comments, suggestions should be sent to:
+  *
+  *	Syd Weinstein, Elm Coordinator
+  *	elm@DSI.COM			dsinc!elm
+  *
+  *******************************************************************************
+  * $Log: regexp.c,v $
+  * Revision 5.4  1992/11/17  18:25:42  syd
+  * change to use index instead of strchr
+  * From: Syd
+  *
+  * Revision 5.3  1992/11/17  04:10:01  syd
+  * The changes to filter/regexp.c are to correct compiler warnings about
+  * unreachable statements.
+  *
+  * The changes to filter/rules.c are to correct the fact that we are passing
+  * a pointer to a condition_rec structore to a function expecting a pointer to
+  * a character string.
+  * From: Tom Moore <tmoore@fievel.DaytonOH.NCR.COM>
+  *
+  * Revision 5.2  1992/11/15  01:39:31  syd
+  * add proper headers
+  *
+  *
+  ******************************************************************************/
+ 
+ /*
+  * regcomp and regexec -- regsub and regerror are elsewhere
+  *
+  *	Copyright (c) 1986 by University of Toronto.
+  *	Written by Henry Spencer.  Not derived from licensed software.
+  *
+  *	Permission is granted to anyone to use this software for any
+  *	purpose on any computer system, and to redistribute it freely,
+  *	subject to the following restrictions:
+  *
+  *	1. The author is not responsible for the consequences of use of
+  *		this software, no matter how awful, even if they arise
+  *		from defects in it.
+  *
+  *	2. The origin of this software must not be misrepresented, either
+  *		by explicit claim or by omission.
+  *
+  *	3. Altered versions must be plainly marked as such, and must not
+  *		be misrepresented as being the original software.
+  *
+  * Beware that some of this code is subtly aware of the way operator
+  * precedence is structured in regular expressions.  Serious changes in
+  * regular-expression syntax might require a total rethink.
+  */
+ 
+ /*
+  * Altered by Jan D. (Jan.Djarv@sa.erisoft.se) for use in elm:s filter
+  */
+ #include "defs.h"
+ #include "regexp.h"
+ #include <stdio.h>
+ 
+ /* #include "regmagic.h" -- Inlined here (next 5 lines) */
+ /*
+  * The first byte of the regexp internal "program" is actually this magic
+  * number; the start node begins in the second byte.
+  */
+ #define	MAGIC	0234
+ 
+ /*
+  * The "internal use only" fields in regexp.h are present to pass info from
+  * compile to execute that permits the execute phase to run lots faster on
+  * simple cases.  They are:
+  *
+  * regstart	char that must begin a match; '\0' if none obvious
+  * reganch	is the match anchored (at beginning-of-line only)?
+  * regmust	string (pointer into program) that match must include, or NULL
+  * regmlen	length of regmust string
+  *
+  * Regstart and reganch permit very fast decisions on suitable starting points
+  * for a match, cutting down the work a lot.  Regmust permits fast rejection
+  * of lines that cannot possibly match.  The regmust tests are costly enough
+  * that regcomp() supplies a regmust only if the r.e. contains something
+  * potentially expensive (at present, the only such thing detected is * or +
+  * at the start of the r.e., which can involve a lot of backup).  Regmlen is
+  * supplied because the test in regexec() needs it and regcomp() is computing
+  * it anyway.
+  */
+ 
+ /*
+  * Structure for regexp "program".  This is essentially a linear encoding
+  * of a nondeterministic finite-state machine (aka syntax charts or
+  * "railroad normal form" in parsing technology).  Each node is an opcode
+  * plus a "next" pointer, possibly plus an operand.  "Next" pointers of
+  * all nodes except BRANCH implement concatenation; a "next" pointer with
+  * a BRANCH on both ends of it is connecting two alternatives.  (Here we
+  * have one of the subtle syntax dependencies:  an individual BRANCH (as
+  * opposed to a collection of them) is never concatenated with anything
+  * because of operator precedence.)  The operand of some types of node is
+  * a literal string; for others, it is a node leading into a sub-FSM.  In
+  * particular, the operand of a BRANCH node is the first node of the branch.
+  * (NB this is *not* a tree structure:  the tail of the branch connects
+  * to the thing following the set of BRANCHes.)  The opcodes are:
+  */
+ 
+ /* definition	number	opnd?	meaning */
+ #define	END	0	/* no	End of program. */
+ #define	BOL	1	/* no	Match "" at beginning of line. */
+ #define	EOL	2	/* no	Match "" at end of line. */
+ #define	ANY	3	/* no	Match any one character. */
+ #define	ANYOF	4	/* str	Match any character in this string. */
+ #define	ANYBUT	5	/* str	Match any character not in this string. */
+ #define	BRANCH	6	/* node	Match this alternative, or the next... */
+ #define	BACK	7	/* no	Match "", "next" ptr points backward. */
+ #define	EXACTLY	8	/* str	Match this string. */
+ #define	NOTHING	9	/* no	Match empty string. */
+ #define	STAR	10	/* node	Match this (simple) thing 0 or more times. */
+ #define	PLUS	11	/* node	Match this (simple) thing 1 or more times. */
+ #define	OPEN	20	/* no	Mark this point in input as start of #n. */
+ 			/*	OPEN+1 is number 1, etc. */
+ #define	CLOSE	30	/* no	Analogous to OPEN. */
+ 
+ /*
+  * Opcode notes:
+  *
+  * BRANCH	The set of branches constituting a single choice are hooked
+  *		together with their "next" pointers, since precedence prevents
+  *		anything being concatenated to any individual branch.  The
+  *		"next" pointer of the last BRANCH in a choice points to the
+  *		thing following the whole choice.  This is also where the
+  *		final "next" pointer of each individual branch points; each
+  *		branch starts with the operand node of a BRANCH node.
+  *
+  * BACK		Normal "next" pointers all implicitly point forward; BACK
+  *		exists to make loop structures possible.
+  *
+  * STAR,PLUS	'?', and complex '*' and '+', are implemented as circular
+  *		BRANCH structures using BACK.  Simple cases (one character
+  *		per match) are implemented with STAR and PLUS for speed
+  *		and to minimize recursive plunges.
+  *
+  * OPEN,CLOSE	...are numbered at compile time.
+  */
+ 
+ /*
+  * A node is one char of opcode followed by two chars of "next" pointer.
+  * "Next" pointers are stored as two 8-bit pieces, high order first.  The
+  * value is a positive offset from the opcode of the node containing it.
+  * An operand, if any, simply follows the node.  (Note that much of the
+  * code generation knows about this implicit relationship.)
+  *
+  * Using two bytes for the "next" pointer is vast overkill for most things,
+  * but allows patterns to get big without disasters.
+  */
+ #define	OP(p)	(*(p))
+ #define	NEXT(p)	(((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
+ #define	OPERAND(p)	((p) + 3)
+ 
+ /*
+  * See regmagic.h for one further detail of program structure.
+  */
+ 
+ 
+ /*
+  * Utility definitions.
+  */
+ #ifndef CHARBITS
+ #define	UCHARAT(p)	((int)*(unsigned char *)(p))
+ #else
+ #define	UCHARAT(p)	((int)*(p)&CHARBITS)
+ #endif
+ 
+ #define	FAIL(m)	{ regerror(m); return(NULL); }
+ #define	ISMULT(c)	((c) == '*' || (c) == '+' || (c) == '?')
+ #define	META	"^$.[()|?+*\\"
+ 
+ /*
+  * Flags to be passed up and down.
+  */
+ #define	HASWIDTH	01	/* Known never to match null string. */
+ #define	SIMPLE		02	/* Simple enough to be STAR/PLUS operand. */
+ #define	SPSTART		04	/* Starts with * or +. */
+ #define	WORST		0	/* Worst case. */
+ 
+ /*
+  * Global work variables for regcomp().
+  */
+ static char *regparse;		/* Input-scan pointer. */
+ static int regnpar;		/* () count. */
+ static char regdummy;
+ static char *regcode;		/* Code-emit pointer; &regdummy = don't. */
+ static long regsize;		/* Code size. */
+ 
+ /*
+  * Forward declarations for regcomp()'s friends.
+  */
+ #ifndef STATIC
+ #define	STATIC	static
+ #endif
+ STATIC char *reg();
+ STATIC char *regbranch();
+ STATIC char *regpiece();
+ STATIC char *regatom();
+ STATIC char *regnode();
+ STATIC char *regnext();
+ STATIC void regc();
+ STATIC void reginsert();
+ STATIC void regtail();
+ STATIC void regoptail();
+ 
+ /*
+  - regcomp - compile a regular expression into internal code
+  *
+  * We can't allocate space until we know how big the compiled form will be,
+  * but we can't compile it (and thus know how big it is) until we've got a
+  * place to put the code.  So we cheat:  we compile it twice, once with code
+  * generation turned off and size counting turned on, and once "for real".
+  * This also means that we don't allocate space until we are sure that the
+  * thing really will compile successfully, and we never have to move the
+  * code and thus invalidate pointers into it.  (Note that it has to be in
+  * one piece because free() must be able to free it all.)
+  *
+  * Beware that the optimization-preparation code in here knows about some
+  * of the structure of the compiled regexp.
+  */
+ regexp *
+ regcomp(exp)
+ char *exp;
+ {
+ 	register regexp *r;
+ 	register char *scan;
+ 	register char *longest;
+ 	register int len;
+ 	int flags;
+ 
+ 	if (exp == NULL)
+ 		FAIL("NULL argument");
+ 
+ 	/* First pass: determine size, legality. */
+ 	regparse = exp;
+ 	regnpar = 1;
+ 	regsize = 0L;
+ 	regcode = &regdummy;
+ 	regc(MAGIC);
+ 	if (reg(0, &flags) == NULL)
+ 		return(NULL);
+ 
+ 	/* Small enough for pointer-storage convention? */
+ 	if (regsize >= 32767L)		/* Probably could be 65535L. */
+ 		FAIL("regexp too big");
+ 
+ 	/* Allocate space. */
+ 	r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize);
+ 	if (r == NULL)
+ 		FAIL("out of space");
+ 
+ 	/* Second pass: emit code. */
+ 	regparse = exp;
+ 	regnpar = 1;
+ 	regcode = r->program;
+ 	regc(MAGIC);
+ 	if (reg(0, &flags) == NULL)
+ 		return(NULL);
+ 
+ 	/* Dig out information for optimizations. */
+ 	r->regstart = '\0';	/* Worst-case defaults. */
+ 	r->reganch = 0;
+ 	r->regmust = NULL;
+ 	r->regmlen = 0;
+ 	scan = r->program+1;			/* First BRANCH. */
+ 	if (OP(regnext(scan)) == END) {		/* Only one top-level choice. */
+ 		scan = OPERAND(scan);
+ 
+ 		/* Starting-point info. */
+ 		if (OP(scan) == EXACTLY)
+ 			r->regstart = *OPERAND(scan);
+ 		else if (OP(scan) == BOL)
+ 			r->reganch++;
+ 
+ 		/*
+ 		 * If there's something expensive in the r.e., find the
+ 		 * longest literal string that must appear and make it the
+ 		 * regmust.  Resolve ties in favor of later strings, since
+ 		 * the regstart check works with the beginning of the r.e.
+ 		 * and avoiding duplication strengthens checking.  Not a
+ 		 * strong reason, but sufficient in the absence of others.
+ 		 */
+ 		if (flags&SPSTART) {
+ 			longest = NULL;
+ 			len = 0;
+ 			for (; scan != NULL; scan = regnext(scan))
+ 				if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
+ 					longest = OPERAND(scan);
+ 					len = strlen(OPERAND(scan));
+ 				}
+ 			r->regmust = longest;
+ 			r->regmlen = len;
+ 		}
+ 	}
+ 
+ 	return(r);
+ }
+ 
+ /*
+  - reg - regular expression, i.e. main body or parenthesized thing
+  *
+  * Caller must absorb opening parenthesis.
+  *
+  * Combining parenthesis handling with the base level of regular expression
+  * is a trifle forced, but the need to tie the tails of the branches to what
+  * follows makes it hard to avoid.
+  */
+ static char *
+ reg(paren, flagp)
+ int paren;			/* Parenthesized? */
+ int *flagp;
+ {
+ 	register char *ret;
+ 	register char *br;
+ 	register char *ender;
+ 	register int parno;
+ 	int flags;
+ 
+ 	*flagp = HASWIDTH;	/* Tentatively. */
+ 
+ 	/* Make an OPEN node, if parenthesized. */
+ 	if (paren) {
+ 		if (regnpar >= NSUBEXP)
+ 			FAIL("too many ()");
+ 		parno = regnpar;
+ 		regnpar++;
+ 		ret = regnode(OPEN+parno);
+ 	} else
+ 		ret = NULL;
+ 
+ 	/* Pick up the branches, linking them together. */
+ 	br = regbranch(&flags);
+ 	if (br == NULL)
+ 		return(NULL);
+ 	if (ret != NULL)
+ 		regtail(ret, br);	/* OPEN -> first. */
+ 	else
+ 		ret = br;
+ 	if (!(flags&HASWIDTH))
+ 		*flagp &= ~HASWIDTH;
+ 	*flagp |= flags&SPSTART;
+ 	while (*regparse == '|') {
+ 		regparse++;
+ 		br = regbranch(&flags);
+ 		if (br == NULL)
+ 			return(NULL);
+ 		regtail(ret, br);	/* BRANCH -> BRANCH. */
+ 		if (!(flags&HASWIDTH))
+ 			*flagp &= ~HASWIDTH;
+ 		*flagp |= flags&SPSTART;
+ 	}
+ 
+ 	/* Make a closing node, and hook it on the end. */
+ 	ender = regnode((paren) ? CLOSE+parno : END);	
+ 	regtail(ret, ender);
+ 
+ 	/* Hook the tails of the branches to the closing node. */
+ 	for (br = ret; br != NULL; br = regnext(br))
+ 		regoptail(br, ender);
+ 
+ 	/* Check for proper termination. */
+ 	if (paren && *regparse++ != ')') {
+ 		FAIL("unmatched ()");
+ 	} else if (!paren && *regparse != '\0') {
+ 		if (*regparse == ')') {
+ 			FAIL("unmatched ()");
+ 		} else
+ 			FAIL("junk on end");	/* "Can't happen". */
+ 		/* NOTREACHED */
+ 	}
+ 
+ 	return(ret);
+ }
+ 
+ /*
+  - regbranch - one alternative of an | operator
+  *
+  * Implements the concatenation operator.
+  */
+ static char *
+ regbranch(flagp)
+ int *flagp;
+ {
+ 	register char *ret;
+ 	register char *chain;
+ 	register char *latest;
+ 	int flags;
+ 
+ 	*flagp = WORST;		/* Tentatively. */
+ 
+ 	ret = regnode(BRANCH);
+ 	chain = NULL;
+ 	while (*regparse != '\0' && *regparse != '|' && *regparse != ')') {
+ 		latest = regpiece(&flags);
+ 		if (latest == NULL)
+ 			return(NULL);
+ 		*flagp |= flags&HASWIDTH;
+ 		if (chain == NULL)	/* First piece. */
+ 			*flagp |= flags&SPSTART;
+ 		else
+ 			regtail(chain, latest);
+ 		chain = latest;
+ 	}
+ 	if (chain == NULL)	/* Loop ran zero times. */
+ 		(void) regnode(NOTHING);
+ 
+ 	return(ret);
+ }
+ 
+ /*
+  - regpiece - something followed by possible [*+?]
+  *
+  * Note that the branching code sequences used for ? and the general cases
+  * of * and + are somewhat optimized:  they use the same NOTHING node as
+  * both the endmarker for their branch list and the body of the last branch.
+  * It might seem that this node could be dispensed with entirely, but the
+  * endmarker role is not redundant.
+  */
+ static char *
+ regpiece(flagp)
+ int *flagp;
+ {
+ 	register char *ret;
+ 	register char op;
+ 	register char *next;
+ 	int flags;
+ 
+ 	ret = regatom(&flags);
+ 	if (ret == NULL)
+ 		return(NULL);
+ 
+ 	op = *regparse;
+ 	if (!ISMULT(op)) {
+ 		*flagp = flags;
+ 		return(ret);
+ 	}
+ 
+ 	if (!(flags&HASWIDTH) && op != '?')
+ 		FAIL("*+ operand could be empty");
+ 	*flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH);
+ 
+ 	if (op == '*' && (flags&SIMPLE))
+ 		reginsert(STAR, ret);
+ 	else if (op == '*') {
+ 		/* Emit x* as (x&|), where & means "self". */
+ 		reginsert(BRANCH, ret);			/* Either x */
+ 		regoptail(ret, regnode(BACK));		/* and loop */
+ 		regoptail(ret, ret);			/* back */
+ 		regtail(ret, regnode(BRANCH));		/* or */
+ 		regtail(ret, regnode(NOTHING));		/* null. */
+ 	} else if (op == '+' && (flags&SIMPLE))
+ 		reginsert(PLUS, ret);
+ 	else if (op == '+') {
+ 		/* Emit x+ as x(&|), where & means "self". */
+ 		next = regnode(BRANCH);			/* Either */
+ 		regtail(ret, next);
+ 		regtail(regnode(BACK), ret);		/* loop back */
+ 		regtail(next, regnode(BRANCH));		/* or */
+ 		regtail(ret, regnode(NOTHING));		/* null. */
+ 	} else if (op == '?') {
+ 		/* Emit x? as (x|) */
+ 		reginsert(BRANCH, ret);			/* Either x */
+ 		regtail(ret, regnode(BRANCH));		/* or */
+ 		next = regnode(NOTHING);		/* null. */
+ 		regtail(ret, next);
+ 		regoptail(ret, next);
+ 	}
+ 	regparse++;
+ 	if (ISMULT(*regparse))
+ 		FAIL("nested *?+");
+ 
+ 	return(ret);
+ }
+ 
+ /*
+  - regatom - the lowest level
+  *
+  * Optimization:  gobbles an entire sequence of ordinary characters so that
+  * it can turn them into a single node, which is smaller to store and
+  * faster to run.  Backslashed characters are exceptions, each becoming a
+  * separate node; the code is simpler that way and it's not worth fixing.
+  */
+ static char *
+ regatom(flagp)
+ int *flagp;
+ {
+ 	register char *ret;
+ 	int flags;
+ 
+ 	*flagp = WORST;		/* Tentatively. */
+ 
+ 	switch (*regparse++) {
+ 	case '^':
+ 		ret = regnode(BOL);
+ 		break;
+ 	case '$':
+ 		ret = regnode(EOL);
+ 		break;
+ 	case '.':
+ 		ret = regnode(ANY);
+ 		*flagp |= HASWIDTH|SIMPLE;
+ 		break;
+ 	case '[': {
+ 			register int class;
+ 			register int classend;
+ 
+ 			if (*regparse == '^') {	/* Complement of range. */
+ 				ret = regnode(ANYBUT);
+ 				regparse++;
+ 			} else
+ 				ret = regnode(ANYOF);
+ 			if (*regparse == ']' || *regparse == '-')
+ 				regc(*regparse++);
+ 			while (*regparse != '\0' && *regparse != ']') {
+ 				if (*regparse == '-') {
+ 					regparse++;
+ 					if (*regparse == ']' || *regparse == '\0')
+ 						regc('-');
+ 					else {
+ 						class = UCHARAT(regparse-2)+1;
+ 						classend = UCHARAT(regparse);
+ 						if (class > classend+1)
+ 							FAIL("invalid [] range");
+ 						for (; class <= classend; class++)
+ 							regc(class);
+ 						regparse++;
+ 					}
+ 				} else
+ 					regc(*regparse++);
+ 			}
+ 			regc('\0');
+ 			if (*regparse != ']')
+ 				FAIL("unmatched []");
+ 			regparse++;
+ 			*flagp |= HASWIDTH|SIMPLE;
+ 		}
+ 		break;
+ 	case '(':
+ 		ret = reg(1, &flags);
+ 		if (ret == NULL)
+ 			return(NULL);
+ 		*flagp |= flags&(HASWIDTH|SPSTART);
+ 		break;
+ 	case '\0':
+ 	case '|':
+ 	case ')':
+ 		FAIL("internal urp");	/* Supposed to be caught earlier. */
+ 	case '?':
+ 	case '+':
+ 	case '*':
+ 		FAIL("?+* follows nothing");
+ 	case '\\':
+ 		if (*regparse == '\0')
+ 			FAIL("trailing \\");
+ 		ret = regnode(EXACTLY);
+ 		regc(*regparse++);
+ 		regc('\0');
+ 		*flagp |= HASWIDTH|SIMPLE;
+ 		break;
+ 	default: {
+ 			register int len;
+ 			register char ender;
+ 
+ 			regparse--;
+ 			len = strcspn(regparse, META);
+ 			if (len <= 0)
+ 				FAIL("internal disaster");
+ 			ender = *(regparse+len);
+ 			if (len > 1 && ISMULT(ender))
+ 				len--;		/* Back off clear of ?+* operand. */
+ 			*flagp |= HASWIDTH;
+ 			if (len == 1)
+ 				*flagp |= SIMPLE;
+ 			ret = regnode(EXACTLY);
+ 			while (len > 0) {
+ 				regc(*regparse++);
+ 				len--;
+ 			}
+ 			regc('\0');
+ 		}
+ 		break;
+ 	}
+ 
+ 	return(ret);
+ }
+ 
+ /*
+  - regnode - emit a node
+  */
+ static char *			/* Location. */
+ regnode(op)
+ char op;
+ {
+ 	register char *ret;
+ 	register char *ptr;
+ 
+ 	ret = regcode;
+ 	if (ret == &regdummy) {
+ 		regsize += 3;
+ 		return(ret);
+ 	}
+ 
+ 	ptr = ret;
+ 	*ptr++ = op;
+ 	*ptr++ = '\0';		/* Null "next" pointer. */
+ 	*ptr++ = '\0';
+ 	regcode = ptr;
+ 
+ 	return(ret);
+ }
+ 
+ /*
+  - regc - emit (if appropriate) a byte of code
+  */
+ static void
+ regc(b)
+ char b;
+ {
+ 	if (regcode != &regdummy)
+ 		*regcode++ = b;
+ 	else
+ 		regsize++;
+ }
+ 
+ /*
+  - reginsert - insert an operator in front of already-emitted operand
+  *
+  * Means relocating the operand.
+  */
+ static void
+ reginsert(op, opnd)
+ char op;
+ char *opnd;
+ {
+ 	register char *src;
+ 	register char *dst;
+ 	register char *place;
+ 
+ 	if (regcode == &regdummy) {
+ 		regsize += 3;
+ 		return;
+ 	}
+ 
+ 	src = regcode;
+ 	regcode += 3;
+ 	dst = regcode;
+ 	while (src > opnd)
+ 		*--dst = *--src;
+ 
+ 	place = opnd;		/* Op node, where operand used to be. */
+ 	*place++ = op;
+ 	*place++ = '\0';
+ 	*place++ = '\0';
+ }
+ 
+ /*
+  - regtail - set the next-pointer at the end of a node chain
+  */
+ static void
+ regtail(p, val)
+ char *p;
+ char *val;
+ {
+ 	register char *scan;
+ 	register char *temp;
+ 	register int offset;
+ 
+ 	if (p == &regdummy)
+ 		return;
+ 
+ 	/* Find last node. */
+ 	scan = p;
+ 	for (;;) {
+ 		temp = regnext(scan);
+ 		if (temp == NULL)
+ 			break;
+ 		scan = temp;
+ 	}
+ 
+ 	if (OP(scan) == BACK)
+ 		offset = scan - val;
+ 	else
+ 		offset = val - scan;
+ 	*(scan+1) = (offset>>8)&0377;
+ 	*(scan+2) = offset&0377;
+ }
+ 
+ /*
+  - regoptail - regtail on operand of first argument; nop if operandless
+  */
+ static void
+ regoptail(p, val)
+ char *p;
+ char *val;
+ {
+ 	/* "Operandless" and "op != BRANCH" are synonymous in practice. */
+ 	if (p == NULL || p == &regdummy || OP(p) != BRANCH)
+ 		return;
+ 	regtail(OPERAND(p), val);
+ }
+ 
+ /*
+  * regexec and friends
+  */
+ 
+ /*
+  * Global work variables for regexec().
+  */
+ static char *reginput;		/* String-input pointer. */
+ static char *regbol;		/* Beginning of input, for ^ check. */
+ static char **regstartp;	/* Pointer to startp array. */
+ static char **regendp;		/* Ditto for endp. */
+ 
+ /*
+  * Forwards.
+  */
+ STATIC int regtry();
+ STATIC int regmatch();
+ STATIC int regrepeat();
+ 
+ #ifdef DEBUG
+ int regnarrate = 0;
+ void regdump();
+ STATIC char *regprop();
+ #endif
+ 
+ /*
+  - regexec - match a regexp against a string
+  */
+ int
+ regexec(prog, string)
+ register regexp *prog;
+ register char *string;
+ {
+ 	register char *s;
+ 
+ 	/* Be paranoid... */
+ 	if (prog == NULL || string == NULL) {
+ 		regerror("NULL parameter");
+ 		return(0);
+ 	}
+ 
+ 	/* Check validity of program. */
+ 	if (UCHARAT(prog->program) != MAGIC) {
+ 		regerror("corrupted program");
+ 		return(0);
+ 	}
+ 
+ 	/* If there is a "must appear" string, look for it. */
+ 	if (prog->regmust != NULL) {
+ 		s = string;
+ 		while ((s = index(s, prog->regmust[0])) != NULL) {
+ 			if (strncmp(s, prog->regmust, prog->regmlen) == 0)
+ 				break;	/* Found it. */
+ 			s++;
+ 		}
+ 		if (s == NULL)	/* Not present. */
+ 			return(0);
+ 	}
+ 
+ 	/* Mark beginning of line for ^ . */
+ 	regbol = string;
+ 
+ 	/* Simplest case:  anchored match need be tried only once. */
+ 	if (prog->reganch)
+ 		return(regtry(prog, string));
+ 
+ 	/* Messy cases:  unanchored match. */
+ 	s = string;
+ 	if (prog->regstart != '\0')
+ 		/* We know what char it must start with. */
+ 		while ((s = index(s, prog->regstart)) != NULL) {
+ 			if (regtry(prog, s))
+ 				return(1);
+ 			s++;
+ 		}
+ 	else
+ 		/* We don't -- general case. */
+ 		do {
+ 			if (regtry(prog, s))
+ 				return(1);
+ 		} while (*s++ != '\0');
+ 
+ 	/* Failure. */
+ 	return(0);
+ }
+ 
+ /*
+  - regtry - try match at specific point
+  */
+ static int			/* 0 failure, 1 success */
+ regtry(prog, string)
+ regexp *prog;
+ char *string;
+ {
+ 	register int i;
+ 	register char **sp;
+ 	register char **ep;
+ 
+ 	reginput = string;
+ 	regstartp = prog->startp;
+ 	regendp = prog->endp;
+ 
+ 	sp = prog->startp;
+ 	ep = prog->endp;
+ 	for (i = NSUBEXP; i > 0; i--) {
+ 		*sp++ = NULL;
+ 		*ep++ = NULL;
+ 	}
+ 	if (regmatch(prog->program + 1)) {
+ 		prog->startp[0] = string;
+ 		prog->endp[0] = reginput;
+ 		return(1);
+ 	} else
+ 		return(0);
+ }
+ 
+ /*
+  - regmatch - main matching routine
+  *
+  * Conceptually the strategy is simple:  check to see whether the current
+  * node matches, call self recursively to see whether the rest matches,
+  * and then act accordingly.  In practice we make some effort to avoid
+  * recursion, in particular by going through "ordinary" nodes (that don't
+  * need to know whether the rest of the match failed) by a loop instead of
+  * by recursion.
+  */
+ static int			/* 0 failure, 1 success */
+ regmatch(prog)
+ char *prog;
+ {
+ 	register char *scan;	/* Current node. */
+ 	char *next;		/* Next node. */
+ 
+ 	scan = prog;
+ #ifdef DEBUG
+ 	if (scan != NULL && regnarrate)
+ 		fprintf(stderr, "%s(\n", regprop(scan));
+ #endif
+ 	while (scan != NULL) {
+ #ifdef DEBUG
+ 		if (regnarrate)
+ 			fprintf(stderr, "%s...\n", regprop(scan));
+ #endif
+ 		next = regnext(scan);
+ 
+ 		switch (OP(scan)) {
+ 		case BOL:
+ 			if (reginput != regbol)
+ 				return(0);
+ 			break;
+ 		case EOL:
+ 			if (*reginput != '\0')
+ 				return(0);
+ 			break;
+ 		case ANY:
+ 			if (*reginput == '\0')
+ 				return(0);
+ 			reginput++;
+ 			break;
+ 		case EXACTLY: {
+ 				register int len;
+ 				register char *opnd;
+ 
+ 				opnd = OPERAND(scan);
+ 				/* Inline the first character, for speed. */
+ 				if (*opnd != *reginput)
+ 					return(0);
+ 				len = strlen(opnd);
+ 				if (len > 1 && strncmp(opnd, reginput, len) != 0)
+ 					return(0);
+ 				reginput += len;
+ 			}
+ 			break;
+ 		case ANYOF:
+  			if (*reginput == '\0' || index(OPERAND(scan), *reginput) == NULL)
+ 				return(0);
+ 			reginput++;
+ 			break;
+ 		case ANYBUT:
+  			if (*reginput == '\0' || index(OPERAND(scan), *reginput) != NULL)
+ 				return(0);
+ 			reginput++;
+ 			break;
+ 		case NOTHING:
+ 			break;
+ 		case BACK:
+ 			break;
+ 		case OPEN+1:
+ 		case OPEN+2:
+ 		case OPEN+3:
+ 		case OPEN+4:
+ 		case OPEN+5:
+ 		case OPEN+6:
+ 		case OPEN+7:
+ 		case OPEN+8:
+ 		case OPEN+9: {
+ 				register int no;
+ 				register char *save;
+ 
+ 				no = OP(scan) - OPEN;
+ 				save = reginput;
+ 
+ 				if (regmatch(next)) {
+ 					/*
+ 					 * Don't set startp if some later
+ 					 * invocation of the same parentheses
+ 					 * already has.
+ 					 */
+ 					if (regstartp[no] == NULL)
+ 						regstartp[no] = save;
+ 					return(1);
+ 				} else
+ 					return(0);
+ 			}
+ 			break;
+ 		case CLOSE+1:
+ 		case CLOSE+2:
+ 		case CLOSE+3:
+ 		case CLOSE+4:
+ 		case CLOSE+5:
+ 		case CLOSE+6:
+ 		case CLOSE+7:
+ 		case CLOSE+8:
+ 		case CLOSE+9: {
+ 				register int no;
+ 				register char *save;
+ 
+ 				no = OP(scan) - CLOSE;
+ 				save = reginput;
+ 
+ 				if (regmatch(next)) {
+ 					/*
+ 					 * Don't set endp if some later
+ 					 * invocation of the same parentheses
+ 					 * already has.
+ 					 */
+ 					if (regendp[no] == NULL)
+ 						regendp[no] = save;
+ 					return(1);
+ 				} else
+ 					return(0);
+ 			}
+ 			break;
+ 		case BRANCH: {
+ 				register char *save;
+ 
+ 				if (OP(next) != BRANCH)		/* No choice. */
+ 					next = OPERAND(scan);	/* Avoid recursion. */
+ 				else {
+ 					do {
+ 						save = reginput;
+ 						if (regmatch(OPERAND(scan)))
+ 							return(1);
+ 						reginput = save;
+ 						scan = regnext(scan);
+ 					} while (scan != NULL && OP(scan) == BRANCH);
+ 					return(0);
+ 					/* NOTREACHED */
+ 				}
+ 			}
+ 			break;
+ 		case STAR:
+ 		case PLUS: {
+ 				register char nextch;
+ 				register int no;
+ 				register char *save;
+ 				register int mmin;
+ 
+ 				/*
+ 				 * Lookahead to avoid useless match attempts
+ 				 * when we know what character comes next.
+ 				 */
+ 				nextch = '\0';
+ 				if (OP(next) == EXACTLY)
+ 					nextch = *OPERAND(next);
+ 				mmin = (OP(scan) == STAR) ? 0 : 1;
+ 				save = reginput;
+ 				no = regrepeat(OPERAND(scan));
+ 				while (no >= mmin) {
+ 					/* If it could work, try it. */
+ 					if (nextch == '\0' || *reginput == nextch)
+ 						if (regmatch(next))
+ 							return(1);
+ 					/* Couldn't or didn't -- back up. */
+ 					no--;
+ 					reginput = save + no;
+ 				}
+ 				return(0);
+ 			}
+ 		case END:
+ 			return(1);	/* Success! */
+ 		default:
+ 			regerror("memory corruption");
+ 			return(0);
+ 		}
+ 
+ 		scan = next;
+ 	}
+ 
+ 	/*
+ 	 * We get here only if there's trouble -- normally "case END" is
+ 	 * the terminating point.
+ 	 */
+ 	regerror("corrupted pointers");
+ 	return(0);
+ }
+ 
+ /*
+  - regrepeat - repeatedly match something simple, report how many
+  */
+ static int
+ regrepeat(p)
+ char *p;
+ {
+ 	register int count = 0;
+ 	register char *scan;
+ 	register char *opnd;
+ 
+ 	scan = reginput;
+ 	opnd = OPERAND(p);
+ 	switch (OP(p)) {
+ 	case ANY:
+ 		count = strlen(scan);
+ 		scan += count;
+ 		break;
+ 	case EXACTLY:
+ 		while (*opnd == *scan) {
+ 			count++;
+ 			scan++;
+ 		}
+ 		break;
+ 	case ANYOF:
+ 		while (*scan != '\0' && index(opnd, *scan) != NULL) {
+ 			count++;
+ 			scan++;
+ 		}
+ 		break;
+ 	case ANYBUT:
+ 		while (*scan != '\0' && index(opnd, *scan) == NULL) {
+ 			count++;
+ 			scan++;
+ 		}
+ 		break;
+ 	default:		/* Oh dear.  Called inappropriately. */
+ 		regerror("internal foulup");
+ 		count = 0;	/* Best compromise. */
+ 		break;
+ 	}
+ 	reginput = scan;
+ 
+ 	return(count);
+ }
+ 
+ /*
+  - regnext - dig the "next" pointer out of a node
+  */
+ static char *
+ regnext(p)
+ register char *p;
+ {
+ 	register int offset;
+ 
+ 	if (p == &regdummy)
+ 		return(NULL);
+ 
+ 	offset = NEXT(p);
+ 	if (offset == 0)
+ 		return(NULL);
+ 
+ 	if (OP(p) == BACK)
+ 		return(p-offset);
+ 	else
+ 		return(p+offset);
+ }
+ 
+ #ifdef DEBUG
+ 
+ STATIC char *regprop();
+ 
+ /*
+  - regdump - dump a regexp onto stdout in vaguely comprehensible form
+  */
+ void
+ regdump(r)
+ regexp *r;
+ {
+ 	register char *s;
+ 	register char op = EXACTLY;	/* Arbitrary non-END op. */
+ 	register char *next;
+ 
+ 
+ 	s = r->program + 1;
+ 	while (op != END) {	/* While that wasn't END last time... */
+ 		op = OP(s);
+ 		printf("%2d%s", s-r->program, regprop(s));	/* Where, what. */
+ 		next = regnext(s);
+ 		if (next == NULL)		/* Next ptr. */
+ 			printf("(0)");
+ 		else 
+ 			printf("(%d)", (s-r->program)+(next-s));
+ 		s += 3;
+ 		if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
+ 			/* Literal string, where present. */
+ 			while (*s != '\0') {
+ 				putchar(*s);
+ 				s++;
+ 			}
+ 			s++;
+ 		}
+ 		putchar('\n');
+ 	}
+ 
+ 	/* Header fields of interest. */
+ 	if (r->regstart != '\0')
+ 		printf("start `%c' ", r->regstart);
+ 	if (r->reganch)
+ 		printf("anchored ");
+ 	if (r->regmust != NULL)
+ 		printf("must have \"%s\"", r->regmust);
+ 	printf("\n");
+ }
+ 
+ /*
+  - regprop - printable representation of opcode
+  */
+ static char *
+ regprop(op)
+ char *op;
+ {
+ 	register char *p;
+ 	static char buf[50];
+ 
+ 	(void) strcpy(buf, ":");
+ 
+ 	switch (OP(op)) {
+ 	case BOL:
+ 		p = "BOL";
+ 		break;
+ 	case EOL:
+ 		p = "EOL";
+ 		break;
+ 	case ANY:
+ 		p = "ANY";
+ 		break;
+ 	case ANYOF:
+ 		p = "ANYOF";
+ 		break;
+ 	case ANYBUT:
+ 		p = "ANYBUT";
+ 		break;
+ 	case BRANCH:
+ 		p = "BRANCH";
+ 		break;
+ 	case EXACTLY:
+ 		p = "EXACTLY";
+ 		break;
+ 	case NOTHING:
+ 		p = "NOTHING";
+ 		break;
+ 	case BACK:
+ 		p = "BACK";
+ 		break;
+ 	case END:
+ 		p = "END";
+ 		break;
+ 	case OPEN+1:
+ 	case OPEN+2:
+ 	case OPEN+3:
+ 	case OPEN+4:
+ 	case OPEN+5:
+ 	case OPEN+6:
+ 	case OPEN+7:
+ 	case OPEN+8:
+ 	case OPEN+9:
+ 		sprintf(buf+strlen(buf), "OPEN%d", OP(op)-OPEN);
+ 		p = NULL;
+ 		break;
+ 	case CLOSE+1:
+ 	case CLOSE+2:
+ 	case CLOSE+3:
+ 	case CLOSE+4:
+ 	case CLOSE+5:
+ 	case CLOSE+6:
+ 	case CLOSE+7:
+ 	case CLOSE+8:
+ 	case CLOSE+9:
+ 		sprintf(buf+strlen(buf), "CLOSE%d", OP(op)-CLOSE);
+ 		p = NULL;
+ 		break;
+ 	case STAR:
+ 		p = "STAR";
+ 		break;
+ 	case PLUS:
+ 		p = "PLUS";
+ 		break;
+ 	default:
+ 		regerror("corrupted opcode");
+ 		break;
+ 	}
+ 	if (p != NULL)
+ 		(void) strcat(buf, p);
+ 	return(buf);
+ }
+ #endif

Index: src/date.c
Prereq: 5.1
*** ../elm2.4/src/date.c	Sat Oct  3 18:58:48 1992
--- src/date.c	Sat Nov 14 21:10:21 1992
***************
*** 1,8 ****
  
! static char rcsid[] = "@(#)$Id: date.c,v 5.1 1992/10/03 22:58:40 syd Exp $";
  
  /*******************************************************************************
!  *  The Elm Mail System  -  $Revision: 5.1 $   $State: Exp $
   *
   * 			Copyright (c) 1988-1992 USENET Community Trust
   * 			Copyright (c) 1986,1987 Dave Taylor
--- 1,8 ----
  
! static char rcsid[] = "@(#)$Id: date.c,v 5.2 1992/11/15 02:10:11 syd Exp $";
  
  /*******************************************************************************
!  *  The Elm Mail System  -  $Revision: 5.2 $   $State: Exp $
   *
   * 			Copyright (c) 1988-1992 USENET Community Trust
   * 			Copyright (c) 1986,1987 Dave Taylor
***************
*** 14,19 ****
--- 14,23 ----
   *
   *******************************************************************************
   * $Log: date.c,v $
+  * Revision 5.2  1992/11/15  02:10:11  syd
+  * remove no longer used tzname
+  * From: Syd
+  *
   * Revision 5.1  1992/10/03  22:58:40  syd
   * Initial checkin as of 2.4 Release at PL0
   *
***************
*** 65,76 ****
  
  int  days_in_month[] = { 31,    28,    31,    30,    31,     30, 
  		  31,     31,    30,   31,    30,     31,  -1};
- 
- #ifdef BSD
-   char *timezone();
- #else
-   extern char *tzname[];
- #endif
  
  days_ahead(days, buffer)
  int days;
--- 69,74 ----

Index: lib/getarpdate.c
Prereq: 5.2
*** ../elm2.4/lib/getarpdate.c	Tue Nov 10 15:20:24 1992
--- lib/getarpdate.c	Sat Nov 14 21:18:27 1992
***************
*** 1,7 ****
! static char rcsid[] = "@(#)$Id: getarpdate.c,v 5.2 1992/11/07 19:27:30 syd Exp $";
  
  /*******************************************************************************
!  *  The Elm Mail System  -  $Revision: 5.2 $   $State: Exp $
   *
   *			Copyright (c) 1988-1992 USENET Community Trust
   *			Copyright (c) 1986,1987 Dave Taylor
--- 1,7 ----
! static char rcsid[] = "@(#)$Id: getarpdate.c,v 5.4 1992/11/15 02:18:15 syd Exp $";
  
  /*******************************************************************************
!  *  The Elm Mail System  -  $Revision: 5.4 $   $State: Exp $
   *
   *			Copyright (c) 1988-1992 USENET Community Trust
   *			Copyright (c) 1986,1987 Dave Taylor
***************
*** 13,18 ****
--- 13,27 ----
   *
   *******************************************************************************
   * $Log: getarpdate.c,v $
+  * Revision 5.4  1992/11/15  02:18:15  syd
+  * Change most of the rest of the BSDs to TZNAME
+  * From: Syd
+  *
+  * Revision 5.3  1992/11/15  02:10:58  syd
+  * change tzname ifdef from ndefBSD to ifdef TZNAME on its own
+  * configure variable
+  * From: Syd
+  *
   * Revision 5.2  1992/11/07  19:27:30  syd
   * Symbol change for AIX370
   * From: uri@watson.ibm.com
***************
*** 54,63 ****
  static char *arpa_monname[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  		  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ""};
  
! #if defined(BSD) && !defined(_POSIX_SOURCE)
!   char *timezone();
! #else
    extern char *tzname[];
  #endif
  
  #ifdef _AIX370
--- 63,72 ----
  static char *arpa_monname[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  		  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ""};
  
! #ifdef TZNAME
    extern char *tzname[];
+ #else
+   char *timezone();
  #endif
  
  #ifdef _AIX370
***************
*** 80,86 ****
  	long       tzmin;		/* number of minutes off gmt 	 */
  	char	  *tzsign;		/* + or - gmt 			 */
  
! #ifdef BSD
  # ifndef TZ_MINUTESWEST
  	struct timeb	loc_time;
  
--- 89,95 ----
  	long       tzmin;		/* number of minutes off gmt 	 */
  	char	  *tzsign;		/* + or - gmt 			 */
  
! #ifndef TZNAME
  # ifndef TZ_MINUTESWEST
  	struct timeb	loc_time;
  
***************
*** 94,100 ****
  	junk = time_val.tv_sec;
  # endif /* TZ_MINUTESWEST */
  
! #else /* BSD */
  	extern	time_t	timezone;
  # ifdef ALTCHECK
  	extern	time_t	altzone;
--- 103,109 ----
  	junk = time_val.tv_sec;
  # endif /* TZ_MINUTESWEST */
  
! #else /* TZNAME */
  	extern	time_t	timezone;
  # ifdef ALTCHECK
  	extern	time_t	altzone;
***************
*** 101,112 ****
  # endif
  
  	junk = time((time_t *) 0);	/* this must be here for it to work! */
! #endif /* BSD */
  
  	the_time = localtime(&junk);
  	if (the_time->tm_year < 100)
  		the_time->tm_year += 1900;
! #ifdef BSD
  # ifdef TZ_MINUTESWEST
  	if (the_time->tm_isdst && time_zone.tz_dsttime != DST_NONE)
  		tzmin = - (time_zone.tz_minuteswest - 60);
--- 110,121 ----
  # endif
  
  	junk = time((time_t *) 0);	/* this must be here for it to work! */
! #endif /* TZNAME */
  
  	the_time = localtime(&junk);
  	if (the_time->tm_year < 100)
  		the_time->tm_year += 1900;
! #ifndef TZNAME
  # ifdef TZ_MINUTESWEST
  	if (the_time->tm_isdst && time_zone.tz_dsttime != DST_NONE)
  		tzmin = - (time_zone.tz_minuteswest - 60);
***************
*** 115,121 ****
  # else /* TZ_MINUTESWEST */
  	tzmin = the_time->tm_gmtoff / 60;
  # endif /* TZ_MINUTESWEST */
! #else /* BSD */
  # ifdef ALTCHECK
  	if (the_time->tm_isdst)
  		tzmin = - (altzone / 60);
--- 124,130 ----
  # else /* TZ_MINUTESWEST */
  	tzmin = the_time->tm_gmtoff / 60;
  # endif /* TZ_MINUTESWEST */
! #else /* TZNAME */
  # ifdef ALTCHECK
  	if (the_time->tm_isdst)
  		tzmin = - (altzone / 60);
***************
*** 124,130 ****
  # else /* ALTCHECK */
  	tzmin = - (timezone / 60);
  # endif /* ALTCHECK */
! #endif /* BSD */
  
  	if (tzmin >= 0)
  		tzsign = "+";
--- 133,139 ----
  # else /* ALTCHECK */
  	tzmin = - (timezone / 60);
  # endif /* ALTCHECK */
! #endif /* TZNAME */
  
  	if (tzmin >= 0)
  		tzsign = "+";
***************
*** 139,145 ****
  	  arpa_monname[the_time->tm_mon], the_time->tm_year,
  	  the_time->tm_hour, the_time->tm_min, the_time->tm_sec,
  	  tzsign, tzmin / 60, tzmin % 60,
! #ifdef BSD
  #ifdef TZ_MINUTESWEST
  # ifdef GOULD_NP1
  	  the_time->tm_zone);
--- 148,154 ----
  	  arpa_monname[the_time->tm_mon], the_time->tm_year,
  	  the_time->tm_hour, the_time->tm_min, the_time->tm_sec,
  	  tzsign, tzmin / 60, tzmin % 60,
! #ifndef TZNAME
  #ifdef TZ_MINUTESWEST
  # ifdef GOULD_NP1
  	  the_time->tm_zone);

Index: src/editmsg.c
Prereq: 5.2
*** ../elm2.4/src/editmsg.c	Mon Oct 19 13:04:08 1992
--- src/editmsg.c	Mon Nov 16 23:05:25 1992
***************
*** 1,8 ****
  
! static char rcsid[] = "@(#)$Id: editmsg.c,v 5.2 1992/10/17 22:22:33 syd Exp $";
  
  /*******************************************************************************
!  *  The Elm Mail System  -  $Revision: 5.2 $   $State: Exp $
   *
   * 			Copyright (c) 1988-1992 USENET Community Trust
   *			Copyright (c) 1986,1987 Dave Taylor
--- 1,8 ----
  
! static char rcsid[] = "@(#)$Id: editmsg.c,v 5.3 1992/11/17 04:05:24 syd Exp $";
  
  /*******************************************************************************
!  *  The Elm Mail System  -  $Revision: 5.3 $   $State: Exp $
   *
   * 			Copyright (c) 1988-1992 USENET Community Trust
   *			Copyright (c) 1986,1987 Dave Taylor
***************
*** 14,19 ****
--- 14,23 ----
   *
   *******************************************************************************
   * $Log: editmsg.c,v $
+  * Revision 5.3  1992/11/17  04:05:24  syd
+  * Fix for editing interrupt broken by posix_signal().
+  * From cs.utexas.edu!chinacat!chip Mon Nov 16 09:03:04 1992
+  *
   * Revision 5.2  1992/10/17  22:22:33  syd
   * Fix warnings from my ANSI C compiler because the declaration of
   * edit_interrupt did not match the prototype for the second argument of
***************
*** 38,43 ****
--- 42,53 ----
  #include <setjmp.h>
  #endif /* BSD */
  #include <ctype.h>
+ 
+ #ifdef POSIX_SIGNALS
+ #define jmp_buf			sigjmp_buf
+ #define setjmp(env)		sigsetjmp((env), 1)
+ #define longjmp(env,val)	siglongjmp((env), (val))
+ #endif
  
  #ifdef BSD
  #undef        tolower

-- 
========================================================================
Sydney S. Weinstein, CDP, CCP          Elm Coordinator - Current 2.4PL08
Datacomp Systems, Inc.                 Projected 3.0 Release: ??? ?,1994
syd@DSI.COM or dsinc!syd      Voice: (215) 947-9900, FAX: (215) 938-0235
Newsgroups: comp.mail.elm