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