Import ezmlm 0.53 djb djb/0.53
authorMark Wooding <mdw@metalzone.distorted.org.uk>
Tue, 14 Feb 2006 13:41:33 +0000 (13:41 +0000)
committerMark Wooding <mdw@metalzone.distorted.org.uk>
Tue, 14 Feb 2006 13:41:33 +0000 (13:41 +0000)
279 files changed:
BIN [new file with mode: 0644]
BLURB [new file with mode: 0644]
CHANGES [new file with mode: 0644]
FILES [new file with mode: 0644]
INSTALL [new file with mode: 0644]
MAN [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
SYSDEPS [new file with mode: 0644]
THANKS [new file with mode: 0644]
TODO [new file with mode: 0644]
VERSION [new file with mode: 0644]
alloc.3 [new file with mode: 0644]
alloc.c [new file with mode: 0644]
alloc.h [new file with mode: 0644]
alloc=0 [new file with mode: 0644]
alloc=l [new file with mode: 0644]
alloc_re.c [new file with mode: 0644]
auto-str.c [new file with mode: 0644]
auto-str=x [new file with mode: 0644]
auto_bin.c.do [new file with mode: 0644]
auto_bin.h [new file with mode: 0644]
auto_qmail.c.do [new file with mode: 0644]
auto_qmail.h [new file with mode: 0644]
byte.h [new file with mode: 0644]
byte_chr.c [new file with mode: 0644]
byte_copy.c [new file with mode: 0644]
byte_cr.c [new file with mode: 0644]
byte_diff.c [new file with mode: 0644]
byte_rchr.c [new file with mode: 0644]
byte_zero.c [new file with mode: 0644]
case.3 [new file with mode: 0644]
case.h [new file with mode: 0644]
case=0 [new file with mode: 0644]
case=l [new file with mode: 0644]
case_diffb.c [new file with mode: 0644]
case_lowerb.c [new file with mode: 0644]
case_startb.c [new file with mode: 0644]
conf-bin [new file with mode: 0644]
conf-cc [new file with mode: 0644]
conf-ld [new file with mode: 0644]
conf-man [new file with mode: 0644]
conf-qmail [new file with mode: 0644]
constmap.c [new file with mode: 0644]
constmap.h [new file with mode: 0644]
cookie.c [new file with mode: 0644]
cookie.h [new file with mode: 0644]
date822fmt.c [new file with mode: 0644]
date822fmt.h [new file with mode: 0644]
datetime.3 [new file with mode: 0644]
datetime.c [new file with mode: 0644]
datetime.h [new file with mode: 0644]
datetime=0 [new file with mode: 0644]
default.0.do [new file with mode: 0644]
default.a.do [new file with mode: 0644]
default.do [new file with mode: 0644]
default.o.do [new file with mode: 0644]
direntry.3 [new file with mode: 0644]
direntry.h.do [new file with mode: 0644]
direntry.h1 [new file with mode: 0644]
direntry.h2 [new file with mode: 0644]
direntry=0 [new file with mode: 0644]
env.3 [new file with mode: 0644]
env.h [new file with mode: 0644]
env=0 [new file with mode: 0644]
env=l [new file with mode: 0644]
envread.c [new file with mode: 0644]
error.3 [new file with mode: 0644]
error.c [new file with mode: 0644]
error.h [new file with mode: 0644]
error=0 [new file with mode: 0644]
error=l [new file with mode: 0644]
error_str.3 [new file with mode: 0644]
error_str.c [new file with mode: 0644]
error_str=0 [new file with mode: 0644]
error_temp.3 [new file with mode: 0644]
error_temp=0 [new file with mode: 0644]
exit.h [new file with mode: 0644]
ezmlm-list.1 [new file with mode: 0644]
ezmlm-list.c [new file with mode: 0644]
ezmlm-list=0 [new file with mode: 0644]
ezmlm-list=x [new file with mode: 0644]
ezmlm-make.1 [new file with mode: 0644]
ezmlm-make.c [new file with mode: 0644]
ezmlm-make=0 [new file with mode: 0644]
ezmlm-make=x [new file with mode: 0644]
ezmlm-manage.1 [new file with mode: 0644]
ezmlm-manage.c [new file with mode: 0644]
ezmlm-manage=0 [new file with mode: 0644]
ezmlm-manage=x [new file with mode: 0644]
ezmlm-reject.1 [new file with mode: 0644]
ezmlm-reject.c [new file with mode: 0644]
ezmlm-reject=0 [new file with mode: 0644]
ezmlm-reject=x [new file with mode: 0644]
ezmlm-return.1 [new file with mode: 0644]
ezmlm-return.c [new file with mode: 0644]
ezmlm-return=0 [new file with mode: 0644]
ezmlm-return=x [new file with mode: 0644]
ezmlm-send.1 [new file with mode: 0644]
ezmlm-send.c [new file with mode: 0644]
ezmlm-send=0 [new file with mode: 0644]
ezmlm-send=x [new file with mode: 0644]
ezmlm-sub.1 [new file with mode: 0644]
ezmlm-sub.c [new file with mode: 0644]
ezmlm-sub=0 [new file with mode: 0644]
ezmlm-sub=x [new file with mode: 0644]
ezmlm-unsub.1 [new file with mode: 0644]
ezmlm-unsub.c [new file with mode: 0644]
ezmlm-unsub=0 [new file with mode: 0644]
ezmlm-unsub=x [new file with mode: 0644]
ezmlm-warn.1 [new file with mode: 0644]
ezmlm-warn.c [new file with mode: 0644]
ezmlm-warn=0 [new file with mode: 0644]
ezmlm-warn=x [new file with mode: 0644]
ezmlm-weed.1 [new file with mode: 0644]
ezmlm-weed.c [new file with mode: 0644]
ezmlm-weed=0 [new file with mode: 0644]
ezmlm-weed=x [new file with mode: 0644]
ezmlm.5 [new file with mode: 0644]
ezmlm=0 [new file with mode: 0644]
fd.h [new file with mode: 0644]
fd=l [new file with mode: 0644]
fd_copy.3 [new file with mode: 0644]
fd_copy.c [new file with mode: 0644]
fd_copy=0 [new file with mode: 0644]
fd_move.3 [new file with mode: 0644]
fd_move.c [new file with mode: 0644]
fd_move=0 [new file with mode: 0644]
find-systype.sh [new file with mode: 0644]
fmt.h [new file with mode: 0644]
fmt_str.c [new file with mode: 0644]
fmt_uint.c [new file with mode: 0644]
fmt_uint0.c [new file with mode: 0644]
fmt_ulong.c [new file with mode: 0644]
fork.h.do [new file with mode: 0644]
fork.h1 [new file with mode: 0644]
fork.h2 [new file with mode: 0644]
fs=l [new file with mode: 0644]
gen_alloc.h [new file with mode: 0644]
gen_allocdefs.h [new file with mode: 0644]
getconf.c [new file with mode: 0644]
getconf.h [new file with mode: 0644]
getln.3 [new file with mode: 0644]
getln.c [new file with mode: 0644]
getln.h [new file with mode: 0644]
getln2.3 [new file with mode: 0644]
getln2.c [new file with mode: 0644]
getln2=0 [new file with mode: 0644]
getln=0 [new file with mode: 0644]
getln=l [new file with mode: 0644]
getopt.3 [new file with mode: 0644]
getopt=0 [new file with mode: 0644]
getopt=l [new file with mode: 0644]
hasflock.h.do [new file with mode: 0644]
hassgact.h.do [new file with mode: 0644]
install.c [new file with mode: 0644]
install=x [new file with mode: 0644]
issub.c [new file with mode: 0644]
issub.h [new file with mode: 0644]
it.do [new file with mode: 0644]
lock.h [new file with mode: 0644]
lock=l [new file with mode: 0644]
lock_ex.c [new file with mode: 0644]
log.c [new file with mode: 0644]
log.h [new file with mode: 0644]
make-compile.sh [new file with mode: 0644]
make-load.sh [new file with mode: 0644]
make-makelib.sh [new file with mode: 0644]
man.do [new file with mode: 0644]
now.3 [new file with mode: 0644]
now.c [new file with mode: 0644]
now.h [new file with mode: 0644]
now=0 [new file with mode: 0644]
open.h [new file with mode: 0644]
open=l [new file with mode: 0644]
open_append.c [new file with mode: 0644]
open_read.c [new file with mode: 0644]
open_trunc.c [new file with mode: 0644]
qmail.c [new file with mode: 0644]
qmail.h [new file with mode: 0644]
quote.c [new file with mode: 0644]
quote.h [new file with mode: 0644]
readwrite.h [new file with mode: 0644]
scan.h [new file with mode: 0644]
scan_8long.c [new file with mode: 0644]
scan_ulong.c [new file with mode: 0644]
seek.h [new file with mode: 0644]
seek=l [new file with mode: 0644]
seek_set.c [new file with mode: 0644]
setup.do [new file with mode: 0644]
sgetopt.3 [new file with mode: 0644]
sgetopt.c [new file with mode: 0644]
sgetopt.h [new file with mode: 0644]
sgetopt=0 [new file with mode: 0644]
sig.h [new file with mode: 0644]
sig=l [new file with mode: 0644]
sig_catch.c [new file with mode: 0644]
sig_pipe.c [new file with mode: 0644]
slurp.c [new file with mode: 0644]
slurp.h [new file with mode: 0644]
slurpclose.c [new file with mode: 0644]
slurpclose.h [new file with mode: 0644]
str.h [new file with mode: 0644]
str=l [new file with mode: 0644]
str_chr.c [new file with mode: 0644]
str_cpy.c [new file with mode: 0644]
str_diff.c [new file with mode: 0644]
str_diffn.c [new file with mode: 0644]
str_len.c [new file with mode: 0644]
str_rchr.c [new file with mode: 0644]
str_start.c [new file with mode: 0644]
stralloc.3 [new file with mode: 0644]
stralloc.h [new file with mode: 0644]
stralloc=0 [new file with mode: 0644]
stralloc=l [new file with mode: 0644]
stralloc_arts.c [new file with mode: 0644]
stralloc_cat.c [new file with mode: 0644]
stralloc_catb.c [new file with mode: 0644]
stralloc_cats.c [new file with mode: 0644]
stralloc_copy.c [new file with mode: 0644]
stralloc_eady.c [new file with mode: 0644]
stralloc_opyb.c [new file with mode: 0644]
stralloc_opys.c [new file with mode: 0644]
stralloc_pend.c [new file with mode: 0644]
strerr.c [new file with mode: 0644]
strerr.h [new file with mode: 0644]
strerr=l [new file with mode: 0644]
strerr_die.c [new file with mode: 0644]
strerr_sys.c [new file with mode: 0644]
subfd.3 [new file with mode: 0644]
subfd.h [new file with mode: 0644]
subfd=0 [new file with mode: 0644]
subfderr.c [new file with mode: 0644]
subgetopt.3 [new file with mode: 0644]
subgetopt.c [new file with mode: 0644]
subgetopt.h [new file with mode: 0644]
subgetopt=0 [new file with mode: 0644]
subscribe.c [new file with mode: 0644]
subscribe.h [new file with mode: 0644]
substdi.c [new file with mode: 0644]
substdio.3 [new file with mode: 0644]
substdio.c [new file with mode: 0644]
substdio.h [new file with mode: 0644]
substdio=0 [new file with mode: 0644]
substdio=l [new file with mode: 0644]
substdio_copy.3 [new file with mode: 0644]
substdio_copy.c [new file with mode: 0644]
substdio_copy=0 [new file with mode: 0644]
substdio_in.3 [new file with mode: 0644]
substdio_in=0 [new file with mode: 0644]
substdio_out.3 [new file with mode: 0644]
substdio_out=0 [new file with mode: 0644]
substdo.c [new file with mode: 0644]
surf.3 [new file with mode: 0644]
surf.c [new file with mode: 0644]
surf.h [new file with mode: 0644]
surf=0 [new file with mode: 0644]
surf=l [new file with mode: 0644]
surfpcs.3 [new file with mode: 0644]
surfpcs.c [new file with mode: 0644]
surfpcs.h [new file with mode: 0644]
surfpcs=0 [new file with mode: 0644]
targets.do [new file with mode: 0644]
trycpp.c [new file with mode: 0644]
trydrent.c [new file with mode: 0644]
tryflock.c [new file with mode: 0644]
trysgact.c [new file with mode: 0644]
tryulong32.c [new file with mode: 0644]
tryvfork.c [new file with mode: 0644]
trywaitp.c [new file with mode: 0644]
uint32.h.do [new file with mode: 0644]
uint32.h1 [new file with mode: 0644]
uint32.h2 [new file with mode: 0644]
wait.3 [new file with mode: 0644]
wait.h [new file with mode: 0644]
wait=0 [new file with mode: 0644]
wait=l [new file with mode: 0644]
wait_pid.c [new file with mode: 0644]
warn-auto.sh [new file with mode: 0644]

diff --git a/BIN b/BIN
new file mode 100644 (file)
index 0000000..76f0320
--- /dev/null
+++ b/BIN
@@ -0,0 +1,11 @@
+d:::755:::
+c:::755:/:ezmlm-make:
+c:::755:/:ezmlm-manage:
+c:::755:/:ezmlm-send:
+c:::755:/:ezmlm-reject:
+c:::755:/:ezmlm-return:
+c:::755:/:ezmlm-warn:
+c:::755:/:ezmlm-weed:
+c:::755:/:ezmlm-list:
+c:::755:/:ezmlm-sub:
+c:::755:/:ezmlm-unsub:
diff --git a/BLURB b/BLURB
new file mode 100644 (file)
index 0000000..a9ca479
--- /dev/null
+++ b/BLURB
@@ -0,0 +1,50 @@
+ezmlm is an easy-to-use, high-speed mailing list manager for qmail.
+
+ezmlm lets users set up their own mailing lists within qmail's address
+hierarchy. A user, Joe, types
+
+   ezmlm-make ~/SOS ~/.qmail-sos joe-sos isp.net
+
+and instantly has a functioning mailing list, joe-sos@isp.net, with all
+relevant information stored in a new ~/SOS directory.
+
+ezmlm sets up joe-sos-subscribe and joe-sos-unsubscribe for automatic
+processing of subscription and unsubscription requests. Any message to
+joe-sos-subscribe will work; Joe doesn't have to explain any tricky
+command formats. ezmlm will send back instructions if a subscriber sends
+a message to joe-sos-request or joe-sos-help.
+
+ezmlm automatically archives new messages. Messages are labelled with
+sequence numbers; a subscriber can fetch message 123 by sending mail to
+joe-sos-get.123. The archive format supports fast message retrieval even
+when there are thousands of messages.
+
+ezmlm takes advantage of qmail's VERPs to reliably determine the
+recipient address and message number for every incoming bounce message.
+It waits ten days and then sends the subscriber a list of message
+numbers that bounced. If that warning bounces, ezmlm sends a probe; if
+the probe bounces, ezmlm automatically removes the subscriber from the
+mailing list.
+
+ezmlm is easy for users to control. Joe can edit ~/SOS/text/* to change
+any of the administrative messages sent to subscribers. He can remove
+~/SOS/public and ~/SOS/archived to disable automatic subscription and
+archiving. He can put his own address into ~/SOS/editor to set up a
+moderated mailing list. He can edit ~/SOS/{headeradd,headerremove} to
+control outgoing headers. ezmlm has several utilities to manually
+inspect and manage mailing lists.
+
+ezmlm uses Delivered-To to stop forwarding loops, Mailing-List to
+protect other mailing lists against false subscription requests, and
+real cryptographic cookies to protect normal users against false
+subscription requests. ezmlm can also be used for a sublist,
+redistributing messages from another list.
+
+ezmlm is reliable, even in the face of system crashes. It writes each
+new subscription and each new message safely to disk before it reports
+success to qmail.
+
+ezmlm doesn't mind huge mailing lists. Lists don't even have to fit into
+memory. ezmlm hashes the subscription list into a set of independent
+files so that it can handle subscription requests quickly. ezmlm uses
+qmail for blazingly fast parallel SMTP deliveries.
diff --git a/CHANGES b/CHANGES
new file mode 100644 (file)
index 0000000..4badf63
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,4 @@
+19970629 ezmlm 0.53, alpha.
+19970406 ezmlm 0.52, alpha.
+19970222 ezmlm 0.51, alpha.
+19970203 ezmlm 0.50, alpha.
diff --git a/FILES b/FILES
new file mode 100644 (file)
index 0000000..d3ae087
--- /dev/null
+++ b/FILES
@@ -0,0 +1,279 @@
+BLURB
+README
+INSTALL
+TODO
+THANKS
+CHANGES
+FILES
+BIN
+MAN
+VERSION
+SYSDEPS
+Makefile
+ezmlm=0
+ezmlm.5
+ezmlm-make=0
+ezmlm-make.1
+ezmlm-make=x
+ezmlm-make.c
+ezmlm-send=0
+ezmlm-send.1
+ezmlm-send=x
+ezmlm-send.c
+ezmlm-reject=0
+ezmlm-reject.1
+ezmlm-reject=x
+ezmlm-reject.c
+ezmlm-list=0
+ezmlm-list.1
+ezmlm-list=x
+ezmlm-list.c
+ezmlm-sub=0
+ezmlm-sub.1
+ezmlm-sub=x
+ezmlm-sub.c
+ezmlm-unsub=0
+ezmlm-unsub.1
+ezmlm-unsub=x
+ezmlm-unsub.c
+ezmlm-manage=0
+ezmlm-manage.1
+ezmlm-manage=x
+ezmlm-manage.c
+ezmlm-return=0
+ezmlm-return.1
+ezmlm-return=x
+ezmlm-return.c
+ezmlm-warn=0
+ezmlm-warn.1
+ezmlm-warn=x
+ezmlm-warn.c
+ezmlm-weed=0
+ezmlm-weed.1
+ezmlm-weed=x
+ezmlm-weed.c
+getconf.h
+getconf.c
+log.h
+log.c
+issub.h
+issub.c
+subscribe.h
+subscribe.c
+cookie.h
+cookie.c
+auto-str=x
+auto-str.c
+conf-bin
+auto_bin.c.do
+auto_bin.h
+conf-man
+install=x
+install.c
+setup.do
+man.do
+it.do
+targets.do
+default.do
+default.a.do
+default.o.do
+default.0.do
+conf-cc
+conf-ld
+find-systype.sh
+make-compile.sh
+make-load.sh
+make-makelib.sh
+trycpp.c
+warn-auto.sh
+fork.h.do
+fork.h1
+fork.h2
+tryvfork.c
+wait.3
+wait=0
+wait=l
+wait.h
+wait_pid.c
+trywaitp.c
+error.3
+error=0
+error_str.3
+error_str=0
+error_temp.3
+error_temp=0
+error=l
+error.h
+error.c
+error_str.c
+substdio.3
+substdio=0
+substdio_copy.3
+substdio_copy=0
+substdio_in.3
+substdio_in=0
+substdio_out.3
+substdio_out=0
+substdio=l
+substdio.h
+substdio.c
+substdi.c
+substdo.c
+substdio_copy.c
+subfd.3
+subfd=0
+subfd.h
+subfderr.c
+readwrite.h
+exit.h
+str=l
+byte.h
+byte_chr.c
+byte_copy.c
+byte_cr.c
+byte_diff.c
+byte_rchr.c
+byte_zero.c
+str.h
+str_chr.c
+str_cpy.c
+str_diff.c
+str_diffn.c
+str_len.c
+str_rchr.c
+str_start.c
+getopt.3
+getopt=0
+sgetopt.3
+sgetopt=0
+subgetopt.3
+subgetopt=0
+getopt=l
+sgetopt.h
+sgetopt.c
+subgetopt.h
+subgetopt.c
+strerr=l
+strerr.h
+strerr.c
+strerr_sys.c
+strerr_die.c
+stralloc=l
+gen_alloc.h
+gen_allocdefs.h
+stralloc.3
+stralloc=0
+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
+alloc=0
+alloc.3
+alloc=l
+alloc.h
+alloc.c
+alloc_re.c
+open=l
+open.h
+open_append.c
+open_read.c
+open_trunc.c
+uint32.h.do
+uint32.h1
+uint32.h2
+tryulong32.c
+case.3
+case=0
+case=l
+case.h
+case_diffb.c
+case_lowerb.c
+case_startb.c
+fs=l
+fmt.h
+fmt_str.c
+fmt_uint.c
+fmt_uint0.c
+fmt_ulong.c
+scan.h
+scan_ulong.c
+scan_8long.c
+lock=l
+hasflock.h.do
+lock.h
+lock_ex.c
+tryflock.c
+env.3
+env=0
+env=l
+env.h
+envread.c
+slurpclose.h
+slurpclose.c
+sig=l
+sig.h
+sig_catch.c
+sig_pipe.c
+hassgact.h.do
+trysgact.c
+datetime.3
+datetime=0
+datetime.h
+datetime.c
+date822fmt.h
+date822fmt.c
+now.3
+now=0
+now.h
+now.c
+quote.h
+quote.c
+seek=l
+seek.h
+seek_set.c
+conf-qmail
+auto_qmail.c.do
+auto_qmail.h
+qmail.h
+qmail.c
+direntry.3
+direntry=0
+direntry.h.do
+direntry.h1
+direntry.h2
+trydrent.c
+getln=0
+getln2=0
+getln=l
+getln.3
+getln.h
+getln.c
+getln2.3
+getln2.c
+fd=l
+fd_copy=0
+fd_move=0
+fd.h
+fd_copy.3
+fd_copy.c
+fd_move.3
+fd_move.c
+surf=0
+surfpcs=0
+surf=l
+surf.3
+surf.h
+surf.c
+surfpcs.3
+surfpcs.h
+surfpcs.c
+slurp.h
+slurp.c
+constmap.h
+constmap.c
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..a85e9cb
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,52 @@
+Like any other piece of software (and information generally), ezmlm
+comes with NO WARRANTY.
+
+
+Things you have to decide before starting:
+
+* Where programs will be installed, normally /usr/local/bin/ezmlm. To
+change this directory, edit conf-bin now.
+
+* Where man pages will be installed, normally /usr/local/man. To change
+this directory, edit conf-man now.
+
+* Where the qmail home directory is, normally /var/qmail. To change this
+directory, edit conf-qmail now.
+
+
+How to install:
+
+ 1. Compile the programs:
+       % make
+ 2. Format the man pages:
+       % make man
+ 3. Install the programs and man pages:
+       # make setup
+
+
+How to test:
+
+ 4. Make sure ezmlm-make is in your path. Create a mailing list:
+       % ezmlm-make ~/testlist ~/.qmail-testlist me-testlist host
+    Replace ``me'' and ``host'' with your e-mail address.
+ 5. Subscribe yourself to the list manually:
+       % ezmlm-sub ~/testlist me@host
+ 6. Send a message to the list:
+       % echo subject:testing | qmail-inject me-testlist@host
+    You should receive a copy of the message at me@host.
+ 7. View the list membership:
+       % ezmlm-list ~/testlist
+    You should see just one line, containing your address.
+ 8. Unsubscribe yourself through e-mail:
+       % qmail-inject me-testlist-unsubscribe@host < /dev/null
+    When you receive the confirmation number, reply to complete your
+    unsubscription. Use ezmlm-list to check that the list is empty.
+ 9. Retrieve the first message from the archive:
+       % qmail-inject me-testlist-get.1@host < /dev/null
+    You should receive a copy of your subject:testing message.
+
+
+That's it! To report success:
+       % ( echo 'First M. Last'; cat `cat SYSDEPS` ) \
+         | mail djb-qst@koobera.math.uic.edu
+Replace First M. Last with your name.
diff --git a/MAN b/MAN
new file mode 100644 (file)
index 0000000..df7b2a7
--- /dev/null
+++ b/MAN
@@ -0,0 +1,27 @@
+d:::755:::
+d:::755:/man1::
+d:::755:/man5::
+c:::644:/man5/:ezmlm.5:
+c:::644:/man1/:ezmlm-list.1:
+c:::644:/man1/:ezmlm-make.1:
+c:::644:/man1/:ezmlm-manage.1:
+c:::644:/man1/:ezmlm-reject.1:
+c:::644:/man1/:ezmlm-return.1:
+c:::644:/man1/:ezmlm-send.1:
+c:::644:/man1/:ezmlm-sub.1:
+c:::644:/man1/:ezmlm-unsub.1:
+c:::644:/man1/:ezmlm-warn.1:
+c:::644:/man1/:ezmlm-weed.1:
+d:::755:/cat1::
+d:::755:/cat5::
+c:::644:/cat5/:ezmlm.0:
+c:::644:/cat1/:ezmlm-list.0:
+c:::644:/cat1/:ezmlm-make.0:
+c:::644:/cat1/:ezmlm-manage.0:
+c:::644:/cat1/:ezmlm-reject.0:
+c:::644:/cat1/:ezmlm-return.0:
+c:::644:/cat1/:ezmlm-send.0:
+c:::644:/cat1/:ezmlm-sub.0:
+c:::644:/cat1/:ezmlm-unsub.0:
+c:::644:/cat1/:ezmlm-warn.0:
+c:::644:/cat1/:ezmlm-weed.0:
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..111a542
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,909 @@
+SHELL=/bin/sh
+
+default: it
+
+alloc.0: \
+alloc.3
+       nroff -man alloc.3 > alloc.0
+
+alloc.a: \
+makelib alloc.o alloc_re.o
+       ./makelib alloc.a alloc.o alloc_re.o
+
+alloc.o: \
+compile alloc.c alloc.h alloc.c error.h alloc.c
+       ./compile alloc.c
+
+alloc_re.o: \
+compile alloc_re.c alloc.h alloc_re.c byte.h alloc_re.c
+       ./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 auto-str.c readwrite.h auto-str.c \
+exit.h auto-str.c
+       ./compile auto-str.c
+
+auto_bin.c: \
+auto-str conf-bin
+       ./auto-str auto_bin `head -1 conf-bin` > auto_bin.c
+
+auto_bin.o: \
+compile auto_bin.c
+       ./compile auto_bin.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 byte_chr.c
+       ./compile byte_chr.c
+
+byte_copy.o: \
+compile byte_copy.c byte.h byte_copy.c
+       ./compile byte_copy.c
+
+byte_cr.o: \
+compile byte_cr.c byte.h byte_cr.c
+       ./compile byte_cr.c
+
+byte_diff.o: \
+compile byte_diff.c byte.h byte_diff.c
+       ./compile byte_diff.c
+
+byte_rchr.o: \
+compile byte_rchr.c byte.h byte_rchr.c
+       ./compile byte_rchr.c
+
+byte_zero.o: \
+compile byte_zero.c byte.h byte_zero.c
+       ./compile byte_zero.c
+
+case.0: \
+case.3
+       nroff -man case.3 > case.0
+
+case.a: \
+makelib case_diffb.o case_lowerb.o case_startb.o
+       ./makelib case.a case_diffb.o case_lowerb.o case_startb.o
+
+case_diffb.o: \
+compile case_diffb.c case.h case_diffb.c
+       ./compile case_diffb.c
+
+case_lowerb.o: \
+compile case_lowerb.c case.h case_lowerb.c
+       ./compile case_lowerb.c
+
+case_startb.o: \
+compile case_startb.c case.h case_startb.c
+       ./compile case_startb.c
+
+compile: \
+make-compile warn-auto.sh systype
+       ( cat warn-auto.sh; ./make-compile "`cat systype`" ) > \
+       compile
+       chmod 755 compile
+
+constmap.o: \
+compile constmap.c constmap.h constmap.c alloc.h constmap.c case.h \
+constmap.c
+       ./compile constmap.c
+
+cookie.o: \
+compile cookie.c cookie.h cookie.c str.h cookie.c uint32.h cookie.c \
+surfpcs.h uint32.h surfpcs.h cookie.c
+       ./compile cookie.c
+
+date822fmt.o: \
+compile date822fmt.c datetime.h date822fmt.c fmt.h date822fmt.c \
+date822fmt.h date822fmt.c
+       ./compile date822fmt.c
+
+datetime.0: \
+datetime.3
+       nroff -man datetime.3 > datetime.0
+
+datetime.o: \
+compile datetime.c datetime.h datetime.c
+       ./compile datetime.c
+
+direntry.0: \
+direntry.3
+       nroff -man direntry.3 > direntry.0
+
+direntry.h: \
+compile trydrent.c direntry.h1 direntry.h2
+       ( ./compile trydrent.c >/dev/null 2>&1 \
+       && cat direntry.h2 || cat direntry.h1 ) > direntry.h
+       rm -f trydrent.o
+
+env.0: \
+env.3
+       nroff -man env.3 > env.0
+
+env.a: \
+makelib envread.o
+       ./makelib env.a envread.o
+
+envread.o: \
+compile envread.c env.h envread.c str.h envread.c
+       ./compile envread.c
+
+error.0: \
+error.3
+       nroff -man error.3 > error.0
+
+error.a: \
+makelib error.o error_str.o
+       ./makelib error.a error.o error_str.o
+
+error.o: \
+compile error.c error.c error.h error.c
+       ./compile error.c
+
+error_str.0: \
+error_str.3
+       nroff -man error_str.3 > error_str.0
+
+error_str.o: \
+compile error_str.c error_str.c error.h error_str.c
+       ./compile error_str.c
+
+error_temp.0: \
+error_temp.3
+       nroff -man error_temp.3 > error_temp.0
+
+ezmlm-list: \
+load ezmlm-list.o strerr.a getln.a substdio.a stralloc.a alloc.a \
+error.a open.a str.a
+       ./load ezmlm-list strerr.a getln.a substdio.a stralloc.a \
+       alloc.a error.a open.a str.a 
+
+ezmlm-list.0: \
+ezmlm-list.1
+       nroff -man ezmlm-list.1 > ezmlm-list.0
+
+ezmlm-list.o: \
+compile ezmlm-list.c stralloc.h gen_alloc.h stralloc.h ezmlm-list.c \
+substdio.h ezmlm-list.c getln.h ezmlm-list.c strerr.h ezmlm-list.c \
+error.h ezmlm-list.c readwrite.h ezmlm-list.c exit.h ezmlm-list.c \
+open.h ezmlm-list.c
+       ./compile ezmlm-list.c
+
+ezmlm-make: \
+load ezmlm-make.o auto_bin.o open.a getopt.a substdio.a strerr.a \
+stralloc.a alloc.a error.a str.a
+       ./load ezmlm-make auto_bin.o open.a getopt.a substdio.a \
+       strerr.a stralloc.a alloc.a error.a str.a 
+
+ezmlm-make.0: \
+ezmlm-make.1
+       nroff -man ezmlm-make.1 > ezmlm-make.0
+
+ezmlm-make.o: \
+compile ezmlm-make.c ezmlm-make.c ezmlm-make.c sgetopt.h subgetopt.h \
+sgetopt.h ezmlm-make.c stralloc.h gen_alloc.h stralloc.h ezmlm-make.c \
+strerr.h ezmlm-make.c exit.h ezmlm-make.c readwrite.h ezmlm-make.c \
+open.h ezmlm-make.c substdio.h ezmlm-make.c str.h ezmlm-make.c \
+auto_bin.h ezmlm-make.c ezmlm-make.c ezmlm-make.c ezmlm-make.c \
+ezmlm-make.c
+       ./compile ezmlm-make.c
+
+ezmlm-manage: \
+load ezmlm-manage.o auto_qmail.o getconf.o subscribe.o log.o cookie.o \
+now.o datetime.o date822fmt.o slurpclose.o slurp.o qmail.o quote.o \
+surf.a getln.a env.a sig.a strerr.a substdio.a stralloc.a alloc.a \
+error.a str.a fs.a case.a open.a seek.a wait.a lock.a fd.a
+       ./load ezmlm-manage auto_qmail.o getconf.o subscribe.o \
+       log.o cookie.o now.o datetime.o date822fmt.o slurpclose.o \
+       slurp.o qmail.o quote.o surf.a getln.a env.a sig.a strerr.a \
+       substdio.a stralloc.a alloc.a error.a str.a fs.a case.a \
+       open.a seek.a wait.a lock.a fd.a 
+
+ezmlm-manage.0: \
+ezmlm-manage.1
+       nroff -man ezmlm-manage.1 > ezmlm-manage.0
+
+ezmlm-manage.o: \
+compile ezmlm-manage.c ezmlm-manage.c ezmlm-manage.c error.h \
+ezmlm-manage.c stralloc.h gen_alloc.h stralloc.h ezmlm-manage.c str.h \
+ezmlm-manage.c env.h ezmlm-manage.c sig.h ezmlm-manage.c slurp.h \
+ezmlm-manage.c getconf.h ezmlm-manage.c strerr.h ezmlm-manage.c \
+byte.h ezmlm-manage.c getln.h ezmlm-manage.c case.h ezmlm-manage.c \
+qmail.h substdio.h qmail.h ezmlm-manage.c substdio.h substdio.h \
+ezmlm-manage.c readwrite.h ezmlm-manage.c seek.h ezmlm-manage.c \
+quote.h ezmlm-manage.c datetime.h ezmlm-manage.c now.h datetime.h \
+datetime.h now.h ezmlm-manage.c date822fmt.h ezmlm-manage.c fmt.h \
+ezmlm-manage.c subscribe.h strerr.h strerr.h subscribe.h \
+ezmlm-manage.c cookie.h ezmlm-manage.c
+       ./compile ezmlm-manage.c
+
+ezmlm-reject: \
+load ezmlm-reject.o getln.a strerr.a substdio.a error.a stralloc.a \
+alloc.a str.a getopt.a case.a
+       ./load ezmlm-reject getln.a strerr.a substdio.a error.a \
+       stralloc.a alloc.a str.a getopt.a case.a 
+
+ezmlm-reject.0: \
+ezmlm-reject.1
+       nroff -man ezmlm-reject.1 > ezmlm-reject.0
+
+ezmlm-reject.o: \
+compile ezmlm-reject.c strerr.h ezmlm-reject.c substdio.h \
+ezmlm-reject.c readwrite.h ezmlm-reject.c stralloc.h gen_alloc.h \
+stralloc.h ezmlm-reject.c getln.h ezmlm-reject.c sgetopt.h \
+subgetopt.h sgetopt.h ezmlm-reject.c
+       ./compile ezmlm-reject.c
+
+ezmlm-return: \
+load ezmlm-return.o quote.o getconf.o issub.o subscribe.o log.o \
+slurpclose.o slurp.o now.o cookie.o surf.a lock.a env.a sig.a \
+strerr.a getln.a substdio.a stralloc.a alloc.a error.a str.a fs.a \
+case.a open.a
+       ./load ezmlm-return quote.o getconf.o issub.o subscribe.o \
+       log.o slurpclose.o slurp.o now.o cookie.o surf.a lock.a \
+       env.a sig.a strerr.a getln.a substdio.a stralloc.a alloc.a \
+       error.a str.a fs.a case.a open.a 
+
+ezmlm-return.0: \
+ezmlm-return.1
+       nroff -man ezmlm-return.1 > ezmlm-return.0
+
+ezmlm-return.o: \
+compile ezmlm-return.c stralloc.h gen_alloc.h stralloc.h \
+ezmlm-return.c str.h ezmlm-return.c env.h ezmlm-return.c sig.h \
+ezmlm-return.c slurp.h ezmlm-return.c getconf.h ezmlm-return.c \
+strerr.h ezmlm-return.c byte.h ezmlm-return.c case.h ezmlm-return.c \
+getln.h ezmlm-return.c substdio.h ezmlm-return.c error.h \
+ezmlm-return.c quote.h ezmlm-return.c readwrite.h ezmlm-return.c \
+fmt.h ezmlm-return.c now.h datetime.h now.h ezmlm-return.c cookie.h \
+ezmlm-return.c subscribe.h strerr.h strerr.h subscribe.h \
+ezmlm-return.c issub.h strerr.h strerr.h issub.h ezmlm-return.c
+       ./compile ezmlm-return.c
+
+ezmlm-send: \
+load ezmlm-send.o auto_qmail.o getconf.o qmail.o constmap.o slurp.o \
+slurpclose.o wait.a getln.a strerr.a sig.a env.a open.a lock.a \
+substdio.a stralloc.a alloc.a error.a str.a fd.a case.a fs.a
+       ./load ezmlm-send auto_qmail.o getconf.o qmail.o \
+       constmap.o slurp.o slurpclose.o wait.a getln.a strerr.a \
+       sig.a env.a open.a lock.a substdio.a stralloc.a alloc.a \
+       error.a str.a fd.a case.a fs.a 
+
+ezmlm-send.0: \
+ezmlm-send.1
+       nroff -man ezmlm-send.1 > ezmlm-send.0
+
+ezmlm-send.o: \
+compile ezmlm-send.c stralloc.h gen_alloc.h stralloc.h ezmlm-send.c \
+subfd.h substdio.h subfd.h ezmlm-send.c strerr.h ezmlm-send.c error.h \
+ezmlm-send.c qmail.h substdio.h substdio.h qmail.h ezmlm-send.c env.h \
+ezmlm-send.c lock.h ezmlm-send.c sig.h ezmlm-send.c open.h \
+ezmlm-send.c getln.h ezmlm-send.c case.h ezmlm-send.c scan.h \
+ezmlm-send.c str.h ezmlm-send.c fmt.h ezmlm-send.c readwrite.h \
+ezmlm-send.c exit.h ezmlm-send.c substdio.h substdio.h ezmlm-send.c \
+getconf.h ezmlm-send.c constmap.h ezmlm-send.c
+       ./compile ezmlm-send.c
+
+ezmlm-sub: \
+load ezmlm-sub.o subscribe.o log.o now.o fs.a strerr.a getln.a \
+substdio.a stralloc.a alloc.a error.a str.a case.a open.a lock.a
+       ./load ezmlm-sub subscribe.o log.o now.o fs.a strerr.a \
+       getln.a substdio.a stralloc.a alloc.a error.a str.a case.a \
+       open.a lock.a 
+
+ezmlm-sub.0: \
+ezmlm-sub.1
+       nroff -man ezmlm-sub.1 > ezmlm-sub.0
+
+ezmlm-sub.o: \
+compile ezmlm-sub.c strerr.h ezmlm-sub.c subscribe.h strerr.h \
+strerr.h subscribe.h ezmlm-sub.c log.h ezmlm-sub.c
+       ./compile ezmlm-sub.c
+
+ezmlm-unsub: \
+load ezmlm-unsub.o subscribe.o log.o now.o fs.a strerr.a getln.a \
+substdio.a stralloc.a alloc.a error.a str.a case.a open.a lock.a
+       ./load ezmlm-unsub subscribe.o log.o now.o fs.a strerr.a \
+       getln.a substdio.a stralloc.a alloc.a error.a str.a case.a \
+       open.a lock.a 
+
+ezmlm-unsub.0: \
+ezmlm-unsub.1
+       nroff -man ezmlm-unsub.1 > ezmlm-unsub.0
+
+ezmlm-unsub.o: \
+compile ezmlm-unsub.c strerr.h ezmlm-unsub.c subscribe.h strerr.h \
+strerr.h subscribe.h ezmlm-unsub.c log.h ezmlm-unsub.c
+       ./compile ezmlm-unsub.c
+
+ezmlm-warn: \
+load ezmlm-warn.o auto_qmail.o getconf.o cookie.o issub.o now.o \
+slurpclose.o slurp.o quote.o datetime.o date822fmt.o qmail.o surf.a \
+case.a strerr.a sig.a getln.a substdio.a stralloc.a alloc.a error.a \
+open.a lock.a str.a fs.a fd.a wait.a
+       ./load ezmlm-warn auto_qmail.o getconf.o cookie.o issub.o \
+       now.o slurpclose.o slurp.o quote.o datetime.o date822fmt.o \
+       qmail.o surf.a case.a strerr.a sig.a getln.a substdio.a \
+       stralloc.a alloc.a error.a open.a lock.a str.a fs.a fd.a \
+       wait.a 
+
+ezmlm-warn.0: \
+ezmlm-warn.1
+       nroff -man ezmlm-warn.1 > ezmlm-warn.0
+
+ezmlm-warn.o: \
+compile ezmlm-warn.c ezmlm-warn.c ezmlm-warn.c direntry.h direntry.h \
+direntry.h ezmlm-warn.c readwrite.h ezmlm-warn.c getln.h ezmlm-warn.c \
+substdio.h ezmlm-warn.c stralloc.h gen_alloc.h stralloc.h \
+ezmlm-warn.c slurp.h ezmlm-warn.c getconf.h ezmlm-warn.c byte.h \
+ezmlm-warn.c error.h ezmlm-warn.c str.h ezmlm-warn.c strerr.h \
+ezmlm-warn.c sig.h ezmlm-warn.c now.h datetime.h now.h ezmlm-warn.c \
+datetime.h datetime.h ezmlm-warn.c date822fmt.h ezmlm-warn.c fmt.h \
+ezmlm-warn.c cookie.h ezmlm-warn.c qmail.h substdio.h substdio.h \
+qmail.h ezmlm-warn.c
+       ./compile ezmlm-warn.c
+
+ezmlm-weed: \
+load ezmlm-weed.o getln.a strerr.a substdio.a error.a stralloc.a \
+alloc.a str.a
+       ./load ezmlm-weed getln.a strerr.a substdio.a error.a \
+       stralloc.a alloc.a str.a 
+
+ezmlm-weed.0: \
+ezmlm-weed.1
+       nroff -man ezmlm-weed.1 > ezmlm-weed.0
+
+ezmlm-weed.o: \
+compile ezmlm-weed.c stralloc.h gen_alloc.h stralloc.h ezmlm-weed.c \
+str.h ezmlm-weed.c byte.h ezmlm-weed.c readwrite.h ezmlm-weed.c \
+substdio.h ezmlm-weed.c getln.h ezmlm-weed.c strerr.h ezmlm-weed.c
+       ./compile ezmlm-weed.c
+
+ezmlm.0: \
+ezmlm.5
+       nroff -man ezmlm.5 > ezmlm.0
+
+fd.a: \
+makelib fd_copy.o fd_move.o
+       ./makelib fd.a fd_copy.o fd_move.o
+
+fd_copy.0: \
+fd_copy.3
+       nroff -man fd_copy.3 > fd_copy.0
+
+fd_copy.o: \
+compile fd_copy.c fd_copy.c fd.h fd_copy.c
+       ./compile fd_copy.c
+
+fd_move.0: \
+fd_move.3
+       nroff -man fd_move.3 > fd_move.0
+
+fd_move.o: \
+compile fd_move.c fd.h fd_move.c
+       ./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_str.o: \
+compile fmt_str.c fmt.h fmt_str.c
+       ./compile fmt_str.c
+
+fmt_uint.o: \
+compile fmt_uint.c fmt.h fmt_uint.c
+       ./compile fmt_uint.c
+
+fmt_uint0.o: \
+compile fmt_uint0.c fmt.h fmt_uint0.c
+       ./compile fmt_uint0.c
+
+fmt_ulong.o: \
+compile fmt_ulong.c fmt.h fmt_ulong.c
+       ./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_str.o fmt_uint.o fmt_uint0.o fmt_ulong.o scan_ulong.o \
+scan_8long.o
+       ./makelib fs.a fmt_str.o fmt_uint.o fmt_uint0.o \
+       fmt_ulong.o scan_ulong.o scan_8long.o
+
+getconf.o: \
+compile getconf.c stralloc.h gen_alloc.h stralloc.h getconf.c slurp.h \
+getconf.c strerr.h getconf.c getconf.h getconf.c
+       ./compile getconf.c
+
+getln.0: \
+getln.3
+       nroff -man getln.3 > getln.0
+
+getln.a: \
+makelib getln.o getln2.o
+       ./makelib getln.a getln.o getln2.o
+
+getln.o: \
+compile getln.c substdio.h getln.c byte.h getln.c stralloc.h \
+gen_alloc.h stralloc.h getln.c getln.h getln.c
+       ./compile getln.c
+
+getln2.0: \
+getln2.3
+       nroff -man getln2.3 > getln2.0
+
+getln2.o: \
+compile getln2.c substdio.h getln2.c stralloc.h gen_alloc.h \
+stralloc.h getln2.c byte.h getln2.c getln.h getln2.c
+       ./compile getln2.c
+
+getopt.0: \
+getopt.3
+       nroff -man getopt.3 > getopt.0
+
+getopt.a: \
+makelib subgetopt.o sgetopt.o
+       ./makelib getopt.a subgetopt.o sgetopt.o
+
+hasflock.h: \
+tryflock.c compile load
+       ( ( ./compile tryflock.c && ./load tryflock ) >/dev/null \
+       2>&1 \
+       && echo \#define HASFLOCK 1 || exit 0 ) > hasflock.h
+       rm -f tryflock.o tryflock
+
+hassgact.h: \
+trysgact.c compile load
+       ( ( ./compile trysgact.c && ./load trysgact ) >/dev/null \
+       2>&1 \
+       && echo \#define HASSIGACTION 1 || exit 0 ) > hassgact.h
+       rm -f trysgact.o trysgact
+
+install: \
+load install.o getln.a strerr.a substdio.a stralloc.a alloc.a open.a \
+error.a str.a fs.a
+       ./load install getln.a strerr.a substdio.a stralloc.a \
+       alloc.a open.a error.a str.a fs.a 
+
+install.o: \
+compile install.c substdio.h install.c stralloc.h gen_alloc.h \
+stralloc.h install.c getln.h install.c readwrite.h install.c exit.h \
+install.c open.h install.c error.h install.c strerr.h install.c \
+byte.h install.c
+       ./compile install.c
+
+issub.o: \
+compile issub.c stralloc.h gen_alloc.h stralloc.h issub.c getln.h \
+issub.c readwrite.h issub.c substdio.h issub.c open.h issub.c byte.h \
+issub.c case.h issub.c lock.h issub.c error.h issub.c issub.h \
+strerr.h issub.h issub.c uint32.h issub.c
+       ./compile issub.c
+
+it: \
+ezmlm-make ezmlm-manage ezmlm-send ezmlm-reject ezmlm-return \
+ezmlm-warn ezmlm-weed ezmlm-list ezmlm-sub ezmlm-unsub
+
+load: \
+make-load warn-auto.sh systype
+       ( cat warn-auto.sh; ./make-load "`cat systype`" ) > load
+       chmod 755 load
+
+lock.a: \
+makelib lock_ex.o
+       ./makelib lock.a lock_ex.o
+
+lock_ex.o: \
+compile lock_ex.c lock_ex.c lock_ex.c lock_ex.c hasflock.h lock_ex.c \
+lock.h lock_ex.c
+       ./compile lock_ex.c
+
+log.o: \
+compile log.c substdio.h log.c readwrite.h log.c stralloc.h \
+gen_alloc.h stralloc.h log.c log.h log.c now.h datetime.h now.h log.c \
+fmt.h log.c open.h log.c
+       ./compile log.c
+
+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: \
+ezmlm.0 ezmlm-make.0 ezmlm-manage.0 ezmlm-send.0 ezmlm-reject.0 \
+ezmlm-return.0 ezmlm-warn.0 ezmlm-weed.0 ezmlm-list.0 ezmlm-sub.0 \
+ezmlm-unsub.0 alloc.0 case.0 datetime.0 direntry.0 env.0 error.0 \
+error_str.0 error_temp.0 ezmlm.0 fd_copy.0 fd_move.0 getln.0 getln2.0 \
+getopt.0 now.0 sgetopt.0 stralloc.0 subfd.0 subgetopt.0 substdio.0 \
+substdio_copy.0 substdio_in.0 substdio_out.0 surf.0 surfpcs.0 wait.0
+
+now.0: \
+now.3
+       nroff -man now.3 > now.0
+
+now.o: \
+compile now.c now.c datetime.h now.c now.h datetime.h datetime.h \
+now.h now.c
+       ./compile now.c
+
+open.a: \
+makelib open_append.o open_read.o open_trunc.o
+       ./makelib open.a open_append.o open_read.o open_trunc.o
+
+open_append.o: \
+compile open_append.c open_append.c open_append.c open.h \
+open_append.c
+       ./compile open_append.c
+
+open_read.o: \
+compile open_read.c open_read.c open_read.c open.h open_read.c
+       ./compile open_read.c
+
+open_trunc.o: \
+compile open_trunc.c open_trunc.c open_trunc.c open.h open_trunc.c
+       ./compile open_trunc.c
+
+qmail.o: \
+compile qmail.c substdio.h qmail.c readwrite.h qmail.c wait.h qmail.c \
+exit.h qmail.c fork.h qmail.c fd.h qmail.c qmail.h substdio.h \
+substdio.h qmail.h qmail.c auto_qmail.h qmail.c
+       ./compile qmail.c
+
+quote.o: \
+compile quote.c stralloc.h gen_alloc.h stralloc.h quote.c str.h \
+quote.c quote.h quote.c
+       ./compile quote.c
+
+scan_8long.o: \
+compile scan_8long.c scan.h scan_8long.c
+       ./compile scan_8long.c
+
+scan_ulong.o: \
+compile scan_ulong.c scan.h scan_ulong.c
+       ./compile scan_ulong.c
+
+seek.a: \
+makelib seek_set.o
+       ./makelib seek.a seek_set.o
+
+seek_set.o: \
+compile seek_set.c seek_set.c seek.h seek_set.c
+       ./compile seek_set.c
+
+setup: \
+it man install conf-bin conf-man
+       ./install "`head -1 conf-bin`" < BIN
+       ./install "`head -1 conf-man`" < MAN
+
+sgetopt.0: \
+sgetopt.3
+       nroff -man sgetopt.3 > sgetopt.0
+
+sgetopt.o: \
+compile sgetopt.c substdio.h sgetopt.c subfd.h substdio.h substdio.h \
+subfd.h sgetopt.c sgetopt.h sgetopt.h subgetopt.h sgetopt.h sgetopt.c \
+subgetopt.h subgetopt.h sgetopt.c
+       ./compile sgetopt.c
+
+shar: \
+FILES BLURB README INSTALL TODO THANKS CHANGES FILES BIN MAN VERSION \
+SYSDEPS Makefile ezmlm.5 ezmlm-make.1 ezmlm-make.c ezmlm-send.1 \
+ezmlm-send.c ezmlm-reject.1 ezmlm-reject.c ezmlm-list.1 ezmlm-list.c \
+ezmlm-sub.1 ezmlm-sub.c ezmlm-unsub.1 ezmlm-unsub.c ezmlm-manage.1 \
+ezmlm-manage.c ezmlm-return.1 ezmlm-return.c ezmlm-warn.1 \
+ezmlm-warn.c ezmlm-weed.1 ezmlm-weed.c getconf.h getconf.c log.h \
+log.c issub.h issub.c subscribe.h subscribe.c cookie.h cookie.c \
+auto-str.c conf-bin auto_bin.h conf-man install.c conf-cc conf-ld \
+find-systype.sh make-compile.sh make-load.sh make-makelib.sh trycpp.c \
+warn-auto.sh fork.h1 fork.h2 tryvfork.c wait.3 wait.h wait_pid.c \
+trywaitp.c error.3 error_str.3 error_temp.3 error.h error.c \
+error_str.c substdio.3 substdio_copy.3 substdio_in.3 substdio_out.3 \
+substdio.h substdio.c substdi.c substdo.c substdio_copy.c subfd.3 \
+subfd.h subfderr.c readwrite.h exit.h byte.h byte_chr.c byte_copy.c \
+byte_cr.c byte_diff.c byte_rchr.c byte_zero.c str.h str_chr.c \
+str_cpy.c str_diff.c str_diffn.c str_len.c str_rchr.c str_start.c \
+getopt.3 sgetopt.3 subgetopt.3 sgetopt.h sgetopt.c subgetopt.h \
+subgetopt.c strerr.h strerr.c strerr_sys.c strerr_die.c gen_alloc.h \
+gen_allocdefs.h stralloc.3 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 alloc.3 alloc.h \
+alloc.c alloc_re.c open.h open_append.c open_read.c open_trunc.c \
+uint32.h1 uint32.h2 tryulong32.c case.3 case.h case_diffb.c \
+case_lowerb.c case_startb.c fmt.h fmt_str.c fmt_uint.c fmt_uint0.c \
+fmt_ulong.c scan.h scan_ulong.c scan_8long.c lock.h lock_ex.c \
+tryflock.c env.3 env.h envread.c slurpclose.h slurpclose.c sig.h \
+sig_catch.c sig_pipe.c trysgact.c datetime.3 datetime.h datetime.c \
+date822fmt.h date822fmt.c now.3 now.h now.c quote.h quote.c seek.h \
+seek_set.c conf-qmail auto_qmail.h qmail.h qmail.c direntry.3 \
+direntry.h1 direntry.h2 trydrent.c getln.3 getln.h getln.c getln2.3 \
+getln2.c fd.h fd_copy.3 fd_copy.c fd_move.3 fd_move.c surf.3 surf.h \
+surf.c surfpcs.3 surfpcs.h surfpcs.c slurp.h slurp.c constmap.h \
+constmap.c
+       shar -m `cat FILES` > shar
+       chmod 400 shar
+
+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_catch.c sig.h sig_catch.c hassgact.h \
+sig_catch.c
+       ./compile sig_catch.c
+
+sig_pipe.o: \
+compile sig_pipe.c sig_pipe.c sig.h sig_pipe.c
+       ./compile sig_pipe.c
+
+slurp.o: \
+compile slurp.c stralloc.h gen_alloc.h stralloc.h slurp.c slurp.h \
+slurp.c error.h slurp.c open.h slurp.c
+       ./compile slurp.c
+
+slurpclose.o: \
+compile slurpclose.c stralloc.h gen_alloc.h stralloc.h slurpclose.c \
+readwrite.h slurpclose.c slurpclose.h slurpclose.c error.h \
+slurpclose.c
+       ./compile slurpclose.c
+
+str.a: \
+makelib str_len.o str_diff.o str_diffn.o str_cpy.o str_chr.o \
+str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_diff.o byte_copy.o \
+byte_cr.o byte_zero.o
+       ./makelib str.a str_len.o str_diff.o str_diffn.o str_cpy.o \
+       str_chr.o str_rchr.o str_start.o byte_chr.o byte_rchr.o \
+       byte_diff.o byte_copy.o byte_cr.o byte_zero.o
+
+str_chr.o: \
+compile str_chr.c str.h str_chr.c
+       ./compile str_chr.c
+
+str_cpy.o: \
+compile str_cpy.c str.h str_cpy.c
+       ./compile str_cpy.c
+
+str_diff.o: \
+compile str_diff.c str.h str_diff.c
+       ./compile str_diff.c
+
+str_diffn.o: \
+compile str_diffn.c str.h str_diffn.c
+       ./compile str_diffn.c
+
+str_len.o: \
+compile str_len.c str.h str_len.c
+       ./compile str_len.c
+
+str_rchr.o: \
+compile str_rchr.c str.h str_rchr.c
+       ./compile str_rchr.c
+
+str_start.o: \
+compile str_start.c str.h str_start.c
+       ./compile str_start.c
+
+stralloc.0: \
+stralloc.3
+       nroff -man stralloc.3 > stralloc.0
+
+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 stralloc_arts.c str.h stralloc_arts.c \
+stralloc.h gen_alloc.h stralloc.h stralloc_arts.c
+       ./compile stralloc_arts.c
+
+stralloc_cat.o: \
+compile stralloc_cat.c byte.h stralloc_cat.c stralloc.h gen_alloc.h \
+stralloc.h stralloc_cat.c
+       ./compile stralloc_cat.c
+
+stralloc_catb.o: \
+compile stralloc_catb.c stralloc.h gen_alloc.h stralloc.h \
+stralloc_catb.c byte.h stralloc_catb.c
+       ./compile stralloc_catb.c
+
+stralloc_cats.o: \
+compile stralloc_cats.c byte.h stralloc_cats.c str.h stralloc_cats.c \
+stralloc.h gen_alloc.h stralloc.h stralloc_cats.c
+       ./compile stralloc_cats.c
+
+stralloc_copy.o: \
+compile stralloc_copy.c byte.h stralloc_copy.c stralloc.h gen_alloc.h \
+stralloc.h stralloc_copy.c
+       ./compile stralloc_copy.c
+
+stralloc_eady.o: \
+compile stralloc_eady.c alloc.h stralloc_eady.c stralloc.h \
+gen_alloc.h stralloc.h stralloc_eady.c gen_allocdefs.h \
+gen_allocdefs.h gen_allocdefs.h stralloc_eady.c
+       ./compile stralloc_eady.c
+
+stralloc_opyb.o: \
+compile stralloc_opyb.c stralloc.h gen_alloc.h stralloc.h \
+stralloc_opyb.c byte.h stralloc_opyb.c
+       ./compile stralloc_opyb.c
+
+stralloc_opys.o: \
+compile stralloc_opys.c byte.h stralloc_opys.c str.h stralloc_opys.c \
+stralloc.h gen_alloc.h stralloc.h stralloc_opys.c
+       ./compile stralloc_opys.c
+
+stralloc_pend.o: \
+compile stralloc_pend.c alloc.h stralloc_pend.c stralloc.h \
+gen_alloc.h stralloc.h stralloc_pend.c gen_allocdefs.h \
+gen_allocdefs.h gen_allocdefs.h stralloc_pend.c
+       ./compile stralloc_pend.c
+
+strerr.a: \
+makelib strerr.o strerr_sys.o strerr_die.o
+       ./makelib strerr.a strerr.o strerr_sys.o strerr_die.o
+
+strerr.o: \
+compile strerr.c stralloc.h gen_alloc.h stralloc.h strerr.c strerr.h \
+strerr.c
+       ./compile strerr.c
+
+strerr_die.o: \
+compile strerr_die.c substdio.h strerr_die.c subfd.h substdio.h \
+substdio.h subfd.h strerr_die.c exit.h strerr_die.c strerr.h \
+strerr_die.c
+       ./compile strerr_die.c
+
+strerr_sys.o: \
+compile strerr_sys.c error.h strerr_sys.c strerr.h strerr_sys.c
+       ./compile strerr_sys.c
+
+subfd.0: \
+subfd.3
+       nroff -man subfd.3 > subfd.0
+
+subfderr.o: \
+compile subfderr.c readwrite.h subfderr.c substdio.h subfderr.c \
+subfd.h substdio.h substdio.h subfd.h subfderr.c
+       ./compile subfderr.c
+
+subgetopt.0: \
+subgetopt.3
+       nroff -man subgetopt.3 > subgetopt.0
+
+subgetopt.o: \
+compile subgetopt.c subgetopt.h subgetopt.h subgetopt.c
+       ./compile subgetopt.c
+
+subscribe.o: \
+compile subscribe.c stralloc.h gen_alloc.h stralloc.h subscribe.c \
+getln.h subscribe.c readwrite.h subscribe.c substdio.h subscribe.c \
+strerr.h subscribe.c open.h subscribe.c byte.h subscribe.c case.h \
+subscribe.c lock.h subscribe.c error.h subscribe.c uint32.h \
+subscribe.c subscribe.h strerr.h strerr.h subscribe.h subscribe.c
+       ./compile subscribe.c
+
+substdi.o: \
+compile substdi.c substdio.h substdi.c byte.h substdi.c error.h \
+substdi.c
+       ./compile substdi.c
+
+substdio.0: \
+substdio.3
+       nroff -man substdio.3 > substdio.0
+
+substdio.a: \
+makelib substdio.o substdi.o substdo.o subfderr.o substdio_copy.o
+       ./makelib substdio.a substdio.o substdi.o substdo.o \
+       subfderr.o substdio_copy.o
+
+substdio.o: \
+compile substdio.c substdio.h substdio.c
+       ./compile substdio.c
+
+substdio_copy.0: \
+substdio_copy.3
+       nroff -man substdio_copy.3 > substdio_copy.0
+
+substdio_copy.o: \
+compile substdio_copy.c substdio.h substdio_copy.c
+       ./compile substdio_copy.c
+
+substdio_in.0: \
+substdio_in.3
+       nroff -man substdio_in.3 > substdio_in.0
+
+substdio_out.0: \
+substdio_out.3
+       nroff -man substdio_out.3 > substdio_out.0
+
+substdo.o: \
+compile substdo.c substdio.h substdo.c str.h substdo.c byte.h \
+substdo.c error.h substdo.c
+       ./compile substdo.c
+
+surf.0: \
+surf.3
+       nroff -man surf.3 > surf.0
+
+surf.a: \
+makelib surf.o surfpcs.o
+       ./makelib surf.a surf.o surfpcs.o
+
+surf.o: \
+compile surf.c surf.h surf.c uint32.h surf.c
+       ./compile surf.c
+
+surfpcs.0: \
+surfpcs.3
+       nroff -man surfpcs.3 > surfpcs.0
+
+surfpcs.o: \
+compile surfpcs.c surf.h surfpcs.c surfpcs.h uint32.h surfpcs.h \
+surfpcs.c
+       ./compile surfpcs.c
+
+systype: \
+find-systype trycpp.c
+       ./find-systype > systype
+
+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.0: \
+wait.3
+       nroff -man wait.3 > wait.0
+
+wait.a: \
+makelib wait_pid.o
+       ./makelib wait.a wait_pid.o
+
+wait_pid.o: \
+compile wait_pid.c wait_pid.c wait_pid.c error.h wait_pid.c
+       ./compile wait_pid.c
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..f1a5806
--- /dev/null
+++ b/README
@@ -0,0 +1,31 @@
+ezmlm 0.53, alpha.
+19970629
+Copyright 1997
+D. J. Bernstein, djb@pobox.com
+
+ezmlm is an easy-to-use, high-speed mailing list manager for qmail. See
+BLURB for a more detailed advertisement.
+
+INSTALL says how to set up and test ezmlm.
+
+See http://pobox.com/~djb/ezmlm.html for the latest information about
+ezmlm.
+
+The rest of this file is a list of systypes where various versions of
+ezmlm have been reported to work.
+
+0.52: aix-4-1-:-:-:00910033a000-:- (tnx KJJ)
+0.52: freebsd-2.1.6-release-:i386-:-:-:- (tnx TM)
+0.52: freebsd-2.2-stable-:i386-:-:pentium-:- (tnx fedi=???)
+0.52: linux-2.0.18-:i386-:-:ppro-:- (tnx MAN)
+0.52: linux-2.0.28-osfmach3-:-:-:ppc-:- (tnx CWG)
+0.52: linux-2.0.29-:i386-:-:i486-:- (tnx FPL)
+0.52: linux-2.0.29-:i386-:-:pentium-:- (tnx AG)
+0.52: linux-2.0.30-:i386-:-:pentium-:- (tnx CH)
+0.52: linux-2.0.30-:i386-:-:pentium-:- (tnx LMB)
+0.52: linux-2.1.32-:i386-:-:pentium-:- (tnx JF)
+0.52: netbsd-1.2c-:i386-:-:intel.pentium.(p54c).(586-class)-:- (tnx GW)
+0.52: news-os-6.1.2-#2-:news5000-:-:news5000-:- (tnx yamada=???)
+0.52: openbsd-2.0-generic#0-:openbsd.m68k-:-:mac68k-:- (tnx GS)
+0.52: sunos-5.5-generic_103093-06-:sparc-:sun4-:sun4d-:sun4d- (tnx KT)
+0.52: sunos-5.5.1-generic-:sparc-:sun4-:sun4c-:sun4c- (tnx CWG)
diff --git a/SYSDEPS b/SYSDEPS
new file mode 100644 (file)
index 0000000..c358b69
--- /dev/null
+++ b/SYSDEPS
@@ -0,0 +1,7 @@
+VERSION
+systype
+direntry.h
+fork.h
+hasflock.h
+hassgact.h
+uint32.h
diff --git a/THANKS b/THANKS
new file mode 100644 (file)
index 0000000..d26ef09
--- /dev/null
+++ b/THANKS
@@ -0,0 +1,14 @@
+LMB = Lars Marowsky-Bree
+EC = Evan Champion
+JF = Janos Farkas
+PGF = Paul Fox
+CWG = Chris Garrigues
+AG = Andi Gutmans
+CH = Christian Hudon
+KJJ = Kevin J. Johnson
+FPL = Frederik P. Lindberg
+TM = Toshinori Maeno
+MAN = Mark Nowlin
+GS = Glen Stewart
+KT = Karsten Thygesen
+GW = Geoff Wing
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..1afaa05
--- /dev/null
+++ b/TODO
@@ -0,0 +1,7 @@
+think about how to disable ezmlm-return, ezmlm-warn
+test generally
+test ezmlm-warn deleting ancient files in bounce/
+test Delivered-To handling
+test cookie expiration for ezmlm-return
+test ezmlm-warn only handling susbcribers
+test ezmlm-return only handling susbcribers
diff --git a/VERSION b/VERSION
new file mode 100644 (file)
index 0000000..6049539
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+ezmlm 0.53
diff --git a/alloc.3 b/alloc.3
new file mode 100644 (file)
index 0000000..58b4432
--- /dev/null
+++ b/alloc.3
@@ -0,0 +1,62 @@
+.TH alloc 3
+.SH NAME
+alloc \- allocate memory
+.SH SYNTAX
+.B #include <alloc.h>
+
+char *\fBalloc\fP(\fInew\fR);
+
+void \fBalloc_free\fP(\fIx\fR);
+
+void \fBalloc_re\fP(&\fIx\fR,\fIold\fR,\fInew\fR);
+
+char *\fIx\fR;
+.br
+unsigned int \fIold\fR;
+.br
+unsigned int \fInew\fR;
+.SH DESCRIPTION
+.B alloc
+allocates enough space from the heap for
+.I new
+bytes of data,
+adequately aligned for any data type.
+.I new
+may be 0.
+.B alloc
+returns a pointer to the space.
+If space is not available,
+.B alloc
+returns 0,
+setting
+.B errno
+appropriately.
+
+.B alloc_free
+returns space to the heap.
+
+.B alloc_re
+expands the space allocated to
+.I x
+from
+.I old
+bytes to
+.I new
+bytes.
+It allocates new space,
+copies
+.I old
+bytes from the old space to the new space,
+returns the old space to the heap,
+and changes
+.I x
+to point to the new space.
+It then returns 1.
+If space is not available,
+.B alloc_re
+returns 0,
+leaving the old space alone.
+.SH "SEE ALSO"
+sbrk(2),
+malloc(3),
+error(3)
diff --git a/alloc.c b/alloc.c
new file mode 100644 (file)
index 0000000..c661453
--- /dev/null
+++ b/alloc.c
@@ -0,0 +1,32 @@
+#include "alloc.h"
+#include "error.h"
+extern char *malloc();
+extern void free();
+
+#define ALIGNMENT 16 /* XXX: assuming that this alignment is enough */
+#define SPACE 4096 /* must be multiple of ALIGNMENT */
+
+typedef union { char irrelevant[ALIGNMENT]; double d; } aligned;
+static aligned realspace[SPACE / ALIGNMENT];
+#define space ((char *) realspace)
+static unsigned int avail = SPACE; /* multiple of ALIGNMENT; 0<=avail<=SPACE */
+
+/*@null@*//*@out@*/char *alloc(n)
+unsigned int n;
+{
+  char *x;
+  n = ALIGNMENT + n - (n & (ALIGNMENT - 1)); /* XXX: could overflow */
+  if (n <= avail) { avail -= n; return space + avail; }
+  x = malloc(n);
+  if (!x) errno = error_nomem;
+  return x;
+}
+
+void alloc_free(x)
+char *x;
+{
+  if (x >= space)
+    if (x < space + SPACE)
+      return; /* XXX: assuming that pointers are flat */
+  free(x);
+}
diff --git a/alloc.h b/alloc.h
new file mode 100644 (file)
index 0000000..1b1d893
--- /dev/null
+++ b/alloc.h
@@ -0,0 +1,8 @@
+#ifndef ALLOC_H
+#define ALLOC_H
+
+extern /*@null@*//*@out@*/char *alloc();
+extern void alloc_free();
+extern int alloc_re();
+
+#endif
diff --git a/alloc=0 b/alloc=0
new file mode 100644 (file)
index 0000000..5974100
--- /dev/null
+++ b/alloc=0
@@ -0,0 +1 @@
+alloc.3
diff --git a/alloc=l b/alloc=l
new file mode 100644 (file)
index 0000000..6dc62ab
--- /dev/null
+++ b/alloc=l
@@ -0,0 +1,2 @@
+alloc.o
+alloc_re.o
diff --git a/alloc_re.c b/alloc_re.c
new file mode 100644 (file)
index 0000000..feb8b49
--- /dev/null
@@ -0,0 +1,17 @@
+#include "alloc.h"
+#include "byte.h"
+
+int alloc_re(x,m,n)
+char **x;
+unsigned int m;
+unsigned int n;
+{
+  char *y;
+  y = alloc(n);
+  if (!y) return 0;
+  byte_copy(y,m,*x);
+  alloc_free(*x);
+  *x = y;
+  return 1;
+}
diff --git a/auto-str.c b/auto-str.c
new file mode 100644 (file)
index 0000000..acc3d60
--- /dev/null
@@ -0,0 +1,44 @@
+#include "substdio.h"
+#include "readwrite.h"
+#include "exit.h"
+
+char buf1[256];
+substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1));
+
+void puts(s)
+char *s;
+{
+  if (substdio_puts(&ss1,s) == -1) _exit(111);
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  char *name;
+  char *value;
+  unsigned char ch;
+  char octal[4];
+
+  name = argv[1];
+  if (!name) _exit(100);
+  value = argv[2];
+  if (!value) _exit(100);
+
+  puts("char ");
+  puts(name);
+  puts("[] = \"\\\n");
+
+  while (ch = *value++) {
+    puts("\\");
+    octal[3] = 0;
+    octal[2] = '0' + (ch & 7); ch >>= 3;
+    octal[1] = '0' + (ch & 7); ch >>= 3;
+    octal[0] = '0' + (ch & 7);
+    puts(octal);
+  }
+
+  puts("\\\n\";\n");
+  if (substdio_flush(&ss1) == -1) _exit(111);
+  _exit(0);
+}
diff --git a/auto-str=x b/auto-str=x
new file mode 100644 (file)
index 0000000..f3e9229
--- /dev/null
@@ -0,0 +1,3 @@
+substdio.a
+error.a
+str.a
diff --git a/auto_bin.c.do b/auto_bin.c.do
new file mode 100644 (file)
index 0000000..8b51408
--- /dev/null
@@ -0,0 +1,3 @@
+dependon auto-str conf-bin
+formake './auto-str auto_bin `head -1 conf-bin` > auto_bin.c'
+./auto-str auto_bin `head -1 conf-bin`
diff --git a/auto_bin.h b/auto_bin.h
new file mode 100644 (file)
index 0000000..8c30166
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef AUTO_BIN_H
+#define AUTO_BIN_H
+
+extern char auto_bin[];
+
+#endif
diff --git a/auto_qmail.c.do b/auto_qmail.c.do
new file mode 100644 (file)
index 0000000..c072bf3
--- /dev/null
@@ -0,0 +1,3 @@
+dependon auto-str conf-qmail
+formake './auto-str auto_qmail `head -1 conf-qmail` > auto_qmail.c'
+./auto-str auto_qmail `head -1 conf-qmail`
diff --git a/auto_qmail.h b/auto_qmail.h
new file mode 100644 (file)
index 0000000..0c56001
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef AUTO_QMAIL_H
+#define AUTO_QMAIL_H
+
+extern char auto_qmail[];
+
+#endif
diff --git a/byte.h b/byte.h
new file mode 100644 (file)
index 0000000..de06c69
--- /dev/null
+++ b/byte.h
@@ -0,0 +1,13 @@
+#ifndef BYTE_H
+#define BYTE_H
+
+extern unsigned int byte_chr();
+extern unsigned int byte_rchr();
+extern void byte_copy();
+extern void byte_copyr();
+extern int byte_diff();
+extern void byte_zero();
+
+#define byte_equal(s,n,t) (!byte_diff((s),(n),(t)))
+
+#endif
diff --git a/byte_chr.c b/byte_chr.c
new file mode 100644 (file)
index 0000000..f81dde8
--- /dev/null
@@ -0,0 +1,20 @@
+#include "byte.h"
+
+unsigned int byte_chr(s,n,c)
+char *s;
+register unsigned int n;
+int c;
+{
+  register char ch;
+  register char *t;
+
+  ch = c;
+  t = s;
+  for (;;) {
+    if (!n) break; if (*t == ch) break; ++t; --n;
+    if (!n) break; if (*t == ch) break; ++t; --n;
+    if (!n) break; if (*t == ch) break; ++t; --n;
+    if (!n) break; if (*t == ch) break; ++t; --n;
+  }
+  return t - s;
+}
diff --git a/byte_copy.c b/byte_copy.c
new file mode 100644 (file)
index 0000000..eaad11b
--- /dev/null
@@ -0,0 +1,14 @@
+#include "byte.h"
+
+void byte_copy(to,n,from)
+register char *to;
+register unsigned int n;
+register char *from;
+{
+  for (;;) {
+    if (!n) return; *to++ = *from++; --n;
+    if (!n) return; *to++ = *from++; --n;
+    if (!n) return; *to++ = *from++; --n;
+    if (!n) return; *to++ = *from++; --n;
+  }
+}
diff --git a/byte_cr.c b/byte_cr.c
new file mode 100644 (file)
index 0000000..3e7a1d5
--- /dev/null
+++ b/byte_cr.c
@@ -0,0 +1,16 @@
+#include "byte.h"
+
+void byte_copyr(to,n,from)
+register char *to;
+register unsigned int n;
+register char *from;
+{
+  to += n;
+  from += n;
+  for (;;) {
+    if (!n) return; *--to = *--from; --n;
+    if (!n) return; *--to = *--from; --n;
+    if (!n) return; *--to = *--from; --n;
+    if (!n) return; *--to = *--from; --n;
+  }
+}
diff --git a/byte_diff.c b/byte_diff.c
new file mode 100644 (file)
index 0000000..cdbd760
--- /dev/null
@@ -0,0 +1,16 @@
+#include "byte.h"
+
+int byte_diff(s,n,t)
+register char *s;
+register unsigned int n;
+register char *t;
+{
+  for (;;) {
+    if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
+    if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
+    if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
+    if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
+  }
+  return ((int)(unsigned int)(unsigned char) *s)
+       - ((int)(unsigned int)(unsigned char) *t);
+}
diff --git a/byte_rchr.c b/byte_rchr.c
new file mode 100644 (file)
index 0000000..476bc22
--- /dev/null
@@ -0,0 +1,23 @@
+#include "byte.h"
+
+unsigned int byte_rchr(s,n,c)
+char *s;
+register unsigned int n;
+int c;
+{
+  register char ch;
+  register char *t;
+  register char *u;
+
+  ch = c;
+  t = s;
+  u = 0;
+  for (;;) {
+    if (!n) break; if (*t == ch) u = t; ++t; --n;
+    if (!n) break; if (*t == ch) u = t; ++t; --n;
+    if (!n) break; if (*t == ch) u = t; ++t; --n;
+    if (!n) break; if (*t == ch) u = t; ++t; --n;
+  }
+  if (!u) u = t;
+  return u - s;
+}
diff --git a/byte_zero.c b/byte_zero.c
new file mode 100644 (file)
index 0000000..92009ba
--- /dev/null
@@ -0,0 +1,13 @@
+#include "byte.h"
+
+void byte_zero(s,n)
+char *s;
+register unsigned int n;
+{
+  for (;;) {
+    if (!n) break; *s++ = 0; --n;
+    if (!n) break; *s++ = 0; --n;
+    if (!n) break; *s++ = 0; --n;
+    if (!n) break; *s++ = 0; --n;
+  }
+}
diff --git a/case.3 b/case.3
new file mode 100644 (file)
index 0000000..58bd724
--- /dev/null
+++ b/case.3
@@ -0,0 +1,100 @@
+.TH case 3
+.SH NAME
+case \- convert ASCII uppercase bytes to lowercase
+.SH SYNTAX
+.B #include <case.h>
+
+void \fBcase_lowers\fP(\fIs\fR);
+.br
+void \fBcase_lowerb\fP(\fIs\fR,\fIlen\fR);
+
+int \fBcase_diffs\fP(\fIs\fR,\fIt\fR);
+.br
+int \fBcase_equals\fP(\fIs\fR,\fIt\fR);
+.br
+int \fBcase_starts\fP(\fIs\fR,\fIt\fR);
+
+int \fBcase_diffb\fP(\fIs\fR,\fIlen\fR,\fIt\fR);
+.br
+int \fBcase_startb\fP(\fIs\fR,\fIlen\fR,\fIt\fR);
+
+char *\fIs\fR;
+.br
+char *\fIt\fR;
+.br
+unsigned int \fIlen\fR;
+.SH DESCRIPTION
+.B case_lowers
+converts each uppercase byte in the string
+.I s
+to lowercase.
+.I s
+must be 0-terminated.
+
+.B case_lowerb
+converts each uppercase byte in the buffer
+.IR s ,
+of length
+.IR len ,
+to lowercase.
+
+.B case_diffs
+lexicographically compares lowercase versions of the strings
+.I s
+and
+.IR t .
+It returns something positive, negative, or zero
+when the first is larger than, smaller than, or equal to the second.
+.I s
+and
+.I t
+must be 0-terminated.
+
+.B case_equals
+means
+.BR !case_diffs .
+
+.B case_starts
+returns 1 if a lowercase version of
+.I s
+starts with a lowercase version of
+.IR t .
+.I s
+and
+.I t
+must be 0-terminated.
+
+.B case_diffb
+lexicographically compares lowercase versions of the buffers
+.I s
+and
+.IR t ,
+each of length
+.IR len .
+It returns something positive, negative, or zero
+when the first is larger than, smaller than, or equal to the second.
+
+.B case_startb
+returns 1 if a lowercase version of the buffer
+.IR s ,
+of length
+.IR len ,
+starts with a lowercase version of the string
+.IR t .
+.I t
+must be 0-terminated.
+
+The
+.B case
+routines
+are ASCII-specific.
+They are suitable for programs that handle
+case-independent networking protocols.
+
+All comparisons are performed on unsigned bytes.
+.SH "SEE ALSO"
+byte_diff(3),
+byte_equal(3),
+str_diff(3),
+str_equal(3),
+str_start(3)
diff --git a/case.h b/case.h
new file mode 100644 (file)
index 0000000..35a2434
--- /dev/null
+++ b/case.h
@@ -0,0 +1,13 @@
+#ifndef CASE_H
+#define CASE_H
+
+extern void case_lowers();
+extern void case_lowerb();
+extern int case_diffs();
+extern int case_diffb();
+extern int case_starts();
+extern int case_startb();
+
+#define case_equals(s,t) (!case_diffs((s),(t)))
+
+#endif
diff --git a/case=0 b/case=0
new file mode 100644 (file)
index 0000000..4750e7f
--- /dev/null
+++ b/case=0
@@ -0,0 +1 @@
+case.3
diff --git a/case=l b/case=l
new file mode 100644 (file)
index 0000000..9e09750
--- /dev/null
+++ b/case=l
@@ -0,0 +1,3 @@
+case_diffb.o
+case_lowerb.o
+case_startb.o
diff --git a/case_diffb.c b/case_diffb.c
new file mode 100644 (file)
index 0000000..9064b8a
--- /dev/null
@@ -0,0 +1,21 @@
+#include "case.h"
+
+int case_diffb(s,len,t)
+register char *s;
+unsigned int len;
+register char *t;
+{
+  register unsigned char x;
+  register unsigned char y;
+
+  while (len > 0) {
+    --len;
+    x = *s++ - 'A';
+    if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
+    y = *t++ - 'A';
+    if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
+    if (x != y)
+      return ((int)(unsigned int) x) - ((int)(unsigned int) y);
+  }
+  return 0;
+}
diff --git a/case_lowerb.c b/case_lowerb.c
new file mode 100644 (file)
index 0000000..4034c14
--- /dev/null
@@ -0,0 +1,14 @@
+#include "case.h"
+
+void case_lowerb(s,len)
+char *s;
+unsigned int len;
+{
+  unsigned char x;
+  while (len > 0) {
+    --len;
+    x = *s - 'A';
+    if (x <= 'Z' - 'A') *s = x + 'a';
+    ++s;
+  }
+}
diff --git a/case_startb.c b/case_startb.c
new file mode 100644 (file)
index 0000000..ee88efe
--- /dev/null
@@ -0,0 +1,21 @@
+#include "case.h"
+
+int case_startb(s,len,t)
+register char *s;
+unsigned int len;
+register char *t;
+{
+  register unsigned char x;
+  register unsigned char y;
+
+  for (;;) {
+    y = *t++ - 'A';
+    if (y <= 'Z' - 'A') y += 'a'; else y += 'A';
+    if (!y) return 1;
+    if (!len) return 0;
+    --len;
+    x = *s++ - 'A';
+    if (x <= 'Z' - 'A') x += 'a'; else x += 'A';
+    if (x != y) return 0;
+  }
+}
diff --git a/conf-bin b/conf-bin
new file mode 100644 (file)
index 0000000..813dc94
--- /dev/null
+++ b/conf-bin
@@ -0,0 +1,3 @@
+/usr/local/bin/ezmlm
+
+Programs will be installed in this directory.
diff --git a/conf-cc b/conf-cc
new file mode 100644 (file)
index 0000000..e58fb9b
--- /dev/null
+++ b/conf-cc
@@ -0,0 +1,3 @@
+cc -O2
+
+This will be used to compile .c files.
diff --git a/conf-ld b/conf-ld
new file mode 100644 (file)
index 0000000..a9e796a
--- /dev/null
+++ b/conf-ld
@@ -0,0 +1,3 @@
+cc -s
+
+This will be used to link .o files into an executable.
diff --git a/conf-man b/conf-man
new file mode 100644 (file)
index 0000000..b4e70a4
--- /dev/null
+++ b/conf-man
@@ -0,0 +1,5 @@
+/usr/local/man
+
+Man pages will be installed in subdirectories of this directory. An
+unformatted man page foo.1 will go into .../man1/foo.1; a formatted man
+page foo.0 will go into .../cat1/foo.0.
diff --git a/conf-qmail b/conf-qmail
new file mode 100644 (file)
index 0000000..6acc5a5
--- /dev/null
@@ -0,0 +1,3 @@
+/var/qmail
+
+This is the qmail home directory.
diff --git a/constmap.c b/constmap.c
new file mode 100644 (file)
index 0000000..722e3b8
--- /dev/null
@@ -0,0 +1,114 @@
+#include "constmap.h"
+#include "alloc.h"
+#include "case.h"
+
+static constmap_hash hash(s,len)
+char *s;
+int len;
+{
+  unsigned char ch;
+  constmap_hash h;
+  h = 5381;
+  while (len > 0) {
+    ch = *s++ - 'A';
+    if (ch <= 'Z' - 'A') ch += 'a' - 'A';
+    h = ((h << 5) + h) ^ ch;
+    --len;
+  }
+  return h;
+}
+
+char *constmap(cm,s,len)
+struct constmap *cm;
+char *s;
+int len;
+{
+  constmap_hash h;
+  int pos;
+  h = hash(s,len);
+  pos = cm->first[h & cm->mask];
+  while (pos != -1) {
+    if (h == cm->hash[pos])
+      if (len == cm->inputlen[pos])
+        if (!case_diffb(cm->input[pos],len,s))
+         return cm->input[pos] + cm->inputlen[pos] + 1;
+    pos = cm->next[pos];
+  }
+  return 0;
+}
+
+int constmap_init(cm,s,len,flagcolon)
+struct constmap *cm;
+char *s;
+int len;
+int flagcolon;
+{
+  int i;
+  int j;
+  int k;
+  int pos;
+  constmap_hash h;
+  cm->num = 0;
+  for (j = 0;j < len;++j) if (!s[j]) ++cm->num;
+  h = 64;
+  while (h && (h < cm->num)) h += h;
+  cm->mask = h - 1;
+  cm->first = (int *) alloc(sizeof(int) * h);
+  if (cm->first) {
+    cm->input = (char **) alloc(sizeof(char *) * cm->num);
+    if (cm->input) {
+      cm->inputlen = (int *) alloc(sizeof(int) * cm->num);
+      if (cm->inputlen) {
+        cm->hash = (constmap_hash *) alloc(sizeof(constmap_hash) * cm->num);
+        if (cm->hash) {
+         cm->next = (int *) alloc(sizeof(int) * cm->num);
+         if (cm->next) {
+           for (h = 0;h <= cm->mask;++h)
+             cm->first[h] = -1;
+            pos = 0;
+            i = 0;
+            for (j = 0;j < len;++j)
+              if (!s[j]) {
+               k = j - i;
+               if (flagcolon) {
+                 for (k = i;k < j;++k)
+                   if (s[k] == ':')
+                     break;
+                 if (k >= j) { i = j + 1; continue; }
+                 k -= i;
+               }
+                cm->input[pos] = s + i;
+                cm->inputlen[pos] = k;
+                h = hash(s + i,k);
+                cm->hash[pos] = h;
+                h &= cm->mask;
+                cm->next[pos] = cm->first[h];
+                cm->first[h] = pos;
+                ++pos;
+                i = j + 1;
+              }
+            return 1;
+         }
+         alloc_free(cm->hash);
+       }
+        alloc_free(cm->inputlen);
+      }
+      alloc_free(cm->input);
+    }
+    alloc_free(cm->first);
+  }
+  return 0;
+}
+
+void constmap_free(cm)
+struct constmap *cm;
+{
+  alloc_free(cm->next);
+  alloc_free(cm->hash);
+  alloc_free(cm->inputlen);
+  alloc_free(cm->input);
+  alloc_free(cm->first);
+}
diff --git a/constmap.h b/constmap.h
new file mode 100644 (file)
index 0000000..3f29179
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef CONSTMAP_H
+#define CONSTMAP_H
+
+typedef unsigned long constmap_hash;
+
+struct constmap {
+  int num;
+  constmap_hash mask;
+  constmap_hash *hash;
+  int *first;
+  int *next;
+  char **input;
+  int *inputlen;
+} ;
+
+extern int constmap_init();
+extern void constmap_free();
+extern char *constmap();
+
+#endif
diff --git a/cookie.c b/cookie.c
new file mode 100644 (file)
index 0000000..59089c1
--- /dev/null
+++ b/cookie.c
@@ -0,0 +1,47 @@
+#include "cookie.h"
+#include "str.h"
+#include "uint32.h"
+#include "surfpcs.h"
+
+void cookie(hash,key,keylen,date,addr,action)
+char *hash;
+char *key;
+unsigned int keylen;
+char *date;
+char *addr;
+char *action;
+{
+  surfpcs s;
+  uint32 seed[32];
+  unsigned char out[32];
+  int i;
+  int j;
+
+/*
+step 1: create seed from key. note that this doesn't have to be
+cryptographic; it simply has to avoid destroying the user's entropy.
+if speed turns out to be a problem, switch to a CRC.
+*/
+  for (i = 0;i < 32;++i) seed[i] = 0;
+  for (j = 0;j < 4;++j) {
+    surfpcs_init(&s,seed);
+    surfpcs_add(&s,key,keylen);
+    surfpcs_out(&s,out);
+    for (i = 0;i < 32;++i) seed[i] = (seed[i] << 8) + out[i];
+  }
+
+/*
+step 2: apply SURF.
+*/
+  surfpcs_init(&s,seed);
+  surfpcs_add(&s,date,str_len(date) + 1);
+  surfpcs_add(&s,addr,str_len(addr) + 1);
+  surfpcs_add(&s,action,1);
+  surfpcs_out(&s,out);
+
+/*
+step 3: extract a readable cookie from the SURF output.
+*/
+  for (i = 0;i < 20;++i) 
+    hash[i] = 'a' + (out[i] & 15);
+}
diff --git a/cookie.h b/cookie.h
new file mode 100644 (file)
index 0000000..041cb6a
--- /dev/null
+++ b/cookie.h
@@ -0,0 +1,8 @@
+#ifndef COOKIE_H
+#define COOKIE_H
+
+#define COOKIE 20
+
+extern void cookie();
+
+#endif
diff --git a/date822fmt.c b/date822fmt.c
new file mode 100644 (file)
index 0000000..7674bd1
--- /dev/null
@@ -0,0 +1,29 @@
+#include "datetime.h"
+#include "fmt.h"
+#include "date822fmt.h"
+
+static char *montab[12] = {
+"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
+};
+
+unsigned int date822fmt(s,dt)
+char *s;
+struct datetime *dt;
+{
+  unsigned int i;
+  unsigned int len;
+  len = 0;
+  i = fmt_uint(s,dt->mday); len += i; if (s) s += i;
+  i = fmt_str(s," "); len += i; if (s) s += i;
+  i = fmt_str(s,montab[dt->mon]); len += i; if (s) s += i;
+  i = fmt_str(s," "); len += i; if (s) s += i;
+  i = fmt_uint(s,dt->year + 1900); len += i; if (s) s += i;
+  i = fmt_str(s," "); len += i; if (s) s += i;
+  i = fmt_uint0(s,dt->hour,2); len += i; if (s) s += i;
+  i = fmt_str(s,":"); len += i; if (s) s += i;
+  i = fmt_uint0(s,dt->min,2); len += i; if (s) s += i;
+  i = fmt_str(s,":"); len += i; if (s) s += i;
+  i = fmt_uint0(s,dt->sec,2); len += i; if (s) s += i;
+  i = fmt_str(s," -0000\n"); len += i; if (s) s += i;
+  return len;
+}
diff --git a/date822fmt.h b/date822fmt.h
new file mode 100644 (file)
index 0000000..1848e5a
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef DATE822FMT_H
+#define DATE822FMT_H
+
+extern unsigned int date822fmt();
+#define DATE822FMT 60
+
+#endif
diff --git a/datetime.3 b/datetime.3
new file mode 100644 (file)
index 0000000..33a623f
--- /dev/null
@@ -0,0 +1,73 @@
+.TH datetime 3
+.SH NAME
+datetime \- convert between TAI labels and seconds
+.SH SYNTAX
+.B #include <datetime.h>
+
+void \fBdatetime_tai\fP(&\fIdt\fR,\fIt\fR);
+
+datetime_sec \fBdatetime_untai\fP(&\fIdt\fR);
+
+struct datetime \fIdt\fR;
+.br
+datetime_sec \fIt\fR;
+.SH DESCRIPTION
+International Atomic Time, TAI,
+is the fundamental unit for time measurements.
+TAI has one label for every second of real time,
+without complications such as leap seconds.
+
+A
+struct datetime
+variable,
+such as
+.IR dt ,
+stores a TAI label.
+.I dt\fB.year
+is the year number minus 1900;
+.I dt\fB.mon
+is the month number, from 0 (January) through 11 (December);
+.I dt\fB.mday
+is the day of the month, from 1 through 31;
+.I dt\fB.hour
+is the hour, from 0 through 23;
+.I dt\fB.min
+is the minute, from 0 through 59;
+.I dt\fB.sec
+is the second, from 0 through 59;
+.I dt\fB.wday
+is the day of the week, from 0 (Sunday) through 6 (Saturday);
+.I dt\fB.yday
+is the day of the year, from 0 through 365.
+
+The
+.B datetime
+library supports more convenient TAI manipulation with
+the datetime_sec type.
+A datetime_sec value, such as
+.IR t ,
+is an integer referring to the
+.IR t th
+second after the beginning of 1970 TAI.
+The first second of 1970 TAI was 0;
+the next second was 1;
+the last second of 1969 TAI was -1.
+The difference between two datetime_sec values is a number
+of real-time seconds.
+
+.B datetime_tai
+converts a datetime_sec to a TAI label.
+
+.B datetime_untai
+reads a TAI label
+(specifically
+.IR dt\fB.year ,
+.IR dt\fB.mon ,
+.IR dt\fB.mday ,
+.IR dt\fB.hour ,
+.IR dt\fB.min ,
+and
+.IR dt\fB.sec )
+and returns a datetime_sec.
+.SH "SEE ALSO"
+now(3)
diff --git a/datetime.c b/datetime.c
new file mode 100644 (file)
index 0000000..7b8a803
--- /dev/null
@@ -0,0 +1,55 @@
+/* 19950925 */
+#include "datetime.h"
+
+void datetime_tai(dt,t)
+struct datetime *dt;
+datetime_sec t;
+{
+  int day;
+  int tod;
+  int year;
+  int yday;
+  int wday;
+  int mon;
+  tod = t % 86400;
+  day = t / 86400;
+  if (tod < 0) { tod += 86400; --day; }
+  dt->hour = tod / 3600;
+  tod %= 3600;
+  dt->min = tod / 60;
+  dt->sec = tod % 60;
+  wday = (day + 4) % 7; if (wday < 0) wday += 7;
+  dt->wday = wday;
+  day -= 11017;
+  /* day 0 is march 1, 2000 */
+  year = 5 + day / 146097;
+  day = day % 146097; if (day < 0) { day += 146097; --year; }
+  /* from now on, day is nonnegative */
+  year *= 4;
+  if (day == 146096) { year += 3; day = 36524; }
+  else { year += day / 36524; day %= 36524; }
+  year *= 25;
+  year += day / 1461;
+  day %= 1461;
+  year *= 4;
+  yday = (day < 306);
+  if (day == 1460) { year += 3; day = 365; }
+  else { year += day / 365; day %= 365; }
+  yday += day;
+  day *= 10;
+  mon = (day + 5) / 306;
+  day = day + 5 - 306 * mon;
+  day /= 10;
+  if (mon >= 10) { yday -= 306; ++year; mon -= 10; }
+  else { yday += 59; mon += 2; }
+  dt->yday = yday;
+  dt->year = year - 1900;
+  dt->mon = mon;
+  dt->mday = day + 1;
+}
diff --git a/datetime.h b/datetime.h
new file mode 100644 (file)
index 0000000..cde2a9b
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef DATETIME_H
+#define DATETIME_H
+
+struct datetime {
+  int hour;
+  int min;
+  int sec;
+  int wday;
+  int mday;
+  int yday;
+  int mon;
+  int year;
+} ;
+
+typedef long datetime_sec;
+
+extern void datetime_tai();
+extern datetime_sec datetime_untai();
+
+#endif
diff --git a/datetime=0 b/datetime=0
new file mode 100644 (file)
index 0000000..8a4bea1
--- /dev/null
@@ -0,0 +1 @@
+datetime.3
diff --git a/default.0.do b/default.0.do
new file mode 100644 (file)
index 0000000..e40b50c
--- /dev/null
@@ -0,0 +1,9 @@
+if test -r "$2=0"
+then
+  dependon "$2=0"
+  dependon `cat "$2=0"`
+  nroff -man `cat "$2=0"`
+  formake nroff -man `cat "$2=0"` '>' $1
+else
+  nosuchtarget
+fi
diff --git a/default.a.do b/default.a.do
new file mode 100644 (file)
index 0000000..b61fc5c
--- /dev/null
@@ -0,0 +1,10 @@
+if test -r "$2=l"
+then
+  dependon "$2=l"
+  dependon makelib `cat "$2=l"`
+  directtarget
+  formake ./makelib $1 `cat "$2=l"`
+  ./makelib $1 `cat "$2=l"`
+else
+  nosuchtarget
+fi
diff --git a/default.do b/default.do
new file mode 100644 (file)
index 0000000..792917f
--- /dev/null
@@ -0,0 +1,76 @@
+if test -r $1=x
+then
+  dependon $1=x
+  libs=`grep '\.lib *$' "$1=x"`
+  libscat=''
+  for i in $libs
+  do
+    libscat="$libscat "'`'"cat $i"'`'
+  done
+  objs=`grep -v '\.lib *$' "$1=x"`
+  dependon load $1.o $objs $libs
+  directtarget
+  formake ./load $1 $objs "$libscat"
+  eval ./load $1 $objs $libscat
+  exit 0
+fi
+
+if test -r $1=s
+then
+  dependon $1=s warn-auto.sh $1.sh
+  formake cat warn-auto.sh $1.sh '>' $1
+  formake chmod 755 $1
+  cat warn-auto.sh $1.sh
+  chmod 755 $3
+  exit 0
+fi
+
+case "$1" in
+  shar)
+    dependon FILES `cat FILES`
+    formake 'shar -m `cat FILES` > shar'
+    formake 'chmod 400 shar'
+    shar -m `cat FILES`
+    chmod 400 $3
+    ;;
+  compile|load|makelib)
+    dependon make-$1 warn-auto.sh systype
+    formake "( cat warn-auto.sh; ./make-$1 "'"`cat systype`"'" ) > $1"
+    formake "chmod 755 $1"
+    cat warn-auto.sh
+    ./make-$1 "`cat systype`"
+    chmod 755 $3
+    ;;
+  make-compile|make-load|make-makelib)
+    dependon $1.sh auto-ccld.sh
+    formake "cat auto-ccld.sh $1.sh > $1"
+    formake "chmod 755 $1"
+    cat auto-ccld.sh $1.sh
+    chmod 755 $3
+    ;;
+  systype)
+    dependon find-systype trycpp.c
+    formake './find-systype > systype'
+    ./find-systype
+    ;;
+  find-systype)
+    dependon find-systype.sh auto-ccld.sh
+    formake 'cat auto-ccld.sh find-systype.sh > find-systype'
+    formake 'chmod 755 find-systype'
+    cat auto-ccld.sh find-systype.sh
+    chmod 755 $3
+    ;;
+  auto-ccld.sh)
+    dependon conf-cc conf-ld warn-auto.sh
+    formake '( cat warn-auto.sh; \'
+    formake 'echo CC=\'\''`head -1 conf-cc`\'\''; \'
+    formake 'echo LD=\'\''`head -1 conf-ld`\'\'' \'
+    formake ') > auto-ccld.sh'
+    cat warn-auto.sh
+    echo CC=\'`head -1 conf-cc`\'
+    echo LD=\'`head -1 conf-ld`\'
+    ;;
+  *)
+    nosuchtarget
+    ;;
+esac
diff --git a/default.o.do b/default.o.do
new file mode 100644 (file)
index 0000000..5d5ee1e
--- /dev/null
@@ -0,0 +1,15 @@
+if test -r $2=m
+then
+  dependon $2=m $2.s
+  directtarget
+  as -o $1 $2.s
+  formake as -o $1 $2.s
+  exit 0
+fi
+depend -$2=m
+
+directtarget
+dependon compile
+dependcc $2.c
+formake ./compile $2.c
+./compile $2.c
diff --git a/direntry.3 b/direntry.3
new file mode 100644 (file)
index 0000000..8928fbb
--- /dev/null
@@ -0,0 +1,36 @@
+.TH direntry 3
+.SH NAME
+direntry \- read directory entries
+.SH SYNTAX
+.B #include <direntry.h>
+
+DIR *\fBopendir\fP(\fIfn\fR);
+
+struct direntry *\fBreaddir\fP(\fIdir\fP);
+
+void \fBclosedir\fP(\fIdir\fP);
+
+DIR *\fIdir\fR;
+.br
+char *\fIfn\fR;
+.SH DESCRIPTION
+The point of
+.B direntry.h
+is to provide a uniform interface to BSD's
+.B sys/dir.h
+and POSIX's
+.BR dirent.h .
+
+The
+.B readdir
+interface is highly unsatisfactory.
+It does not distinguish between I/O errors and end-of-directory.
+It uses
+.BR malloc .
+The return type for
+.B closedir
+varies: some implementations return the
+.B close
+return value.
+.SH "SEE ALSO"
+readdir(3)
diff --git a/direntry.h.do b/direntry.h.do
new file mode 100644 (file)
index 0000000..4470197
--- /dev/null
@@ -0,0 +1,7 @@
+dependon compile trydrent.c direntry.h1 direntry.h2
+./compile trydrent.c >/dev/null 2>&1 \
+&& cat direntry.h2 || cat direntry.h1
+rm -f trydrent.o
+formake '( ./compile trydrent.c >/dev/null 2>&1 \'
+formake '&& cat direntry.h2 || cat direntry.h1 ) > direntry.h'
+formake 'rm -f trydrent.o'
diff --git a/direntry.h1 b/direntry.h1
new file mode 100644 (file)
index 0000000..f737676
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef DIRENTRY_H
+#define DIRENTRY_H
+
+#include <sys/types.h>
+#include <sys/dir.h>
+#define direntry struct direct
+
+#endif
diff --git a/direntry.h2 b/direntry.h2
new file mode 100644 (file)
index 0000000..0302ebe
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef DIRENTRY_H
+#define DIRENTRY_H
+
+#include <sys/types.h>
+#include <dirent.h>
+#define direntry struct dirent
+
+#endif
diff --git a/direntry=0 b/direntry=0
new file mode 100644 (file)
index 0000000..6086256
--- /dev/null
@@ -0,0 +1 @@
+direntry.3
diff --git a/env.3 b/env.3
new file mode 100644 (file)
index 0000000..53a5f89
--- /dev/null
+++ b/env.3
@@ -0,0 +1,31 @@
+.TH env 3
+.SH NAME
+env \- manage the environment
+.SH SYNTAX
+.B #include <env.h>
+
+char **\fBenviron\fP;
+
+char *\fBenv_get\fP(\fIname\fR);
+.br
+char *\fBenv_pick\fP();
+
+char *\fIname\fR;
+.SH DESCRIPTION
+The environment,
+.BR environ ,
+is a 0-terminated array of 0-terminated strings,
+called environment variables.
+Each environment variable is of the form
+.IR name\fB=\fIvalue .
+
+.B env_get
+returns the value of the first variable whose name is
+.IR name ,
+or 0 if there is no such variable.
+
+.B env_pick
+returns any variable in the environment,
+or 0 if the environment is empty.
+.SH "SEE ALSO"
+environ(7)
diff --git a/env.h b/env.h
new file mode 100644 (file)
index 0000000..9befc79
--- /dev/null
+++ b/env.h
@@ -0,0 +1,17 @@
+#ifndef ENV_H
+#define ENV_H
+
+extern int env_isinit;
+
+extern int env_init();
+extern int env_put();
+extern int env_put2();
+extern int env_unset();
+extern /*@null@*/char *env_get();
+extern char *env_pick();
+extern void env_clear();
+extern char *env_findeq();
+
+extern char **environ;
+
+#endif
diff --git a/env=0 b/env=0
new file mode 100644 (file)
index 0000000..567ab81
--- /dev/null
+++ b/env=0
@@ -0,0 +1 @@
+env.3
diff --git a/env=l b/env=l
new file mode 100644 (file)
index 0000000..21db6c5
--- /dev/null
+++ b/env=l
@@ -0,0 +1 @@
+envread.o
diff --git a/envread.c b/envread.c
new file mode 100644 (file)
index 0000000..80185de
--- /dev/null
+++ b/envread.c
@@ -0,0 +1,30 @@
+#include "env.h"
+#include "str.h"
+
+extern /*@null@*/char *env_get(s)
+char *s;
+{
+  int i;
+  unsigned int slen;
+  char *envi;
+  slen = str_len(s);
+  for (i = 0;envi = environ[i];++i)
+    if ((!str_diffn(s,envi,slen)) && (envi[slen] == '='))
+      return envi + slen + 1;
+  return 0;
+}
+
+extern char *env_pick()
+{
+  return environ[0];
+}
+
+extern char *env_findeq(s)
+char *s;
+{
+  for (;*s;++s)
+    if (*s == '=')
+      return s;
+  return 0;
+}
diff --git a/error.3 b/error.3
new file mode 100644 (file)
index 0000000..e955b35
--- /dev/null
+++ b/error.3
@@ -0,0 +1,45 @@
+.TH error 3
+.SH NAME
+error \- syscall error codes
+.SH SYNTAX
+.B #include <error.h>
+
+extern int \fBerrno\fP;
+
+extern int \fBerror_intr\fP;
+.br
+extern int \fBerror_nomem\fP;
+.br
+extern int \fBerror_noent\fP;
+.br
+extern int \fBerror_txtbsy\fP;
+.br
+extern int \fBerror_io\fP;
+.br
+extern int \fBerror_exist\fP;
+.br
+extern int \fBerror_timeout\fP;
+.br
+extern int \fBerror_inprogress\fP;
+.br
+extern int \fBerror_wouldblock\fP;
+.br
+extern int \fBerror_again\fP;
+.br
+extern int \fBerror_pipe\fP;
+.br
+extern int \fBerror_perm\fP;
+.br
+extern int \fBerror_acces\fP;
+.SH DESCRIPTION
+UNIX syscalls provide detailed error codes in the
+.B errno
+variable.
+The
+.B error
+library provides portable names for a variety of possible
+.B errno
+values.
+.SH "SEE ALSO"
+error_str(3),
+error_temp(3)
diff --git a/error.c b/error.c
new file mode 100644 (file)
index 0000000..d51304f
--- /dev/null
+++ b/error.c
@@ -0,0 +1,95 @@
+#include <errno.h>
+#include "error.h"
+
+/* warning: as coverage improves here, should update error_{str,temp} */
+
+int error_intr =
+#ifdef EINTR
+EINTR;
+#else
+-1;
+#endif
+
+int error_nomem =
+#ifdef ENOMEM
+ENOMEM;
+#else
+-2;
+#endif
+
+int error_noent = 
+#ifdef ENOENT
+ENOENT;
+#else
+-3;
+#endif
+
+int error_txtbsy =
+#ifdef ETXTBSY
+ETXTBSY;
+#else
+-4;
+#endif
+
+int error_io =
+#ifdef EIO
+EIO;
+#else
+-5;
+#endif
+
+int error_exist =
+#ifdef EEXIST
+EEXIST;
+#else
+-6;
+#endif
+
+int error_timeout =
+#ifdef ETIMEDOUT
+ETIMEDOUT;
+#else
+-7;
+#endif
+
+int error_inprogress =
+#ifdef EINPROGRESS
+EINPROGRESS;
+#else
+-8;
+#endif
+
+int error_wouldblock =
+#ifdef EWOULDBLOCK
+EWOULDBLOCK;
+#else
+-9;
+#endif
+
+int error_again =
+#ifdef EAGAIN
+EAGAIN;
+#else
+-10;
+#endif
+
+int error_pipe =
+#ifdef EPIPE
+EPIPE;
+#else
+-11;
+#endif
+
+int error_perm =
+#ifdef EPERM
+EPERM;
+#else
+-12;
+#endif
+
+int error_acces =
+#ifdef EACCES
+EACCES;
+#else
+-13;
+#endif
diff --git a/error.h b/error.h
new file mode 100644 (file)
index 0000000..01bd3dc
--- /dev/null
+++ b/error.h
@@ -0,0 +1,23 @@
+#ifndef ERROR_H
+#define ERROR_H
+
+extern int errno;
+
+extern int error_intr;
+extern int error_nomem;
+extern int error_noent;
+extern int error_txtbsy;
+extern int error_io;
+extern int error_exist;
+extern int error_timeout;
+extern int error_inprogress;
+extern int error_wouldblock;
+extern int error_again;
+extern int error_pipe;
+extern int error_perm;
+extern int error_acces;
+
+extern char *error_str();
+extern int error_temp();
+
+#endif
diff --git a/error=0 b/error=0
new file mode 100644 (file)
index 0000000..d7bbc09
--- /dev/null
+++ b/error=0
@@ -0,0 +1 @@
+error.3
diff --git a/error=l b/error=l
new file mode 100644 (file)
index 0000000..2a9cb84
--- /dev/null
+++ b/error=l
@@ -0,0 +1,2 @@
+error.o
+error_str.o
diff --git a/error_str.3 b/error_str.3
new file mode 100644 (file)
index 0000000..62043c4
--- /dev/null
@@ -0,0 +1,19 @@
+.TH error_str 3
+.SH NAME
+error_str \- names for syscall error codes
+.SH SYNTAX
+.B #include <error.h>
+
+char *\fBerror_str\fP(\fIe\fR);
+
+int \fIe\fR;
+.SH DESCRIPTION
+.B error_str
+returns a printable string describing syscall error code
+.IR e .
+Normally
+.I e
+is
+.BR errno .
+.SH "SEE ALSO"
+error(3)
diff --git a/error_str.c b/error_str.c
new file mode 100644 (file)
index 0000000..804d1fa
--- /dev/null
@@ -0,0 +1,276 @@
+#include <errno.h>
+#include "error.h"
+
+#define X(e,s) if (i == e) return s;
+
+char *error_str(i)
+int i;
+{
+  X(0,"no error")
+  X(error_intr,"interrupted system call")
+  X(error_nomem,"out of memory")
+  X(error_noent,"file does not exist")
+  X(error_txtbsy,"text busy")
+  X(error_io,"input/output error")
+  X(error_exist,"file already exists")
+  X(error_timeout,"timed out")
+  X(error_inprogress,"operation in progress")
+  X(error_again,"temporary failure")
+  X(error_wouldblock,"input/output would block")
+  X(error_pipe,"broken pipe")
+  X(error_perm,"permission denied")
+  X(error_acces,"access denied")
+#ifdef ESRCH
+  X(ESRCH,"no such process")
+#endif
+#ifdef ENXIO
+  X(ENXIO,"device not configured")
+#endif
+#ifdef E2BIG
+  X(E2BIG,"argument list too long")
+#endif
+#ifdef ENOEXEC
+  X(ENOEXEC,"exec format error")
+#endif
+#ifdef EBADF
+  X(EBADF,"file descriptor not open")
+#endif
+#ifdef ECHILD
+  X(ECHILD,"no child processes")
+#endif
+#ifdef EDEADLK
+  X(EDEADLK,"operation would cause deadlock")
+#endif
+#ifdef EFAULT
+  X(EFAULT,"bad address")
+#endif
+#ifdef ENOTBLK
+  X(ENOTBLK,"not a block device")
+#endif
+#ifdef EBUSY
+  X(EBUSY,"device busy")
+#endif
+#ifdef EXDEV
+  X(EXDEV,"cross-device link")
+#endif
+#ifdef ENODEV
+  X(ENODEV,"device does not support operation")
+#endif
+#ifdef ENOTDIR
+  X(ENOTDIR,"not a directory")
+#endif
+#ifdef EISDIR
+  X(EISDIR,"is a directory")
+#endif
+#ifdef EINVAL
+  X(EINVAL,"invalid argument")
+#endif
+#ifdef ENFILE
+  X(ENFILE,"system cannot open more files")
+#endif
+#ifdef EMFILE
+  X(EMFILE,"process cannot open more files")
+#endif
+#ifdef ENOTTY
+  X(ENOTTY,"not a tty")
+#endif
+#ifdef EFBIG
+  X(EFBIG,"file too big")
+#endif
+#ifdef ENOSPC
+  X(ENOSPC,"out of disk space")
+#endif
+#ifdef ESPIPE
+  X(ESPIPE,"unseekable descriptor")
+#endif
+#ifdef EROFS
+  X(EROFS,"read-only file system")
+#endif
+#ifdef EMLINK
+  X(EMLINK,"too many links")
+#endif
+#ifdef EDOM
+  X(EDOM,"input out of range")
+#endif
+#ifdef ERANGE
+  X(ERANGE,"output out of range")
+#endif
+#ifdef EALREADY
+  X(EALREADY,"operation already in progress")
+#endif
+#ifdef ENOTSOCK
+  X(ENOTSOCK,"not a socket")
+#endif
+#ifdef EDESTADDRREQ
+  X(EDESTADDRREQ,"destination address required")
+#endif
+#ifdef EMSGSIZE
+  X(EMSGSIZE,"message too long")
+#endif
+#ifdef EPROTOTYPE
+  X(EPROTOTYPE,"incorrect protocol type")
+#endif
+#ifdef ENOPROTOOPT
+  X(ENOPROTOOPT,"protocol not available")
+#endif
+#ifdef EPROTONOSUPPORT
+  X(EPROTONOSUPPORT,"protocol not supported")
+#endif
+#ifdef ESOCKTNOSUPPORT
+  X(ESOCKTNOSUPPORT,"socket type not supported")
+#endif
+#ifdef EOPNOTSUPP
+  X(EOPNOTSUPP,"operation not supported")
+#endif
+#ifdef EPFNOSUPPORT
+  X(EPFNOSUPPORT,"protocol family not supported")
+#endif
+#ifdef EAFNOSUPPORT
+  X(EAFNOSUPPORT,"address family not supported")
+#endif
+#ifdef EADDRINUSE
+  X(EADDRINUSE,"address already used")
+#endif
+#ifdef EADDRNOTAVAIL
+  X(EADDRNOTAVAIL,"address not available")
+#endif
+#ifdef ENETDOWN
+  X(ENETDOWN,"network down")
+#endif
+#ifdef ENETUNREACH
+  X(ENETUNREACH,"network unreachable")
+#endif
+#ifdef ENETRESET
+  X(ENETRESET,"network reset")
+#endif
+#ifdef ECONNABORTED
+  X(ECONNABORTED,"connection aborted")
+#endif
+#ifdef ECONNRESET
+  X(ECONNRESET,"connection reset")
+#endif
+#ifdef ENOBUFS
+  X(ENOBUFS,"out of buffer space")
+#endif
+#ifdef EISCONN
+  X(EISCONN,"already connected")
+#endif
+#ifdef ENOTCONN
+  X(ENOTCONN,"not connected")
+#endif
+#ifdef ESHUTDOWN
+  X(ESHUTDOWN,"socket shut down")
+#endif
+#ifdef ETOOMANYREFS
+  X(ETOOMANYREFS,"too many references")
+#endif
+#ifdef ECONNREFUSED
+  X(ECONNREFUSED,"connection refused")
+#endif
+#ifdef ELOOP
+  X(ELOOP,"symbolic link loop")
+#endif
+#ifdef ENAMETOOLONG
+  X(ENAMETOOLONG,"file name too long")
+#endif
+#ifdef EHOSTDOWN
+  X(EHOSTDOWN,"host down")
+#endif
+#ifdef EHOSTUNREACH
+  X(EHOSTUNREACH,"host unreachable")
+#endif
+#ifdef ENOTEMPTY
+  X(ENOTEMPTY,"directory not empty")
+#endif
+#ifdef EPROCLIM
+  X(EPROCLIM,"too many processes")
+#endif
+#ifdef EUSERS
+  X(EUSERS,"too many users")
+#endif
+#ifdef EDQUOT
+  X(EDQUOT,"disk quota exceeded")
+#endif
+#ifdef ESTALE
+  X(ESTALE,"stale NFS file handle")
+#endif
+#ifdef EREMOTE
+  X(EREMOTE,"too many levels of remote in path")
+#endif
+#ifdef EBADRPC
+  X(EBADRPC,"RPC structure is bad")
+#endif
+#ifdef ERPCMISMATCH
+  X(ERPCMISMATCH,"RPC version mismatch")
+#endif
+#ifdef EPROGUNAVAIL
+  X(EPROGUNAVAIL,"RPC program unavailable")
+#endif
+#ifdef EPROGMISMATCH
+  X(EPROGMISMATCH,"program version mismatch")
+#endif
+#ifdef EPROCUNAVAIL
+  X(EPROCUNAVAIL,"bad procedure for program")
+#endif
+#ifdef ENOLCK
+  X(ENOLCK,"no locks available")
+#endif
+#ifdef ENOSYS
+  X(ENOSYS,"system call not available")
+#endif
+#ifdef EFTYPE
+  X(EFTYPE,"bad file type")
+#endif
+#ifdef EAUTH
+  X(EAUTH,"authentication error")
+#endif
+#ifdef ENEEDAUTH
+  X(ENEEDAUTH,"not authenticated")
+#endif
+#ifdef ENOSTR
+  X(ENOSTR,"not a stream device")
+#endif
+#ifdef ETIME
+  X(ETIME,"timer expired")
+#endif
+#ifdef ENOSR
+  X(ENOSR,"out of stream resources")
+#endif
+#ifdef ENOMSG
+  X(ENOMSG,"no message of desired type")
+#endif
+#ifdef EBADMSG
+  X(EBADMSG,"bad message type")
+#endif
+#ifdef EIDRM
+  X(EIDRM,"identifier removed")
+#endif
+#ifdef ENONET
+  X(ENONET,"machine not on network")
+#endif
+#ifdef ERREMOTE
+  X(ERREMOTE,"object not local")
+#endif
+#ifdef ENOLINK
+  X(ENOLINK,"link severed")
+#endif
+#ifdef EADV
+  X(EADV,"advertise error")
+#endif
+#ifdef ESRMNT
+  X(ESRMNT,"srmount error")
+#endif
+#ifdef ECOMM
+  X(ECOMM,"communication error")
+#endif
+#ifdef EPROTO
+  X(EPROTO,"protocol error")
+#endif
+#ifdef EMULTIHOP
+  X(EMULTIHOP,"multihop attempted")
+#endif
+#ifdef EREMCHG
+  X(EREMCHG,"remote address changed")
+#endif
+  return "unknown error";
+}
diff --git a/error_str=0 b/error_str=0
new file mode 100644 (file)
index 0000000..3f2616e
--- /dev/null
@@ -0,0 +1 @@
+error_str.3
diff --git a/error_temp.3 b/error_temp.3
new file mode 100644 (file)
index 0000000..2f8229d
--- /dev/null
@@ -0,0 +1,27 @@
+.TH error_temp 3
+.SH NAME
+error_temp \- identify soft syscall error codes
+.SH SYNTAX
+.B #include <error.h>
+
+int \fBerror_temp\fP(\fIe\fR);
+
+int \fIe\fR;
+.SH DESCRIPTION
+.B error_temp
+returns 1 if syscall error code
+.I e
+is a soft error, 0 if it is a hard error.
+Normally
+.I e
+is
+.BR errno .
+
+A hard error is persistent:
+file not found, read-only file system, symbolic link loop, etc.
+
+A soft error is usually transient:
+out of memory, out of disk space, I/O error, disk quota exceeded,
+connection refused, host unreachable, etc.
+.SH "SEE ALSO"
+error(3)
diff --git a/error_temp=0 b/error_temp=0
new file mode 100644 (file)
index 0000000..9b5ad0f
--- /dev/null
@@ -0,0 +1 @@
+error_temp.3
diff --git a/exit.h b/exit.h
new file mode 100644 (file)
index 0000000..39011c8
--- /dev/null
+++ b/exit.h
@@ -0,0 +1,6 @@
+#ifndef EXIT_H
+#define EXIT_H
+
+extern void _exit();
+
+#endif
diff --git a/ezmlm-list.1 b/ezmlm-list.1
new file mode 100644 (file)
index 0000000..38e140a
--- /dev/null
@@ -0,0 +1,22 @@
+.TH ezmlm-list 1
+.SH NAME
+ezmlm-list \- show the addresses on a mailing list
+.SH SYNOPSIS
+.B ezmlm-list
+.I dir
+.SH DESCRIPTION
+.B ezmlm-list
+prints the subscriber list for the mailing list stored in
+.IR dir ,
+one address per line.
+
+.B WARNING:
+Unless you have disabled automatic subscriptions,
+the addresses in a mailing list are under the control
+of a possibly malicious remote user.
+.B ezmlm-list
+does not strip control characters.
+.SH "SEE ALSO"
+ezmlm-sub(1),
+ezmlm-unsub(1),
+ezmlm(5)
diff --git a/ezmlm-list.c b/ezmlm-list.c
new file mode 100644 (file)
index 0000000..c6ec801
--- /dev/null
@@ -0,0 +1,62 @@
+#include "stralloc.h"
+#include "substdio.h"
+#include "getln.h"
+#include "strerr.h"
+#include "error.h"
+#include "readwrite.h"
+#include "exit.h"
+#include "open.h"
+
+#define FATAL "ezmlm-list: fatal: "
+void die_write()
+{
+  strerr_die2sys(111,FATAL,"unable to write: ");
+}
+
+char outbuf[1024];
+substdio out = SUBSTDIO_FDBUF(write,1,outbuf,sizeof(outbuf));
+char inbuf[1024];
+substdio in;
+
+stralloc line = {0};
+
+char fn[14] = "subscribers/?";
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  char *dir;
+  int fd;
+  int match;
+
+  dir = argv[1];
+  if (!dir) strerr_die1x(100,"ezmlm-list: usage: ezmlm-list dir");
+
+  if (chdir(dir) == -1)
+    strerr_die4sys(111,FATAL,"unable to switch to ",dir,": ");
+
+  for (fn[12] = 64;fn[12] < 64 + 53;++fn[12]) {
+    fd = open_read(fn);
+    if (fd == -1) {
+      if (errno != error_noent)
+       strerr_die4sys(111,FATAL,"unable to open ",fn,": ");
+    }
+    else {
+      substdio_fdbuf(&in,read,fd,inbuf,sizeof(inbuf));
+      for (;;) {
+        if (getln(&in,&line,&match,'\0') == -1)
+         strerr_die4sys(111,FATAL,"unable to read ",fn,": ");
+       if (!match) break;
+       if (line.s[str_chr(line.s,'\n')])
+         strerr_die3x(111,FATAL,"newline in ",fn);
+       if (substdio_puts(&out,line.s + 1)) die_write();
+       if (substdio_put(&out,"\n",1) == -1) die_write();
+      }
+    }
+
+  }
+
+  if (substdio_flush(&out) == -1) die_write();
+  _exit(0);
+}
diff --git a/ezmlm-list=0 b/ezmlm-list=0
new file mode 100644 (file)
index 0000000..a4c12ea
--- /dev/null
@@ -0,0 +1 @@
+ezmlm-list.1
diff --git a/ezmlm-list=x b/ezmlm-list=x
new file mode 100644 (file)
index 0000000..acaa3c7
--- /dev/null
@@ -0,0 +1,8 @@
+strerr.a
+getln.a
+substdio.a
+stralloc.a
+alloc.a
+error.a
+open.a
+str.a
diff --git a/ezmlm-make.1 b/ezmlm-make.1
new file mode 100644 (file)
index 0000000..834feed
--- /dev/null
@@ -0,0 +1,87 @@
+.TH ezmlm-make 1
+.SH NAME
+ezmlm-make \- create a new mailing list
+.SH SYNOPSIS
+.B ezmlm-make
+[
+.B \-aApP
+]
+.I dir
+.I dot
+.I local
+.I host
+.SH DESCRIPTION
+.B ezmlm-make
+sets up a new mailing list,
+.IR local\fB@\fIhost ,
+along with several extra addresses to handle administrative requests.
+
+All mailing list information is stored in a new directory,
+.IR dir .
+.I dir
+must be an absolute pathname, starting with a slash.
+
+.B ezmlm-make
+sets up four
+.B .qmail
+files:
+.IR dot ,
+.IR dot\fB-owner ,
+.IR dot\fB-return-default ,
+and
+.IR dot\fB-default .
+You should make sure that messages to
+.IR local\fB@\fIhost ,
+.IR local\fB-owner@\fIhost ,
+etc. are controlled by
+these
+.B .qmail
+files.
+
+Typical use of
+.B ezmlm-make
+by a normal user:
+
+.EX
+   ezmlm-make ~/SOS ~/.qmail-sos joe-sos isp.net
+.EE
+
+Typical use of
+.B ezmlm-make
+by
+.BR alias :
+
+.EX
+   ezmlm-make ~alias/SOS ~alias/.qmail-sos sos isp.net
+.EE
+.SH OPTIONS
+.TP 5
+.B \-a
+(Default.) Archived.
+.B ezmlm-make
+will touch
+.IR dir\fB/archived ,
+so that
+.B ezmlm-send
+will archive new messages.
+.TP
+.B \-A
+Not archived.
+.TP 5
+.B \-p
+(Default.) Public.
+.B ezmlm-make
+will touch
+.IR dir\fB/public ,
+so that
+.B ezmlm-manage
+will respond to administrative requests.
+.TP
+.B \-P
+Private.
+.SH "SEE ALSO"
+ezmlm-manage(1),
+ezmlm-send(1),
+ezmlm-sub(1),
+ezmlm-unsub(1),
+ezmlm(5)
diff --git a/ezmlm-make.c b/ezmlm-make.c
new file mode 100644 (file)
index 0000000..09d2b3e
--- /dev/null
@@ -0,0 +1,435 @@
+#include <sys/types.h>
+#include <sys/time.h>
+#include "sgetopt.h"
+#include "stralloc.h"
+#include "strerr.h"
+#include "exit.h"
+#include "readwrite.h"
+#include "open.h"
+#include "substdio.h"
+#include "str.h"
+#include "auto_bin.h"
+
+#define FATAL "ezmlm-make: fatal: "
+
+void die_usage()
+{
+  strerr_die1x(100,"ezmlm-make: usage: ezmlm-make [ -aApP ] dir dot local host");
+}
+void die_relative()
+{
+  strerr_die2x(100,FATAL,"dir must start with slash");
+}
+void die_newline()
+{
+  strerr_die2x(100,FATAL,"newlines not allowed");
+}
+void die_quote()
+{
+  strerr_die2x(100,FATAL,"quotes not allowed");
+}
+void die_nomem()
+{
+  strerr_die2x(111,FATAL,"out of memory");
+}
+
+stralloc key = {0};
+struct timeval tv;
+
+void keyadd(u)
+unsigned long u;
+{
+  char ch;
+  ch = u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
+  ch = u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
+  ch = u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
+  ch = u; if (!stralloc_append(&key,&ch)) die_nomem();
+}
+
+void keyaddtime()
+{
+  gettimeofday(&tv,(struct timezone *) 0);
+  keyadd(tv.tv_usec);
+}
+
+char *dir;
+char *dot;
+char *local;
+char *host;
+
+stralloc dotplus = {0};
+stralloc dirplus = {0};
+
+void dirplusmake(slash)
+char *slash;
+{
+  if (!stralloc_copys(&dirplus,dir)) die_nomem();
+  if (!stralloc_cats(&dirplus,slash)) die_nomem();
+  if (!stralloc_0(&dirplus)) die_nomem();
+}
+
+void linkdotdir(dash,slash)
+char *dash;
+char *slash;
+{
+  if (!stralloc_copys(&dotplus,dot)) die_nomem();
+  if (!stralloc_cats(&dotplus,dash)) die_nomem();
+  if (!stralloc_0(&dotplus)) die_nomem();
+  dirplusmake(slash);
+  if (symlink(dirplus.s,dotplus.s) == -1)
+    strerr_die4sys(111,FATAL,"unable to create ",dotplus.s,": ");
+  keyaddtime();
+}
+
+void dcreate(slash)
+char *slash;
+{
+  dirplusmake(slash);
+  if (mkdir(dirplus.s,0755) == -1)
+    strerr_die4sys(111,FATAL,"unable to create ",dirplus.s,": ");
+  keyaddtime();
+}
+
+substdio ss;
+char ssbuf[SUBSTDIO_OUTSIZE];
+
+void fopen(slash)
+char *slash;
+{
+  int fd;
+
+  dirplusmake(slash);
+  fd = open_trunc(dirplus.s);
+  if (fd == -1)
+    strerr_die4sys(111,FATAL,"unable to create ",dirplus.s,": ");
+
+  substdio_fdbuf(&ss,write,fd,ssbuf,sizeof(ssbuf));
+}
+
+void fput(buf,len)
+char *buf;
+unsigned int len;
+{
+  if (substdio_bput(&ss,buf,len) == -1)
+    strerr_die4sys(111,FATAL,"unable to write to ",dirplus.s,": ");
+}
+void fputs(buf)
+char *buf;
+{
+  if (substdio_bputs(&ss,buf) == -1)
+    strerr_die4sys(111,FATAL,"unable to write to ",dirplus.s,": ");
+}
+
+void fclose()
+{
+  if (substdio_flush(&ss) == -1)
+    strerr_die4sys(111,FATAL,"unable to write to ",dirplus.s,": ");
+  if (fsync(ss.fd) == -1)
+    strerr_die4sys(111,FATAL,"unable to write to ",dirplus.s,": ");
+  if (close(ss.fd) == -1) /* NFS stupidity */
+    strerr_die4sys(111,FATAL,"unable to write to ",dirplus.s,": ");
+  keyaddtime();
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  int opt;
+  int flagarchived;
+  int flagpublic;
+
+  keyadd(getpid());
+  keyadd(getppid());
+  keyadd(getuid());
+  keyadd(getgid());
+  gettimeofday(&tv,(struct timezone *) 0);
+  keyadd(tv.tv_sec);
+
+  umask(077);
+
+  flagarchived = 1;
+  flagpublic = 1;
+
+  while ((opt = getopt(argc,argv,"aApP")) != opteof)
+    switch(opt) {
+      case 'a': flagarchived = 1; break;
+      case 'A': flagarchived = 0; break;
+      case 'p': flagpublic = 1; break;
+      case 'P': flagpublic = 0; break;
+      default:
+       die_usage();
+    }
+  argv += optind;
+
+  if (!(dir = *argv++)) die_usage();
+  if (!(dot = *argv++)) die_usage();
+  if (!(local = *argv++)) die_usage();
+  if (!(host = *argv++)) die_usage();
+
+  if (dir[0] != '/') die_relative();
+  if (dir[str_chr(dir,'\'')]) die_quote();
+  if (dir[str_chr(dir,'\n')]) die_newline();
+  if (local[str_chr(local,'\n')]) die_newline();
+  if (host[str_chr(host,'\n')]) die_newline();
+
+  dcreate("");
+  dcreate("/archive");
+  dcreate("/subscribers");
+  dcreate("/bounce");
+  dcreate("/text");
+
+
+  linkdotdir("-owner","/owner");
+  linkdotdir("-default","/manager");
+  linkdotdir("-return-default","/bouncer");
+  linkdotdir("","/editor");
+
+  fopen("/lock"); fclose();
+  fopen("/lockbounce"); fclose();
+  if (flagpublic) {
+    fopen("/public"); fclose();
+  }
+  if (flagarchived) {
+    fopen("/archived"); fclose();
+  }
+  fopen("/num"); fputs("0\n"); fclose();
+  fopen("/inhost"); fputs(host); fputs("\n"); fclose();
+  fopen("/outhost"); fputs(host); fputs("\n"); fclose();
+  fopen("/inlocal"); fputs(local); fputs("\n"); fclose();
+  fopen("/outlocal"); fputs(local); fputs("\n"); fclose();
+
+  fopen("/mailinglist");
+  fputs("contact ");
+  fputs(local); fputs("-help@"); fputs(host); fputs("; run by ezmlm\n");
+  fclose();
+
+  fopen("/owner");
+  fputs(dir); fputs("/Mailbox\n");
+  fputs("|"); fputs(auto_bin); fputs("/ezmlm-warn '"); fputs(dir);
+  fputs("' || exit 0\n");
+  fclose();
+
+  fopen("/manager");
+  fputs("|"); fputs(auto_bin); fputs("/ezmlm-manage '"); fputs(dir); fputs("'\n");
+  fputs("|"); fputs(auto_bin); fputs("/ezmlm-warn '"); fputs(dir);
+  fputs("' || exit 0\n");
+  fclose();
+
+  fopen("/editor");
+  fputs("|"); fputs(auto_bin); fputs("/ezmlm-reject\n");
+  fputs("|"); fputs(auto_bin); fputs("/ezmlm-send '"); fputs(dir); fputs("'\n");
+  fputs("|"); fputs(auto_bin); fputs("/ezmlm-warn '"); fputs(dir);
+  fputs("' || exit 0\n");
+  fclose();
+
+  fopen("/bouncer");
+  fputs("|"); fputs(auto_bin); fputs("/ezmlm-warn '"); fputs(dir);
+  fputs("' || exit 0\n");
+  fputs("|"); fputs(auto_bin); fputs("/ezmlm-weed\n");
+  fputs("|"); fputs(auto_bin); fputs("/ezmlm-return '"); fputs(dir); fputs("'\n");
+  fclose();
+
+  fopen("/headerremove");
+  fputs("\
+return-path\n\
+return-receipt-to\n\
+content-length\n\
+");
+  fclose();
+
+  fopen("/headeradd");
+  fclose();
+
+
+  fopen("/text/top");
+  fputs("Hi! This is the ezmlm program. I'm managing the\n");
+  fputs(local); fputs("@"); fputs(host); fputs(" mailing list.\n\n");
+  fclose();
+
+  fopen("/text/bottom");
+  fputs("\n--- Here are the ezmlm command addresses.\n\
+\n\
+I can handle administrative requests automatically.\n\
+Just send an empty note to any of these addresses:\n\n   <");
+  fputs(local); fputs("-subscribe@"); fputs(host); fputs(">:\n");
+  fputs("   Receive future messages sent to the mailing list.\n\n   <");
+  fputs(local); fputs("-unsubscribe@"); fputs(host); fputs(">:\n");
+  fputs("   Stop receiving messages.\n\n   <");
+  fputs(local); fputs("-get.12345@"); fputs(host); fputs(">:\n");
+  fputs("   Retrieve a copy of message 12345 from the archive.\n\
+\n\
+DO NOT SEND ADMINISTRATIVE REQUESTS TO THE MAILING LIST!\n\
+If you do, I won't see them, and subscribers will yell at you.\n\
+\n\
+To specify God@heaven.af.mil as your subscription address, send mail\n\
+to <");
+  fputs(local); fputs("-subscribe-God=heaven.af.mil@"); fputs(host);
+  fputs(">.\n\
+I'll send a confirmation message to that address; when you receive that\n\
+message, simply reply to it to complete your subscription.\n\
+\n");
+  fputs("\n--- Below this line is a copy of the request I received.\n\n");
+  fclose();
+
+  fopen("/text/sub-confirm");
+  fputs("To confirm that you would like\n\
+\n\
+!A\n\
+\n\
+added to this mailing list, please send an empty reply to this address:\n\
+\n\
+!R\n\
+\n\
+Your mailer should have a Reply feature that uses this address automatically.\n\
+\n\
+This confirmation serves two purposes. First, it verifies that I am able\n\
+to get mail through to you. Second, it protects you in case someone\n\
+forges a subscription request in your name.\n\
+\n");
+  fclose();
+
+  fopen("/text/unsub-confirm");
+  fputs("To confirm that you would like\n\
+\n\
+!A\n\
+\n\
+removed from this mailing list, please send an empty reply to this address:\n\
+\n\
+!R\n\
+\n\
+Your mailer should have a Reply feature that uses this address automatically.\n\
+\n\
+I haven't checked whether your address is currently on the mailing list.\n\
+To see what address you used to subscribe, look at the messages you are\n\
+receiving from the mailing list. Each message has your address hidden\n\
+inside its return path; for example, God@heaven.af.mil receives messages\n\
+with return path ...-God=heaven.af.mil.\n\
+\n");
+  fclose();
+
+  fopen("/text/sub-ok");
+  fputs("Acknowledgment: I have added the address\n\
+\n\
+!A\n\
+\n\
+to this mailing list.\n\
+\n");
+  fclose();
+
+  fopen("/text/unsub-ok");
+  fputs("Acknowledgment: I have removed the address\n\
+\n\
+!A\n\
+\n\
+from this mailing list.\n\
+\n");
+  fclose();
+
+  fopen("/text/sub-nop");
+  fputs("Acknowledgment: The address\n\
+\n\
+!A\n\
+\n\
+is on this mailing list.\n\
+\n");
+  fclose();
+
+  fopen("/text/unsub-nop");
+  fputs("Acknowledgment: The address\n\
+\n\
+!A\n\
+\n\
+is not on this mailing list.\n\
+\n");
+  fclose();
+
+  fopen("/text/sub-bad");
+  fputs("Oops, that confirmation number appears to be invalid.\n\
+\n\
+The most common reason for invalid numbers is expiration. I have to\n\
+receive confirmation of each request within ten days.\n\
+\n\
+I've set up a new confirmation number. To confirm that you would like\n\
+\n\
+!A\n\
+\n\
+added to this mailing list, please send an empty reply to this address:\n\
+\n\
+!R\n\
+\n\
+Sorry for the trouble.\n\
+\n");
+  fclose();
+
+  fopen("/text/unsub-bad");
+  fputs("Oops, that confirmation number appears to be invalid.\n\
+\n\
+The most common reason for invalid numbers is expiration. I have to\n\
+receive confirmation of each request within ten days.\n\
+\n\
+I've set up a new confirmation number. To confirm that you would like\n\
+\n\
+!A\n\
+\n\
+removed from this mailing list, please send an empty reply to this address:\n\
+\n\
+!R\n\
+\n\
+Sorry for the trouble.\n\
+\n");
+  fclose();
+
+  fopen("/text/get-bad");
+  fputs("Sorry, I don't see that message.\n\n");
+  fclose();
+
+  fopen("/text/bounce-bottom");
+  fputs("\n\
+--- Below this line is a copy of the bounce message I received.\n\n");
+  fclose();
+
+  fopen("/text/bounce-warn");
+  fputs("\n\
+Messages to you seem to have been bouncing. I've attached a copy of\n\
+the first bounce message I received.\n\
+\n\
+If this message bounces too, I will send you a probe. If the probe bounces,\n\
+I will remove your address from the mailing list, without further notice.\n\
+\n");
+  fclose();
+
+  fopen("/text/bounce-probe");
+  fputs("\n\
+Messages to you seem to have been bouncing. I sent you a warning\n\
+message, but it bounced. I've attached a copy of the bounce message.\n\
+\n\
+This is a probe to check whether your address is reachable. If this\n\
+probe bounces, I will remove your address from the mailing list, without\n\
+further notice.\n\
+\n");
+  fclose();
+
+  fopen("/text/bounce-num");
+  fputs("\n\
+I've kept a list of which messages bounced from your address. Copies of\n\
+these messages may be in the archive. To get message 12345 from the\n\
+archive, send an empty note to ");
+  fputs(local); fputs("-get.12345@"); fputs(host); fputs(".\n\
+Here are the message numbers:\n\
+\n");
+  fclose();
+
+  fopen("/text/help");
+  fputs("\
+This is a generic help message. The message I received wasn't sent to\n\
+any of my command addresses.\n\
+\n");
+  fclose();
+
+  fopen("/key");
+  fput(key.s,key.len);
+  fclose();
+
+  _exit(0);
+}
diff --git a/ezmlm-make=0 b/ezmlm-make=0
new file mode 100644 (file)
index 0000000..d49ded8
--- /dev/null
@@ -0,0 +1 @@
+ezmlm-make.1
diff --git a/ezmlm-make=x b/ezmlm-make=x
new file mode 100644 (file)
index 0000000..1295af4
--- /dev/null
@@ -0,0 +1,9 @@
+auto_bin.o
+open.a
+getopt.a
+substdio.a
+strerr.a
+stralloc.a
+alloc.a
+error.a
+str.a
diff --git a/ezmlm-manage.1 b/ezmlm-manage.1
new file mode 100644 (file)
index 0000000..a68438f
--- /dev/null
@@ -0,0 +1,129 @@
+.TH ezmlm-manage 1
+.SH NAME
+ezmlm-manage \- automatically manage a mailing list
+.SH SYNOPSIS
+.B ezmlm-manage
+.I dir
+.SH DESCRIPTION
+.B ezmlm-manage
+handles administrative requests for the mailing list
+stored in
+.IR dir .
+
+.B ezmlm-manage
+is normally invoked from a
+.B .qmail
+file.
+It reads a mail message from its standard input,
+and a mail envelope from the
+.BR SENDER ,
+.BR LOCAL ,
+and
+.BR HOST
+environment variables.
+
+.B ezmlm-manage
+expects
+.B LOCAL
+to be of the form
+.IR list\fB-\fIaction\fB-\fIbox\fB=\fIdomain .
+Here
+.I list
+is the first line of
+.IR dir\fB/inlocal ,
+.I action
+is a request,
+and
+.I box\fB@\fIdomain
+is the target of the request.
+.B ezmlm-manage
+sends a response to the target.
+It attaches the original message to the end of its response.
+
+.B LOCAL
+may instead be of the form
+.IR list\fB-\fIaction .
+Then the envelope sender
+is used as the target.
+
+.B ezmlm-manage
+expects
+.B HOST
+to match the first line of
+.IR dir\fB/inhost .
+
+.B ezmlm-manage
+copies
+.I dir\fB/mailinglist
+into a
+.B Mailing-List
+field in its response.
+If the incoming message has a
+.B Mailing-List
+field,
+.B ezmlm-manage
+refuses to respond.
+.B ezmlm-manage
+also refuses to respond to bounce messages.
+.SH SUBSCRIPTIONS
+If
+.I action
+is
+.BR sc.\fIcookie ,
+where
+.I cookie
+is an appropriate code
+(depending on the target, the approximate time, and other factors),
+.B ezmlm-manage
+adds the target to the mailing list.
+
+If
+.I action
+is
+.BR subscribe ,
+.B ezmlm-manage
+does not subscribe the target,
+but it identifies the right
+.BR sc.\fIcookie
+address in its response.
+
+This confirmation mechanism
+(1) verifies that the target is reachable 
+and
+(2) protects the target against forged subscription requests.
+
+Actions of
+.B uc.\fIcookie
+and
+.B unsubscribe
+are used in the same way to delete the target from the mailing list.
+
+If
+.I dir\fB/public
+does not exist,
+.B ezmlm-manage
+rejects all subscription and unsubscription attempts.
+.SH "ARCHIVE RETRIEVALS"
+If
+.I action
+is
+.BR get.\fInum ,
+.B ezmlm-manage
+sends back message
+.I num
+from
+.IR dir\fB/archive .
+
+If
+.I dir\fB/public
+does not exist,
+.B ezmlm-manage
+rejects all archive retrieval attempts.
+.SH "SEE ALSO"
+ezmlm-make(1),
+ezmlm-return(1),
+ezmlm-send(1),
+ezmlm-sub(1),
+ezmlm-unsub(1),
+ezmlm(5),
+qmail-command(8)
diff --git a/ezmlm-manage.c b/ezmlm-manage.c
new file mode 100644 (file)
index 0000000..d40ba0f
--- /dev/null
@@ -0,0 +1,403 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "error.h"
+#include "stralloc.h"
+#include "str.h"
+#include "env.h"
+#include "sig.h"
+#include "slurp.h"
+#include "getconf.h"
+#include "strerr.h"
+#include "byte.h"
+#include "getln.h"
+#include "case.h"
+#include "qmail.h"
+#include "substdio.h"
+#include "readwrite.h"
+#include "seek.h"
+#include "quote.h"
+#include "datetime.h"
+#include "now.h"
+#include "date822fmt.h"
+#include "fmt.h"
+#include "subscribe.h"
+#include "cookie.h"
+
+#define FATAL "ezmlm-manage: fatal: "
+void die_usage() { strerr_die1x(100,"ezmlm-manage: usage: ezmlm-manage dir"); }
+void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
+void die_badaddr()
+{
+  strerr_die2x(100,FATAL,"I do not accept messages at this address (#5.1.1)");
+}
+
+stralloc inhost = {0};
+stralloc outhost = {0};
+stralloc inlocal = {0};
+stralloc outlocal = {0};
+stralloc key = {0};
+stralloc mailinglist = {0};
+
+datetime_sec when;
+struct datetime dt;
+
+char strnum[FMT_ULONG];
+char date[DATE822FMT];
+char hash[COOKIE];
+datetime_sec hashdate;
+stralloc target = {0};
+stralloc confirm = {0};
+stralloc line = {0};
+stralloc quoted = {0};
+
+int hashok(action)
+char *action;
+{
+  char *x;
+  unsigned long u;
+
+  x = action + 4;
+  x += scan_ulong(x,&u);
+  hashdate = u;
+  if (hashdate > when) return 0;
+  if (hashdate < when - 1000000) return 0;
+
+  u = hashdate;
+  strnum[fmt_ulong(strnum,u)] = 0;
+  cookie(hash,key.s,key.len,strnum,target.s,action + 1);
+
+  if (*x == '.') ++x;
+  if (str_len(x) != COOKIE) return 0;
+  return byte_equal(hash,COOKIE,x);
+}
+
+struct qmail qq;
+int qqwrite(fd,buf,len) int fd; char *buf; unsigned int len;
+{
+  qmail_put(&qq,buf,len);
+  return len;
+}
+char qqbuf[1];
+substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,sizeof(qqbuf));
+
+char inbuf[1024];
+substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof(inbuf));
+substdio ssin2 = SUBSTDIO_FDBUF(read,0,inbuf,sizeof(inbuf));
+
+substdio sstext;
+char textbuf[1024];
+
+void copy(fn)
+char *fn;
+{
+  int fd;
+  int match;
+
+  fd = open_read(fn);
+  if (fd == -1)
+    strerr_die4sys(111,FATAL,"unable to open ",fn,": ");
+
+  substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
+  for (;;) {
+    if (getln(&sstext,&line,&match,'\n') == -1)
+      strerr_die4sys(111,FATAL,"unable to read ",fn,": ");
+
+    if (match)
+      if (line.s[0] == '!') {
+       if (line.s[1] == 'R') {
+         qmail_puts(&qq,"   ");
+         qmail_puts(&qq,confirm.s);
+         qmail_puts(&qq,"\n");
+         continue;
+       }
+       if (line.s[1] == 'A') {
+         qmail_puts(&qq,"   ");
+         qmail_puts(&qq,target.s);
+         qmail_puts(&qq,"\n");
+         continue;
+       }
+      }
+
+    qmail_put(&qq,line.s,line.len);
+
+    if (!match)
+      break;
+  }
+
+  close(fd);
+}
+
+stralloc mydtline = {0};
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  char *dir;
+  char *sender;
+  char *host;
+  char *local;
+  char *action;
+  int fd;
+  int i;
+  int flagconfirm;
+  int flaghashok;
+  int flaggoodfield;
+  int match;
+
+  umask(022);
+  sig_pipeignore();
+  when = now();
+
+  dir = argv[1];
+  if (!dir) die_usage();
+
+  sender = env_get("SENDER");
+  if (!sender) strerr_die2x(100,FATAL,"SENDER not set");
+  local = env_get("LOCAL");
+  if (!local) strerr_die2x(100,FATAL,"LOCAL not set");
+  host = env_get("HOST");
+  if (!host) strerr_die2x(100,FATAL,"HOST not set");
+
+  if (!*sender)
+    strerr_die2x(100,FATAL,"I don't reply to bounce messages (#5.7.2)");
+  if (!sender[str_chr(sender,'@')])
+    strerr_die2x(100,FATAL,"I don't reply to senders without host names (#5.7.2)");
+  if (str_equal(sender,"#@[]"))
+    strerr_die2x(100,FATAL,"I don't reply to bounce messages (#5.7.2)");
+
+  if (chdir(dir) == -1)
+    strerr_die4sys(111,FATAL,"unable to switch to ",dir,": ");
+
+  switch(slurp("key",&key,32)) {
+    case -1:
+      strerr_die4sys(111,FATAL,"unable to read ",dir,"/key: ");
+    case 0:
+      strerr_die3x(100,FATAL,dir,"/key does not exist");
+  }
+  getconf_line(&mailinglist,"mailinglist",1,FATAL,dir);
+  getconf_line(&inhost,"inhost",1,FATAL,dir);
+  getconf_line(&inlocal,"inlocal",1,FATAL,dir);
+  getconf_line(&outhost,"outhost",1,FATAL,dir);
+  getconf_line(&outlocal,"outlocal",1,FATAL,dir);
+
+  if (inhost.len != str_len(host)) die_badaddr();
+  if (case_diffb(inhost.s,inhost.len,host)) die_badaddr();
+  if (inlocal.len > str_len(local)) die_badaddr();
+  if (case_diffb(inlocal.s,inlocal.len,local)) die_badaddr();
+
+  action = local + inlocal.len;
+
+  switch(slurp("public",&line,1)) {
+    case -1:
+      strerr_die4sys(111,FATAL,"unable to read ",dir,"/public: ");
+    case 0:
+      strerr_die2x(100,FATAL,"sorry, I've been told to reject all requests (#5.7.2)");
+  }
+
+  if (!stralloc_copys(&target,sender)) die_nomem();
+  if (action[0]) {
+    i = 1 + str_chr(action + 1,'-');
+    if (action[i]) {
+      action[i] = 0;
+      if (!stralloc_copys(&target,action + i + 1)) die_nomem();
+      i = byte_rchr(target.s,target.len,'=');
+      if (i < target.len)
+       target.s[i] = '@';
+    }
+  }
+  if (!stralloc_0(&target)) die_nomem();
+  if (!stralloc_copys(&confirm,"")) die_nomem();
+
+  if (qmail_open(&qq) == -1)
+    strerr_die2sys(111,FATAL,"unable to run qmail-queue: ");
+
+  qmail_puts(&qq,"Mailing-List: ");
+  qmail_put(&qq,mailinglist.s,mailinglist.len);
+  qmail_puts(&qq,"\nDate: ");
+  datetime_tai(&dt,when);
+  qmail_put(&qq,date,date822fmt(date,&dt));
+  qmail_puts(&qq,"Message-ID: <");
+  qmail_put(&qq,strnum,fmt_ulong(strnum,(unsigned long) when));
+  qmail_puts(&qq,".");
+  qmail_put(&qq,strnum,fmt_ulong(strnum,(unsigned long) getpid()));
+  qmail_puts(&qq,".ezmlm@");
+  qmail_put(&qq,outhost.s,outhost.len);
+  qmail_puts(&qq,">\nFrom: ");
+  if (!quote(&quoted,&outlocal)) die_nomem();
+  qmail_put(&qq,quoted.s,quoted.len);
+  qmail_puts(&qq,"-help@");
+  qmail_put(&qq,outhost.s,outhost.len);
+  qmail_puts(&qq,"\nTo: ");
+  if (!quote2(&quoted,target.s)) die_nomem();
+  qmail_put(&qq,quoted.s,quoted.len);
+  qmail_puts(&qq,"\n");
+
+  flaghashok = 1;
+  if (str_start(action,"-sc.")) flaghashok = hashok(action);
+  if (str_start(action,"-uc.")) flaghashok = hashok(action);
+
+  flagconfirm = 0;
+  if (str_equal(action,"-subscribe")) flagconfirm = 1;
+  if (str_equal(action,"-unsubscribe")) flagconfirm = 1;
+  if (!flaghashok) flagconfirm = 1;
+  
+  if (flagconfirm) {
+    strnum[fmt_ulong(strnum,(unsigned long) when)] = 0;
+    cookie(hash,key.s,key.len,strnum,target.s,action + 1);
+    if (!stralloc_copy(&confirm,&outlocal)) die_nomem();
+    if (!stralloc_cats(&confirm,"-")) die_nomem();
+    if (!stralloc_catb(&confirm,action + 1,1)) die_nomem();
+    if (!stralloc_cats(&confirm,"c.")) die_nomem();
+    if (!stralloc_cats(&confirm,strnum)) die_nomem();
+    if (!stralloc_cats(&confirm,".")) die_nomem();
+    if (!stralloc_catb(&confirm,hash,COOKIE)) die_nomem();
+    if (!stralloc_cats(&confirm,"-")) die_nomem();
+    i = str_rchr(target.s,'@');
+    if (!stralloc_catb(&confirm,target.s,i)) die_nomem();
+    if (target.s[i]) {
+      if (!stralloc_cats(&confirm,"=")) die_nomem();
+      if (!stralloc_cats(&confirm,target.s + i + 1)) die_nomem();
+    }
+    if (!stralloc_cats(&confirm,"@")) die_nomem();
+    if (!stralloc_cat(&confirm,&outhost)) die_nomem();
+    if (!stralloc_0(&confirm)) die_nomem();
+
+    qmail_puts(&qq,"Reply-To: ");
+    if (!quote2(&quoted,confirm.s)) die_nomem();
+    qmail_put(&qq,quoted.s,quoted.len);
+    qmail_puts(&qq,"\n");
+  }
+  if (!stralloc_0(&confirm)) die_nomem();
+
+  qmail_puts(&qq,"Subject: ezmlm response\n");
+
+  if (!stralloc_copys(&mydtline,"Delivered-To: responder for ")) die_nomem();
+  if (!stralloc_catb(&mydtline,outlocal.s,outlocal.len)) die_nomem();
+  if (!stralloc_cats(&mydtline,"@")) die_nomem();
+  if (!stralloc_catb(&mydtline,outhost.s,outhost.len)) die_nomem();
+  if (!stralloc_cats(&mydtline,"\n")) die_nomem();
+
+  qmail_put(&qq,mydtline.s,mydtline.len);
+
+  flaggoodfield = 0;
+  for (;;) {
+    if (getln(&ssin,&line,&match,'\n') == -1)
+      strerr_die2sys(111,FATAL,"unable to read input: ");
+    if (!match) break;
+    if (line.len == 1) break;
+    if ((line.s[0] != ' ') && (line.s[0] != '\t')) {
+      flaggoodfield = 0;
+      if (case_startb(line.s,line.len,"mailing-list:"))
+        strerr_die2x(100,FATAL,"incoming message has Mailing-List (#5.7.2)");
+      if (line.len == mydtline.len)
+       if (byte_equal(line.s,line.len,mydtline.s))
+          strerr_die2x(100,FATAL,"this message is looping: it already has my Delivered-To line (#5.4.6)");
+      if (case_startb(line.s,line.len,"delivered-to:"))
+        flaggoodfield = 1;
+      if (case_startb(line.s,line.len,"received:"))
+        flaggoodfield = 1;
+    }
+    if (flaggoodfield)
+      qmail_put(&qq,line.s,line.len);
+  }
+  if (seek_begin(0) == -1)
+    strerr_die2sys(111,FATAL,"unable to seek input: ");
+
+  qmail_puts(&qq,"\n");
+  copy("text/top");
+  if (str_equal(action,"-subscribe"))
+    copy("text/sub-confirm");
+  else if (str_equal(action,"-unsubscribe"))
+    copy("text/unsub-confirm");
+  else if (str_start(action,"-sc.")) {
+    if (!flaghashok)
+      copy("text/sub-bad");
+    else
+      switch(subscribe(target.s,1)) {
+        case -1: strerr_die1(111,FATAL,&subscribe_err);
+        case -2: strerr_die1(100,FATAL,&subscribe_err);
+       case 1: log("+",target.s); copy("text/sub-ok"); break;
+       default: copy("text/sub-nop"); break;
+      }
+  }
+  else if (str_start(action,"-uc.")) {
+    if (!flaghashok)
+      copy("text/unsub-bad");
+    else
+      switch(subscribe(target.s,0)) {
+        case -1: strerr_die1(111,FATAL,&subscribe_err);
+        case -2: strerr_die1(100,FATAL,&subscribe_err);
+       case 1: log("-",target.s); copy("text/unsub-ok"); break;
+       default: copy("text/unsub-nop"); break;
+      }
+  }
+  else if (str_start(action,"-get.")) {
+    unsigned long u;
+    struct stat st;
+    char ch;
+    int r;
+
+    scan_ulong(action + 5,&u);
+
+    if (!stralloc_copys(&line,"archive/")) die_nomem();
+    if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,u / 100))) die_nomem();
+    if (!stralloc_cats(&line,"/")) die_nomem();
+    if (!stralloc_catb(&line,strnum,fmt_uint0(strnum,(unsigned int) (u % 100),2))) die_nomem();
+    if (!stralloc_0(&line)) die_nomem();
+
+    fd = open_read(line.s);
+    if (fd == -1)
+      if (errno != error_noent)
+       strerr_die4sys(111,FATAL,"unable to open ",line.s,": ");
+      else
+        copy("text/get-bad");
+    else {
+      if (fstat(fd,&st) == -1)
+       copy("text/get-bad");
+      else if (!(st.st_mode & 0100))
+       copy("text/get-bad");
+      else {
+        substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
+       qmail_puts(&qq,"> ");
+       for (;;) {
+         r = substdio_get(&sstext,&ch,1);
+         if (r == -1) strerr_die4sys(111,FATAL,"unable to read ",line.s,": ");
+         if (r == 0) break;
+         qmail_put(&qq,&ch,1);
+         if (ch == '\n') qmail_puts(&qq,"> ");
+       }
+       qmail_puts(&qq,"\n");
+      }
+      close(fd);
+    }
+  }
+  else
+    copy("text/help");
+
+  copy("text/bottom");
+
+  qmail_puts(&qq,"Return-Path: <");
+  if (!quote2(&quoted,sender)) die_nomem();
+  qmail_put(&qq,quoted.s,quoted.len);
+  qmail_puts(&qq,">\n");
+  if (substdio_copy(&ssqq,&ssin2) != 0)
+    strerr_die2sys(111,FATAL,"unable to read input: ");
+
+  if (!stralloc_copy(&line,&outlocal)) die_nomem();
+  if (!stralloc_cats(&line,"-return-@")) die_nomem();
+  if (!stralloc_cat(&line,&outhost)) die_nomem();
+  if (!stralloc_0(&line)) die_nomem();
+  qmail_from(&qq,line.s);
+
+  qmail_to(&qq,target.s);
+
+  switch(qmail_close(&qq)) {
+    case 0:
+      strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
+      strerr_die2x(0,"ezmlm-manage: info: qp ",strnum);
+    default:
+      /* don't worry about undoing actions; everything is idempotent */
+      strerr_die2x(111,FATAL,"temporary qmail-queue error");
+  }
+}
diff --git a/ezmlm-manage=0 b/ezmlm-manage=0
new file mode 100644 (file)
index 0000000..844327b
--- /dev/null
@@ -0,0 +1 @@
+ezmlm-manage.1
diff --git a/ezmlm-manage=x b/ezmlm-manage=x
new file mode 100644 (file)
index 0000000..c51b445
--- /dev/null
@@ -0,0 +1,29 @@
+auto_qmail.o
+getconf.o
+subscribe.o
+log.o
+cookie.o
+now.o
+datetime.o
+date822fmt.o
+slurpclose.o
+slurp.o
+qmail.o
+quote.o
+surf.a
+getln.a
+env.a
+sig.a
+strerr.a
+substdio.a
+stralloc.a
+alloc.a
+error.a
+str.a
+fs.a
+case.a
+open.a
+seek.a
+wait.a
+lock.a
+fd.a
diff --git a/ezmlm-reject.1 b/ezmlm-reject.1
new file mode 100644 (file)
index 0000000..cb8cb7a
--- /dev/null
@@ -0,0 +1,32 @@
+.TH ezmlm-reject 1
+.SH NAME
+ezmlm-reject \- reject messages unsuitable for distribution
+.SH SYNOPSIS
+.B ezmlm-reject
+[
+.B \-cCsS
+]
+.SH DESCRIPTION
+.B ezmlm-reject
+reads a mail message from its standard input.
+It rejects the message if it sees something it doesn't like.
+.SH OPTIONS
+.TP
+.B \-c
+(Default.)
+Commands are not permitted in the Subject line.
+A Subject line consisting solely of HELP, SUBSCRIBE, or UNSUBSCRIBE
+is rejected.
+.TP
+.B \-C
+Commands are permitted in the subject line.
+.TP
+.B \-s
+(Default.)
+A nonempty Subject line is required.
+.TP
+.B \-S
+A Subject line is not required.
+.SH "SEE ALSO"
+ezmlm-send(1),
+qmail-command(8)
diff --git a/ezmlm-reject.c b/ezmlm-reject.c
new file mode 100644 (file)
index 0000000..ffeea99
--- /dev/null
@@ -0,0 +1,82 @@
+#include "strerr.h"
+#include "substdio.h"
+#include "readwrite.h"
+#include "stralloc.h"
+#include "getln.h"
+#include "sgetopt.h"
+
+int flagrejectcommands = 1;
+int flagneedsubject = 1;
+
+int flaghavesubject = 0;
+int flaghavecommand = 0;
+
+char buf0[256];
+substdio ss0 = SUBSTDIO_FDBUF(read,0,buf0,sizeof(buf0));
+stralloc line = {0};
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  int opt;
+  char *x;
+  int len;
+  int match;
+
+  while ((opt = getopt(argc,argv,"cCsS")) != opteof)
+    switch(opt) {
+      case 'c': flagrejectcommands = 1; break;
+      case 'C': flagrejectcommands = 0; break;
+      case 's': flagneedsubject = 1; break;
+      case 'S': flagneedsubject = 0; break;
+      default:
+       strerr_die1x(100,"ezmlm-reject: usage: ezmlm-reject [ -cCsS ]");
+    }
+
+  for (;;) {
+    if (getln(&ss0,&line,&match,'\n') == -1)
+      strerr_die2sys(111,"ezmlm-reject: fatal: ","unable to read input: ");
+    if (!match) break;
+    if (line.len == 1) break;
+
+    x = line.s; len = line.len - 1;
+    while (len && ((x[len - 1] == ' ') || (x[len - 1] == '\t'))) --len;
+
+    if (case_startb(x,len,"subject:")) {
+      x += 8; len -= 8;
+      while (len && ((*x == ' ') || (*x == '\t'))) { ++x; --len; }
+      if (len) {
+        flaghavesubject = 1;
+
+        if (len == 4)
+          if (!case_diffb("help",4,x))
+            flaghavecommand = 1;
+
+        if (len == 9)
+          if (!case_diffb("subscribe",9,x))
+            flaghavecommand = 1;
+
+        if (len == 11)
+          if (!case_diffb("unsubscribe",11,x))
+            flaghavecommand = 1;
+      }
+    }
+  }
+
+  if (flagneedsubject && !flaghavesubject)
+    strerr_die1x(100,"\
+ezmlm-reject: fatal: I need a nonempty Subject line in every message.\n\
+If you are trying to subscribe or unsubscribe, WRONG ADDRESS!\n\
+Do not send administrative requests to the mailing list.\n\
+Send an empty message to ...-help@... for automated assistance.");
+
+  if (flagrejectcommands && flaghavecommand)
+    strerr_die1x(100,"\
+ezmlm-reject: fatal: Your Subject line looks like a command word.\n\
+If you are trying to subscribe or unsubscribe, WRONG ADDRESS!\n\
+Do not send administrative requests to the mailing list.\n\
+Send an empty message to ...-help@... for automated assistance.");
+
+  _exit(0);
+}
diff --git a/ezmlm-reject=0 b/ezmlm-reject=0
new file mode 100644 (file)
index 0000000..92fa5a2
--- /dev/null
@@ -0,0 +1 @@
+ezmlm-reject.1
diff --git a/ezmlm-reject=x b/ezmlm-reject=x
new file mode 100644 (file)
index 0000000..166f429
--- /dev/null
@@ -0,0 +1,9 @@
+getln.a
+strerr.a
+substdio.a
+error.a
+stralloc.a
+alloc.a
+str.a
+getopt.a
+case.a
diff --git a/ezmlm-return.1 b/ezmlm-return.1
new file mode 100644 (file)
index 0000000..3f84989
--- /dev/null
@@ -0,0 +1,71 @@
+.TH ezmlm-return 1
+.SH NAME
+ezmlm-return \- handle mailing list bounces
+.SH SYNOPSIS
+.B ezmlm-return
+.I dir
+.SH DESCRIPTION
+.B ezmlm-return
+handles bounces for the mailing list
+stored in
+.IR dir .
+
+.B ezmlm-return
+is normally invoked from a
+.B .qmail
+file.
+It reads a mail message from its standard input,
+and a mail envelope from the
+.BR SENDER ,
+.BR LOCAL ,
+and
+.BR HOST
+environment variables.
+.SH ADDRESSES
+.B ezmlm-return
+handles mail sent to any of the following addresses:
+.TP
+.I local\fB\-return\-
+Trash.
+A help message or subscription acknowledgment bounced.
+.TP
+.I local\fB\-return\-\fImsg\fB\-\fIbox\fB=\fIdomain
+Distribution bounce.
+Message number
+.I msg
+was lost on the way to
+.IR box\fB@\fIdomain .
+.TP
+.I local\fB\-return\-\fImsg\fB\-
+Pre-VERP distribution bounce, in QSBMF format.
+Message number
+.I msg
+was lost on the way to one or more addresses;
+.B ezmlm-return
+will parse the bounce to figure out the addresses.
+.TP
+.I local\fB\-return\-warn\-\fIcookie\fB-\fIbox\fB=\fIdomain
+Warning bounce.
+A warning message from
+.B ezmlm-warn
+bounced.
+.TP
+.I local\fB\-return\-probe\-\fIcookie\fB-\fIbox\fB=\fIdomain
+Probe bounce.
+A probe from
+.B ezmlm-warn
+bounced.
+.B ezmlm-return
+will remove
+.I box\fB@\fIdomain
+from the mailing list.
+.SH "SEE ALSO"
+ezmlm-manage(1),
+ezmlm-make(1),
+ezmlm-send(1),
+ezmlm-sub(1),
+ezmlm-unsub(1),
+ezmlm-warn(1),
+ezmlm-weed(1),
+ezmlm(5),
+qmail-command(8)
diff --git a/ezmlm-return.c b/ezmlm-return.c
new file mode 100644 (file)
index 0000000..3bc2421
--- /dev/null
@@ -0,0 +1,364 @@
+#include "stralloc.h"
+#include "str.h"
+#include "env.h"
+#include "sig.h"
+#include "slurp.h"
+#include "getconf.h"
+#include "strerr.h"
+#include "byte.h"
+#include "case.h"
+#include "getln.h"
+#include "substdio.h"
+#include "error.h"
+#include "quote.h"
+#include "readwrite.h"
+#include "fmt.h"
+#include "now.h"
+#include "cookie.h"
+#include "subscribe.h"
+#include "issub.h"
+
+#define FATAL "ezmlm-return: fatal: "
+void die_usage() { strerr_die1x(100,"ezmlm-return: usage: ezmlm-return dir"); }
+void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
+void die_badaddr()
+{
+  strerr_die2x(100,FATAL,"I do not accept messages at this address (#5.1.1)");
+}
+void die_trash()
+{
+  strerr_die1x(0,"ezmlm-return: info: trash address");
+}
+
+char outbuf[1024];
+substdio ssout;
+char inbuf[1024];
+substdio ssin;
+
+char strnum[FMT_ULONG];
+char hash[COOKIE];
+char hashcopy[COOKIE];
+unsigned long cookiedate;
+stralloc fndate = {0};
+stralloc fndatenew = {0};
+stralloc fnhash = {0};
+stralloc fnhashnew = {0};
+
+stralloc quoted = {0};
+char *sender;
+
+void die_hashnew()
+{ strerr_die4sys(111,FATAL,"unable to write ",fnhashnew.s,": "); }
+void die_datenew()
+{ strerr_die4sys(111,FATAL,"unable to write ",fndatenew.s,": "); }
+void die_msgin()
+{ strerr_die2sys(111,FATAL,"unable to read input: "); }
+
+void dowit(addr,when,bounce)
+char *addr;
+unsigned long when;
+stralloc *bounce;
+{
+  int fd;
+
+  if (!issub(addr)) return;
+
+  if (!stralloc_copys(&fndate,"bounce/w")) die_nomem();
+  if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,when))) die_nomem();
+  if (!stralloc_cats(&fndate,".")) die_nomem();
+  if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
+  if (!stralloc_0(&fndate)) die_nomem();
+  if (!stralloc_copy(&fndatenew,&fndate)) die_nomem();
+  fndatenew.s[7] = 'W';
+
+  fd = open_trunc(fndatenew.s);
+  if (fd == -1) die_datenew();
+  substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
+  if (substdio_puts(&ssout,addr) == -1) die_datenew();
+  if (substdio_put(&ssout,"",1) == -1) die_datenew();
+  if (substdio_puts(&ssout,"Return-Path: <") == -1) die_datenew();
+  if (!quote2(&quoted,sender)) die_nomem();
+  if (substdio_put(&ssout,quoted.s,quoted.len) == -1) die_datenew();
+  if (substdio_puts(&ssout,">\n") == -1) die_datenew();
+  if (substdio_put(&ssout,bounce->s,bounce->len) == -1) die_datenew();
+  if (substdio_flush(&ssout) == -1) die_datenew();
+  if (fsync(fd) == -1) die_datenew();
+  if (close(fd) == -1) die_datenew(); /* NFS stupidity */
+
+  if (rename(fndatenew.s,fndate.s) == -1)
+    strerr_die6sys(111,FATAL,"unable to rename ",fndatenew.s," to ",fndate.s,": ");
+}
+
+void doit(addr,msgnum,when,bounce)
+char *addr;
+unsigned long msgnum;
+unsigned long when;
+stralloc *bounce;
+{
+  int fd;
+  int fdnew;
+
+  if (!issub(addr)) return;
+
+  if (!stralloc_copys(&fndate,"bounce/d")) die_nomem();
+  if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,when))) die_nomem();
+  if (!stralloc_cats(&fndate,".")) die_nomem();
+  if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem();
+  if (!stralloc_0(&fndate)) die_nomem();
+  if (!stralloc_copy(&fndatenew,&fndate)) die_nomem();
+  fndatenew.s[7] = 'D';
+
+  fd = open_trunc(fndatenew.s);
+  if (fd == -1) die_datenew();
+  substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
+  if (substdio_puts(&ssout,addr) == -1) die_datenew();
+  if (substdio_put(&ssout,"",1) == -1) die_datenew();
+  if (substdio_puts(&ssout,"Return-Path: <") == -1) die_datenew();
+  if (!quote2(&quoted,sender)) die_nomem();
+  if (substdio_put(&ssout,quoted.s,quoted.len) == -1) die_datenew();
+  if (substdio_puts(&ssout,">\n") == -1) die_datenew();
+  if (substdio_put(&ssout,bounce->s,bounce->len) == -1) die_datenew();
+  if (substdio_flush(&ssout) == -1) die_datenew();
+  if (fsync(fd) == -1) die_datenew();
+  if (close(fd) == -1) die_datenew(); /* NFS stupidity */
+
+  cookie(hash,"",0,"",addr,"");
+  if (!stralloc_copys(&fnhash,"bounce/h")) die_nomem();
+  if (!stralloc_catb(&fnhash,hash,COOKIE)) die_nomem();
+  if (!stralloc_0(&fnhash)) die_nomem();
+  if (!stralloc_copy(&fnhashnew,&fnhash)) die_nomem();
+  fnhashnew.s[7] = 'H';
+
+  fdnew = open_trunc(fnhashnew.s);
+  if (fdnew == -1) die_hashnew();
+  substdio_fdbuf(&ssout,write,fdnew,outbuf,sizeof(outbuf));
+
+  fd = open_read(fnhash.s);
+  if (fd == -1) {
+    if (errno != error_noent)
+      strerr_die4sys(111,FATAL,"unable to read ",fnhash.s,": ");
+    if (rename(fndatenew.s,fndate.s) == -1)
+      strerr_die6sys(111,FATAL,"unable to rename ",fndatenew.s," to ",fndate.s,": ");
+  }
+  else {
+    substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+    switch(substdio_copy(&ssout,&ssin)) {
+      case -2: die_msgin();
+      case -3: die_hashnew();
+    }
+    close(fd);
+    if (unlink(fndatenew.s) == -1)
+      strerr_die4sys(111,FATAL,"unable to unlink ",fndatenew.s,": ");
+  }
+  if (substdio_puts(&ssout,"   ") == -1) die_hashnew();
+  if (substdio_put(&ssout,strnum,fmt_ulong(strnum,msgnum)) == -1) die_hashnew();
+  if (substdio_puts(&ssout,"\n") == -1) die_hashnew();
+  if (substdio_flush(&ssout) == -1) die_hashnew();
+  if (fsync(fdnew) == -1) die_hashnew();
+  if (close(fdnew) == -1) die_hashnew(); /* NFS stupidity */
+
+  if (rename(fnhashnew.s,fnhash.s) == -1)
+    strerr_die6sys(111,FATAL,"unable to rename ",fnhashnew.s," to ",fnhash.s,": ");
+}
+
+stralloc bounce = {0};
+stralloc line = {0};
+stralloc header = {0};
+stralloc intro = {0};
+stralloc failure = {0};
+stralloc paragraph = {0};
+int flaghaveheader;
+int flaghaveintro;
+
+stralloc key = {0};
+stralloc inhost = {0};
+stralloc outhost = {0};
+stralloc inlocal = {0};
+stralloc outlocal = {0};
+
+char msginbuf[1024];
+substdio ssmsgin;
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  char *dir;
+  char *host;
+  char *local;
+  char *action;
+  unsigned long msgnum;
+  unsigned long cookiedate;
+  unsigned long when;
+  int match;
+  int i;
+  int fdlock;
+
+  umask(022);
+  sig_pipeignore();
+  when = (unsigned long) now();
+
+  dir = argv[1];
+  if (!dir) die_usage();
+
+  sender = env_get("SENDER");
+  if (!sender) strerr_die2x(100,FATAL,"SENDER not set");
+  local = env_get("LOCAL");
+  if (!local) strerr_die2x(100,FATAL,"LOCAL not set");
+  host = env_get("HOST");
+  if (!host) strerr_die2x(100,FATAL,"HOST not set");
+
+  if (chdir(dir) == -1)
+    strerr_die4sys(111,FATAL,"unable to switch to ",dir,": ");
+
+  switch(slurp("key",&key,32)) {
+    case -1:
+      strerr_die4sys(111,FATAL,"unable to read ",dir,"/key: ");
+    case 0:
+      strerr_die3x(100,FATAL,dir,"/key does not exist");
+  }
+  getconf_line(&inhost,"inhost",1,FATAL,dir);
+  getconf_line(&inlocal,"inlocal",1,FATAL,dir);
+  getconf_line(&outhost,"outhost",1,FATAL,dir);
+  getconf_line(&outlocal,"outlocal",1,FATAL,dir);
+
+  if (inhost.len != str_len(host)) die_badaddr();
+  if (case_diffb(inhost.s,inhost.len,host)) die_badaddr();
+  if (inlocal.len > str_len(local)) die_badaddr();
+  if (case_diffb(inlocal.s,inlocal.len,local)) die_badaddr();
+
+  action = local + inlocal.len;
+
+  if (!str_start(action,"-return-")) die_badaddr();
+  action += 8;
+
+  if (!*action) die_trash();
+
+  if (str_start(action,"probe-")) {
+    action += 6;
+    action += scan_ulong(action,&cookiedate);
+    if (now() - cookiedate > 3000000) die_trash();
+    if (*action++ != '.') die_trash();
+    i = str_chr(action,'-');
+    if (i != COOKIE) die_trash();
+    byte_copy(hashcopy,COOKIE,action);
+    action += COOKIE;
+    if (*action++ != '-') die_trash();
+    i = str_rchr(action,'=');
+    if (!stralloc_copyb(&line,action,i)) die_nomem();
+    if (action[i]) {
+      if (!stralloc_cats(&line,"@")) die_nomem();
+      if (!stralloc_cats(&line,action + i + 1)) die_nomem();
+    }
+    if (!stralloc_0(&line)) die_nomem();
+    strnum[fmt_ulong(strnum,cookiedate)] = 0;
+    cookie(hash,key.s,key.len,strnum,line.s,"P");
+    if (byte_diff(hash,COOKIE,hashcopy)) die_trash();
+
+    if (subscribe(line.s,0) == 1) log("-probe",line.s);
+    _exit(0);
+  }
+
+  fdlock = open_append("lockbounce");
+  if (fdlock == -1)
+    strerr_die4sys(111,FATAL,"unable to open ",dir,"/lockbounce: ");
+  if (lock_ex(fdlock) == -1)
+    strerr_die4sys(111,FATAL,"unable to lock ",dir,"/lockbounce: ");
+
+  if (str_start(action,"warn-")) {
+    action += 5;
+    action += scan_ulong(action,&cookiedate);
+    if (now() - cookiedate > 3000000) die_trash();
+    if (*action++ != '.') die_trash();
+    i = str_chr(action,'-');
+    if (i != COOKIE) die_trash();
+    byte_copy(hashcopy,COOKIE,action);
+    action += COOKIE;
+    if (*action++ != '-') die_trash();
+    i = str_rchr(action,'=');
+    if (!stralloc_copyb(&line,action,i)) die_nomem();
+    if (action[i]) {
+      if (!stralloc_cats(&line,"@")) die_nomem();
+      if (!stralloc_cats(&line,action + i + 1)) die_nomem();
+    }
+    if (!stralloc_0(&line)) die_nomem();
+    strnum[fmt_ulong(strnum,cookiedate)] = 0;
+    cookie(hash,key.s,key.len,strnum,line.s,"W");
+    if (byte_diff(hash,COOKIE,hashcopy)) die_trash();
+
+    if (slurpclose(0,&bounce,1024) == -1) die_msgin();
+    dowit(line.s,when,&bounce);
+    _exit(0);
+  }
+
+  action += scan_ulong(action,&msgnum);
+  if (*action != '-') die_badaddr();
+  ++action;
+
+  if (*action) {
+    if (slurpclose(0,&bounce,1024) == -1) die_msgin();
+
+    i = str_rchr(action,'=');
+    if (!stralloc_copyb(&line,action,i)) die_nomem();
+    if (action[i]) {
+      if (!stralloc_cats(&line,"@")) die_nomem();
+      if (!stralloc_cats(&line,action + i + 1)) die_nomem();
+    }
+    if (!stralloc_0(&line)) die_nomem();
+    doit(line.s,msgnum,when,&bounce);
+    _exit(0);
+  }
+
+  /* pre-VERP bounce, in QSBMF format */
+
+  substdio_fdbuf(&ssmsgin,read,0,msginbuf,sizeof(msginbuf));
+
+  flaghaveheader = 0;
+  flaghaveintro = 0;
+
+  for (;;) {
+    if (!stralloc_copys(&paragraph,"")) die_nomem();
+    for (;;) {
+      if (getln(&ssmsgin,&line,&match,'\n') == -1) die_msgin();
+      if (!match) die_trash();
+      if (!stralloc_cat(&paragraph,&line)) die_nomem();
+      if (line.len <= 1) break;
+    }
+
+    if (!flaghaveheader) {
+      if (!stralloc_copy(&header,&paragraph)) die_nomem();
+      flaghaveheader = 1;
+      continue;
+    }
+
+    if (!flaghaveintro) {
+      if (paragraph.len < 15) die_trash();
+      if (str_diffn(paragraph.s,"Hi. This is the",15)) die_trash();
+      if (!stralloc_copy(&intro,&paragraph)) die_nomem();
+      flaghaveintro = 1;
+      continue;
+    }
+
+    if (paragraph.s[0] == '-')
+      break;
+
+    if (paragraph.s[0] == '<') {
+      if (!stralloc_copy(&failure,&paragraph)) die_nomem();
+
+      if (!stralloc_copy(&bounce,&header)) die_nomem();
+      if (!stralloc_cat(&bounce,&intro)) die_nomem();
+      if (!stralloc_cat(&bounce,&failure)) die_nomem();
+
+      i = byte_chr(failure.s,failure.len,'\n');
+      if (i < 3) die_trash();
+
+      if (!stralloc_copyb(&line,failure.s + 1,i - 3)) die_nomem();
+      if (byte_chr(line.s,line.len,'\0') == line.len) {
+        if (!stralloc_0(&line)) die_nomem();
+        doit(line.s,msgnum,when,&bounce);
+      }
+    }
+  }
+
+  _exit(0);
+}
diff --git a/ezmlm-return=0 b/ezmlm-return=0
new file mode 100644 (file)
index 0000000..0eaa248
--- /dev/null
@@ -0,0 +1 @@
+ezmlm-return.1
diff --git a/ezmlm-return=x b/ezmlm-return=x
new file mode 100644 (file)
index 0000000..7f99f7f
--- /dev/null
@@ -0,0 +1,23 @@
+quote.o
+getconf.o
+issub.o
+subscribe.o
+log.o
+slurpclose.o
+slurp.o
+now.o
+cookie.o
+surf.a
+lock.a
+env.a
+sig.a
+strerr.a
+getln.a
+substdio.a
+stralloc.a
+alloc.a
+error.a
+str.a
+fs.a
+case.a
+open.a
diff --git a/ezmlm-send.1 b/ezmlm-send.1
new file mode 100644 (file)
index 0000000..ff70976
--- /dev/null
@@ -0,0 +1,86 @@
+.TH ezmlm-send 1
+.SH NAME
+ezmlm-send \- distribute a message to a mailing list
+.SH SYNOPSIS
+.B ezmlm-send
+.I dir
+.SH DESCRIPTION
+.B ezmlm-send
+reads a mail message and
+sends it to the mailing list stored in
+.IR dir .
+If
+.I dir\fB/archived
+exists,
+.B ezmlm-send
+records a copy of the message in the
+.I dir\fB/archive
+directory.
+
+At the beginning of the message,
+.B ezmlm-send
+prints a new
+.B Mailing-List
+field with the contents of
+.IR dir\fB/mailinglist .
+It rejects the message if there is already a
+.B Mailing-List
+field.
+
+.B ezmlm-send
+then prints all the new fields listed in
+.IR dir\fB/headeradd ,
+followed by an appropriate
+.B Delivered-To
+line.
+
+.B ezmlm-send
+deletes any incoming fields with names listed in
+.IR dir\fB/headerremove .
+
+.B ezmlm-send
+does not distribute bounce messages:
+if the environment variable
+.B SENDER
+is set, and is either empty or
+.BR #@[] ,
+.B ezmlm-send
+rejects the message.
+.SH "SUBLISTS"
+If
+.I dir\fB/sublist
+exists,
+.B ezmlm-send
+changes its behavior in several ways.
+
+First, if
+.B SENDER
+is set,
+and the first line of
+.I dir\fB/sublist
+has the form
+.IR parent\fB@\fIparenthost ,
+.B ezmlm-send
+insists that
+.B SENDER
+have the form
+.IR parent\fB...@\fIparenthost .
+
+Second,
+.B ezmlm-send
+demands that the message already have a
+.B Mailing-List
+field.
+
+Third,
+.B ezmlm-send
+does not add its own
+.B Mailing-List
+field.
+.SH "SEE ALSO"
+ezmlm-manage(1),
+ezmlm-make(1),
+ezmlm-sub(1),
+ezmlm-unsub(1),
+ezmlm-reject(1),
+ezmlm(5)
diff --git a/ezmlm-send.c b/ezmlm-send.c
new file mode 100644 (file)
index 0000000..5748891
--- /dev/null
@@ -0,0 +1,319 @@
+#include "stralloc.h"
+#include "subfd.h"
+#include "strerr.h"
+#include "error.h"
+#include "qmail.h"
+#include "env.h"
+#include "lock.h"
+#include "sig.h"
+#include "open.h"
+#include "getln.h"
+#include "case.h"
+#include "scan.h"
+#include "str.h"
+#include "fmt.h"
+#include "readwrite.h"
+#include "exit.h"
+#include "substdio.h"
+#include "getconf.h"
+#include "constmap.h"
+
+#define FATAL "ezmlm-send: fatal: "
+
+void die_usage()
+{
+  strerr_die1x(100,"ezmlm-send: usage: ezmlm-send dir");
+}
+void die_nomem()
+{
+  strerr_die2x(111,FATAL,"out of memory");
+}
+
+char strnum[FMT_ULONG];
+
+stralloc fnadir = {0};
+stralloc fnaf = {0};
+stralloc fnsub = {0};
+stralloc line = {0};
+
+int flagarchived;
+int fdarchive;
+substdio ssarchive;
+char archivebuf[1024];
+
+int flagsublist;
+stralloc sublist = {0};
+stralloc mailinglist = {0};
+stralloc outlocal = {0};
+stralloc outhost = {0};
+stralloc headerremove = {0};
+struct constmap headerremovemap;
+stralloc headeradd = {0};
+
+struct qmail qq;
+substdio ssin;
+char inbuf[1024];
+substdio ssout;
+char outbuf[1];
+
+int mywrite(fd,buf,len)
+int fd;
+char *buf;
+unsigned int len;
+{
+  qmail_put(&qq,buf,len);
+  return len;
+}
+
+void die_archive()
+{
+  strerr_die4sys(111,FATAL,"unable to write to ",fnaf.s,": ");
+}
+void die_numnew()
+{
+  strerr_die2sys(111,FATAL,"unable to create numnew: ");
+}
+
+void put(buf,len) char *buf; int len;
+{
+  qmail_put(&qq,buf,len);
+  if (flagarchived)
+    if (substdio_put(&ssarchive,buf,len) == -1) die_archive();
+}
+
+void puts(buf) char *buf;
+{
+  qmail_puts(&qq,buf);
+  if (flagarchived)
+    if (substdio_puts(&ssarchive,buf) == -1) die_archive();
+}
+
+int sublistmatch(sender)
+char *sender;
+{
+  int i;
+  int j;
+
+  j = str_len(sender);
+  if (j < sublist.len) return 0;
+
+  i = byte_rchr(sublist.s,sublist.len,'@');
+  if (i == sublist.len) return 1;
+
+  if (byte_diff(sublist.s,i,sender)) return 0;
+  if (case_diffb(sublist.s + i,sublist.len - i,sender + j - (sublist.len - i)))
+    return 0;
+
+  return 1;
+}
+
+substdio ssnumnew;
+char numnewbuf[16];
+unsigned long msgnum;
+stralloc num = {0};
+
+char buf0[256];
+substdio ss0 = SUBSTDIO_FDBUF(read,0,buf0,sizeof(buf0));
+
+void numwrite()
+{
+  int fd;
+
+  fd = open_trunc("numnew");
+  if (fd == -1) die_numnew();
+  substdio_fdbuf(&ssnumnew,write,fd,numnewbuf,sizeof(numnewbuf));
+  if (substdio_put(&ssnumnew,strnum,fmt_ulong(strnum,msgnum)) == -1)
+    die_numnew();
+  if (substdio_puts(&ssnumnew,"\n") == -1) die_numnew();
+  if (substdio_flush(&ssnumnew) == -1) die_numnew();
+  if (fsync(fd) == -1) die_numnew();
+  if (close(fd) == -1) die_numnew(); /* NFS stupidity */
+  if (rename("numnew","num") == -1)
+    strerr_die2sys(111,FATAL,"unable to move numnew to num: ");
+}
+
+stralloc mydtline = {0};
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  int fd;
+  char *dir;
+  int fdlock;
+  char *sender;
+  int flagmlwasthere;
+  int match;
+  int i;
+  char ch;
+  int flaginheader;
+  int flagbadfield;
+
+  umask(022);
+  sig_pipeignore();
+
+  dir = argv[1];
+  if (!dir) die_usage();
+
+  sender = env_get("SENDER");
+
+  if (chdir(dir) == -1)
+    strerr_die4sys(111,FATAL,"unable to switch to ",dir,": ");
+
+  fdlock = open_append("lock");
+  if (fdlock == -1)
+    strerr_die4sys(111,FATAL,"unable to open ",dir,"/lock: ");
+  if (lock_ex(fdlock) == -1)
+    strerr_die4sys(111,FATAL,"unable to obtain ",dir,"/lock: ");
+
+  if (qmail_open(&qq) == -1)
+    strerr_die2sys(111,FATAL,"unable to run qmail-queue: ");
+
+  flagarchived = getconf_line(&line,"archived",0,FATAL,dir);
+
+  getconf_line(&num,"num",1,FATAL,dir);
+  if (!stralloc_0(&num)) die_nomem();
+  scan_ulong(num.s,&msgnum);
+  ++msgnum;
+
+  getconf_line(&outhost,"outhost",1,FATAL,dir);
+  getconf_line(&outlocal,"outlocal",1,FATAL,dir);
+  getconf_line(&mailinglist,"mailinglist",1,FATAL,dir);
+  flagsublist = getconf_line(&sublist,"sublist",0,FATAL,dir);
+
+  getconf(&headerremove,"headerremove",1,FATAL,dir);
+  constmap_init(&headerremovemap,headerremove.s,headerremove.len,0);
+
+  getconf(&headeradd,"headeradd",1,FATAL,dir);
+  for (i = 0;i < headeradd.len;++i)
+    if (!headeradd.s[i])
+      headeradd.s[i] = '\n';
+
+  if (!stralloc_copys(&mydtline,"Delivered-To: mailing list ")) die_nomem();
+  if (!stralloc_catb(&mydtline,outlocal.s,outlocal.len)) die_nomem();
+  if (!stralloc_cats(&mydtline,"@")) die_nomem();
+  if (!stralloc_catb(&mydtline,outhost.s,outhost.len)) die_nomem();
+  if (!stralloc_cats(&mydtline,"\n")) die_nomem();
+
+  if (sender) {
+    if (!*sender)
+      strerr_die2x(100,FATAL,"I don't distribute bounce messages (#5.7.2)");
+    if (str_equal(sender,"#@[]"))
+      strerr_die2x(100,FATAL,"I don't distribute bounce messages (#5.7.2)");
+    if (flagsublist)
+      if (!sublistmatch(sender))
+        strerr_die2x(100,FATAL,"this message is not from my parent list (#5.7.2)");
+  }
+
+  if (flagarchived) {
+    if (!stralloc_copys(&fnadir,"archive/")) die_nomem();
+    if (!stralloc_catb(&fnadir,strnum,fmt_ulong(strnum,msgnum / 100))) die_nomem();
+    if (!stralloc_copy(&fnaf,&fnadir)) die_nomem();
+    if (!stralloc_cats(&fnaf,"/")) die_nomem();
+    if (!stralloc_catb(&fnaf,strnum,fmt_uint0(strnum,(unsigned int) (msgnum % 100),2))) die_nomem();
+    if (!stralloc_0(&fnadir)) die_nomem();
+    if (!stralloc_0(&fnaf)) die_nomem();
+
+    if (mkdir(fnadir.s,0755) == -1)
+      if (errno != error_exist)
+       strerr_die4sys(111,FATAL,"unable to create ",fnadir.s,": ");
+    fdarchive = open_trunc(fnaf.s);
+    if (fdarchive == -1)
+      strerr_die4sys(111,FATAL,"unable to write ",fnaf.s,": ");
+
+    substdio_fdbuf(&ssarchive,write,fdarchive,archivebuf,sizeof(archivebuf));
+  }
+
+  if (!flagsublist) {
+    puts("Mailing-List: ");
+    put(mailinglist.s,mailinglist.len);
+    puts("\n");
+  }
+  put(headeradd.s,headeradd.len);
+  put(mydtline.s,mydtline.len);
+
+  flagmlwasthere = 0;
+  flaginheader = 1;
+  flagbadfield = 0;
+
+  for (;;) {
+    if (getln(&ss0,&line,&match,'\n') == -1)
+      strerr_die2sys(111,FATAL,"unable to read input: ");
+
+    if (flaginheader && match) {
+      if (line.len == 1)
+       flaginheader = 0;
+      if ((line.s[0] != ' ') && (line.s[0] != '\t')) {
+       flagbadfield = 0;
+       if (constmap(&headerremovemap,line.s,byte_chr(line.s,line.len,':')))
+         flagbadfield = 1;
+       if (case_startb(line.s,line.len,"mailing-list:"))
+         flagmlwasthere = 1;
+       if (line.len == mydtline.len)
+         if (!byte_diff(line.s,line.len,mydtline.s))
+            strerr_die2x(100,FATAL,"this message is looping: it already has my Delivered-To line (#5.4.6)");
+      }
+    }
+
+    if (!(flaginheader && flagbadfield))
+      put(line.s,line.len);
+
+    if (!match)
+      break;
+  }
+
+  if (flagsublist)
+    if (!flagmlwasthere)
+      strerr_die2x(100,FATAL,"sublist messages must have Mailing-List (#5.7.2)");
+  if (!flagsublist)
+    if (flagmlwasthere)
+      strerr_die2x(100,FATAL,"message already has Mailing-List (#5.7.2)");
+
+  if (flagarchived) {
+    if (substdio_flush(&ssarchive) == -1) die_archive();
+    if (fsync(fdarchive) == -1) die_archive();
+    if (fchmod(fdarchive,0744) == -1) die_archive();
+    if (close(fdarchive) == -1) die_archive(); /* NFS stupidity */
+  }
+
+  numwrite();
+
+  if (!stralloc_copy(&line,&outlocal)) die_nomem();
+  if (!stralloc_cats(&line,"-return-")) die_nomem();
+  if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,msgnum))) die_nomem();
+  if (!stralloc_cats(&line,"-@")) die_nomem();
+  if (!stralloc_cat(&line,&outhost)) die_nomem();
+  if (!stralloc_cats(&line,"-@[]")) die_nomem();
+  if (!stralloc_0(&line)) die_nomem();
+
+  qmail_from(&qq,line.s);
+
+  for (i = 0;i < 53;++i) {
+    ch = 64 + i;
+    if (!stralloc_copys(&fnsub,"subscribers/")) die_nomem();
+    if (!stralloc_catb(&fnsub,&ch,1)) strerr_die2x(111,FATAL,"out of memory");
+    if (!stralloc_0(&fnsub)) strerr_die2x(111,FATAL,"out of memory");
+    fd = open_read(fnsub.s);
+    if (fd == -1) {
+      if (errno != error_noent)
+       strerr_die4sys(111,FATAL,"unable to read ",fnsub.s,": ");
+    }
+    else {
+      substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+      substdio_fdbuf(&ssout,mywrite,-1,outbuf,sizeof(outbuf));
+      if (substdio_copy(&ssout,&ssin) != 0)
+       strerr_die4sys(111,FATAL,"unable to read ",fnsub.s,": ");
+      close(fd);
+    }
+  }
+
+  switch(qmail_close(&qq)) {
+    case 0:
+      strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
+      strerr_die2x(0,"ezmlm-send: info: qp ",strnum);
+    default:
+      --msgnum;
+      numwrite();
+      strerr_die2x(111,FATAL,"temporary qmail-queue error");
+  }
+}
diff --git a/ezmlm-send=0 b/ezmlm-send=0
new file mode 100644 (file)
index 0000000..40a6d36
--- /dev/null
@@ -0,0 +1 @@
+ezmlm-send.1
diff --git a/ezmlm-send=x b/ezmlm-send=x
new file mode 100644 (file)
index 0000000..2ba0ed9
--- /dev/null
@@ -0,0 +1,21 @@
+auto_qmail.o
+getconf.o
+qmail.o
+constmap.o
+slurp.o
+slurpclose.o
+wait.a
+getln.a
+strerr.a
+sig.a
+env.a
+open.a
+lock.a
+substdio.a
+stralloc.a
+alloc.a
+error.a
+str.a
+fd.a
+case.a
+fs.a
diff --git a/ezmlm-sub.1 b/ezmlm-sub.1
new file mode 100644 (file)
index 0000000..219b88f
--- /dev/null
@@ -0,0 +1,38 @@
+.TH ezmlm-sub 1
+.SH NAME
+ezmlm-sub \- manually add addresses to a mailing list
+.SH SYNOPSIS
+.B ezmlm-sub
+.I dir
+[
+.I box\fB@\fIdomain ...
+]
+.SH DESCRIPTION
+.B ezmlm-sub
+adds each address
+.I box\fB@\fIdomain
+to the mailing list stored in
+.IR dir .
+
+If
+.I box\fB@\fIdomain
+is already on the mailing list,
+.B ezmlm-sub
+leaves it there.
+
+.B ezmlm-sub
+converts
+.I domain
+to lowercase before adding
+.I box\fB@\fIdomain
+to the mailing list.
+
+.I box\fB@\fIdomain
+cannot be longer than 400 characters.
+.SH "SEE ALSO"
+ezmlm-list(1),
+ezmlm-manage(1),
+ezmlm-make(1),
+ezmlm-send(1),
+ezmlm-unsub(1),
+ezmlm(5)
diff --git a/ezmlm-sub.c b/ezmlm-sub.c
new file mode 100644 (file)
index 0000000..85fce6a
--- /dev/null
@@ -0,0 +1,33 @@
+#include "strerr.h"
+#include "subscribe.h"
+#include "log.h"
+
+#define FATAL "ezmlm-sub: fatal: "
+#define WARNING "ezmlm-sub: warning: "
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  char *dir;
+  char *addr;
+
+  dir = argv[1];
+  if (!dir)
+    strerr_die1x(100,"ezmlm-sub: usage: ezmlm-sub dir box@domain ...");
+  if (chdir(dir) == -1)
+    strerr_die4sys(111,FATAL,"unable to switch to ",dir,": ");
+
+  argv += 2;
+  while (addr = *argv++)
+    switch(subscribe(addr,1)) {
+      case -1:
+       strerr_die1(111,FATAL,&subscribe_err);
+      case -2:
+       strerr_warn4(WARNING,"cannot subscribe ",addr,": ",&subscribe_err);
+       break;
+      case 1:
+       log("+manual",addr);
+    }
+  _exit(0);
+}
diff --git a/ezmlm-sub=0 b/ezmlm-sub=0
new file mode 100644 (file)
index 0000000..2d4630f
--- /dev/null
@@ -0,0 +1 @@
+ezmlm-sub.1
diff --git a/ezmlm-sub=x b/ezmlm-sub=x
new file mode 100644 (file)
index 0000000..9218d9a
--- /dev/null
@@ -0,0 +1,14 @@
+subscribe.o
+log.o
+now.o
+fs.a
+strerr.a
+getln.a
+substdio.a
+stralloc.a
+alloc.a
+error.a
+str.a
+case.a
+open.a
+lock.a
diff --git a/ezmlm-unsub.1 b/ezmlm-unsub.1
new file mode 100644 (file)
index 0000000..7223ae9
--- /dev/null
@@ -0,0 +1,35 @@
+.TH ezmlm-unsub 1
+.SH NAME
+ezmlm-unsub \- manually remove addresses from a mailing list
+.SH SYNOPSIS
+.B ezmlm-unsub
+.I dir
+[
+.I box\fB@\fIdomain ...
+]
+.SH DESCRIPTION
+.B ezmlm-unsub
+removes each address
+.I box\fB@\fIdomain
+from the mailing list stored in
+.IR dir .
+
+If
+.I box\fB@\fIdomain
+is not on the mailing list,
+.B ezmlm-unsub
+keeps it off.
+
+.B ezmlm-unsub
+converts
+.I domain
+to lowercase before removing
+.I box\fB@\fIdomain
+from the mailing list.
+.SH "SEE ALSO"
+ezmlm-list(1),
+ezmlm-manage(1),
+ezmlm-make(1),
+ezmlm-send(1),
+ezmlm-sub(1),
+ezmlm(5)
diff --git a/ezmlm-unsub.c b/ezmlm-unsub.c
new file mode 100644 (file)
index 0000000..3f53890
--- /dev/null
@@ -0,0 +1,34 @@
+#include "strerr.h"
+#include "subscribe.h"
+#include "log.h"
+
+#define FATAL "ezmlm-unsub: fatal: "
+#define WARNING "ezmlm-unsub: warning: "
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  char *dir;
+  char *addr;
+
+  dir = argv[1];
+  if (!dir)
+    strerr_die1x(100,"ezmlm-unsub: usage: ezmlm-unsub dir box@domain ...");
+  if (chdir(dir) == -1)
+    strerr_die4sys(111,FATAL,"unable to switch to ",dir,": ");
+
+  argv += 2;
+  while (addr = *argv++)
+    switch(subscribe(addr,0)) {
+      case -1:
+       strerr_die1(111,FATAL,&subscribe_err);
+      case -2:
+       strerr_warn4(WARNING,"cannot unsubscribe ",addr,": ",&subscribe_err);
+       break;
+      case 1:
+       log("-manual",addr);
+    }
+
+  _exit(0);
+}
diff --git a/ezmlm-unsub=0 b/ezmlm-unsub=0
new file mode 100644 (file)
index 0000000..b651717
--- /dev/null
@@ -0,0 +1 @@
+ezmlm-unsub.1
diff --git a/ezmlm-unsub=x b/ezmlm-unsub=x
new file mode 100644 (file)
index 0000000..9218d9a
--- /dev/null
@@ -0,0 +1,14 @@
+subscribe.o
+log.o
+now.o
+fs.a
+strerr.a
+getln.a
+substdio.a
+stralloc.a
+alloc.a
+error.a
+str.a
+case.a
+open.a
+lock.a
diff --git a/ezmlm-warn.1 b/ezmlm-warn.1
new file mode 100644 (file)
index 0000000..06fd0b0
--- /dev/null
@@ -0,0 +1,39 @@
+.TH ezmlm-warn 1
+.SH NAME
+ezmlm-warn \- send out bounce warnings
+.SH SYNOPSIS
+.B ezmlm-warn
+.I dir
+.SH DESCRIPTION
+.B ezmlm-warn
+sends out warning messages
+for the mailing list stored in
+.IR dir .
+
+.B ezmlm-warn
+scans
+.I dir\fB/bounce
+for bounce messages received by
+.BR ezmlm-return .
+If it sees a distribution bounce for
+.I box\fB@\fIdomain
+received more than ten days ago,
+it sends
+.I box\fB@\fIdomain
+a list of all the message numbers missed recently,
+and deletes the bounce.
+If it sees a warning bounce for
+.I box\fB@\fIdomain
+received more than ten days ago,
+it sends
+.I box\fB@\fIdomain
+a probe,
+and deletes the bounce.
+
+.B ezmlm-warn
+will not send a warning or probe to an address that is
+not currently a subscriber.
+.SH "SEE ALSO"
+ezmlm-make(1),
+ezmlm-return(1),
+ezmlm(5)
diff --git a/ezmlm-warn.c b/ezmlm-warn.c
new file mode 100644 (file)
index 0000000..3140976
--- /dev/null
@@ -0,0 +1,254 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "direntry.h"
+#include "readwrite.h"
+#include "getln.h"
+#include "substdio.h"
+#include "stralloc.h"
+#include "slurp.h"
+#include "getconf.h"
+#include "byte.h"
+#include "error.h"
+#include "str.h"
+#include "strerr.h"
+#include "sig.h"
+#include "now.h"
+#include "datetime.h"
+#include "date822fmt.h"
+#include "fmt.h"
+#include "cookie.h"
+#include "qmail.h"
+
+#define FATAL "ezmlm-warn: fatal: "
+void die_usage() { strerr_die1x(100,"ezmlm-warn: usage: ezmlm-warn dir"); }
+void die_nomem() { strerr_die2x(111,FATAL,"out of memory"); }
+
+stralloc key = {0};
+stralloc outhost = {0};
+stralloc outlocal = {0};
+stralloc mailinglist = {0};
+
+unsigned long when;
+char *dir;
+stralloc fn = {0};
+struct stat st;
+
+void die_read() { strerr_die6sys(111,FATAL,"unable to read ",dir,"/",fn.s,": "); }
+
+char inbuf[1024];
+substdio ssin;
+char textbuf[1024];
+substdio sstext;
+
+stralloc addr = {0};
+char strnum[FMT_ULONG];
+char hash[COOKIE];
+stralloc fnhash = {0};
+stralloc quoted = {0};
+stralloc line = {0};
+
+struct qmail qq;
+int qqwrite(fd,buf,len) int fd; char *buf; unsigned int len;
+{
+  qmail_put(&qq,buf,len);
+  return len;
+}
+char qqbuf[1];
+substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,sizeof(qqbuf));
+struct datetime dt;
+char date[DATE822FMT];
+
+void copy(fn)
+char *fn;
+{
+  int fd;
+  int match;
+
+  fd = open_read(fn);
+  if (fd == -1)
+    strerr_die4sys(111,FATAL,"unable to open ",fn,": ");
+
+  substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
+  for (;;) {
+    if (getln(&sstext,&line,&match,'\n') == -1)
+      strerr_die4sys(111,FATAL,"unable to read ",fn,": ");
+    if (!match)
+      break;
+    qmail_put(&qq,line.s,line.len);
+  }
+
+  close(fd);
+}
+
+void doit(flagw)
+int flagw;
+{
+  int i;
+  int fd;
+  int match;
+  int fdhash;
+  datetime_sec msgwhen;
+
+  fd = open_read(fn.s);
+  if (fd == -1) die_read();
+  substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+
+  if (getln(&ssin,&addr,&match,'\0') == -1) die_read();
+  if (!match) { close(fd); return; }
+
+  if (!issub(addr.s)) { close(fd); /*XXX*/unlink(fn.s); return; }
+
+  cookie(hash,"",0,"",addr.s,"");
+  if (!stralloc_copys(&fnhash,"bounce/h")) die_nomem();
+  if (!stralloc_catb(&fnhash,hash,COOKIE)) die_nomem();
+  if (!stralloc_0(&fnhash)) die_nomem();
+
+  if (qmail_open(&qq) == -1)
+    strerr_die2sys(111,FATAL,"unable to run qmail-queue: ");
+
+  msgwhen = now();
+  qmail_puts(&qq,"Mailing-List: ");
+  qmail_put(&qq,mailinglist.s,mailinglist.len);
+  qmail_puts(&qq,"\nDate: ");
+  datetime_tai(&dt,msgwhen);
+  qmail_put(&qq,date,date822fmt(date,&dt));
+  qmail_puts(&qq,"Message-ID: <");
+  qmail_put(&qq,strnum,fmt_ulong(strnum,(unsigned long) msgwhen));
+  qmail_puts(&qq,".");
+  qmail_put(&qq,strnum,fmt_ulong(strnum,(unsigned long) getpid()));
+  qmail_puts(&qq,".ezmlm-warn@");
+  qmail_put(&qq,outhost.s,outhost.len);
+  qmail_puts(&qq,">\nFrom: ");
+  if (!quote(&quoted,&outlocal)) die_nomem();
+  qmail_put(&qq,quoted.s,quoted.len);
+  qmail_puts(&qq,"-help@");
+  qmail_put(&qq,outhost.s,outhost.len);
+  qmail_puts(&qq,"\nTo: ");
+  if (!quote2(&quoted,addr.s)) die_nomem();
+  qmail_put(&qq,quoted.s,quoted.len);
+  qmail_puts(&qq,flagw ? "\nSubject: ezmlm probe\n\n" : "\nSubject: ezmlm warning\n\n");
+
+  copy("text/top");
+  copy(flagw ? "text/bounce-probe" : "text/bounce-warn");
+
+  if (!flagw) {
+    fdhash = open_read(fnhash.s);
+    if (fdhash == -1) {
+      if (errno != error_noent)
+        strerr_die6sys(111,FATAL,"unable to open ",dir,"/",fnhash.s,": ");
+    }
+    else {
+      copy("text/bounce-num");
+      substdio_fdbuf(&sstext,read,fdhash,textbuf,sizeof(textbuf));
+      if (substdio_copy(&ssqq,&sstext) < 0)
+        strerr_die6sys(111,FATAL,"unable to read ",dir,"/",fnhash.s,": ");
+      close(fdhash);
+    }
+  }
+
+  copy("text/bounce-bottom");
+  if (substdio_copy(&ssqq,&ssin) < 0) die_read();
+  close(fd);
+
+  strnum[fmt_ulong(strnum,when)] = 0;
+  cookie(hash,key.s,key.len,strnum,addr.s,flagw ? "P" : "W");
+  if (!stralloc_copy(&line,&outlocal)) die_nomem();
+  if (!stralloc_cats(&line,flagw ? "-return-probe-" : "-return-warn-")) die_nomem();
+  if (!stralloc_cats(&line,strnum)) die_nomem();
+  if (!stralloc_cats(&line,".")) die_nomem();
+  if (!stralloc_catb(&line,hash,COOKIE)) die_nomem();
+  if (!stralloc_cats(&line,"-")) die_nomem();
+  i = str_chr(addr.s,'@');
+  if (!stralloc_catb(&line,addr.s,i)) die_nomem();
+  if (addr.s[i]) {
+    if (!stralloc_cats(&line,"=")) die_nomem();
+    if (!stralloc_cats(&line,addr.s + i + 1)) die_nomem();
+  }
+  if (!stralloc_cats(&line,"@")) die_nomem();
+  if (!stralloc_cat(&line,&outhost)) die_nomem();
+  if (!stralloc_0(&line)) die_nomem();
+  qmail_from(&qq,line.s);
+
+  qmail_to(&qq,addr.s);
+  if (qmail_close(&qq) != 0)
+    strerr_die2x(111,FATAL,"temporary qmail-queue error");
+
+  strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
+  strerr_warn2("ezmlm-warn: info: qp ",strnum,0);
+
+  if (!flagw) {
+    if (unlink(fnhash.s) == -1)
+      if (errno != error_noent)
+        strerr_die6sys(111,FATAL,"unable to remove ",dir,"/",fnhash.s,": ");
+  }
+  if (unlink(fn.s) == -1)
+    strerr_die6sys(111,FATAL,"unable to remove ",dir,"/",fn.s,": ");
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  DIR *bouncedir;
+  direntry *d;
+  unsigned long bouncedate;
+  int fdlock;
+
+  umask(022);
+  sig_pipeignore();
+  when = (unsigned long) now();
+
+  dir = argv[1];
+  if (!dir) die_usage();
+
+  if (chdir(dir) == -1)
+    strerr_die4sys(111,FATAL,"unable to switch to ",dir,": ");
+
+  switch(slurp("key",&key,32)) {
+    case -1:
+      strerr_die4sys(111,FATAL,"unable to read ",dir,"/key: ");
+    case 0:
+      strerr_die3x(100,FATAL,dir,"/key does not exist");
+  }
+  getconf_line(&outhost,"outhost",1,FATAL,dir);
+  getconf_line(&outlocal,"outlocal",1,FATAL,dir);
+  getconf_line(&mailinglist,"mailinglist",1,FATAL,dir);
+
+  fdlock = open_append("lockbounce");
+  if (fdlock == -1)
+    strerr_die4sys(111,FATAL,"unable to open ",dir,"/lockbounce: ");
+  if (lock_ex(fdlock) == -1)
+    strerr_die4sys(111,FATAL,"unable to lock ",dir,"/lockbounce: ");
+
+  bouncedir = opendir("bounce");
+  if (!bouncedir)
+    strerr_die4sys(111,FATAL,"unable to open ",dir,"/bounce: ");
+
+  while (d = readdir(bouncedir)) {
+    if (str_equal(d->d_name,".")) continue;
+    if (str_equal(d->d_name,"..")) continue;
+
+    if (!stralloc_copys(&fn,"bounce/")) die_nomem();
+    if (!stralloc_cats(&fn,d->d_name)) die_nomem();
+    if (!stralloc_0(&fn)) die_nomem();
+
+    if (stat(fn.s,&st) == -1) {
+      if (errno == error_noent) continue;
+      strerr_die6sys(111,FATAL,"unable to stat ",dir,"/",fn.s,": ");
+    }
+
+    if (when > st.st_mtime + 3000000)
+      if (unlink(fn.s) == -1)
+        strerr_die6sys(111,FATAL,"unable to remove ",dir,"/",fn.s,": ");
+
+    if ((d->d_name[0] == 'd') || (d->d_name[0] == 'w')) {
+      scan_ulong(d->d_name + 1,&bouncedate);
+      if (when > bouncedate + 1000000)
+       doit(d->d_name[0] == 'w');
+    }
+  }
+
+  closedir(bouncedir);
+
+  _exit(0);
+}
diff --git a/ezmlm-warn=0 b/ezmlm-warn=0
new file mode 100644 (file)
index 0000000..e171750
--- /dev/null
@@ -0,0 +1 @@
+ezmlm-warn.1
diff --git a/ezmlm-warn=x b/ezmlm-warn=x
new file mode 100644 (file)
index 0000000..4425236
--- /dev/null
@@ -0,0 +1,26 @@
+auto_qmail.o
+getconf.o
+cookie.o
+issub.o
+now.o
+slurpclose.o
+slurp.o
+quote.o
+datetime.o
+date822fmt.o
+qmail.o
+surf.a
+case.a
+strerr.a
+sig.a
+getln.a
+substdio.a
+stralloc.a
+alloc.a
+error.a
+open.a
+lock.a
+str.a
+fs.a
+fd.a
+wait.a
diff --git a/ezmlm-weed.1 b/ezmlm-weed.1
new file mode 100644 (file)
index 0000000..5a5911f
--- /dev/null
@@ -0,0 +1,109 @@
+.TH ezmlm-weed 1
+.SH NAME
+ezmlm-weed \- weed out useless messages
+.SH SYNOPSIS
+.B ezmlm-weed
+.SH DESCRIPTION
+.B ezmlm-weed
+reads a mail message from its standard input.
+If it recognizes the message as an MTA warning message or success message,
+it exits 99;
+this will cause
+.B qmail-alias
+to skip all further delivery instructions if
+.B ezmlm-weed
+is run from a
+.B .qmail
+file.
+If the message seems innocent,
+.B ezmlm-weed
+exits 0.
+.B ezmlm-weed
+exits 111 if it runs out of memory.
+.SH "RECOGNIZED FORMATS"
+Generic success message, recommended for all MTAs:
+
+.EX
+   Subject: success notice
+.EE
+
+Generic warning message, recommended for all MTAs:
+
+.EX
+   Subject: deferral notice
+.EE
+
+Warning message from sendmail, MIME form:
+
+.EX
+   From: Mail Delivery Subsystem
+.EE
+.br
+.EX
+   Subject: Warning
+.EE
+.br
+.EX
+   Auto-Submitted: auto-generated (warning-timeout)
+.EE
+.br
+.EX
+   This is a MIME-encapsulated message
+.EE
+.br
+.EX
+   THIS IS A WARNING MESSAGE ONLY
+.EE
+
+Non-MIME form:
+
+.EX
+   From: Mail Delivery Subsystem
+.EE
+.br
+.EX
+   Subject: Warning
+.EE
+.br
+.EX
+   Auto-Submitted: auto-generated (warning-timeout)
+.EE
+.br
+.EX
+   THIS IS A WARNING MESSAGE ONLY
+.EE
+
+Warning message from older version of sendmail:
+
+.EX
+   From: Mail Delivery Subsystem
+.EE
+.br
+.EX
+   Subject: Returned mail: warning
+.EE
+.br
+.EX
+   This is a MIME-encapsulated message
+.EE
+.br
+.EX
+   THIS IS A WARNING MESSAGE ONLY
+.EE
+
+Non-MIME form:
+
+.EX
+   From: Mail Delivery Subsystem
+.EE
+.br
+.EX
+   Subject: Returned mail: warning
+.EE
+.br
+.EX
+   THIS IS A WARNING MESSAGE ONLY
+.EE
+.SH "SEE ALSO"
+ezmlm-return(1),
+qmail-command(8)
diff --git a/ezmlm-weed.c b/ezmlm-weed.c
new file mode 100644 (file)
index 0000000..777fa03
--- /dev/null
@@ -0,0 +1,94 @@
+#include "stralloc.h"
+#include "str.h"
+#include "byte.h"
+#include "readwrite.h"
+#include "substdio.h"
+#include "getln.h"
+#include "strerr.h"
+
+char buf0[256];
+substdio ss0 = SUBSTDIO_FDBUF(read,0,buf0,sizeof(buf0));
+
+#define FATAL "ezmlm-weed: fatal: "
+
+void get(sa)
+stralloc *sa;
+{
+  int match;
+  if (getln(&ss0,sa,&match,'\n') == -1)
+    strerr_die2sys(111,FATAL,"unable to read input: ");
+  if (!match) _exit(0);
+}
+
+stralloc line = {0};
+stralloc line1 = {0};
+stralloc line2 = {0};
+stralloc line3 = {0};
+stralloc line4 = {0};
+stralloc line5 = {0};
+stralloc line6 = {0};
+stralloc line7 = {0};
+stralloc line8 = {0};
+
+char warn1[] = "    **********************************************";
+char warn2[] = "    **      THIS IS A WARNING MESSAGE ONLY      **";
+char warn3[] = "    **  YOU DO NOT NEED TO RESEND YOUR MESSAGE  **";
+char warn4[] = "    **********************************************";
+
+int flagmds = 0;
+int flagsw = 0;
+int flagsr = 0;
+int flagas = 0;
+int flagbw = 0;
+
+void main()
+{
+  int match;
+
+  for (;;) {
+    get(&line);
+    if (line.len == 1) break;
+
+    if (stralloc_starts(&line,"Subject: success notice"))
+      _exit(99);
+    if (stralloc_starts(&line,"Subject: deferral notice"))
+      _exit(99);
+
+    if (stralloc_starts(&line,"From: Mail Delivery Subsystem <MAILER-DAEMON@"))
+      flagmds = 1;
+    if (stralloc_starts(&line,"Subject: Warning: could not send message"))
+      flagsw = 1;
+    if (stralloc_starts(&line,"Subject: Returned mail: warning: cannot send message"))
+      flagsr = 1;
+    if (stralloc_starts(&line,"Auto-Submitted: auto-generated (warning"))
+      flagas = 1;
+  }
+
+  get(&line1);
+  get(&line2);
+  get(&line3);
+  get(&line4);
+  get(&line5);
+  get(&line6);
+  get(&line7);
+  get(&line8);
+
+  if (stralloc_starts(&line1,"This is a MIME-encapsulated message"))
+  if (stralloc_starts(&line3,"--"))
+  if (stralloc_starts(&line5,warn1))
+  if (stralloc_starts(&line6,warn2))
+  if (stralloc_starts(&line7,warn3))
+  if (stralloc_starts(&line8,warn4))
+    flagbw = 1;
+
+  if (stralloc_starts(&line1,warn1))
+  if (stralloc_starts(&line2,warn2))
+  if (stralloc_starts(&line3,warn3))
+  if (stralloc_starts(&line4,warn4))
+    flagbw = 1;
+
+  if (flagmds && flagsw && flagas && flagbw) _exit(99);
+  if (flagmds && flagsr && flagbw) _exit(99);
+
+  _exit(0);
+}
diff --git a/ezmlm-weed=0 b/ezmlm-weed=0
new file mode 100644 (file)
index 0000000..f1aeb0d
--- /dev/null
@@ -0,0 +1 @@
+ezmlm-weed.1
diff --git a/ezmlm-weed=x b/ezmlm-weed=x
new file mode 100644 (file)
index 0000000..b1cccf1
--- /dev/null
@@ -0,0 +1,7 @@
+getln.a
+strerr.a
+substdio.a
+error.a
+stralloc.a
+alloc.a
+str.a
diff --git a/ezmlm.5 b/ezmlm.5
new file mode 100644 (file)
index 0000000..2866b91
--- /dev/null
+++ b/ezmlm.5
@@ -0,0 +1,317 @@
+.TH ezmlm 5
+.SH NAME
+ezmlm \- format of ezmlm directory
+.SH OVERVIEW
+An
+.B ezmlm
+directory,
+.IR dir ,
+stores information about an
+.B ezmlm
+mailing list.
+.B ezmlm-make
+creates
+.IR dir ;
+.B ezmlm-sub
+and
+.B ezmlm-unsub
+manipulate the subscriber list stored under
+.IR dir ;
+.B ezmlm-manage
+handles administrative requests automatically;
+.B ezmlm-send
+sends a message to all subscribers listed in
+.IR dir .
+.SH SUBSCRIBERS
+.I dir\fB/subscribers
+is a directory containing the subscriber list.
+.B ezmlm-manage
+allows automatic subscription if
+.I dir\fB/public
+exists.
+
+The list is hashed into 53 files, named
+.B @ 
+through
+.B t
+in ASCII.
+A nonexistent file is treated as an empty file.
+
+Each file contains a series of addresses.
+An address can be any string of non-NUL characters up to 400 bytes long.
+Each address is preceded by the letter T and followed by a NUL.
+
+For reliability,
+when an address is added to or removed from the mailing list,
+the relevant file is recreated under a temporary name
+inside
+.I dir\fB/subscribers
+and then moved into place.
+
+.I dir\fB/key
+is a binary file.
+.B ezmlm-manage
+uses
+.I dir\fB/key
+to create confirmation numbers.
+Anyone who can guess the contents of
+.I dir\fB/key
+can forge subscription requests.
+.B ezmlm-make
+does not put much effort into making
+.I dir\fB/key
+difficult to guess;
+for better security, you should add some random garbage to
+.IR dir\fB/key .
+.SH ARCHIVE
+.I dir\fB/archive
+is a directory containing messages previously sent to subscribers.
+.B ezmlm-send
+archives all new messages if
+.I dir\fB/archived
+exists.
+
+Messages sent to the mailing list are numbered from 1 upwards,
+whether or not they are archived.
+.I dir\fB/num
+is the number of messages sent so far.
+
+.I dir\fB/archive
+has subdirectories,
+each subdirectory storing up to 100 messages.
+Message number 100m+n, with n between 0 and 99, is stored in
+.IR dir\fB/archive/\fIm\fB/\fIn .
+For example, message number 15307 is stored in
+.IR dir\fB/archive/153/07 .
+
+.B ezmlm-manage
+will ignore message files without the owner-execute bit set.
+.B ezmlm-send
+turns on the owner-execute bit after safely writing the message to disk.
+.SH BOUNCES
+.I dir\fB/bounce
+is a directory containing bounce messages.
+.B ezmlm-return
+stores several types of files here.
+.SH "DELIVERY INSTRUCTIONS"
+.B ezmlm-make
+sets up four files to control mailing list deliveries.
+Each file is a series of delivery instructions in
+.B dot-qmail
+format.
+
+.I dir\fB/editor
+handles incoming mailing list submissions.
+.B ezmlm-make
+sets up
+.I dir\fB/editor
+to invoke
+.B ezmlm-send
+to immediately forward each message to all subscribers
+and then to run
+.BR ezmlm-warn .
+
+.I dir\fB/owner
+handles incoming messages for the mailing list's owner.
+.B ezmlm-make
+sets up 
+.I dir\fB/owner
+to store messages in
+.I dir\fB/Mailbox
+and then to run
+.BR ezmlm-warn .
+
+.I dir\fB/bouncer
+handles incoming bounce messages.
+.B ezmlm-make
+sets up
+.I dir\fB/bouncer
+to invoke
+.B ezmlm-return
+and then
+.BR ezmlm-warn .
+
+.I dir\fB/manager
+handles incoming administrative requests.
+.B ezmlm-make
+sets up
+.I dir\fB/manager
+to invoke
+.B ezmlm-manage
+and then
+.BR ezmlm-warn .
+.SH TEXT
+.I dir\fB/text
+is a directory
+containing files sent out by
+.B ezmlm-manage
+in response to administrative requests:
+.TP 15
+.B top
+Introducing
+.BR ezmlm .
+This is placed at the top of each response.
+.TP
+.B bottom
+Explaining how to use
+.BR ezmlm .
+This is placed at the bottom of each response.
+.TP
+.B sub-confirm
+Explaining how to confirm a subscription request.
+.TP
+.B sub-ok
+Acknowledging successful subscription.
+.TP
+.B sub-nop
+Acknowledging a subscription request for an address already
+on the mailing list.
+.TP
+.B sub-bad
+Rejecting a bad subscription confirmation number.
+.TP
+.B unsub-confirm
+Explaining how to confirm an unsubscription request,
+and explaining how to figure out the subscription address.
+.TP
+.B unsub-ok
+Acknowledging successful unsubscription.
+.TP
+.B unsub-nop
+Acknowledging an unsubscription request for an address not
+on the mailing list.
+.TP
+.B unsub-bad
+Rejecting a bad unsubscription confirmation number.
+.TP
+.B get-bad
+Rejecting a bad archive retrieval request.
+.TP
+.B bounce-warn
+Pointing out that messages have bounced.
+.TP
+.B bounce-probe
+Pointing out that a warning message has bounced.
+.TP
+.B bounce-num
+Explaining that
+.B ezmlm-return
+has kept a list of bounced message numbers.
+.TP
+.B bounce-bottom
+Separating the bounce message.
+.PP
+.B ezmlm-manage
+replaces the line
+.B !A
+in any of these files
+with the name of the subscription address.
+It replaces the line
+.B !R
+in
+.B sub-confirm
+and
+.B unsub-confirm
+with the address that the subscriber must reply to.
+.SH "OUTGOING MESSAGE EDITING"
+.I dir\fB/headerremove
+is a list of bad header field names,
+one per line.
+.B ezmlm-send
+removes these header fields from all outgoing messages.
+.B ezmlm-make
+sets up
+.I dir\fB/headerremove
+to remove
+.BR Return-Path ,
+.BR Return-Receipt-To ,
+and
+.B Return-Path
+fields.
+
+.I dir\fB/headeradd
+is a list of new header fields.
+.B ezmlm-send
+adds these fields to every outgoing message.
+.B ezmlm-make
+sets up
+.I dir\fB/headeradd
+with no new fields.
+.SH MISCELLANY
+The first line of
+.I dir\fB/mailinglist
+is a line of text.
+.B ezmlm-send
+creates a new
+.B Mailing-List
+field, showing the contents of
+.IR dir\fB/mailinglist ,
+in every outgoing message.
+
+The first lines of
+.I dir\fB/outlocal
+and
+.I dir\fB/outhost
+give the outgoing name of the mailing list.
+These are used by
+.B ezmlm-manage
+and
+.B ezmlm-send
+to construct sender addresses for outgoing messages.
+
+The first lines of
+.I dir\fB/inlocal
+and
+.I dir\fB/inhost
+give the incoming name of the mailing list.
+These are used by
+.B ezmlm-manage
+to parse incoming envelopes.
+Normally
+.I inlocal
+and
+.I inhost
+are the same as
+.I outlocal
+and
+.IR outhost ,
+but sometimes they are different,
+with
+.I outlocal\fB@\fIouthost
+forwarded to
+.IR inlocal\fB@\fIinhost .
+
+If
+.I dir\fB/sublist
+exists,
+this mailing list is a sublist,
+redistributing messages from a parent mailing list.
+The first line of
+.I dir\fB/sublist
+is the name of the parent list.
+This affects the behavior of
+.BR ezmlm-send .
+
+.I dir\fB/lock
+is an empty file.
+Any program that reads or writes the subscriber list,
+or adds messages to the archive,
+locks
+.IR dir\fB/lock .
+
+.I dir\fB/Log
+is an advisory log of subscription and unsubscription actions.
+.B WARNING:
+.B Log
+is not protected against system crashes.
+Log entries may be missing or corrupted if the system goes down.
+.SH "SEE ALSO"
+ezmlm-list(1),
+ezmlm-make(1),
+ezmlm-manage(1),
+ezmlm-return(1),
+ezmlm-send(1),
+ezmlm-sub(1),
+ezmlm-unsub(1),
+ezmlm-warn(1),
+dot-qmail(5)
diff --git a/ezmlm=0 b/ezmlm=0
new file mode 100644 (file)
index 0000000..b3cbb0c
--- /dev/null
+++ b/ezmlm=0
@@ -0,0 +1 @@
+ezmlm.5
diff --git a/fd.h b/fd.h
new file mode 100644 (file)
index 0000000..c3d6e3e
--- /dev/null
+++ b/fd.h
@@ -0,0 +1,7 @@
+#ifndef FD_H
+#define FD_H
+
+extern int fd_copy();
+extern int fd_move();
+
+#endif
diff --git a/fd=l b/fd=l
new file mode 100644 (file)
index 0000000..402ffe2
--- /dev/null
+++ b/fd=l
@@ -0,0 +1,2 @@
+fd_copy.o
+fd_move.o
diff --git a/fd_copy.3 b/fd_copy.3
new file mode 100644 (file)
index 0000000..758a7e7
--- /dev/null
+++ b/fd_copy.3
@@ -0,0 +1,44 @@
+.TH fd_copy 3
+.SH NAME
+fd_copy \- duplicate a descriptor
+.SH SYNTAX
+.B #include <fd.h>
+
+int \fBfd_copy\fP(\fIto\fR,\fIfrom\fR);
+
+int \fIto\fR;
+.br
+int \fIfrom\fR;
+.SH DESCRIPTION
+.B fd_copy
+copies 
+descriptor
+.I from
+to descriptor
+.IR to .
+If
+.I to
+was already open,
+.B fd_copy
+closes it.
+.B fd_copy
+always leaves
+.I from
+intact;
+if
+.I to
+and
+.I from
+are the same number,
+.B fd_copy
+does nothing.
+
+.B fd_copy
+returns 0 on success, -1 on error.
+.B fd_copy
+does not guarantee that
+.I to
+will remain open, if it was open, in case of error.
+.SH "SEE ALSO"
+dup(2),
+fd_move(3)
diff --git a/fd_copy.c b/fd_copy.c
new file mode 100644 (file)
index 0000000..b9f7167
--- /dev/null
+++ b/fd_copy.c
@@ -0,0 +1,13 @@
+#include <fcntl.h>
+#include "fd.h"
+
+int fd_copy(to,from)
+int to;
+int from;
+{
+  if (to == from) return 0;
+  if (fcntl(from,F_GETFL,0) == -1) return -1;
+  close(to);
+  if (fcntl(from,F_DUPFD,to) == -1) return -1;
+  return 0;
+}
diff --git a/fd_copy=0 b/fd_copy=0
new file mode 100644 (file)
index 0000000..f8a195e
--- /dev/null
+++ b/fd_copy=0
@@ -0,0 +1 @@
+fd_copy.3
diff --git a/fd_move.3 b/fd_move.3
new file mode 100644 (file)
index 0000000..94aa1b7
--- /dev/null
+++ b/fd_move.3
@@ -0,0 +1,41 @@
+.TH fd_move 3
+.SH NAME
+fd_move \- renumber a descriptor
+.SH SYNTAX
+.B #include <fd.h>
+
+int \fBfd_move\fP(\fIto\fR,\fIfrom\fR);
+
+int \fIto\fR;
+.br
+int \fIfrom\fR;
+.SH DESCRIPTION
+.B fd_move
+moves
+descriptor
+.I from
+to descriptor
+.IR to .
+If
+.I to
+was already open,
+.B fd_move
+closes it.
+If the move is successful,
+.B fd_move
+closes
+.IR from .
+Exception:
+if
+.I to
+and
+.I from
+are the same number,
+.B fd_move
+does nothing.
+
+.B fd_move
+returns 0 on success, -1 on error.
+.SH "SEE ALSO"
+dup(2),
+fd_copy(3)
diff --git a/fd_move.c b/fd_move.c
new file mode 100644 (file)
index 0000000..1aa557f
--- /dev/null
+++ b/fd_move.c
@@ -0,0 +1,11 @@
+#include "fd.h"
+
+int fd_move(to,from)
+int to;
+int from;
+{
+  if (to == from) return 0;
+  if (fd_copy(to,from) == -1) return -1;
+  close(from);
+  return 0;
+}
diff --git a/fd_move=0 b/fd_move=0
new file mode 100644 (file)
index 0000000..c2c8185
--- /dev/null
+++ b/fd_move=0
@@ -0,0 +1 @@
+fd_move.3
diff --git a/find-systype.sh b/find-systype.sh
new file mode 100644 (file)
index 0000000..16266d3
--- /dev/null
@@ -0,0 +1,144 @@
+# oper-:arch-:syst-:chip-:kern-
+# oper = operating system type; e.g., sunos-4.1.4
+# arch = machine language; e.g., sparc
+# syst = which binaries can run; e.g., sun4
+# chip = chip model; e.g., micro-2-80
+# kern = kernel version; e.g., sun4m
+# dependence: arch --- chip
+#                 \        \
+#          oper --- syst --- kern
+# so, for example, syst is interpreted in light of oper, but chip is not.
+# anyway, no slashes, no extra colons, no uppercase letters.
+# the point of the extra -'s is to ease parsing: can add hierarchies later.
+# e.g., *:i386-*:*:pentium-*:* would handle pentium-100 as well as pentium,
+# and i386-486 (486s do have more instructions, you know) as well as i386.
+# the idea here is to include ALL useful available information.
+
+exec 2>/dev/null
+sys="`uname -s | tr '/:[A-Z]' '..[a-z]'`"
+if [ x"$sys" != x ]
+then
+  unamer="`uname -r | tr /: ..`"
+  unamem="`uname -m | tr /: ..`"
+  unamev="`uname -v | tr /: ..`"
+
+  case "$sys" in
+  bsd.os)
+    # in bsd 4.4, uname -v does not have useful info.
+    # in bsd 4.4, uname -m is arch, not chip.
+    oper="$sys-$unamer"
+    arch="$unamem"
+    syst=""
+    chip="`sysctl -n hw.model`"
+    kern=""
+    ;;
+  freebsd)
+    # see above about bsd 4.4
+    oper="$sys-$unamer"
+    arch="$unamem"
+    syst=""
+    chip="`sysctl -n hw.model`" # hopefully
+    kern=""
+    ;;
+  netbsd)
+    # see above about bsd 4.4
+    oper="$sys-$unamer"
+    arch="$unamem"
+    syst=""
+    chip="`sysctl -n hw.model`" # hopefully
+    kern=""
+    ;;
+  linux)
+    # as in bsd 4.4, uname -v does not have useful info.
+    oper="$sys-$unamer"
+    syst=""
+    chip="$unamem"
+    kern=""
+    case "$chip" in
+    i386|i486|i586|i686)
+      arch="i386"
+      ;;
+    alpha)
+      arch="alpha"
+      ;;
+    esac
+    ;;
+  aix)
+    # naturally IBM has to get uname -r and uname -v backwards. dorks.
+    oper="$sys-$unamev-$unamer"
+    arch="`arch | tr /: ..`"
+    syst=""
+    chip="$unamem"
+    kern=""
+    ;;
+  sunos)
+    oper="$sys-$unamer-$unamev"
+    arch="`(uname -p || mach) | tr /: ..`"
+    syst="`arch | tr /: ..`"
+    chip="$unamem" # this is wrong; is there any way to get the real info?
+    kern="`arch -k | tr /: ..`"
+    ;;
+  unix_sv)
+    oper="$sys-$unamer-$unamev"
+    arch="`uname -m`"
+    syst=""
+    chip="$unamem"
+    kern=""
+    ;;
+  *)
+    oper="$sys-$unamer-$unamev"
+    arch="`arch | tr /: ..`"
+    syst=""
+    chip="$unamem"
+    kern=""
+    ;;
+  esac
+else
+  $CC -c trycpp.c
+  $LD -o trycpp trycpp.o
+  case `./trycpp` in
+  nextstep)
+    oper="nextstep-`hostinfo | sed -n 's/^[    ]*NeXT Mach \([^:]*\):.*$/\1/p'`"
+    arch="`hostinfo | sed -n 's/^Processor type: \(.*\) (.*)$/\1/p' | tr /: ..`"
+    syst=""
+    chip="`hostinfo | sed -n 's/^Processor type: .* (\(.*\))$/\1/p' | tr ' /:' '...'`"
+    kern=""
+    ;;
+  *)
+    oper="unknown"
+    arch=""
+    syst=""
+    chip=""
+    kern=""
+    ;;
+  esac
+  rm -f trycpp.o trycpp
+fi
+
+case "$chip" in
+80486)
+  # let's try to be consistent here. (BSD/OS)
+  chip=i486
+  ;;
+i486DX)
+  # respect the hyphen hierarchy. (FreeBSD)
+  chip=i486-dx
+  ;;
+i486.DX2)
+  # respect the hyphen hierarchy. (FreeBSD)
+  chip=i486-dx2
+  ;;
+Intel.586)
+  # no, you nitwits, there is no such chip. (NeXTStep)
+  chip=pentium
+  ;;
+i586)
+  # no, you nitwits, there is no such chip. (Linux)
+  chip=pentium
+  ;;
+i686)
+  # STOP SAYING THAT! (Linux)
+  chip=ppro
+esac
+
+echo "$oper-:$arch-:$syst-:$chip-:$kern-" | tr ' [A-Z]' '.[a-z]'
diff --git a/fmt.h b/fmt.h
new file mode 100644 (file)
index 0000000..ba2fe16
--- /dev/null
+++ b/fmt.h
@@ -0,0 +1,25 @@
+#ifndef FMT_H
+#define FMT_H
+
+#define FMT_ULONG 40 /* enough space to hold 2^128 - 1 in decimal, plus \0 */
+#define FMT_LEN ((char *) 0) /* convenient abbreviation */
+
+extern unsigned int fmt_uint();
+extern unsigned int fmt_uint0();
+extern unsigned int fmt_xint();
+extern unsigned int fmt_nbbint();
+extern unsigned int fmt_ushort();
+extern unsigned int fmt_xshort();
+extern unsigned int fmt_nbbshort();
+extern unsigned int fmt_ulong();
+extern unsigned int fmt_xlong();
+extern unsigned int fmt_nbblong();
+
+extern unsigned int fmt_plusminus();
+extern unsigned int fmt_minus();
+extern unsigned int fmt_0x();
+
+extern unsigned int fmt_str();
+extern unsigned int fmt_strn();
+
+#endif
diff --git a/fmt_str.c b/fmt_str.c
new file mode 100644 (file)
index 0000000..48930cf
--- /dev/null
+++ b/fmt_str.c
@@ -0,0 +1,12 @@
+#include "fmt.h"
+
+unsigned int fmt_str(s,t)
+register char *s; register char *t;
+{
+  register unsigned int len;
+  char ch;
+  len = 0;
+  if (s) { while (ch = t[len]) s[len++] = ch; }
+  else while (t[len]) len++;
+  return len;
+}
diff --git a/fmt_uint.c b/fmt_uint.c
new file mode 100644 (file)
index 0000000..8595be8
--- /dev/null
@@ -0,0 +1,6 @@
+#include "fmt.h"
+
+unsigned int fmt_uint(s,u) register char *s; register unsigned int u;
+{
+  register unsigned long l; l = u; return fmt_ulong(s,l);
+}
diff --git a/fmt_uint0.c b/fmt_uint0.c
new file mode 100644 (file)
index 0000000..81705f6
--- /dev/null
@@ -0,0 +1,10 @@
+#include "fmt.h"
+
+unsigned int fmt_uint0(s,u,n) char *s; unsigned int u; unsigned int n;
+{
+  unsigned int len;
+  len = fmt_uint(FMT_LEN,u);
+  while (len < n) { if (s) *s++ = '0'; ++len; }
+  if (s) fmt_uint(s,u);
+  return len;
+}
diff --git a/fmt_ulong.c b/fmt_ulong.c
new file mode 100644 (file)
index 0000000..ab12e8c
--- /dev/null
@@ -0,0 +1,13 @@
+#include "fmt.h"
+
+unsigned int fmt_ulong(s,u) register char *s; register unsigned long u;
+{
+  register unsigned int len; register unsigned long q;
+  len = 1; q = u;
+  while (q > 9) { ++len; q /= 10; }
+  if (s) {
+    s += len;
+    do { *--s = '0' + (u % 10); u /= 10; } while(u); /* handles u == 0 */
+  }
+  return len;
+}
diff --git a/fork.h.do b/fork.h.do
new file mode 100644 (file)
index 0000000..e095298
--- /dev/null
+++ b/fork.h.do
@@ -0,0 +1,7 @@
+dependon compile load tryvfork.c fork.h1 fork.h2
+( ./compile tryvfork.c && ./load tryvfork ) >/dev/null 2>&1 \
+&& cat fork.h2 || cat fork.h1
+rm -f tryvfork.o tryvfork
+formake '( ( ./compile tryvfork.c && ./load tryvfork ) >/dev/null 2>&1 \'
+formake '&& cat fork.h2 || cat fork.h1 ) > fork.h'
+formake 'rm -f tryvfork.o tryvfork'
diff --git a/fork.h1 b/fork.h1
new file mode 100644 (file)
index 0000000..b786255
--- /dev/null
+++ b/fork.h1
@@ -0,0 +1,7 @@
+#ifndef FORK_H
+#define FORK_H
+
+extern int fork();
+#define vfork fork
+
+#endif
diff --git a/fork.h2 b/fork.h2
new file mode 100644 (file)
index 0000000..41773b6
--- /dev/null
+++ b/fork.h2
@@ -0,0 +1,7 @@
+#ifndef FORK_H
+#define FORK_H
+
+extern int fork();
+extern int vfork();
+
+#endif
diff --git a/fs=l b/fs=l
new file mode 100644 (file)
index 0000000..9e09636
--- /dev/null
+++ b/fs=l
@@ -0,0 +1,6 @@
+fmt_str.o
+fmt_uint.o
+fmt_uint0.o
+fmt_ulong.o
+scan_ulong.o
+scan_8long.o
diff --git a/gen_alloc.h b/gen_alloc.h
new file mode 100644 (file)
index 0000000..b94a956
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef GEN_ALLOC_H
+#define GEN_ALLOC_H
+
+#define GEN_ALLOC_typedef(ta,type,field,len,a) \
+  typedef struct ta { type *field; unsigned int len; unsigned int a; } ta;
+
+#endif
diff --git a/gen_allocdefs.h b/gen_allocdefs.h
new file mode 100644 (file)
index 0000000..783a9b1
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef GEN_ALLOC_DEFS_H
+#define GEN_ALLOC_DEFS_H
+
+#define GEN_ALLOC_ready(ta,type,field,len,a,i,n,x,base,ta_ready) \
+int ta_ready(x,n) register ta *x; register unsigned int n; \
+{ register unsigned int i; \
+  if (x->field) { \
+    i = x->a; \
+    if (n > i) { \
+      x->a = base + n + (n >> 3); \
+      if (alloc_re(&x->field,i * sizeof(type),x->a * sizeof(type))) return 1; \
+      x->a = i; return 0; } \
+    return 1; } \
+  x->len = 0; \
+  return !!(x->field = (type *) alloc((x->a = n) * sizeof(type))); }
+
+#define GEN_ALLOC_readyplus(ta,type,field,len,a,i,n,x,base,ta_rplus) \
+int ta_rplus(x,n) register ta *x; register unsigned int n; \
+{ register unsigned int i; \
+  if (x->field) { \
+    i = x->a; n += x->len; \
+    if (n > i) { \
+      x->a = base + n + (n >> 3); \
+      if (alloc_re(&x->field,i * sizeof(type),x->a * sizeof(type))) return 1; \
+      x->a = i; return 0; } \
+    return 1; } \
+  x->len = 0; \
+  return !!(x->field = (type *) alloc((x->a = n) * sizeof(type))); }
+
+#define GEN_ALLOC_append(ta,type,field,len,a,i,n,x,base,ta_rplus,ta_append) \
+int ta_append(x,i) register ta *x; register type *i; \
+{ if (!ta_rplus(x,1)) return 0; x->field[x->len++] = *i; return 1; }
+
+#endif
diff --git a/getconf.c b/getconf.c
new file mode 100644 (file)
index 0000000..89b6d31
--- /dev/null
+++ b/getconf.c
@@ -0,0 +1,61 @@
+#include "stralloc.h"
+#include "slurp.h"
+#include "strerr.h"
+#include "getconf.h"
+
+static stralloc data = {0};
+
+void nomem(fatal)
+char *fatal;
+{
+  strerr_die2x(111,fatal,"out of memory");
+}
+
+int getconf(sa,fn,flagrequired,fatal,dir)
+stralloc *sa;
+char *fatal;
+int flagrequired;
+char *dir;
+char *fn;
+{
+  int i;
+  int j;
+  int k;
+
+  if (!stralloc_copys(&data,""))
+    nomem(fatal);
+  switch(slurp(fn,&data,128)) {
+    case -1:
+      strerr_die6sys(111,fatal,"unable to read ",dir,"/",fn,": ");
+    case 0:
+      if (!flagrequired)
+       return 0;
+      strerr_die5x(100,fatal,dir,"/",fn," does not exist");
+  }
+  if (!stralloc_append(&data,"\n")) nomem(fatal);
+  if (!stralloc_copys(sa,"")) nomem(fatal);
+  i = 0;
+  for (j = 0;j < data.len;++j)
+    if (data.s[j] == '\n') {
+      k = j;
+      while ((k > i) && ((data.s[k-1] == ' ') || (data.s[k-1] == '\t'))) --k;
+      if ((k > i) && (data.s[i] != '#')) {
+        if (!stralloc_catb(sa,data.s + i,k - i)) nomem(fatal);
+        if (!stralloc_0(sa)) nomem(fatal);
+      }
+      i = j + 1;
+    }
+  return 1;
+}
+
+int getconf_line(sa,fn,flagrequired,fatal,dir)
+stralloc *sa;
+char *fatal;
+int flagrequired;
+char *dir;
+char *fn;
+{
+  if (!getconf(sa,fn,flagrequired,fatal,dir)) return 0;
+  sa->len = byte_chr(sa->s,sa->len,0);
+  return 1;
+}
diff --git a/getconf.h b/getconf.h
new file mode 100644 (file)
index 0000000..20962f0
--- /dev/null
+++ b/getconf.h
@@ -0,0 +1,7 @@
+#ifndef GETCONF_H
+#define GETCONF_H
+
+extern int getconf();
+extern int getconf_line();
+
+#endif
diff --git a/getln.3 b/getln.3
new file mode 100644 (file)
index 0000000..ffe1953
--- /dev/null
+++ b/getln.3
@@ -0,0 +1,51 @@
+.TH getln 3
+.SH NAME
+getln \- read one line of data
+.SH SYNTAX
+.B #include <getln.h>
+
+int \fBgetln\fP(&\fIss\fR,&\fIsa\fR,&\fImatch\fR,\fIsep\fR);
+
+substdio \fIss\fR;
+.br
+stralloc \fIsa\fR;
+.br
+int \fImatch\fR;
+.br
+int \fIsep\fR;
+.SH DESCRIPTION
+.B getln
+reads a line of characters, terminated by a 
+.I sep
+character,
+from
+.IR ss .
+It returns the line in
+.I sa
+and sets
+.I match
+to 1.
+
+If
+.B getln
+sees end-of-input before it sees
+.IR sep ,
+it returns the partial line in
+.I sa
+and sets
+.I match
+to 0.
+
+.B getln
+normally returns 0.
+If it runs out of memory,
+or encounters an error from
+.IR ss ,
+it returns -1,
+setting
+.B errno
+appropriately.
+.SH "SEE ALSO"
+stralloc(3),
+substdio(3),
+getln2(3)
diff --git a/getln.c b/getln.c
new file mode 100644 (file)
index 0000000..c5cb097
--- /dev/null
+++ b/getln.c
@@ -0,0 +1,20 @@
+#include "substdio.h"
+#include "byte.h"
+#include "stralloc.h"
+#include "getln.h"
+
+int getln(ss,sa,match,sep)
+register substdio *ss;
+register stralloc *sa;
+int *match;
+int sep;
+{
+  char *cont;
+  unsigned int clen;
+
+  if (getln2(ss,sa,&cont,&clen,sep) == -1) return -1;
+  if (!clen) { *match = 0; return 0; }
+  if (!stralloc_catb(sa,cont,clen)) return -1;
+  *match = 1;
+  return 0;
+}
diff --git a/getln.h b/getln.h
new file mode 100644 (file)
index 0000000..cf4f934
--- /dev/null
+++ b/getln.h
@@ -0,0 +1,7 @@
+#ifndef GETLN_H
+#define GETLN_H
+
+extern int getln();
+extern int getln2();
+
+#endif
diff --git a/getln2.3 b/getln2.3
new file mode 100644 (file)
index 0000000..f95105a
--- /dev/null
+++ b/getln2.3
@@ -0,0 +1,64 @@
+.TH getln2 3
+.SH NAME
+getln2 \- read one line of data
+.SH SYNTAX
+.B #include <getln.h>
+
+int \fBgetln2\fP(&\fIss\fR,&\fIsa\fR,&\fIcont\fR,&\fIclen\fR,\fIsep\fR);
+
+substdio \fIss\fR;
+.br
+stralloc \fIsa\fR;
+.br
+char *\fIcont\fR;
+.br
+unsigned int \fIclen\fR;
+.br
+int \fIsep\fR;
+.SH DESCRIPTION
+.B getln2
+reads a line of characters, terminated by a 
+.I sep
+character,
+from
+.IR ss .
+
+The line is returned in two pieces.
+The first piece is stored in
+.IR sa .
+The second piece is
+.IR cont ,
+a pointer to
+.I clen
+characters inside the
+.I ss
+buffer.
+The second piece must be copied somewhere else
+before
+.I ss
+is used again.
+
+If
+.B getln2
+sees end-of-input before it sees
+.IR sep ,
+it sets
+.I clen
+to 0 and does not set
+.IR cont .
+It puts the partial line into
+.IR sa .
+
+.B getln2
+normally returns 0.
+If it runs out of memory,
+or encounters an error from
+.IR ss ,
+it returns -1,
+setting
+.B errno
+appropriately.
+.SH "SEE ALSO"
+stralloc(3),
+substdio(3),
+getln(3)
diff --git a/getln2.c b/getln2.c
new file mode 100644 (file)
index 0000000..9e576e9
--- /dev/null
+++ b/getln2.c
@@ -0,0 +1,31 @@
+#include "substdio.h"
+#include "stralloc.h"
+#include "byte.h"
+#include "getln.h"
+
+int getln2(ss,sa,cont,clen,sep)
+register substdio *ss;
+register stralloc *sa;
+/*@out@*/char **cont;
+/*@out@*/unsigned int *clen;
+int sep;
+{
+  register char *x;
+  register unsigned int i;
+  int n;
+  if (!stralloc_ready(sa,0)) return -1;
+  sa->len = 0;
+  for (;;) {
+    n = substdio_feed(ss);
+    if (n < 0) return -1;
+    if (n == 0) { *clen = 0; return 0; }
+    x = substdio_PEEK(ss);
+    i = byte_chr(x,n,sep);
+    if (i < n) { substdio_SEEK(ss,*clen = i + 1); *cont = x; return 0; }
+    if (!stralloc_readyplus(sa,n)) return -1;
+    i = sa->len;
+    sa->len = i + substdio_get(ss,sa->s + i,n);
+  }
+}
diff --git a/getln2=0 b/getln2=0
new file mode 100644 (file)
index 0000000..ee6879c
--- /dev/null
+++ b/getln2=0
@@ -0,0 +1 @@
+getln2.3
diff --git a/getln=0 b/getln=0
new file mode 100644 (file)
index 0000000..68df95c
--- /dev/null
+++ b/getln=0
@@ -0,0 +1 @@
+getln.3
diff --git a/getln=l b/getln=l
new file mode 100644 (file)
index 0000000..b0f37fa
--- /dev/null
+++ b/getln=l
@@ -0,0 +1,2 @@
+getln.o
+getln2.o
diff --git a/getopt.3 b/getopt.3
new file mode 100644 (file)
index 0000000..ecbf50a
--- /dev/null
+++ b/getopt.3
@@ -0,0 +1,235 @@
+.TH getopt 3
+.SH NAME
+getopt \- get option character from command line
+.SH SYNTAX
+.B #include <getopt.h>
+
+char *\fBoptarg\fP;
+.br
+int \fBoptind\fP;
+.br
+int \fBoptpos\fP;
+.br
+int \fBopteof\fP;
+.br
+int \fBoptproblem\fP;
+.br
+char *\fBoptprogname\fP;
+.br
+int \fBopterr\fP;
+
+int \fBgetopt(\fP\fIargc,argv,opts\fR\fB)\fP;
+
+int \fIargc\fR;
+.br
+char **\fIargv\fR;
+.br
+char *\fIopts\fR;
+.SH DESCRIPTION
+This is a clone version of
+the standard
+.B getopt
+library,
+built on top of
+.BR subgetopt(3) .
+
+See
+.B subgetopt(3)
+for a detailed description of
+.B getopt
+processing.
+The main difference between
+.B getopt
+and
+.B subgetopt
+is that
+.B getopt
+prints error messages
+in case of problems.
+To turn off these error messages, set
+.B opterr
+(default nonzero)
+to zero.
+
+This clone version of
+.B getopt
+also provides an
+.B optprogname
+variable.
+There are two uses for this variable:
+
+(1)
+By default
+.B optprogname
+is null.
+When
+.B getopt
+sees this,
+it
+attempts to initialize
+.B optprogname
+from
+.IR argv\fB[0] ,
+stripping the directory name.
+The calling program can use
+.B optprogname
+after calling
+.B getopt
+at least once.
+This is appropriate if the name of the program should be
+determined from its command line.
+
+(2)
+.B getopt
+prints
+.B optprogname
+at the beginning
+of any error messages.
+So the calling program can,
+before calling
+.BR getopt ,
+initialize
+.B optprogname
+as desired.
+This is appropriate if the name of the program should not be
+determined from its command line.
+.SH "COMPATIBILITY"
+Old versions of
+.B getopt
+do not include
+.BR opterr .
+.BR optpos ,
+.BR opteof ,
+.BR optproblem ,
+and
+.B optprogname
+are specific to this clone version of
+.BR getopt .
+
+Many features of this clone version of
+.B getopt
+are poorly defined, if available at all,
+in most versions of
+.BR getopt .
+For example, the standard
+.B getopt
+interface does not define
+.B optind
+until the end of the option list.
+And
+.B optarg
+is not defined
+unless
+.B getopt
+has just returned
+an option which takes an argument.
+In this clone version,
+.B optind
+and
+.B optpos
+always indicate the next character to be read,
+and
+.B optarg
+is null whenever
+the current option does not take an argument.
+See
+.B subgetopt(3)
+for precise definitions of the parsing procedure.
+
+When it reaches the end of the option list,
+this version of
+.B getopt
+always returns
+.BR opteof ,
+which is the same as
+.BR subgetoptdone ,
+which is initialized to
+.BR SUBGETOPTDONE ,
+which is defined as \-1.
+The standard behavior is to return
+EOF
+from
+.B stdio(3).
+This is incompatible
+on any weird machine where
+EOF is different from \-1.
+The calling program could set
+.B opteof
+to EOF to imitate the standard behavior.
+
+Like most versions of
+.BR getopt ,
+this clone version allows, but does not demand, that
+option arguments be
+separated from the option by whitespace, i.e., be
+in the next command-line argument.
+
+Some versions of
+.B getopt
+provide an
+.B optopt
+variable.
+.B optopt
+is incompatible across systems:
+for example,
+GNU
+.B getopt
+uses it the same way that this clone version uses
+.BR optproblem ,
+while
+BSD
+.B getopt
+uses it to
+indicate the last option character returned by
+.BR getopt .
+This clone version does not provide
+.BR optopt .
+The use of
+.B optopt
+is strongly discouraged.
+
+Some versions of
+.B getopt
+do not recognize a double hyphen as the end of arguments.
+This version allows a double hyphen, or in fact any argument beginning
+with two hyphens.
+
+A lone hyphen is always recognized as the end of arguments.
+Some versions of
+.B getopt
+allow lone hyphens as options.
+This practice is wrong and is strongly discouraged.
+.SH "SYNTAX NOTE"
+.B getopt
+is actually a macro abbreviation for
+.BR getoptmine .
+The external
+.B opterr
+and
+.B optprogname
+variables
+are macros for
+.B getopterr
+and
+.BR getoptprogname .
+All the other
+.B opt
+variables are macros
+for
+.BR subgetopt .
+These macros are defined in
+.BR <getopt.h> ,
+unless
+.B GETOPTNOSHORT
+is defined.
+Further macros are defined in
+.BR <subgetopt.h> ,
+which is included by
+.BR <getopt.h> ,
+unless
+.B SUBGETOPTNOSHORT
+is defined.
+.SH VERSION
+getopt version 1.9, 931129.
+.SH AUTHOR
+Placed into the public domain by Daniel J. Bernstein.
diff --git a/getopt=0 b/getopt=0
new file mode 100644 (file)
index 0000000..52e530c
--- /dev/null
+++ b/getopt=0
@@ -0,0 +1 @@
+getopt.3
diff --git a/getopt=l b/getopt=l
new file mode 100644 (file)
index 0000000..a667e1e
--- /dev/null
+++ b/getopt=l
@@ -0,0 +1,2 @@
+subgetopt.o
+sgetopt.o
diff --git a/hasflock.h.do b/hasflock.h.do
new file mode 100644 (file)
index 0000000..c7d8678
--- /dev/null
@@ -0,0 +1,7 @@
+dependon tryflock.c compile load
+( ./compile tryflock.c && ./load tryflock ) >/dev/null 2>&1 \
+&& echo \#define HASFLOCK 1
+rm -f tryflock.o tryflock
+formake '( ( ./compile tryflock.c && ./load tryflock ) >/dev/null 2>&1 \'
+formake '&& echo \#define HASFLOCK 1 || exit 0 ) > hasflock.h'
+formake 'rm -f tryflock.o tryflock'
diff --git a/hassgact.h.do b/hassgact.h.do
new file mode 100644 (file)
index 0000000..df277d5
--- /dev/null
@@ -0,0 +1,7 @@
+dependon trysgact.c compile load
+( ./compile trysgact.c && ./load trysgact ) >/dev/null 2>&1 \
+&& echo \#define HASSIGACTION 1
+rm -f trysgact.o trysgact
+formake '( ( ./compile trysgact.c && ./load trysgact ) >/dev/null 2>&1 \'
+formake '&& echo \#define HASSIGACTION 1 || exit 0 ) > hassgact.h'
+formake 'rm -f trysgact.o trysgact'
diff --git a/install.c b/install.c
new file mode 100644 (file)
index 0000000..24f0916
--- /dev/null
+++ b/install.c
@@ -0,0 +1,140 @@
+#include "substdio.h"
+#include "stralloc.h"
+#include "getln.h"
+#include "readwrite.h"
+#include "exit.h"
+#include "open.h"
+#include "error.h"
+#include "strerr.h"
+#include "byte.h"
+
+stralloc target = {0};
+char *to;
+
+#define FATAL "install: fatal: "
+void nomem() { strerr_die2x(111,FATAL,"out of memory"); }
+
+char inbuf[SUBSTDIO_INSIZE];
+char outbuf[SUBSTDIO_OUTSIZE];
+substdio ssin;
+substdio ssout;
+
+void doit(line)
+stralloc *line;
+{
+  char *x;
+  unsigned int xlen;
+  unsigned int i;
+  char *type;
+  char *uidstr;
+  char *gidstr;
+  char *modestr;
+  char *mid;
+  char *name;
+  unsigned long uid;
+  unsigned long gid;
+  unsigned long mode;
+  int fdin;
+  int fdout;
+
+  x = line->s; xlen = line->len;
+
+  type = x;
+  i = byte_chr(x,xlen,':'); if (i == xlen) return;
+  x[i++] = 0; x += i; xlen -= i;
+
+  uidstr = x;
+  i = byte_chr(x,xlen,':'); if (i == xlen) return;
+  x[i++] = 0; x += i; xlen -= i;
+
+  gidstr = x;
+  i = byte_chr(x,xlen,':'); if (i == xlen) return;
+  x[i++] = 0; x += i; xlen -= i;
+
+  modestr = x;
+  i = byte_chr(x,xlen,':'); if (i == xlen) return;
+  x[i++] = 0; x += i; xlen -= i;
+
+  mid = x;
+  i = byte_chr(x,xlen,':'); if (i == xlen) return;
+  x[i++] = 0; x += i; xlen -= i;
+
+  name = x;
+  i = byte_chr(x,xlen,':'); if (i == xlen) return;
+  x[i++] = 0; x += i; xlen -= i;
+
+  if (!stralloc_copys(&target,to)) nomem();
+  if (!stralloc_cats(&target,mid)) nomem();
+  if (!stralloc_cats(&target,name)) nomem();
+  if (!stralloc_0(&target)) nomem();
+
+  uid = -1; if (*uidstr) scan_ulong(uidstr,&uid);
+  gid = -1; if (*gidstr) scan_ulong(gidstr,&gid);
+  scan_8long(modestr,&mode);
+
+  switch(*type) {
+    case 'd':
+      if (mkdir(target.s,0700) == -1)
+        if (errno != error_exist)
+         strerr_die4sys(111,FATAL,"unable to mkdir ",target.s,": ");
+      break;
+
+    case 'c':
+      fdin = open_read(name);
+      if (fdin == -1)
+       strerr_die4sys(111,FATAL,"unable to read ",name,": ");
+      substdio_fdbuf(&ssin,read,fdin,inbuf,sizeof(inbuf));
+
+      fdout = open_trunc(target.s);
+      if (fdout == -1)
+       strerr_die4sys(111,FATAL,"unable to write ",target.s,": ");
+      substdio_fdbuf(&ssout,write,fdout,outbuf,sizeof(outbuf));
+
+      switch(substdio_copy(&ssout,&ssin)) {
+       case -2:
+         strerr_die4sys(111,FATAL,"unable to read ",name,": ");
+       case -3:
+         strerr_die4sys(111,FATAL,"unable to write ",target.s,": ");
+      }
+
+      close(fdin);
+      if (substdio_flush(&ssout) == -1)
+       strerr_die4sys(111,FATAL,"unable to write ",target.s,": ");
+      if (fsync(fdout) == -1)
+       strerr_die4sys(111,FATAL,"unable to write ",target.s,": ");
+      close(fdout);
+      break;
+
+    default:
+      return;
+  }
+
+  if (chown(target.s,uid,gid) == -1)
+    strerr_die4sys(111,FATAL,"unable to chown ",target.s,": ");
+  if (chmod(target.s,mode) == -1)
+    strerr_die4sys(111,FATAL,"unable to chmod ",target.s,": ");
+}
+
+char buf[256];
+substdio in = SUBSTDIO_FDBUF(read,0,buf,sizeof(buf));
+stralloc line = {0};
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+  int match;
+
+  umask(077);
+
+  to = argv[1];
+  if (!to) strerr_die2x(100,FATAL,"install: usage: install dir");
+
+  for (;;) {
+    if (getln(&in,&line,&match,'\n') == -1)
+      strerr_die2sys(111,FATAL,"unable to read input: ");
+    doit(&line);
+    if (!match)
+      _exit(0);
+  }
+}
diff --git a/install=x b/install=x
new file mode 100644 (file)
index 0000000..6dc960b
--- /dev/null
+++ b/install=x
@@ -0,0 +1,9 @@
+getln.a
+strerr.a
+substdio.a
+stralloc.a
+alloc.a
+open.a
+error.a
+str.a
+fs.a
diff --git a/issub.c b/issub.c
new file mode 100644 (file)
index 0000000..81ab6ad
--- /dev/null
+++ b/issub.c
@@ -0,0 +1,87 @@
+#include "stralloc.h"
+#include "getln.h"
+#include "readwrite.h"
+#include "substdio.h"
+#include "open.h"
+#include "byte.h"
+#include "case.h"
+#include "lock.h"
+#include "error.h"
+#include "issub.h"
+#include "uint32.h"
+
+static stralloc addr = {0};
+static stralloc line = {0};
+static stralloc fn = {0};
+static int fd;
+static substdio ss;
+static char ssbuf[256];
+
+static int doit(userhost)
+char *userhost;
+{
+  int j;
+  uint32 h;
+  char ch;
+  int match;
+
+  if (!stralloc_copys(&addr,"T")) return -2;
+  if (!stralloc_cats(&addr,userhost)) return -2;
+
+  j = byte_rchr(addr.s,addr.len,'@');
+  if (j == addr.len) return 0;
+  case_lowerb(addr.s + j + 1,addr.len - j - 1);
+
+  h = 5381;
+  for (j = 0;j < addr.len;++j)
+    h = (h + (h << 5)) ^ (uint32) (unsigned char) addr.s[j];
+  ch = 64 + (h % 53);
+
+  if (!stralloc_0(&addr)) return -2;
+
+  if (!stralloc_copys(&fn,"subscribers/")) return -2;
+  if (!stralloc_catb(&fn,&ch,1)) return -2;
+  if (!stralloc_0(&fn)) return -2;
+
+  fd = open_read(fn.s);
+  if (fd == -1) {
+    if (errno != error_noent) return -3;
+    return 0;
+  }
+  substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
+
+  for (;;) {
+    if (getln(&ss,&line,&match,'\0') == -1) { close(fd); return -3; }
+    if (!match) break;
+    if (line.len == addr.len)
+      if (!byte_diff(line.s,line.len,addr.s)) { close(fd); return 1; }
+  }
+
+  close(fd);
+  return 0;
+}
+
+struct strerr issub_err;
+
+int issub(userhost)
+char *userhost;
+{
+  int fdlock;
+  int r;
+
+  fdlock = open_append("lock");
+  if (fdlock == -1)
+    STRERR_SYS(-1,issub_err,"unable to open lock: ")
+  if (lock_ex(fdlock) == -1) {
+    close(fdlock);
+    STRERR_SYS(-1,issub_err,"unable to obtain lock: ")
+  }
+
+  r = doit(userhost);
+  close(fdlock);
+
+  if (r == -2) STRERR(-1,issub_err,"out of memory")
+  if (r == -3) STRERR_SYS3(-1,issub_err,"unable to read ",fn.s,": ")
+
+  return r;
+}
diff --git a/issub.h b/issub.h
new file mode 100644 (file)
index 0000000..71fb379
--- /dev/null
+++ b/issub.h
@@ -0,0 +1,10 @@
+#ifndef ISSUB_H
+#define ISSUB_H
+
+#include "strerr.h"
+
+extern struct strerr issub_err;
+
+extern int issub();
+
+#endif
diff --git a/it.do b/it.do
new file mode 100644 (file)
index 0000000..667603f
--- /dev/null
+++ b/it.do
@@ -0,0 +1,4 @@
+dependon \
+ezmlm-make ezmlm-manage ezmlm-send ezmlm-reject \
+ezmlm-return ezmlm-warn ezmlm-weed \
+ezmlm-list ezmlm-sub ezmlm-unsub
diff --git a/lock.h b/lock.h
new file mode 100644 (file)
index 0000000..a7dee41
--- /dev/null
+++ b/lock.h
@@ -0,0 +1,8 @@
+#ifndef LOCK_H
+#define LOCK_H
+
+extern int lock_ex();
+extern int lock_un();
+extern int lock_exnb();
+
+#endif
diff --git a/lock=l b/lock=l
new file mode 100644 (file)
index 0000000..9927cef
--- /dev/null
+++ b/lock=l
@@ -0,0 +1 @@
+lock_ex.o
diff --git a/lock_ex.c b/lock_ex.c
new file mode 100644 (file)
index 0000000..a3351c9
--- /dev/null
+++ b/lock_ex.c
@@ -0,0 +1,11 @@
+#include <sys/types.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include "hasflock.h"
+#include "lock.h"
+
+#ifdef HASFLOCK
+int lock_ex(fd) int fd; { return flock(fd,LOCK_EX); }
+#else
+int lock_ex(fd) int fd; { return lockf(fd,1,0); }
+#endif
diff --git a/log.c b/log.c
new file mode 100644 (file)
index 0000000..15e732e
--- /dev/null
+++ b/log.c
@@ -0,0 +1,37 @@
+#include "substdio.h"
+#include "readwrite.h"
+#include "stralloc.h"
+#include "log.h"
+#include "now.h"
+#include "fmt.h"
+#include "open.h"
+
+static substdio ss;
+static char buf[1];
+static char num[FMT_ULONG];
+static stralloc line = {0};
+
+void log(event,addr)
+char *event;
+char *addr;
+{
+  char ch;
+  int fd;
+
+  if (!stralloc_copyb(&line,num,fmt_ulong(num,(unsigned long) now()))) return;
+  if (!stralloc_cats(&line," ")) return;
+  if (!stralloc_cats(&line,event)) return;
+  if (!stralloc_cats(&line," ")) return;
+  while (ch = *addr++) {
+    if ((ch < 33) || (ch > 126)) ch = '?';
+    if (!stralloc_append(&line,&ch)) return;
+  }
+  if (!stralloc_cats(&line,"\n")) return;
+
+  fd = open_append("Log");
+  if (fd == -1) return;
+  substdio_fdbuf(&ss,write,fd,buf,sizeof(buf));
+  substdio_putflush(&ss,line.s,line.len);
+  close(fd);
+  return;
+}
diff --git a/log.h b/log.h
new file mode 100644 (file)
index 0000000..d973be9
--- /dev/null
+++ b/log.h
@@ -0,0 +1,6 @@
+#ifndef LOG_H
+#define LOG_H
+
+extern void log();
+
+#endif
diff --git a/make-compile.sh b/make-compile.sh
new file mode 100644 (file)
index 0000000..a1eb501
--- /dev/null
@@ -0,0 +1 @@
+echo exec "$CC" -c '${1+"$@"}'
diff --git a/make-load.sh b/make-load.sh
new file mode 100644 (file)
index 0000000..de07d2e
--- /dev/null
@@ -0,0 +1,2 @@
+echo 'main="$1"; shift'
+echo exec "$LD" '-o "$main" "$main".o ${1+"$@"}'
diff --git a/make-makelib.sh b/make-makelib.sh
new file mode 100644 (file)
index 0000000..d6b7c8c
--- /dev/null
@@ -0,0 +1,16 @@
+echo 'main="$1"; shift'
+echo 'rm -f "$main"'
+echo 'ar cr "$main" ${1+"$@"}'
+
+case "$1" in
+sunos-5.*) ;;
+unix_sv*) ;;
+irix64-*) ;;
+irix-*) ;;
+dgux-*) ;;
+hp-ux-*) ;;
+sco*) ;;
+*)
+  echo 'ranlib "$main"'
+  ;;
+esac
diff --git a/man.do b/man.do
new file mode 100644 (file)
index 0000000..d9e3f48
--- /dev/null
+++ b/man.do
@@ -0,0 +1,31 @@
+dependon \
+ezmlm.0 \
+ezmlm-make.0 ezmlm-manage.0 ezmlm-send.0 ezmlm-reject.0 \
+ezmlm-return.0 ezmlm-warn.0 ezmlm-weed.0 \
+ezmlm-list.0 ezmlm-sub.0 ezmlm-unsub.0 \
+alloc.0 \
+case.0 \
+datetime.0 \
+direntry.0 \
+env.0 \
+error.0 \
+error_str.0 \
+error_temp.0 \
+ezmlm.0 \
+fd_copy.0 \
+fd_move.0 \
+getln.0 \
+getln2.0 \
+getopt.0 \
+now.0 \
+sgetopt.0 \
+stralloc.0 \
+subfd.0 \
+subgetopt.0 \
+substdio.0 \
+substdio_copy.0 \
+substdio_in.0 \
+substdio_out.0 \
+surf.0 \
+surfpcs.0 \
+wait.0
diff --git a/now.3 b/now.3
new file mode 100644 (file)
index 0000000..3d845b1
--- /dev/null
+++ b/now.3
@@ -0,0 +1,14 @@
+.TH now 3
+.SH NAME
+now \- get current time, in seconds
+.SH SYNTAX
+.B #include <now.h>
+
+datetime_sec \fBnow\fP();
+.SH DESCRIPTION
+.B now
+returns the number of real-time seconds that have elapsed
+since the end of 1969 TAI.
+.SH "SEE ALSO"
+datetime(3),
+time(3)
diff --git a/now.c b/now.c
new file mode 100644 (file)
index 0000000..5ce4d90
--- /dev/null
+++ b/now.c
@@ -0,0 +1,8 @@
+#include <time.h>
+#include "datetime.h"
+#include "now.h"
+
+datetime_sec now()
+{
+  return time((long *) 0);
+}
diff --git a/now.h b/now.h
new file mode 100644 (file)
index 0000000..b8be182
--- /dev/null
+++ b/now.h
@@ -0,0 +1,8 @@
+#ifndef NOW_H
+#define NOW_H
+
+#include "datetime.h"
+
+extern datetime_sec now();
+
+#endif
diff --git a/now=0 b/now=0
new file mode 100644 (file)
index 0000000..3f02d34
--- /dev/null
+++ b/now=0
@@ -0,0 +1 @@
+now.3
diff --git a/open.h b/open.h
new file mode 100644 (file)
index 0000000..c903113
--- /dev/null
+++ b/open.h
@@ -0,0 +1,10 @@
+#ifndef OPEN_H
+#define OPEN_H
+
+extern int open_read();
+extern int open_excl();
+extern int open_append();
+extern int open_trunc();
+extern int open_write();
+
+#endif
diff --git a/open=l b/open=l
new file mode 100644 (file)
index 0000000..9104cd6
--- /dev/null
+++ b/open=l
@@ -0,0 +1,3 @@
+open_append.o
+open_read.o
+open_trunc.o
diff --git a/open_append.c b/open_append.c
new file mode 100644 (file)
index 0000000..93a0862
--- /dev/null
@@ -0,0 +1,6 @@
+#include <sys/types.h>
+#include <fcntl.h>
+#include "open.h"
+
+int open_append(fn) char *fn;
+{ return open(fn,O_WRONLY | O_NDELAY | O_APPEND | O_CREAT,0600); }
diff --git a/open_read.c b/open_read.c
new file mode 100644 (file)
index 0000000..f503e48
--- /dev/null
@@ -0,0 +1,6 @@
+#include <sys/types.h>
+#include <fcntl.h>
+#include "open.h"
+
+int open_read(fn) char *fn;
+{ return open(fn,O_RDONLY | O_NDELAY); }
diff --git a/open_trunc.c b/open_trunc.c
new file mode 100644 (file)
index 0000000..e275085
--- /dev/null
@@ -0,0 +1,6 @@
+#include <sys/types.h>
+#include <fcntl.h>
+#include "open.h"
+
+int open_trunc(fn) char *fn;
+{ return open(fn,O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT,0644); }
diff --git a/qmail.c b/qmail.c
new file mode 100644 (file)
index 0000000..7484331
--- /dev/null
+++ b/qmail.c
@@ -0,0 +1,103 @@
+#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(120);
+      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);
+}
+
+int qmail_close(qq)
+struct qmail *qq;
+{
+  int wstat;
+
+  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 QMAIL_WAITPID;
+  if (wait_crashed(wstat)) return QMAIL_CRASHED;
+  switch(wait_exitcode(wstat)) {
+    case 0: if (qq->flagerr) return QMAIL_BUG; return 0;
+    case 112: return QMAIL_USAGE;
+    case 115: return QMAIL_TOOLONG;
+    case 103: case 104: case 105: case 106: case 108: return QMAIL_SYS;
+    case 121: return QMAIL_READ;
+    case 122: return QMAIL_WRITE;
+    case 123: return QMAIL_NOMEM;
+    case 124: return QMAIL_TIMEOUT;
+    case 120: return QMAIL_EXECSOFT;
+    default: /* 101 or 102 */ return QMAIL_BUG;
+  }
+}
diff --git a/qmail.h b/qmail.h
new file mode 100644 (file)
index 0000000..7864ea1
--- /dev/null
+++ b/qmail.h
@@ -0,0 +1,36 @@
+#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 int qmail_close();
+extern unsigned long qmail_qp();
+
+#define QMAIL_WAITPID -2
+#define QMAIL_CRASHED -3
+#define QMAIL_USAGE -4
+#define QMAIL_BUG -5
+#define QMAIL_SYS -6
+#define QMAIL_READ -7
+#define QMAIL_WRITE -8
+#define QMAIL_NOMEM -9
+#define QMAIL_EXECSOFT -11
+#define QMAIL_TIMEOUT -13
+#define QMAIL_TOOLONG -14
+
+#endif
diff --git a/quote.c b/quote.c
new file mode 100644 (file)
index 0000000..9f97d93
--- /dev/null
+++ b/quote.c
@@ -0,0 +1,82 @@
+#include "stralloc.h"
+#include "str.h"
+#include "quote.h"
+
+/*
+quote() encodes a box as per rfc 821 and rfc 822,
+while trying to do as little quoting as possible.
+no, 821 and 822 don't have the same encoding. they're not even close.
+no special encoding here for bytes above 127.
+*/
+
+static char ok[128] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+,0,7,0,7,7,7,7,7,0,0,7,7,0,7,7,7 ,7,7,7,7,7,7,7,7,7,7,0,0,0,7,0,7
+,0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 ,7,7,7,7,7,7,7,7,7,7,7,0,0,0,7,7
+,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 ,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0
+} ;
+
+static int doit(saout,sain)
+stralloc *saout;
+stralloc *sain;
+{
+ char ch;
+ int i;
+ int j;
+
+ if (!stralloc_ready(saout,sain->len * 2 + 2)) return 0;
+ j = 0;
+ saout->s[j++] = '"';
+ for (i = 0;i < sain->len;++i)
+  {
+   ch = sain->s[i];
+   if ((ch == '\r') || (ch == '\n') || (ch == '"') || (ch == '\\'))
+     saout->s[j++] = '\\';
+   saout->s[j++] = ch;
+  }
+ saout->s[j++] = '"';
+ saout->len = j;
+ return 1;
+}
+
+int quote_need(s,n)
+char *s;
+unsigned int n;
+{
+ unsigned char uch;
+ int i;
+ if (!n) return 0;
+ for (i = 0;i < n;++i)
+  {
+   uch = s[i];
+   if (uch >= 128) return 1;
+   if (!ok[uch]) return 1;
+  }
+ if (s[0] == '.') return 1;
+ if (s[n - 1] == '.') return 1;
+ for (i = 0;i < n - 1;++i) if (s[i] == '.') if (s[i + 1] == '.') return 1;
+ return 0;
+}
+
+int quote(saout,sain)
+stralloc *saout;
+stralloc *sain;
+{
+ if (quote_need(sain->s,sain->len)) return doit(saout,sain);
+ return stralloc_copy(saout,sain);
+}
+
+static stralloc foo = {0};
+
+int quote2(sa,s)
+stralloc *sa;
+char *s;
+{
+ int j;
+ j = str_rchr(s,'@');
+ if (!stralloc_copys(&foo,s)) return 0;
+ if (!s[j]) return quote(sa,&foo);
+ foo.len = j;
+ if (!quote(sa,&foo)) return 0;
+ return stralloc_cats(sa,s + j);
+}
diff --git a/quote.h b/quote.h
new file mode 100644 (file)
index 0000000..8ab7356
--- /dev/null
+++ b/quote.h
@@ -0,0 +1,8 @@
+#ifndef QUOTE_H
+#define QUOTE_H
+
+extern int quote_need();
+extern int quote();
+extern int quote2();
+
+#endif
diff --git a/readwrite.h b/readwrite.h
new file mode 100644 (file)
index 0000000..2a64968
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef READWRITE_H
+#define READWRITE_H
+
+extern int read();
+extern int write();
+
+#endif
diff --git a/scan.h b/scan.h
new file mode 100644 (file)
index 0000000..53ce703
--- /dev/null
+++ b/scan.h
@@ -0,0 +1,27 @@
+#ifndef SCAN_H
+#define SCAN_H
+
+extern unsigned int scan_uint();
+extern unsigned int scan_xint();
+extern unsigned int scan_nbbint();
+extern unsigned int scan_ushort();
+extern unsigned int scan_xshort();
+extern unsigned int scan_nbbshort();
+extern unsigned int scan_ulong();
+extern unsigned int scan_xlong();
+extern unsigned int scan_nbblong();
+
+extern unsigned int scan_plusminus();
+extern unsigned int scan_0x();
+
+extern unsigned int scan_whitenskip();
+extern unsigned int scan_nonwhitenskip();
+extern unsigned int scan_charsetnskip();
+extern unsigned int scan_noncharsetnskip();
+
+extern unsigned int scan_strncmp();
+extern unsigned int scan_memcmp();
+
+extern unsigned int scan_long();
+
+#endif
diff --git a/scan_8long.c b/scan_8long.c
new file mode 100644 (file)
index 0000000..8b3a6df
--- /dev/null
@@ -0,0 +1,11 @@
+#include "scan.h"
+
+unsigned int scan_8long(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')) < 8)
+    { result = result * 8 + c; ++pos; }
+  *u = result; return pos;
+}
diff --git a/scan_ulong.c b/scan_ulong.c
new file mode 100644 (file)
index 0000000..27c41ea
--- /dev/null
@@ -0,0 +1,11 @@
+#include "scan.h"
+
+unsigned int scan_ulong(s,u) register char *s; register unsigned long *u;
+{
+  register unsigned int pos; register unsigned long result;
+  register unsigned long c;
+  pos = 0; result = 0;
+  while ((c = (unsigned long) (unsigned char) (s[pos] - '0')) < 10)
+    { result = result * 10 + c; ++pos; }
+  *u = result; return pos;
+}
diff --git a/seek.h b/seek.h
new file mode 100644 (file)
index 0000000..964fba3
--- /dev/null
+++ b/seek.h
@@ -0,0 +1,15 @@
+#ifndef SEEK_H
+#define SEEK_H
+
+typedef unsigned long seek_pos;
+
+extern seek_pos seek_cur();
+
+extern int seek_set();
+extern int seek_end();
+
+extern int seek_trunc();
+
+#define seek_begin(fd) (seek_set((fd),(seek_pos) 0))
+
+#endif
diff --git a/seek=l b/seek=l
new file mode 100644 (file)
index 0000000..8f34e13
--- /dev/null
+++ b/seek=l
@@ -0,0 +1 @@
+seek_set.o
diff --git a/seek_set.c b/seek_set.c
new file mode 100644 (file)
index 0000000..f540664
--- /dev/null
@@ -0,0 +1,7 @@
+#include <sys/types.h>
+#include "seek.h"
+
+#define SET 0 /* sigh */
+
+int seek_set(fd,pos) int fd; seek_pos pos;
+{ if (lseek(fd,(off_t) pos,SET) == -1) return -1; return 0; }
diff --git a/setup.do b/setup.do
new file mode 100644 (file)
index 0000000..d667234
--- /dev/null
+++ b/setup.do
@@ -0,0 +1,5 @@
+dependon it man install conf-bin conf-man BIN MAN
+formake './install "`head -1 conf-bin`" < BIN'
+formake './install "`head -1 conf-man`" < MAN'
+./install "`head -1 conf-bin`" < BIN
+./install "`head -1 conf-man`" < MAN
diff --git a/sgetopt.3 b/sgetopt.3
new file mode 100644 (file)
index 0000000..bde0c2b
--- /dev/null
+++ b/sgetopt.3
@@ -0,0 +1,28 @@
+.TH sgetopt 3
+.SH NAME
+sgetopt \- get option character from command line
+.SH SYNTAX
+.B #include <sgetopt.h>
+.SH DESCRIPTION
+The
+.B sgetopt
+library is just like the
+.B getopt
+library,
+except that it prints errors using
+.B substdio
+rather than
+.BR stdio .
+
+See
+.B getopt(3)
+for interface details.
+.SH VERSION
+sgetopt version 1.9, 931201.
+.SH AUTHOR
+Placed into the public domain by Daniel J. Bernstein.
+.SH "SEE ALSO"
+getopt(3),
+subgetopt(3),
+subfd(3),
+substdio(3)
diff --git a/sgetopt.c b/sgetopt.c
new file mode 100644 (file)
index 0000000..a8bffc0
--- /dev/null
+++ b/sgetopt.c
@@ -0,0 +1,54 @@
+/* sgetopt.c, sgetopt.h: (yet another) improved getopt clone, outer layer
+D. J. Bernstein, djb@pobox.com.
+Depends on subgetopt.h, substdio.h, subfd.h.
+No system requirements.
+19970208: Cleanups.
+931201: Baseline.
+No known patent problems.
+
+Documentation in sgetopt.3.
+*/
+
+#include "substdio.h"
+#include "subfd.h"
+#define SGETOPTNOSHORT
+#include "sgetopt.h"
+#define SUBGETOPTNOSHORT
+#include "subgetopt.h"
+
+#define getopt sgetoptmine
+#define optind subgetoptind
+#define opterr sgetopterr
+#define optproblem subgetoptproblem
+#define optprogname sgetoptprogname
+
+int opterr = 1;
+char *optprogname = 0;
+
+int getopt(argc,argv,opts)
+int argc;
+char **argv;
+char *opts;
+{
+  int c;
+  char *s;
+
+  if (!optprogname) {
+    optprogname = *argv;
+    if (!optprogname) optprogname = "";
+    for (s = optprogname;*s;++s) if (*s == '/') optprogname = s + 1;
+  }
+  c = subgetopt(argc,argv,opts);
+  if (opterr)
+    if (c == '?') {
+      char chp[2]; chp[0] = optproblem; chp[1] = '\n';
+      substdio_puts(subfderr,optprogname);
+      if (argv[optind] && (optind < argc))
+        substdio_puts(subfderr,": illegal option -- ");
+      else
+        substdio_puts(subfderr,": option requires an argument -- ");
+      substdio_put(subfderr,chp,2);
+      substdio_flush(subfderr);
+    }
+  return c;
+}
diff --git a/sgetopt.h b/sgetopt.h
new file mode 100644 (file)
index 0000000..5f89127
--- /dev/null
+++ b/sgetopt.h
@@ -0,0 +1,21 @@
+#ifndef SGETOPT_H
+#define SGETOPT_H
+
+#ifndef SGETOPTNOSHORT
+#define getopt sgetoptmine
+#define optarg subgetoptarg
+#define optind subgetoptind
+#define optpos subgetoptpos
+#define opterr sgetopterr
+#define optproblem subgetoptproblem
+#define optprogname sgetoptprogname
+#define opteof subgetoptdone
+#endif
+
+#include "subgetopt.h"
+
+extern int sgetoptmine();
+extern int sgetopterr;
+extern char *sgetoptprogname;
+
+#endif
diff --git a/sgetopt=0 b/sgetopt=0
new file mode 100644 (file)
index 0000000..7291e07
--- /dev/null
+++ b/sgetopt=0
@@ -0,0 +1 @@
+sgetopt.3
diff --git a/sig.h b/sig.h
new file mode 100644 (file)
index 0000000..9c3a28c
--- /dev/null
+++ b/sig.h
@@ -0,0 +1,43 @@
+#ifndef SIG_H
+#define SIG_H
+
+extern void sig_catch();
+extern void sig_block();
+extern void sig_unblock();
+extern void sig_blocknone();
+extern void sig_pause();
+
+extern void sig_dfl();
+
+extern void sig_miscignore();
+extern void sig_bugcatch();
+
+extern void sig_pipeignore();
+extern void sig_pipedefault();
+
+extern void sig_contblock();
+extern void sig_contunblock();
+extern void sig_contcatch();
+extern void sig_contdefault();
+
+extern void sig_termblock();
+extern void sig_termunblock();
+extern void sig_termcatch();
+extern void sig_termdefault();
+
+extern void sig_alarmblock();
+extern void sig_alarmunblock();
+extern void sig_alarmcatch();
+extern void sig_alarmdefault();
+
+extern void sig_childblock();
+extern void sig_childunblock();
+extern void sig_childcatch();
+extern void sig_childdefault();
+
+extern void sig_hangupblock();
+extern void sig_hangupunblock();
+extern void sig_hangupcatch();
+extern void sig_hangupdefault();
+
+#endif
diff --git a/sig=l b/sig=l
new file mode 100644 (file)
index 0000000..a5456da
--- /dev/null
+++ b/sig=l
@@ -0,0 +1,2 @@
+sig_catch.o
+sig_pipe.o
diff --git a/sig_catch.c b/sig_catch.c
new file mode 100644 (file)
index 0000000..1888765
--- /dev/null
@@ -0,0 +1,18 @@
+#include <signal.h>
+#include "sig.h"
+#include "hassgact.h"
+
+void sig_catch(sig,f)
+int sig;
+void (*f)();
+{
+#ifdef HASSIGACTION
+  struct sigaction sa;
+  sa.sa_handler = f;
+  sa.sa_flags = 0;
+  sigemptyset(&sa.sa_mask);
+  sigaction(sig,&sa,(struct sigaction *) 0);
+#else
+  signal(sig,f); /* won't work under System V, even nowadays---dorks */
+#endif
+}
diff --git a/sig_pipe.c b/sig_pipe.c
new file mode 100644 (file)
index 0000000..594ae7d
--- /dev/null
@@ -0,0 +1,5 @@
+#include <signal.h>
+#include "sig.h"
+
+void sig_pipeignore() { sig_catch(SIGPIPE,SIG_IGN); }
+void sig_pipedefault() { sig_catch(SIGPIPE,SIG_DFL); }
diff --git a/slurp.c b/slurp.c
new file mode 100644 (file)
index 0000000..7d50194
--- /dev/null
+++ b/slurp.c
@@ -0,0 +1,19 @@
+#include "stralloc.h"
+#include "slurp.h"
+#include "error.h"
+#include "open.h"
+
+int slurp(fn,sa,bufsize)
+char *fn;
+stralloc *sa;
+int bufsize;
+{
+  int fd;
+  fd = open_read(fn);
+  if (fd == -1) {
+    if (errno == error_noent) return 0;
+    return -1;
+  }
+  if (slurpclose(fd,sa,bufsize) == -1) return -1;
+  return 1;
+}
diff --git a/slurp.h b/slurp.h
new file mode 100644 (file)
index 0000000..16aa656
--- /dev/null
+++ b/slurp.h
@@ -0,0 +1,6 @@
+#ifndef SLURP_H
+#define SLURP_H
+
+extern int slurp();
+
+#endif
diff --git a/slurpclose.c b/slurpclose.c
new file mode 100644 (file)
index 0000000..2fcef15
--- /dev/null
@@ -0,0 +1,19 @@
+#include "stralloc.h"
+#include "readwrite.h"
+#include "slurpclose.h"
+#include "error.h"
+
+int slurpclose(fd,sa,bufsize)
+int fd;
+stralloc *sa;
+int bufsize;
+{
+  int r;
+  for (;;) {
+    if (!stralloc_readyplus(sa,bufsize)) { close(fd); return -1; }
+    r = read(fd,sa->s + sa->len,bufsize);
+    if (r == -1) if (errno == error_intr) continue;
+    if (r <= 0) { close(fd); return r; }
+    sa->len += r;
+  }
+}
diff --git a/slurpclose.h b/slurpclose.h
new file mode 100644 (file)
index 0000000..57e9eec
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef SLURPCLOSE_H
+#define SLURPCLOSE_H
+
+extern int slurpclose();
+
+#endif
diff --git a/str.h b/str.h
new file mode 100644 (file)
index 0000000..e00773c
--- /dev/null
+++ b/str.h
@@ -0,0 +1,14 @@
+#ifndef STR_H
+#define STR_H
+
+extern unsigned int str_copy();
+extern int str_diff();
+extern int str_diffn();
+extern unsigned int str_len();
+extern unsigned int str_chr();
+extern unsigned int str_rchr();
+extern int str_start();
+
+#define str_equal(s,t) (!str_diff((s),(t)))
+
+#endif
diff --git a/str=l b/str=l
new file mode 100644 (file)
index 0000000..e520d2c
--- /dev/null
+++ b/str=l
@@ -0,0 +1,13 @@
+str_len.o
+str_diff.o
+str_diffn.o
+str_cpy.o
+str_chr.o
+str_rchr.o
+str_start.o
+byte_chr.o
+byte_rchr.o
+byte_diff.o
+byte_copy.o
+byte_cr.o
+byte_zero.o
diff --git a/str_chr.c b/str_chr.c
new file mode 100644 (file)
index 0000000..3691826
--- /dev/null
+++ b/str_chr.c
@@ -0,0 +1,19 @@
+#include "str.h"
+
+unsigned int str_chr(s,c)
+register char *s;
+int c;
+{
+  register char ch;
+  register char *t;
+
+  ch = c;
+  t = s;
+  for (;;) {
+    if (!*t) break; if (*t == ch) break; ++t;
+    if (!*t) break; if (*t == ch) break; ++t;
+    if (!*t) break; if (*t == ch) break; ++t;
+    if (!*t) break; if (*t == ch) break; ++t;
+  }
+  return t - s;
+}
diff --git a/str_cpy.c b/str_cpy.c
new file mode 100644 (file)
index 0000000..453d790
--- /dev/null
+++ b/str_cpy.c
@@ -0,0 +1,16 @@
+#include "str.h"
+
+unsigned int str_copy(s,t)
+register char *s;
+register char *t;
+{
+  register int len;
+
+  len = 0;
+  for (;;) {
+    if (!(*s = *t)) return len; ++s; ++t; ++len;
+    if (!(*s = *t)) return len; ++s; ++t; ++len;
+    if (!(*s = *t)) return len; ++s; ++t; ++len;
+    if (!(*s = *t)) return len; ++s; ++t; ++len;
+  }
+}
diff --git a/str_diff.c b/str_diff.c
new file mode 100644 (file)
index 0000000..18f8927
--- /dev/null
@@ -0,0 +1,17 @@
+#include "str.h"
+
+int str_diff(s,t)
+register char *s;
+register char *t;
+{
+  register char x;
+
+  for (;;) {
+    x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+    x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+    x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+    x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+  }
+  return ((int)(unsigned int)(unsigned char) x)
+       - ((int)(unsigned int)(unsigned char) *t);
+}
diff --git a/str_diffn.c b/str_diffn.c
new file mode 100644 (file)
index 0000000..89142f1
--- /dev/null
@@ -0,0 +1,18 @@
+#include "str.h"
+
+int str_diffn(s,t,len)
+register char *s;
+register char *t;
+unsigned int len;
+{
+  register char x;
+
+  for (;;) {
+    if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+    if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+    if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+    if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+  }
+  return ((int)(unsigned int)(unsigned char) x)
+       - ((int)(unsigned int)(unsigned char) *t);
+}
diff --git a/str_len.c b/str_len.c
new file mode 100644 (file)
index 0000000..2d2f88b
--- /dev/null
+++ b/str_len.c
@@ -0,0 +1,15 @@
+#include "str.h"
+
+unsigned int str_len(s)
+register char *s;
+{
+  register char *t;
+
+  t = s;
+  for (;;) {
+    if (!*t) return t - s; ++t;
+    if (!*t) return t - s; ++t;
+    if (!*t) return t - s; ++t;
+    if (!*t) return t - s; ++t;
+  }
+}
diff --git a/str_rchr.c b/str_rchr.c
new file mode 100644 (file)
index 0000000..1bf19d3
--- /dev/null
@@ -0,0 +1,22 @@
+#include "str.h"
+
+unsigned int str_rchr(s,c)
+register char *s;
+int c;
+{
+  register char ch;
+  register char *t;
+  register char *u;
+
+  ch = c;
+  t = s;
+  u = 0;
+  for (;;) {
+    if (!*t) break; if (*t == ch) u = t; ++t;
+    if (!*t) break; if (*t == ch) u = t; ++t;
+    if (!*t) break; if (*t == ch) u = t; ++t;
+    if (!*t) break; if (*t == ch) u = t; ++t;
+  }
+  if (!u) u = t;
+  return u - s;
+}
diff --git a/str_start.c b/str_start.c
new file mode 100644 (file)
index 0000000..2750af8
--- /dev/null
@@ -0,0 +1,15 @@
+#include "str.h"
+
+int str_start(s,t)
+register char *s;
+register char *t;
+{
+  register char x;
+
+  for (;;) {
+    x = *t++; if (!x) return 1; if (x != *s++) return 0;
+    x = *t++; if (!x) return 1; if (x != *s++) return 0;
+    x = *t++; if (!x) return 1; if (x != *s++) return 0;
+    x = *t++; if (!x) return 1; if (x != *s++) return 0;
+  }
+}
diff --git a/stralloc.3 b/stralloc.3
new file mode 100644 (file)
index 0000000..3123521
--- /dev/null
@@ -0,0 +1,160 @@
+.TH stralloc 3
+.SH NAME
+stralloc \- dynamically allocated strings
+.SH SYNTAX
+.B #include <stralloc.h>
+
+int \fBstralloc_ready\fP(&\fIsa\fR,\fIlen\fR);
+.br
+int \fBstralloc_readyplus\fP(&\fIsa\fR,\fIlen\fR);
+
+int \fBstralloc_copy\fP(&\fIsa\fR,&\fIsa2\fR);
+.br
+int \fBstralloc_copys\fP(&\fIsa\fR,\fIbuf\fR);
+.br
+int \fBstralloc_copyb\fP(&\fIsa\fR,\fIbuf\fR,\fIlen\fR);
+
+int \fBstralloc_cat\fP(&\fIsa\fR,&\fIsa2\fR);
+.br
+int \fBstralloc_cats\fP(&\fIsa\fR,\fIbuf\fR);
+.br
+int \fBstralloc_catb\fP(&\fIsa\fR,\fIbuf\fR,\fIlen\fR);
+
+int \fBstralloc_append\fP(&\fIsa\fR,\fIbuf\fR);
+.br
+int \fBstralloc_0\fP(&\fIsa\fR);
+
+int \fBstralloc_starts\fP(&\fIsa\fR,\fIbuf\fR);
+
+stralloc \fIsa\fR = {0};
+.br
+stralloc \fIsa2\fR = {0};
+.br
+unsigned int \fIlen\fR;
+.br
+char *\fIbuf\fR;
+.SH DESCRIPTION
+A
+.B stralloc
+variable holds a string in dynamically allocated space.
+String length is limited only by memory.
+String contents are unrestricted.
+
+The
+.B stralloc
+structure has three components:
+.I sa\fB.s
+is a pointer to the string, or 0 if it is not allocated;
+.I sa\fB.len
+is the number of bytes in the string, if it is allocated;
+.I sa\fB.a
+is the number of bytes allocated for the string, if it is allocated.
+A
+.B stralloc
+variable should be initialized to {0},
+meaning unallocated.
+
+.B stralloc_ready
+makes sure that
+.I sa
+has enough space allocated for
+.I len
+characters.
+It allocates extra space if necessary.
+
+.B stralloc_readyplus
+makes sure that
+.I sa
+has enough space allocated for
+.I len
+characters more than its current length.
+If
+.I sa
+is unallocated,
+.B stralloc_readyplus
+is the same as
+.BR stralloc_ready .
+
+.B stralloc_copy
+copies
+.I sa2
+to
+.IR sa ,
+allocating space if necessary.
+Here
+.I sa2
+is an allocated
+.B stralloc
+variable.
+
+.B stralloc_copys
+copies a 0-terminated string,
+.IR buf ,
+to
+.IR sa ,
+without the 0.
+
+.B stralloc_copyb
+copies
+.I len
+characters from
+.I buf
+to
+.IR sa .
+
+.B stralloc_cat
+appends
+.I sa2
+to
+.IR sa ,
+allocating space if necessary.
+If
+.I sa
+is unallocated,
+.B stralloc_cat
+is the same as
+.BR stralloc_copy .
+
+.B stralloc_cats
+and
+.B stralloc_catb
+are analogous to
+.B stralloc_copys
+and
+.BR stralloc_copyb .
+
+.B stralloc_append
+adds a single character,
+.IR *buf ,
+to
+.IR sa ,
+allocating space if necessary.
+
+.B stralloc_0
+adds a single 0 character
+to
+.IR sa .
+
+.B stralloc_starts
+returns 1 if the 0-terminated string
+.IR buf ,
+without the 0,
+is a prefix of
+.IR sa .
+.SH "ERROR HANDLING"
+If a
+.B stralloc
+routine runs out of memory,
+it leaves
+.I sa
+alone and returns 0,
+setting
+.B errno
+appropriately.
+On success it returns 1;
+this guarantees that
+.I sa
+is allocated.
+.SH "SEE ALSO"
+alloc(3),
+error(3)
diff --git a/stralloc.h b/stralloc.h
new file mode 100644 (file)
index 0000000..fca496c
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef STRALLOC_H
+#define STRALLOC_H
+
+#include "gen_alloc.h"
+
+GEN_ALLOC_typedef(stralloc,char,s,len,a)
+
+extern int stralloc_ready();
+extern int stralloc_readyplus();
+extern int stralloc_copy();
+extern int stralloc_cat();
+extern int stralloc_copys();
+extern int stralloc_cats();
+extern int stralloc_copyb();
+extern int stralloc_catb();
+extern int stralloc_append(); /* beware: this takes a pointer to 1 char */
+extern int stralloc_starts();
+
+#define stralloc_0(sa) stralloc_append(sa,"")
+
+#endif
diff --git a/stralloc=0 b/stralloc=0
new file mode 100644 (file)
index 0000000..12889aa
--- /dev/null
@@ -0,0 +1 @@
+stralloc.3
diff --git a/stralloc=l b/stralloc=l
new file mode 100644 (file)
index 0000000..6aac39b
--- /dev/null
@@ -0,0 +1,9 @@
+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
diff --git a/stralloc_arts.c b/stralloc_arts.c
new file mode 100644 (file)
index 0000000..1ccb5a4
--- /dev/null
@@ -0,0 +1,12 @@
+#include "byte.h"
+#include "str.h"
+#include "stralloc.h"
+
+int stralloc_starts(sa,s)
+stralloc *sa;
+char *s;
+{
+  int len;
+  len = str_len(s);
+  return (sa->len >= len) && byte_equal(s,len,sa->s);
+}
diff --git a/stralloc_cat.c b/stralloc_cat.c
new file mode 100644 (file)
index 0000000..efbb112
--- /dev/null
@@ -0,0 +1,9 @@
+#include "byte.h"
+#include "stralloc.h"
+
+int stralloc_cat(sato,safrom)
+stralloc *sato;
+stralloc *safrom;
+{
+  return stralloc_catb(sato,safrom->s,safrom->len);
+}
diff --git a/stralloc_catb.c b/stralloc_catb.c
new file mode 100644 (file)
index 0000000..67dbcc0
--- /dev/null
@@ -0,0 +1,15 @@
+#include "stralloc.h"
+#include "byte.h"
+
+int stralloc_catb(sa,s,n)
+stralloc *sa;
+char *s;
+unsigned int n;
+{
+  if (!sa->s) return stralloc_copyb(sa,s,n);
+  if (!stralloc_readyplus(sa,n + 1)) return 0;
+  byte_copy(sa->s + sa->len,n,s);
+  sa->len += n;
+  sa->s[sa->len] = 'Z'; /* ``offensive programming'' */
+  return 1;
+}
diff --git a/stralloc_cats.c b/stralloc_cats.c
new file mode 100644 (file)
index 0000000..d300286
--- /dev/null
@@ -0,0 +1,10 @@
+#include "byte.h"
+#include "str.h"
+#include "stralloc.h"
+
+int stralloc_cats(sa,s)
+stralloc *sa;
+char *s;
+{
+  return stralloc_catb(sa,s,str_len(s));
+}
diff --git a/stralloc_copy.c b/stralloc_copy.c
new file mode 100644 (file)
index 0000000..652aed6
--- /dev/null
@@ -0,0 +1,9 @@
+#include "byte.h"
+#include "stralloc.h"
+
+int stralloc_copy(sato,safrom)
+stralloc *sato;
+stralloc *safrom;
+{
+  return stralloc_copyb(sato,safrom->s,safrom->len);
+}
diff --git a/stralloc_eady.c b/stralloc_eady.c
new file mode 100644 (file)
index 0000000..3a31f4b
--- /dev/null
@@ -0,0 +1,6 @@
+#include "alloc.h"
+#include "stralloc.h"
+#include "gen_allocdefs.h"
+
+GEN_ALLOC_ready(stralloc,char,s,len,a,i,n,x,30,stralloc_ready)
+GEN_ALLOC_readyplus(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus)
diff --git a/stralloc_opyb.c b/stralloc_opyb.c
new file mode 100644 (file)
index 0000000..ac258b3
--- /dev/null
@@ -0,0 +1,14 @@
+#include "stralloc.h"
+#include "byte.h"
+
+int stralloc_copyb(sa,s,n)
+stralloc *sa;
+char *s;
+unsigned int n;
+{
+  if (!stralloc_ready(sa,n + 1)) return 0;
+  byte_copy(sa->s,n,s);
+  sa->len = n;
+  sa->s[n] = 'Z'; /* ``offensive programming'' */
+  return 1;
+}
diff --git a/stralloc_opys.c b/stralloc_opys.c
new file mode 100644 (file)
index 0000000..fdd7807
--- /dev/null
@@ -0,0 +1,10 @@
+#include "byte.h"
+#include "str.h"
+#include "stralloc.h"
+
+int stralloc_copys(sa,s)
+stralloc *sa;
+char *s;
+{
+  return stralloc_copyb(sa,s,str_len(s));
+}
diff --git a/stralloc_pend.c b/stralloc_pend.c
new file mode 100644 (file)
index 0000000..a3443b8
--- /dev/null
@@ -0,0 +1,5 @@
+#include "alloc.h"
+#include "stralloc.h"
+#include "gen_allocdefs.h"
+
+GEN_ALLOC_append(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus,stralloc_append)
diff --git a/strerr.c b/strerr.c
new file mode 100644 (file)
index 0000000..a6bc00c
--- /dev/null
+++ b/strerr.c
@@ -0,0 +1,22 @@
+#include "stralloc.h"
+#include "strerr.h"
+
+static stralloc sa = {0};
+
+char *strerr(se)
+struct strerr *se;
+{
+  strerr_sysinit();
+  if (!stralloc_copys(&sa,"")) return "out of memory";
+  while(se) {
+    if (se->x) if (!stralloc_cats(&sa,se->x)) return "out of memory";
+    if (se->y) if (!stralloc_cats(&sa,se->y)) return "out of memory";
+    if (se->z) if (!stralloc_cats(&sa,se->z)) return "out of memory";
+    se = se->who;
+  }
+  if (!stralloc_0(&sa)) return "out of memory";
+  return sa.s;
+}
diff --git a/strerr.h b/strerr.h
new file mode 100644 (file)
index 0000000..d18e833
--- /dev/null
+++ b/strerr.h
@@ -0,0 +1,80 @@
+#ifndef STRERR_H
+#define STRERR_H
+
+struct strerr
+ {
+  struct strerr *who;
+  char *x;
+  char *y;
+  char *z;
+ }
+;
+
+extern struct strerr strerr_sys;
+extern void strerr_sysinit();
+
+extern char *strerr();
+extern void strerr_warn();
+extern void strerr_die();
+
+#define STRERR(r,se,a) \
+{ se.who = 0; se.x = a; se.y = 0; se.z = 0; return r; }
+
+#define STRERR_SYS(r,se,a) \
+{ se.who = &strerr_sys; se.x = a; se.y = 0; se.z = 0; return r; }
+#define STRERR_SYS3(r,se,a,b,c) \
+{ se.who = &strerr_sys; se.x = a; se.y = b; se.z = c; return r; }
+
+#define strerr_warn6(x1,x2,x3,x4,x5,x6,se) \
+strerr_warn((x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) (se))
+#define strerr_warn5(x1,x2,x3,x4,x5,se) \
+strerr_warn((x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) (se))
+#define strerr_warn4(x1,x2,x3,x4,se) \
+strerr_warn((x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_warn3(x1,x2,x3,se) \
+strerr_warn((x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_warn2(x1,x2,se) \
+strerr_warn((x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_warn1(x1,se) \
+strerr_warn((x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+
+#define strerr_die6(e,x1,x2,x3,x4,x5,x6,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) (se))
+#define strerr_die5(e,x1,x2,x3,x4,x5,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) (se))
+#define strerr_die4(e,x1,x2,x3,x4,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_die3(e,x1,x2,x3,se) \
+strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_die2(e,x1,x2,se) \
+strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_die1(e,x1,se) \
+strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+
+#define strerr_die6sys(e,x1,x2,x3,x4,x5,x6) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),&strerr_sys)
+#define strerr_die5sys(e,x1,x2,x3,x4,x5) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,&strerr_sys)
+#define strerr_die4sys(e,x1,x2,x3,x4) \
+strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,&strerr_sys)
+#define strerr_die3sys(e,x1,x2,x3) \
+strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
+#define strerr_die2sys(e,x1,x2) \
+strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
+#define strerr_die1sys(e,x1) \
+strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
+
+#define strerr_die6x(e,x1,x2,x3,x4,x5,x6) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) 0)
+#define strerr_die5x(e,x1,x2,x3,x4,x5) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) 0)
+#define strerr_die4x(e,x1,x2,x3,x4) \
+strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) 0)
+#define strerr_die3x(e,x1,x2,x3) \
+strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
+#define strerr_die2x(e,x1,x2) \
+strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
+#define strerr_die1x(e,x1) \
+strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
+
+#endif
diff --git a/strerr=l b/strerr=l
new file mode 100644 (file)
index 0000000..f3580a1
--- /dev/null
+++ b/strerr=l
@@ -0,0 +1,3 @@
+strerr.o
+strerr_sys.o
+strerr_die.o
diff --git a/strerr_die.c b/strerr_die.c
new file mode 100644 (file)
index 0000000..6092020
--- /dev/null
@@ -0,0 +1,37 @@
+#include "substdio.h"
+#include "subfd.h"
+#include "exit.h"
+#include "strerr.h"
+
+void strerr_warn(x1,x2,x3,x4,x5,x6,se)
+char *x1; char *x2; char *x3; char *x4; char *x5; char *x6;
+struct strerr *se;
+{
+  strerr_sysinit();
+  if (x1) substdio_puts(subfderr,x1);
+  if (x2) substdio_puts(subfderr,x2);
+  if (x3) substdio_puts(subfderr,x3);
+  if (x4) substdio_puts(subfderr,x4);
+  if (x5) substdio_puts(subfderr,x5);
+  if (x6) substdio_puts(subfderr,x6);
+  while(se) {
+    if (se->x) substdio_puts(subfderr,se->x);
+    if (se->y) substdio_puts(subfderr,se->y);
+    if (se->z) substdio_puts(subfderr,se->z);
+    se = se->who;
+  }
+  substdio_puts(subfderr,"\n");
+  substdio_flush(subfderr);
+}
+
+void strerr_die(e,x1,x2,x3,x4,x5,x6,se)
+int e;
+char *x1; char *x2; char *x3; char *x4; char *x5; char *x6;
+struct strerr *se;
+{
+  strerr_warn(x1,x2,x3,x4,x5,x6,se);
+  _exit(e);
+}
diff --git a/strerr_sys.c b/strerr_sys.c
new file mode 100644 (file)
index 0000000..198198b
--- /dev/null
@@ -0,0 +1,12 @@
+#include "error.h"
+#include "strerr.h"
+
+struct strerr strerr_sys;
+
+void strerr_sysinit()
+{
+  strerr_sys.who = 0;
+  strerr_sys.x = error_str(errno);
+  strerr_sys.y = "";
+  strerr_sys.z = "";
+}
diff --git a/subfd.3 b/subfd.3
new file mode 100644 (file)
index 0000000..6859e64
--- /dev/null
+++ b/subfd.3
@@ -0,0 +1,61 @@
+.TH subfd 3
+.SH NAME
+subfd \- standard input, output, error for substdio
+.SH SYNTAX
+.B #include <subfd.h>
+
+substdio *\fBsubfdin\fP;
+.br
+substdio *\fBsubfdout\fP;
+.br
+substdio *\fBsubfderr\fP;
+
+int \fBsubfd_read\fP(\fIfd\fR,\fIbuf\fR,\fIlen\fR);
+
+substdio *\fBsubfdinsmall\fP;
+.br
+substdio *\fBsubfdoutsmall\fP;
+
+int \fBsubfd_readsmall\fP(\fIfd\fR,\fIbuf\fR,\fIlen\fR);
+
+int \fIfd\fR;
+.br
+char *\fIbuf\fR;
+.br
+int \fIlen\fR;
+.SH DESCRIPTION
+.B subfderr
+writes data to descriptor 2.
+
+.B subfdout
+writes data to descriptor 1.
+
+.B subfdin
+reads data from descriptor 0.
+It coordinates with
+.BR subfdout :
+it flushes
+.B subfdout
+before refilling its buffer.
+This eliminates the need for
+.B subfdout
+flushing in most programs.
+To set up the same flushing mechanism for another input descriptor,
+use
+.B subfd_read
+in place of
+.BR read .
+
+.BR subfdoutsmall ,
+.BR subfd_readsmall ,
+and
+.B subfdinsmall
+behave the same way as
+.BR subfdout ,
+.BR subfd_read ,
+and
+.BR subfdin ,
+except that they use small (256-byte) buffers.
+This is appropriate for programs that read data in small chunks.
+.SH "SEE ALSO"
+substdio(3)
diff --git a/subfd.h b/subfd.h
new file mode 100644 (file)
index 0000000..bcb2e1e
--- /dev/null
+++ b/subfd.h
@@ -0,0 +1,15 @@
+#ifndef SUBFD_H
+#define SUBFD_H
+
+#include "substdio.h"
+
+extern substdio *subfdin;
+extern substdio *subfdinsmall;
+extern substdio *subfdout;
+extern substdio *subfdoutsmall;
+extern substdio *subfderr;
+
+extern int subfd_read();
+extern int subfd_readsmall();
+
+#endif
diff --git a/subfd=0 b/subfd=0
new file mode 100644 (file)
index 0000000..9e10976
--- /dev/null
+++ b/subfd=0
@@ -0,0 +1 @@
+subfd.3
diff --git a/subfderr.c b/subfderr.c
new file mode 100644 (file)
index 0000000..011ab0f
--- /dev/null
@@ -0,0 +1,7 @@
+#include "readwrite.h"
+#include "substdio.h"
+#include "subfd.h"
+
+char subfd_errbuf[256];
+static substdio it = SUBSTDIO_FDBUF(write,2,subfd_errbuf,256);
+substdio *subfderr = &it;
diff --git a/subgetopt.3 b/subgetopt.3
new file mode 100644 (file)
index 0000000..aae03aa
--- /dev/null
@@ -0,0 +1,357 @@
+.TH subgetopt 3
+.SH NAME
+subgetopt \- get option character from command line
+.SH SYNTAX
+.B #include <subgetopt.h>
+
+char *\fBsgoptarg\fP;
+.br
+int \fBsgoptind\fP;
+.br
+int \fBsgoptpos\fP;
+.br
+int \fBsgoptdone\fP;
+.br
+int \fBsgoptproblem\fP;
+
+int \fBsgopt(\fP\fIargc,argv,opts\fR\fB)\fP;
+
+int \fIargc\fR;
+.br
+char **\fIargv\fR;
+.br
+char *\fIopts\fR;
+.SH DESCRIPTION
+.B sgopt
+returns the next valid command-line option character
+from
+.IR argv .
+
+Valid option characters are listed in the
+.I opts
+string.
+.I opts
+may be empty.
+A character in
+.I opts
+may be followed by a colon,
+in which case it
+takes an
+.I option argument\fR.
+Avoid using the characters ?, :, and \- as option characters.
+
+Below
+.I option argument
+is abbreviated
+as
+.I optarg
+and
+.I command-line argument
+is abbreviated as
+.IR cmdarg .
+
+Options are listed in cmdargs which begin with
+a minus sign.
+Several options which do not take optargs may be combined
+into one cmdarg.
+
+An option which takes an optarg may be handled in two ways.
+If it appears at the very end of a cmdarg,
+then the entire next cmdarg is the optarg.
+But if there are any characters in the cmdarg
+after the option character,
+then those characters form the optarg.
+The optarg is returned in
+.BR sgoptarg .
+Next time
+.B sgopt
+looks at the cmdarg which follows the optarg.
+
+If a cmdarg does not begin with a hyphen,
+or if it is a lone hyphen not followed by any characters,
+or if it begins with two hyphens,
+then it terminates option processing,
+and
+.B sgopt
+returns an appropriate code.
+If there are two hyphens,
+.B sgopt
+will advance attention to the next cmdarg,
+so it can be called again to read further options.
+.SH "PROPER USAGE"
+.B sgoptproblem
+should be used only when
+.B sgopt
+returns ?.
+.B sgoptind
+and
+.B sgoptpos
+are defined all the time.
+.B sgoptarg
+is defined all the time;
+it is null unless
+.B sgopt
+has just returned an option with optarg.
+
+.B sgopt
+is typically used as follows.
+
+.EX
+#include <subgetopt.h>
+
+main(argc,argv) int argc; char **argv; { int opt;
+
+while ((opt = sgopt(argc,argv,"a:s")) != sgoptdone)
+.br
+  switch(opt) {
+.br
+    case 'a':
+.br
+      printf("opt a with optarg %s\\n",sgoptarg); break;
+.br
+    case 's':
+.br
+      printf("opt s with no optarg\\n"); break;
+.br
+    case '?':
+.br
+      if (argv[sgoptind] && (sgoptind < argc))
+.br
+        printf("illegal opt %c\\n",sgoptproblem);
+.br
+      else
+.br
+        printf("missing arg, opt %c\\n",sgoptproblem);
+.br
+      exit(1);
+.br
+  }
+
+argv += sgoptind;
+.br
+while (*argv) printf("argument %s\\n",*argv++);
+.br
+exit(0);
+.br
+}
+.EE
+
+The end of the command line is
+marked by either
+.IR argc ,
+or a null pointer in
+.IR argv ,
+whichever comes first.
+Normally
+these two markers coincide,
+so it is redundant
+to test for
+both
+.I argv\fB[sgoptind]
+and
+.B sgoptind < \fIargc\fR.
+The above code shows both tests as an illustration.
+
+.B Multiple option sets:
+One useful technique is to call
+.B sgopt
+with a primary
+.I opts
+until it returns EOF,
+then call
+.B sgopt
+with a secondary
+.I opts
+until it returns EOF.
+The user can provide primary options, then a double hyphen,
+and then secondary options.
+No special handling is needed if some or all of the options are
+omitted.
+The same technique can be used for any number of option sets
+in series.
+
+.B Multiple command lines:
+Before parsing a new
+.BR argv ,
+make sure to
+set
+.B sgoptind
+and
+.B sgoptpos
+back to
+1 and 0.
+.SH "PARSING STAGES"
+.B sgopt
+keeps track of its position in
+.I argv
+with
+.B sgoptind
+and
+.BR sgoptpos ,
+which are initialized to 1 and 0.
+It looks at
+.I argv\fB[sgoptind][sgoptpos]
+and following characters.
+
+.B sgopt
+indicates
+that no more options are available by
+returning
+.BR sgoptdone ,
+which is initialized to
+.BR SUBGETOPTDONE ,
+which is defined as \-1.
+
+.B sgopt
+begins by setting
+.B optarg
+to null.
+
+.B Ending conditions:
+If
+.I argv
+is null, or
+.B sgoptind
+is larger than
+.IR argc ,
+or the current cmdarg
+.I argv\fB[sgoptind]
+is null,
+then
+.B sgopt
+returns
+.BR optdone .
+
+.B Stage one:
+If the current character
+is zero,
+.B sgopt
+moves to the beginning of the next cmdarg.
+It then checks the ending conditions again.
+
+.B Stage two:
+If
+the current position is the begining of the cmdarg,
+.B sgopt
+checks whether
+the current character
+is a minus sign.
+If not it returns
+.BR optdone .
+It then
+moves
+to the next character.
+If that character is zero,
+.B sgopt
+moves
+back to the beginning of the cmdarg,
+and returns
+.BR sgoptdone .
+If the character is a minus sign,
+.B sgopt
+moves to the beginning of the next cmdarg,
+and returns
+.BR sgoptdone .
+
+.B Stage three:
+.B sgopt
+records the current character,
+.IR c ,
+and moves to the next character.
+There are three possibilities:
+(1)
+.I c
+is an option character without optarg in
+.IR opts ,
+or
+(2)
+.I c
+is an option character with optarg in
+.IR opts ,
+or
+(3)
+.I c
+does not appear in
+.IR opts .
+
+(1)
+If
+.I c
+appears as an option character without optarg in
+.IR opts ,
+.B sgopt
+returns
+.IR c .
+
+(2)
+If
+.I c
+appears as an option character with optarg in
+.IR opts ,
+.B sgopt
+sets
+.B sgoptarg
+to the current position,
+and moves to the next cmdarg.
+If
+.B sgoptarg
+is nonempty,
+.B sgopt
+returns
+.IR c .
+
+Then
+.B sgopt
+sets
+.B sgoptarg
+to
+the current cmdarg.
+If
+the current cmdarg is null,
+or past
+.IR argc ,
+.B sgopt
+sets
+.B sgoptproblem
+to
+.I c
+and returns ?.
+Otherwise
+.B sgopt
+moves to the next
+argument
+and returns
+.IR c .
+
+(2)
+If
+.I c
+does not appear in
+.IR opts ,
+.B sgopt
+sets
+.B sgoptproblem
+to
+.I c
+and returns ?.
+.SH "SYNTAX NOTE"
+.B sgopt
+is actually a macro abbreviation for
+.BR subgetopt .
+The external
+.B sg
+variables are also macros
+for
+.BR subget .
+These macros are defined in
+.BR <subgetopt.h> ,
+unless
+.B SUBGETOPTNOSHORT
+is defined
+when
+.B <subgetopt.h>
+is included.
+.SH VERSION
+subgetopt version 0.9, 931129.
+.SH AUTHOR
+Placed into the public domain by Daniel J. Bernstein.
diff --git a/subgetopt.c b/subgetopt.c
new file mode 100644 (file)
index 0000000..dacf376
--- /dev/null
@@ -0,0 +1,79 @@
+/* subgetopt.c, subgetopt.h: (yet another) improved getopt clone, inner layer
+D. J. Bernstein, djb@pobox.com.
+No dependencies.
+No system requirements.
+19970228: Cleanups.
+931129: Adapted from getopt.c.
+No known patent problems.
+
+Documentation in subgetopt.3.
+*/
+
+#define SUBGETOPTNOSHORT
+#include "subgetopt.h"
+
+#define sgopt subgetopt
+#define optind subgetoptind
+#define optpos subgetoptpos
+#define optarg subgetoptarg
+#define optproblem subgetoptproblem
+#define optdone subgetoptdone
+
+int optind = 1;
+int optpos = 0;
+char *optarg = 0;
+int optproblem = 0;
+int optdone = SUBGETOPTDONE;
+
+int sgopt(argc,argv,opts)
+int argc;
+char **argv;
+char *opts;
+{
+  int c;
+  char *s;
+
+  optarg = 0;
+  if (!argv || (optind >= argc) || !argv[optind]) return optdone;
+  if (optpos && !argv[optind][optpos]) {
+    ++optind;
+    optpos = 0;
+    if ((optind >= argc) || !argv[optind]) return optdone;
+  }
+  if (!optpos) {
+    if (argv[optind][0] != '-') return optdone;
+    ++optpos;
+    c = argv[optind][1];
+    if ((c == '-') || (c == 0)) {
+      if (c) ++optind;
+      optpos = 0;
+      return optdone;
+    }
+    /* otherwise c is reassigned below */
+  }
+  c = argv[optind][optpos];
+  ++optpos;
+  s = opts;
+  while (*s) {
+    if (c == *s) {
+      if (s[1] == ':') {
+        optarg = argv[optind] + optpos;
+        ++optind;
+        optpos = 0;
+        if (!*optarg) {
+          optarg = argv[optind];
+          if ((optind >= argc) || !optarg) { /* argument past end */
+            optproblem = c;
+            return '?';
+          }
+         ++optind;
+        }
+      }
+      return c;
+    }
+    ++s;
+    if (*s == ':') ++s;
+  }
+  optproblem = c;
+  return '?';
+}
diff --git a/subgetopt.h b/subgetopt.h
new file mode 100644 (file)
index 0000000..d26c62a
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef SUBGETOPT_H
+#define SUBGETOPT_H
+
+#ifndef SUBGETOPTNOSHORT
+#define sgopt subgetopt
+#define sgoptarg subgetoptarg
+#define sgoptind subgetoptind
+#define sgoptpos subgetoptpos
+#define sgoptproblem subgetoptproblem
+#define sgoptprogname subgetoptprogname
+#define sgoptdone subgetoptdone
+#endif
+
+#define SUBGETOPTDONE -1
+
+extern int subgetopt();
+extern char *subgetoptarg;
+extern int subgetoptind;
+extern int subgetoptpos;
+extern int subgetoptproblem;
+extern char *subgetoptprogname;
+extern int subgetoptdone;
+
+#endif
diff --git a/subgetopt=0 b/subgetopt=0
new file mode 100644 (file)
index 0000000..38ed342
--- /dev/null
@@ -0,0 +1 @@
+subgetopt.3
diff --git a/subscribe.c b/subscribe.c
new file mode 100644 (file)
index 0000000..fee21e2
--- /dev/null
@@ -0,0 +1,127 @@
+#include "stralloc.h"
+#include "getln.h"
+#include "readwrite.h"
+#include "substdio.h"
+#include "strerr.h"
+#include "open.h"
+#include "byte.h"
+#include "case.h"
+#include "lock.h"
+#include "error.h"
+#include "uint32.h"
+#include "subscribe.h"
+
+static stralloc addr = {0};
+static stralloc line = {0};
+static stralloc fnnew = {0};
+static stralloc fn = {0};
+
+static int fd;
+static substdio ss;
+static char ssbuf[256];
+static int fdnew;
+static substdio ssnew;
+static char ssnewbuf[256];
+
+static int doit(userhost,flagadd)
+char *userhost;
+int flagadd;
+{
+  int j;
+  uint32 h;
+  char ch;
+  int match;
+  int flagwasthere;
+
+  if (userhost[str_chr(userhost,'\n')]) return -8;
+  if (!stralloc_copys(&addr,"T")) return -2;
+  if (!stralloc_cats(&addr,userhost)) return -2;
+  if (addr.len > 401) return -7;
+
+  j = byte_rchr(addr.s,addr.len,'@');
+  if (j == addr.len) return -6;
+  case_lowerb(addr.s + j + 1,addr.len - j - 1);
+
+  h = 5381;
+  for (j = 0;j < addr.len;++j)
+    h = (h + (h << 5)) ^ (uint32) (unsigned char) addr.s[j];
+  ch = 64 + (h % 53);
+
+  if (!stralloc_0(&addr)) return -2;
+
+  if (!stralloc_copys(&fn,"subscribers/")) return -2;
+  if (!stralloc_catb(&fn,&ch,1)) return -2;
+  if (!stralloc_copy(&fnnew,&fn)) return -2;
+  if (!stralloc_cats(&fnnew,"n")) return -2;
+  if (!stralloc_0(&fnnew)) return -2;
+  if (!stralloc_0(&fn)) return -2;
+
+  fdnew = open_trunc(fnnew.s);
+  if (fdnew == -1) return -4;
+  substdio_fdbuf(&ssnew,write,fdnew,ssnewbuf,sizeof(ssnewbuf));
+
+  flagwasthere = 0;
+
+  fd = open_read(fn.s);
+  if (fd == -1) {
+    if (errno != error_noent) { close(fdnew); return -3; }
+  }
+  else {
+    substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
+
+    for (;;) {
+      if (getln(&ss,&line,&match,'\0') == -1) { close(fd); close(fdnew); return -3; }
+      if (!match) break;
+      if (line.len == addr.len)
+        if (!byte_diff(line.s,line.len,addr.s)) {
+         flagwasthere = 1;
+         if (!flagadd)
+           continue;
+       }
+      if (substdio_bput(&ssnew,line.s,line.len) == -1) { close(fd); close(fdnew); return -4; }
+    }
+
+    close(fd);
+  }
+
+  if (flagadd && !flagwasthere)
+    if (substdio_bput(&ssnew,addr.s,addr.len) == -1) { close(fdnew); return -4; }
+
+  if (substdio_flush(&ssnew) == -1) { close(fdnew); return -4; }
+  if (fsync(fdnew) == -1) { close(fdnew); return -4; }
+  close(fdnew);
+
+  if (rename(fnnew.s,fn.s) == -1) return -5;
+  return flagadd ^ flagwasthere;
+}
+
+struct strerr subscribe_err;
+
+int subscribe(userhost,flagadd)
+char *userhost;
+int flagadd;
+{
+  int fdlock;
+  int r;
+
+  fdlock = open_append("lock");
+  if (fdlock == -1)
+    STRERR_SYS(-1,subscribe_err,"unable to open lock: ")
+  if (lock_ex(fdlock) == -1) {
+    close(fdlock);
+    STRERR_SYS(-1,subscribe_err,"unable to obtain lock: ")
+  }
+
+  r = doit(userhost,flagadd);
+  close(fdlock);
+
+  if (r == -2) STRERR(-1,subscribe_err,"out of memory")
+  if (r == -3) STRERR_SYS3(-1,subscribe_err,"unable to read ",fn.s,": ")
+  if (r == -4) STRERR_SYS3(-1,subscribe_err,"unable to write ",fnnew.s,": ")
+  if (r == -5) STRERR_SYS3(-1,subscribe_err,"unable to move temporary file to ",fn.s,": ")
+  if (r == -6) STRERR(-2,subscribe_err,"address does not contain @")
+  if (r == -7) STRERR(-2,subscribe_err,"address is too long")
+  if (r == -8) STRERR(-2,subscribe_err,"address contains newline")
+
+  return r;
+}
diff --git a/subscribe.h b/subscribe.h
new file mode 100644 (file)
index 0000000..48a2f1c
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef SUBSCRIBE_H
+#define SUBSCRIBE_H
+
+#include "strerr.h"
+
+extern struct strerr subscribe_err;
+
+extern int subscribe();
+
+#endif
diff --git a/substdi.c b/substdi.c
new file mode 100644 (file)
index 0000000..42407a1
--- /dev/null
+++ b/substdi.c
@@ -0,0 +1,91 @@
+#include "substdio.h"
+#include "byte.h"
+#include "error.h"
+
+static int oneread(op,fd,buf,len)
+register int (*op)();
+register int fd;
+register char *buf;
+register int len;
+{
+  register int r;
+
+  for (;;) {
+    r = op(fd,buf,len);
+    if (r == -1) if (errno == error_intr) continue;
+    return r;
+  }
+}
+
+static int getthis(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+  register int r;
+  register int q;
+  r = s->p;
+  q = r - len;
+  if (q > 0) { r = len; s->p = q; } else s->p = 0;
+  byte_copy(buf,r,s->x + s->n);
+  s->n += r;
+  return r;
+}
+
+int substdio_feed(s)
+register substdio *s;
+{
+  register int r;
+  register int q;
+
+  if (s->p) return s->p;
+  q = s->n;
+  r = oneread(s->op,s->fd,s->x,q);
+  if (r <= 0) return r;
+  s->p = r;
+  q -= r;
+  s->n = q;
+  if (q > 0) /* damn, gotta shift */ byte_copyr(s->x + q,r,s->x);
+  return r;
+}
+
+int substdio_bget(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+  register int r;
+  if (s->p > 0) return getthis(s,buf,len);
+  r = s->n; if (r <= len) return oneread(s->op,s->fd,buf,r);
+  r = substdio_feed(s); if (r <= 0) return r;
+  return getthis(s,buf,len);
+}
+
+int substdio_get(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+  register int r;
+  if (s->p > 0) return getthis(s,buf,len);
+  if (s->n <= len) return oneread(s->op,s->fd,buf,len);
+  r = substdio_feed(s); if (r <= 0) return r;
+  return getthis(s,buf,len);
+}
+
+char *substdio_peek(s)
+register substdio *s;
+{
+  return s->x + s->n;
+}
+
+void substdio_seek(s,len)
+register substdio *s;
+register int len;
+{
+  s->n += len;
+  s->p -= len;
+}
diff --git a/substdio.3 b/substdio.3
new file mode 100644 (file)
index 0000000..2bb459d
--- /dev/null
@@ -0,0 +1,115 @@
+.TH substdio 3
+.SH NAME
+substdio \- the Sub-Standard Input/Output Library
+.SH SYNTAX
+.B #include <substdio.h>
+
+void \fBsubstdio_fdbuf\fP(&\fIs\fR,\fIop\fR,\fIfd\fR,\fIbuf\fR,\fIlen\fR);
+
+int \fBsubstdio_fileno\fP(&\fIs\fR);
+
+substdio \fIs\fR;
+.br
+int (*\fIop\fR)();
+.br
+int \fIfd\fR;
+.br
+char *\fIbuf\fR;
+.br
+int \fIlen\fR;
+.SH DESCRIPTION
+.B substdio
+is the Sub-Standard I/O Library.
+.B substdio
+contains only a few of the features of stdio;
+it is a fast, lightweight, low-level library,
+suitable for use as a component of higher-level I/O libraries.
+
+The point of
+.B substdio
+is to provide buffered I/O.
+The basic object in
+.B substdio
+is the
+.B substdio
+structure;
+a
+.B substdio
+variable stores
+an operation,
+a descriptor,
+and
+a pointer into a buffer of some nonzero length.
+The
+.B substdio
+operations read data from the buffer,
+filling the buffer as necessary using the operation on the descriptor,
+or write data to the buffer,
+flushing the buffer as necessary using the operation on the descriptor.
+Input and output operations cannot be mixed.
+
+.B substdio_fdbuf
+initializes a
+.B substdio
+variable.
+The operation is
+.IR op .
+The descriptor is
+.IR fd .
+The buffer is
+.IR buf ,
+an array of
+.I len
+chars.
+
+.I op
+will be called as
+.I op\fR(\fIfd\fR,\fIx\fR,\fIn\fR).
+Here
+.I x
+is a pointer to an array of characters of length
+.IR n ;
+.I op
+must read some characters from
+.I fd
+to that array, or write some characters to
+.I fd
+from that array.
+The return value from
+.I op
+must be the number of characters read or written.
+0 characters read means end of input;
+0 characters written means that the write operation
+should be tried again immediately.
+On error,
+.I op
+must return -1,
+setting
+.B errno
+appropriately, without reading or writing anything.
+Most errors are returned directly to the
+.B substdio
+caller, but an error of
+.B error_intr
+means that the operation should be tried again immediately.
+
+There is a
+.B SUBSTDIO_FDBUF
+macro that can be used to statically initialize a
+.B substdio
+variable:
+
+.EX
+   substdio s = SUBSTDIO_FDBUF(op,fd,buf,len);
+.EE
+
+.B substdio_fileno
+returns the
+descriptor for an initialized
+.B substdio
+variable.
+.SH "SEE ALSO"
+substdio_in(3),
+substdio_out(3),
+substdio_copy(3),
+error(3)
diff --git a/substdio.c b/substdio.c
new file mode 100644 (file)
index 0000000..d03dff2
--- /dev/null
@@ -0,0 +1,15 @@
+#include "substdio.h"
+
+void substdio_fdbuf(s,op,fd,buf,len)
+register substdio *s;
+register int (*op)();
+register int fd;
+register char *buf;
+register int len;
+{
+  s->x = buf;
+  s->fd = fd;
+  s->op = op;
+  s->p = 0;
+  s->n = len;
+}
diff --git a/substdio.h b/substdio.h
new file mode 100644 (file)
index 0000000..c3f7f7d
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef SUBSTDIO_H
+#define SUBSTDIO_H
+
+typedef struct substdio {
+  char *x;
+  int p;
+  int n;
+  int fd;
+  int (*op)();
+} substdio;
+
+#define SUBSTDIO_FDBUF(op,fd,buf,len) { (buf), 0, (len), (fd), (op) }
+
+extern void substdio_fdbuf();
+
+extern int substdio_flush();
+extern int substdio_put();
+extern int substdio_bput();
+extern int substdio_putflush();
+extern int substdio_puts();
+extern int substdio_bputs();
+extern int substdio_putsflush();
+
+extern int substdio_get();
+extern int substdio_bget();
+extern int substdio_feed();
+
+extern char *substdio_peek();
+extern void substdio_seek();
+
+#define substdio_fileno(s) ((s)->fd)
+
+#define SUBSTDIO_INSIZE 8192
+#define SUBSTDIO_OUTSIZE 8192
+
+#define substdio_PEEK(s) ( (s)->x + (s)->n )
+#define substdio_SEEK(s,len) ( ( (s)->p -= (len) ) , ( (s)->n += (len) ) )
+
+#define substdio_BPUTC(s,c) \
+  ( ((s)->n != (s)->p) \
+    ? ( (s)->x[(s)->p++] = (c), 0 ) \
+    : substdio_bput((s),&(c),1) \
+  )
+
+extern int substdio_copy();
+
+#endif
diff --git a/substdio=0 b/substdio=0
new file mode 100644 (file)
index 0000000..1da81e2
--- /dev/null
@@ -0,0 +1 @@
+substdio.3
diff --git a/substdio=l b/substdio=l
new file mode 100644 (file)
index 0000000..074aa76
--- /dev/null
@@ -0,0 +1,5 @@
+substdio.o
+substdi.o
+substdo.o
+subfderr.o
+substdio_copy.o
diff --git a/substdio_copy.3 b/substdio_copy.3
new file mode 100644 (file)
index 0000000..6fc02e6
--- /dev/null
@@ -0,0 +1,41 @@
+.TH substdio_copy 3
+.SH NAME
+substdio_copy \- copy an entire input to an output
+.SH SYNTAX
+.B #include <substdio.h>
+
+int \fBsubstdio_copy\fP(&\fIsout\fR,&\fIsin\fR);
+
+substdio \fIsout\fR;
+.br
+substdio \fIsin\fR;
+.SH DESCRIPTION
+.B substdio_copy
+reads characters from
+.I sin
+until end of file,
+writing each character to
+.IR sout .
+It then returns 0.
+It does not flush
+.IR sout .
+
+Upon a
+.I sin
+error,
+.B substdio_copy
+returns -2,
+leaving
+.B errno
+set appropriately.
+
+Upon a
+.I sout
+error,
+.B substdio_copy
+returns -3,
+leaving
+.B errno
+set appropriately.
+.SH "SEE ALSO"
+substdio(3)
diff --git a/substdio_copy.c b/substdio_copy.c
new file mode 100644 (file)
index 0000000..71cf200
--- /dev/null
@@ -0,0 +1,18 @@
+#include "substdio.h"
+
+int substdio_copy(ssout,ssin)
+register substdio *ssout;
+register substdio *ssin;
+{
+  register int n;
+  register char *x;
+
+  for (;;) {
+    n = substdio_feed(ssin);
+    if (n < 0) return -2;
+    if (!n) return 0;
+    x = substdio_PEEK(ssin);
+    if (substdio_put(ssout,x,n) == -1) return -3;
+    substdio_SEEK(ssin,n);
+  }
+}
diff --git a/substdio_copy=0 b/substdio_copy=0
new file mode 100644 (file)
index 0000000..1af3455
--- /dev/null
@@ -0,0 +1 @@
+substdio_copy.3
diff --git a/substdio_in.3 b/substdio_in.3
new file mode 100644 (file)
index 0000000..f81d7a8
--- /dev/null
@@ -0,0 +1,140 @@
+.TH substdio_in 3
+.SH NAME
+substdio_in \- substdio input routines
+.SH SYNTAX
+.B #include <substdio.h>
+
+int \fBsubstdio_get\fP(&\fIs\fR,\fIto\fR,\fIlen\fR);
+
+int \fBsubstdio_bget\fP(&\fIs\fR,\fIto\fR,\fIlen\fR);
+
+int \fBsubstdio_feed\fP(&\fIs\fR);
+
+char *\fBsubstdio_peek\fP(&\fIs\fR);
+
+void \fBsubstdio_seek\fP(&\fIs\fR,\fIlen\fR);
+
+substdio \fIs\fR;
+.br
+char *\fIto\fR;
+.br
+int \fIlen\fR;
+.SH DESCRIPTION
+.B substdio_get
+reads at most
+.I len
+characters from
+.I s
+into the character array
+.IR to .
+It returns the number of characters read,
+0 for end of file,
+or -1 for error,
+setting
+.B errno
+appropriately.
+
+.B substdio_bget
+has the same function as
+.BR substdio_get .
+The difference is what happens when there is no buffered data and
+.I len
+exceeds the buffer size:
+.B substdio_get
+tries to read
+.I len
+characters, whereas
+.B substdio_bget
+tries to read one buffer of characters.
+In some cases
+.B substdio_bget
+will be more efficient than
+.BR substdio_get .
+
+.B substdio_feed
+makes sure that there is buffered data,
+so that the next
+.B substdio_get
+or
+.B substdio_bget
+will succeed.
+If the buffer is empty,
+.B substdio_feed
+tries to fill it;
+it returns 0 for end of file, -1 for error,
+or the number of buffered characters on success.
+If the buffer already had data,
+.B substdio_feed
+leaves it alone and returns the number of buffered characters.
+
+.B substdio_peek
+returns a pointer to the buffered data.
+
+.B substdio_seek
+throws away
+.I len
+buffered characters,
+as if they had been read.
+.I len
+must be at least 0 and at most the amount of buffered data.
+
+The
+.B substdio_PEEK
+and
+.B substdio_SEEK
+macros behave the same way as
+.B substdio_peek
+and
+.B substdio_seek
+but may evaluate their arguments several times.
+
+The point of
+.B substdio_peek
+and
+.B substdio_seek
+is to read data without unnecessary copies.
+Sample code:
+
+.EX
+  for (;;) {
+.br
+    n = substdio_feed(s);
+.br
+    if (n <= 0) return n;
+.br
+    x = substdio_PEEK(s);
+.br
+    handle(x,n);
+.br
+    substdio_SEEK(s,n);
+.br
+  }
+.EE
+
+The
+.B SUBSTDIO_INSIZE
+macro is defined as a reasonably large input buffer size for
+.BR substdio_fdbuf .
+.SH "INTERNALS"
+When a
+.B substdio
+variable
+.I s
+is used for input,
+there is free buffer space from
+.I s\fB.x
+to
+.I s\fB.x\fR +
+.I s\fB.n\fR;
+data is buffered from
+.I s\fB.x\fR +
+.I s\fB.n
+to
+.I s\fB.x\fR +
+.I s\fB.n\fR +
+.I s\fB.p\fR;
+the total buffer length is
+.I s\fB.n\fR +
+.I s\fB.p\fR.
+.SH "SEE ALSO"
+substdio(3)
diff --git a/substdio_in=0 b/substdio_in=0
new file mode 100644 (file)
index 0000000..93b4f71
--- /dev/null
@@ -0,0 +1 @@
+substdio_in.3
diff --git a/substdio_out.3 b/substdio_out.3
new file mode 100644 (file)
index 0000000..9371c9d
--- /dev/null
@@ -0,0 +1,99 @@
+.TH substdio_out 3
+.SH NAME
+substdio_out \- substdio output routines
+.SH SYNTAX
+.B #include <substdio.h>
+
+int \fBsubstdio_put\fP(&\fIs\fR,\fIfrom\fR,\fIlen\fR);
+.br
+int \fBsubstdio_puts\fP(&\fIs\fR,\fIfrom\fR);
+
+int \fBsubstdio_bput\fP(&\fIs\fR,\fIfrom\fR,\fIlen\fR);
+.br
+int \fBsubstdio_bputs\fP(&\fIs\fR,\fIfrom\fR);
+
+int \fBsubstdio_flush\fP(&\fIs\fR);
+
+int \fBsubstdio_putflush\fP(&\fIs\fR,\fIfrom\fR,\fIlen\fR);
+.br
+int \fBsubstdio_putsflush\fP(&\fIs\fR,\fIfrom\fR);
+
+substdio \fIs\fR;
+.br
+char *\fIfrom\fR;
+.br
+int \fIlen\fR;
+.SH DESCRIPTION
+.B substdio_put
+writes
+.I len
+characters to
+.I s
+out of the character array
+.IR from .
+It returns 0 on success, -1 on error.
+
+.B substdio_bput
+has the same function as
+.BR substdio_put .
+The difference is how the buffer is flushed
+when there isn't enough room for
+.I len
+characters:
+.B substdio_put
+flushes the buffered data before copying the new data,
+whereas
+.B substdio_bput
+fills the buffer with new data before flushing.
+
+.B substdio_flush
+forces all data to be written from the internal buffer.
+It returns 0 on success, -1 on error.
+
+.B substdio_putflush
+is similar to
+.B substdio_put
+followed by
+.BR substdio_flush ,
+but it avoids all internal copies.
+
+.BR substdio_puts ,
+.BR substdio_bputs ,
+and
+.B substdio_putsflush
+are the same as
+.BR substdio_put ,
+.BR substdio_bput ,
+and
+.B substdio_putflush
+except that
+.I from
+must be a 0-terminated string of characters.
+The string, not including the 0, is written.
+
+The
+.B SUBSTDIO_OUTSIZE
+macro is defined as a reasonably large output buffer size for
+.BR substdio_fdbuf .
+.SH "INTERNALS"
+When a
+.B substdio
+variable
+.I s
+is used for output,
+data is buffered from
+.I s\fB.x
+to
+.I s\fB.x\fR +
+.I s\fB.p\fR;
+there is free buffer space from
+to
+.I s\fB.x\fR +
+.I s\fB.p
+to
+.I s\fB.x\fR +
+.I s\fB.n\fR;
+the total buffer length is
+.I s\fB.n\fR.
+.SH "SEE ALSO"
+substdio(3)
diff --git a/substdio_out=0 b/substdio_out=0
new file mode 100644 (file)
index 0000000..4124e68
--- /dev/null
@@ -0,0 +1 @@
+substdio_out.3
diff --git a/substdo.c b/substdo.c
new file mode 100644 (file)
index 0000000..fb616f7
--- /dev/null
+++ b/substdo.c
@@ -0,0 +1,108 @@
+#include "substdio.h"
+#include "str.h"
+#include "byte.h"
+#include "error.h"
+
+static int allwrite(op,fd,buf,len)
+register int (*op)();
+register int fd;
+register char *buf;
+register int len;
+{
+  register int w;
+
+  while (len) {
+    w = op(fd,buf,len);
+    if (w == -1) {
+      if (errno == error_intr) continue;
+      return -1; /* note that some data may have been written */
+    }
+    if (w == 0) ; /* luser's fault */
+    buf += w;
+    len -= w;
+  }
+  return 0;
+}
+
+int substdio_flush(s)
+register substdio *s;
+{
+  register int p;
+  p = s->p;
+  if (!p) return 0;
+  s->p = 0;
+  return allwrite(s->op,s->fd,s->x,p);
+}
+
+int substdio_bput(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+  register int n;
+  while (len > (n = s->n - s->p)) {
+    byte_copy(s->x + s->p,n,buf); s->p += n; buf += n; len -= n;
+    if (substdio_flush(s) == -1) return -1;
+  }
+  /* now len <= s->n - s->p */
+  byte_copy(s->x + s->p,len,buf);
+  s->p += len;
+  return 0;
+}
+
+int substdio_put(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+  register int n;
+  n = s->n;
+  if (len > n - s->p) {
+    if (substdio_flush(s) == -1) return -1;
+    /* now s->p == 0 */
+    if (n < SUBSTDIO_OUTSIZE) n = SUBSTDIO_OUTSIZE;
+    while (len > s->n) {
+      if (n > len) n = len;
+      if (allwrite(s->op,s->fd,buf,n) == -1) return -1;
+      buf += n;
+      len -= n;
+    }
+  }
+  /* now len <= s->n - s->p */
+  byte_copy(s->x + s->p,len,buf);
+  s->p += len;
+  return 0;
+}
+
+int substdio_putflush(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+  if (substdio_flush(s) == -1) return -1;
+  return allwrite(s->op,s->fd,buf,len);
+}
+
+int substdio_bputs(s,buf)
+register substdio *s;
+register char *buf;
+{
+  return substdio_bput(s,buf,str_len(buf));
+}
+
+int substdio_puts(s,buf)
+register substdio *s;
+register char *buf;
+{
+  return substdio_put(s,buf,str_len(buf));
+}
+
+int substdio_putsflush(s,buf)
+register substdio *s;
+register char *buf;
+{
+  return substdio_putflush(s,buf,str_len(buf));
+}
diff --git a/surf.3 b/surf.3
new file mode 100644 (file)
index 0000000..ba8a520
--- /dev/null
+++ b/surf.3
@@ -0,0 +1,23 @@
+.TH surf 3
+.SH NAME
+surf \- Simple Unpredictable Random Function
+.SH SYNTAX
+.B #include <surf.h>
+
+void \fBsurf\fP(\fIout\fR,\fIin\fR,\fIseed\fR);
+
+uint32 \fIout\fR[8];
+.br
+uint32 \fIin\fR[12];
+.br
+uint32 \fIseed\fR[32];
+.SH DESCRIPTION
+.B surf
+changes a 384-bit input to a 256-bit output,
+under control of a 1024-bit seed.
+When the seed is secret,
+.B surf
+appears to be indistinguishable from a completely random
+384-bit-to-256-bit function.
+.SH "SEE ALSO"
+surfpcs(3)
diff --git a/surf.c b/surf.c
new file mode 100644 (file)
index 0000000..c4a17d0
--- /dev/null
+++ b/surf.c
@@ -0,0 +1,27 @@
+/* 19970320, overlap allowed 19970406 */
+
+#include "surf.h"
+#include "uint32.h"
+
+#define ROTATE(x,b) (((x) << (b)) | ((x) >> (32 - (b))))
+#define MUSH(i,b) x = t[i] += (((x ^ seed[i]) + sum) ^ ROTATE(x,b));
+
+void surf(out,in,seed)
+uint32 out[8]; uint32 in[12]; uint32 seed[32];
+{
+  uint32 t[12]; uint32 x; uint32 sum = 0;
+  int r; int i; int loop;
+
+  for (i = 0;i < 12;++i) t[i] = in[i] ^ seed[12 + i];
+  for (i = 0;i < 8;++i) out[i] = seed[24 + i];
+  x = t[11];
+  for (loop = 0;loop < 2;++loop) {
+    for (r = 0;r < 16;++r) {
+      sum += 0x9e3779b9;
+      MUSH(0,5) MUSH(1,7) MUSH(2,9) MUSH(3,13)
+      MUSH(4,5) MUSH(5,7) MUSH(6,9) MUSH(7,13)
+      MUSH(8,5) MUSH(9,7) MUSH(10,9) MUSH(11,13)
+    }
+    for (i = 0;i < 8;++i) out[i] ^= t[i + 4];
+  }
+}
diff --git a/surf.h b/surf.h
new file mode 100644 (file)
index 0000000..523f528
--- /dev/null
+++ b/surf.h
@@ -0,0 +1,6 @@
+#ifndef SURF_H
+#define SURF_H
+
+extern void surf();
+
+#endif
diff --git a/surf=0 b/surf=0
new file mode 100644 (file)
index 0000000..4a4f676
--- /dev/null
+++ b/surf=0
@@ -0,0 +1 @@
+surf.3
diff --git a/surf=l b/surf=l
new file mode 100644 (file)
index 0000000..84b5dc8
--- /dev/null
+++ b/surf=l
@@ -0,0 +1,2 @@
+surf.o
+surfpcs.o
diff --git a/surfpcs.3 b/surfpcs.3
new file mode 100644 (file)
index 0000000..8bd0e11
--- /dev/null
+++ b/surfpcs.3
@@ -0,0 +1,69 @@
+.TH surfpcs 3
+.SH NAME
+surfpcs \- SURF protected counter sums
+.SH SYNTAX
+.B #include <surfpcs.h>
+
+void \fBsurfpcs_init\fP(&\fIs\fR,\fIseed\fR);
+
+void \fBsurfpcs_add\fP(&\fIs\fR,\fIbuf\fR,\fIlen\fR);
+
+void \fBsurfpcs_out\fP(&\fIs\fR,\fIh\fR);
+
+surfpcs \fIs\fR;
+.br
+uint32 \fIseed\fR[32];
+.br
+unsigned char *\fIbuf\fR;
+.br
+unsigned int \fIlen\fR;
+.br
+unsigned char \fIh\fR[SURFPCS_LEN];
+.SH DESCRIPTION
+.B surfpcs
+converts a character string to a 256-bit output,
+under control of a 1024-bit seed.
+When the seed is secret,
+.B surfpcs
+appears to be indistinguishable from a completely random
+variable-length-to-256-bit function.
+
+Applying
+.B surfpcs
+takes three steps.
+First, initialize a
+.B surfpcs
+variable,
+.IR s ,
+with
+.BR surfpcs_init .
+The seed will be recorded inside
+.IR s .
+
+Second, feed the input to
+.BR surfpcs_add .
+.I buf
+is a pointer to
+.I len
+characters of input.
+You can split the input across any number of
+.B surfpcs_add
+calls.
+
+Third, call
+.BR surfpcs_out .
+The output will be placed into
+.IR h ,
+an array of
+.B SURFPCS_LEN
+bytes.
+.B SURFPCS_LEN
+is 32.
+
+To apply
+.B surfpcs
+to another input you must call
+.B surfpcs_init
+again.
+.SH "SEE ALSO"
+surf(3)
diff --git a/surfpcs.c b/surfpcs.c
new file mode 100644 (file)
index 0000000..92c3243
--- /dev/null
+++ b/surfpcs.c
@@ -0,0 +1,58 @@
+/* XXX: this needs testing */
+
+#include "surf.h"
+#include "surfpcs.h"
+
+void surfpcs_init(s,k)
+surfpcs *s;
+uint32 k[32];
+{
+  int i;
+  for (i = 0;i < 32;++i) s->seed[i] = k[i];
+  for (i = 0;i < 8;++i) s->sum[i] = 0;
+  for (i = 0;i < 12;++i) s->in[i] = 0;
+  s->todo = 0;
+}
+
+static uint32 littleendian[8] = {
+  50462976, 117835012, 185207048, 252579084,
+  319951120, 387323156, 454695192, 522067228
+} ;
+#define end ((unsigned char *) &littleendian)
+
+#define data ((unsigned char *) s->in)
+#define outdata ((unsigned char *) s->out)
+
+void surfpcs_add(s,x,n)
+surfpcs *s;
+unsigned char *x;
+unsigned int n;
+{
+  int i;
+  while (n--) {
+    data[end[s->todo++]] = *x++;
+    if (s->todo == 32) {
+      s->todo = 0;
+      if (!++s->in[8])
+        if (!++s->in[9])
+          if (!++s->in[10])
+            ++s->in[11];
+      surf(s->out,s->in,s->seed);
+      for (i = 0;i < 8;++i)
+       s->sum[i] += s->out[i];
+    }
+  }
+}
+
+void surfpcs_out(s,h)
+surfpcs *s;
+unsigned char h[32];
+{
+  int i;
+  surfpcs_add(s,".",1);
+  while (s->todo) surfpcs_add(s,"",1);
+  for (i = 0;i < 8;++i) s->in[i] = s->sum[i];
+  for (;i < 12;++i) s->in[i] = 0;
+  surf(s->out,s->in,s->seed);
+  for (i = 0;i < 32;++i) h[i] = outdata[end[i]];
+}
diff --git a/surfpcs.h b/surfpcs.h
new file mode 100644 (file)
index 0000000..6e1b60a
--- /dev/null
+++ b/surfpcs.h
@@ -0,0 +1,20 @@
+#ifndef SURFPCS_H
+#define SURFPCS_H
+
+#include "uint32.h"
+
+typedef struct {
+  uint32 seed[32];
+  uint32 sum[8];
+  uint32 out[8];
+  uint32 in[12];
+  int todo;
+} surfpcs;
+
+#define SURFPCS_LEN 32
+
+extern void surfpcs_init();
+extern void surfpcs_add();
+extern void surfpcs_out();
+
+#endif
diff --git a/surfpcs=0 b/surfpcs=0
new file mode 100644 (file)
index 0000000..ad052cc
--- /dev/null
+++ b/surfpcs=0
@@ -0,0 +1 @@
+surfpcs.3
diff --git a/targets.do b/targets.do
new file mode 100644 (file)
index 0000000..38d1e7d
--- /dev/null
@@ -0,0 +1 @@
+dependon it man install conf-bin conf-man
diff --git a/trycpp.c b/trycpp.c
new file mode 100644 (file)
index 0000000..d7d83ad
--- /dev/null
+++ b/trycpp.c
@@ -0,0 +1,7 @@
+void main()
+{
+#ifdef NeXT
+  printf("nextstep\n"); exit(0);
+#endif
+  printf("unknown\n"); exit(0);
+}
diff --git a/trydrent.c b/trydrent.c
new file mode 100644 (file)
index 0000000..c778176
--- /dev/null
@@ -0,0 +1,8 @@
+#include <sys/types.h>
+#include <dirent.h>
+
+void foo()
+{
+  DIR *dir;
+  struct dirent *d;
+}
diff --git a/tryflock.c b/tryflock.c
new file mode 100644 (file)
index 0000000..8c8aa76
--- /dev/null
@@ -0,0 +1,8 @@
+#include <sys/types.h>
+#include <sys/file.h>
+#include <fcntl.h>
+
+void main()
+{
+  flock(0,LOCK_EX | LOCK_UN | LOCK_NB);
+}
diff --git a/trysgact.c b/trysgact.c
new file mode 100644 (file)
index 0000000..263cb21
--- /dev/null
@@ -0,0 +1,10 @@
+#include <signal.h>
+
+void main()
+{
+  struct sigaction sa;
+  sa.sa_handler = 0;
+  sa.sa_flags = 0;
+  sigemptyset(&sa.sa_mask);
+  sigaction(0,&sa,(struct sigaction *) 0);
+}
diff --git a/tryulong32.c b/tryulong32.c
new file mode 100644 (file)
index 0000000..a108076
--- /dev/null
@@ -0,0 +1,11 @@
+void main()
+{
+  unsigned long u;
+  u = 1;
+  u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+  u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+  u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+  u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+  if (!u) _exit(0);
+  _exit(1);
+}
diff --git a/tryvfork.c b/tryvfork.c
new file mode 100644 (file)
index 0000000..21387e4
--- /dev/null
@@ -0,0 +1,4 @@
+void main()
+{
+  vfork();
+}
diff --git a/trywaitp.c b/trywaitp.c
new file mode 100644 (file)
index 0000000..7e73bfa
--- /dev/null
@@ -0,0 +1,7 @@
+#include <sys/types.h>
+#include <sys/wait.h>
+
+void main()
+{
+  waitpid(0,0,0);
+}
diff --git a/uint32.h.do b/uint32.h.do
new file mode 100644 (file)
index 0000000..b87401d
--- /dev/null
@@ -0,0 +1,7 @@
+dependon tryulong32.c compile load uint32.h1 uint32.h2
+( ./compile tryulong32.c && ./load tryulong32 && ./tryulong32 ) >/dev/null 2>&1 \
+&& cat uint32.h2 || cat uint32.h1
+rm -f tryulong32.o tryulong32
+formake '( ( ./compile tryulong32.c && ./load tryulong32 && ./tryulong32 ) >/dev/null 2>&1 \'
+formake '&& cat uint32.h2 || cat uint32.h1 ) > uint32.h'
+formake 'rm -f tryulong32.o tryulong32'
diff --git a/uint32.h1 b/uint32.h1
new file mode 100644 (file)
index 0000000..6599aa0
--- /dev/null
+++ b/uint32.h1
@@ -0,0 +1,6 @@
+#ifndef UINT32_H
+#define UINT32_H
+
+typedef unsigned int uint32;
+
+#endif
diff --git a/uint32.h2 b/uint32.h2
new file mode 100644 (file)
index 0000000..716430d
--- /dev/null
+++ b/uint32.h2
@@ -0,0 +1,6 @@
+#ifndef UINT32_H
+#define UINT32_H
+
+typedef unsigned long uint32;
+
+#endif
diff --git a/wait.3 b/wait.3
new file mode 100644 (file)
index 0000000..8c41f4b
--- /dev/null
+++ b/wait.3
@@ -0,0 +1,93 @@
+.TH wait 3
+.SH NAME
+wait \- check child process status
+.SH SYNTAX
+.B #include <wait.h>
+
+int \fBwait_nohang\fP(&\fIwstat\fR);
+.br
+int \fBwait_stop\fP(&\fIwstat\fR);
+.br
+int \fBwait_stopnohang\fP(&\fIwstat\fR);
+.br
+int \fBwait_pid\fP(&\fIwstat\fR,\fIpid\fR);
+
+int \fBwait_exitcode\fP(\fIwstat\fR);
+.br
+int \fBwait_crashed\fP(\fIwstat\fR);
+.br
+int \fBwait_stopped\fP(\fIwstat\fR);
+.br
+int \fBwait_stopsig\fP(\fIwstat\fR);
+
+int \fIpid\fR;
+.br
+int \fIwstat\fR;
+.SH DESCRIPTION
+.B wait_nohang
+looks for zombies (child processes that have exited).
+If it sees a zombie,
+it eliminates the zombie,
+puts the zombie's exit status into
+.IR wstat ,
+and returns the zombie's process ID.
+If there are several zombies,
+.B wait_nohang
+picks one.
+If there are children but no zombies,
+.B wait_nohang
+returns 0.
+If there are no children,
+.B wait_nohang
+returns -1,
+setting
+.B errno
+appropriately.
+
+.B wait_stopnohang
+is similar to
+.BR wait_nohang ,
+but it also looks for children that have stopped.
+
+.B wait_stop
+is similar to
+.BR wait_stopnohang ,
+but if there are children it will pause waiting for one of them
+to stop or exit.
+
+.B wait_pid
+waits for child process
+.I pid
+to exit.
+It eliminates any zombie that shows up in the meantime,
+discarding the exit status.
+
+.B wait_stop
+and
+.B wait_pid
+retry upon
+.BR error_intr .
+.SH "STATUS PARSING"
+If the child stopped,
+.B wait_stopped
+is nonzero;
+.B wait_stopsig
+is the signal that caused the child to stop.
+
+If the child exited by crashing,
+.B wait_stopped
+is zero;
+.B wait_crashed
+is nonzero.
+
+If the child exited normally,
+.B wait_stopped
+is zero;
+.B wait_crashed
+is zero;
+and
+.B wait_exitcode
+is the child's exit code.
+.SH "SEE ALSO"
+wait(2),
+error(3)
diff --git a/wait.h b/wait.h
new file mode 100644 (file)
index 0000000..cdb77c3
--- /dev/null
+++ b/wait.h
@@ -0,0 +1,14 @@
+#ifndef WAIT_H
+#define WAIT_H
+
+extern int wait_pid();
+extern int wait_nohang();
+extern int wait_stop();
+extern int wait_stopnohang();
+
+#define wait_crashed(w) ((w) & 127)
+#define wait_exitcode(w) ((w) >> 8)
+#define wait_stopsig(w) ((w) >> 8)
+#define wait_stopped(w) (((w) & 127) == 127)
+
+#endif
diff --git a/wait=0 b/wait=0
new file mode 100644 (file)
index 0000000..62a28ab
--- /dev/null
+++ b/wait=0
@@ -0,0 +1 @@
+wait.3
diff --git a/wait=l b/wait=l
new file mode 100644 (file)
index 0000000..022ccb2
--- /dev/null
+++ b/wait=l
@@ -0,0 +1 @@
+wait_pid.o
diff --git a/wait_pid.c b/wait_pid.c
new file mode 100644 (file)
index 0000000..8dde830
--- /dev/null
@@ -0,0 +1,13 @@
+#include <sys/types.h>
+#include <sys/wait.h>
+#include "error.h"
+
+/* restriction: you must not care about any other child. */
+int wait_pid(wstat,pid) int *wstat; int pid;
+{
+  int r;
+  do
+    r = wait(wstat);
+  while ((r != pid) && ((r != -1) || (errno == error_intr)));
+  return r;
+}
diff --git a/warn-auto.sh b/warn-auto.sh
new file mode 100644 (file)
index 0000000..36d2313
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+# WARNING: This file was auto-generated. Do not edit!