diff -r -P -u3 fastforward-0.51_orig/Makefile fastforward-0.51_my/Makefile
--- fastforward-0.51_orig/Makefile	Mon Jun 14 21:20:12 1999
+++ fastforward-0.51_my/Makefile	Tue Jun 15 00:34:35 1999
@@ -129,16 +129,20 @@
 	./compile control.c
 
 env.a: \
-makelib envread.o
-	./makelib env.a envread.o
+makelib env.o envread.o
+	./makelib env.a env.o envread.o
+
+env.o: \
+compile env.c str.h alloc.h env.h
+	./compile env.c
 
 envread.o: \
 compile envread.c env.h str.h
 	./compile envread.c
 
 error.a: \
-makelib error.o error_str.o
-	./makelib error.a error.o error_str.o
+makelib error.o error_str.o error_temp.o
+	./makelib error.a error.o error_str.o error_temp.o
 
 error.o: \
 compile error.c error.h
@@ -148,14 +152,24 @@
 compile error_str.c error.h
 	./compile error_str.c
 
+error_temp.o: \
+compile error_temp.c error.h
+	./compile error_temp.c
+
 fastforward: \
 load fastforward.o slurpclose.o coe.o strset.o qmail.o auto_qmail.o \
 getopt.a cdb.a env.a strerr.a substdio.a stralloc.a alloc.a error.a \
-case.a str.a fs.a sig.a wait.a seek.a open.a fd.a
+case.a str.a fs.a sig.a wait.a seek.a open.a fd.a seek.a now.o lock.a \
+gfrom.o getln.a byte_chr.o filedelivery.o
 	./load fastforward slurpclose.o coe.o strset.o qmail.o \
-	auto_qmail.o getopt.a cdb.a env.a strerr.a substdio.a \
-	stralloc.a alloc.a error.a case.a str.a fs.a sig.a wait.a \
-	seek.a open.a fd.a 
+	auto_qmail.o byte_chr.o filedelivery.o getopt.a cdb.a env.a \
+	strerr.a substdio.a stralloc.a alloc.a error.a case.a str.a \
+	fs.a sig.a wait.a seek.a open.a fd.a seek.a now.o lock.a \
+	gfrom.o getln.a
+
+filedelivery.o: \
+compile filedelivery.c filedelivery.h
+	./compile filedelivery.c
 
 fastforward.0: \
 fastforward.1
@@ -165,7 +179,7 @@
 compile fastforward.c stralloc.h gen_alloc.h substdio.h subfd.h \
 substdio.h strset.h uint32.h sgetopt.h subgetopt.h readwrite.h exit.h \
 strerr.h env.h sig.h qmail.h substdio.h fmt.h case.h alloc.h coe.h \
-seek.h wait.h fork.h
+seek.h wait.h fork.h filedelivery.c filedelivery.h
 	./compile fastforward.c
 
 fd.a: \
@@ -185,6 +199,14 @@
 	cat auto-ccld.sh find-systype.sh > find-systype
 	chmod 755 find-systype
 
+fmt_str.o: \
+compile fmt_str.c fmt.h
+	./compile fmt_str.c
+
+fmt_strn.o: \
+compile fmt_strn.c fmt.h
+	./compile fmt_strn.c
+
 fmt_ulong.o: \
 compile fmt_ulong.c fmt.h
 	./compile fmt_ulong.c
@@ -197,8 +219,8 @@
 	rm -f tryvfork.o tryvfork
 
 fs.a: \
-makelib fmt_ulong.o scan_ulong.o
-	./makelib fs.a fmt_ulong.o scan_ulong.o
+makelib fmt_ulong.o fmt_str.o fmt_strn.o scan_ulong.o
+	./makelib fs.a fmt_ulong.o fmt_str.o fmt_strn.o scan_ulong.o
 
 getln.a: \
 makelib getln.o getln2.o
@@ -216,6 +238,17 @@
 makelib subgetopt.o sgetopt.o
 	./makelib getopt.a subgetopt.o sgetopt.o
 
