--- /dev/null
+--- 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.
--- /dev/null
+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.
--- /dev/null
+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.
--- /dev/null
+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
--- /dev/null
+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.
--- /dev/null
+# 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
--- /dev/null
+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)
--- /dev/null
+VERSION
+systype
+hassgact.h
+haswaitp.h
+fork.h
+uint32.h
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+consider other table formats
+test
--- /dev/null
+fastforward 0.51
--- /dev/null
+#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);
+}
--- /dev/null
+#ifndef ALLOC_H
+#define ALLOC_H
+
+extern /*@null@*//*@out@*/char *alloc();
+extern void alloc_free();
+extern int alloc_re();
+
+#endif
--- /dev/null
+#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;
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#ifndef AUTO_QMAIL_H
+#define AUTO_QMAIL_H
+
+extern char auto_qmail[];
+
+#endif
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+ }
+}
--- /dev/null
+#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;
+ }
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#include "cdbmake.h"
+
+uint32 cdbmake_hashadd(h,c)
+uint32 h;
+unsigned int c;
+{
+ h += (h << 5);
+ h ^= (uint32) (unsigned char) c;
+ return h;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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));
+}
--- /dev/null
+#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
--- /dev/null
+#include <fcntl.h>
+#include "coe.h"
+
+int coe(fd)
+int fd;
+{
+ return fcntl(fd,F_SETFD,1);
+}
--- /dev/null
+#ifndef COE_H
+#define COE_H
+
+extern int coe();
+
+#endif
--- /dev/null
+cc -O2
+
+This will be used to compile .c files.
--- /dev/null
+cc -s
+
+This will be used to link .o files into an executable.
--- /dev/null
+/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.
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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";
+}
--- /dev/null
+#ifndef EXIT_H
+#define EXIT_H
+
+extern void _exit();
+
+#endif
--- /dev/null
+.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)
--- /dev/null
+#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);
+}
--- /dev/null
+#ifndef FD_H
+#define FD_H
+
+extern int fd_copy();
+extern int fd_move();
+
+#endif
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+# 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]'
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#ifndef FORK_H
+#define FORK_H
+
+extern int fork();
+#define vfork fork
+
+#endif
--- /dev/null
+#ifndef FORK_H
+#define FORK_H
+
+extern int fork();
+extern int vfork();
+
+#endif
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#ifndef GETLN_H
+#define GETLN_H
+
+extern int getln();
+extern int getln2();
+
+#endif
--- /dev/null
+#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);
+ }
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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);
+}
--- /dev/null
+echo exec "$CC" -c '${1+"$@"}'
--- /dev/null
+echo 'main="$1"; shift'
+echo exec "$LD" '-o "$main" "$main".o ${1+"$@"}'
--- /dev/null
+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
--- /dev/null
+.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)
--- /dev/null
+#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);
+}
--- /dev/null
+.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)
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#include <sys/types.h>
+#include <fcntl.h>
+#include "open.h"
+
+int open_read(fn) char *fn;
+{ return open(fn,O_RDONLY | O_NDELAY); }
--- /dev/null
+#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); }
--- /dev/null
+.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)
--- /dev/null
+#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);
+}
--- /dev/null
+.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)
--- /dev/null
+#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();
+ }
+}
--- /dev/null
+#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)";
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#ifndef READWRITE_H
+#define READWRITE_H
+
+extern int read();
+extern int write();
+
+#endif
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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; }
--- /dev/null
+.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)
--- /dev/null
+#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);
+}
--- /dev/null
+.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)
--- /dev/null
+#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);
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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
+}
--- /dev/null
+#include <signal.h>
+#include "sig.h"
+
+void sig_pipeignore() { sig_catch(SIGPIPE,SIG_IGN); }
+void sig_pipedefault() { sig_catch(SIGPIPE,SIG_DFL); }
--- /dev/null
+#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;
+ }
+}
--- /dev/null
+#ifndef SLURPCLOSE_H
+#define SLURPCLOSE_H
+
+extern int slurpclose();
+
+#endif
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+ }
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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;
+ }
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#include "byte.h"
+#include "stralloc.h"
+
+int stralloc_cat(sato,safrom)
+stralloc *sato;
+stralloc *safrom;
+{
+ return stralloc_catb(sato,safrom->s,safrom->len);
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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));
+}
--- /dev/null
+#include "byte.h"
+#include "stralloc.h"
+
+int stralloc_copy(sato,safrom)
+stralloc *sato;
+stralloc *safrom;
+{
+ return stralloc_copyb(sato,safrom->s,safrom->len);
+}
--- /dev/null
+#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)
--- /dev/null
+#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;
+}
--- /dev/null
+#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));
+}
--- /dev/null
+#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)
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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 = "";
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#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 = ⁢
--- /dev/null
+#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 = ⁢
--- /dev/null
+#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 = ⁢
--- /dev/null
+/* 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 '?';
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+ }
+}
--- /dev/null
+#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));
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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
--- /dev/null
+void main()
+{
+#ifdef NeXT
+ printf("nextstep\n"); exit(0);
+#endif
+ printf("unknown\n"); exit(0);
+}
--- /dev/null
+#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);
+}
--- /dev/null
+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);
+}
--- /dev/null
+void main()
+{
+ vfork();
+}
--- /dev/null
+#include <sys/types.h>
+#include <sys/wait.h>
+
+void main()
+{
+ waitpid(0,0,0);
+}
--- /dev/null
+#ifndef UINT32_H
+#define UINT32_H
+
+typedef unsigned int uint32;
+
+#endif
--- /dev/null
+#ifndef UINT32_H
+#define UINT32_H
+
+typedef unsigned long uint32;
+
+#endif
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#!/bin/sh
+# WARNING: This file was auto-generated. Do not edit!