Import fastforward 0.51 djb djb/0.51
authorMark Wooding <mdw@distorted.org.uk>
Wed, 15 Feb 2006 18:27:27 +0000 (18:27 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Wed, 15 Feb 2006 18:27:27 +0000 (18:27 +0000)
142 files changed:
ALIASES [new file with mode: 0644]
BLURB [new file with mode: 0644]
CHANGES [new file with mode: 0644]
FILES [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
SYSDEPS [new file with mode: 0644]
TARGETS [new file with mode: 0644]
THANKS [new file with mode: 0644]
TODO [new file with mode: 0644]
VERSION [new file with mode: 0644]
alloc.c [new file with mode: 0644]
alloc.h [new file with mode: 0644]
alloc_re.c [new file with mode: 0644]
auto-str.c [new file with mode: 0644]
auto_qmail.h [new file with mode: 0644]
byte.h [new file with mode: 0644]
byte_chr.c [new file with mode: 0644]
byte_copy.c [new file with mode: 0644]
byte_cr.c [new file with mode: 0644]
byte_diff.c [new file with mode: 0644]
case.h [new file with mode: 0644]
case_lowerb.c [new file with mode: 0644]
cdb.h [new file with mode: 0644]
cdb_hash.c [new file with mode: 0644]
cdb_seek.c [new file with mode: 0644]
cdb_unpack.c [new file with mode: 0644]
cdbmake.h [new file with mode: 0644]
cdbmake_add.c [new file with mode: 0644]
cdbmake_hash.c [new file with mode: 0644]
cdbmake_pack.c [new file with mode: 0644]
cdbmss.c [new file with mode: 0644]
cdbmss.h [new file with mode: 0644]
coe.c [new file with mode: 0644]
coe.h [new file with mode: 0644]
conf-cc [new file with mode: 0644]
conf-ld [new file with mode: 0644]
conf-qmail [new file with mode: 0644]
control.c [new file with mode: 0644]
control.h [new file with mode: 0644]
env.h [new file with mode: 0644]
envread.c [new file with mode: 0644]
error.c [new file with mode: 0644]
error.h [new file with mode: 0644]
error_str.c [new file with mode: 0644]
exit.h [new file with mode: 0644]
fastforward.1 [new file with mode: 0644]
fastforward.c [new file with mode: 0644]
fd.h [new file with mode: 0644]
fd_copy.c [new file with mode: 0644]
fd_move.c [new file with mode: 0644]
find-systype.sh [new file with mode: 0644]
fmt.h [new file with mode: 0644]
fmt_ulong.c [new file with mode: 0644]
fork.h1 [new file with mode: 0644]
fork.h2 [new file with mode: 0644]
gen_alloc.h [new file with mode: 0644]
gen_allocdefs.h [new file with mode: 0644]
getln.c [new file with mode: 0644]
getln.h [new file with mode: 0644]
getln2.c [new file with mode: 0644]
hier.c [new file with mode: 0644]
install.c [new file with mode: 0644]
instcheck.c [new file with mode: 0644]
make-compile.sh [new file with mode: 0644]
make-load.sh [new file with mode: 0644]
make-makelib.sh [new file with mode: 0644]
newaliases.1 [new file with mode: 0644]
newaliases.c [new file with mode: 0644]
newinclude.1 [new file with mode: 0644]
newinclude.c [new file with mode: 0644]
open.h [new file with mode: 0644]
open_read.c [new file with mode: 0644]
open_trunc.c [new file with mode: 0644]
printforward.1 [new file with mode: 0644]
printforward.c [new file with mode: 0644]
printmaillist.1 [new file with mode: 0644]
printmaillist.c [new file with mode: 0644]
qmail.c [new file with mode: 0644]
qmail.h [new file with mode: 0644]
readwrite.h [new file with mode: 0644]
scan.h [new file with mode: 0644]
scan_ulong.c [new file with mode: 0644]
seek.h [new file with mode: 0644]
seek_set.c [new file with mode: 0644]
setforward.1 [new file with mode: 0644]
setforward.c [new file with mode: 0644]
setmaillist.1 [new file with mode: 0644]
setmaillist.c [new file with mode: 0644]
sgetopt.c [new file with mode: 0644]
sgetopt.h [new file with mode: 0644]
sig.h [new file with mode: 0644]
sig_catch.c [new file with mode: 0644]
sig_pipe.c [new file with mode: 0644]
slurpclose.c [new file with mode: 0644]
slurpclose.h [new file with mode: 0644]
str.h [new file with mode: 0644]
str_chr.c [new file with mode: 0644]
str_cpy.c [new file with mode: 0644]
str_diff.c [new file with mode: 0644]
str_diffn.c [new file with mode: 0644]
str_len.c [new file with mode: 0644]
str_rchr.c [new file with mode: 0644]
stralloc.h [new file with mode: 0644]
stralloc_arts.c [new file with mode: 0644]
stralloc_cat.c [new file with mode: 0644]
stralloc_catb.c [new file with mode: 0644]
stralloc_cats.c [new file with mode: 0644]
stralloc_copy.c [new file with mode: 0644]
stralloc_eady.c [new file with mode: 0644]
stralloc_opyb.c [new file with mode: 0644]
stralloc_opys.c [new file with mode: 0644]
stralloc_pend.c [new file with mode: 0644]
strerr.h [new file with mode: 0644]
strerr_die.c [new file with mode: 0644]
strerr_sys.c [new file with mode: 0644]
strset.c [new file with mode: 0644]
strset.h [new file with mode: 0644]
subfd.h [new file with mode: 0644]
subfderr.c [new file with mode: 0644]
subfdins.c [new file with mode: 0644]
subfdouts.c [new file with mode: 0644]
subgetopt.c [new file with mode: 0644]
subgetopt.h [new file with mode: 0644]
substdi.c [new file with mode: 0644]
substdio.c [new file with mode: 0644]
substdio.h [new file with mode: 0644]
substdio_copy.c [new file with mode: 0644]
substdo.c [new file with mode: 0644]
token822.c [new file with mode: 0644]
token822.h [new file with mode: 0644]
trycpp.c [new file with mode: 0644]
trysgact.c [new file with mode: 0644]
tryulong32.c [new file with mode: 0644]
tryvfork.c [new file with mode: 0644]
trywaitp.c [new file with mode: 0644]
uint32.h1 [new file with mode: 0644]
uint32.h2 [new file with mode: 0644]
wait.h [new file with mode: 0644]
wait_pid.c [new file with mode: 0644]
warn-auto.sh [new file with mode: 0644]

diff --git a/ALIASES b/ALIASES
new file mode 100644 (file)
index 0000000..5775487
--- /dev/null
+++ b/ALIASES
@@ -0,0 +1,100 @@
+--- Setting up /etc/aliases
+
+Create /etc/aliases if it does not already exist. You should include
+forwarding instructions for mailer-daemon, postmaster, and root:
+
+        root: alias
+        postmaster: alias
+        mailer-daemon: alias
+
+Note that qmail never delivers mail to root. The instructions shown here
+will deliver messages to the mailbox of the ``alias'' user.
+
+For further details on the format of /etc/aliases, and a list of
+sendmail compatibility warnings, see the newaliases man page.
+
+
+--- Compiling /etc/aliases
+
+Once /etc/aliases is ready, run newaliases to compile /etc/aliases into
+/etc/aliases.cdb:
+
+   # newaliases
+
+Review /etc/aliases.cdb to make sure it has the instructions you want:
+
+   % printforward < /etc/aliases.cdb | more
+
+For the format of printforward's output, see the setforward man page.
+
+If you change /etc/aliases you will have to run newaliases again. You
+may want to add a comment at the top of /etc/aliases as a reminder.
+
+
+--- Compiling :include: files
+
+If you have an :include: file, say /etc/staff-list, compile it into
+/etc/staff-list.bin:
+
+   # newinclude /etc/staff-list
+
+See the newinclude man page for a list of sendmail compatibility
+warnings. Review /etc/staff-list.bin:
+
+   % printmaillist < /etc/staff-list.bin | more
+
+For the format of printmaillist's output, see the setmaillist man page.
+
+If you change /etc/staff-list you will have to run newinclude again. You
+may want to add a comment at the top of /etc/staff-list as a reminder.
+
+
+--- Configuring qmail to use /etc/aliases
+
+To activate /etc/aliases, put this line into ~alias/.qmail-default:
+
+        | fastforward -d /etc/aliases.cdb
+
+If qmail is already running, make sure to chmod +t ~alias before you
+edit .qmail files in ~alias, and chmod -t ~alias after.
+
+
+--- Testing aliases
+
+To check the expansion of postmaster@your.host without sending any mail:
+
+   % env DEFAULT=postmaster HOST=your.host fastforward -nd /etc/aliases.cdb
+
+Replace your.host with your fully qualified domain name. Make sure to
+include the -nd.
+
+Next, try sending a message to postmaster@your.host. Watch the qmail log
+and the final mailbox to make sure the alias works the way you want.
+
+You can check other aliases the same way.
+
+
+--- Using /etc/aliases for virtual domains
+
+To put all addresses at virt.dom under control of /etc/aliases, add
+
+        virt.dom:alias
+
+to /var/qmail/control/virtualdomains, and give qmail-send a HUP signal.
+Also add
+
+        virt.dom
+
+to /var/qmail/control/rcpthosts so that qmail accepts mail for virt.dom
+from remote hosts. Now you can handle virt.dom in /etc/aliases:
+
+        billing@virt.dom: joe, fred
+        (this line catches all other addresses)@virt.dom: joe
+
+Note that postmaster@virt.dom will go to joe; the @virt.dom instruction
+overrides the postmaster instruction. Note, however, that other .qmail
+files in ~alias override ~alias/.qmail-default, so you can set up
+~alias/.qmail-postmaster to handle postmaster@everything.
+
+Beware that sendmail does not support domain-specific instructions in
+/etc/aliases; they are a fastforward feature.
diff --git a/BLURB b/BLURB
new file mode 100644 (file)
index 0000000..9604f46
--- /dev/null
+++ b/BLURB
@@ -0,0 +1,20 @@
+fastforward handles qmail forwarding according to a cdb database. It can
+create forwarding databases from a sendmail-style /etc/aliases or from
+user-oriented virtual-domain tables.
+
+fastforward supports external mailing lists, stored in a binary format
+for fast access. It has a tool to convert sendmail-style include files
+into binary lists.
+
+fastforward is more reliable than sendmail. sendmail can't deal with
+long aliases, or deeply nested aliases, or deeply nested include files;
+fastforward has no limits other than memory. sendmail can produce
+corrupted alias files if the system crashes; fastforward is crashproof.
+
+fastforward's database-building tools are much faster than sendmail's
+newaliases. Even better, fastforward deliveries don't pause while the
+database is being rebuilt.
+
+fastforward does not support insecure sendmail-style program deliveries
+from include files; you can use qmail's secure built-in mechanisms
+instead. fastforward does support program deliveries from /etc/aliases.
diff --git a/CHANGES b/CHANGES
new file mode 100644 (file)
index 0000000..813ba6f
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,15 @@
+19980519 version: fastforward 0.51, alpha.
+19980519 code: added preline variant in fastforward; updated setforward,
+         printforward, newaliases accordingly.
+19980519 doc: added virtual-domains section to ALIASES.
+19980519 doc: used -d in ALIASES.
+19980519 code: added fastforward -d.
+19980519 doc: put version and home page into fastforward.1.
+19980519 doc: simplified INSTALL.
+19980519 doc: added ALIASES.
+19980519 doc: expanded explanation in conf-qmail.
+19980519 code: switched to new install.
+19980420 portability problem: IRIX doesn't have vfork(). impact:
+         couldn't compile under IRIX. fix: include fork.h in
+        fastforward.c. tnx JB.
+19980420 fastforward 0.50, alpha.
diff --git a/FILES b/FILES
new file mode 100644 (file)
index 0000000..2f00b77
--- /dev/null
+++ b/FILES
@@ -0,0 +1,142 @@
+BLURB
+README
+TODO
+THANKS
+CHANGES
+FILES
+TARGETS
+VERSION
+SYSDEPS
+Makefile
+ALIASES
+INSTALL
+hier.c
+fastforward.1
+fastforward.c
+printforward.1
+printforward.c
+setforward.1
+setforward.c
+printmaillist.1
+printmaillist.c
+setmaillist.1
+setmaillist.c
+newaliases.1
+newaliases.c
+newinclude.1
+newinclude.c
+auto-str.c
+install.c
+instcheck.c
+conf-cc
+conf-ld
+find-systype.sh
+make-compile.sh
+make-load.sh
+make-makelib.sh
+trycpp.c
+warn-auto.sh
+alloc.h
+alloc.c
+alloc_re.c
+case.h
+case_lowerb.c
+cdb.h
+cdb_hash.c
+cdb_seek.c
+cdb_unpack.c
+cdbmake.h
+cdbmake_add.c
+cdbmake_hash.c
+cdbmake_pack.c
+cdbmss.h
+cdbmss.c
+control.h
+control.c
+env.h
+envread.c
+error.h
+error.c
+error_str.c
+fd.h
+fd_copy.c
+fd_move.c
+fork.h1
+fork.h2
+tryvfork.c
+fmt.h
+fmt_ulong.c
+scan.h
+scan_ulong.c
+getln.h
+getln.c
+getln2.c
+sgetopt.h
+sgetopt.c
+subgetopt.h
+subgetopt.c
+open.h
+open_read.c
+open_trunc.c
+conf-qmail
+auto_qmail.h
+qmail.h
+qmail.c
+seek.h
+seek_set.c
+sig.h
+sig_catch.c
+sig_pipe.c
+trysgact.c
+byte.h
+byte_chr.c
+byte_copy.c
+byte_cr.c
+byte_diff.c
+str.h
+str_chr.c
+str_cpy.c
+str_diff.c
+str_diffn.c
+str_len.c
+str_rchr.c
+gen_alloc.h
+gen_allocdefs.h
+stralloc.h
+stralloc_eady.c
+stralloc_pend.c
+stralloc_copy.c
+stralloc_opyb.c
+stralloc_opys.c
+stralloc_cat.c
+stralloc_catb.c
+stralloc_cats.c
+stralloc_arts.c
+strset.h
+strset.c
+substdio.h
+substdio.c
+substdi.c
+substdo.c
+substdio_copy.c
+subfd.h
+subfderr.c
+subfdouts.c
+subfdins.c
+readwrite.h
+exit.h
+token822.h
+token822.c
+uint32.h1
+uint32.h2
+tryulong32.c
+wait.h
+wait_pid.c
+trywaitp.c
+strerr.h
+strerr_sys.c
+strerr_die.c
+slurpclose.h
+slurpclose.c
+coe.h
+coe.c
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..7e564f4
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,21 @@
+Like any other piece of software (and information generally),
+fastforward comes with NO WARRANTY.
+
+
+Things you have to decide before starting:
+
+* Where qmail is installed, normally /var/qmail. (To change this
+directory, edit conf-qmail now.)
+
+
+How to install:
+
+ 1. Create and install the programs and man pages:
+       # make setup check
+
+ 2. To configure qmail to use /etc/aliases, see ALIASES.
+
+
+That's it! To report success:
+       % ( echo 'First M. Last'; cat `cat SYSDEPS` ) | mail djb-qst@cr.yp.to
+Replace First M. Last with your name.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..c16d93c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,593 @@
+# Don't edit Makefile! Use conf-* for configuration.
+
+SHELL=/bin/sh
+
+default: it
+
+alloc.a: \
+makelib alloc.o alloc_re.o
+       ./makelib alloc.a alloc.o alloc_re.o
+
+alloc.o: \
+compile alloc.c alloc.h error.h
+       ./compile alloc.c
+
+alloc_re.o: \
+compile alloc_re.c alloc.h byte.h
+       ./compile alloc_re.c
+
+auto-ccld.sh: \
+conf-cc conf-ld warn-auto.sh
+       ( cat warn-auto.sh; \
+       echo CC=\'`head -1 conf-cc`\'; \
+       echo LD=\'`head -1 conf-ld`\' \
+       ) > auto-ccld.sh
+
+auto-str: \
+load auto-str.o substdio.a error.a str.a
+       ./load auto-str substdio.a error.a str.a 
+
+auto-str.o: \
+compile auto-str.c substdio.h readwrite.h exit.h
+       ./compile auto-str.c
+
+auto_qmail.c: \
+auto-str conf-qmail
+       ./auto-str auto_qmail `head -1 conf-qmail` > auto_qmail.c
+
+auto_qmail.o: \
+compile auto_qmail.c
+       ./compile auto_qmail.c
+
+byte_chr.o: \
+compile byte_chr.c byte.h
+       ./compile byte_chr.c
+
+byte_copy.o: \
+compile byte_copy.c byte.h
+       ./compile byte_copy.c
+
+byte_cr.o: \
+compile byte_cr.c byte.h
+       ./compile byte_cr.c
+
+byte_diff.o: \
+compile byte_diff.c byte.h
+       ./compile byte_diff.c
+
+case.a: \
+makelib case_lowerb.o
+       ./makelib case.a case_lowerb.o
+
+case_lowerb.o: \
+compile case_lowerb.c case.h
+       ./compile case_lowerb.c
+
+cdb.a: \
+makelib cdb_hash.o cdb_unpack.o cdb_seek.o
+       ./makelib cdb.a cdb_hash.o cdb_unpack.o cdb_seek.o
+
+cdb_hash.o: \
+compile cdb_hash.c cdb.h uint32.h
+       ./compile cdb_hash.c
+
+cdb_seek.o: \
+compile cdb_seek.c cdb.h uint32.h
+       ./compile cdb_seek.c
+
+cdb_unpack.o: \
+compile cdb_unpack.c cdb.h uint32.h
+       ./compile cdb_unpack.c
+
+cdbmake.a: \
+makelib cdbmake_pack.o cdbmake_hash.o cdbmake_add.o
+       ./makelib cdbmake.a cdbmake_pack.o cdbmake_hash.o \
+       cdbmake_add.o
+
+cdbmake_add.o: \
+compile cdbmake_add.c cdbmake.h uint32.h
+       ./compile cdbmake_add.c
+
+cdbmake_hash.o: \
+compile cdbmake_hash.c cdbmake.h uint32.h
+       ./compile cdbmake_hash.c
+
+cdbmake_pack.o: \
+compile cdbmake_pack.c cdbmake.h uint32.h
+       ./compile cdbmake_pack.c
+
+cdbmss.o: \
+compile cdbmss.c readwrite.h seek.h alloc.h cdbmss.h cdbmake.h \
+uint32.h substdio.h
+       ./compile cdbmss.c
+
+check: \
+it instcheck
+       ./instcheck
+
+coe.o: \
+compile coe.c coe.h
+       ./compile coe.c
+
+compile: \
+make-compile warn-auto.sh systype
+       ( cat warn-auto.sh; ./make-compile "`cat systype`" ) > \
+       compile
+       chmod 755 compile
+
+control.o: \
+compile control.c readwrite.h open.h getln.h stralloc.h gen_alloc.h \
+substdio.h error.h control.h alloc.h scan.h
+       ./compile control.c
+
+env.a: \
+makelib envread.o
+       ./makelib env.a envread.o
+
+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
+
+error.o: \
+compile error.c error.h
+       ./compile error.c
+
+error_str.o: \
+compile error_str.c error.h
+       ./compile error_str.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
+       ./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 
+
+fastforward.0: \
+fastforward.1
+       nroff -man fastforward.1 > fastforward.0
+
+fastforward.o: \
+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
+       ./compile fastforward.c
+
+fd.a: \
+makelib fd_copy.o fd_move.o
+       ./makelib fd.a fd_copy.o fd_move.o
+
+fd_copy.o: \
+compile fd_copy.c fd.h
+       ./compile fd_copy.c
+
+fd_move.o: \
+compile fd_move.c fd.h
+       ./compile fd_move.c
+
+find-systype: \
+find-systype.sh auto-ccld.sh
+       cat auto-ccld.sh find-systype.sh > find-systype
+       chmod 755 find-systype
+
+fmt_ulong.o: \
+compile fmt_ulong.c fmt.h
+       ./compile fmt_ulong.c
+
+fork.h: \
+compile load tryvfork.c fork.h1 fork.h2
+       ( ( ./compile tryvfork.c && ./load tryvfork ) >/dev/null \
+       2>&1 \
+       && cat fork.h2 || cat fork.h1 ) > fork.h
+       rm -f tryvfork.o tryvfork
+
+fs.a: \
+makelib fmt_ulong.o scan_ulong.o
+       ./makelib fs.a fmt_ulong.o scan_ulong.o
+
+getln.a: \
+makelib getln.o getln2.o
+       ./makelib getln.a getln.o getln2.o
+
+getln.o: \
+compile getln.c substdio.h byte.h stralloc.h gen_alloc.h getln.h
+       ./compile getln.c
+
+getln2.o: \
+compile getln2.c substdio.h stralloc.h gen_alloc.h byte.h getln.h
+       ./compile getln2.c
+
+getopt.a: \
+makelib subgetopt.o sgetopt.o
+       ./makelib getopt.a subgetopt.o sgetopt.o
+
+hassgact.h: \
+trysgact.c compile load
+       ( ( ./compile trysgact.c && ./load trysgact ) >/dev/null \
+       2>&1 \
+       && echo \#define HASSIGACTION 1 || exit 0 ) > hassgact.h
+       rm -f trysgact.o trysgact
+
+haswaitp.h: \
+trywaitp.c compile load
+       ( ( ./compile trywaitp.c && ./load trywaitp ) >/dev/null \
+       2>&1 \
+       && echo \#define HASWAITPID 1 || exit 0 ) > haswaitp.h
+       rm -f trywaitp.o trywaitp
+
+hier.o: \
+compile hier.c auto_qmail.h
+       ./compile hier.c
+
+install: \
+load install.o hier.o auto_qmail.o strerr.a substdio.a error.a open.a \
+str.a
+       ./load install hier.o auto_qmail.o strerr.a substdio.a \
+       error.a open.a str.a 
+
+install.o: \
+compile install.c substdio.h strerr.h error.h open.h readwrite.h \
+exit.h
+       ./compile install.c
+
+instcheck: \
+load instcheck.o hier.o auto_qmail.o strerr.a substdio.a error.a \
+str.a
+       ./load instcheck hier.o auto_qmail.o strerr.a substdio.a \
+       error.a str.a 
+
+instcheck.o: \
+compile instcheck.c strerr.h error.h readwrite.h exit.h
+       ./compile instcheck.c
+
+it: \
+prog man
+
+load: \
+make-load warn-auto.sh systype
+       ( cat warn-auto.sh; ./make-load "`cat systype`" ) > load
+       chmod 755 load
+
+make-compile: \
+make-compile.sh auto-ccld.sh
+       cat auto-ccld.sh make-compile.sh > make-compile
+       chmod 755 make-compile
+
+make-load: \
+make-load.sh auto-ccld.sh
+       cat auto-ccld.sh make-load.sh > make-load
+       chmod 755 make-load
+
+make-makelib: \
+make-makelib.sh auto-ccld.sh
+       cat auto-ccld.sh make-makelib.sh > make-makelib
+       chmod 755 make-makelib
+
+makelib: \
+make-makelib warn-auto.sh systype
+       ( cat warn-auto.sh; ./make-makelib "`cat systype`" ) > \
+       makelib
+       chmod 755 makelib
+
+man: \
+fastforward.0 printforward.0 setforward.0 newaliases.0 \
+printmaillist.0 setmaillist.0 newinclude.0
+
+newaliases: \
+load newaliases.o auto_qmail.o token822.o control.o cdbmss.o \
+cdbmake.a strerr.a getln.a substdio.a stralloc.a alloc.a error.a \
+str.a fs.a seek.a open.a case.a
+       ./load newaliases auto_qmail.o token822.o control.o \
+       cdbmss.o cdbmake.a strerr.a getln.a substdio.a stralloc.a \
+       alloc.a error.a str.a fs.a seek.a open.a case.a 
+
+newaliases.0: \
+newaliases.1
+       nroff -man newaliases.1 > newaliases.0
+
+newaliases.o: \
+compile newaliases.c substdio.h strerr.h stralloc.h gen_alloc.h \
+getln.h open.h readwrite.h token822.h gen_alloc.h control.h \
+auto_qmail.h case.h cdbmss.h cdbmake.h uint32.h substdio.h
+       ./compile newaliases.c
+
+newinclude: \
+load newinclude.o auto_qmail.o token822.o control.o getln.a strerr.a \
+stralloc.a env.a alloc.a substdio.a error.a str.a fs.a open.a wait.a \
+fd.a
+       ./load newinclude auto_qmail.o token822.o control.o \
+       getln.a strerr.a stralloc.a env.a alloc.a substdio.a \
+       error.a str.a fs.a open.a wait.a fd.a 
+
+newinclude.0: \
+newinclude.1
+       nroff -man newinclude.1 > newinclude.0
+
+newinclude.o: \
+compile newinclude.c substdio.h strerr.h stralloc.h gen_alloc.h \
+getln.h open.h readwrite.h token822.h gen_alloc.h control.h \
+auto_qmail.h env.h
+       ./compile newinclude.c
+
+open.a: \
+makelib open_read.o open_trunc.o
+       ./makelib open.a open_read.o open_trunc.o
+
+open_read.o: \
+compile open_read.c open.h
+       ./compile open_read.c
+
+open_trunc.o: \
+compile open_trunc.c open.h
+       ./compile open_trunc.c
+
+printforward: \
+load printforward.o cdb.a strerr.a substdio.a stralloc.a alloc.a \
+error.a str.a
+       ./load printforward cdb.a strerr.a substdio.a stralloc.a \
+       alloc.a error.a str.a 
+
+printforward.0: \
+printforward.1
+       nroff -man printforward.1 > printforward.0
+
+printforward.o: \
+compile printforward.c substdio.h subfd.h substdio.h strerr.h \
+stralloc.h gen_alloc.h cdb.h uint32.h
+       ./compile printforward.c
+
+printmaillist: \
+load printmaillist.o getln.a strerr.a substdio.a stralloc.a alloc.a \
+error.a str.a
+       ./load printmaillist getln.a strerr.a substdio.a \
+       stralloc.a alloc.a error.a str.a 
+
+printmaillist.0: \
+printmaillist.1
+       nroff -man printmaillist.1 > printmaillist.0
+
+printmaillist.o: \
+compile printmaillist.c substdio.h subfd.h substdio.h strerr.h \
+stralloc.h gen_alloc.h getln.h
+       ./compile printmaillist.c
+
+prog: \
+fastforward printforward setforward newaliases printmaillist \
+setmaillist newinclude
+
+qmail.o: \
+compile qmail.c substdio.h readwrite.h wait.h exit.h fork.h fd.h \
+qmail.h substdio.h auto_qmail.h
+       ./compile qmail.c
+
+scan_ulong.o: \
+compile scan_ulong.c scan.h
+       ./compile scan_ulong.c
+
+seek.a: \
+makelib seek_set.o
+       ./makelib seek.a seek_set.o
+
+seek_set.o: \
+compile seek_set.c seek.h
+       ./compile seek_set.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
+       ./load setforward cdbmss.o cdbmake.a strerr.a substdio.a \
+       stralloc.a alloc.a error.a str.a seek.a open.a case.a 
+
+setforward.0: \
+setforward.1
+       nroff -man setforward.1 > setforward.0
+
+setforward.o: \
+compile setforward.c substdio.h subfd.h substdio.h strerr.h \
+stralloc.h gen_alloc.h open.h case.h readwrite.h cdbmss.h cdbmake.h \
+uint32.h substdio.h
+       ./compile setforward.c
+
+setmaillist: \
+load setmaillist.o getln.a strerr.a substdio.a stralloc.a alloc.a \
+error.a str.a open.a
+       ./load setmaillist getln.a strerr.a substdio.a stralloc.a \
+       alloc.a error.a str.a open.a 
+
+setmaillist.0: \
+setmaillist.1
+       nroff -man setmaillist.1 > setmaillist.0
+
+setmaillist.o: \
+compile setmaillist.c substdio.h subfd.h substdio.h strerr.h \
+stralloc.h gen_alloc.h getln.h open.h readwrite.h
+       ./compile setmaillist.c
+
+setup: \
+it install
+       ./install
+
+sgetopt.o: \
+compile sgetopt.c substdio.h subfd.h substdio.h sgetopt.h subgetopt.h \
+subgetopt.h
+       ./compile sgetopt.c
+
+sig.a: \
+makelib sig_catch.o sig_pipe.o
+       ./makelib sig.a sig_catch.o sig_pipe.o
+
+sig_catch.o: \
+compile sig_catch.c sig.h hassgact.h
+       ./compile sig_catch.c
+
+sig_pipe.o: \
+compile sig_pipe.c sig.h
+       ./compile sig_pipe.c
+
+slurpclose.o: \
+compile slurpclose.c stralloc.h gen_alloc.h readwrite.h slurpclose.h \
+error.h
+       ./compile slurpclose.c
+
+str.a: \
+makelib str_len.o str_diff.o str_diffn.o str_cpy.o str_chr.o \
+str_rchr.o byte_chr.o byte_diff.o byte_copy.o byte_cr.o
+       ./makelib str.a str_len.o str_diff.o str_diffn.o str_cpy.o \
+       str_chr.o str_rchr.o byte_chr.o byte_diff.o byte_copy.o \
+       byte_cr.o
+
+str_chr.o: \
+compile str_chr.c str.h
+       ./compile str_chr.c
+
+str_cpy.o: \
+compile str_cpy.c str.h
+       ./compile str_cpy.c
+
+str_diff.o: \
+compile str_diff.c str.h
+       ./compile str_diff.c
+
+str_diffn.o: \
+compile str_diffn.c str.h
+       ./compile str_diffn.c
+
+str_len.o: \
+compile str_len.c str.h
+       ./compile str_len.c
+
+str_rchr.o: \
+compile str_rchr.c str.h
+       ./compile str_rchr.c
+
+stralloc.a: \
+makelib stralloc_eady.o stralloc_pend.o stralloc_copy.o \
+stralloc_opys.o stralloc_opyb.o stralloc_cat.o stralloc_cats.o \
+stralloc_catb.o stralloc_arts.o
+       ./makelib stralloc.a stralloc_eady.o stralloc_pend.o \
+       stralloc_copy.o stralloc_opys.o stralloc_opyb.o \
+       stralloc_cat.o stralloc_cats.o stralloc_catb.o \
+       stralloc_arts.o
+
+stralloc_arts.o: \
+compile stralloc_arts.c byte.h str.h stralloc.h gen_alloc.h
+       ./compile stralloc_arts.c
+
+stralloc_cat.o: \
+compile stralloc_cat.c byte.h stralloc.h gen_alloc.h
+       ./compile stralloc_cat.c
+
+stralloc_catb.o: \
+compile stralloc_catb.c stralloc.h gen_alloc.h byte.h
+       ./compile stralloc_catb.c
+
+stralloc_cats.o: \
+compile stralloc_cats.c byte.h str.h stralloc.h gen_alloc.h
+       ./compile stralloc_cats.c
+
+stralloc_copy.o: \
+compile stralloc_copy.c byte.h stralloc.h gen_alloc.h
+       ./compile stralloc_copy.c
+
+stralloc_eady.o: \
+compile stralloc_eady.c alloc.h stralloc.h gen_alloc.h \
+gen_allocdefs.h
+       ./compile stralloc_eady.c
+
+stralloc_opyb.o: \
+compile stralloc_opyb.c stralloc.h gen_alloc.h byte.h
+       ./compile stralloc_opyb.c
+
+stralloc_opys.o: \
+compile stralloc_opys.c byte.h str.h stralloc.h gen_alloc.h
+       ./compile stralloc_opys.c
+
+stralloc_pend.o: \
+compile stralloc_pend.c alloc.h stralloc.h gen_alloc.h \
+gen_allocdefs.h
+       ./compile stralloc_pend.c
+
+strerr.a: \
+makelib strerr_sys.o strerr_die.o
+       ./makelib strerr.a strerr_sys.o strerr_die.o
+
+strerr_die.o: \
+compile strerr_die.c substdio.h subfd.h substdio.h exit.h strerr.h
+       ./compile strerr_die.c
+
+strerr_sys.o: \
+compile strerr_sys.c error.h strerr.h
+       ./compile strerr_sys.c
+
+strset.o: \
+compile strset.c strset.h uint32.h str.h byte.h
+       ./compile strset.c
+
+subfderr.o: \
+compile subfderr.c readwrite.h substdio.h subfd.h substdio.h
+       ./compile subfderr.c
+
+subfdins.o: \
+compile subfdins.c readwrite.h substdio.h subfd.h substdio.h
+       ./compile subfdins.c
+
+subfdouts.o: \
+compile subfdouts.c readwrite.h substdio.h subfd.h substdio.h
+       ./compile subfdouts.c
+
+subgetopt.o: \
+compile subgetopt.c subgetopt.h
+       ./compile subgetopt.c
+
+substdi.o: \
+compile substdi.c substdio.h byte.h error.h
+       ./compile substdi.c
+
+substdio.a: \
+makelib substdio.o substdi.o substdo.o subfderr.o subfdouts.o \
+subfdins.o substdio_copy.o
+       ./makelib substdio.a substdio.o substdi.o substdo.o \
+       subfderr.o subfdouts.o subfdins.o substdio_copy.o
+
+substdio.o: \
+compile substdio.c substdio.h
+       ./compile substdio.c
+
+substdio_copy.o: \
+compile substdio_copy.c substdio.h
+       ./compile substdio_copy.c
+
+substdo.o: \
+compile substdo.c substdio.h str.h byte.h error.h
+       ./compile substdo.c
+
+systype: \
+find-systype trycpp.c
+       ./find-systype > systype
+
+token822.o: \
+compile token822.c stralloc.h gen_alloc.h alloc.h str.h token822.h \
+gen_alloc.h gen_allocdefs.h
+       ./compile token822.c
+
+uint32.h: \
+tryulong32.c compile load uint32.h1 uint32.h2
+       ( ( ./compile tryulong32.c && ./load tryulong32 && \
+       ./tryulong32 ) >/dev/null 2>&1 \
+       && cat uint32.h2 || cat uint32.h1 ) > uint32.h
+       rm -f tryulong32.o tryulong32
+
+wait.a: \
+makelib wait_pid.o
+       ./makelib wait.a wait_pid.o
+
+wait_pid.o: \
+compile wait_pid.c error.h haswaitp.h
+       ./compile wait_pid.c
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..f8da024
--- /dev/null
+++ b/README
@@ -0,0 +1,23 @@
+fastforward 0.51, alpha.
+19980519
+Copyright 1998
+D. J. Bernstein, djb@pobox.com
+
+fastforward handles qmail forwarding according to a cdb database. It can
+create forwarding databases from a sendmail-style /etc/aliases or from
+user-oriented virtual-domain tables. See BLURB for a more detailed
+advertisement.
+
+INSTALL says how to set up fastforward.
+
+You may distribute unmodified copies of the fastforward package.
+
+The rest of this file is a list of systypes where various versions of
+fastforward have been reported to work.
+
+0.50: bsd.os-2.0.1-:i386-:-:pentium-:-
+0.50: irix-5.3-11091811-:-:-:ip19-:- (tnx JB)
+0.50: linux-2.0.7-:i386-:-:i486-:- (tnx TLM)
+0.50: linux-2.0.33-:i386-:-:i486-:- (tnx TB)
+0.50: osf1-v4.0-878-:-:-:alpha-:- (tnx BJM)
+0.50: sunos-5.6-generic-:sparc-:sun4-:sun4m-:sun4m- (tnx TWH)
diff --git a/SYSDEPS b/SYSDEPS
new file mode 100644 (file)
index 0000000..ba3c676
--- /dev/null
+++ b/SYSDEPS
@@ -0,0 +1,6 @@
+VERSION
+systype
+hassgact.h
+haswaitp.h
+fork.h
+uint32.h
diff --git a/TARGETS b/TARGETS
new file mode 100644 (file)
index 0000000..f7824b3
--- /dev/null
+++ b/TARGETS
@@ -0,0 +1,127 @@
+auto-ccld.sh
+make-load
+find-systype
+systype
+load
+make-compile
+compile
+uint32.h
+fork.h
+fastforward.o
+slurpclose.o
+coe.o
+strset.o
+qmail.o
+auto-str.o
+make-makelib
+makelib
+substdio.o
+substdi.o
+substdo.o
+subfderr.o
+subfdouts.o
+subfdins.o
+substdio_copy.o
+substdio.a
+error.o
+error_str.o
+error.a
+str_len.o
+str_diff.o
+str_diffn.o
+str_cpy.o
+str_chr.o
+str_rchr.o
+byte_chr.o
+byte_diff.o
+byte_copy.o
+byte_cr.o
+str.a
+auto-str
+auto_qmail.c
+auto_qmail.o
+subgetopt.o
+sgetopt.o
+getopt.a
+cdb_hash.o
+cdb_unpack.o
+cdb_seek.o
+cdb.a
+envread.o
+env.a
+strerr_sys.o
+strerr_die.o
+strerr.a
+stralloc_eady.o
+stralloc_pend.o
+stralloc_copy.o
+stralloc_opys.o
+stralloc_opyb.o
+stralloc_cat.o
+stralloc_cats.o
+stralloc_catb.o
+stralloc_arts.o
+stralloc.a
+alloc.o
+alloc_re.o
+alloc.a
+case_lowerb.o
+case.a
+fmt_ulong.o
+scan_ulong.o
+fs.a
+hassgact.h
+sig_catch.o
+sig_pipe.o
+sig.a
+haswaitp.h
+wait_pid.o
+wait.a
+seek_set.o
+seek.a
+open_read.o
+open_trunc.o
+open.a
+fd_copy.o
+fd_move.o
+fd.a
+fastforward
+printforward.o
+printforward
+setforward.o
+cdbmss.o
+cdbmake_pack.o
+cdbmake_hash.o
+cdbmake_add.o
+cdbmake.a
+setforward
+newaliases.o
+token822.o
+control.o
+getln.o
+getln2.o
+getln.a
+newaliases
+printmaillist.o
+printmaillist
+setmaillist.o
+setmaillist
+newinclude.o
+newinclude
+prog
+fastforward.0
+printforward.0
+setforward.0
+newaliases.0
+printmaillist.0
+setmaillist.0
+newinclude.0
+man
+it
+install.o
+hier.o
+install
+setup
+instcheck.o
+instcheck
+check
diff --git a/THANKS b/THANKS
new file mode 100644 (file)
index 0000000..5bb53e7
--- /dev/null
+++ b/THANKS
@@ -0,0 +1,9 @@
+BJM = Barry J. Miller
+JB = Jos Backus
+LW = Lionel Widdifield
+MS = Mikael Suokas
+PJG = Paul Graham
+SAC = Shawn A. Clifford
+TB = Thomas Bullinger
+TLM = Timothy L. Mayo
+TWH = Thomas W. Holt
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..6ad2d49
--- /dev/null
+++ b/TODO
@@ -0,0 +1,2 @@
+consider other table formats
+test
diff --git a/VERSION b/VERSION
new file mode 100644 (file)
index 0000000..108e0ca
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+fastforward 0.51
diff --git a/alloc.c b/alloc.c
new file mode 100644 (file)
index 0000000..c661453
--- /dev/null
+++ b/alloc.c
@@ -0,0 +1,32 @@
+#include "alloc.h"
+#include "error.h"
+extern char *malloc();
+extern void free();
+
+#define ALIGNMENT 16 /* XXX: assuming that this alignment is enough */
+#define SPACE 4096 /* must be multiple of ALIGNMENT */
+
+typedef union { char irrelevant[ALIGNMENT]; double d; } aligned;
+static aligned realspace[SPACE / ALIGNMENT];
+#define space ((char *) realspace)
+static unsigned int avail = SPACE; /* multiple of ALIGNMENT; 0<=avail<=SPACE */
+
+/*@null@*//*@out@*/char *alloc(n)
+unsigned int n;
+{
+  char *x;
+  n = ALIGNMENT + n - (n & (ALIGNMENT - 1)); /* XXX: could overflow */
+  if (n <= avail) { avail -= n; return space + avail; }
+  x = malloc(n);
+  if (!x) errno = error_nomem;
+  return x;
+}
+
+void alloc_free(x)
+char *x;
+{
+  if (x >= space)
+    if (x < space + SPACE)
+      return; /* XXX: assuming that pointers are flat */
+  free(x);
+}
diff --git a/alloc.h b/alloc.h
new file mode 100644 (file)
index 0000000..1b1d893
--- /dev/null
+++ b/alloc.h
@@ -0,0 +1,8 @@
+#ifndef ALLOC_H
+#define ALLOC_H
+
+extern /*@null@*//*@out@*/char *alloc();
+extern void alloc_free();
+extern int alloc_re();
+
+#endif
diff --git a/alloc_re.c b/alloc_re.c
new file mode 100644 (file)
index 0000000..feb8b49
--- /dev/null
@@ -0,0 +1,17 @@
+#include "alloc.h"
+#include "byte.h"
+
+int alloc_re(x,m,n)
+char **x;
+unsigned int m;
+unsigned int n;
+{
+  char *y;
+  y = alloc(n);
+  if (!y) return 0;
+  byte_copy(y,m,*x);
+  alloc_free(*x);
+  *x = y;
+  return 1;
+}
diff --git a/auto-str.c b/auto-str.c
new file mode 100644 (file)
index 0000000..acc3d60
--- /dev/null
@@ -0,0 +1,44 @@
+#include "substdio.h"
+#include "readwrite.h"
+#include "exit.h"
+
+char buf1[256];
+substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1));
+
+void puts(s)
+char *s;
+{
+  if (substdio_puts(&ss1,s) == -1) _exit(111);
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  char *name;
+  char *value;
+  unsigned char ch;
+  char octal[4];
+
+  name = argv[1];
+  if (!name) _exit(100);
+  value = argv[2];
+  if (!value) _exit(100);
+
+  puts("char ");
+  puts(name);
+  puts("[] = \"\\\n");
+
+  while (ch = *value++) {
+    puts("\\");
+    octal[3] = 0;
+    octal[2] = '0' + (ch & 7); ch >>= 3;
+    octal[1] = '0' + (ch & 7); ch >>= 3;
+    octal[0] = '0' + (ch & 7);
+    puts(octal);
+  }
+
+  puts("\\\n\";\n");
+  if (substdio_flush(&ss1) == -1) _exit(111);
+  _exit(0);
+}
diff --git a/auto_qmail.h b/auto_qmail.h
new file mode 100644 (file)
index 0000000..0c56001
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef AUTO_QMAIL_H
+#define AUTO_QMAIL_H
+
+extern char auto_qmail[];
+
+#endif
diff --git a/byte.h b/byte.h
new file mode 100644 (file)
index 0000000..de06c69
--- /dev/null
+++ b/byte.h
@@ -0,0 +1,13 @@
+#ifndef BYTE_H
+#define BYTE_H
+
+extern unsigned int byte_chr();
+extern unsigned int byte_rchr();
+extern void byte_copy();
+extern void byte_copyr();
+extern int byte_diff();
+extern void byte_zero();
+
+#define byte_equal(s,n,t) (!byte_diff((s),(n),(t)))
+
+#endif
diff --git a/byte_chr.c b/byte_chr.c
new file mode 100644 (file)
index 0000000..f81dde8
--- /dev/null
@@ -0,0 +1,20 @@
+#include "byte.h"
+
+unsigned int byte_chr(s,n,c)
+char *s;
+register unsigned int n;
+int c;
+{
+  register char ch;
+  register char *t;
+
+  ch = c;
+  t = s;
+  for (;;) {
+    if (!n) break; if (*t == ch) break; ++t; --n;
+    if (!n) break; if (*t == ch) break; ++t; --n;
+    if (!n) break; if (*t == ch) break; ++t; --n;
+    if (!n) break; if (*t == ch) break; ++t; --n;
+  }
+  return t - s;
+}
diff --git a/byte_copy.c b/byte_copy.c
new file mode 100644 (file)
index 0000000..eaad11b
--- /dev/null
@@ -0,0 +1,14 @@
+#include "byte.h"
+
+void byte_copy(to,n,from)
+register char *to;
+register unsigned int n;
+register char *from;
+{
+  for (;;) {
+    if (!n) return; *to++ = *from++; --n;
+    if (!n) return; *to++ = *from++; --n;
+    if (!n) return; *to++ = *from++; --n;
+    if (!n) return; *to++ = *from++; --n;
+  }
+}
diff --git a/byte_cr.c b/byte_cr.c
new file mode 100644 (file)
index 0000000..3e7a1d5
--- /dev/null
+++ b/byte_cr.c
@@ -0,0 +1,16 @@
+#include "byte.h"
+
+void byte_copyr(to,n,from)
+register char *to;
+register unsigned int n;
+register char *from;
+{
+  to += n;
+  from += n;
+  for (;;) {
+    if (!n) return; *--to = *--from; --n;
+    if (!n) return; *--to = *--from; --n;
+    if (!n) return; *--to = *--from; --n;
+    if (!n) return; *--to = *--from; --n;
+  }
+}
diff --git a/byte_diff.c b/byte_diff.c
new file mode 100644 (file)
index 0000000..cdbd760
--- /dev/null
@@ -0,0 +1,16 @@
+#include "byte.h"
+
+int byte_diff(s,n,t)
+register char *s;
+register unsigned int n;
+register char *t;
+{
+  for (;;) {
+    if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
+    if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
+    if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
+    if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
+  }
+  return ((int)(unsigned int)(unsigned char) *s)
+       - ((int)(unsigned int)(unsigned char) *t);
+}
diff --git a/case.h b/case.h
new file mode 100644 (file)
index 0000000..35a2434
--- /dev/null
+++ b/case.h
@@ -0,0 +1,13 @@
+#ifndef CASE_H
+#define CASE_H
+
+extern void case_lowers();
+extern void case_lowerb();
+extern int case_diffs();
+extern int case_diffb();
+extern int case_starts();
+extern int case_startb();
+
+#define case_equals(s,t) (!case_diffs((s),(t)))
+
+#endif
diff --git a/case_lowerb.c b/case_lowerb.c
new file mode 100644 (file)
index 0000000..4034c14
--- /dev/null
@@ -0,0 +1,14 @@
+#include "case.h"
+
+void case_lowerb(s,len)
+char *s;
+unsigned int len;
+{
+  unsigned char x;
+  while (len > 0) {
+    --len;
+    x = *s - 'A';
+    if (x <= 'Z' - 'A') *s = x + 'a';
+    ++s;
+  }
+}
diff --git a/cdb.h b/cdb.h
new file mode 100644 (file)
index 0000000..571e5d6
--- /dev/null
+++ b/cdb.h
@@ -0,0 +1,12 @@
+#ifndef CDB_H
+#define CDB_H
+
+#include "uint32.h"
+
+extern uint32 cdb_hash();
+extern uint32 cdb_unpack();
+
+extern int cdb_bread();
+extern int cdb_seek();
+
+#endif
diff --git a/cdb_hash.c b/cdb_hash.c
new file mode 100644 (file)
index 0000000..8238020
--- /dev/null
@@ -0,0 +1,16 @@
+#include "cdb.h"
+
+uint32 cdb_hash(buf,len)
+unsigned char *buf;
+unsigned int len;
+{
+  uint32 h;
+
+  h = 5381;
+  while (len) {
+    --len;
+    h += (h << 5);
+    h ^= (uint32) *buf++;
+  }
+  return h;
+}
diff --git a/cdb_seek.c b/cdb_seek.c
new file mode 100644 (file)
index 0000000..87ab614
--- /dev/null
@@ -0,0 +1,95 @@
+#include <sys/types.h>
+#include <errno.h>
+extern int errno;
+#include "cdb.h"
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+int cdb_bread(fd,buf,len)
+int fd;
+char *buf;
+int len;
+{
+  int r;
+  while (len > 0) {
+    do
+      r = read(fd,buf,len);
+    while ((r == -1) && (errno == EINTR));
+    if (r == -1) return -1;
+    if (r == 0) { errno = EIO; return -1; }
+    buf += r;
+    len -= r;
+  }
+  return 0;
+}
+
+static int match(fd,key,len)
+int fd;
+char *key;
+unsigned int len;
+{
+  char buf[32];
+  int n;
+  int i;
+
+  while (len > 0) {
+    n = sizeof(buf);
+    if (n > len) n = len;
+    if (cdb_bread(fd,buf,n) == -1) return -1;
+    for (i = 0;i < n;++i) if (buf[i] != key[i]) return 0;
+    key += n;
+    len -= n;
+  }
+  return 1;
+}
+
+int cdb_seek(fd,key,len,dlen)
+int fd;
+char *key;
+unsigned int len;
+uint32 *dlen;
+{
+  char packbuf[8];
+  uint32 pos;
+  uint32 h;
+  uint32 lenhash;
+  uint32 h2;
+  uint32 loop;
+  uint32 poskd;
+
+  h = cdb_hash(key,len);
+
+  pos = 8 * (h & 255);
+  if (lseek(fd,(off_t) pos,SEEK_SET) == -1) return -1;
+
+  if (cdb_bread(fd,packbuf,8) == -1) return -1;
+
+  pos = cdb_unpack(packbuf);
+  lenhash = cdb_unpack(packbuf + 4);
+
+  if (!lenhash) return 0;
+  h2 = (h >> 8) % lenhash;
+
+  for (loop = 0;loop < lenhash;++loop) {
+    if (lseek(fd,(off_t) (pos + 8 * h2),SEEK_SET) == -1) return -1;
+    if (cdb_bread(fd,packbuf,8) == -1) return -1;
+    poskd = cdb_unpack(packbuf + 4);
+    if (!poskd) return 0;
+    if (cdb_unpack(packbuf) == h) {
+      if (lseek(fd,(off_t) poskd,SEEK_SET) == -1) return -1;
+      if (cdb_bread(fd,packbuf,8) == -1) return -1;
+      if (cdb_unpack(packbuf) == len)
+       switch(match(fd,key,len)) {
+         case -1:
+           return -1;
+         case 1:
+           *dlen = cdb_unpack(packbuf + 4);
+           return 1;
+       }
+    }
+    if (++h2 == lenhash) h2 = 0;
+  }
+  return 0;
+}
diff --git a/cdb_unpack.c b/cdb_unpack.c
new file mode 100644 (file)
index 0000000..c882202
--- /dev/null
@@ -0,0 +1,12 @@
+#include "cdb.h"
+
+uint32 cdb_unpack(buf)
+unsigned char *buf;
+{
+  uint32 num;
+  num = buf[3]; num <<= 8;
+  num += buf[2]; num <<= 8;
+  num += buf[1]; num <<= 8;
+  num += buf[0];
+  return num;
+}
diff --git a/cdbmake.h b/cdbmake.h
new file mode 100644 (file)
index 0000000..883a231
--- /dev/null
+++ b/cdbmake.h
@@ -0,0 +1,35 @@
+#ifndef CDBMAKE_H
+#define CDBMAKE_H
+
+#include "uint32.h"
+
+#define CDBMAKE_HPLIST 1000
+
+struct cdbmake_hp { uint32 h; uint32 p; } ;
+
+struct cdbmake_hplist {
+  struct cdbmake_hp hp[CDBMAKE_HPLIST];
+  struct cdbmake_hplist *next;
+  int num;
+} ;
+
+struct cdbmake {
+  char final[2048];
+  uint32 count[256];
+  uint32 start[256];
+  struct cdbmake_hplist *head;
+  struct cdbmake_hp *split; /* includes space for hash */
+  struct cdbmake_hp *hash;
+  uint32 numentries;
+} ;
+
+extern void cdbmake_pack();
+#define CDBMAKE_HASHSTART ((uint32) 5381)
+extern uint32 cdbmake_hashadd();
+
+extern void cdbmake_init();
+extern int cdbmake_add();
+extern int cdbmake_split();
+extern uint32 cdbmake_throw();
+
+#endif
diff --git a/cdbmake_add.c b/cdbmake_add.c
new file mode 100644 (file)
index 0000000..115f828
--- /dev/null
@@ -0,0 +1,117 @@
+#include "cdbmake.h"
+
+void cdbmake_init(cdbm)
+struct cdbmake *cdbm;
+{
+  cdbm->head = 0;
+  cdbm->split = 0;
+  cdbm->hash = 0;
+  cdbm->numentries = 0;
+}
+
+int cdbmake_add(cdbm,h,p,alloc)
+struct cdbmake *cdbm;
+uint32 h;
+uint32 p;
+char *(*alloc)();
+{
+  struct cdbmake_hplist *head;
+
+  head = cdbm->head;
+  if (!head || (head->num >= CDBMAKE_HPLIST)) {
+    head = (struct cdbmake_hplist *) alloc(sizeof(struct cdbmake_hplist));
+    if (!head) return 0;
+    head->num = 0;
+    head->next = cdbm->head;
+    cdbm->head = head;
+  }
+  head->hp[head->num].h = h;
+  head->hp[head->num].p = p;
+  ++head->num;
+  ++cdbm->numentries;
+  return 1;
+}
+
+int cdbmake_split(cdbm,alloc)
+struct cdbmake *cdbm;
+char *(*alloc)();
+{
+  int i;
+  uint32 u;
+  uint32 memsize;
+  struct cdbmake_hplist *x;
+
+  for (i = 0;i < 256;++i)
+    cdbm->count[i] = 0;
+
+  for (x = cdbm->head;x;x = x->next) {
+    i = x->num;
+    while (i--)
+      ++cdbm->count[255 & x->hp[i].h];
+  }
+
+  memsize = 1;
+  for (i = 0;i < 256;++i) {
+    u = cdbm->count[i] * 2;
+    if (u > memsize)
+      memsize = u;
+  }
+
+  memsize += cdbm->numentries; /* no overflow possible up to now */
+  u = (uint32) 0 - (uint32) 1;
+  u /= sizeof(struct cdbmake_hp);
+  if (memsize > u) return 0;
+
+  cdbm->split = (struct cdbmake_hp *) alloc(memsize * sizeof(struct cdbmake_hp));
+  if (!cdbm->split) return 0;
+
+  cdbm->hash = cdbm->split + cdbm->numentries;
+
+  u = 0;
+  for (i = 0;i < 256;++i) {
+    u += cdbm->count[i]; /* bounded by numentries, so no overflow */
+    cdbm->start[i] = u;
+  }
+
+  for (x = cdbm->head;x;x = x->next) {
+    i = x->num;
+    while (i--)
+      cdbm->split[--cdbm->start[255 & x->hp[i].h]] = x->hp[i];
+  }
+
+  return 1;
+}
+
+uint32 cdbmake_throw(cdbm,pos,b)
+struct cdbmake *cdbm;
+uint32 pos;
+int b;
+{
+  uint32 len;
+  uint32 j;
+  uint32 count;
+  struct cdbmake_hp *hp;
+  uint32 where;
+
+  count = cdbm->count[b];
+
+  len = count + count; /* no overflow possible */
+  cdbmake_pack(cdbm->final + 8 * b,pos);
+  cdbmake_pack(cdbm->final + 8 * b + 4,len);
+
+  if (len) {
+    for (j = 0;j < len;++j)
+      cdbm->hash[j].h = cdbm->hash[j].p = 0;
+
+    hp = cdbm->split + cdbm->start[b];
+    for (j = 0;j < count;++j) {
+      where = (hp->h >> 8) % len;
+      while (cdbm->hash[where].p)
+       if (++where == len)
+         where = 0;
+      cdbm->hash[where] = *hp++;
+    }
+  }
+
+  return len;
+}
diff --git a/cdbmake_hash.c b/cdbmake_hash.c
new file mode 100644 (file)
index 0000000..f9dc3e5
--- /dev/null
@@ -0,0 +1,10 @@
+#include "cdbmake.h"
+
+uint32 cdbmake_hashadd(h,c)
+uint32 h;
+unsigned int c;
+{
+  h += (h << 5);
+  h ^= (uint32) (unsigned char) c;
+  return h;
+}
diff --git a/cdbmake_pack.c b/cdbmake_pack.c
new file mode 100644 (file)
index 0000000..04b5f5b
--- /dev/null
@@ -0,0 +1,11 @@
+#include "cdbmake.h"
+
+void cdbmake_pack(buf,num)
+unsigned char *buf;
+uint32 num;
+{
+  *buf++ = num; num >>= 8;
+  *buf++ = num; num >>= 8;
+  *buf++ = num; num >>= 8;
+  *buf = num;
+}
diff --git a/cdbmss.c b/cdbmss.c
new file mode 100644 (file)
index 0000000..2d8f367
--- /dev/null
+++ b/cdbmss.c
@@ -0,0 +1,65 @@
+#include "readwrite.h"
+#include "seek.h"
+#include "alloc.h"
+#include "cdbmss.h"
+
+int cdbmss_start(c,fd)
+struct cdbmss *c;
+int fd;
+{
+  cdbmake_init(&c->cdbm);
+  c->fd = fd;
+  c->pos = sizeof(c->cdbm.final);
+  substdio_fdbuf(&c->ss,write,fd,c->ssbuf,sizeof(c->ssbuf));
+  return seek_set(fd,(seek_pos) c->pos);
+}
+
+int cdbmss_add(c,key,keylen,data,datalen)
+struct cdbmss *c;
+unsigned char *key;
+unsigned int keylen;
+unsigned char *data;
+unsigned int datalen;
+{
+  uint32 h;
+  int i;
+
+  cdbmake_pack(c->packbuf,(uint32) keylen);
+  cdbmake_pack(c->packbuf + 4,(uint32) datalen);
+  if (substdio_put(&c->ss,c->packbuf,8) == -1) return -1;
+  if (substdio_put(&c->ss,key,keylen) == -1) return -1;
+  if (substdio_put(&c->ss,data,datalen) == -1) return -1;
+
+  h = CDBMAKE_HASHSTART;
+  for (i = 0;i < keylen;++i)
+    h = cdbmake_hashadd(h,(unsigned int) key[i]);
+
+  if (!cdbmake_add(&c->cdbm,h,c->pos,alloc)) return -1;
+
+  c->pos += 8 + keylen + datalen; /* XXX: overflow? */
+  return 0;
+}
+
+int cdbmss_finish(c)
+struct cdbmss *c;
+{
+  int i;
+  uint32 len;
+  uint32 u;
+
+  if (!cdbmake_split(&c->cdbm,alloc)) return -1;
+
+  for (i = 0;i < 256;++i) {
+    len = cdbmake_throw(&c->cdbm,c->pos,i);
+    for (u = 0;u < len;++u) {
+      cdbmake_pack(c->packbuf,c->cdbm.hash[u].h);
+      cdbmake_pack(c->packbuf + 4,c->cdbm.hash[u].p);
+      if (substdio_put(&c->ss,c->packbuf,8) == -1) return -1;
+      c->pos += 8; /* XXX: overflow? */
+    }
+  }
+
+  if (substdio_flush(&c->ss) == -1) return -1;
+  if (seek_begin(c->fd) == -1) return -1;
+  return substdio_putflush(&c->ss,c->cdbm.final,sizeof(c->cdbm.final));
+}
diff --git a/cdbmss.h b/cdbmss.h
new file mode 100644 (file)
index 0000000..5e6bdf4
--- /dev/null
+++ b/cdbmss.h
@@ -0,0 +1,16 @@
+#ifndef CDBMSS_H
+#define CDBMSS_H
+
+#include "cdbmake.h"
+#include "substdio.h"
+
+struct cdbmss {
+  char ssbuf[1024];
+  struct cdbmake cdbm;
+  substdio ss;
+  char packbuf[8];
+  uint32 pos;
+  int fd;
+} ;
+
+#endif
diff --git a/coe.c b/coe.c
new file mode 100644 (file)
index 0000000..d855158
--- /dev/null
+++ b/coe.c
@@ -0,0 +1,8 @@
+#include <fcntl.h>
+#include "coe.h"
+
+int coe(fd)
+int fd;
+{
+  return fcntl(fd,F_SETFD,1);
+}
diff --git a/coe.h b/coe.h
new file mode 100644 (file)
index 0000000..1559bc1
--- /dev/null
+++ b/coe.h
@@ -0,0 +1,6 @@
+#ifndef COE_H
+#define COE_H
+
+extern int coe();
+
+#endif
diff --git a/conf-cc b/conf-cc
new file mode 100644 (file)
index 0000000..e58fb9b
--- /dev/null
+++ b/conf-cc
@@ -0,0 +1,3 @@
+cc -O2
+
+This will be used to compile .c files.
diff --git a/conf-ld b/conf-ld
new file mode 100644 (file)
index 0000000..a9e796a
--- /dev/null
+++ b/conf-ld
@@ -0,0 +1,3 @@
+cc -s
+
+This will be used to link .o files into an executable.
diff --git a/conf-qmail b/conf-qmail
new file mode 100644 (file)
index 0000000..0cdd23c
--- /dev/null
@@ -0,0 +1,6 @@
+/var/qmail
+
+This is the qmail home directory.
+
+The fastforward programs will be installed in the bin subdirectory; they
+also need to know where qmail is so that they can forward mail properly.
diff --git a/control.c b/control.c
new file mode 100644 (file)
index 0000000..b655352
--- /dev/null
+++ b/control.c
@@ -0,0 +1,130 @@
+#include "readwrite.h"
+#include "open.h"
+#include "getln.h"
+#include "stralloc.h"
+#include "substdio.h"
+#include "error.h"
+#include "control.h"
+#include "alloc.h"
+#include "scan.h"
+
+static char inbuf[64];
+static stralloc line = {0};
+static stralloc me = {0};
+static int meok = 0;
+
+static void striptrailingwhitespace(sa)
+stralloc *sa;
+{
+ while (sa->len > 0)
+   switch(sa->s[sa->len - 1])
+    {
+     case '\n': case ' ': case '\t':
+       --sa->len;
+       break;
+     default:
+       return;
+    }
+}
+
+int control_init()
+{
+ int r;
+ r = control_readline(&me,"control/me");
+ if (r == 1) meok = 1;
+ return r;
+}
+
+int control_rldef(sa,fn,flagme,def)
+stralloc *sa;
+char *fn;
+int flagme;
+char *def;
+{
+ int r;
+ r = control_readline(sa,fn);
+ if (r) return r;
+ if (flagme) if (meok) return stralloc_copy(sa,&me) ? 1 : -1;
+ if (def) return stralloc_copys(sa,def) ? 1 : -1;
+ return r;
+}
+
+int control_readline(sa,fn)
+stralloc *sa;
+char *fn;
+{
+ substdio ss;
+ int fd;
+ int match;
+
+ fd = open_read(fn);
+ if (fd == -1) { if (errno == error_noent) return 0; return -1; }
+ substdio_fdbuf(&ss,read,fd,inbuf,sizeof(inbuf));
+
+ if (getln(&ss,sa,&match,'\n') == -1) { close(fd); return -1; }
+
+ striptrailingwhitespace(sa);
+ close(fd);
+ return 1;
+}
+
+int control_readint(i,fn)
+int *i;
+char *fn;
+{
+ unsigned long u;
+ switch(control_readline(&line,fn))
+  {
+   case 0: return 0;
+   case -1: return -1;
+  }
+ if (!stralloc_0(&line)) return -1;
+ if (!scan_ulong(line.s,&u)) return 0;
+ *i = u;
+ return 1;
+}
+
+int control_readfile(sa,fn,flagme)
+stralloc *sa;
+char *fn;
+int flagme;
+{
+ substdio ss;
+ int fd;
+ int match;
+
+ if (!stralloc_copys(sa,"")) return -1;
+
+ fd = open_read(fn);
+ if (fd == -1) 
+  {
+   if (errno == error_noent)
+    {
+     if (flagme && meok)
+      {
+       if (!stralloc_copy(sa,&me)) return -1;
+       if (!stralloc_0(sa)) return -1;
+       return 1;
+      }
+     return 0;
+    }
+   return -1;
+  }
+
+ substdio_fdbuf(&ss,read,fd,inbuf,sizeof(inbuf));
+
+ for (;;)
+  {
+   if (getln(&ss,&line,&match,'\n') == -1) break;
+   if (!match && !line.len) { close(fd); return 1; }
+   striptrailingwhitespace(&line);
+   if (!stralloc_0(&line)) break;
+   if (line.s[0])
+     if (line.s[0] != '#')
+       if (!stralloc_cat(sa,&line)) break;
+   if (!match) { close(fd); return 1; }
+  }
+ close(fd);
+ return -1;
+}
diff --git a/control.h b/control.h
new file mode 100644 (file)
index 0000000..7cba89b
--- /dev/null
+++ b/control.h
@@ -0,0 +1,10 @@
+#ifndef CONTROL_H
+#define CONTROL_H
+
+extern int control_init();
+extern int control_readline();
+extern int control_rldef();
+extern int control_readint();
+extern int control_readfile();
+
+#endif
diff --git a/env.h b/env.h
new file mode 100644 (file)
index 0000000..9befc79
--- /dev/null
+++ b/env.h
@@ -0,0 +1,17 @@
+#ifndef ENV_H
+#define ENV_H
+
+extern int env_isinit;
+
+extern int env_init();
+extern int env_put();
+extern int env_put2();
+extern int env_unset();
+extern /*@null@*/char *env_get();
+extern char *env_pick();
+extern void env_clear();
+extern char *env_findeq();
+
+extern char **environ;
+
+#endif
diff --git a/envread.c b/envread.c
new file mode 100644 (file)
index 0000000..80185de
--- /dev/null
+++ b/envread.c
@@ -0,0 +1,30 @@
+#include "env.h"
+#include "str.h"
+
+extern /*@null@*/char *env_get(s)
+char *s;
+{
+  int i;
+  unsigned int slen;
+  char *envi;
+  slen = str_len(s);
+  for (i = 0;envi = environ[i];++i)
+    if ((!str_diffn(s,envi,slen)) && (envi[slen] == '='))
+      return envi + slen + 1;
+  return 0;
+}
+
+extern char *env_pick()
+{
+  return environ[0];
+}
+
+extern char *env_findeq(s)
+char *s;
+{
+  for (;*s;++s)
+    if (*s == '=')
+      return s;
+  return 0;
+}
diff --git a/error.c b/error.c
new file mode 100644 (file)
index 0000000..d51304f
--- /dev/null
+++ b/error.c
@@ -0,0 +1,95 @@
+#include <errno.h>
+#include "error.h"
+
+/* warning: as coverage improves here, should update error_{str,temp} */
+
+int error_intr =
+#ifdef EINTR
+EINTR;
+#else
+-1;
+#endif
+
+int error_nomem =
+#ifdef ENOMEM
+ENOMEM;
+#else
+-2;
+#endif
+
+int error_noent = 
+#ifdef ENOENT
+ENOENT;
+#else
+-3;
+#endif
+
+int error_txtbsy =
+#ifdef ETXTBSY
+ETXTBSY;
+#else
+-4;
+#endif
+
+int error_io =
+#ifdef EIO
+EIO;
+#else
+-5;
+#endif
+
+int error_exist =
+#ifdef EEXIST
+EEXIST;
+#else
+-6;
+#endif
+
+int error_timeout =
+#ifdef ETIMEDOUT
+ETIMEDOUT;
+#else
+-7;
+#endif
+
+int error_inprogress =
+#ifdef EINPROGRESS
+EINPROGRESS;
+#else
+-8;
+#endif
+
+int error_wouldblock =
+#ifdef EWOULDBLOCK
+EWOULDBLOCK;
+#else
+-9;
+#endif
+
+int error_again =
+#ifdef EAGAIN
+EAGAIN;
+#else
+-10;
+#endif
+
+int error_pipe =
+#ifdef EPIPE
+EPIPE;
+#else
+-11;
+#endif
+
+int error_perm =
+#ifdef EPERM
+EPERM;
+#else
+-12;
+#endif
+
+int error_acces =
+#ifdef EACCES
+EACCES;
+#else
+-13;
+#endif
diff --git a/error.h b/error.h
new file mode 100644 (file)
index 0000000..01bd3dc
--- /dev/null
+++ b/error.h
@@ -0,0 +1,23 @@
+#ifndef ERROR_H
+#define ERROR_H
+
+extern int errno;
+
+extern int error_intr;
+extern int error_nomem;
+extern int error_noent;
+extern int error_txtbsy;
+extern int error_io;
+extern int error_exist;
+extern int error_timeout;
+extern int error_inprogress;
+extern int error_wouldblock;
+extern int error_again;
+extern int error_pipe;
+extern int error_perm;
+extern int error_acces;
+
+extern char *error_str();
+extern int error_temp();
+
+#endif
diff --git a/error_str.c b/error_str.c
new file mode 100644 (file)
index 0000000..804d1fa
--- /dev/null
@@ -0,0 +1,276 @@
+#include <errno.h>
+#include "error.h"
+
+#define X(e,s) if (i == e) return s;
+
+char *error_str(i)
+int i;
+{
+  X(0,"no error")
+  X(error_intr,"interrupted system call")
+  X(error_nomem,"out of memory")
+  X(error_noent,"file does not exist")
+  X(error_txtbsy,"text busy")
+  X(error_io,"input/output error")
+  X(error_exist,"file already exists")
+  X(error_timeout,"timed out")
+  X(error_inprogress,"operation in progress")
+  X(error_again,"temporary failure")
+  X(error_wouldblock,"input/output would block")
+  X(error_pipe,"broken pipe")
+  X(error_perm,"permission denied")
+  X(error_acces,"access denied")
+#ifdef ESRCH
+  X(ESRCH,"no such process")
+#endif
+#ifdef ENXIO
+  X(ENXIO,"device not configured")
+#endif
+#ifdef E2BIG
+  X(E2BIG,"argument list too long")
+#endif
+#ifdef ENOEXEC
+  X(ENOEXEC,"exec format error")
+#endif
+#ifdef EBADF
+  X(EBADF,"file descriptor not open")
+#endif
+#ifdef ECHILD
+  X(ECHILD,"no child processes")
+#endif
+#ifdef EDEADLK
+  X(EDEADLK,"operation would cause deadlock")
+#endif
+#ifdef EFAULT
+  X(EFAULT,"bad address")
+#endif
+#ifdef ENOTBLK
+  X(ENOTBLK,"not a block device")
+#endif
+#ifdef EBUSY
+  X(EBUSY,"device busy")
+#endif
+#ifdef EXDEV
+  X(EXDEV,"cross-device link")
+#endif
+#ifdef ENODEV
+  X(ENODEV,"device does not support operation")
+#endif
+#ifdef ENOTDIR
+  X(ENOTDIR,"not a directory")
+#endif
+#ifdef EISDIR
+  X(EISDIR,"is a directory")
+#endif
+#ifdef EINVAL
+  X(EINVAL,"invalid argument")
+#endif
+#ifdef ENFILE
+  X(ENFILE,"system cannot open more files")
+#endif
+#ifdef EMFILE
+  X(EMFILE,"process cannot open more files")
+#endif
+#ifdef ENOTTY
+  X(ENOTTY,"not a tty")
+#endif
+#ifdef EFBIG
+  X(EFBIG,"file too big")
+#endif
+#ifdef ENOSPC
+  X(ENOSPC,"out of disk space")
+#endif
+#ifdef ESPIPE
+  X(ESPIPE,"unseekable descriptor")
+#endif
+#ifdef EROFS
+  X(EROFS,"read-only file system")
+#endif
+#ifdef EMLINK
+  X(EMLINK,"too many links")
+#endif
+#ifdef EDOM
+  X(EDOM,"input out of range")
+#endif
+#ifdef ERANGE
+  X(ERANGE,"output out of range")
+#endif
+#ifdef EALREADY
+  X(EALREADY,"operation already in progress")
+#endif
+#ifdef ENOTSOCK
+  X(ENOTSOCK,"not a socket")
+#endif
+#ifdef EDESTADDRREQ
+  X(EDESTADDRREQ,"destination address required")
+#endif
+#ifdef EMSGSIZE
+  X(EMSGSIZE,"message too long")
+#endif
+#ifdef EPROTOTYPE
+  X(EPROTOTYPE,"incorrect protocol type")
+#endif
+#ifdef ENOPROTOOPT
+  X(ENOPROTOOPT,"protocol not available")
+#endif
+#ifdef EPROTONOSUPPORT
+  X(EPROTONOSUPPORT,"protocol not supported")
+#endif
+#ifdef ESOCKTNOSUPPORT
+  X(ESOCKTNOSUPPORT,"socket type not supported")
+#endif
+#ifdef EOPNOTSUPP
+  X(EOPNOTSUPP,"operation not supported")
+#endif
+#ifdef EPFNOSUPPORT
+  X(EPFNOSUPPORT,"protocol family not supported")
+#endif
+#ifdef EAFNOSUPPORT
+  X(EAFNOSUPPORT,"address family not supported")
+#endif
+#ifdef EADDRINUSE
+  X(EADDRINUSE,"address already used")
+#endif
+#ifdef EADDRNOTAVAIL
+  X(EADDRNOTAVAIL,"address not available")
+#endif
+#ifdef ENETDOWN
+  X(ENETDOWN,"network down")
+#endif
+#ifdef ENETUNREACH
+  X(ENETUNREACH,"network unreachable")
+#endif
+#ifdef ENETRESET
+  X(ENETRESET,"network reset")
+#endif
+#ifdef ECONNABORTED
+  X(ECONNABORTED,"connection aborted")
+#endif
+#ifdef ECONNRESET
+  X(ECONNRESET,"connection reset")
+#endif
+#ifdef ENOBUFS
+  X(ENOBUFS,"out of buffer space")
+#endif
+#ifdef EISCONN
+  X(EISCONN,"already connected")
+#endif
+#ifdef ENOTCONN
+  X(ENOTCONN,"not connected")
+#endif
+#ifdef ESHUTDOWN
+  X(ESHUTDOWN,"socket shut down")
+#endif
+#ifdef ETOOMANYREFS
+  X(ETOOMANYREFS,"too many references")
+#endif
+#ifdef ECONNREFUSED
+  X(ECONNREFUSED,"connection refused")
+#endif
+#ifdef ELOOP
+  X(ELOOP,"symbolic link loop")
+#endif
+#ifdef ENAMETOOLONG
+  X(ENAMETOOLONG,"file name too long")
+#endif
+#ifdef EHOSTDOWN
+  X(EHOSTDOWN,"host down")
+#endif
+#ifdef EHOSTUNREACH
+  X(EHOSTUNREACH,"host unreachable")
+#endif
+#ifdef ENOTEMPTY
+  X(ENOTEMPTY,"directory not empty")
+#endif
+#ifdef EPROCLIM
+  X(EPROCLIM,"too many processes")
+#endif
+#ifdef EUSERS
+  X(EUSERS,"too many users")
+#endif
+#ifdef EDQUOT
+  X(EDQUOT,"disk quota exceeded")
+#endif
+#ifdef ESTALE
+  X(ESTALE,"stale NFS file handle")
+#endif
+#ifdef EREMOTE
+  X(EREMOTE,"too many levels of remote in path")
+#endif
+#ifdef EBADRPC
+  X(EBADRPC,"RPC structure is bad")
+#endif
+#ifdef ERPCMISMATCH
+  X(ERPCMISMATCH,"RPC version mismatch")
+#endif
+#ifdef EPROGUNAVAIL
+  X(EPROGUNAVAIL,"RPC program unavailable")
+#endif
+#ifdef EPROGMISMATCH
+  X(EPROGMISMATCH,"program version mismatch")
+#endif
+#ifdef EPROCUNAVAIL
+  X(EPROCUNAVAIL,"bad procedure for program")
+#endif
+#ifdef ENOLCK
+  X(ENOLCK,"no locks available")
+#endif
+#ifdef ENOSYS
+  X(ENOSYS,"system call not available")
+#endif
+#ifdef EFTYPE
+  X(EFTYPE,"bad file type")
+#endif
+#ifdef EAUTH
+  X(EAUTH,"authentication error")
+#endif
+#ifdef ENEEDAUTH
+  X(ENEEDAUTH,"not authenticated")
+#endif
+#ifdef ENOSTR
+  X(ENOSTR,"not a stream device")
+#endif
+#ifdef ETIME
+  X(ETIME,"timer expired")
+#endif
+#ifdef ENOSR
+  X(ENOSR,"out of stream resources")
+#endif
+#ifdef ENOMSG
+  X(ENOMSG,"no message of desired type")
+#endif
+#ifdef EBADMSG
+  X(EBADMSG,"bad message type")
+#endif
+#ifdef EIDRM
+  X(EIDRM,"identifier removed")
+#endif
+#ifdef ENONET
+  X(ENONET,"machine not on network")
+#endif
+#ifdef ERREMOTE
+  X(ERREMOTE,"object not local")
+#endif
+#ifdef ENOLINK
+  X(ENOLINK,"link severed")
+#endif
+#ifdef EADV
+  X(EADV,"advertise error")
+#endif
+#ifdef ESRMNT
+  X(ESRMNT,"srmount error")
+#endif
+#ifdef ECOMM
+  X(ECOMM,"communication error")
+#endif
+#ifdef EPROTO
+  X(EPROTO,"protocol error")
+#endif
+#ifdef EMULTIHOP
+  X(EMULTIHOP,"multihop attempted")
+#endif
+#ifdef EREMCHG
+  X(EREMCHG,"remote address changed")
+#endif
+  return "unknown error";
+}
diff --git a/exit.h b/exit.h
new file mode 100644 (file)
index 0000000..39011c8
--- /dev/null
+++ b/exit.h
@@ -0,0 +1,6 @@
+#ifndef EXIT_H
+#define EXIT_H
+
+extern void _exit();
+
+#endif
diff --git a/fastforward.1 b/fastforward.1
new file mode 100644 (file)
index 0000000..d4be2e5
--- /dev/null
@@ -0,0 +1,119 @@
+.TH fastforward 1
+.SH NAME
+fastforward \- forward mail according to a cdb database
+.SH SYNOPSIS
+in
+.BR .qmail-default :
+.B | fastforward
+[
+.B \-nNpPdD
+]
+.I cdb
+.SH DESCRIPTION
+.B fastforward
+forwards each incoming message
+according to instructions in
+.I cdb
+created by
+.BR setforward .
+
+If there is no forwarding instruction in
+.I cdb
+for the incoming recipient address,
+.B fastforward
+will bounce the message.
+
+You can override
+.B .qmail-default
+with a specific
+.BR .qmail-\fIrecipient ;
+see
+.BR dot-qmail (5).
+
+Warning to system administrators:
+Messages do not reach
+.B ~alias/.qmail-default
+unless they are controlled by the
+.B alias
+user.
+See
+.BR qmail-getpw (8).
+
+.B SECURITY WARNING:
+If
+.I cdb
+includes instructions pointing to a mailing list owned by another user,
+that user gains some amount of control over
+.BR fastforward 's
+behavior.
+In particular, he can force
+.B fastforward
+to open any file that you can access,
+and to read any world-readable file that you own,
+even if the file is in a world-inaccessible directory.
+.SH "OPTIONS"
+.TP 5
+.B \-n
+No delivery.
+.B fastforward
+will print a description of its actions,
+but will not actually read or forward a message.
+.TP
+.B \-N
+(Default.)
+Forward a message as usual.
+.TP
+.B \-p
+Pass through.
+If
+.B fastforward
+does not find the recipient in
+.IR cdb ,
+it exits 0,
+giving the message to further commands in
+.BR .qmail-default .
+If
+.B fastforward
+finds the recipient,
+it forwards the message and exits 99,
+so that further commands are skipped.
+.TP
+.B \-P
+(Default.)
+Do not pass through.
+If
+.B fastforward
+finds the recipient,
+it forwards the message and exits 0.
+Otherwise it bounces the message.
+.TP
+.B \-d
+Use
+.B $DEFAULT@$HOST
+as the recipient address, or
+.B $EXT@$HOST
+if
+.B $DEFAULT
+is not set.
+.TP
+.B \-D
+(Default.)
+Use
+.B $RECIPIENT
+as the recipient address.
+.SH VERSION
+This is
+.B fastforward
+0.51.
+The
+.B fastforward
+home page is
+.BR http://pobox.com/~djb/fastforward.html .
+.SH "SEE ALSO"
+newaliases(1),
+printforward(1),
+setforward(1),
+dot-qmail(5),
+qmail-command(8),
+qmail-local(8),
+qmail-getpw(8)
diff --git a/fastforward.c b/fastforward.c
new file mode 100644 (file)
index 0000000..e4ecab2
--- /dev/null
@@ -0,0 +1,408 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "stralloc.h"
+#include "substdio.h"
+#include "subfd.h"
+#include "strset.h"
+#include "sgetopt.h"
+#include "readwrite.h"
+#include "exit.h"
+#include "strerr.h"
+#include "env.h"
+#include "sig.h"
+#include "qmail.h"
+#include "fmt.h"
+#include "case.h"
+#include "alloc.h"
+#include "coe.h"
+#include "seek.h"
+#include "wait.h"
+#include "fork.h"
+
+#define FATAL "fastforward: fatal: "
+
+void usage()
+{
+  strerr_die1x(100,"fastforward: usage: fastforward [ -nNpP ] data.cdb");
+}
+void nomem()
+{
+  strerr_die2x(111,FATAL,"out of memory");
+}
+
+void print(s)
+char *s;
+{
+  char ch;
+  while (ch = *s++) {
+    substdio_put(subfderr,&ch,1);
+  }
+}
+
+void printsafe(s)
+char *s;
+{
+  char ch;
+  while (ch = *s++) {
+    if (ch < 32) ch = '_';
+    substdio_put(subfderr,&ch,1);
+  }
+}
+
+struct qmail qq;
+char qp[FMT_ULONG];
+char qqbuf[1];
+
+int qqwrite(fd,buf,len) int fd; char *buf; int len;
+{
+  qmail_put(&qq,buf,len);
+  return len;
+}
+
+substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,sizeof qqbuf);
+
+char messbuf[4096];
+substdio ssmess = SUBSTDIO_FDBUF(read,0,messbuf,sizeof messbuf);
+
+int flagdeliver = 1;
+int flagpassthrough = 0;
+
+char *dtline;
+stralloc sender = {0};
+stralloc programs = {0};
+stralloc forward = {0};
+
+strset done;
+stralloc todo = {0};
+
+stralloc mailinglist = {0};
+
+void dofile(fn)
+char *fn;
+{
+  int fd;
+  struct stat st;
+  int i;
+  int j;
+
+  if (!stralloc_copys(&mailinglist,"")) nomem();
+
+  fd = open_read(fn);
+  if (fd == -1)
+    strerr_die4sys(111,FATAL,"unable to read ",fn,": ");
+  if (fstat(fd,&st) == -1)
+    strerr_die4sys(111,FATAL,"unable to stat ",fn,": ");
+  if ((st.st_mode & 0444) != 0444)
+    strerr_die3x(111,FATAL,fn," is not world-readable");
+  if (slurpclose(fd,&mailinglist,1024) == -1)
+    strerr_die4sys(111,FATAL,"unable to read ",fn,": ");
+
+  i = 0;
+  for (j = 0;j < mailinglist.len;++j)
+    if (!mailinglist.s[j]) {
+      if ((mailinglist.s[i] == '.') || (mailinglist.s[i] == '/')) {
+        if (!stralloc_cats(&todo,mailinglist.s + i)) nomem();
+        if (!stralloc_0(&todo)) nomem();
+      }
+      else if ((mailinglist.s[i] == '&') && (j - i < 900)) {
+        if (!stralloc_cats(&todo,mailinglist.s + i)) nomem();
+        if (!stralloc_0(&todo)) nomem();
+      }
+      i = j + 1;
+    }
+}
+
+char *fncdb;
+int fdcdb;
+stralloc key = {0};
+uint32 dlen;
+stralloc data = {0};
+
+void cdbreaderror()
+{
+  strerr_die4sys(111,FATAL,"unable to read ",fncdb,": ");
+}
+
+int findtarget(flagwild,prepend,addr)
+int flagwild;
+char *prepend;
+char *addr;
+{
+  int r;
+  int at;
+
+  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 (!stralloc_copys(&key,prepend)) 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;
+
+  if (!stralloc_copys(&key,prepend)) 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;
+
+  return 0;
+}
+
+int gettarget(flagwild,prepend,addr)
+int flagwild;
+char *prepend;
+char *addr;
+{
+  if (!findtarget(flagwild,prepend,addr)) return 0;
+
+  if (!stralloc_ready(&data,(unsigned int) dlen)) nomem();
+  data.len = dlen;
+  if (cdb_bread(fdcdb,data.s,data.len) == -1) cdbreaderror();
+
+  return 1;
+}
+
+void doprogram(arg)
+char *arg;
+{
+  char *args[5];
+  int child;
+  int wstat;
+
+  if (!flagdeliver) {
+    print("run ");
+    printsafe(arg);
+    print("\n");
+    substdio_flush(subfderr);
+    return;
+  }
+
+  if (*arg == '!') {
+    args[0] = "preline";
+    args[1] = "sh";
+    args[2] = "-c";
+    args[3] = arg + 1;
+    args[4] = 0;
+  }
+  else {
+    args[0] = "sh";
+    args[1] = "-c";
+    args[2] = arg + 1;
+    args[3] = 0;
+  }
+
+  switch(child = vfork()) {
+    case -1:
+      strerr_die2sys(111,FATAL,"unable to fork: ");
+    case 0:
+      sig_pipedefault();
+      execvp(*args,args);
+      strerr_die4sys(111,FATAL,"unable to run ",arg,": ");
+  }
+
+  wait_pid(&wstat,child);
+  if (wait_crashed(wstat))
+    strerr_die4sys(111,FATAL,"child crashed in ",arg,": ");
+
+  switch(wait_exitcode(wstat)) {
+    case 64: case 65: case 70: case 76: case 77: case 78: case 112:
+    case 100: _exit(100);
+    case 0: break;
+    default: _exit(111);
+  }
+
+  if (seek_begin(0) == -1)
+    strerr_die2sys(111,FATAL,"unable to rewind input: ");
+}
+
+void dodata()
+{
+  int i;
+  int j;
+  i = 0;
+  for (j = 0;j < data.len;++j)
+    if (!data.s[j]) {
+      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] == '&') && (j - i < 900)) {
+        if (!stralloc_cats(&todo,data.s + i)) nomem();
+        if (!stralloc_0(&todo)) nomem();
+      }
+      i = j + 1;
+    }
+}
+
+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();
+}
+
+void doorigrecip(addr)
+char *addr;
+{
+  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 (flagpassthrough)
+      _exit(0);
+    else
+      strerr_die1x(100,"Sorry, no mailbox here by that name. (#5.1.1)");
+  dodata();
+}
+
+stralloc recipient = {0};
+int flagdefault = 0;
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  int opt;
+  char *x;
+  int i;
+
+  sig_pipeignore();
+
+  dtline = env_get("DTLINE");
+  if (!dtline) dtline = "";
+
+  x = env_get("SENDER");
+  if (!x) x = "original envelope sender";
+  if (!stralloc_copys(&sender,x)) nomem();
+
+  if (!stralloc_copys(&forward,"")) nomem();
+  if (!strset_init(&done)) nomem();
+
+  while ((opt = getopt(argc,argv,"nNpPdD")) != opteof)
+    switch(opt) {
+      case 'n': flagdeliver = 0; break;
+      case 'N': flagdeliver = 1; break;
+      case 'p': flagpassthrough = 1; break;
+      case 'P': flagpassthrough = 0; break;
+      case 'd': flagdefault = 1; break;
+      case 'D': flagdefault = 0; break;
+      default: usage();
+    }
+  argv += optind;
+
+  fncdb = *argv;
+  if (!fncdb) usage();
+  fdcdb = open_read(fncdb);
+  if (fdcdb == -1) cdbreaderror();
+  coe(fdcdb);
+
+  if (flagdefault) {
+    x = env_get("DEFAULT");
+    if (!x) x = env_get("EXT");
+    if (!x) strerr_die2x(100,FATAL,"$DEFAULT or $EXT must be set");
+    if (!stralloc_copys(&recipient,x)) nomem();
+    if (!stralloc_cats(&recipient,"@")) nomem();
+    x = env_get("HOST");
+    if (!x) strerr_die2x(100,FATAL,"$HOST must be set");
+    if (!stralloc_cats(&recipient,x)) nomem();
+    if (!stralloc_0(&recipient)) nomem();
+    x = recipient.s;
+  }
+  else {
+    x = env_get("RECIPIENT");
+    if (!x) strerr_die2x(100,FATAL,"$RECIPIENT must be set");
+  }
+  if (!strset_add(&done,x)) nomem();
+  doorigrecip(x);
+
+  while (todo.len) {
+    i = todo.len - 1;
+    while ((i > 0) && todo.s[i - 1]) --i;
+    todo.len = i;
+
+    if (strset_in(&done,todo.s + i)) continue;
+
+    x = alloc(str_len(todo.s + i) + 1);
+    if (!x) nomem();
+    str_copy(x,todo.s + i);
+    if (!strset_add(&done,x)) nomem();
+
+    x = todo.s + i;
+    if (*x == 0)
+      continue;
+    else if ((*x == '.') || (*x == '/'))
+      dofile(x);
+    else
+      dorecip(x + 1);
+  }
+
+  if (!forward.len) {
+    if (!flagdeliver) {
+      print("no forwarding\n");
+      substdio_flush(subfderr);
+    }
+    _exit(flagpassthrough ? 99 : 0);
+  }
+
+  if (!stralloc_0(&sender)) nomem();
+
+  if (!flagdeliver) {
+    print("from <");
+    printsafe(sender.s);
+    print(">\n");
+    while (forward.len) {
+      i = forward.len - 1;
+      while ((i > 0) && forward.s[i - 1]) --i;
+      forward.len = i;
+      print("to <");
+      printsafe(forward.s + i);
+      print(">\n");
+    }
+    substdio_flush(subfderr);
+    _exit(flagpassthrough ? 99 : 0);
+  }
+
+  if (qmail_open(&qq) == -1)
+    strerr_die2sys(111,FATAL,"unable to fork: ");
+  qmail_puts(&qq,dtline);
+  if (substdio_copy(&ssqq,&ssmess) != 0)
+    strerr_die2sys(111,FATAL,"unable to read message: ");
+  substdio_flush(&ssqq);
+  qp[fmt_ulong(qp,qmail_qp(&qq))] = 0;
+
+  qmail_from(&qq,sender.s);
+
+  while (forward.len) {
+    i = forward.len - 1;
+    while ((i > 0) && forward.s[i - 1]) --i;
+    forward.len = i;
+    qmail_to(&qq,forward.s + i);
+  }
+
+  x = qmail_close(&qq);
+  if (*x) strerr_die2x(*x == 'D' ? 100 : 111,FATAL,x + 1);
+  strerr_die2x(flagpassthrough ? 99 : 0,"fastforward: qp ",qp);
+}
diff --git a/fd.h b/fd.h
new file mode 100644 (file)
index 0000000..c3d6e3e
--- /dev/null
+++ b/fd.h
@@ -0,0 +1,7 @@
+#ifndef FD_H
+#define FD_H
+
+extern int fd_copy();
+extern int fd_move();
+
+#endif
diff --git a/fd_copy.c b/fd_copy.c
new file mode 100644 (file)
index 0000000..b9f7167
--- /dev/null
+++ b/fd_copy.c
@@ -0,0 +1,13 @@
+#include <fcntl.h>
+#include "fd.h"
+
+int fd_copy(to,from)
+int to;
+int from;
+{
+  if (to == from) return 0;
+  if (fcntl(from,F_GETFL,0) == -1) return -1;
+  close(to);
+  if (fcntl(from,F_DUPFD,to) == -1) return -1;
+  return 0;
+}
diff --git a/fd_move.c b/fd_move.c
new file mode 100644 (file)
index 0000000..1aa557f
--- /dev/null
+++ b/fd_move.c
@@ -0,0 +1,11 @@
+#include "fd.h"
+
+int fd_move(to,from)
+int to;
+int from;
+{
+  if (to == from) return 0;
+  if (fd_copy(to,from) == -1) return -1;
+  close(from);
+  return 0;
+}
diff --git a/find-systype.sh b/find-systype.sh
new file mode 100644 (file)
index 0000000..16266d3
--- /dev/null
@@ -0,0 +1,144 @@
+# oper-:arch-:syst-:chip-:kern-
+# oper = operating system type; e.g., sunos-4.1.4
+# arch = machine language; e.g., sparc
+# syst = which binaries can run; e.g., sun4
+# chip = chip model; e.g., micro-2-80
+# kern = kernel version; e.g., sun4m
+# dependence: arch --- chip
+#                 \        \
+#          oper --- syst --- kern
+# so, for example, syst is interpreted in light of oper, but chip is not.
+# anyway, no slashes, no extra colons, no uppercase letters.
+# the point of the extra -'s is to ease parsing: can add hierarchies later.
+# e.g., *:i386-*:*:pentium-*:* would handle pentium-100 as well as pentium,
+# and i386-486 (486s do have more instructions, you know) as well as i386.
+# the idea here is to include ALL useful available information.
+
+exec 2>/dev/null
+sys="`uname -s | tr '/:[A-Z]' '..[a-z]'`"
+if [ x"$sys" != x ]
+then
+  unamer="`uname -r | tr /: ..`"
+  unamem="`uname -m | tr /: ..`"
+  unamev="`uname -v | tr /: ..`"
+
+  case "$sys" in
+  bsd.os)
+    # in bsd 4.4, uname -v does not have useful info.
+    # in bsd 4.4, uname -m is arch, not chip.
+    oper="$sys-$unamer"
+    arch="$unamem"
+    syst=""
+    chip="`sysctl -n hw.model`"
+    kern=""
+    ;;
+  freebsd)
+    # see above about bsd 4.4
+    oper="$sys-$unamer"
+    arch="$unamem"
+    syst=""
+    chip="`sysctl -n hw.model`" # hopefully
+    kern=""
+    ;;
+  netbsd)
+    # see above about bsd 4.4
+    oper="$sys-$unamer"
+    arch="$unamem"
+    syst=""
+    chip="`sysctl -n hw.model`" # hopefully
+    kern=""
+    ;;
+  linux)
+    # as in bsd 4.4, uname -v does not have useful info.
+    oper="$sys-$unamer"
+    syst=""
+    chip="$unamem"
+    kern=""
+    case "$chip" in
+    i386|i486|i586|i686)
+      arch="i386"
+      ;;
+    alpha)
+      arch="alpha"
+      ;;
+    esac
+    ;;
+  aix)
+    # naturally IBM has to get uname -r and uname -v backwards. dorks.
+    oper="$sys-$unamev-$unamer"
+    arch="`arch | tr /: ..`"
+    syst=""
+    chip="$unamem"
+    kern=""
+    ;;
+  sunos)
+    oper="$sys-$unamer-$unamev"
+    arch="`(uname -p || mach) | tr /: ..`"
+    syst="`arch | tr /: ..`"
+    chip="$unamem" # this is wrong; is there any way to get the real info?
+    kern="`arch -k | tr /: ..`"
+    ;;
+  unix_sv)
+    oper="$sys-$unamer-$unamev"
+    arch="`uname -m`"
+    syst=""
+    chip="$unamem"
+    kern=""
+    ;;
+  *)
+    oper="$sys-$unamer-$unamev"
+    arch="`arch | tr /: ..`"
+    syst=""
+    chip="$unamem"
+    kern=""
+    ;;
+  esac
+else
+  $CC -c trycpp.c
+  $LD -o trycpp trycpp.o
+  case `./trycpp` in
+  nextstep)
+    oper="nextstep-`hostinfo | sed -n 's/^[    ]*NeXT Mach \([^:]*\):.*$/\1/p'`"
+    arch="`hostinfo | sed -n 's/^Processor type: \(.*\) (.*)$/\1/p' | tr /: ..`"
+    syst=""
+    chip="`hostinfo | sed -n 's/^Processor type: .* (\(.*\))$/\1/p' | tr ' /:' '...'`"
+    kern=""
+    ;;
+  *)
+    oper="unknown"
+    arch=""
+    syst=""
+    chip=""
+    kern=""
+    ;;
+  esac
+  rm -f trycpp.o trycpp
+fi
+
+case "$chip" in
+80486)
+  # let's try to be consistent here. (BSD/OS)
+  chip=i486
+  ;;
+i486DX)
+  # respect the hyphen hierarchy. (FreeBSD)
+  chip=i486-dx
+  ;;
+i486.DX2)
+  # respect the hyphen hierarchy. (FreeBSD)
+  chip=i486-dx2
+  ;;
+Intel.586)
+  # no, you nitwits, there is no such chip. (NeXTStep)
+  chip=pentium
+  ;;
+i586)
+  # no, you nitwits, there is no such chip. (Linux)
+  chip=pentium
+  ;;
+i686)
+  # STOP SAYING THAT! (Linux)
+  chip=ppro
+esac
+
+echo "$oper-:$arch-:$syst-:$chip-:$kern-" | tr ' [A-Z]' '.[a-z]'
diff --git a/fmt.h b/fmt.h
new file mode 100644 (file)
index 0000000..ba2fe16
--- /dev/null
+++ b/fmt.h
@@ -0,0 +1,25 @@
+#ifndef FMT_H
+#define FMT_H
+
+#define FMT_ULONG 40 /* enough space to hold 2^128 - 1 in decimal, plus \0 */
+#define FMT_LEN ((char *) 0) /* convenient abbreviation */
+
+extern unsigned int fmt_uint();
+extern unsigned int fmt_uint0();
+extern unsigned int fmt_xint();
+extern unsigned int fmt_nbbint();
+extern unsigned int fmt_ushort();
+extern unsigned int fmt_xshort();
+extern unsigned int fmt_nbbshort();
+extern unsigned int fmt_ulong();
+extern unsigned int fmt_xlong();
+extern unsigned int fmt_nbblong();
+
+extern unsigned int fmt_plusminus();
+extern unsigned int fmt_minus();
+extern unsigned int fmt_0x();
+
+extern unsigned int fmt_str();
+extern unsigned int fmt_strn();
+
+#endif
diff --git a/fmt_ulong.c b/fmt_ulong.c
new file mode 100644 (file)
index 0000000..ab12e8c
--- /dev/null
@@ -0,0 +1,13 @@
+#include "fmt.h"
+
+unsigned int fmt_ulong(s,u) register char *s; register unsigned long u;
+{
+  register unsigned int len; register unsigned long q;
+  len = 1; q = u;
+  while (q > 9) { ++len; q /= 10; }
+  if (s) {
+    s += len;
+    do { *--s = '0' + (u % 10); u /= 10; } while(u); /* handles u == 0 */
+  }
+  return len;
+}
diff --git a/fork.h1 b/fork.h1
new file mode 100644 (file)
index 0000000..b786255
--- /dev/null
+++ b/fork.h1
@@ -0,0 +1,7 @@
+#ifndef FORK_H
+#define FORK_H
+
+extern int fork();
+#define vfork fork
+
+#endif
diff --git a/fork.h2 b/fork.h2
new file mode 100644 (file)
index 0000000..41773b6
--- /dev/null
+++ b/fork.h2
@@ -0,0 +1,7 @@
+#ifndef FORK_H
+#define FORK_H
+
+extern int fork();
+extern int vfork();
+
+#endif
diff --git a/gen_alloc.h b/gen_alloc.h
new file mode 100644 (file)
index 0000000..b94a956
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef GEN_ALLOC_H
+#define GEN_ALLOC_H
+
+#define GEN_ALLOC_typedef(ta,type,field,len,a) \
+  typedef struct ta { type *field; unsigned int len; unsigned int a; } ta;
+
+#endif
diff --git a/gen_allocdefs.h b/gen_allocdefs.h
new file mode 100644 (file)
index 0000000..783a9b1
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef GEN_ALLOC_DEFS_H
+#define GEN_ALLOC_DEFS_H
+
+#define GEN_ALLOC_ready(ta,type,field,len,a,i,n,x,base,ta_ready) \
+int ta_ready(x,n) register ta *x; register unsigned int n; \
+{ register unsigned int i; \
+  if (x->field) { \
+    i = x->a; \
+    if (n > i) { \
+      x->a = base + n + (n >> 3); \
+      if (alloc_re(&x->field,i * sizeof(type),x->a * sizeof(type))) return 1; \
+      x->a = i; return 0; } \
+    return 1; } \
+  x->len = 0; \
+  return !!(x->field = (type *) alloc((x->a = n) * sizeof(type))); }
+
+#define GEN_ALLOC_readyplus(ta,type,field,len,a,i,n,x,base,ta_rplus) \
+int ta_rplus(x,n) register ta *x; register unsigned int n; \
+{ register unsigned int i; \
+  if (x->field) { \
+    i = x->a; n += x->len; \
+    if (n > i) { \
+      x->a = base + n + (n >> 3); \
+      if (alloc_re(&x->field,i * sizeof(type),x->a * sizeof(type))) return 1; \
+      x->a = i; return 0; } \
+    return 1; } \
+  x->len = 0; \
+  return !!(x->field = (type *) alloc((x->a = n) * sizeof(type))); }
+
+#define GEN_ALLOC_append(ta,type,field,len,a,i,n,x,base,ta_rplus,ta_append) \
+int ta_append(x,i) register ta *x; register type *i; \
+{ if (!ta_rplus(x,1)) return 0; x->field[x->len++] = *i; return 1; }
+
+#endif
diff --git a/getln.c b/getln.c
new file mode 100644 (file)
index 0000000..c5cb097
--- /dev/null
+++ b/getln.c
@@ -0,0 +1,20 @@
+#include "substdio.h"
+#include "byte.h"
+#include "stralloc.h"
+#include "getln.h"
+
+int getln(ss,sa,match,sep)
+register substdio *ss;
+register stralloc *sa;
+int *match;
+int sep;
+{
+  char *cont;
+  unsigned int clen;
+
+  if (getln2(ss,sa,&cont,&clen,sep) == -1) return -1;
+  if (!clen) { *match = 0; return 0; }
+  if (!stralloc_catb(sa,cont,clen)) return -1;
+  *match = 1;
+  return 0;
+}
diff --git a/getln.h b/getln.h
new file mode 100644 (file)
index 0000000..cf4f934
--- /dev/null
+++ b/getln.h
@@ -0,0 +1,7 @@
+#ifndef GETLN_H
+#define GETLN_H
+
+extern int getln();
+extern int getln2();
+
+#endif
diff --git a/getln2.c b/getln2.c
new file mode 100644 (file)
index 0000000..9e576e9
--- /dev/null
+++ b/getln2.c
@@ -0,0 +1,31 @@
+#include "substdio.h"
+#include "stralloc.h"
+#include "byte.h"
+#include "getln.h"
+
+int getln2(ss,sa,cont,clen,sep)
+register substdio *ss;
+register stralloc *sa;
+/*@out@*/char **cont;
+/*@out@*/unsigned int *clen;
+int sep;
+{
+  register char *x;
+  register unsigned int i;
+  int n;
+  if (!stralloc_ready(sa,0)) return -1;
+  sa->len = 0;
+  for (;;) {
+    n = substdio_feed(ss);
+    if (n < 0) return -1;
+    if (n == 0) { *clen = 0; return 0; }
+    x = substdio_PEEK(ss);
+    i = byte_chr(x,n,sep);
+    if (i < n) { substdio_SEEK(ss,*clen = i + 1); *cont = x; return 0; }
+    if (!stralloc_readyplus(sa,n)) return -1;
+    i = sa->len;
+    sa->len = i + substdio_get(ss,sa->s + i,n);
+  }
+}
diff --git a/hier.c b/hier.c
new file mode 100644 (file)
index 0000000..b5caa7f
--- /dev/null
+++ b/hier.c
@@ -0,0 +1,39 @@
+#include "auto_qmail.h"
+
+void hier()
+{
+  h(auto_qmail,-1,-1,0755);
+
+  d(auto_qmail,"bin",-1,-1,0755);
+  d(auto_qmail,"doc",-1,-1,0755);
+  d(auto_qmail,"doc/fastforward",-1,-1,0755);
+  d(auto_qmail,"man",-1,-1,0755);
+  d(auto_qmail,"man/man1",-1,-1,0755);
+  d(auto_qmail,"man/cat1",-1,-1,0755);
+
+  c(auto_qmail,"bin","fastforward",-1,-1,0755);
+  c(auto_qmail,"bin","printforward",-1,-1,0755);
+  c(auto_qmail,"bin","setforward",-1,-1,0755);
+  c(auto_qmail,"bin","newaliases",-1,-1,0755);
+  c(auto_qmail,"bin","printmaillist",-1,-1,0755);
+  c(auto_qmail,"bin","setmaillist",-1,-1,0755);
+  c(auto_qmail,"bin","newinclude",-1,-1,0755);
+
+  c(auto_qmail,"doc/fastforward","ALIASES",-1,-1,0644);
+
+  c(auto_qmail,"man/man1","fastforward.1",-1,-1,0644);
+  c(auto_qmail,"man/man1","printforward.1",-1,-1,0644);
+  c(auto_qmail,"man/man1","setforward.1",-1,-1,0644);
+  c(auto_qmail,"man/man1","newaliases.1",-1,-1,0644);
+  c(auto_qmail,"man/man1","printmaillist.1",-1,-1,0644);
+  c(auto_qmail,"man/man1","setmaillist.1",-1,-1,0644);
+  c(auto_qmail,"man/man1","newinclude.1",-1,-1,0644);
+
+  c(auto_qmail,"man/cat1","fastforward.0",-1,-1,0644);
+  c(auto_qmail,"man/cat1","printforward.0",-1,-1,0644);
+  c(auto_qmail,"man/cat1","setforward.0",-1,-1,0644);
+  c(auto_qmail,"man/cat1","newaliases.0",-1,-1,0644);
+  c(auto_qmail,"man/cat1","printmaillist.0",-1,-1,0644);
+  c(auto_qmail,"man/cat1","setmaillist.0",-1,-1,0644);
+  c(auto_qmail,"man/cat1","newinclude.0",-1,-1,0644);
+}
diff --git a/install.c b/install.c
new file mode 100644 (file)
index 0000000..beec00c
--- /dev/null
+++ b/install.c
@@ -0,0 +1,111 @@
+#include "substdio.h"
+#include "strerr.h"
+#include "error.h"
+#include "open.h"
+#include "readwrite.h"
+#include "exit.h"
+
+extern void hier();
+
+#define FATAL "install: fatal: "
+
+int fdsourcedir = -1;
+
+void h(home,uid,gid,mode)
+char *home;
+int uid;
+int gid;
+int mode;
+{
+  if (mkdir(home,0700) == -1)
+    if (errno != error_exist)
+      strerr_die4sys(111,FATAL,"unable to mkdir ",home,": ");
+  if (chown(home,uid,gid) == -1)
+    strerr_die4sys(111,FATAL,"unable to chown ",home,": ");
+  if (chmod(home,mode) == -1)
+    strerr_die4sys(111,FATAL,"unable to chmod ",home,": ");
+}
+
+void d(home,subdir,uid,gid,mode)
+char *home;
+char *subdir;
+int uid;
+int gid;
+int mode;
+{
+  if (chdir(home) == -1)
+    strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
+  if (mkdir(subdir,0700) == -1)
+    if (errno != error_exist)
+      strerr_die6sys(111,FATAL,"unable to mkdir ",home,"/",subdir,": ");
+  if (chown(subdir,uid,gid) == -1)
+    strerr_die6sys(111,FATAL,"unable to chown ",home,"/",subdir,": ");
+  if (chmod(subdir,mode) == -1)
+    strerr_die6sys(111,FATAL,"unable to chmod ",home,"/",subdir,": ");
+}
+
+char inbuf[SUBSTDIO_INSIZE];
+char outbuf[SUBSTDIO_OUTSIZE];
+substdio ssin;
+substdio ssout;
+
+void c(home,subdir,file,uid,gid,mode)
+char *home;
+char *subdir;
+char *file;
+int uid;
+int gid;
+int mode;
+{
+  int fdin;
+  int fdout;
+
+  if (fchdir(fdsourcedir) == -1)
+    strerr_die2sys(111,FATAL,"unable to switch back to source directory: ");
+
+  fdin = open_read(file);
+  if (fdin == -1)
+    strerr_die4sys(111,FATAL,"unable to read ",file,": ");
+  substdio_fdbuf(&ssin,read,fdin,inbuf,sizeof inbuf);
+
+  if (chdir(home) == -1)
+    strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
+  if (chdir(subdir) == -1)
+    strerr_die6sys(111,FATAL,"unable to switch to ",home,"/",subdir,": ");
+
+  fdout = open_trunc(file);
+  if (fdout == -1)
+    strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
+  substdio_fdbuf(&ssout,write,fdout,outbuf,sizeof outbuf);
+
+  switch(substdio_copy(&ssout,&ssin)) {
+    case -2:
+      strerr_die4sys(111,FATAL,"unable to read ",file,": ");
+    case -3:
+      strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
+  }
+
+  close(fdin);
+  if (substdio_flush(&ssout) == -1)
+    strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
+  if (fsync(fdout) == -1)
+    strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
+  if (close(fdout) == -1) /* NFS silliness */
+    strerr_die6sys(111,FATAL,"unable to write .../",subdir,"/",file,": ");
+
+  if (chown(file,uid,gid) == -1)
+    strerr_die6sys(111,FATAL,"unable to chown .../",subdir,"/",file,": ");
+  if (chmod(file,mode) == -1)
+    strerr_die6sys(111,FATAL,"unable to chmod .../",subdir,"/",file,": ");
+}
+
+void main()
+{
+  fdsourcedir = open_read(".");
+  if (fdsourcedir == -1)
+    strerr_die2sys(111,FATAL,"unable to open current directory: ");
+
+  umask(077);
+  hier();
+  _exit(0);
+}
diff --git a/instcheck.c b/instcheck.c
new file mode 100644 (file)
index 0000000..0b77e45
--- /dev/null
@@ -0,0 +1,83 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "strerr.h"
+#include "error.h"
+#include "readwrite.h"
+#include "exit.h"
+
+extern void hier();
+
+#define FATAL "instcheck: fatal: "
+#define WARNING "instcheck: warning: "
+
+void perm(prefix1,prefix2,prefix3,file,type,uid,gid,mode)
+char *prefix1;
+char *prefix2;
+char *prefix3;
+char *file;
+int type;
+int uid;
+int gid;
+int mode;
+{
+  struct stat st;
+
+  if (stat(file,&st) == -1) {
+    if (errno == error_noent)
+      strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," does not exist",0);
+    else
+      strerr_warn4(WARNING,"unable to stat .../",file,": ",&strerr_sys);
+    return;
+  }
+
+  if ((uid != -1) && (st.st_uid != uid))
+    strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," has wrong owner",0);
+  if ((gid != -1) && (st.st_gid != gid))
+    strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," has wrong group",0);
+  if ((st.st_mode & 07777) != mode)
+    strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," has wrong permissions",0);
+  if ((st.st_mode & S_IFMT) != type)
+    strerr_warn6(WARNING,prefix1,prefix2,prefix3,file," has wrong type",0);
+}
+
+void h(home,uid,gid,mode)
+char *home;
+int uid;
+int gid;
+int mode;
+{
+  perm("","","",home,S_IFDIR,uid,gid,mode);
+}
+
+void d(home,subdir,uid,gid,mode)
+char *home;
+char *subdir;
+int uid;
+int gid;
+int mode;
+{
+  if (chdir(home) == -1)
+    strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
+  perm("",home,"/",subdir,S_IFDIR,uid,gid,mode);
+}
+
+void c(home,subdir,file,uid,gid,mode)
+char *home;
+char *subdir;
+char *file;
+int uid;
+int gid;
+int mode;
+{
+  if (chdir(home) == -1)
+    strerr_die4sys(111,FATAL,"unable to switch to ",home,": ");
+  if (chdir(subdir) == -1)
+    strerr_die6sys(111,FATAL,"unable to switch to ",home,"/",subdir,": ");
+  perm(".../",subdir,"/",file,S_IFREG,uid,gid,mode);
+}
+
+void main()
+{
+  hier();
+  _exit(0);
+}
diff --git a/make-compile.sh b/make-compile.sh
new file mode 100644 (file)
index 0000000..a1eb501
--- /dev/null
@@ -0,0 +1 @@
+echo exec "$CC" -c '${1+"$@"}'
diff --git a/make-load.sh b/make-load.sh
new file mode 100644 (file)
index 0000000..de07d2e
--- /dev/null
@@ -0,0 +1,2 @@
+echo 'main="$1"; shift'
+echo exec "$LD" '-o "$main" "$main".o ${1+"$@"}'
diff --git a/make-makelib.sh b/make-makelib.sh
new file mode 100644 (file)
index 0000000..d6b7c8c
--- /dev/null
@@ -0,0 +1,16 @@
+echo 'main="$1"; shift'
+echo 'rm -f "$main"'
+echo 'ar cr "$main" ${1+"$@"}'
+
+case "$1" in
+sunos-5.*) ;;
+unix_sv*) ;;
+irix64-*) ;;
+irix-*) ;;
+dgux-*) ;;
+hp-ux-*) ;;
+sco*) ;;
+*)
+  echo 'ranlib "$main"'
+  ;;
+esac
diff --git a/newaliases.1 b/newaliases.1
new file mode 100644 (file)
index 0000000..c94868f
--- /dev/null
@@ -0,0 +1,366 @@
+.TH newaliases 1
+.SH NAME
+newaliases \- create a forwarding database from /etc/aliases
+.SH SYNOPSIS
+.B newaliases
+.SH DESCRIPTION
+.B newaliases
+reads a table of
+sendmail-style
+forwarding instructions from
+.B /etc/aliases
+and converts them into a forwarding database in
+.BR /etc/aliases.cdb .
+The forwarding database can be used by
+.BR fastforward .
+
+For safety,
+.B newaliases
+writes the forwarding database to
+.B /etc/aliases.tmp
+and then moves
+.B /etc/aliases.tmp
+to
+.BR /etc/aliases.cdb .
+If there is a problem creating
+.BR /etc/aliases.tmp ,
+.B newaliases
+complains and leaves
+.B /etc/aliases.cdb
+alone.
+Deliveries can continue using
+.B /etc/aliases.cdb
+in the meantime.
+
+.B newaliases
+always creates
+.B /etc/aliases.cdb
+world-readable.
+
+.B newaliases
+makes no attempt to protect against
+simultaneous updates of
+.BR /etc/aliases.cdb .
+.SH "INSTRUCTION FORMAT"
+.B newaliases
+imitates
+sendmail's
+handling of
+.BR /etc/aliases .
+For example,
+
+.EX
+   root: alice, bill
+.EE
+
+says that mail for
+.B root
+should be forwarded to
+.B alice
+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
+
+.EX
+   alias: recip
+.EE
+
+Any message sent to
+.I alias
+will be forwarded to the recipient address
+.IR recip .
+Addresses are compared to
+.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,
+entries in
+.B /etc/aliases
+can override usernames.
+With
+.BR qmail ,
+if you install
+.B fastforward
+in
+.BR ~alias/.qmail-default ,
+it will not see addresses that are controlled by other users.
+See
+.BR qmail-getpw (8).
+To change this, see
+.BR qmail-users (5).
+
+.B COMPATIBILITY WARNING:
+Various versions of
+sendmail
+do various strange things with circular alias definitions.
+See
+.BR setforward (1)
+for details on
+.BR fastforward 's
+behavior.
+
+.B COMPATIBILITY WARNING:
+If there are several forwarding instructions for a single
+.IR alias ,
+sendmail
+will complain;
+.B fastforward
+will silently use the first instruction.
+.SH "WILDCARDS"
+.I alias
+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.
+
+.B COMPATIBILITY WARNING:
+sendmail
+supports only
+.IR user ;
+it does not support per-host aliases.
+It accepts
+.I user@host.dom
+if
+.I host.dom
+is a local host,
+but it then treats it the same way as
+.IR user ,
+applying to all local hosts and virtual domains.
+.SH "ADDRESS FORMATS"
+Addresses in
+.B /etc/aliases
+are parsed the same way as addresses in RFC 822 message headers.
+Parenthesized comments and bracketed addresses are permitted:
+
+.EX
+   root: bob (Bob, the postmaster)
+   joe: Joe Shmoe <shmoe@heaven.af.mil>
+.EE
+
+Addresses with special characters must be quoted:
+
+.EX
+   fred: "spaced out mailbox"@heaven.af.mil
+.EE
+
+Address groups are not permitted,
+since colons have a different use in
+.BR /etc/aliases .
+
+Any recipient address without a fully qualified domain name is
+fed through the
+.BR defaulthost ,
+.BR defaultdomain ,
+and
+.B plusdomain
+mechanisms described in
+.BR qmail-header (5).
+
+.B COMPATIBILITY WARNING:
+sendmail's
+handling of quotes and backslashes violates RFC 821 and RFC 822,
+and is not supported by
+.BR newaliases .
+The
+.B qmail-local
+delivery mechanism
+lets each user manage several addresses,
+so there is no need for a special syntax to get around forwarding.
+.SH "MULTIPLE RECIPIENTS"
+An instruction may list more than one recipient address:
+
+.EX
+   alias: recip1, recip2, recip3
+.EE
+
+Any message sent to
+.I alias
+will be forwarded to all of the addresses.
+
+A forwarding instruction may be split across several lines.
+Each line past the first must either (1) begin with space or tab
+or (2) be empty:
+
+.EX
+   hostmaster:
+.EE
+.br
+.EX
+      fred,
+.EE
+.br
+.EX
+      joe
+.EE
+
+.B COMPATIBILITY WARNING:
+sendmail
+requires the colon to be on the first line
+of a multi-line forwarding instruction.
+.B newaliases
+doesn't care whether the colon is present at all.
+
+.B COMPATIBILITY WARNING:
+sendmail
+does not permit blank lines in the middle of continuations.
+This has the undesirable effect that a blank line behaves differently
+from a line containing a single space.
+.SH "COMMENTS"
+Any line in
+.B /etc/aliases
+that begins with # is ignored:
+
+.EX
+   # this is a comment
+.EE
+
+A comment may be split across several lines.
+Each line past the first must either (1) begin with space or tab
+or (2) be empty.
+
+.B COMPATIBILITY WARNING:
+sendmail
+does not permit continuations of comment lines.
+.SH "PROGRAMS"
+If a recipient address does not contain a domain name,
+and begins with a vertical bar,
+.B newaliases
+takes the rest of the address as a program to run:
+
+.EX
+   weather: "|weather-server"
+.EE
+
+.B fastforward
+will run
+.B weather-server
+when a message arrives for
+.BR weather .
+
+.B COMPATIBILITY WARNING:
+Internet addresses can legitimately start with
+a slash or vertical bar.
+.B newaliases
+treats anything with an unquoted @ as an address.
+sendmail appears to have various problems
+coping with these addresses,
+and with commands that contain @ signs.
+
+.B COMPATIBILITY WARNING:
+.B newaliases
+does not allow a vertical bar before double quotes.
+.SH "INCLUDE FILES"
+A recipient address of the form
+.B :include:\fIfile
+means ``every address listed in
+.IR file .''
+(Actually
+.B fastforward
+reads
+.IR file\fB.bin ;
+see
+.BR newinclude (1)
+for further details.)
+
+Note that
+.I file
+is read by
+.BR fastforward ,
+not
+.BR newaliases ,
+so the system administrator does not have to run
+.B newaliases
+every time
+.I file
+changes.
+.I file
+must be world-readable
+and accessible to
+.BR fastforward .
+
+.B COMPATIBILITY WARNING:
+If an
+.B :include:
+file is unreadable or nonexistent,
+sendmail
+skips it;
+.B fastforward
+defers delivery of the message.
+
+.B COMPATIBILITY WARNING:
+sendmail
+does not permit spaces inside the literal text
+.BR :include: .
+.B newaliases
+does.
+
+.B COMPATIBILITY WARNING:
+Versions of
+sendmail
+before V8 did not strip quotes from
+.B :include:
+filenames.
+.SH "ALIAS OWNERS"
+If there is an alias for
+.BR owner-\fIlist ,
+any message forwarded through
+.I list
+will have its envelope sender set to
+.BR owner-\fIlist ,
+so that bounces go back to
+.BR owner-\fIlist .
+
+.B COMPATIBILITY WARNING:
+When an alias includes the same recipient both inside and outside
+a mailing list,
+.B fastforward
+sends the message twice,
+once with each envelope sender.
+sendmail
+sends the message only once;
+its choice of envelope sender for that recipient
+depends on the phase of the moon.
+.SH "SEE ALSO"
+fastforward(1),
+setforward(1),
+newinclude(1),
+printforward(1),
+dot-qmail(5)
diff --git a/newaliases.c b/newaliases.c
new file mode 100644 (file)
index 0000000..3025b01
--- /dev/null
@@ -0,0 +1,321 @@
+#include "substdio.h"
+#include "strerr.h"
+#include "stralloc.h"
+#include "getln.h"
+#include "open.h"
+#include "readwrite.h"
+#include "token822.h"
+#include "control.h"
+#include "auto_qmail.h"
+#include "case.h"
+#include "cdbmss.h"
+
+#define FATAL "newaliases: fatal: "
+
+void nomem()
+{
+  strerr_die2x(111,FATAL,"out of memory");
+}
+void nulbyte()
+{
+  strerr_die2x(100,FATAL,"NUL bytes are not permitted");
+}
+void longaddress()
+{
+  strerr_die2x(100,FATAL,"addresses over 800 bytes are not permitted");
+}
+void writeerr()
+{
+  strerr_die2sys(111,FATAL,"unable to write to /etc/aliases.tmp: ");
+}
+void readerr()
+{
+  strerr_die2sys(111,FATAL,"unable to read /etc/aliases: ");
+}
+void die_control()
+{
+  strerr_die2sys(111,FATAL,"unable to read controls: ");
+}
+
+stralloc me = {0};
+stralloc defaulthost = {0};
+stralloc defaultdomain = {0};
+stralloc plusdomain = {0};
+
+void readcontrols()
+{
+  int r;
+  int fddir;
+
+  fddir = open_read(".");
+  if (fddir == -1)
+    strerr_die2sys(111,FATAL,"unable to open current directory: ");
+
+  if (chdir(auto_qmail) == -1)
+    strerr_die4sys(111,FATAL,"unable to chdir to ",auto_qmail,": ");
+
+  r = control_readline(&me,"control/me");
+  if (r == -1) die_control();
+  if (!r) if (!stralloc_copys(&me,"me")) nomem();
+
+  r = control_readline(&defaultdomain,"control/defaultdomain");
+  if (r == -1) die_control();
+  if (!r) if (!stralloc_copy(&defaultdomain,&me)) nomem();
+
+  r = control_readline(&defaulthost,"control/defaulthost");
+  if (r == -1) die_control();
+  if (!r) if (!stralloc_copy(&defaulthost,&me)) nomem();
+
+  r = control_readline(&plusdomain,"control/plusdomain");
+  if (r == -1) die_control();
+  if (!r) if (!stralloc_copy(&plusdomain,&me)) nomem();
+
+  if (fchdir(fddir) == -1)
+    strerr_die2sys(111,FATAL,"unable to set current directory: ");
+}
+
+stralloc target = {0};
+stralloc fulltarget = {0};
+stralloc instr = {0};
+
+stralloc cbuf = {0};
+token822_alloc toks = {0};
+token822_alloc tokaddr = {0};
+stralloc address = {0};
+
+void gotincl()
+{
+  token822_reverse(&tokaddr);
+  if (token822_unquote(&address,&tokaddr) != 1) nomem();
+  tokaddr.len = 0;
+
+  if (!address.len)
+    strerr_die2x(111,FATAL,"empty :include: filenames not permitted");
+  if (byte_chr(address.s,address.len,'\0') < address.len)
+    strerr_die2x(111,FATAL,"NUL not permitted in :include: filenames");
+
+  if ((address.s[0] != '.') && (address.s[0] != '/'))
+    if (!stralloc_cats(&instr,"./")) nomem();
+  if (!stralloc_cat(&instr,&address)) nomem();
+  if (!stralloc_cats(&instr,".bin")) nomem();
+  if (!stralloc_0(&instr)) nomem();
+}
+
+void gotaddr()
+{
+  int i;
+  int j;
+  int flaghasat;
+  token822_reverse(&tokaddr);
+  if (token822_unquote(&address,&tokaddr) != 1) nomem();
+
+  if (!address.len)
+    strerr_die2x(111,FATAL,"empty recipient addresses not permitted");
+  flaghasat = 0;
+  for (i = 0;i < tokaddr.len;++i)
+    if (tokaddr.t[i].type == TOKEN822_AT)
+      flaghasat = 1;
+  tokaddr.len = 0;
+
+  if (!address.len) return;
+
+  if (!flaghasat)
+    if (address.s[0] == '/') {
+      if (!stralloc_0(&address)) nomem();
+      strerr_die4x(111,FATAL,"file delivery ",address.s," not supported");
+    }
+  if (!flaghasat)
+    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 (target.len) {
+    if (!stralloc_cats(&instr,"&")) nomem();
+    if (!stralloc_cat(&instr,&fulltarget)) nomem();
+    if (!stralloc_0(&instr)) nomem();
+  }
+
+  if (!flaghasat)
+    if (!stralloc_cats(&address,"@")) nomem();
+
+  if (!stralloc_copy(&target,&address)) nomem();
+  if (!stralloc_copy(&fulltarget,&address)) nomem();
+
+  if (fulltarget.s[fulltarget.len - 1] == '@')
+     if (!stralloc_cat(&fulltarget,&defaulthost)) nomem();
+  if (fulltarget.s[fulltarget.len - 1] == '+') {
+    fulltarget.s[fulltarget.len - 1] = '.';
+    if (!stralloc_cat(&fulltarget,&plusdomain)) nomem();
+  }
+
+  j = 0;
+  for (i = 0;i < fulltarget.len;++i) if (fulltarget.s[i] == '@') j = i;
+  for (i = j;i < fulltarget.len;++i) if (fulltarget.s[i] == '.') break;
+  if (i == fulltarget.len) {
+    if (!stralloc_cats(&fulltarget,".")) nomem();
+    if (!stralloc_cat(&fulltarget,&defaultdomain)) nomem();
+  }
+
+  if (fulltarget.len > 800) longaddress();
+  if (byte_chr(fulltarget.s,fulltarget.len,'\0') < fulltarget.len)
+    strerr_die2x(111,FATAL,"NUL not permitted in recipient addresses");
+}
+
+stralloc line = {0};
+stralloc newline = {0};
+int match;
+
+void parseerr()
+{
+  if (!stralloc_0(&line)) nomem();
+  strerr_die3x(111,FATAL,"unable to parse this line: ",line.s);
+}
+
+void parseline()
+{
+  int wordok;
+  struct token822 *t;
+  struct token822 *beginning;
+  switch(token822_parse(&toks,&line,&cbuf)) {
+    case -1: nomem();
+    case 0: parseerr();
+  }
+
+  beginning = toks.t;
+  t = toks.t + toks.len;
+  wordok = 1;
+  if (!token822_readyplus(&tokaddr,1)) nomem();
+  tokaddr.len = 0;
+  while (t > beginning)
+    switch((--t)->type) {
+      case TOKEN822_SEMI:
+        break; /*XXX*/
+      case TOKEN822_COLON:
+        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;
+                }
+        break; /*XXX*/
+      case TOKEN822_RIGHT:
+        if (tokaddr.len) gotaddr();
+        while ((t > beginning) && (t[-1].type != TOKEN822_LEFT))
+          if (!token822_append(&tokaddr,--t)) nomem();
+        gotaddr();
+        if (t <= beginning) parseerr();
+        --t;
+        while ((t > beginning) && ((t[-1].type == TOKEN822_COMMENT) || (t[-1].type == TOKEN822_ATOM) || (t[-1].type == TOKEN822_QUOTE) || (t[-1].type == TOKEN822_AT) || (t[-1].type == TOKEN822_DOT)))
+          --t;
+        wordok = 0;
+        continue;
+      case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL:
+        if (!wordok) if (tokaddr.len) gotaddr();
+        wordok = 0;
+        if (!token822_append(&tokaddr,t)) nomem();
+        continue;
+      case TOKEN822_COMMENT:
+        /* comment is lexically a space; shouldn't affect wordok */
+        break;
+      case TOKEN822_COMMA:
+        if (tokaddr.len) gotaddr();
+        wordok = 1;
+        break;
+      default:
+        wordok = 1;
+        if (!token822_append(&tokaddr,t)) nomem();
+        continue;
+    }
+  if (tokaddr.len) gotaddr();
+}
+
+char inbuf[1024];
+substdio ssin;
+struct cdbmss cdbmss;
+stralloc key = {0};
+
+void doit()
+{
+  if (!instr.len) {
+    if (target.len) parseerr();
+    return;
+  }
+
+  if (!target.len) parseerr();
+
+  if (stralloc_starts(&target,"owner-")) {
+    if (!stralloc_copys(&key,"?")) nomem();
+    if (!stralloc_catb(&key,target.s + 6,target.len - 6)) nomem();
+    case_lowerb(key.s,key.len);
+    if (cdbmss_add(&cdbmss,key.s,key.len,fulltarget.s,fulltarget.len) == -1) writeerr();
+  }
+
+  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()
+{
+  int fd;
+
+  umask(033);
+  readcontrols();
+
+  fd = open_read("/etc/aliases");
+  if (fd == -1) readerr();
+  substdio_fdbuf(&ssin,read,fd,inbuf,sizeof inbuf);
+
+  fd = open_trunc("/etc/aliases.tmp");
+  if (fd == -1) strerr_die2sys(111,FATAL,"unable to create /etc/aliases.tmp: ");
+  if (cdbmss_start(&cdbmss,fd) == -1) writeerr();
+
+  if (!stralloc_copys(&line,"")) nomem();
+
+  for (;;) {
+    if (getln(&ssin,&newline,&match,'\n') != 0) readerr();
+
+    if (match && (newline.s[0] == '\n')) continue;
+
+    if (match && ((newline.s[0] == ' ') || (newline.s[0] == '\t'))) {
+      if (!stralloc_cat(&line,&newline)) nomem();
+      continue;
+    }
+
+    if (line.len)
+      if (line.s[0] != '#') {
+        if (!stralloc_copys(&target,"")) nomem();
+        if (!stralloc_copys(&fulltarget,"")) nomem();
+        if (!stralloc_copys(&instr,"")) nomem();
+        parseline();
+       doit();
+      }
+
+    if (!match) break;
+    if (!stralloc_copy(&line,&newline)) nomem();
+  }
+
+  if (cdbmss_finish(&cdbmss) == -1) writeerr();
+  if (fsync(fd) == -1) writeerr();
+  if (close(fd) == -1) writeerr(); /* NFS stupidity */
+
+  if (rename("/etc/aliases.tmp","/etc/aliases.cdb") == -1)
+    strerr_die2sys(111,FATAL,"unable to move /etc/aliases.tmp to /etc/aliases.cdb: ");
+  
+  _exit(0);
+}
diff --git a/newinclude.1 b/newinclude.1
new file mode 100644 (file)
index 0000000..b3ddf6e
--- /dev/null
@@ -0,0 +1,88 @@
+.TH newinclude 1
+.SH NAME
+newinclude \- create a binary mailing list from an :include: file
+.SH SYNOPSIS
+.B newinclude
+.I list
+.SH DESCRIPTION
+.B newinclude
+reads a
+sendmail-style
+.B :include:
+file,
+.IR list ,
+and converts it into a binary format in
+.I list\fB.bin
+for use by
+.BR fastforward .
+
+.B newinclude
+first writes the mailing list to
+.IR list\fB.tmp ,
+and then moves it to
+.IR list\fB.bin .
+If there is any problem creating
+.IR list\fB.tmp ,
+.B newinclude
+leaves
+.I list\fB.bin
+alone.
+
+.B newinclude
+always creates
+.I list\fB.bin
+world-readable.
+
+.B COMPATIBILITY WARNING:
+sendmail
+reads
+.I list
+directly;
+.B fastforward
+needs
+.IR list\fB.bin .
+sendmail's strategy is a disaster if you save
+.I list
+to disk at the same moment that
+sendmail
+reads it;
+the list will be truncated at a random spot,
+perhaps in the middle of an address.
+Furthermore, if the system crashes while you are writing
+.IR list ,
+.I list
+could be filled with all sorts of garbage.
+.SH "LIST FORMAT"
+.I list
+may contain any number of lines;
+each line may contain any number of addresses
+or further
+.B :include:
+files.
+See
+.BR newaliases (1)
+for details on the address format.
+Any line in
+.I file
+beginning with # is ignored.
+
+.B COMPATIBILITY WARNING:
+.B newinclude
+does not support file or program deliveries in
+.B :include:
+files.
+You can use the secure delivery mechanisms described in
+.B dot-qmail(5)
+instead.
+
+.B COMPATIBILITY WARNING:
+Versions of
+sendmail
+before V8 did not allow comments in
+.B :include:
+files.
+.SH "SEE ALSO"
+fastforward(1),
+newaliases(1),
+setmaillist(1),
+dot-qmail(5)
diff --git a/newinclude.c b/newinclude.c
new file mode 100644 (file)
index 0000000..ee1e0db
--- /dev/null
@@ -0,0 +1,313 @@
+#include "substdio.h"
+#include "strerr.h"
+#include "stralloc.h"
+#include "getln.h"
+#include "open.h"
+#include "readwrite.h"
+#include "token822.h"
+#include "control.h"
+#include "auto_qmail.h"
+#include "env.h"
+
+#define FATAL "newinclude: fatal: "
+
+void nomem()
+{
+  strerr_die2x(111,FATAL,"out of memory");
+}
+void usage()
+{
+  strerr_die1x(100,"newinclude: usage: newinclude list");
+}
+
+
+char *fnlist;
+substdio sslist;
+char listbuf[1024];
+
+stralloc bin = {0};
+#define fnbin bin.s
+stralloc tmp = {0};
+#define fntmp tmp.s
+substdio sstmp;
+char tmpbuf[1024];
+
+void readerr()
+{
+  strerr_die4sys(111,FATAL,"unable to read ",fnlist,": ");
+}
+void writeerr()
+{
+  strerr_die4sys(111,FATAL,"unable to write to ",fntmp,": ");
+}
+
+void out(s,len)
+char *s;
+int len;
+{
+  if (substdio_put(&sstmp,s,len) == -1) writeerr();
+}
+
+void doincl(buf,len)
+char *buf;
+int len;
+{
+  if (!len)
+    strerr_die2x(111,FATAL,"empty :include: filenames not permitted");
+  if (byte_chr(buf,len,'\n') != len)
+    strerr_die2x(111,FATAL,"newlines not permitted in :include: filenames");
+  if (byte_chr(buf,len,'\0') != len)
+    strerr_die2x(111,FATAL,"NUL not permitted in :include: filenames");
+  if ((buf[0] != '.') && (buf[0] != '/'))
+    out("./",2);
+  out(buf,len);
+  out("",1);
+}
+
+void dorecip(buf,len)
+char *buf;
+int len;
+{
+  if (!len)
+    strerr_die2x(111,FATAL,"empty recipient addresses not permitted");
+  if (byte_chr(buf,len,'\n') != len)
+    strerr_die2x(111,FATAL,"newlines not permitted in recipient addresses");
+  if (byte_chr(buf,len,'\0') != len)
+    strerr_die2x(111,FATAL,"NUL not permitted in recipient addresses");
+  if (len > 800)
+    strerr_die2x(111,FATAL,"addresses must be under 800 bytes");
+  if ((buf[len - 1] == ' ') || (buf[len - 1] == '\t'))
+    strerr_die2x(111,FATAL,"spaces and tabs not permitted at ends of addresses");
+  out("&",1);
+  out(buf,len);
+  out("",1);
+}
+
+
+void die_control()
+{
+  strerr_die2sys(111,FATAL,"unable to read controls: ");
+}
+
+stralloc me = {0};
+stralloc defaulthost = {0};
+stralloc defaultdomain = {0};
+stralloc plusdomain = {0};
+
+void readcontrols()
+{
+  int r;
+  int fddir;
+  char *x;
+
+  fddir = open_read(".");
+  if (fddir == -1)
+    strerr_die2sys(111,FATAL,"unable to open current directory: ");
+
+  if (chdir(auto_qmail) == -1)
+    strerr_die4sys(111,FATAL,"unable to chdir to ",auto_qmail,": ");
+
+  r = control_readline(&me,"control/me");
+  if (r == -1) die_control();
+  if (!r) if (!stralloc_copys(&me,"me")) nomem();
+
+  r = control_readline(&defaultdomain,"control/defaultdomain");
+  if (r == -1) die_control();
+  if (!r) if (!stralloc_copy(&defaultdomain,&me)) nomem();
+  x = env_get("QMAILDEFAULTDOMAIN");
+  if (x) if (!stralloc_copys(&defaultdomain,x)) nomem();
+
+  r = control_readline(&defaulthost,"control/defaulthost");
+  if (r == -1) die_control();
+  if (!r) if (!stralloc_copy(&defaulthost,&me)) nomem();
+  x = env_get("QMAILDEFAULTHOST");
+  if (x) if (!stralloc_copys(&defaulthost,x)) nomem();
+
+  r = control_readline(&plusdomain,"control/plusdomain");
+  if (r == -1) die_control();
+  if (!r) if (!stralloc_copy(&plusdomain,&me)) nomem();
+  x = env_get("QMAILPLUSDOMAIN");
+  if (x) if (!stralloc_copys(&plusdomain,x)) nomem();
+
+  if (fchdir(fddir) == -1)
+    strerr_die2sys(111,FATAL,"unable to set current directory: ");
+}
+
+stralloc cbuf = {0};
+token822_alloc toks = {0};
+token822_alloc tokaddr = {0};
+stralloc address = {0};
+
+void gotincl()
+{
+  token822_reverse(&tokaddr);
+  if (token822_unquote(&address,&tokaddr) != 1) nomem();
+  tokaddr.len = 0;
+  doincl(address.s,address.len);
+}
+
+void gotaddr()
+{
+  int i;
+  int j;
+  int flaghasat;
+  token822_reverse(&tokaddr);
+  if (token822_unquote(&address,&tokaddr) != 1) nomem();
+  flaghasat = 0;
+  for (i = 0;i < tokaddr.len;++i)
+    if (tokaddr.t[i].type == TOKEN822_AT)
+      flaghasat = 1;
+  tokaddr.len = 0;
+
+  if (!address.len) return;
+
+  if (!flaghasat)
+    if (address.s[0] == '/') {
+      if (!stralloc_0(&address)) nomem();
+      strerr_die4x(111,FATAL,"file delivery ",address.s," not supported");
+    }
+  if (!flaghasat)
+    if (address.s[0] == '|') {
+      if (!stralloc_0(&address)) nomem();
+      strerr_die4x(111,FATAL,"program delivery ",address.s," not supported");
+    }
+
+  if (!flaghasat) {
+    if (!stralloc_cats(&address,"@")) nomem();
+    if (!stralloc_cat(&address,&defaulthost)) nomem();
+  }
+  if (address.s[address.len - 1] == '+') {
+    address.s[address.len - 1] = '.';
+    if (!stralloc_cat(&address,&plusdomain)) nomem();
+  }
+  j = 0;
+  for (i = 0;i < address.len;++i) if (address.s[i] == '@') j = i;
+  for (i = j;i < address.len;++i) if (address.s[i] == '.') break;
+  if (i == address.len) {
+    if (!stralloc_cats(&address,".")) nomem();
+    if (!stralloc_cat(&address,&defaultdomain)) nomem();
+  }
+
+  dorecip(address.s,address.len);
+}
+
+
+stralloc line = {0};
+int match;
+
+void parseerr()
+{
+  if (!stralloc_0(&line)) nomem();
+  strerr_die3x(111,FATAL,"unable to parse this line: ",line.s);
+}
+
+void parseline()
+{
+  int wordok;
+  struct token822 *t;
+  struct token822 *beginning;
+  switch(token822_parse(&toks,&line,&cbuf)) {
+    case -1: nomem();
+    case 0: parseerr();
+  }
+
+  beginning = toks.t;
+  t = toks.t + toks.len;
+  wordok = 1;
+  if (!token822_readyplus(&tokaddr,1)) nomem();
+  tokaddr.len = 0;
+  while (t > beginning)
+    switch((--t)->type) {
+      case TOKEN822_SEMI:
+        break; /*XXX*/
+      case TOKEN822_COLON:
+        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;
+                }
+        break; /*XXX*/
+      case TOKEN822_RIGHT:
+        if (tokaddr.len) gotaddr();
+        while ((t > beginning) && (t[-1].type != TOKEN822_LEFT))
+          if (!token822_append(&tokaddr,--t)) nomem();
+        gotaddr();
+        if (t <= beginning) parseerr();
+        --t;
+        while ((t > beginning) && ((t[-1].type == TOKEN822_COMMENT) || (t[-1].type == TOKEN822_ATOM) || (t[-1].type == TOKEN822_QUOTE) || (t[-1].type == TOKEN822_AT) || (t[-1].type == TOKEN822_DOT)))
+          --t;
+        wordok = 0;
+        continue;
+      case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL:
+        if (!wordok) if (tokaddr.len) gotaddr();
+        wordok = 0;
+        if (!token822_append(&tokaddr,t)) nomem();
+        continue;
+      case TOKEN822_COMMENT:
+        /* comment is lexically a space; shouldn't affect wordok */
+        break;
+      case TOKEN822_COMMA:
+        if (tokaddr.len) gotaddr();
+        wordok = 1;
+        break;
+      default:
+        wordok = 1;
+        if (!token822_append(&tokaddr,t)) nomem();
+        continue;
+    }
+  if (tokaddr.len) gotaddr();
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  int fd;
+
+  umask(033);
+  readcontrols();
+
+  fnlist = argv[1]; if (!fnlist) usage();
+
+  if (!stralloc_copys(&bin,fnlist)) nomem();
+  if (!stralloc_cats(&bin,".bin")) nomem();
+  if (!stralloc_0(&bin)) nomem();
+
+  if (!stralloc_copys(&tmp,fnlist)) nomem();
+  if (!stralloc_cats(&tmp,".tmp")) nomem();
+  if (!stralloc_0(&tmp)) nomem();
+
+  fd = open_read(fnlist);
+  if (fd == -1) readerr();
+  substdio_fdbuf(&sslist,read,fd,listbuf,sizeof listbuf);
+
+  fd = open_trunc(fntmp);
+  if (fd == -1) writeerr();
+  substdio_fdbuf(&sstmp,write,fd,tmpbuf,sizeof tmpbuf);
+
+  for (;;) {
+    if (getln(&sslist,&line,&match,'\n') == -1) readerr();
+    if (!line.len) break;
+    if (line.s[0] != '#') parseline();
+    if (!match) break;
+  }
+
+  if (substdio_flush(&sstmp) == -1) writeerr();
+  if (fsync(fd) == -1) writeerr();
+  if (close(fd) == -1) writeerr(); /* NFS stupidity */
+
+  if (rename(fntmp,fnbin) == -1)
+    strerr_die6sys(111,FATAL,"unable to move ",fntmp," to ",fnbin,": ");
+  
+  _exit(0);
+}
diff --git a/open.h b/open.h
new file mode 100644 (file)
index 0000000..c903113
--- /dev/null
+++ b/open.h
@@ -0,0 +1,10 @@
+#ifndef OPEN_H
+#define OPEN_H
+
+extern int open_read();
+extern int open_excl();
+extern int open_append();
+extern int open_trunc();
+extern int open_write();
+
+#endif
diff --git a/open_read.c b/open_read.c
new file mode 100644 (file)
index 0000000..f503e48
--- /dev/null
@@ -0,0 +1,6 @@
+#include <sys/types.h>
+#include <fcntl.h>
+#include "open.h"
+
+int open_read(fn) char *fn;
+{ return open(fn,O_RDONLY | O_NDELAY); }
diff --git a/open_trunc.c b/open_trunc.c
new file mode 100644 (file)
index 0000000..e275085
--- /dev/null
@@ -0,0 +1,6 @@
+#include <sys/types.h>
+#include <fcntl.h>
+#include "open.h"
+
+int open_trunc(fn) char *fn;
+{ return open(fn,O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT,0644); }
diff --git a/printforward.1 b/printforward.1
new file mode 100644 (file)
index 0000000..337b081
--- /dev/null
@@ -0,0 +1,16 @@
+.TH printforward 1
+.SH NAME
+printforward \- print the instructions in a forwarding database
+.SH SYNOPSIS
+.B printforward
+.SH DESCRIPTION
+.B printforward
+reads a forwarding database from its standard input
+and prints all the forwarding instructions
+in a format accepted by
+.BR setforward .
+.SH "SEE ALSO"
+fastforward(1),
+newaliases(1),
+printmaillist(1),
+setforward(1)
diff --git a/printforward.c b/printforward.c
new file mode 100644 (file)
index 0000000..f27cdd0
--- /dev/null
@@ -0,0 +1,145 @@
+#include "substdio.h"
+#include "subfd.h"
+#include "strerr.h"
+#include "stralloc.h"
+#include "cdb.h"
+
+#define FATAL "printmaillist: fatal: "
+
+void badformat()
+{
+  strerr_die2x(100,FATAL,"bad database format");
+}
+void nomem()
+{
+  strerr_die2x(111,FATAL,"out of memory");
+}
+
+void getch(ch)
+char *ch;
+{
+  int r;
+  r = substdio_get(subfdinsmall,ch,1);
+  if (r == -1)
+    strerr_die2sys(111,FATAL,"unable to read input: ");
+  if (r == 0)
+    badformat();
+}
+
+void putch(ch)
+char *ch;
+{
+  if (substdio_put(subfdoutsmall,ch,1) == -1)
+    strerr_die2x(111,FATAL,"unable to write output: ");
+}
+
+void print(buf)
+char *buf;
+{
+  while (*buf)
+    putch(buf++);
+}
+
+void printsafe(buf,len)
+char *buf;
+int len;
+{
+  char ch;
+
+  while (len) {
+    ch = *buf;
+    if ((ch <= 32) || (ch == ',') || (ch == ':') || (ch == ';') || (ch == '\\') || (ch == '#'))
+      putch("\\");
+    putch(&ch);
+    ++buf;
+    --len;
+  }
+}
+
+stralloc key = {0};
+stralloc data = {0};
+
+void main()
+{
+  uint32 eod;
+  uint32 pos;
+  uint32 klen;
+  uint32 dlen;
+  char buf[8];
+  char ch;
+  int i;
+  int j;
+
+  for (i = 0;i < 4;++i) getch(buf + i);
+  eod = cdb_unpack(buf);
+
+  for (i = 4;i < 2048;++i) getch(&ch);
+
+  pos = 2048;
+  while (pos < eod) {
+    if (eod - pos < 8) badformat();
+    pos += 8;
+    for (i = 0;i < 8;++i) getch(buf + i);
+    klen = cdb_unpack(buf);
+    dlen = cdb_unpack(buf + 4);
+
+    if (!stralloc_copys(&key,"")) nomem();
+    if (eod - pos < klen) badformat();
+    pos += klen;
+    while (klen) {
+      --klen;
+      getch(&ch);
+      if (!stralloc_append(&key,&ch)) nomem();
+    }
+
+    if (eod - pos < dlen) badformat();
+    pos += dlen;
+    if (!stralloc_copys(&data,"")) nomem();
+    while (dlen) {
+      --dlen;
+      getch(&ch);
+      if (!stralloc_append(&data,&ch)) nomem();
+    }
+
+    if (!key.len) badformat();
+    if (key.s[0] == '?') {
+      printsafe(key.s + 1,key.len - 1);
+      print(": ?");
+      printsafe(data.s,data.len);
+      print(";\n");
+    }
+    else if (key.s[0] == ':') {
+      printsafe(key.s + 1,key.len - 1);
+      print(":\n");
+
+      i = 0;
+      for (j = 0;j < data.len;++j)
+        if (!data.s[j]) {
+          if ((data.s[i] == '.') || (data.s[i] == '/')) {
+            print(", ");
+            printsafe(data.s + i,j - i);
+            print("\n");
+          }
+          else if ((data.s[i] == '|') || (data.s[i] == '!')) {
+            print(", ");
+            printsafe(data.s + i,j - i);
+            print("\n");
+          }
+          else if ((data.s[i] == '&') && (j - i < 900)) {
+            print(", ");
+            printsafe(data.s + i,j - i);
+            print("\n");
+          }
+          else badformat();
+          i = j + 1;
+        }
+      if (i != j) badformat();
+      print(";\n");
+    }
+    else badformat();
+  }
+
+  if (substdio_flush(subfdoutsmall) == -1)
+    strerr_die2sys(111,FATAL,"unable to write output: ");
+  _exit(0);
+}
diff --git a/printmaillist.1 b/printmaillist.1
new file mode 100644 (file)
index 0000000..204b342
--- /dev/null
@@ -0,0 +1,15 @@
+.TH printmaillist 1
+.SH NAME
+printmaillist \- print the contents of a binary mailing list
+.SH SYNOPSIS
+.B printmaillist
+.SH DESCRIPTION
+.B printmaillist
+reads a binary mailing list from its standard input
+and prints all the forwarding instructions
+in a format accepted by
+.BR setmaillist .
+.SH "SEE ALSO"
+newinclude(1),
+printforward(1),
+setmaillist(1)
diff --git a/printmaillist.c b/printmaillist.c
new file mode 100644 (file)
index 0000000..fa3b5a3
--- /dev/null
@@ -0,0 +1,52 @@
+#include "substdio.h"
+#include "subfd.h"
+#include "strerr.h"
+#include "stralloc.h"
+#include "getln.h"
+
+#define FATAL "printmaillist: fatal: "
+
+void badformat()
+{
+  strerr_die2x(100,FATAL,"bad mailing list format");
+}
+
+stralloc line = {0};
+int match;
+
+void main()
+{
+  for (;;) {
+    if (getln(subfdinsmall,&line,&match,'\0') == -1)
+      strerr_die2sys(111,FATAL,"unable to read input: ");
+    if (!match) {
+      if (line.len)
+        badformat();
+      if (substdio_flush(subfdoutsmall) == -1)
+        strerr_die2sys(111,FATAL,"unable to write output: ");
+      _exit(0);
+    }
+
+    if (line.s[str_chr(line.s,'\n')]) badformat();
+    if (line.s[line.len - 1] == ' ') badformat();
+    if (line.s[line.len - 1] == '\t') badformat();
+
+    if ((line.s[0] == '.') || (line.s[0] == '/')) {
+      if (substdio_puts(subfdoutsmall,line.s) == -1)
+        strerr_die2sys(111,FATAL,"unable to write output: ");
+      if (substdio_puts(subfdoutsmall,"\n") == -1)
+        strerr_die2sys(111,FATAL,"unable to write output: ");
+      continue;
+    }
+    if (line.s[0] == '&') {
+      if (line.len > 900) badformat();
+      if (substdio_puts(subfdoutsmall,line.s) == -1)
+        strerr_die2sys(111,FATAL,"unable to write output: ");
+      if (substdio_puts(subfdoutsmall,"\n") == -1)
+        strerr_die2sys(111,FATAL,"unable to write output: ");
+      continue;
+    }
+
+    badformat();
+  }
+}
diff --git a/qmail.c b/qmail.c
new file mode 100644 (file)
index 0000000..0fe0dfa
--- /dev/null
+++ b/qmail.c
@@ -0,0 +1,125 @@
+#include "substdio.h"
+#include "readwrite.h"
+#include "wait.h"
+#include "exit.h"
+#include "fork.h"
+#include "fd.h"
+#include "qmail.h"
+#include "auto_qmail.h"
+
+static char *binqqargs[2] = { "bin/qmail-queue", 0 } ;
+
+int qmail_open(qq)
+struct qmail *qq;
+{
+  int pim[2];
+  int pie[2];
+
+  if (pipe(pim) == -1) return -1;
+  if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; }
+  switch(qq->pid = vfork()) {
+    case -1:
+      close(pim[0]); close(pim[1]);
+      close(pie[0]); close(pie[1]);
+      return -1;
+    case 0:
+      close(pim[1]);
+      close(pie[1]);
+      if (fd_move(0,pim[0]) == -1) _exit(120);
+      if (fd_move(1,pie[0]) == -1) _exit(120);
+      if (chdir(auto_qmail) == -1) _exit(61);
+      execv(*binqqargs,binqqargs);
+      _exit(120);
+  }
+
+  qq->fdm = pim[1]; close(pim[0]);
+  qq->fde = pie[1]; close(pie[0]);
+  substdio_fdbuf(&qq->ss,write,qq->fdm,qq->buf,sizeof(qq->buf));
+  qq->flagerr = 0;
+  return 0;
+}
+
+unsigned long qmail_qp(qq) struct qmail *qq;
+{
+  return qq->pid;
+}
+
+void qmail_fail(qq) struct qmail *qq;
+{
+  qq->flagerr = 1;
+}
+
+void qmail_put(qq,s,len) struct qmail *qq; char *s; int len;
+{
+  if (!qq->flagerr) if (substdio_put(&qq->ss,s,len) == -1) qq->flagerr = 1;
+}
+
+void qmail_puts(qq,s) struct qmail *qq; char *s;
+{
+  if (!qq->flagerr) if (substdio_puts(&qq->ss,s) == -1) qq->flagerr = 1;
+}
+
+void qmail_from(qq,s) struct qmail *qq; char *s;
+{
+  if (substdio_flush(&qq->ss) == -1) qq->flagerr = 1;
+  close(qq->fdm);
+  substdio_fdbuf(&qq->ss,write,qq->fde,qq->buf,sizeof(qq->buf));
+  qmail_put(qq,"F",1);
+  qmail_puts(qq,s);
+  qmail_put(qq,"",1);
+}
+
+void qmail_to(qq,s) struct qmail *qq; char *s;
+{
+  qmail_put(qq,"T",1);
+  qmail_puts(qq,s);
+  qmail_put(qq,"",1);
+}
+
+char *qmail_close(qq)
+struct qmail *qq;
+{
+  int wstat;
+  int exitcode;
+
+  qmail_put(qq,"",1);
+  if (!qq->flagerr) if (substdio_flush(&qq->ss) == -1) qq->flagerr = 1;
+  close(qq->fde);
+
+  if (wait_pid(&wstat,qq->pid) != qq->pid)
+    return "Zqq waitpid surprise (#4.3.0)";
+  if (wait_crashed(wstat))
+    return "Zqq crashed (#4.3.0)";
+  exitcode = wait_exitcode(wstat);
+
+  switch(exitcode) {
+    case 115: /* compatibility */
+    case 11: return "Denvelope address too long for qq (#5.1.3)";
+    case 31: return "Dmail server permanently rejected message (#5.3.0)";
+    case 51: return "Zqq out of memory (#4.3.0)";
+    case 52: return "Zqq timeout (#4.3.0)";
+    case 53: return "Zqq write error or disk full (#4.3.0)";
+    case 0: if (!qq->flagerr) return ""; /* fall through */
+    case 54: return "Zqq read error (#4.3.0)";
+    case 55: return "Zqq unable to read configuration (#4.3.0)";
+    case 56: return "Zqq trouble making network connection (#4.3.0)";
+    case 61: return "Zqq trouble in home directory (#4.3.0)";
+    case 63:
+    case 64:
+    case 65:
+    case 66:
+    case 62: return "Zqq trouble creating files in queue (#4.3.0)";
+    case 71: return "Zmail server temporarily rejected message (#4.3.0)";
+    case 72: return "Zconnection to mail server timed out (#4.4.1)";
+    case 73: return "Zconnection to mail server rejected (#4.4.1)";
+    case 74: return "Zcommunication with mail server failed (#4.4.2)";
+    case 91: /* fall through */
+    case 81: return "Zqq internal bug (#4.3.0)";
+    case 120: return "Zunable to exec qq (#4.3.0)";
+    default:
+      if ((exitcode >= 11) && (exitcode <= 40))
+       return "Dqq permanent problem (#5.3.0)";
+      return "Zqq temporary problem (#4.3.0)";
+  }
+}
diff --git a/qmail.h b/qmail.h
new file mode 100644 (file)
index 0000000..7fa13e2
--- /dev/null
+++ b/qmail.h
@@ -0,0 +1,24 @@
+#ifndef QMAIL_H
+#define QMAIL_H
+
+#include "substdio.h"
+
+struct qmail {
+  int flagerr;
+  unsigned long pid;
+  int fdm;
+  int fde;
+  substdio ss;
+  char buf[1024];
+} ;
+
+extern int qmail_open();
+extern void qmail_put();
+extern void qmail_puts();
+extern void qmail_from();
+extern void qmail_to();
+extern void qmail_fail();
+extern char *qmail_close();
+extern unsigned long qmail_qp();
+
+#endif
diff --git a/readwrite.h b/readwrite.h
new file mode 100644 (file)
index 0000000..2a64968
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef READWRITE_H
+#define READWRITE_H
+
+extern int read();
+extern int write();
+
+#endif
diff --git a/scan.h b/scan.h
new file mode 100644 (file)
index 0000000..53ce703
--- /dev/null
+++ b/scan.h
@@ -0,0 +1,27 @@
+#ifndef SCAN_H
+#define SCAN_H
+
+extern unsigned int scan_uint();
+extern unsigned int scan_xint();
+extern unsigned int scan_nbbint();
+extern unsigned int scan_ushort();
+extern unsigned int scan_xshort();
+extern unsigned int scan_nbbshort();
+extern unsigned int scan_ulong();
+extern unsigned int scan_xlong();
+extern unsigned int scan_nbblong();
+
+extern unsigned int scan_plusminus();
+extern unsigned int scan_0x();
+
+extern unsigned int scan_whitenskip();
+extern unsigned int scan_nonwhitenskip();
+extern unsigned int scan_charsetnskip();
+extern unsigned int scan_noncharsetnskip();
+
+extern unsigned int scan_strncmp();
+extern unsigned int scan_memcmp();
+
+extern unsigned int scan_long();
+
+#endif
diff --git a/scan_ulong.c b/scan_ulong.c
new file mode 100644 (file)
index 0000000..27c41ea
--- /dev/null
@@ -0,0 +1,11 @@
+#include "scan.h"
+
+unsigned int scan_ulong(s,u) register char *s; register unsigned long *u;
+{
+  register unsigned int pos; register unsigned long result;
+  register unsigned long c;
+  pos = 0; result = 0;
+  while ((c = (unsigned long) (unsigned char) (s[pos] - '0')) < 10)
+    { result = result * 10 + c; ++pos; }
+  *u = result; return pos;
+}
diff --git a/seek.h b/seek.h
new file mode 100644 (file)
index 0000000..964fba3
--- /dev/null
+++ b/seek.h
@@ -0,0 +1,15 @@
+#ifndef SEEK_H
+#define SEEK_H
+
+typedef unsigned long seek_pos;
+
+extern seek_pos seek_cur();
+
+extern int seek_set();
+extern int seek_end();
+
+extern int seek_trunc();
+
+#define seek_begin(fd) (seek_set((fd),(seek_pos) 0))
+
+#endif
diff --git a/seek_set.c b/seek_set.c
new file mode 100644 (file)
index 0000000..f540664
--- /dev/null
@@ -0,0 +1,7 @@
+#include <sys/types.h>
+#include "seek.h"
+
+#define SET 0 /* sigh */
+
+int seek_set(fd,pos) int fd; seek_pos pos;
+{ if (lseek(fd,(off_t) pos,SET) == -1) return -1; return 0; }
diff --git a/setforward.1 b/setforward.1
new file mode 100644 (file)
index 0000000..73cb8b5
--- /dev/null
@@ -0,0 +1,204 @@
+.TH setforward 1
+.SH NAME
+setforward \- create a forwarding database
+.SH SYNOPSIS
+.B setforward
+.I cdb
+.I tmp
+.SH DESCRIPTION
+.B setforward
+reads a table of forwarding instructions from its standard input.
+It converts the table into a forwarding database.
+The forwarding database can be used by
+.BR fastforward .
+
+.B setforward
+writes the forwarding database to
+.IR tmp ;
+it then moves
+.I tmp
+to
+.IR cdb .
+.I tmp
+and
+.I cdb
+must be on the same filesystem.
+
+If there is a problem creating
+.IR tmp ,
+.B setforward
+complains and leaves
+.I cdb
+alone.
+
+The forwarding database format is portable across machines.
+.SH "INSTRUCTION FORMAT"
+A forwarding instruction contains a
+.I target\fR,
+a colon, a series of commands, and a semicolon.
+Each command is a
+.I recipient address\fR,
+.I owner address\fR,
+.I external mailing list\fR,
+or
+.I program\fR.
+Commands are separated by commas.
+
+For example,
+
+.EX
+   root@yp.to: god@heaven.af.mil, staff@af.mil;
+.EE
+
+says that mail for
+.B root@yp.to
+should be forwarded to the recipient addresses
+.B god@heaven.af.mil
+and
+.BR staff@af.mil .
+
+When
+.B setforward
+sees # it ignores all text from # to the end of the line:
+
+.EX
+   # this is a comment
+.EE
+
+.B setforward
+ignores all other line endings,
+so you can split a forwarding instruction across lines.
+It also ignores spaces and tabs.
+Exception:
+you can put a space (or tab or comma or whatever)
+into a target or command by putting a backslash in front of it.
+(However, NUL bytes are not permitted anywhere.)
+.SH "TARGETS"
+When
+.B fastforward
+sees the incoming address
+.IR user@host.dom ,
+it tries three targets:
+.IR user@host.dom ,
+.IR @host.dom ,
+and
+.IR user@ .
+It obeys the commands for the first target that it finds.
+Target names are interpreted without regard to case.
+
+All the commands for a single target must be listed in a single instruction.
+Exception: an owner address can be listed in a separate instruction.
+.SH "RECIPIENT ADDRESSES"
+If a command begins with an ampersand,
+.B setforward
+takes the remaining bytes in the command as a recipient address:
+
+.EX
+   boss@yp.to: &god@heaven.af.mil;
+.EE
+
+.B fastforward
+sends each incoming mail message
+to the recipient address.
+The recipient address must include a fully qualified domain name.
+It cannot be longer than 800 bytes.
+
+If a recipient address is itself a target in the forwarding table,
+.B fastforward
+will recursively handle the instructions for that target.
+Note that
+.I @host.dom
+and
+.I user@
+wildcards do not apply here;
+they apply only to the incoming address.
+
+If a command begins with a letter or number,
+.B setforward
+takes the entire command as a recipient address:
+
+.EX
+   boss@yp.to: god@heaven.af.mil;
+.EE
+.SH "OWNER ADDRESSES"
+If a command begins with a question mark,
+.B setforward
+takes the remaining bytes in the command as an owner address:
+
+.EX
+   sos@heaven.af.mil: ?owner-sos@heaven.af.mil;
+.EE
+
+.B fastforward
+uses that address as the envelope sender for forwarded mail,
+so bounces will go back to that address.
+(Normally, if a message is forwarded to a bad address,
+it will bounce back to the original envelope sender.)
+.SH "EXTERNAL MAILING LISTS"
+If a command begins with a dot or slash,
+.B setforward
+takes the entire command as the name of a binary mailing list file created by
+.BR setmaillist :
+
+.EX
+   sos@heaven.af.mil: /etc/lists/sos.bin;
+.EE
+
+.B fastforward
+will read and obey the commands in that file.
+The file must be world-readable
+and accessible to
+.BR fastforward .
+.SH "PROGRAMS"
+If a command begins with a vertical bar or exclamation point,
+.B setforward
+takes the rest of the command as the name of a program to run:
+
+.EX
+   dew@: |dew-monitor;
+.EE
+
+For a vertical bar,
+.B fastforward
+feeds the message
+to that program.
+An exclamation point works the same way except that
+.B fastforward
+inserts
+.BR $UFLINE ,
+.BR $RPLINE ,
+and
+.B $DTLINE
+in front of the message.
+.SH "DUPLICATES"
+When
+.B fastforward
+is building the recipient list for a message,
+it keeps track of the recipient addresses and external mailing lists
+it has used.
+If the same command shows up again, it skips it.
+For example:
+
+.EX
+   everybody@yp.to: programmers@yp.to, testers@yp.to;
+   programmers@yp.to: joe@yp.to, bob@yp.to;
+   testers@yp.to: joe@yp.to, fred@yp.to;
+.EE
+
+A message to
+.B everybody@yp.to
+will be sent to
+.B joe@yp.to
+only once.
+(This also means that addresses in an internal forwarding loop
+are discarded.)
+
+Exception:
+If a target has an owner address,
+commands for that target are considered different
+from commands for ``outside'' targets.
+.SH "SEE ALSO"
+newaliases(1),
+preline(1),
+printforward(1),
+setmaillist(1)
diff --git a/setforward.c b/setforward.c
new file mode 100644 (file)
index 0000000..42b72ae
--- /dev/null
@@ -0,0 +1,174 @@
+#include "substdio.h"
+#include "subfd.h"
+#include "strerr.h"
+#include "stralloc.h"
+#include "open.h"
+#include "case.h"
+#include "readwrite.h"
+#include "cdbmss.h"
+
+#define FATAL "setforward: fatal: "
+
+void usage()
+{
+  strerr_die1x(100,"setforward: usage: setforward data.cdb data.tmp");
+}
+void nomem()
+{
+  strerr_die2x(111,FATAL,"out of memory");
+}
+void missingsemicolon()
+{
+  strerr_die2x(100,FATAL,"final instruction must end with semicolon");
+}
+void extracolon()
+{
+  strerr_die2x(100,FATAL,"double colons are not permitted");
+}
+void extracomma()
+{
+  strerr_die2x(100,FATAL,"commas are not permitted before colons");
+}
+void nulbyte()
+{
+  strerr_die2x(100,FATAL,"NUL bytes are not permitted");
+}
+void longaddress()
+{
+  strerr_die2x(100,FATAL,"addresses over 800 bytes are not permitted");
+}
+
+char *fncdb;
+char *fntmp;
+int fd;
+struct cdbmss cdbmss;
+stralloc key = {0};
+
+stralloc target = {0}; /* always initialized; no NUL */
+stralloc command = {0}; /* always initialized; no NUL */
+stralloc instr = {0}; /* always initialized */
+
+int flagtarget = 0;
+/* 0: reading target; command is empty; instr is empty */
+/* 1: target is complete; instr still has to be written; reading command */
+
+void writeerr()
+{
+  strerr_die4sys(111,FATAL,"unable to write to ",fntmp,": ");
+}
+
+void doit(prepend,data,datalen)
+char *prepend;
+char *data;
+int datalen;
+{
+  if (!stralloc_copys(&key,prepend)) nomem();
+  if (!stralloc_cat(&key,&target)) nomem();
+  case_lowerb(key.s,key.len);
+  if (cdbmss_add(&cdbmss,key.s,key.len,data,datalen) == -1)
+    writeerr();
+}
+
+int getch(ch)
+char *ch;
+{
+  int r;
+
+  r = substdio_get(subfdinsmall,ch,1);
+  if (r == -1)
+    strerr_die2sys(111,FATAL,"unable to read input: ");
+  return r;
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  char ch;
+  int r;
+
+  if (!stralloc_copys(&target,"")) nomem();
+  if (!stralloc_copys(&command,"")) nomem();
+  if (!stralloc_copys(&instr,"")) nomem();
+
+  fncdb = argv[1]; if (!fncdb) usage();
+  fntmp = argv[2]; if (!fntmp) usage();
+
+  fd = open_trunc(fntmp);
+  if (fd == -1)
+    strerr_die4sys(111,FATAL,"unable to create ",fntmp,": ");
+
+  if (cdbmss_start(&cdbmss,fd) == -1) writeerr();
+
+  for (;;) {
+    if (!getch(&ch)) goto eof;
+
+    if (ch == '#') {
+      while (ch != '\n') if (!getch(&ch)) goto eof;
+      continue;
+    }
+
+    if (ch == ' ') continue;
+    if (ch == '\n') continue;
+    if (ch == '\t') continue;
+
+    if (ch == ':') {
+      if (flagtarget) extracolon();
+      flagtarget = 1;
+      continue;
+    }
+
+    if ((ch == ',') || (ch == ';')) {
+      if (!flagtarget) extracomma();
+      if (command.len) {
+        if (command.s[0] == '?') {
+          doit("?",command.s + 1,command.len - 1);
+        }
+        else if ((command.s[0] == '|') || (command.s[0] == '!')) {
+          if (!stralloc_cat(&instr,&command)) nomem();
+          if (!stralloc_0(&instr)) nomem();
+        }
+        else if ((command.s[0] == '.') || (command.s[0] == '/')) {
+          if (!stralloc_cat(&instr,&command)) nomem();
+          if (!stralloc_0(&instr)) nomem();
+        }
+        else {
+          if (command.len > 800) longaddress();
+          if (command.s[0] != '&')
+            if (!stralloc_cats(&instr,"&")) nomem();
+          if (!stralloc_cat(&instr,&command)) nomem();
+          if (!stralloc_0(&instr)) nomem();
+        }
+      }
+
+      if (!stralloc_copys(&command,"")) nomem();
+
+      if (ch == ';') {
+       if (instr.len)
+          doit(":",instr.s,instr.len);
+
+        if (!stralloc_copys(&target,"")) nomem();
+        if (!stralloc_copys(&instr,"")) nomem();
+        flagtarget = 0;
+      }
+      continue;
+    }
+
+    if (ch == '\\') if (!getch(&ch)) goto eof;
+    if (ch == 0) nulbyte();
+    if (!stralloc_append(flagtarget ? &command : &target,&ch)) nomem();
+  }
+
+  eof:
+  if (flagtarget || target.len)
+    missingsemicolon();
+
+  if (cdbmss_finish(&cdbmss) == -1) writeerr();
+  if (fsync(fd) == -1) writeerr();
+  if (close(fd) == -1) writeerr(); /* NFS stupidity */
+
+  if (rename(fntmp,fncdb) == -1)
+    strerr_die6sys(111,FATAL,"unable to move ",fntmp," to ",fncdb,": ");
+  
+  _exit(0);
+}
diff --git a/setmaillist.1 b/setmaillist.1
new file mode 100644 (file)
index 0000000..091a48a
--- /dev/null
@@ -0,0 +1,72 @@
+.TH setmaillist 1
+.SH NAME
+setmaillist \- create a binary mailing list
+.SH SYNOPSIS
+.B setmaillist
+.I bin
+.I tmp
+.SH DESCRIPTION
+.B setmaillist
+reads a mailing list from its standard input.
+
+.B setmaillist
+writes the mailing list in a binary format to
+.IR tmp ;
+it then moves
+.I tmp
+to
+.IR bin .
+.I tmp
+and
+.I bin
+must be on the same filesystem.
+
+If there is a problem creating
+.IR tmp ,
+.B setmaillist
+complains and leaves
+.I bin
+alone.
+
+The binary mailing list format is portable across machines.
+
+.B setmaillist
+always creates
+.I bin
+world-readable.
+.SH "MAILING LIST FORMAT"
+The mailing list read by
+.B setmaillist
+is a series of lines.
+NUL bytes are not allowed.
+
+If a line begins with a dot or slash,
+.B setmaillist
+takes the entire line as an include file name.
+
+If a line begins with an ampersand,
+.B setmaillist
+takes the rest of the line as a recipient address.
+If a line begins with a letter or number,
+.B setmaillist
+takes the entire line as a recipient address.
+Each recipient address must include a fully qualified domain name.
+Recipient addresses longer than 800 bytes are not allowed.
+
+.B setmaillist
+ignores blank lines
+and lines beginning with #.
+It also ignores spaces and tabs at the ends of lines.
+
+For example,
+
+.EX
+   god@heaven.af.mil
+   djb@silverton.berkeley.edu
+.EE
+
+is a mailing list with two addresses.
+.SH "SEE ALSO"
+setforward(1),
+newinclude(1),
+printmaillist(1)
diff --git a/setmaillist.c b/setmaillist.c
new file mode 100644 (file)
index 0000000..44f0240
--- /dev/null
@@ -0,0 +1,94 @@
+#include "substdio.h"
+#include "subfd.h"
+#include "strerr.h"
+#include "stralloc.h"
+#include "getln.h"
+#include "open.h"
+#include "readwrite.h"
+
+#define FATAL "setmaillist: fatal: "
+
+void usage()
+{
+  strerr_die1x(100,"setmaillist: usage: setmaillist list.bin list.tmp");
+}
+
+stralloc line = {0};
+int match;
+
+char *fnbin;
+char *fntmp;
+int fd;
+substdio ss;
+char buf[1024];
+
+void writeerr()
+{
+  strerr_die4sys(111,FATAL,"unable to write to ",fntmp,": ");
+}
+
+void out(s,len)
+char *s;
+int len;
+{
+  if (substdio_put(&ss,s,len) == -1) writeerr();
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  umask(033);
+
+  fnbin = argv[1]; if (!fnbin) usage();
+  fntmp = argv[2]; if (!fntmp) usage();
+
+  fd = open_trunc(fntmp);
+  if (fd == -1)
+    strerr_die4sys(111,FATAL,"unable to create ",fntmp,": ");
+
+  substdio_fdbuf(&ss,write,fd,buf,sizeof buf);
+
+  do {
+    if (getln(subfdinsmall,&line,&match,'\n') == -1)
+      strerr_die2sys(111,FATAL,"unable to read input: ");
+
+    while (line.len) {
+      if (line.s[line.len - 1] != '\n')
+        if (line.s[line.len - 1] != ' ')
+          if (line.s[line.len - 1] != '\t')
+            break;
+      --line.len;
+    }
+
+    if (byte_chr(line.s,line.len,'\0') != line.len)
+      strerr_die2x(111,FATAL,"NUL in input");
+
+    if (line.len)
+      if (line.s[0] != '#') {
+        if ((line.s[0] == '.') || (line.s[0] == '/')) {
+          out(line.s,line.len);
+          out("",1);
+        }
+        else {
+          if (line.len > 800)
+            strerr_die2x(111,FATAL,"addresses must be under 800 bytes");
+          if (line.s[0] != '&')
+            out("&",1);
+          out(line.s,line.len);
+          out("",1);
+        }
+      }
+
+  }
+  while (match);
+
+  if (substdio_flush(&ss) == -1) writeerr();
+  if (fsync(fd) == -1) writeerr();
+  if (close(fd) == -1) writeerr(); /* NFS stupidity */
+
+  if (rename(fntmp,fnbin) == -1)
+    strerr_die6sys(111,FATAL,"unable to move ",fntmp," to ",fnbin,": ");
+  
+  _exit(0);
+}
diff --git a/sgetopt.c b/sgetopt.c
new file mode 100644 (file)
index 0000000..a8bffc0
--- /dev/null
+++ b/sgetopt.c
@@ -0,0 +1,54 @@
+/* sgetopt.c, sgetopt.h: (yet another) improved getopt clone, outer layer
+D. J. Bernstein, djb@pobox.com.
+Depends on subgetopt.h, substdio.h, subfd.h.
+No system requirements.
+19970208: Cleanups.
+931201: Baseline.
+No known patent problems.
+
+Documentation in sgetopt.3.
+*/
+
+#include "substdio.h"
+#include "subfd.h"
+#define SGETOPTNOSHORT
+#include "sgetopt.h"
+#define SUBGETOPTNOSHORT
+#include "subgetopt.h"
+
+#define getopt sgetoptmine
+#define optind subgetoptind
+#define opterr sgetopterr
+#define optproblem subgetoptproblem
+#define optprogname sgetoptprogname
+
+int opterr = 1;
+char *optprogname = 0;
+
+int getopt(argc,argv,opts)
+int argc;
+char **argv;
+char *opts;
+{
+  int c;
+  char *s;
+
+  if (!optprogname) {
+    optprogname = *argv;
+    if (!optprogname) optprogname = "";
+    for (s = optprogname;*s;++s) if (*s == '/') optprogname = s + 1;
+  }
+  c = subgetopt(argc,argv,opts);
+  if (opterr)
+    if (c == '?') {
+      char chp[2]; chp[0] = optproblem; chp[1] = '\n';
+      substdio_puts(subfderr,optprogname);
+      if (argv[optind] && (optind < argc))
+        substdio_puts(subfderr,": illegal option -- ");
+      else
+        substdio_puts(subfderr,": option requires an argument -- ");
+      substdio_put(subfderr,chp,2);
+      substdio_flush(subfderr);
+    }
+  return c;
+}
diff --git a/sgetopt.h b/sgetopt.h
new file mode 100644 (file)
index 0000000..5f89127
--- /dev/null
+++ b/sgetopt.h
@@ -0,0 +1,21 @@
+#ifndef SGETOPT_H
+#define SGETOPT_H
+
+#ifndef SGETOPTNOSHORT
+#define getopt sgetoptmine
+#define optarg subgetoptarg
+#define optind subgetoptind
+#define optpos subgetoptpos
+#define opterr sgetopterr
+#define optproblem subgetoptproblem
+#define optprogname sgetoptprogname
+#define opteof subgetoptdone
+#endif
+
+#include "subgetopt.h"
+
+extern int sgetoptmine();
+extern int sgetopterr;
+extern char *sgetoptprogname;
+
+#endif
diff --git a/sig.h b/sig.h
new file mode 100644 (file)
index 0000000..9c3a28c
--- /dev/null
+++ b/sig.h
@@ -0,0 +1,43 @@
+#ifndef SIG_H
+#define SIG_H
+
+extern void sig_catch();
+extern void sig_block();
+extern void sig_unblock();
+extern void sig_blocknone();
+extern void sig_pause();
+
+extern void sig_dfl();
+
+extern void sig_miscignore();
+extern void sig_bugcatch();
+
+extern void sig_pipeignore();
+extern void sig_pipedefault();
+
+extern void sig_contblock();
+extern void sig_contunblock();
+extern void sig_contcatch();
+extern void sig_contdefault();
+
+extern void sig_termblock();
+extern void sig_termunblock();
+extern void sig_termcatch();
+extern void sig_termdefault();
+
+extern void sig_alarmblock();
+extern void sig_alarmunblock();
+extern void sig_alarmcatch();
+extern void sig_alarmdefault();
+
+extern void sig_childblock();
+extern void sig_childunblock();
+extern void sig_childcatch();
+extern void sig_childdefault();
+
+extern void sig_hangupblock();
+extern void sig_hangupunblock();
+extern void sig_hangupcatch();
+extern void sig_hangupdefault();
+
+#endif
diff --git a/sig_catch.c b/sig_catch.c
new file mode 100644 (file)
index 0000000..1888765
--- /dev/null
@@ -0,0 +1,18 @@
+#include <signal.h>
+#include "sig.h"
+#include "hassgact.h"
+
+void sig_catch(sig,f)
+int sig;
+void (*f)();
+{
+#ifdef HASSIGACTION
+  struct sigaction sa;
+  sa.sa_handler = f;
+  sa.sa_flags = 0;
+  sigemptyset(&sa.sa_mask);
+  sigaction(sig,&sa,(struct sigaction *) 0);
+#else
+  signal(sig,f); /* won't work under System V, even nowadays---dorks */
+#endif
+}
diff --git a/sig_pipe.c b/sig_pipe.c
new file mode 100644 (file)
index 0000000..594ae7d
--- /dev/null
@@ -0,0 +1,5 @@
+#include <signal.h>
+#include "sig.h"
+
+void sig_pipeignore() { sig_catch(SIGPIPE,SIG_IGN); }
+void sig_pipedefault() { sig_catch(SIGPIPE,SIG_DFL); }
diff --git a/slurpclose.c b/slurpclose.c
new file mode 100644 (file)
index 0000000..2fcef15
--- /dev/null
@@ -0,0 +1,19 @@
+#include "stralloc.h"
+#include "readwrite.h"
+#include "slurpclose.h"
+#include "error.h"
+
+int slurpclose(fd,sa,bufsize)
+int fd;
+stralloc *sa;
+int bufsize;
+{
+  int r;
+  for (;;) {
+    if (!stralloc_readyplus(sa,bufsize)) { close(fd); return -1; }
+    r = read(fd,sa->s + sa->len,bufsize);
+    if (r == -1) if (errno == error_intr) continue;
+    if (r <= 0) { close(fd); return r; }
+    sa->len += r;
+  }
+}
diff --git a/slurpclose.h b/slurpclose.h
new file mode 100644 (file)
index 0000000..57e9eec
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef SLURPCLOSE_H
+#define SLURPCLOSE_H
+
+extern int slurpclose();
+
+#endif
diff --git a/str.h b/str.h
new file mode 100644 (file)
index 0000000..e00773c
--- /dev/null
+++ b/str.h
@@ -0,0 +1,14 @@
+#ifndef STR_H
+#define STR_H
+
+extern unsigned int str_copy();
+extern int str_diff();
+extern int str_diffn();
+extern unsigned int str_len();
+extern unsigned int str_chr();
+extern unsigned int str_rchr();
+extern int str_start();
+
+#define str_equal(s,t) (!str_diff((s),(t)))
+
+#endif
diff --git a/str_chr.c b/str_chr.c
new file mode 100644 (file)
index 0000000..3691826
--- /dev/null
+++ b/str_chr.c
@@ -0,0 +1,19 @@
+#include "str.h"
+
+unsigned int str_chr(s,c)
+register char *s;
+int c;
+{
+  register char ch;
+  register char *t;
+
+  ch = c;
+  t = s;
+  for (;;) {
+    if (!*t) break; if (*t == ch) break; ++t;
+    if (!*t) break; if (*t == ch) break; ++t;
+    if (!*t) break; if (*t == ch) break; ++t;
+    if (!*t) break; if (*t == ch) break; ++t;
+  }
+  return t - s;
+}
diff --git a/str_cpy.c b/str_cpy.c
new file mode 100644 (file)
index 0000000..453d790
--- /dev/null
+++ b/str_cpy.c
@@ -0,0 +1,16 @@
+#include "str.h"
+
+unsigned int str_copy(s,t)
+register char *s;
+register char *t;
+{
+  register int len;
+
+  len = 0;
+  for (;;) {
+    if (!(*s = *t)) return len; ++s; ++t; ++len;
+    if (!(*s = *t)) return len; ++s; ++t; ++len;
+    if (!(*s = *t)) return len; ++s; ++t; ++len;
+    if (!(*s = *t)) return len; ++s; ++t; ++len;
+  }
+}
diff --git a/str_diff.c b/str_diff.c
new file mode 100644 (file)
index 0000000..18f8927
--- /dev/null
@@ -0,0 +1,17 @@
+#include "str.h"
+
+int str_diff(s,t)
+register char *s;
+register char *t;
+{
+  register char x;
+
+  for (;;) {
+    x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+    x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+    x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+    x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+  }
+  return ((int)(unsigned int)(unsigned char) x)
+       - ((int)(unsigned int)(unsigned char) *t);
+}
diff --git a/str_diffn.c b/str_diffn.c
new file mode 100644 (file)
index 0000000..89142f1
--- /dev/null
@@ -0,0 +1,18 @@
+#include "str.h"
+
+int str_diffn(s,t,len)
+register char *s;
+register char *t;
+unsigned int len;
+{
+  register char x;
+
+  for (;;) {
+    if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+    if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+    if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+    if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+  }
+  return ((int)(unsigned int)(unsigned char) x)
+       - ((int)(unsigned int)(unsigned char) *t);
+}
diff --git a/str_len.c b/str_len.c
new file mode 100644 (file)
index 0000000..2d2f88b
--- /dev/null
+++ b/str_len.c
@@ -0,0 +1,15 @@
+#include "str.h"
+
+unsigned int str_len(s)
+register char *s;
+{
+  register char *t;
+
+  t = s;
+  for (;;) {
+    if (!*t) return t - s; ++t;
+    if (!*t) return t - s; ++t;
+    if (!*t) return t - s; ++t;
+    if (!*t) return t - s; ++t;
+  }
+}
diff --git a/str_rchr.c b/str_rchr.c
new file mode 100644 (file)
index 0000000..1bf19d3
--- /dev/null
@@ -0,0 +1,22 @@
+#include "str.h"
+
+unsigned int str_rchr(s,c)
+register char *s;
+int c;
+{
+  register char ch;
+  register char *t;
+  register char *u;
+
+  ch = c;
+  t = s;
+  u = 0;
+  for (;;) {
+    if (!*t) break; if (*t == ch) u = t; ++t;
+    if (!*t) break; if (*t == ch) u = t; ++t;
+    if (!*t) break; if (*t == ch) u = t; ++t;
+    if (!*t) break; if (*t == ch) u = t; ++t;
+  }
+  if (!u) u = t;
+  return u - s;
+}
diff --git a/stralloc.h b/stralloc.h
new file mode 100644 (file)
index 0000000..fca496c
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef STRALLOC_H
+#define STRALLOC_H
+
+#include "gen_alloc.h"
+
+GEN_ALLOC_typedef(stralloc,char,s,len,a)
+
+extern int stralloc_ready();
+extern int stralloc_readyplus();
+extern int stralloc_copy();
+extern int stralloc_cat();
+extern int stralloc_copys();
+extern int stralloc_cats();
+extern int stralloc_copyb();
+extern int stralloc_catb();
+extern int stralloc_append(); /* beware: this takes a pointer to 1 char */
+extern int stralloc_starts();
+
+#define stralloc_0(sa) stralloc_append(sa,"")
+
+#endif
diff --git a/stralloc_arts.c b/stralloc_arts.c
new file mode 100644 (file)
index 0000000..1ccb5a4
--- /dev/null
@@ -0,0 +1,12 @@
+#include "byte.h"
+#include "str.h"
+#include "stralloc.h"
+
+int stralloc_starts(sa,s)
+stralloc *sa;
+char *s;
+{
+  int len;
+  len = str_len(s);
+  return (sa->len >= len) && byte_equal(s,len,sa->s);
+}
diff --git a/stralloc_cat.c b/stralloc_cat.c
new file mode 100644 (file)
index 0000000..efbb112
--- /dev/null
@@ -0,0 +1,9 @@
+#include "byte.h"
+#include "stralloc.h"
+
+int stralloc_cat(sato,safrom)
+stralloc *sato;
+stralloc *safrom;
+{
+  return stralloc_catb(sato,safrom->s,safrom->len);
+}
diff --git a/stralloc_catb.c b/stralloc_catb.c
new file mode 100644 (file)
index 0000000..67dbcc0
--- /dev/null
@@ -0,0 +1,15 @@
+#include "stralloc.h"
+#include "byte.h"
+
+int stralloc_catb(sa,s,n)
+stralloc *sa;
+char *s;
+unsigned int n;
+{
+  if (!sa->s) return stralloc_copyb(sa,s,n);
+  if (!stralloc_readyplus(sa,n + 1)) return 0;
+  byte_copy(sa->s + sa->len,n,s);
+  sa->len += n;
+  sa->s[sa->len] = 'Z'; /* ``offensive programming'' */
+  return 1;
+}
diff --git a/stralloc_cats.c b/stralloc_cats.c
new file mode 100644 (file)
index 0000000..d300286
--- /dev/null
@@ -0,0 +1,10 @@
+#include "byte.h"
+#include "str.h"
+#include "stralloc.h"
+
+int stralloc_cats(sa,s)
+stralloc *sa;
+char *s;
+{
+  return stralloc_catb(sa,s,str_len(s));
+}
diff --git a/stralloc_copy.c b/stralloc_copy.c
new file mode 100644 (file)
index 0000000..652aed6
--- /dev/null
@@ -0,0 +1,9 @@
+#include "byte.h"
+#include "stralloc.h"
+
+int stralloc_copy(sato,safrom)
+stralloc *sato;
+stralloc *safrom;
+{
+  return stralloc_copyb(sato,safrom->s,safrom->len);
+}
diff --git a/stralloc_eady.c b/stralloc_eady.c
new file mode 100644 (file)
index 0000000..3a31f4b
--- /dev/null
@@ -0,0 +1,6 @@
+#include "alloc.h"
+#include "stralloc.h"
+#include "gen_allocdefs.h"
+
+GEN_ALLOC_ready(stralloc,char,s,len,a,i,n,x,30,stralloc_ready)
+GEN_ALLOC_readyplus(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus)
diff --git a/stralloc_opyb.c b/stralloc_opyb.c
new file mode 100644 (file)
index 0000000..ac258b3
--- /dev/null
@@ -0,0 +1,14 @@
+#include "stralloc.h"
+#include "byte.h"
+
+int stralloc_copyb(sa,s,n)
+stralloc *sa;
+char *s;
+unsigned int n;
+{
+  if (!stralloc_ready(sa,n + 1)) return 0;
+  byte_copy(sa->s,n,s);
+  sa->len = n;
+  sa->s[n] = 'Z'; /* ``offensive programming'' */
+  return 1;
+}
diff --git a/stralloc_opys.c b/stralloc_opys.c
new file mode 100644 (file)
index 0000000..fdd7807
--- /dev/null
@@ -0,0 +1,10 @@
+#include "byte.h"
+#include "str.h"
+#include "stralloc.h"
+
+int stralloc_copys(sa,s)
+stralloc *sa;
+char *s;
+{
+  return stralloc_copyb(sa,s,str_len(s));
+}
diff --git a/stralloc_pend.c b/stralloc_pend.c
new file mode 100644 (file)
index 0000000..a3443b8
--- /dev/null
@@ -0,0 +1,5 @@
+#include "alloc.h"
+#include "stralloc.h"
+#include "gen_allocdefs.h"
+
+GEN_ALLOC_append(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus,stralloc_append)
diff --git a/strerr.h b/strerr.h
new file mode 100644 (file)
index 0000000..d18e833
--- /dev/null
+++ b/strerr.h
@@ -0,0 +1,80 @@
+#ifndef STRERR_H
+#define STRERR_H
+
+struct strerr
+ {
+  struct strerr *who;
+  char *x;
+  char *y;
+  char *z;
+ }
+;
+
+extern struct strerr strerr_sys;
+extern void strerr_sysinit();
+
+extern char *strerr();
+extern void strerr_warn();
+extern void strerr_die();
+
+#define STRERR(r,se,a) \
+{ se.who = 0; se.x = a; se.y = 0; se.z = 0; return r; }
+
+#define STRERR_SYS(r,se,a) \
+{ se.who = &strerr_sys; se.x = a; se.y = 0; se.z = 0; return r; }
+#define STRERR_SYS3(r,se,a,b,c) \
+{ se.who = &strerr_sys; se.x = a; se.y = b; se.z = c; return r; }
+
+#define strerr_warn6(x1,x2,x3,x4,x5,x6,se) \
+strerr_warn((x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) (se))
+#define strerr_warn5(x1,x2,x3,x4,x5,se) \
+strerr_warn((x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) (se))
+#define strerr_warn4(x1,x2,x3,x4,se) \
+strerr_warn((x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_warn3(x1,x2,x3,se) \
+strerr_warn((x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_warn2(x1,x2,se) \
+strerr_warn((x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_warn1(x1,se) \
+strerr_warn((x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+
+#define strerr_die6(e,x1,x2,x3,x4,x5,x6,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) (se))
+#define strerr_die5(e,x1,x2,x3,x4,x5,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) (se))
+#define strerr_die4(e,x1,x2,x3,x4,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_die3(e,x1,x2,x3,se) \
+strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_die2(e,x1,x2,se) \
+strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_die1(e,x1,se) \
+strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+
+#define strerr_die6sys(e,x1,x2,x3,x4,x5,x6) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),&strerr_sys)
+#define strerr_die5sys(e,x1,x2,x3,x4,x5) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,&strerr_sys)
+#define strerr_die4sys(e,x1,x2,x3,x4) \
+strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,&strerr_sys)
+#define strerr_die3sys(e,x1,x2,x3) \
+strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
+#define strerr_die2sys(e,x1,x2) \
+strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
+#define strerr_die1sys(e,x1) \
+strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
+
+#define strerr_die6x(e,x1,x2,x3,x4,x5,x6) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) 0)
+#define strerr_die5x(e,x1,x2,x3,x4,x5) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) 0)
+#define strerr_die4x(e,x1,x2,x3,x4) \
+strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) 0)
+#define strerr_die3x(e,x1,x2,x3) \
+strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
+#define strerr_die2x(e,x1,x2) \
+strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
+#define strerr_die1x(e,x1) \
+strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
+
+#endif
diff --git a/strerr_die.c b/strerr_die.c
new file mode 100644 (file)
index 0000000..6092020
--- /dev/null
@@ -0,0 +1,37 @@
+#include "substdio.h"
+#include "subfd.h"
+#include "exit.h"
+#include "strerr.h"
+
+void strerr_warn(x1,x2,x3,x4,x5,x6,se)
+char *x1; char *x2; char *x3; char *x4; char *x5; char *x6;
+struct strerr *se;
+{
+  strerr_sysinit();
+  if (x1) substdio_puts(subfderr,x1);
+  if (x2) substdio_puts(subfderr,x2);
+  if (x3) substdio_puts(subfderr,x3);
+  if (x4) substdio_puts(subfderr,x4);
+  if (x5) substdio_puts(subfderr,x5);
+  if (x6) substdio_puts(subfderr,x6);
+  while(se) {
+    if (se->x) substdio_puts(subfderr,se->x);
+    if (se->y) substdio_puts(subfderr,se->y);
+    if (se->z) substdio_puts(subfderr,se->z);
+    se = se->who;
+  }
+  substdio_puts(subfderr,"\n");
+  substdio_flush(subfderr);
+}
+
+void strerr_die(e,x1,x2,x3,x4,x5,x6,se)
+int e;
+char *x1; char *x2; char *x3; char *x4; char *x5; char *x6;
+struct strerr *se;
+{
+  strerr_warn(x1,x2,x3,x4,x5,x6,se);
+  _exit(e);
+}
diff --git a/strerr_sys.c b/strerr_sys.c
new file mode 100644 (file)
index 0000000..198198b
--- /dev/null
@@ -0,0 +1,12 @@
+#include "error.h"
+#include "strerr.h"
+
+struct strerr strerr_sys;
+
+void strerr_sysinit()
+{
+  strerr_sys.who = 0;
+  strerr_sys.x = error_str(errno);
+  strerr_sys.y = "";
+  strerr_sys.z = "";
+}
diff --git a/strset.c b/strset.c
new file mode 100644 (file)
index 0000000..9fb0cbd
--- /dev/null
+++ b/strset.c
@@ -0,0 +1,130 @@
+#include "strset.h"
+#include "str.h"
+#include "byte.h"
+
+uint32 strset_hash(s)
+char *s;
+{
+ unsigned char ch;
+ uint32 h;
+ h = 5381;
+ while (ch = *s)
+  {
+   h = ((h << 5) + h) ^ ch;
+   ++s;
+  }
+ return h;
+}
+
+int strset_init(set)
+strset *set;
+{
+ int h;
+ set->mask = 15;
+ set->n = 0;
+ set->a = 10;
+
+ set->first = (int *) alloc(sizeof(int) * (set->mask + 1));
+ if (!set->first) return 0;
+ set->p = (strset_list *) alloc(sizeof(strset_list) * set->a);
+ if (!set->p) { alloc_free(set->first); return 0; }
+ set->x = (char **) alloc(sizeof(char *) * set->a);
+ if (!set->x) { alloc_free(set->p); alloc_free(set->first); return 0; }
+
+ for (h = 0;h <= set->mask;++h) set->first[h] = -1;
+
+ return 1;
+}
+
+char *strset_in(set,s)
+strset *set;
+char *s;
+{
+ uint32 h;
+ strset_list *sl;
+ int i;
+ char *xi;
+
+ h = strset_hash(s);
+ i = set->first[h & set->mask];
+ while (i >= 0)
+  {
+   sl = set->p + i;
+   if (sl->h == h)
+    {
+     xi = set->x[i];
+     if (!str_diff(xi,s)) return xi;
+    }
+   i = sl->next;
+  }
+ return 0;
+}
+
+int strset_add(set,s)
+strset *set;
+char *s;
+{
+ uint32 h;
+ int n;
+ strset_list *sl;
+
+ n = set->n;
+
+ if (n == set->a)
+  {
+   int newa;
+   strset_list *newp;
+   char **newx;
+
+   newa = n + 10 + (n >> 3);
+   newp = (strset_list *) alloc(sizeof(strset_list) * newa);
+   if (!newp) return 0;
+   newx = (char **) alloc(sizeof(char *) * newa);
+   if (!newx) { alloc_free(newp); return 0; }
+
+   byte_copy(newp,sizeof(strset_list) * n,set->p);
+   byte_copy(newx,sizeof(char *) * n,set->x);
+   alloc_free(set->p);
+   alloc_free(set->x);
+   set->p = newp;
+   set->x = newx;
+   set->a = newa;
+
+   if (n + n + n > set->mask)
+    {
+     int newmask;
+     int *newfirst;
+     int i;
+     uint32 h;
+
+     newmask = set->mask + set->mask + 1;
+     newfirst = (int *) alloc(sizeof(int) * (newmask + 1));
+     if (!newfirst) return 0;
+
+     for (h = 0;h <= newmask;++h) newfirst[h] = -1;
+
+     for (i = 0;i < n;++i)
+      {
+       sl = set->p + i;
+       h = sl->h & newmask;
+       sl->next = newfirst[h];
+       newfirst[h] = i;
+      }
+
+     alloc_free(set->first);
+     set->first = newfirst;
+     set->mask = newmask;
+    }
+  }
+
+ h = strset_hash(s);
+
+ sl = set->p + n;
+ sl->h = h;
+ h &= set->mask;
+ sl->next = set->first[h];
+ set->first[h] = n;
+ set->x[n] = s;
+ set->n = n + 1;
+ return 1;
+}
diff --git a/strset.h b/strset.h
new file mode 100644 (file)
index 0000000..3af0759
--- /dev/null
+++ b/strset.h
@@ -0,0 +1,29 @@
+#ifndef STRSET_H
+#define STRSET_H
+
+#include "uint32.h"
+
+typedef struct strset_list
+ {
+  uint32 h;
+  int next;
+ }
+strset_list;
+
+typedef struct
+ {
+  int mask; /* mask + 1 is power of 2, size of hash table */
+  int n; /* number of entries used in list and x */
+  int a; /* number of entries allocated in list and x */
+  int *first; /* first[h] is front of hash list h */
+  strset_list *p; /* p[i].next is next; p[i].h is hash of x[i] */
+  char **x; /* x[i] is entry i */
+ }
+strset;
+
+extern uint32 strset_hash();
+extern int strset_init();
+extern char *strset_in();
+extern int strset_add();
+
+#endif
diff --git a/subfd.h b/subfd.h
new file mode 100644 (file)
index 0000000..bcb2e1e
--- /dev/null
+++ b/subfd.h
@@ -0,0 +1,15 @@
+#ifndef SUBFD_H
+#define SUBFD_H
+
+#include "substdio.h"
+
+extern substdio *subfdin;
+extern substdio *subfdinsmall;
+extern substdio *subfdout;
+extern substdio *subfdoutsmall;
+extern substdio *subfderr;
+
+extern int subfd_read();
+extern int subfd_readsmall();
+
+#endif
diff --git a/subfderr.c b/subfderr.c
new file mode 100644 (file)
index 0000000..011ab0f
--- /dev/null
@@ -0,0 +1,7 @@
+#include "readwrite.h"
+#include "substdio.h"
+#include "subfd.h"
+
+char subfd_errbuf[256];
+static substdio it = SUBSTDIO_FDBUF(write,2,subfd_errbuf,256);
+substdio *subfderr = &it;
diff --git a/subfdins.c b/subfdins.c
new file mode 100644 (file)
index 0000000..36983ac
--- /dev/null
@@ -0,0 +1,13 @@
+#include "readwrite.h"
+#include "substdio.h"
+#include "subfd.h"
+
+int subfd_readsmall(fd,buf,len) int fd; char *buf; int len;
+{
+  if (substdio_flush(subfdoutsmall) == -1) return -1;
+  return read(fd,buf,len);
+}
+
+char subfd_inbufsmall[256];
+static substdio it = SUBSTDIO_FDBUF(subfd_readsmall,0,subfd_inbufsmall,256);
+substdio *subfdinsmall = &it;
diff --git a/subfdouts.c b/subfdouts.c
new file mode 100644 (file)
index 0000000..5be356d
--- /dev/null
@@ -0,0 +1,7 @@
+#include "readwrite.h"
+#include "substdio.h"
+#include "subfd.h"
+
+char subfd_outbufsmall[256];
+static substdio it = SUBSTDIO_FDBUF(write,1,subfd_outbufsmall,256);
+substdio *subfdoutsmall = &it;
diff --git a/subgetopt.c b/subgetopt.c
new file mode 100644 (file)
index 0000000..dacf376
--- /dev/null
@@ -0,0 +1,79 @@
+/* subgetopt.c, subgetopt.h: (yet another) improved getopt clone, inner layer
+D. J. Bernstein, djb@pobox.com.
+No dependencies.
+No system requirements.
+19970228: Cleanups.
+931129: Adapted from getopt.c.
+No known patent problems.
+
+Documentation in subgetopt.3.
+*/
+
+#define SUBGETOPTNOSHORT
+#include "subgetopt.h"
+
+#define sgopt subgetopt
+#define optind subgetoptind
+#define optpos subgetoptpos
+#define optarg subgetoptarg
+#define optproblem subgetoptproblem
+#define optdone subgetoptdone
+
+int optind = 1;
+int optpos = 0;
+char *optarg = 0;
+int optproblem = 0;
+int optdone = SUBGETOPTDONE;
+
+int sgopt(argc,argv,opts)
+int argc;
+char **argv;
+char *opts;
+{
+  int c;
+  char *s;
+
+  optarg = 0;
+  if (!argv || (optind >= argc) || !argv[optind]) return optdone;
+  if (optpos && !argv[optind][optpos]) {
+    ++optind;
+    optpos = 0;
+    if ((optind >= argc) || !argv[optind]) return optdone;
+  }
+  if (!optpos) {
+    if (argv[optind][0] != '-') return optdone;
+    ++optpos;
+    c = argv[optind][1];
+    if ((c == '-') || (c == 0)) {
+      if (c) ++optind;
+      optpos = 0;
+      return optdone;
+    }
+    /* otherwise c is reassigned below */
+  }
+  c = argv[optind][optpos];
+  ++optpos;
+  s = opts;
+  while (*s) {
+    if (c == *s) {
+      if (s[1] == ':') {
+        optarg = argv[optind] + optpos;
+        ++optind;
+        optpos = 0;
+        if (!*optarg) {
+          optarg = argv[optind];
+          if ((optind >= argc) || !optarg) { /* argument past end */
+            optproblem = c;
+            return '?';
+          }
+         ++optind;
+        }
+      }
+      return c;
+    }
+    ++s;
+    if (*s == ':') ++s;
+  }
+  optproblem = c;
+  return '?';
+}
diff --git a/subgetopt.h b/subgetopt.h
new file mode 100644 (file)
index 0000000..d26c62a
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef SUBGETOPT_H
+#define SUBGETOPT_H
+
+#ifndef SUBGETOPTNOSHORT
+#define sgopt subgetopt
+#define sgoptarg subgetoptarg
+#define sgoptind subgetoptind
+#define sgoptpos subgetoptpos
+#define sgoptproblem subgetoptproblem
+#define sgoptprogname subgetoptprogname
+#define sgoptdone subgetoptdone
+#endif
+
+#define SUBGETOPTDONE -1
+
+extern int subgetopt();
+extern char *subgetoptarg;
+extern int subgetoptind;
+extern int subgetoptpos;
+extern int subgetoptproblem;
+extern char *subgetoptprogname;
+extern int subgetoptdone;
+
+#endif
diff --git a/substdi.c b/substdi.c
new file mode 100644 (file)
index 0000000..42407a1
--- /dev/null
+++ b/substdi.c
@@ -0,0 +1,91 @@
+#include "substdio.h"
+#include "byte.h"
+#include "error.h"
+
+static int oneread(op,fd,buf,len)
+register int (*op)();
+register int fd;
+register char *buf;
+register int len;
+{
+  register int r;
+
+  for (;;) {
+    r = op(fd,buf,len);
+    if (r == -1) if (errno == error_intr) continue;
+    return r;
+  }
+}
+
+static int getthis(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+  register int r;
+  register int q;
+  r = s->p;
+  q = r - len;
+  if (q > 0) { r = len; s->p = q; } else s->p = 0;
+  byte_copy(buf,r,s->x + s->n);
+  s->n += r;
+  return r;
+}
+
+int substdio_feed(s)
+register substdio *s;
+{
+  register int r;
+  register int q;
+
+  if (s->p) return s->p;
+  q = s->n;
+  r = oneread(s->op,s->fd,s->x,q);
+  if (r <= 0) return r;
+  s->p = r;
+  q -= r;
+  s->n = q;
+  if (q > 0) /* damn, gotta shift */ byte_copyr(s->x + q,r,s->x);
+  return r;
+}
+
+int substdio_bget(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+  register int r;
+  if (s->p > 0) return getthis(s,buf,len);
+  r = s->n; if (r <= len) return oneread(s->op,s->fd,buf,r);
+  r = substdio_feed(s); if (r <= 0) return r;
+  return getthis(s,buf,len);
+}
+
+int substdio_get(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+  register int r;
+  if (s->p > 0) return getthis(s,buf,len);
+  if (s->n <= len) return oneread(s->op,s->fd,buf,len);
+  r = substdio_feed(s); if (r <= 0) return r;
+  return getthis(s,buf,len);
+}
+
+char *substdio_peek(s)
+register substdio *s;
+{
+  return s->x + s->n;
+}
+
+void substdio_seek(s,len)
+register substdio *s;
+register int len;
+{
+  s->n += len;
+  s->p -= len;
+}
diff --git a/substdio.c b/substdio.c
new file mode 100644 (file)
index 0000000..d03dff2
--- /dev/null
@@ -0,0 +1,15 @@
+#include "substdio.h"
+
+void substdio_fdbuf(s,op,fd,buf,len)
+register substdio *s;
+register int (*op)();
+register int fd;
+register char *buf;
+register int len;
+{
+  s->x = buf;
+  s->fd = fd;
+  s->op = op;
+  s->p = 0;
+  s->n = len;
+}
diff --git a/substdio.h b/substdio.h
new file mode 100644 (file)
index 0000000..c3f7f7d
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef SUBSTDIO_H
+#define SUBSTDIO_H
+
+typedef struct substdio {
+  char *x;
+  int p;
+  int n;
+  int fd;
+  int (*op)();
+} substdio;
+
+#define SUBSTDIO_FDBUF(op,fd,buf,len) { (buf), 0, (len), (fd), (op) }
+
+extern void substdio_fdbuf();
+
+extern int substdio_flush();
+extern int substdio_put();
+extern int substdio_bput();
+extern int substdio_putflush();
+extern int substdio_puts();
+extern int substdio_bputs();
+extern int substdio_putsflush();
+
+extern int substdio_get();
+extern int substdio_bget();
+extern int substdio_feed();
+
+extern char *substdio_peek();
+extern void substdio_seek();
+
+#define substdio_fileno(s) ((s)->fd)
+
+#define SUBSTDIO_INSIZE 8192
+#define SUBSTDIO_OUTSIZE 8192
+
+#define substdio_PEEK(s) ( (s)->x + (s)->n )
+#define substdio_SEEK(s,len) ( ( (s)->p -= (len) ) , ( (s)->n += (len) ) )
+
+#define substdio_BPUTC(s,c) \
+  ( ((s)->n != (s)->p) \
+    ? ( (s)->x[(s)->p++] = (c), 0 ) \
+    : substdio_bput((s),&(c),1) \
+  )
+
+extern int substdio_copy();
+
+#endif
diff --git a/substdio_copy.c b/substdio_copy.c
new file mode 100644 (file)
index 0000000..71cf200
--- /dev/null
@@ -0,0 +1,18 @@
+#include "substdio.h"
+
+int substdio_copy(ssout,ssin)
+register substdio *ssout;
+register substdio *ssin;
+{
+  register int n;
+  register char *x;
+
+  for (;;) {
+    n = substdio_feed(ssin);
+    if (n < 0) return -2;
+    if (!n) return 0;
+    x = substdio_PEEK(ssin);
+    if (substdio_put(ssout,x,n) == -1) return -3;
+    substdio_SEEK(ssin,n);
+  }
+}
diff --git a/substdo.c b/substdo.c
new file mode 100644 (file)
index 0000000..fb616f7
--- /dev/null
+++ b/substdo.c
@@ -0,0 +1,108 @@
+#include "substdio.h"
+#include "str.h"
+#include "byte.h"
+#include "error.h"
+
+static int allwrite(op,fd,buf,len)
+register int (*op)();
+register int fd;
+register char *buf;
+register int len;
+{
+  register int w;
+
+  while (len) {
+    w = op(fd,buf,len);
+    if (w == -1) {
+      if (errno == error_intr) continue;
+      return -1; /* note that some data may have been written */
+    }
+    if (w == 0) ; /* luser's fault */
+    buf += w;
+    len -= w;
+  }
+  return 0;
+}
+
+int substdio_flush(s)
+register substdio *s;
+{
+  register int p;
+  p = s->p;
+  if (!p) return 0;
+  s->p = 0;
+  return allwrite(s->op,s->fd,s->x,p);
+}
+
+int substdio_bput(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+  register int n;
+  while (len > (n = s->n - s->p)) {
+    byte_copy(s->x + s->p,n,buf); s->p += n; buf += n; len -= n;
+    if (substdio_flush(s) == -1) return -1;
+  }
+  /* now len <= s->n - s->p */
+  byte_copy(s->x + s->p,len,buf);
+  s->p += len;
+  return 0;
+}
+
+int substdio_put(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+  register int n;
+  n = s->n;
+  if (len > n - s->p) {
+    if (substdio_flush(s) == -1) return -1;
+    /* now s->p == 0 */
+    if (n < SUBSTDIO_OUTSIZE) n = SUBSTDIO_OUTSIZE;
+    while (len > s->n) {
+      if (n > len) n = len;
+      if (allwrite(s->op,s->fd,buf,n) == -1) return -1;
+      buf += n;
+      len -= n;
+    }
+  }
+  /* now len <= s->n - s->p */
+  byte_copy(s->x + s->p,len,buf);
+  s->p += len;
+  return 0;
+}
+
+int substdio_putflush(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+  if (substdio_flush(s) == -1) return -1;
+  return allwrite(s->op,s->fd,buf,len);
+}
+
+int substdio_bputs(s,buf)
+register substdio *s;
+register char *buf;
+{
+  return substdio_bput(s,buf,str_len(buf));
+}
+
+int substdio_puts(s,buf)
+register substdio *s;
+register char *buf;
+{
+  return substdio_put(s,buf,str_len(buf));
+}
+
+int substdio_putsflush(s,buf)
+register substdio *s;
+register char *buf;
+{
+  return substdio_putflush(s,buf,str_len(buf));
+}
diff --git a/token822.c b/token822.c
new file mode 100644 (file)
index 0000000..48a4388
--- /dev/null
@@ -0,0 +1,513 @@
+#include "stralloc.h"
+#include "alloc.h"
+#include "str.h"
+#include "token822.h"
+#include "gen_allocdefs.h"
+
+static struct token822 comma = { TOKEN822_COMMA };
+
+void token822_reverse(ta)
+token822_alloc *ta;
+{
+ int i;
+ int n;
+ struct token822 temp;
+
+ n = ta->len - 1;
+ for (i = 0;i + i < n;++i)
+  {
+   temp = ta->t[i];
+   ta->t[i] = ta->t[n - i];
+   ta->t[n - i] = temp;
+  }
+}
+
+GEN_ALLOC_ready(token822_alloc,struct token822,t,len,a,i,n,x,30,token822_ready)
+GEN_ALLOC_readyplus(token822_alloc,struct token822,t,len,a,i,n,x,30,token822_readyplus)
+GEN_ALLOC_append(token822_alloc,struct token822,t,len,a,i,n,x,30,token822_readyplus,token822_append)
+
+static int needspace(t1,t2)
+int t1;
+int t2;
+{
+ if (!t1) return 0;
+ if (t1 == TOKEN822_COLON) return 1;
+ if (t1 == TOKEN822_COMMA) return 1;
+ if (t2 == TOKEN822_LEFT) return 1;
+ switch(t1)
+  {
+   case TOKEN822_ATOM: case TOKEN822_LITERAL:
+   case TOKEN822_QUOTE: case TOKEN822_COMMENT:
+     switch(t2)
+      {
+       case TOKEN822_ATOM: case TOKEN822_LITERAL:
+       case TOKEN822_QUOTE: case TOKEN822_COMMENT:
+         return 1;
+      }
+  }
+ return 0;
+}
+
+static int atomok(ch)
+char ch;
+{
+ switch(ch)
+  {
+   case ' ': case '\t': case '\r': case '\n':
+   case '(': case '[': case '"':
+   case '<': case '>': case ';': case ':':
+   case '@': case ',': case '.':
+     return 0;
+  }
+ return 1;
+}
+
+static void atomcheck(t)
+struct token822 *t;
+{
+ int i;
+ char ch;
+ for (i = 0;i < t->slen;++i)
+  {
+   ch = t->s[i];
+   if ((ch < 32) || (ch > 126) || (ch == ')') || (ch == ']') || (ch == '\\'))
+    {
+     t->type = TOKEN822_QUOTE;
+     return;
+    }
+  }
+}
+
+int token822_unparse(sa,ta,linelen)
+stralloc *sa;
+token822_alloc *ta;
+unsigned int linelen;
+{
+ struct token822 *t;
+ int len;
+ int ch;
+ int i;
+ int j;
+ int lasttype;
+ int newtype;
+ char *s;
+ char *lineb;
+ char *linee;
+
+ len = 0;
+ lasttype = 0;
+ for (i = 0;i < ta->len;++i)
+  {
+   t = ta->t + i;
+   newtype = t->type;
+   if (needspace(lasttype,newtype))
+     ++len;
+   lasttype = newtype;
+   switch(newtype)
+    {
+     case TOKEN822_COMMA:
+       len += 3; break;
+     case TOKEN822_AT: case TOKEN822_DOT: case TOKEN822_LEFT: case TOKEN822_RIGHT:
+     case TOKEN822_SEMI: case TOKEN822_COLON:
+       ++len; break;
+     case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL: case TOKEN822_COMMENT:
+       if (t->type != TOKEN822_ATOM) len += 2;
+       for (j = 0;j < t->slen;++j)
+        switch(ch = t->s[j])
+         {
+          case '"': case '[': case ']': case '(': case ')':
+          case '\\': case '\r': case '\n': ++len;
+          default: ++len;
+         }
+       break;
+    }
+  }
+ len += 2;
+
+ if (!stralloc_ready(sa,len))
+   return -1;
+
+ s = sa->s;
+ lineb = s;
+ linee = 0;
+
+ lasttype = 0;
+ for (i = 0;i < ta->len;++i)
+  {
+   t = ta->t + i;
+   newtype = t->type;
+   if (needspace(lasttype,newtype))
+     *s++ = ' ';
+   lasttype = newtype;
+   switch(newtype)
+    {
+     case TOKEN822_COMMA:
+       *s++ = ',';
+#define NSUW \
+ s[0] = '\n'; s[1] = ' '; \
+ if (linee && (!linelen || (s - lineb <= linelen))) \
+  { while (linee < s) { linee[0] = linee[2]; ++linee; } linee -= 2; } \
+ else { if (linee) lineb = linee + 1; linee = s; s += 2; }
+       NSUW
+       break;
+     case TOKEN822_AT: *s++ = '@'; break;
+     case TOKEN822_DOT: *s++ = '.'; break;
+     case TOKEN822_LEFT: *s++ = '<'; break;
+     case TOKEN822_RIGHT: *s++ = '>'; break;
+     case TOKEN822_SEMI: *s++ = ';'; break;
+     case TOKEN822_COLON: *s++ = ':'; break;
+     case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL: case TOKEN822_COMMENT:
+       if (t->type == TOKEN822_QUOTE) *s++ = '"';
+       if (t->type == TOKEN822_LITERAL) *s++ = '[';
+       if (t->type == TOKEN822_COMMENT) *s++ = '(';
+       for (j = 0;j < t->slen;++j)
+        switch(ch = t->s[j])
+         {
+          case '"': case '[': case ']': case '(': case ')':
+          case '\\': case '\r': case '\n': *s++ = '\\';
+          default: *s++ = ch;
+         }
+       if (t->type == TOKEN822_QUOTE) *s++ = '"';
+       if (t->type == TOKEN822_LITERAL) *s++ = ']';
+       if (t->type == TOKEN822_COMMENT) *s++ = ')';
+       break;
+    }
+  }
+ NSUW
+ --s;
+ sa->len = s - sa->s;
+ return 1;
+}
+
+int token822_unquote(sa,ta)
+stralloc *sa;
+token822_alloc *ta;
+{
+ struct token822 *t;
+ int len;
+ int i;
+ int j;
+ char *s;
+
+ len = 0;
+ for (i = 0;i < ta->len;++i)
+  {
+   t = ta->t + i;
+   switch(t->type)
+    {
+     case TOKEN822_COMMA: case TOKEN822_AT: case TOKEN822_DOT: case TOKEN822_LEFT: 
+     case TOKEN822_RIGHT: case TOKEN822_SEMI: case TOKEN822_COLON: 
+       ++len; break;
+     case TOKEN822_LITERAL:
+       len += 2;
+     case TOKEN822_ATOM: case TOKEN822_QUOTE:
+       len += t->slen;
+    }
+  }
+
+ if (!stralloc_ready(sa,len))
+   return -1;
+
+ s = sa->s;
+
+ for (i = 0;i < ta->len;++i)
+  {
+   t = ta->t + i;
+   switch(t->type)
+    {
+     case TOKEN822_COMMA: *s++ = ','; break;
+     case TOKEN822_AT: *s++ = '@'; break;
+     case TOKEN822_DOT: *s++ = '.'; break;
+     case TOKEN822_LEFT: *s++ = '<'; break;
+     case TOKEN822_RIGHT: *s++ = '>'; break;
+     case TOKEN822_SEMI: *s++ = ';'; break;
+     case TOKEN822_COLON: *s++ = ':'; break;
+     case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL:
+       if (t->type == TOKEN822_LITERAL) *s++ = '[';
+       for (j = 0;j < t->slen;++j)
+        *s++ = t->s[j];
+       if (t->type == TOKEN822_LITERAL) *s++ = ']';
+       break;
+     case TOKEN822_COMMENT: break;
+    }
+  }
+ sa->len = s - sa->s;
+ return 1;
+}
+
+int token822_parse(ta,sa,buf)
+token822_alloc *ta;
+stralloc *sa;
+stralloc *buf;
+{
+ int i;
+ int salen;
+ int level;
+ struct token822 *t;
+ int numtoks;
+ int numchars;
+ char *cbuf;
+
+ salen = sa->len;
+
+ numchars = 0;
+ numtoks = 0;
+ for (i = 0;i < salen;++i)
+   switch(sa->s[i])
+    {
+     case '.': case ',': case '@': case '<': case '>': case ':': case ';':
+       ++numtoks; break;
+     case ' ': case '\t': case '\r': case '\n': break;
+     case ')': case ']': return 0;
+     /* other control chars and non-ASCII chars are also bad, in theory */
+     case '(':
+       level = 1;
+       while (level)
+       {
+        if (++i >= salen) return 0;
+        switch(sa->s[i])
+         {
+          case '(': ++level; break;
+          case ')': --level; break;
+          case '\\': if (++i >= salen) return 0;
+          default: ++numchars;
+         }
+       }
+       ++numtoks;
+       break;
+     case '"':
+       level = 1;
+       while (level)
+       {
+        if (++i >= salen) return 0;
+        switch(sa->s[i])
+         {
+          case '"': --level; break;
+          case '\\': if (++i >= salen) return 0;
+          default: ++numchars;
+         }
+       }
+       ++numtoks;
+       break;
+     case '[':
+       level = 1;
+       while (level)
+       {
+        if (++i >= salen) return 0;
+        switch(sa->s[i])
+         {
+          case ']': --level; break;
+          case '\\': if (++i >= salen) return 0;
+          default: ++numchars;
+         }
+       }
+       ++numtoks;
+       break;
+     default:
+       do
+       {
+        if (sa->s[i] == '\\') if (++i >= salen) break;
+        ++numchars;
+        if (++i >= salen)
+          break;
+       }
+       while (atomok(sa->s[i]));
+       --i;
+       ++numtoks;
+    }
+
+ if (!token822_ready(ta,numtoks))
+   return -1;
+ if (!stralloc_ready(buf,numchars))
+   return -1;
+ cbuf = buf->s;
+ ta->len = numtoks;
+
+ t = ta->t;
+ for (i = 0;i < salen;++i)
+   switch(sa->s[i])
+    {
+     case '.': t->type = TOKEN822_DOT; ++t; break;
+     case ',': t->type = TOKEN822_COMMA; ++t; break;
+     case '@': t->type = TOKEN822_AT; ++t; break;
+     case '<': t->type = TOKEN822_LEFT; ++t; break;
+     case '>': t->type = TOKEN822_RIGHT; ++t; break;
+     case ':': t->type = TOKEN822_COLON; ++t; break;
+     case ';': t->type = TOKEN822_SEMI; ++t; break;
+     case ' ': case '\t': case '\r': case '\n': break;
+     case '(':
+       t->type = TOKEN822_COMMENT; t->s = cbuf; t->slen = 0;
+       level = 1;
+       while (level)
+       {
+        ++i; /* assert: < salen */
+        switch(sa->s[i])
+         {
+          case '(': ++level; break;
+          case ')': --level; break;
+          case '\\': ++i; /* assert: < salen */
+          default: *cbuf++ = sa->s[i]; ++t->slen;
+         }
+       }
+       ++t;
+       break;
+     case '"':
+       t->type = TOKEN822_QUOTE; t->s = cbuf; t->slen = 0;
+       level = 1;
+       while (level)
+       {
+        ++i; /* assert: < salen */
+        switch(sa->s[i])
+         {
+          case '"': --level; break;
+          case '\\': ++i; /* assert: < salen */
+          default: *cbuf++ = sa->s[i]; ++t->slen;
+         }
+       }
+       ++t;
+       break;
+     case '[':
+       t->type = TOKEN822_LITERAL; t->s = cbuf; t->slen = 0;
+       level = 1;
+       while (level)
+       {
+        ++i; /* assert: < salen */
+        switch(sa->s[i])
+         {
+          case ']': --level; break;
+          case '\\': ++i; /* assert: < salen */
+          default: *cbuf++ = sa->s[i]; ++t->slen;
+         }
+       }
+       ++t;
+       break;
+     default:
+       t->type = TOKEN822_ATOM; t->s = cbuf; t->slen = 0;
+       do
+       {
+        if (sa->s[i] == '\\') if (++i >= salen) break;
+        *cbuf++ = sa->s[i]; ++t->slen;
+        if (++i >= salen)
+          break;
+       }
+       while (atomok(sa->s[i]));
+       atomcheck(t);
+       --i;
+       ++t;
+    }
+ return 1;
+}
+
+static int gotaddr(taout,taaddr,callback)
+token822_alloc *taout;
+token822_alloc *taaddr;
+int (*callback)();
+{
+ int i;
+
+ if (callback(taaddr) != 1)
+   return 0;
+
+ if (!token822_readyplus(taout,taaddr->len))
+   return 0;
+ for (i = 0;i < taaddr->len;++i)
+   taout->t[taout->len++] = taaddr->t[i];
+
+ taaddr->len = 0;
+ return 1;
+}
+
+int token822_addrlist(taout,taaddr,ta,callback)
+token822_alloc *taout;
+token822_alloc *taaddr;
+token822_alloc *ta;
+int (*callback)();
+{
+ struct token822 *t;
+ struct token822 *beginning;
+ int ingroup;
+ int wordok;
+
+ taout->len = 0;
+ taaddr->len = 0;
+
+ if (!token822_readyplus(taout,1)) return -1;
+ if (!token822_readyplus(taaddr,1)) return -1;
+ ingroup = 0;
+ wordok = 1;
+
+ beginning = ta->t + 2;
+ t = ta->t + ta->len - 1;
+
+ /* rfc 822 address lists are easy to parse from right to left */
+
+#define FLUSH if (taaddr->len) if (!gotaddr(taout,taaddr,callback)) return -1;
+#define FLUSHCOMMA if (taaddr->len) { \
+if (!gotaddr(taout,taaddr,callback)) return -1; \
+if (!token822_append(taout,&comma)) return -1; }
+#define ADDRLEFT if (!token822_append(taaddr,t--)) return -1;
+#define OUTLEFT if (!token822_append(taout,t--)) return -1;
+
+ while (t >= beginning)
+  {
+   switch(t->type)
+    {
+     case TOKEN822_SEMI:
+       FLUSHCOMMA
+       if (ingroup) return 0;
+       ingroup = 1;
+       wordok = 1;
+       break;
+     case TOKEN822_COLON:
+       FLUSH
+       if (!ingroup) return 0;
+       ingroup = 0;
+       while ((t >= beginning) && (t->type != TOKEN822_COMMA))
+        OUTLEFT
+       if (t >= beginning)
+        OUTLEFT
+       wordok = 1;
+       continue;
+     case TOKEN822_RIGHT:
+       FLUSHCOMMA
+       OUTLEFT
+       while ((t >= beginning) && (t->type != TOKEN822_LEFT))
+        ADDRLEFT
+       /* important to use address here even if it's empty: <> */
+       if (!gotaddr(taout,taaddr,callback)) return -1;
+       if (t < beginning) return 0;
+       OUTLEFT
+       while ((t >= beginning) && ((t->type == TOKEN822_COMMENT) || (t->type == TOKEN822_ATOM) || (t->type == TOKEN822_QUOTE) || (t->type == TOKEN822_AT) || (t->type == TOKEN822_DOT)))
+        OUTLEFT
+       wordok = 0;
+       continue;
+     case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL:
+       if (!wordok)
+        FLUSHCOMMA
+       wordok = 0;
+       ADDRLEFT
+       continue;
+     case TOKEN822_COMMENT:
+       /* comment is lexically a space; shouldn't affect wordok */
+       break;
+     case TOKEN822_COMMA:
+       FLUSH
+       wordok = 1;
+       break;
+     default:
+       wordok = 1;
+       ADDRLEFT
+       continue;
+    }
+   OUTLEFT
+  }
+ FLUSH
+ ++t;
+ while (t > ta->t)
+   if (!token822_append(taout,--t)) return -1;
+
+ token822_reverse(taout);
+ return 1;
+}
diff --git a/token822.h b/token822.h
new file mode 100644 (file)
index 0000000..9ca35cf
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef TOKEN822_H
+#define TOKEN822_H
+
+struct token822
+ {
+  int type;
+  char *s;
+  int slen;
+ }
+;
+
+#include "gen_alloc.h"
+GEN_ALLOC_typedef(token822_alloc,struct token822,t,len,a)
+
+extern int token822_parse();
+extern int token822_addrlist();
+extern int token822_unquote();
+extern int token822_unparse();
+extern void token822_free();
+extern void token822_reverse();
+extern int token822_ready();
+extern int token822_readyplus();
+extern int token822_append();
+
+#define TOKEN822_ATOM 1
+#define TOKEN822_QUOTE 2
+#define TOKEN822_LITERAL 3
+#define TOKEN822_COMMENT 4
+#define TOKEN822_LEFT 5
+#define TOKEN822_RIGHT 6
+#define TOKEN822_AT 7
+#define TOKEN822_COMMA 8
+#define TOKEN822_SEMI 9
+#define TOKEN822_COLON 10
+#define TOKEN822_DOT 11
+
+#endif
diff --git a/trycpp.c b/trycpp.c
new file mode 100644 (file)
index 0000000..d7d83ad
--- /dev/null
+++ b/trycpp.c
@@ -0,0 +1,7 @@
+void main()
+{
+#ifdef NeXT
+  printf("nextstep\n"); exit(0);
+#endif
+  printf("unknown\n"); exit(0);
+}
diff --git a/trysgact.c b/trysgact.c
new file mode 100644 (file)
index 0000000..263cb21
--- /dev/null
@@ -0,0 +1,10 @@
+#include <signal.h>
+
+void main()
+{
+  struct sigaction sa;
+  sa.sa_handler = 0;
+  sa.sa_flags = 0;
+  sigemptyset(&sa.sa_mask);
+  sigaction(0,&sa,(struct sigaction *) 0);
+}
diff --git a/tryulong32.c b/tryulong32.c
new file mode 100644 (file)
index 0000000..a108076
--- /dev/null
@@ -0,0 +1,11 @@
+void main()
+{
+  unsigned long u;
+  u = 1;
+  u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+  u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+  u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+  u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+  if (!u) _exit(0);
+  _exit(1);
+}
diff --git a/tryvfork.c b/tryvfork.c
new file mode 100644 (file)
index 0000000..21387e4
--- /dev/null
@@ -0,0 +1,4 @@
+void main()
+{
+  vfork();
+}
diff --git a/trywaitp.c b/trywaitp.c
new file mode 100644 (file)
index 0000000..7e73bfa
--- /dev/null
@@ -0,0 +1,7 @@
+#include <sys/types.h>
+#include <sys/wait.h>
+
+void main()
+{
+  waitpid(0,0,0);
+}
diff --git a/uint32.h1 b/uint32.h1
new file mode 100644 (file)
index 0000000..6599aa0
--- /dev/null
+++ b/uint32.h1
@@ -0,0 +1,6 @@
+#ifndef UINT32_H
+#define UINT32_H
+
+typedef unsigned int uint32;
+
+#endif
diff --git a/uint32.h2 b/uint32.h2
new file mode 100644 (file)
index 0000000..716430d
--- /dev/null
+++ b/uint32.h2
@@ -0,0 +1,6 @@
+#ifndef UINT32_H
+#define UINT32_H
+
+typedef unsigned long uint32;
+
+#endif
diff --git a/wait.h b/wait.h
new file mode 100644 (file)
index 0000000..cdb77c3
--- /dev/null
+++ b/wait.h
@@ -0,0 +1,14 @@
+#ifndef WAIT_H
+#define WAIT_H
+
+extern int wait_pid();
+extern int wait_nohang();
+extern int wait_stop();
+extern int wait_stopnohang();
+
+#define wait_crashed(w) ((w) & 127)
+#define wait_exitcode(w) ((w) >> 8)
+#define wait_stopsig(w) ((w) >> 8)
+#define wait_stopped(w) (((w) & 127) == 127)
+
+#endif
diff --git a/wait_pid.c b/wait_pid.c
new file mode 100644 (file)
index 0000000..d7a7e84
--- /dev/null
@@ -0,0 +1,39 @@
+#include <sys/types.h>
+#include <sys/wait.h>
+#include "error.h"
+#include "haswaitp.h"
+
+#ifdef HASWAITPID
+
+int wait_pid(wstat,pid) int *wstat; int pid;
+{
+  int r;
+
+  do
+    r = waitpid(pid,wstat,0);
+  while ((r == -1) && (errno == error_intr));
+  return r;
+}
+
+#else
+
+/* XXX untested */
+/* XXX breaks down with more than two children */
+static int oldpid = 0;
+static int oldwstat; /* defined if(oldpid) */
+
+int wait_pid(wstat,pid) int *wstat; int pid;
+{
+  int r;
+
+  if (pid == oldpid) { *wstat = oldwstat; oldpid = 0; return pid; }
+
+  do {
+    r = wait(wstat);
+    if ((r != pid) && (r != -1)) { oldwstat = *wstat; oldpid = r; continue; }
+  }
+  while ((r == -1) && (errno == error_intr));
+  return r;
+}
+
+#endif
diff --git a/warn-auto.sh b/warn-auto.sh
new file mode 100644 (file)
index 0000000..36d2313
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+# WARNING: This file was auto-generated. Do not edit!