+gfrom.o: \
+compile gfrom.c str.h gfrom.h
+	./compile gfrom.c
+
+hasflock.h: \
+tryflock.c compile load
+	( ( ./compile tryflock.c && ./load tryflock ) >/dev/null \
+	2>&1 \
+	&& echo \#define HASFLOCK 1 || exit 0 ) > hasflock.h
+	rm -f tryflock.o tryflock
+
 hassgact.h: \
 trysgact.c compile load
 	( ( ./compile trysgact.c && ./load trysgact ) >/dev/null \
@@ -223,6 +256,13 @@
 	&& echo \#define HASSIGACTION 1 || exit 0 ) > hassgact.h
 	rm -f trysgact.o trysgact
 
+hassgprm.h: \
+	trysgprm.c compile load
+	( ( ./compile trysgprm.c && ./load trysgprm ) >/dev/null \
+	2>&1 \
+	&& echo \#define HASSIGPROCMASK 1 || exit 0 ) > hassgprm.h
+	rm -f trysgprm.o trysgprm
+
 haswaitp.h: \
 trywaitp.c compile load
 	( ( ./compile trywaitp.c && ./load trywaitp ) >/dev/null \
@@ -273,6 +313,14 @@
 	( cat warn-auto.sh; ./make-load "`cat systype`" ) > load
 	chmod 755 load
 
+lock.a: \
+makelib lock_ex.o
+	./makelib lock.a lock_ex.o
+
+lock_ex.o: \
+compile lock_ex.c hasflock.h lock.h
+	./compile lock_ex.c
+
 make-compile: \
 make-compile.sh auto-ccld.sh
 	cat auto-ccld.sh make-compile.sh > make-compile
@@ -334,9 +382,13 @@
 auto_qmail.h env.h
 	./compile newinclude.c
 
+now.o: \
+compile now.c datetime.h now.h datetime.h
+	./compile now.c
+
 open.a: \
-makelib open_read.o open_trunc.o
-	./makelib open.a open_read.o open_trunc.o
+makelib open_read.o open_trunc.o open_excl.o open_append.o
+	./makelib open.a open_read.o open_trunc.o open_excl.o open_append.o
 
 open_read.o: \
 compile open_read.c open.h
@@ -346,6 +398,14 @@
 compile open_trunc.c open.h
 	./compile open_trunc.c
 
+open_excl.o: \
+compile open_excl.c open.h
+	./compile open_excl.c
+
+open_append.o: \
+compile open_append.c open.h
+	./compile open_append.c
+
 printforward: \
 load printforward.o cdb.a strerr.a substdio.a stralloc.a alloc.a \
 error.a str.a
@@ -390,13 +450,25 @@
 	./compile scan_ulong.c
 
 seek.a: \
-makelib seek_set.o
-	./makelib seek.a seek_set.o
+makelib seek_set.o seek_cur.o seek_end.o seek_trunc.o
+	./makelib seek.a seek_set.o seek_cur.o seek_end.o seek_trunc.o
 
 seek_set.o: \
 compile seek_set.c seek.h
 	./compile seek_set.c
 
+seek_cur.o: \
+compile seek_cur.c seek.h
+	./compile seek_cur.c
+
+seek_end.o: \
+compile seek_end.c seek.h
+	./compile seek_end.c
+
+seek_trunc.o: \
+compile seek_trunc.c seek.h
+	./compile seek_trunc.c
+
 setforward: \
 load setforward.o cdbmss.o cdbmake.a strerr.a substdio.a stralloc.a \
 alloc.a error.a str.a seek.a open.a case.a
@@ -438,8 +510,8 @@
 	./compile sgetopt.c
 
 sig.a: \
-makelib sig_catch.o sig_pipe.o
-	./makelib sig.a sig_catch.o sig_pipe.o
+makelib sig_catch.o sig_pipe.o sig_alarm.o sig_block.o
+	./makelib sig.a sig_catch.o sig_pipe.o sig_alarm.o sig_block.o
 
 sig_catch.o: \
 compile sig_catch.c sig.h hassgact.h
@@ -448,6 +520,14 @@
 sig_pipe.o: \
 compile sig_pipe.c sig.h
 	./compile sig_pipe.c
+
+sig_alarm.o: \
+compile sig_alarm.c sig.h
+	./compile sig_alarm.c
+
+sig_block.o: \
+compile sig_block.c sig.h hassgprm.h
+	./compile sig_block.c
 
 slurpclose.o: \
 compile slurpclose.c stralloc.h gen_alloc.h readwrite.h slurpclose.h \
diff -r -P -u3 fastforward-0.51_orig/README.idx fastforward-0.51_my/README.idx
--- fastforward-0.51_orig/README.idx	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/README.idx	Tue Jun 15 21:27:51 1999
@@ -0,0 +1,43 @@
+
+README
+fastforward-idx-1.01.patch
+
+by David Harris <dharris@drh.net>
+
+    Info
+    ----
+
+See the web page for details on this patch.
+
+http://www.davideous.com/fastforward-idx/
+
+This patch is licensed under GPL.
+
+
+    Disclaimer of Warranty
+    ----------------------
+
+THIS SOFTWARE IS PROVIDED BY DAVID R. HARRIS "AS IS" AND ANY EXPRESSED
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL DAVID R. HARRIS OR HIS CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+
+
+    Copyright
+    ---------
+
+Copyright (c) 1999 David R. Harris <dharris@drh.net>
+All Rights Reserved.
+
+You may freely distributed this software under the Gnu Public Liscense (GPL).
+The GPL is available from the GNU organization at www.gnu.org.
+
+
+
diff -r -P -u3 fastforward-0.51_orig/datetime.h fastforward-0.51_my/datetime.h
--- fastforward-0.51_orig/datetime.h	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/datetime.h	Tue Jun 15 00:20:19 1999
@@ -0,0 +1,20 @@
+#ifndef DATETIME_H
+#define DATETIME_H
+
+struct datetime {
+  int hour;
+  int min;
+  int sec;
+  int wday;
+  int mday;
+  int yday;
+  int mon;
+  int year;
+} ;
+
+typedef long datetime_sec;
+
+extern void datetime_tai();
+extern datetime_sec datetime_untai();
+
+#endif
diff -r -P -u3 fastforward-0.51_orig/env.c fastforward-0.51_my/env.c
--- fastforward-0.51_orig/env.c	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/env.c	Mon Jun 14 21:16:44 1999
@@ -0,0 +1,113 @@
+/* env.c, envread.c, env.h: environ library
+Daniel J. Bernstein, djb@silverton.berkeley.edu.
+Depends on str.h, alloc.h.
+Requires environ.
+19960113: rewrite. warning: interface is different.
+No known patent problems.
+*/
+
+#include "str.h"
+#include "alloc.h"
+#include "env.h"
+
+int env_isinit = 0; /* if env_isinit: */
+static int ea; /* environ is a pointer to ea+1 char*'s. */
+static int en; /* the first en of those are ALLOCATED. environ[en] is 0. */
+
+static void env_goodbye(i) int i;
+{
+ alloc_free(environ[i]);
+ environ[i] = environ[--en];
+ environ[en] = 0;
+}
+
+static char *null = 0;
+
+void env_clear()
+{
+ if (env_isinit) while (en) env_goodbye(0);
+ else environ = &null;
+}
+
+static void env_unsetlen(s,len) char *s; int len;
+{
+ int i;
+ for (i = en - 1;i >= 0;--i)
+   if (!str_diffn(s,environ[i],len))
+     if (environ[i][len] == '=')
+       env_goodbye(i);
+}
+
+int env_unset(s) char *s;
+{
+ if (!env_isinit) if (!env_init()) return 0;
+ env_unsetlen(s,str_len(s));
+ return 1;
+}
+
+static int env_add(s) char *s;
+{
+ char *t;
+ t = env_findeq(s);
+ if (t) env_unsetlen(s,t - s);
+ if (en == ea)
+  {
+   ea += 30;
+   if (!alloc_re(&environ,(en + 1) * sizeof(char *),(ea + 1) * sizeof(char *)))
+    { ea = en; return 0; }
+  }
+ environ[en++] = s;
+ environ[en] = 0;
+ return 1;
+}
+
+int env_put(s) char *s;
+{
+ char *u;
+ if (!env_isinit) if (!env_init()) return 0;
+ u = alloc(str_len(s) + 1);
+ if (!u) return 0;
+ str_copy(u,s);
+ if (!env_add(u)) { alloc_free(u); return 0; }
+ return 1;
+}
+
+int env_put2(s,t) char *s; char *t;
+{
+ char *u;
+ int slen;
+ if (!env_isinit) if (!env_init()) return 0;
+ slen = str_len(s);
+ u = alloc(slen + str_len(t) + 2);
+ if (!u) return 0;
+ str_copy(u,s);
+ u[slen] = '=';
+ str_copy(u + slen + 1,t);
+ if (!env_add(u)) { alloc_free(u); return 0; }
+ return 1;
+}
+
+int env_init()
+{
+ char **newenviron;
+ int i;
+ for (en = 0;environ[en];++en) ;
+ ea = en + 10;
+ newenviron = (char **) alloc((ea + 1) * sizeof(char *));
+ if (!newenviron) return 0;
+ for (en = 0;environ[en];++en)
+  {
+   newenviron[en] = alloc(str_len(environ[en]) + 1);
+   if (!newenviron[en])
+    {
+     for (i = 0;i < en;++i) alloc_free(newenviron[i]);
+     alloc_free(newenviron);
+     return 0;
+    }
+   str_copy(newenviron[en],environ[en]);
+  }
+ newenviron[en] = 0;
+ environ = newenviron;
+ env_isinit = 1;
+ return 1;
+}
diff -r -P -u3 fastforward-0.51_orig/error_temp.c fastforward-0.51_my/error_temp.c
--- fastforward-0.51_orig/error_temp.c	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/error_temp.c	Tue Jun 15 00:20:19 1999
@@ -0,0 +1,80 @@
+#include <errno.h>
+#include "error.h"
+
+#define X(n) if (e == n) return 1;
+
+int error_temp(e)
+int e;
+{
+  X(error_intr)
+  X(error_nomem)
+  X(error_txtbsy)
+  X(error_io)
+  X(error_timeout)
+  X(error_wouldblock)
+  X(error_again)
+#ifdef EDEADLK
+  X(EDEADLK)
+#endif
+#ifdef EBUSY
+  X(EBUSY)
+#endif
+#ifdef ENFILE
+  X(ENFILE)
+#endif
+#ifdef EMFILE
+  X(EMFILE)
+#endif
+#ifdef EFBIG
+  X(EFBIG)
+#endif
+#ifdef ENOSPC
+  X(ENOSPC)
+#endif
+#ifdef ENETDOWN
+  X(ENETDOWN)
+#endif
+#ifdef ENETUNREACH
+  X(ENETUNREACH)
+#endif
+#ifdef ENETRESET
+  X(ENETRESET)
+#endif
+#ifdef ECONNABORTED
+  X(ECONNABORTED)
+#endif
+#ifdef ECONNRESET
+  X(ECONNRESET)
+#endif
+#ifdef ENOBUFS
+  X(ENOBUFS)
+#endif
+#ifdef ETOOMANYREFS
+  X(ETOOMANYREFS)
+#endif
+#ifdef ECONNREFUSED
+  X(ECONNREFUSED)
+#endif
+#ifdef EHOSTDOWN
+  X(EHOSTDOWN)
+#endif
+#ifdef EHOSTUNREACH
+  X(EHOSTUNREACH)
+#endif
+#ifdef EPROCLIM
+  X(EPROCLIM)
+#endif
+#ifdef EUSERS
+  X(EUSERS)
+#endif
+#ifdef EDQUOT
+  X(EDQUOT)
+#endif
+#ifdef ESTALE
+  X(ESTALE)
+#endif
+#ifdef ENOLCK
+  X(ENOLCK)
+#endif
+  return 0;
+}
diff -r -P -u3 fastforward-0.51_orig/fastforward.c fastforward-0.51_my/fastforward.c
--- fastforward-0.51_orig/fastforward.c	Tue May 19 12:25:42 1998
+++ fastforward-0.51_my/fastforward.c	Tue Jun 15 14:55:54 1999
@@ -19,6 +19,12 @@
 #include "wait.h"
 #include "fork.h"
 
+#define DELIVERY
+
+#ifdef DELIVERY
+#include "filedelivery.h"
+#endif
+
 #define FATAL "fastforward: fatal: "
 
 void usage()
@@ -49,6 +55,8 @@
   }
 }
 
+int flag99;
+
 struct qmail qq;
 char qp[FMT_ULONG];
 char qqbuf[1];
@@ -77,6 +85,11 @@
 
 stralloc mailinglist = {0};
 
+stralloc env_default = {0};
+stralloc env_newsender = {0};
+
+int have_setenv = 0;
+
 void dofile(fn)
 char *fn;
 {
@@ -112,62 +125,194 @@
     }
 }
 
+void deliver_mailfile(fn)
+char *fn;
+{
+  if (!flagdeliver) {
+    print("mbox ");
+    printsafe(fn);
+    print("\n");
+    substdio_flush(subfderr);
+    return;
+  }
+
+#ifdef DELIVERY
+  mailfile(fn);
+#else
+  strerr_die4sys(111,FATAL,"can not deliver to mailfile ",fn,": feature not yet supported");
+#endif
+}
+
+void deliver_maildir(fn)
+char *fn;
+{
+  if (!flagdeliver) {
+    print("maildir ");
+    printsafe(fn);
+    print("\n");
+    substdio_flush(subfderr);
+    return;
+  }
+
+#ifdef DELIVERY
+  maildir(fn);
+#else
+  strerr_die4sys(111,FATAL,"can not deliver to maildir ",fn,": feature not yet supported");
+#endif
+}
+
 char *fncdb;
 int fdcdb;
 stralloc key = {0};
 uint32 dlen;
 stralloc data = {0};
+stralloc user = {0};
 
 void cdbreaderror()
 {
   strerr_die4sys(111,FATAL,"unable to read ",fncdb,": ");
 }
 
-int findtarget(flagwild,prepend,addr)
-int flagwild;
+int findtarget(flagwild,setenv,prepend,addr)
+int flagwild;  
+/* flagwild = 0 -- nowildcarding at all, 
+              1 -- normal,
+              2 -- no -default wildcarding,
+              3 -- no -default or @domain wildcarding */
+int setenv;
 char *prepend;
 char *addr;
 {
   int r;
   int at;
 
+  int i;
+
+  /* NOTE -- Sat Jun 12 1999 David Harris <dharris@drh.net>
+             We match user@ records then @domain records, which is not the standard
+             fast forward behavior. We do this because each .domains file will
+             probably have more uses than domains.. so the @domain wildcard is much
+             more general (more entropy) than the user@ records. Matching should be
+             from general to specific, so we do user@domain then user@ then @domain. */
+
+  /* NOTE -- Sat Jun 12 1999 David Harris <dharris@drh.net>
+             We are going to take a leap off the wild end and implement
+             qmail-local style -default matching here, so that we can implement
+             ezmlm lists in the .domains file. I'm kind of cringing at all the cdb
+             lookups this creates, but it's a needed feature and cdb is fast anyway. */
+
+  /* clear the DEFAULT env var */
+
+  if (setenv)
+    if (!stralloc_copys(&env_default,"")) nomem();
+
+  /* find the @ and the username for use in the lookups */
+
+  at = str_rchr(addr,'@');
+  if (!addr[at]) return 0;
+  if (!stralloc_copyb(&user,addr,at)) nomem();
+
+  /* check for user@domain records */
+
   if (!stralloc_copys(&key,prepend)) nomem();
   if (!stralloc_cats(&key,addr)) nomem();
   case_lowerb(key.s,key.len);
 
   r = cdb_seek(fdcdb,key.s,key.len,&dlen);
   if (r == -1) cdbreaderror();
-  if (r) return 1;
 
-  if (!flagwild) return 0;
-  at = str_rchr(addr,'@');
-  if (!addr[at]) return 0;
+  if (r) {
+    if (setenv)
+      if (user.len >= 7)
+        if (!byte_diff("default",7,user.s + user.len - 7))
+          if (!stralloc_copys(&env_default,"default")) nomem();
+    return 1;
+  }
+
+  if (flagwild == 0) return 0; /* if no wildcarding, exit out */
+
+  /* check for user@domain -default records */
+
+  if (flagwild < 2)
+    for (i = user.len;i >= 0;--i)
+      if (user.s[i - 1] == '-') {
+        if (!stralloc_copys(&key,prepend)) nomem();    /* prepend part */
+        if (!stralloc_catb(&key,user.s,i)) nomem();    /* stripped username with dash */
+        if (!stralloc_cats(&key,"default")) nomem();   /* "default" */
+        if (!stralloc_cats(&key,addr + at)) nomem();   /* @domain */
+        case_lowerb(key.s,key.len);
+
+        r = cdb_seek(fdcdb,key.s,key.len,&dlen);
+        if (r == -1) cdbreaderror();
+        if (r) {
+          if (setenv)
+            if (i <= user.len) /* paranoia */
+              if (!stralloc_copyb(&env_default,user.s + i,user.len - i)) nomem();
+          return 1;
+        }
+      }
+
+  /* check for user@ records */
 
   if (!stralloc_copys(&key,prepend)) nomem();
-  if (!stralloc_cats(&key,addr + at)) nomem();
+  if (!stralloc_catb(&key,addr,at + 1)) nomem();
   case_lowerb(key.s,key.len);
 
   r = cdb_seek(fdcdb,key.s,key.len,&dlen);
   if (r == -1) cdbreaderror();
-  if (r) return 1;
+
+  if (r) {
+    if (setenv)
+      if (user.len >= 7)
+        if (!byte_diff("default",7,user.s + user.len - 7))
+          if (!stralloc_copys(&env_default,"default")) nomem();
+    return 1;
+  }
+
+  /* check for user@ -default records */
+
+  if (flagwild < 2)
+    for (i = user.len;i >= 0;--i)
+      if (user.s[i - 1] == '-') {
+        if (!stralloc_copys(&key,prepend)) nomem();    /* prepend part */
+        if (!stralloc_catb(&key,user.s,i)) nomem();    /* stripped username with dash */
+        if (!stralloc_cats(&key,"default@")) nomem();  /* "default@" */
+        case_lowerb(key.s,key.len);
+
+        r = cdb_seek(fdcdb,key.s,key.len,&dlen);
+        if (r == -1) cdbreaderror();
+        if (r) {
+          if (setenv)
+            if (i <= user.len) /* paranoia */
+              if (!stralloc_copyb(&env_default,user.s + i,user.len - i)) nomem();
+          return 1;
+        }
+      }
+
+  /* check for @domain records */
+
+  if (flagwild == 3) return 0;
 
   if (!stralloc_copys(&key,prepend)) nomem();
-  if (!stralloc_catb(&key,addr,at + 1)) nomem();
+  if (!stralloc_cats(&key,addr + at)) nomem();
   case_lowerb(key.s,key.len);
 
   r = cdb_seek(fdcdb,key.s,key.len,&dlen);
   if (r == -1) cdbreaderror();
   if (r) return 1;
 
+  /* alas.. nothing found */
+
   return 0;
 }
 
-int gettarget(flagwild,prepend,addr)
+int gettarget(flagwild,setenv,prepend,addr)
 int flagwild;
+int setenv;
 char *prepend;
 char *addr;
 {
-  if (!findtarget(flagwild,prepend,addr)) return 0;
+  if (!findtarget(flagwild,setenv,prepend,addr)) return 0;
 
   if (!stralloc_ready(&data,(unsigned int) dlen)) nomem();
   data.len = dlen;
@@ -205,6 +350,19 @@
     args[3] = 0;
   }
 
+  if (!have_setenv) {
+     if (!stralloc_0(&env_newsender)) nomem();
+     if (!env_put2("NEWSENDER", env_newsender.s)) nomem();
+     if (env_default.len != 0) {
+       if (!stralloc_0(&env_default)) nomem();
+       if (!env_put2("DEFAULT", env_default.s)) nomem();
+     }
+     else {
+       if (!env_unset("DEFAULT")) nomem();
+     }
+     have_setenv = 1;
+  }
+
   switch(child = vfork()) {
     case -1:
       strerr_die2sys(111,FATAL,"unable to fork: ");
@@ -222,6 +380,7 @@
     case 64: case 65: case 70: case 76: case 77: case 78: case 112:
     case 100: _exit(100);
     case 0: break;
+    case 99: flag99 = 1; break;
     default: _exit(111);
   }
 
@@ -233,49 +392,88 @@
 {
   int i;
   int j;
-  i = 0;
-  for (j = 0;j < data.len;++j)
-    if (!data.s[j]) {
+  flag99 = 0;
+  j = data.len - 1;
+  for (i = j ; i >= 0 ; i--) {
+    if (i==0 || !data.s[i-1]) {
       if ((data.s[i] == '|') || (data.s[i] == '!'))
         doprogram(data.s + i);
       else if ((data.s[i] == '.') || (data.s[i] == '/')) {
         if (!stralloc_cats(&todo,data.s + i)) nomem();
         if (!stralloc_0(&todo)) nomem();
       }
+      else if (data.s[i] == '$') {
+        if (data.s[j - 1] == '/')
+           deliver_maildir(data.s + i + 1);
+        else
+           deliver_mailfile(data.s + i + 1);
+      }
       else if ((data.s[i] == '&') && (j - i < 900)) {
         if (!stralloc_cats(&todo,data.s + i)) nomem();
         if (!stralloc_0(&todo)) nomem();
       }
-      i = j + 1;
+      j = i - 1;
+      if (flag99) return;
     }
+  }
 }
 
-void dorecip(addr)
-char *addr;
-{
-
-  if (!findtarget(0,"?",addr))
-    if (gettarget(0,":",addr)) {
-      dodata();
-      return;
-    }
-  if (!stralloc_cats(&forward,addr)) nomem();
-  if (!stralloc_0(&forward)) nomem();
-}
+stralloc foo = {0};
+stralloc bar = {0};
 
 void doorigrecip(addr)
 char *addr;
 {
+
+  char *domain;
+
+  /* apply the envelope sender rewriting */
+
+  /* NOTE -- Mon Jun 14 1999 David Harris <dharris@drh.net>
+             This code to rewrite the envelope sender is weird, but it's designed
+             to exactly mimic qmail-local. */
+
   if (sender.len)
     if ((sender.len != 4) || byte_diff(sender.s,4,"#@[]"))
-      if (gettarget(1,"?",addr))
-        if (!stralloc_copy(&sender,&data)) nomem();
-  if (!gettarget(1,":",addr))
+    {
+
+      if (gettarget(2,0,"?",addr))
+      {
+        if (!stralloc_copy(&foo,&data)) nomem();
+        for ( foo.len-- ; foo.len >= 0 ; foo.len-- ) if ( foo.s[foo.len] == '@' ) break;
+        for ( domain = addr ; *domain ; domain++ ) if ( *domain == '@' ) break;
+
+        if (!stralloc_copy(&bar,&foo)) nomem();
+        if (!stralloc_cats(&bar,"-default")) nomem();
+        if (!stralloc_cats(&bar,domain)) nomem();
+        if (!stralloc_0(&bar)) nomem();
+
+        if (gettarget(3,0,":",bar.s))
+        {
+           if (!stralloc_copy(&sender,&foo)) nomem();
+           if (!stralloc_cats(&sender,"-")) nomem();
+           if (!stralloc_cats(&sender,domain)) nomem();
+           if (!stralloc_cats(&sender,"-@[]")) nomem();
+        }
+        else
+        {
+           if (!stralloc_copy(&sender,&foo)) nomem();
+           if (!stralloc_cats(&sender,domain)) nomem();
+        }
+      }
+    }
+  if (!stralloc_copy(&env_newsender,&sender)) nomem();
+
+  /* get and apply the forwarding data */
+  if (!gettarget(1,1,":",addr))
     if (flagpassthrough)
       _exit(0);
     else
       strerr_die1x(100,"Sorry, no mailbox here by that name. (#5.1.1)");
   dodata();
+
+/* if (!stralloc_0(&env_default)) nomem(); */
+/* print("DEFAULT = "); print(env_default.s); print("\n"); */
 }
 
 stralloc recipient = {0};
@@ -338,6 +536,8 @@
   if (!strset_add(&done,x)) nomem();
   doorigrecip(x);
 
+  if ( flag99 ) _exit(flagpassthrough ? 99 : 0);
+
   while (todo.len) {
     i = todo.len - 1;
     while ((i > 0) && todo.s[i - 1]) --i;
@@ -355,8 +555,10 @@
       continue;
     else if ((*x == '.') || (*x == '/'))
       dofile(x);
-    else
-      dorecip(x + 1);
+    else {
+      if (!stralloc_cats(&forward,x + 1)) nomem();
+      if (!stralloc_0(&forward)) nomem();
+    }
   }
 
   if (!forward.len) {
diff -r -P -u3 fastforward-0.51_orig/filedelivery.c fastforward-0.51_my/filedelivery.c
--- fastforward-0.51_orig/filedelivery.c	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/filedelivery.c	Tue Jun 15 00:32:58 1999
@@ -0,0 +1,224 @@
+#include <sys/stat.h>
+#include "seek.h"
+#include "open.h"
+#include "now.h"
+#include "fmt.h"
+#include "lock.h"
+#include "gfrom.h"
+#include "stralloc.h"
+#include "qmail.h"
+#include "readwrite.h"
+#include "strerr.h"
+#include "sig.h"
+#include "error.h"
+#include "wait.h"
+#include "env.h"
+
+/* *********************************************************** */
+/* NOTE: This code was taken from qmail-1.03/qmail-local.c     */
+
+#define FATAL "filedelivery: fatal: "
+
+void temp_slowlock()
+{ strerr_die1x(111,"File has been locked for 30 seconds straight. (#4.3.0)"); }
+void temp_rewind() { strerr_die1x(111,"Unable to rewind message. (#4.3.0)"); }
+void temp_fork() { strerr_die3x(111,"Unable to fork: ",error_str(errno),". (#4.3.0)"); }
+void temp_childcrashed() { strerr_die1x(111,"Aack, child crashed. (#4.3.0)"); }
+
+stralloc filed_ufline = {0};
+stralloc filed_rpline = {0};
+stralloc filed_dtline = {0};
+
+int filed_envread = 0;
+
+stralloc messline = {0};
+
+char buf[1024];
+char outbuf[1024];
+
+void filed_readenv()
+{
+  char * x;
+
+  if (!filed_envread) {
+
+    x = env_get("UFLINE");
+    if (!x) strerr_die2x(100,FATAL,"$UFLINE must be set");
+    if (!stralloc_copys(&filed_ufline,x)) nomem();
+
+    x = env_get("RPLINE");
+    if (!x) strerr_die2x(100,FATAL,"$RPLINE must be set");
+    if (!stralloc_copys(&filed_rpline,x)) nomem();
+
+    x = env_get("DTLINE");
+    if (!x) strerr_die2x(100,FATAL,"$DTLINE must be set");
+    if (!stralloc_copys(&filed_dtline,x)) nomem();
+
+  }
+}
+
+/* child process */
+
+char fntmptph[80 + FMT_ULONG * 2];
+char fnnewtph[80 + FMT_ULONG * 2];
+void tryunlinktmp() { unlink(fntmptph); }
+void sigalrm() { tryunlinktmp(); _exit(3); }
+
+void maildir_child(dir)
+char *dir;
+{
+ unsigned long pid;
+ unsigned long time;
+ char host[64];
+ char *s;
+ int loop;
+ struct stat st;
+ int fd;
+ substdio ss;
+ substdio ssout;
+
+ sig_alarmcatch(sigalrm);
+ if (chdir(dir) == -1) { if (error_temp(errno)) _exit(1); _exit(2); }
+ pid = getpid();
+ host[0] = 0;
+ gethostname(host,sizeof(host));
+ for (loop = 0;;++loop)
+  {
+   time = now();
+   s = fntmptph;
+   s += fmt_str(s,"tmp/");
+   s += fmt_ulong(s,time); *s++ = '.';
+   s += fmt_ulong(s,pid); *s++ = '.';
+   s += fmt_strn(s,host,sizeof(host)); *s++ = 0;
+   if (stat(fntmptph,&st) == -1) if (errno == error_noent) break;
+   /* really should never get to this point */
+   if (loop == 2) _exit(1);
+   sleep(2);
+  }
+ str_copy(fnnewtph,fntmptph);
+ byte_copy(fnnewtph,3,"new");
+
+ alarm(86400);
+ fd = open_excl(fntmptph);
+ if (fd == -1) _exit(1);
+
+ substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
+ substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
+ if (substdio_put(&ssout,filed_rpline.s,filed_rpline.len) == -1) goto fail;
+ if (substdio_put(&ssout,filed_dtline.s,filed_dtline.len) == -1) goto fail;
+
+ switch(substdio_copy(&ssout,&ss))
+  {
+   case -2: tryunlinktmp(); _exit(4);
+   case -3: goto fail;
+  }
+
+ if (substdio_flush(&ssout) == -1) goto fail;
+ if (fsync(fd) == -1) goto fail;
+ if (close(fd) == -1) goto fail; /* NFS dorks */
+
+ if (link(fntmptph,fnnewtph) == -1) goto fail;
+   /* if it was error_exist, almost certainly successful; i hate NFS */
+ tryunlinktmp(); _exit(0);
+
+ fail: tryunlinktmp(); _exit(1);
+}
+
+/* end child process */
+
+void maildir(fn)
+char *fn;
+{
+ int child;
+ int wstat;
+
+ filed_readenv();
+
+ if (seek_begin(0) == -1) temp_rewind();
+
+ switch(child = fork())
+  {
+   case -1:
+     temp_fork();
+   case 0:
+     maildir_child(fn);
+     _exit(111);
+  }
+
+ wait_pid(&wstat,child);
+ if (wait_crashed(wstat))
+   temp_childcrashed();
+ switch(wait_exitcode(wstat))
+  {
+   case 0: break;
+   case 2: strerr_die1x(111,"Unable to chdir to maildir. (#4.2.1)");
+   case 3: strerr_die1x(111,"Timeout on maildir delivery. (#4.3.0)");
+   case 4: strerr_die1x(111,"Unable to read message. (#4.3.0)");
+   default: strerr_die1x(111,"Temporary error on maildir delivery. (#4.3.0)");
+  }
+}
+
+void mailfile(fn)
+char *fn;
+{
+ int fd;
+ substdio ss;
+ substdio ssout;
+ int match;
+ seek_pos pos;
+ int flaglocked;
+
+ filed_readenv();
+
+ if (seek_begin(0) == -1) temp_rewind();
+
+ fd = open_append(fn);
+ if (fd == -1)
+   strerr_die5x(111,"Unable to open ",fn,": ",error_str(errno),". (#4.2.1)");
+
+ sig_alarmcatch(temp_slowlock);
+ alarm(30);
+ flaglocked = (lock_ex(fd) != -1);
+ alarm(0);
+ sig_alarmdefault();
+
+ seek_end(fd);
+ pos = seek_cur(fd);
+
+ substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
+ substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
+ if (substdio_put(&ssout,filed_ufline.s,filed_ufline.len)) goto writeerrs;
+ if (substdio_put(&ssout,filed_rpline.s,filed_rpline.len)) goto writeerrs;
+ if (substdio_put(&ssout,filed_dtline.s,filed_dtline.len)) goto writeerrs;
+ for (;;)
+  {
+   if (getln(&ss,&messline,&match,'\n') != 0) 
+    {
+     strerr_warn3("Unable to read message: ",error_str(errno),". (#4.3.0)",0);
+     if (flaglocked) seek_trunc(fd,pos); close(fd);
+     _exit(111);
+    }
+   if (!match && !messline.len) break;
+   if (gfrom(messline.s,messline.len))
+     if (substdio_bput(&ssout,">",1)) goto writeerrs;
+   if (substdio_bput(&ssout,messline.s,messline.len)) goto writeerrs;
+   if (!match)
+    {
+     if (substdio_bputs(&ssout,"\n")) goto writeerrs;
+     break;
+    }
+  }
+ if (substdio_bputs(&ssout,"\n")) goto writeerrs;
+ if (substdio_flush(&ssout)) goto writeerrs;
+ if (fsync(fd) == -1) goto writeerrs;
+ close(fd);
+ return;
+
+ writeerrs:
+ strerr_warn5("Unable to write ",fn,": ",error_str(errno),". (#4.3.0)",0);
+ if (flaglocked) seek_trunc(fd,pos);
+ close(fd);
+ _exit(111);
+}
+
+
diff -r -P -u3 fastforward-0.51_orig/filedelivery.h fastforward-0.51_my/filedelivery.h
--- fastforward-0.51_orig/filedelivery.h	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/filedelivery.h	Tue Jun 15 00:32:58 1999
@@ -0,0 +1,7 @@
+#ifndef FILEDELIVERY_H
+#define FILEDELIVERY_H
+
+extern void maildir(char * fn);
+extern void mailfile(char * fn);
+
+#endif
diff -r -P -u3 fastforward-0.51_orig/fmt_str.c fastforward-0.51_my/fmt_str.c
--- fastforward-0.51_orig/fmt_str.c	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/fmt_str.c	Tue Jun 15 00:20:19 1999
@@ -0,0 +1,12 @@
+#include "fmt.h"
+
+unsigned int fmt_str(s,t)
+register char *s; register char *t;
+{
+  register unsigned int len;
+  char ch;
+  len = 0;
+  if (s) { while (ch = t[len]) s[len++] = ch; }
+  else while (t[len]) len++;
+  return len;
+}
diff -r -P -u3 fastforward-0.51_orig/fmt_strn.c fastforward-0.51_my/fmt_strn.c
--- fastforward-0.51_orig/fmt_strn.c	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/fmt_strn.c	Tue Jun 15 00:20:19 1999
@@ -0,0 +1,12 @@
+#include "fmt.h"
+
+unsigned int fmt_strn(s,t,n)
+register char *s; register char *t; register unsigned int n;
+{
+  register unsigned int len;
+  char ch;
+  len = 0;
+  if (s) { while (n-- && (ch = t[len])) s[len++] = ch; }
+  else while (n-- && t[len]) len++;
+  return len;
+}
diff -r -P -u3 fastforward-0.51_orig/gfrom.c fastforward-0.51_my/gfrom.c
--- fastforward-0.51_orig/gfrom.c	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/gfrom.c	Tue Jun 15 00:20:19 1999
@@ -0,0 +1,10 @@
+#include "str.h"
+#include "gfrom.h"
+
+int gfrom(s,len)
+char *s;
+int len;
+{
+ while ((len > 0) && (*s == '>')) { ++s; --len; }
+ return (len >= 5) && !str_diffn(s,"From ",5);
+}
diff -r -P -u3 fastforward-0.51_orig/gfrom.h fastforward-0.51_my/gfrom.h
--- fastforward-0.51_orig/gfrom.h	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/gfrom.h	Tue Jun 15 00:20:19 1999
@@ -0,0 +1,6 @@
+#ifndef GFROM_H
+#define GFROM_H
+
+extern int gfrom();
+
+#endif
diff -r -P -u3 fastforward-0.51_orig/lock.h fastforward-0.51_my/lock.h
--- fastforward-0.51_orig/lock.h	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/lock.h	Tue Jun 15 00:20:19 1999
@@ -0,0 +1,8 @@
+#ifndef LOCK_H
+#define LOCK_H
+
+extern int lock_ex();
+extern int lock_un();
+extern int lock_exnb();
+
+#endif
diff -r -P -u3 fastforward-0.51_orig/lock_ex.c fastforward-0.51_my/lock_ex.c
--- fastforward-0.51_orig/lock_ex.c	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/lock_ex.c	Tue Jun 15 00:20:19 1999
@@ -0,0 +1,11 @@
+#include <sys/types.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include "hasflock.h"
+#include "lock.h"
+
+#ifdef HASFLOCK
+int lock_ex(fd) int fd; { return flock(fd,LOCK_EX); }
+#else
+int lock_ex(fd) int fd; { return lockf(fd,1,0); }
+#endif
diff -r -P -u3 fastforward-0.51_orig/newaliases.1 fastforward-0.51_my/newaliases.1
--- fastforward-0.51_orig/newaliases.1	Tue May 19 12:25:42 1998
+++ fastforward-0.51_my/newaliases.1	Tue Jun 15 23:27:19 1999
@@ -2,13 +2,16 @@
 .SH NAME
 newaliases \- create a forwarding database from /etc/aliases
 .SH SYNOPSIS
-.B newaliases
+.B newaliases [ aliasfile ]
 .SH DESCRIPTION
 .B newaliases
 reads a table of
 sendmail-style
 forwarding instructions from
 .B /etc/aliases
+(or
+.I aliasfile
+if provided)
 and converts them into a forwarding database in
 .BR /etc/aliases.cdb .
 The forwarding database can be used by
@@ -60,12 +63,6 @@
 and
 .BR bill .
 
-.B COMPATIBILITY WARNING:
-.B newaliases
-does not support file deliveries.
-You can use the file delivery mechanism described in
-.B dot-qmail(5)
-instead.
 .SH "SIMPLE ALIASES"
 The simplest type of forwarding instruction
 is a line of the form
@@ -82,30 +79,6 @@
 .I alias
 without regard to case.
 
-Forwarding instructions are cumulative.
-If
-.I recip
-is itself an alias,
-messages to
-.I alias
-will be forwarded the same way as
-messages to
-.IR recip .
-For example, with the following instructions,
-messages to
-.B postmaster@heaven.af.mil
-or
-.B root@heaven.af.mil
-will be delivered to Bob:
-
-.EX
-   postmaster@heaven.af.mil: bob@heaven.af.mil
-.EE
-.br
-.EX
-   root@heaven.af.mil: postmaster@heaven.af.mil
-.EE
-
 .B COMPATIBILITY WARNING:
 With
 sendmail,
@@ -146,10 +119,35 @@
 can have the form
 .I user@host.dom
 for one user at one host,
-.I @host.dom
-for all users at one host, or
 .I user
-for one user at all hosts.
+for one user at all hosts, or
+.I @host.dom
+for all users at one host. They match in this order.
+
+There is also wildcarding in the username field as 
+implemented by
+.B qmail-local
+with the
+.I -default
+postfix. For example, when looking up
+.I foo-bar-baz
+we first look for 
+.IR foo-bar-baz ,
+then for
+.IR foo-bar-default ,
+then for
+.IR foo-default .
+
+Here's an example that shows how the two types of wildcarding
+relate. When looking up the incoming e-mail address
+.I foo-bar@host.dom
+we look for the following patterns in the following order:
+.IR foo-bar@host.dom ,
+.IR foo-default@host.dom ,
+.IR foo-bar ,
+.IR foo-default ,
+then
+.IR @host.dom .
 
 .B COMPATIBILITY WARNING:
 sendmail
@@ -164,6 +162,14 @@
 but it then treats it the same way as
 .IR user ,
 applying to all local hosts and virtual domains.
+
+.B COMBATIBILITY WARNING:
+sendmail does not support the
+.B qmail-local
+style 
+.I -default
+username wildcards.
+
 .SH "ADDRESS FORMATS"
 Addresses in
 .B /etc/aliases
@@ -275,6 +281,31 @@
 when a message arrives for
 .BR weather .
 
+Before fastforward was patched with the 
+.I fastforward-idx
+patch by dharris@drh.net, this used the program
+.B preline
+program to insert the
+inserts
+a UUCP-style
+.B From_
+line, a
+.B Return-Path
+line, and a
+.B Delivered-To
+line at the top of each message.
+Now, for compatability with 
+.I qmail-local
+preline is not used.
+
+To use
+.B preline
+in this manner, begin the forwarding command with two vertical bars:
+
+.EX
+   weather: "||weather-server"
+.EE
+
 .B COMPATIBILITY WARNING:
 Internet addresses can legitimately start with
 a slash or vertical bar.
@@ -287,6 +318,29 @@
 .B COMPATIBILITY WARNING:
 .B newaliases
 does not allow a vertical bar before double quotes.
+
+.B COMPATIBILITY WARNING:
+.B sendmail
+does not allow the double vertical bar syntax.
+
+.SH "FILE DELIVERY"
+A recipient address of the form
+.B :file:\fIfile
+causes the mail to be delivered to either a
+.I maildir
+or
+.I mailbox
+maildrop.
+If
+.I file
+ends in a slash, then the the filename will be interpreted
+as a
+.IR maildirectory ,
+else a
+.I mbox file
+maildrop.
+
+
 .SH "INCLUDE FILES"
 A recipient address of the form
 .B :include:\fIfile
@@ -347,6 +401,14 @@
 .BR owner-\fIlist ,
 so that bounces go back to
 .BR owner-\fIlist .
+
+The
+.B list\fI-owner
+and
+.B list\fI-owner-default
+alias owner rewriting as implemented by 
+.I qmail-local
+is also supported by fastforward.
 
 .B COMPATIBILITY WARNING:
 When an alias includes the same recipient both inside and outside
diff -r -P -u3 fastforward-0.51_orig/newaliases.c fastforward-0.51_my/newaliases.c
--- fastforward-0.51_orig/newaliases.c	Tue May 19 12:25:42 1998
+++ fastforward-0.51_my/newaliases.c	Tue Jun 15 00:32:56 1999
@@ -101,6 +101,24 @@
   if (!stralloc_0(&instr)) nomem();
 }
 
+void gotfile()
+{
+  token822_reverse(&tokaddr);
+  if (token822_unquote(&address,&tokaddr) != 1) nomem();
+  tokaddr.len = 0;
+
+  if (!address.len)
+    strerr_die2x(111,FATAL,"empty :file: filenames not permitted");
+  if (byte_chr(address.s,address.len,'\0') < address.len)
+    strerr_die2x(111,FATAL,"NUL not permitted in :file: filenames");
+
+  if (!stralloc_cats(&instr,"$")) nomem();
+  if ((address.s[0] != '.') && (address.s[0] != '/'))
+    if (!stralloc_cats(&instr,"./")) nomem();
+  if (!stralloc_cat(&instr,&address)) nomem();
+  if (!stralloc_0(&instr)) nomem();
+}
+
 void gotaddr()
 {
   int i;
@@ -131,10 +149,17 @@
     if (address.s[0] == '|') {
       if (byte_chr(address.s,address.len,'\0') < address.len)
         strerr_die2x(111,FATAL,"NUL not permitted in program names");
-      if (!stralloc_cats(&instr,"!")) nomem();
-      if (!stralloc_catb(&instr,address.s + 1,address.len - 1)) nomem();
-      if (!stralloc_0(&instr)) nomem();
-      return;
+      if (address.s[1] != '|') {
+        if (!stralloc_cats(&instr,"|")) nomem();
+        if (!stralloc_catb(&instr,address.s + 1,address.len - 1)) nomem();
+        if (!stralloc_0(&instr)) nomem();
+        return;
+      } else {
+        if (!stralloc_cats(&instr,"!")) nomem();
+        if (!stralloc_catb(&instr,address.s + 2,address.len - 2)) nomem();
+        if (!stralloc_0(&instr)) nomem();
+        return;
+      }
     }
 
 
@@ -206,11 +231,22 @@
         if (t >= beginning + 2)
           if (t[-2].type == TOKEN822_COLON)
             if (t[-1].type == TOKEN822_ATOM)
+            {
               if (t[-1].slen == 7)
+              {
                 if (!byte_diff(t[-1].s,7,"include")) {
                   gotincl();
                   t -= 2;
                 }
+              }
+              else if (t[-1].slen == 4)
+              {
+                if (!byte_diff(t[-1].s,4,"file")) {
+                  gotfile();
+                  t -= 2;
+                }
+              }
+            }
         break; /*XXX*/
       case TOKEN822_RIGHT:
         if (tokaddr.len) gotaddr();
@@ -250,6 +286,8 @@
 
 void doit()
 {
+  int at;
+
   if (!instr.len) {
     if (target.len) parseerr();
     return;
@@ -257,6 +295,21 @@
 
   if (!target.len) parseerr();
 
+  /* Add the envelope rewriting info to the CDB file */
+
+  /* NOTE -- Mon Jun 14 1999 David Harris <dharris@drh.net>
+             This code to rewrite the envelope sender is weird, but it's designed
+             to exactly mimic qmail-local. */
+
+  /* Note: It is possible to have both baz-owner and owner-baz, so that baz gets two
+     ownsership records. Because of the CDB internals, only the first will be recognized. */
+
+  /* Note: I changed the form of the data stored for the "&" records. The origional
+     fastforward implementation stored the first e-mail address specified in the rule.
+     Here we store the address of the rule that genereated the instruction... this way
+     the rule can consist of, say, programs and it will still work. Also, this is the way
+     that qmail-local does things. */
+
   if (stralloc_starts(&target,"owner-")) {
     if (!stralloc_copys(&key,"?")) nomem();
     if (!stralloc_catb(&key,target.s + 6,target.len - 6)) nomem();
@@ -264,24 +317,61 @@
     if (cdbmss_add(&cdbmss,key.s,key.len,fulltarget.s,fulltarget.len) == -1) writeerr();
   }
 
+  for ( at = target.len - 1 ; at >= 0 ; at-- ) if ( target.s[at] == '@' ) break;
+  if (at >= 6 )
+    if (!byte_diff(target.s + at - 6, 6, "-owner")) {
+      if (!stralloc_copys(&key,"?")) nomem();
+      if (!stralloc_catb(&key,target.s,at - 6)) nomem();
+      if (!stralloc_catb(&key,target.s + at,target.len - at)) nomem();
+      case_lowerb(key.s,key.len);
+      if (cdbmss_add(&cdbmss,key.s,key.len,fulltarget.s,fulltarget.len) == -1) writeerr();
+    }
+
+  /* Add the instructions to the CDB file */
+
   if (!stralloc_copys(&key,":")) nomem();
   if (!stralloc_cat(&key,&target)) nomem();
   case_lowerb(key.s,key.len);
   if (cdbmss_add(&cdbmss,key.s,key.len,instr.s,instr.len) == -1) writeerr();
 }
 
-void main()
+void main(argc,argv)
+int argc;
+int **argv;
 {
   int fd;
 
+  stralloc aliases = {0};
+  stralloc tmp = {0};
+  stralloc bin = {0};
+
   umask(033);
   readcontrols();
 
-  fd = open_read("/etc/aliases");
+  if ( argc < 1 )
+  {
+    if (!stralloc_copys(&aliases, "/etc/aliases")) nomem();
+  }
+  else
+  {
+    if (!stralloc_copys(&aliases, argv[1])) nomem();
+  }
+
+  if (!stralloc_copy(&tmp, &aliases)) nomem();
+  if (!stralloc_cats(&tmp, ".tmp")) nomem();
+  if (!stralloc_0(&tmp)) nomem();
+
+  if (!stralloc_copy(&bin, &aliases)) nomem();
+  if (!stralloc_cats(&bin, ".cdb")) nomem();
+  if (!stralloc_0(&bin)) nomem();
+
+  if (!stralloc_0(&aliases)) nomem();
+
+  fd = open_read(aliases.s);
   if (fd == -1) readerr();
   substdio_fdbuf(&ssin,read,fd,inbuf,sizeof inbuf);
 
-  fd = open_trunc("/etc/aliases.tmp");
+  fd = open_trunc(tmp.s);
   if (fd == -1) strerr_die2sys(111,FATAL,"unable to create /etc/aliases.tmp: ");
   if (cdbmss_start(&cdbmss,fd) == -1) writeerr();
 
@@ -314,7 +404,7 @@
   if (fsync(fd) == -1) writeerr();
   if (close(fd) == -1) writeerr(); /* NFS stupidity */
 
-  if (rename("/etc/aliases.tmp","/etc/aliases.cdb") == -1)
+  if (rename(tmp.s,bin.s) == -1)
     strerr_die2sys(111,FATAL,"unable to move /etc/aliases.tmp to /etc/aliases.cdb: ");
   
   _exit(0);
diff -r -P -u3 fastforward-0.51_orig/now.c fastforward-0.51_my/now.c
--- fastforward-0.51_orig/now.c	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/now.c	Tue Jun 15 00:20:19 1999
@@ -0,0 +1,8 @@
+#include <time.h>
+#include "datetime.h"
+#include "now.h"
+
+datetime_sec now()
+{
+  return time((long *) 0);
+}
diff -r -P -u3 fastforward-0.51_orig/now.h fastforward-0.51_my/now.h
--- fastforward-0.51_orig/now.h	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/now.h	Tue Jun 15 00:20:19 1999
@@ -0,0 +1,8 @@
+#ifndef NOW_H
+#define NOW_H
+
+#include "datetime.h"
+
+extern datetime_sec now();
+
+#endif
diff -r -P -u3 fastforward-0.51_orig/open_append.c fastforward-0.51_my/open_append.c
--- fastforward-0.51_orig/open_append.c	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/open_append.c	Tue Jun 15 00:20:19 1999
@@ -0,0 +1,6 @@
+#include <sys/types.h>
+#include <fcntl.h>
+#include "open.h"
+
+int open_append(fn) char *fn;
+{ return open(fn,O_WRONLY | O_NDELAY | O_APPEND | O_CREAT,0600); }
diff -r -P -u3 fastforward-0.51_orig/open_excl.c fastforward-0.51_my/open_excl.c
--- fastforward-0.51_orig/open_excl.c	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/open_excl.c	Tue Jun 15 00:20:19 1999
@@ -0,0 +1,6 @@
+#include <sys/types.h>
+#include <fcntl.h>
+#include "open.h"
+
+int open_excl(fn) char *fn;
+{ return open(fn,O_WRONLY | O_EXCL | O_CREAT,0644); }
diff -r -P -u3 fastforward-0.51_orig/printforward.c fastforward-0.51_my/printforward.c
--- fastforward-0.51_orig/printforward.c	Tue May 19 12:25:42 1998
+++ fastforward-0.51_my/printforward.c	Sun Jun 13 00:10:58 1999
@@ -130,6 +130,11 @@
             printsafe(data.s + i,j - i);
             print("\n");
           }
+          else if (data.s[i] == '$') {
+            print(", ");
+            printsafe(data.s + i,j - i);
+            print("\n");
+          }
           else badformat();
           i = j + 1;
         }
diff -r -P -u3 fastforward-0.51_orig/seek_cur.c fastforward-0.51_my/seek_cur.c
--- fastforward-0.51_orig/seek_cur.c	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/seek_cur.c	Tue Jun 15 00:20:19 1999
@@ -0,0 +1,7 @@
+#include <sys/types.h>
+#include "seek.h"
+
+#define CUR 1 /* sigh */
+
+seek_pos seek_cur(fd) int fd;
+{ return lseek(fd,(off_t) 0,CUR); }
diff -r -P -u3 fastforward-0.51_orig/seek_end.c fastforward-0.51_my/seek_end.c
--- fastforward-0.51_orig/seek_end.c	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/seek_end.c	Tue Jun 15 00:20:19 1999
@@ -0,0 +1,7 @@
+#include <sys/types.h>
+#include "seek.h"
+
+#define END 2 /* sigh */
+
+int seek_end(fd) int fd;
+{ if (lseek(fd,(off_t) 0,END) == -1) return -1; return 0; }
diff -r -P -u3 fastforward-0.51_orig/seek_trunc.c fastforward-0.51_my/seek_trunc.c
--- fastforward-0.51_orig/seek_trunc.c	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/seek_trunc.c	Tue Jun 15 00:20:19 1999
@@ -0,0 +1,5 @@
+#include <sys/types.h>
+#include "seek.h"
+
+int seek_trunc(fd,pos) int fd; seek_pos pos;
+{ return ftruncate(fd,(off_t) pos); }
diff -r -P -u3 fastforward-0.51_orig/setforward.1 fastforward-0.51_my/setforward.1
--- fastforward-0.51_orig/setforward.1	Tue May 19 12:25:42 1998
+++ fastforward-0.51_my/setforward.1	Tue Jun 15 21:24:11 1999
@@ -32,6 +32,11 @@
 alone.
 
 The forwarding database format is portable across machines.
+
+.B WARNING: The format this man page describes has been updated with the 
+.B fastforward-idx patch from dharris@drh.net, but i've not yet bothered
+.B to update this man page.
+
 .SH "INSTRUCTION FORMAT"
 A forwarding instruction contains a
 .I target\fR,
diff -r -P -u3 fastforward-0.51_orig/setforward.c fastforward-0.51_my/setforward.c
--- fastforward-0.51_orig/setforward.c	Tue May 19 12:25:42 1998
+++ fastforward-0.51_my/setforward.c	Sun Jun 13 00:10:57 1999
@@ -132,6 +132,10 @@
           if (!stralloc_cat(&instr,&command)) nomem();
           if (!stralloc_0(&instr)) nomem();
         }
+        else if (command.s[0] == '$') {
+          if (!stralloc_cat(&instr,&command)) nomem();
+          if (!stralloc_0(&instr)) nomem();
+        }
         else {
           if (command.len > 800) longaddress();
           if (command.s[0] != '&')
diff -r -P -u3 fastforward-0.51_orig/sig_alarm.c fastforward-0.51_my/sig_alarm.c
--- fastforward-0.51_orig/sig_alarm.c	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/sig_alarm.c	Tue Jun 15 00:20:19 1999
@@ -0,0 +1,7 @@
+#include <signal.h>
+#include "sig.h"
+
+void sig_alarmblock() { sig_block(SIGALRM); }
+void sig_alarmunblock() { sig_unblock(SIGALRM); }
+void sig_alarmcatch(f) void (*f)(); { sig_catch(SIGALRM,f); }
+void sig_alarmdefault() { sig_catch(SIGALRM,SIG_DFL); }
diff -r -P -u3 fastforward-0.51_orig/sig_block.c fastforward-0.51_my/sig_block.c
--- fastforward-0.51_orig/sig_block.c	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/sig_block.c	Tue Jun 15 00:20:19 1999
@@ -0,0 +1,40 @@
+#include <signal.h>
+#include "sig.h"
+#include "hassgprm.h"
+
+void sig_block(sig)
+int sig;
+{
+#ifdef HASSIGPROCMASK
+  sigset_t ss;
+  sigemptyset(&ss);
+  sigaddset(&ss,sig);
+  sigprocmask(SIG_BLOCK,&ss,(sigset_t *) 0);
+#else
+  sigblock(1 << (sig - 1));
+#endif
+}
+
+void sig_unblock(sig)
+int sig;
+{
+#ifdef HASSIGPROCMASK
+  sigset_t ss;
+  sigemptyset(&ss);
+  sigaddset(&ss,sig);
+  sigprocmask(SIG_UNBLOCK,&ss,(sigset_t *) 0);
+#else
+  sigsetmask(sigsetmask(~0) & ~(1 << (sig - 1)));
+#endif
+}
+
+void sig_blocknone()
+{
+#ifdef HASSIGPROCMASK
+  sigset_t ss;
+  sigemptyset(&ss);
+  sigprocmask(SIG_SETMASK,&ss,(sigset_t *) 0);
+#else
+  sigsetmask(0);
+#endif
+}
diff -r -P -u3 fastforward-0.51_orig/tryflock.c fastforward-0.51_my/tryflock.c
--- fastforward-0.51_orig/tryflock.c	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/tryflock.c	Tue Jun 15 00:20:19 1999
@@ -0,0 +1,8 @@
+#include <sys/types.h>
+#include <sys/file.h>
+#include <fcntl.h>
+
+void main()
+{
+  flock(0,LOCK_EX | LOCK_UN | LOCK_NB);
+}
diff -r -P -u3 fastforward-0.51_orig/trysgprm.c fastforward-0.51_my/trysgprm.c
--- fastforward-0.51_orig/trysgprm.c	Wed Dec 31 19:00:00 1969
+++ fastforward-0.51_my/trysgprm.c	Tue Jun 15 00:20:19 1999
@@ -0,0 +1,10 @@
+#include <signal.h>
+
+void main()
+{
+  sigset_t ss;
+ 
+  sigemptyset(&ss);
+  sigaddset(&ss,SIGCHLD);
+  sigprocmask(SIG_SETMASK,&ss,(sigset_t *) 0);
+}
