--- /dev/null
+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:
--- /dev/null
+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.
--- /dev/null
+19970629 ezmlm 0.53, alpha.
+19970406 ezmlm 0.52, alpha.
+19970222 ezmlm 0.51, alpha.
+19970203 ezmlm 0.50, alpha.
--- /dev/null
+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
--- /dev/null
+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.
--- /dev/null
+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:
--- /dev/null
+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
--- /dev/null
+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)
--- /dev/null
+VERSION
+systype
+direntry.h
+fork.h
+hasflock.h
+hassgact.h
+uint32.h
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+ezmlm 0.53
--- /dev/null
+.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)
--- /dev/null
+#include "alloc.h"
+#include "error.h"
+extern char *malloc();
+extern void free();
+
+#define ALIGNMENT 16 /* XXX: assuming that this alignment is enough */
+#define SPACE 4096 /* must be multiple of ALIGNMENT */
+
+typedef union { char irrelevant[ALIGNMENT]; double d; } aligned;
+static aligned realspace[SPACE / ALIGNMENT];
+#define space ((char *) realspace)
+static unsigned int avail = SPACE; /* multiple of ALIGNMENT; 0<=avail<=SPACE */
+
+/*@null@*//*@out@*/char *alloc(n)
+unsigned int n;
+{
+ char *x;
+ n = ALIGNMENT + n - (n & (ALIGNMENT - 1)); /* XXX: could overflow */
+ if (n <= avail) { avail -= n; return space + avail; }
+ x = malloc(n);
+ if (!x) errno = error_nomem;
+ return x;
+}
+
+void alloc_free(x)
+char *x;
+{
+ if (x >= space)
+ if (x < space + SPACE)
+ return; /* XXX: assuming that pointers are flat */
+ free(x);
+}
--- /dev/null
+#ifndef ALLOC_H
+#define ALLOC_H
+
+extern /*@null@*//*@out@*/char *alloc();
+extern void alloc_free();
+extern int alloc_re();
+
+#endif
--- /dev/null
+alloc.o
+alloc_re.o
--- /dev/null
+#include "alloc.h"
+#include "byte.h"
+
+int alloc_re(x,m,n)
+char **x;
+unsigned int m;
+unsigned int n;
+{
+ char *y;
+
+ y = alloc(n);
+ if (!y) return 0;
+ byte_copy(y,m,*x);
+ alloc_free(*x);
+ *x = y;
+ return 1;
+}
--- /dev/null
+#include "substdio.h"
+#include "readwrite.h"
+#include "exit.h"
+
+char buf1[256];
+substdio ss1 = SUBSTDIO_FDBUF(write,1,buf1,sizeof(buf1));
+
+void puts(s)
+char *s;
+{
+ if (substdio_puts(&ss1,s) == -1) _exit(111);
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ char *name;
+ char *value;
+ unsigned char ch;
+ char octal[4];
+
+ name = argv[1];
+ if (!name) _exit(100);
+ value = argv[2];
+ if (!value) _exit(100);
+
+ puts("char ");
+ puts(name);
+ puts("[] = \"\\\n");
+
+ while (ch = *value++) {
+ puts("\\");
+ octal[3] = 0;
+ octal[2] = '0' + (ch & 7); ch >>= 3;
+ octal[1] = '0' + (ch & 7); ch >>= 3;
+ octal[0] = '0' + (ch & 7);
+ puts(octal);
+ }
+
+ puts("\\\n\";\n");
+ if (substdio_flush(&ss1) == -1) _exit(111);
+ _exit(0);
+}
--- /dev/null
+substdio.a
+error.a
+str.a
--- /dev/null
+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`
--- /dev/null
+#ifndef AUTO_BIN_H
+#define AUTO_BIN_H
+
+extern char auto_bin[];
+
+#endif
--- /dev/null
+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`
--- /dev/null
+#ifndef AUTO_QMAIL_H
+#define AUTO_QMAIL_H
+
+extern char auto_qmail[];
+
+#endif
--- /dev/null
+#ifndef BYTE_H
+#define BYTE_H
+
+extern unsigned int byte_chr();
+extern unsigned int byte_rchr();
+extern void byte_copy();
+extern void byte_copyr();
+extern int byte_diff();
+extern void byte_zero();
+
+#define byte_equal(s,n,t) (!byte_diff((s),(n),(t)))
+
+#endif
--- /dev/null
+#include "byte.h"
+
+unsigned int byte_chr(s,n,c)
+char *s;
+register unsigned int n;
+int c;
+{
+ register char ch;
+ register char *t;
+
+ ch = c;
+ t = s;
+ for (;;) {
+ if (!n) break; if (*t == ch) break; ++t; --n;
+ if (!n) break; if (*t == ch) break; ++t; --n;
+ if (!n) break; if (*t == ch) break; ++t; --n;
+ if (!n) break; if (*t == ch) break; ++t; --n;
+ }
+ return t - s;
+}
--- /dev/null
+#include "byte.h"
+
+void byte_copy(to,n,from)
+register char *to;
+register unsigned int n;
+register char *from;
+{
+ for (;;) {
+ if (!n) return; *to++ = *from++; --n;
+ if (!n) return; *to++ = *from++; --n;
+ if (!n) return; *to++ = *from++; --n;
+ if (!n) return; *to++ = *from++; --n;
+ }
+}
--- /dev/null
+#include "byte.h"
+
+void byte_copyr(to,n,from)
+register char *to;
+register unsigned int n;
+register char *from;
+{
+ to += n;
+ from += n;
+ for (;;) {
+ if (!n) return; *--to = *--from; --n;
+ if (!n) return; *--to = *--from; --n;
+ if (!n) return; *--to = *--from; --n;
+ if (!n) return; *--to = *--from; --n;
+ }
+}
--- /dev/null
+#include "byte.h"
+
+int byte_diff(s,n,t)
+register char *s;
+register unsigned int n;
+register char *t;
+{
+ for (;;) {
+ if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
+ if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
+ if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
+ if (!n) return 0; if (*s != *t) break; ++s; ++t; --n;
+ }
+ return ((int)(unsigned int)(unsigned char) *s)
+ - ((int)(unsigned int)(unsigned char) *t);
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+ }
+}
--- /dev/null
+.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)
--- /dev/null
+#ifndef CASE_H
+#define CASE_H
+
+extern void case_lowers();
+extern void case_lowerb();
+extern int case_diffs();
+extern int case_diffb();
+extern int case_starts();
+extern int case_startb();
+
+#define case_equals(s,t) (!case_diffs((s),(t)))
+
+#endif
--- /dev/null
+case_diffb.o
+case_lowerb.o
+case_startb.o
--- /dev/null
+#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;
+}
--- /dev/null
+#include "case.h"
+
+void case_lowerb(s,len)
+char *s;
+unsigned int len;
+{
+ unsigned char x;
+ while (len > 0) {
+ --len;
+ x = *s - 'A';
+ if (x <= 'Z' - 'A') *s = x + 'a';
+ ++s;
+ }
+}
--- /dev/null
+#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;
+ }
+}
--- /dev/null
+/usr/local/bin/ezmlm
+
+Programs will be installed in this directory.
--- /dev/null
+cc -O2
+
+This will be used to compile .c files.
--- /dev/null
+cc -s
+
+This will be used to link .o files into an executable.
--- /dev/null
+/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.
--- /dev/null
+/var/qmail
+
+This is the qmail home directory.
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#ifndef COOKIE_H
+#define COOKIE_H
+
+#define COOKIE 20
+
+extern void cookie();
+
+#endif
--- /dev/null
+#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;
+}
--- /dev/null
+#ifndef DATE822FMT_H
+#define DATE822FMT_H
+
+extern unsigned int date822fmt();
+#define DATE822FMT 60
+
+#endif
--- /dev/null
+.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)
--- /dev/null
+/* 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;
+}
--- /dev/null
+#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
--- /dev/null
+datetime.3
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+.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)
--- /dev/null
+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'
--- /dev/null
+#ifndef DIRENTRY_H
+#define DIRENTRY_H
+
+#include <sys/types.h>
+#include <sys/dir.h>
+#define direntry struct direct
+
+#endif
--- /dev/null
+#ifndef DIRENTRY_H
+#define DIRENTRY_H
+
+#include <sys/types.h>
+#include <dirent.h>
+#define direntry struct dirent
+
+#endif
--- /dev/null
+direntry.3
--- /dev/null
+.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)
--- /dev/null
+#ifndef ENV_H
+#define ENV_H
+
+extern int env_isinit;
+
+extern int env_init();
+extern int env_put();
+extern int env_put2();
+extern int env_unset();
+extern /*@null@*/char *env_get();
+extern char *env_pick();
+extern void env_clear();
+extern char *env_findeq();
+
+extern char **environ;
+
+#endif
--- /dev/null
+#include "env.h"
+#include "str.h"
+
+extern /*@null@*/char *env_get(s)
+char *s;
+{
+ int i;
+ unsigned int slen;
+ char *envi;
+
+ slen = str_len(s);
+ for (i = 0;envi = environ[i];++i)
+ if ((!str_diffn(s,envi,slen)) && (envi[slen] == '='))
+ return envi + slen + 1;
+ return 0;
+}
+
+extern char *env_pick()
+{
+ return environ[0];
+}
+
+extern char *env_findeq(s)
+char *s;
+{
+ for (;*s;++s)
+ if (*s == '=')
+ return s;
+ return 0;
+}
--- /dev/null
+.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)
--- /dev/null
+#include <errno.h>
+#include "error.h"
+
+/* warning: as coverage improves here, should update error_{str,temp} */
+
+int error_intr =
+#ifdef EINTR
+EINTR;
+#else
+-1;
+#endif
+
+int error_nomem =
+#ifdef ENOMEM
+ENOMEM;
+#else
+-2;
+#endif
+
+int error_noent =
+#ifdef ENOENT
+ENOENT;
+#else
+-3;
+#endif
+
+int error_txtbsy =
+#ifdef ETXTBSY
+ETXTBSY;
+#else
+-4;
+#endif
+
+int error_io =
+#ifdef EIO
+EIO;
+#else
+-5;
+#endif
+
+int error_exist =
+#ifdef EEXIST
+EEXIST;
+#else
+-6;
+#endif
+
+int error_timeout =
+#ifdef ETIMEDOUT
+ETIMEDOUT;
+#else
+-7;
+#endif
+
+int error_inprogress =
+#ifdef EINPROGRESS
+EINPROGRESS;
+#else
+-8;
+#endif
+
+int error_wouldblock =
+#ifdef EWOULDBLOCK
+EWOULDBLOCK;
+#else
+-9;
+#endif
+
+int error_again =
+#ifdef EAGAIN
+EAGAIN;
+#else
+-10;
+#endif
+
+int error_pipe =
+#ifdef EPIPE
+EPIPE;
+#else
+-11;
+#endif
+
+int error_perm =
+#ifdef EPERM
+EPERM;
+#else
+-12;
+#endif
+
+int error_acces =
+#ifdef EACCES
+EACCES;
+#else
+-13;
+#endif
--- /dev/null
+#ifndef ERROR_H
+#define ERROR_H
+
+extern int errno;
+
+extern int error_intr;
+extern int error_nomem;
+extern int error_noent;
+extern int error_txtbsy;
+extern int error_io;
+extern int error_exist;
+extern int error_timeout;
+extern int error_inprogress;
+extern int error_wouldblock;
+extern int error_again;
+extern int error_pipe;
+extern int error_perm;
+extern int error_acces;
+
+extern char *error_str();
+extern int error_temp();
+
+#endif
--- /dev/null
+error.o
+error_str.o
--- /dev/null
+.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)
--- /dev/null
+#include <errno.h>
+#include "error.h"
+
+#define X(e,s) if (i == e) return s;
+
+char *error_str(i)
+int i;
+{
+ X(0,"no error")
+ X(error_intr,"interrupted system call")
+ X(error_nomem,"out of memory")
+ X(error_noent,"file does not exist")
+ X(error_txtbsy,"text busy")
+ X(error_io,"input/output error")
+ X(error_exist,"file already exists")
+ X(error_timeout,"timed out")
+ X(error_inprogress,"operation in progress")
+ X(error_again,"temporary failure")
+ X(error_wouldblock,"input/output would block")
+ X(error_pipe,"broken pipe")
+ X(error_perm,"permission denied")
+ X(error_acces,"access denied")
+#ifdef ESRCH
+ X(ESRCH,"no such process")
+#endif
+#ifdef ENXIO
+ X(ENXIO,"device not configured")
+#endif
+#ifdef E2BIG
+ X(E2BIG,"argument list too long")
+#endif
+#ifdef ENOEXEC
+ X(ENOEXEC,"exec format error")
+#endif
+#ifdef EBADF
+ X(EBADF,"file descriptor not open")
+#endif
+#ifdef ECHILD
+ X(ECHILD,"no child processes")
+#endif
+#ifdef EDEADLK
+ X(EDEADLK,"operation would cause deadlock")
+#endif
+#ifdef EFAULT
+ X(EFAULT,"bad address")
+#endif
+#ifdef ENOTBLK
+ X(ENOTBLK,"not a block device")
+#endif
+#ifdef EBUSY
+ X(EBUSY,"device busy")
+#endif
+#ifdef EXDEV
+ X(EXDEV,"cross-device link")
+#endif
+#ifdef ENODEV
+ X(ENODEV,"device does not support operation")
+#endif
+#ifdef ENOTDIR
+ X(ENOTDIR,"not a directory")
+#endif
+#ifdef EISDIR
+ X(EISDIR,"is a directory")
+#endif
+#ifdef EINVAL
+ X(EINVAL,"invalid argument")
+#endif
+#ifdef ENFILE
+ X(ENFILE,"system cannot open more files")
+#endif
+#ifdef EMFILE
+ X(EMFILE,"process cannot open more files")
+#endif
+#ifdef ENOTTY
+ X(ENOTTY,"not a tty")
+#endif
+#ifdef EFBIG
+ X(EFBIG,"file too big")
+#endif
+#ifdef ENOSPC
+ X(ENOSPC,"out of disk space")
+#endif
+#ifdef ESPIPE
+ X(ESPIPE,"unseekable descriptor")
+#endif
+#ifdef EROFS
+ X(EROFS,"read-only file system")
+#endif
+#ifdef EMLINK
+ X(EMLINK,"too many links")
+#endif
+#ifdef EDOM
+ X(EDOM,"input out of range")
+#endif
+#ifdef ERANGE
+ X(ERANGE,"output out of range")
+#endif
+#ifdef EALREADY
+ X(EALREADY,"operation already in progress")
+#endif
+#ifdef ENOTSOCK
+ X(ENOTSOCK,"not a socket")
+#endif
+#ifdef EDESTADDRREQ
+ X(EDESTADDRREQ,"destination address required")
+#endif
+#ifdef EMSGSIZE
+ X(EMSGSIZE,"message too long")
+#endif
+#ifdef EPROTOTYPE
+ X(EPROTOTYPE,"incorrect protocol type")
+#endif
+#ifdef ENOPROTOOPT
+ X(ENOPROTOOPT,"protocol not available")
+#endif
+#ifdef EPROTONOSUPPORT
+ X(EPROTONOSUPPORT,"protocol not supported")
+#endif
+#ifdef ESOCKTNOSUPPORT
+ X(ESOCKTNOSUPPORT,"socket type not supported")
+#endif
+#ifdef EOPNOTSUPP
+ X(EOPNOTSUPP,"operation not supported")
+#endif
+#ifdef EPFNOSUPPORT
+ X(EPFNOSUPPORT,"protocol family not supported")
+#endif
+#ifdef EAFNOSUPPORT
+ X(EAFNOSUPPORT,"address family not supported")
+#endif
+#ifdef EADDRINUSE
+ X(EADDRINUSE,"address already used")
+#endif
+#ifdef EADDRNOTAVAIL
+ X(EADDRNOTAVAIL,"address not available")
+#endif
+#ifdef ENETDOWN
+ X(ENETDOWN,"network down")
+#endif
+#ifdef ENETUNREACH
+ X(ENETUNREACH,"network unreachable")
+#endif
+#ifdef ENETRESET
+ X(ENETRESET,"network reset")
+#endif
+#ifdef ECONNABORTED
+ X(ECONNABORTED,"connection aborted")
+#endif
+#ifdef ECONNRESET
+ X(ECONNRESET,"connection reset")
+#endif
+#ifdef ENOBUFS
+ X(ENOBUFS,"out of buffer space")
+#endif
+#ifdef EISCONN
+ X(EISCONN,"already connected")
+#endif
+#ifdef ENOTCONN
+ X(ENOTCONN,"not connected")
+#endif
+#ifdef ESHUTDOWN
+ X(ESHUTDOWN,"socket shut down")
+#endif
+#ifdef ETOOMANYREFS
+ X(ETOOMANYREFS,"too many references")
+#endif
+#ifdef ECONNREFUSED
+ X(ECONNREFUSED,"connection refused")
+#endif
+#ifdef ELOOP
+ X(ELOOP,"symbolic link loop")
+#endif
+#ifdef ENAMETOOLONG
+ X(ENAMETOOLONG,"file name too long")
+#endif
+#ifdef EHOSTDOWN
+ X(EHOSTDOWN,"host down")
+#endif
+#ifdef EHOSTUNREACH
+ X(EHOSTUNREACH,"host unreachable")
+#endif
+#ifdef ENOTEMPTY
+ X(ENOTEMPTY,"directory not empty")
+#endif
+#ifdef EPROCLIM
+ X(EPROCLIM,"too many processes")
+#endif
+#ifdef EUSERS
+ X(EUSERS,"too many users")
+#endif
+#ifdef EDQUOT
+ X(EDQUOT,"disk quota exceeded")
+#endif
+#ifdef ESTALE
+ X(ESTALE,"stale NFS file handle")
+#endif
+#ifdef EREMOTE
+ X(EREMOTE,"too many levels of remote in path")
+#endif
+#ifdef EBADRPC
+ X(EBADRPC,"RPC structure is bad")
+#endif
+#ifdef ERPCMISMATCH
+ X(ERPCMISMATCH,"RPC version mismatch")
+#endif
+#ifdef EPROGUNAVAIL
+ X(EPROGUNAVAIL,"RPC program unavailable")
+#endif
+#ifdef EPROGMISMATCH
+ X(EPROGMISMATCH,"program version mismatch")
+#endif
+#ifdef EPROCUNAVAIL
+ X(EPROCUNAVAIL,"bad procedure for program")
+#endif
+#ifdef ENOLCK
+ X(ENOLCK,"no locks available")
+#endif
+#ifdef ENOSYS
+ X(ENOSYS,"system call not available")
+#endif
+#ifdef EFTYPE
+ X(EFTYPE,"bad file type")
+#endif
+#ifdef EAUTH
+ X(EAUTH,"authentication error")
+#endif
+#ifdef ENEEDAUTH
+ X(ENEEDAUTH,"not authenticated")
+#endif
+#ifdef ENOSTR
+ X(ENOSTR,"not a stream device")
+#endif
+#ifdef ETIME
+ X(ETIME,"timer expired")
+#endif
+#ifdef ENOSR
+ X(ENOSR,"out of stream resources")
+#endif
+#ifdef ENOMSG
+ X(ENOMSG,"no message of desired type")
+#endif
+#ifdef EBADMSG
+ X(EBADMSG,"bad message type")
+#endif
+#ifdef EIDRM
+ X(EIDRM,"identifier removed")
+#endif
+#ifdef ENONET
+ X(ENONET,"machine not on network")
+#endif
+#ifdef ERREMOTE
+ X(ERREMOTE,"object not local")
+#endif
+#ifdef ENOLINK
+ X(ENOLINK,"link severed")
+#endif
+#ifdef EADV
+ X(EADV,"advertise error")
+#endif
+#ifdef ESRMNT
+ X(ESRMNT,"srmount error")
+#endif
+#ifdef ECOMM
+ X(ECOMM,"communication error")
+#endif
+#ifdef EPROTO
+ X(EPROTO,"protocol error")
+#endif
+#ifdef EMULTIHOP
+ X(EMULTIHOP,"multihop attempted")
+#endif
+#ifdef EREMCHG
+ X(EREMCHG,"remote address changed")
+#endif
+ return "unknown error";
+}
--- /dev/null
+error_str.3
--- /dev/null
+.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)
--- /dev/null
+error_temp.3
--- /dev/null
+#ifndef EXIT_H
+#define EXIT_H
+
+extern void _exit();
+
+#endif
--- /dev/null
+.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)
--- /dev/null
+#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);
+}
--- /dev/null
+ezmlm-list.1
--- /dev/null
+strerr.a
+getln.a
+substdio.a
+stralloc.a
+alloc.a
+error.a
+open.a
+str.a
--- /dev/null
+.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)
--- /dev/null
+#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);
+}
--- /dev/null
+ezmlm-make.1
--- /dev/null
+auto_bin.o
+open.a
+getopt.a
+substdio.a
+strerr.a
+stralloc.a
+alloc.a
+error.a
+str.a
--- /dev/null
+.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)
--- /dev/null
+#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("ed,&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("ed,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("ed,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("ed,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");
+ }
+}
--- /dev/null
+ezmlm-manage.1
--- /dev/null
+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
--- /dev/null
+.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)
--- /dev/null
+#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);
+}
--- /dev/null
+ezmlm-reject.1
--- /dev/null
+getln.a
+strerr.a
+substdio.a
+error.a
+stralloc.a
+alloc.a
+str.a
+getopt.a
+case.a
--- /dev/null
+.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)
--- /dev/null
+#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("ed,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("ed,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(¶graph,"")) die_nomem();
+ for (;;) {
+ if (getln(&ssmsgin,&line,&match,'\n') == -1) die_msgin();
+ if (!match) die_trash();
+ if (!stralloc_cat(¶graph,&line)) die_nomem();
+ if (line.len <= 1) break;
+ }
+
+ if (!flaghaveheader) {
+ if (!stralloc_copy(&header,¶graph)) 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,¶graph)) die_nomem();
+ flaghaveintro = 1;
+ continue;
+ }
+
+ if (paragraph.s[0] == '-')
+ break;
+
+ if (paragraph.s[0] == '<') {
+ if (!stralloc_copy(&failure,¶graph)) 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);
+}
--- /dev/null
+ezmlm-return.1
--- /dev/null
+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
--- /dev/null
+.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)
--- /dev/null
+#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");
+ }
+}
--- /dev/null
+ezmlm-send.1
--- /dev/null
+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
--- /dev/null
+.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)
--- /dev/null
+#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);
+}
--- /dev/null
+ezmlm-sub.1
--- /dev/null
+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
--- /dev/null
+.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)
--- /dev/null
+#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);
+}
--- /dev/null
+ezmlm-unsub.1
--- /dev/null
+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
--- /dev/null
+.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)
--- /dev/null
+#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("ed,&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("ed,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);
+}
--- /dev/null
+ezmlm-warn.1
--- /dev/null
+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
--- /dev/null
+.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)
--- /dev/null
+#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);
+}
--- /dev/null
+ezmlm-weed.1
--- /dev/null
+getln.a
+strerr.a
+substdio.a
+error.a
+stralloc.a
+alloc.a
+str.a
--- /dev/null
+.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)
--- /dev/null
+#ifndef FD_H
+#define FD_H
+
+extern int fd_copy();
+extern int fd_move();
+
+#endif
--- /dev/null
+fd_copy.o
+fd_move.o
--- /dev/null
+.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)
--- /dev/null
+#include <fcntl.h>
+#include "fd.h"
+
+int fd_copy(to,from)
+int to;
+int from;
+{
+ if (to == from) return 0;
+ if (fcntl(from,F_GETFL,0) == -1) return -1;
+ close(to);
+ if (fcntl(from,F_DUPFD,to) == -1) return -1;
+ return 0;
+}
--- /dev/null
+.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)
--- /dev/null
+#include "fd.h"
+
+int fd_move(to,from)
+int to;
+int from;
+{
+ if (to == from) return 0;
+ if (fd_copy(to,from) == -1) return -1;
+ close(from);
+ return 0;
+}
--- /dev/null
+# oper-:arch-:syst-:chip-:kern-
+# oper = operating system type; e.g., sunos-4.1.4
+# arch = machine language; e.g., sparc
+# syst = which binaries can run; e.g., sun4
+# chip = chip model; e.g., micro-2-80
+# kern = kernel version; e.g., sun4m
+# dependence: arch --- chip
+# \ \
+# oper --- syst --- kern
+# so, for example, syst is interpreted in light of oper, but chip is not.
+# anyway, no slashes, no extra colons, no uppercase letters.
+# the point of the extra -'s is to ease parsing: can add hierarchies later.
+# e.g., *:i386-*:*:pentium-*:* would handle pentium-100 as well as pentium,
+# and i386-486 (486s do have more instructions, you know) as well as i386.
+# the idea here is to include ALL useful available information.
+
+exec 2>/dev/null
+sys="`uname -s | tr '/:[A-Z]' '..[a-z]'`"
+if [ x"$sys" != x ]
+then
+ unamer="`uname -r | tr /: ..`"
+ unamem="`uname -m | tr /: ..`"
+ unamev="`uname -v | tr /: ..`"
+
+ case "$sys" in
+ bsd.os)
+ # in bsd 4.4, uname -v does not have useful info.
+ # in bsd 4.4, uname -m is arch, not chip.
+ oper="$sys-$unamer"
+ arch="$unamem"
+ syst=""
+ chip="`sysctl -n hw.model`"
+ kern=""
+ ;;
+ freebsd)
+ # see above about bsd 4.4
+ oper="$sys-$unamer"
+ arch="$unamem"
+ syst=""
+ chip="`sysctl -n hw.model`" # hopefully
+ kern=""
+ ;;
+ netbsd)
+ # see above about bsd 4.4
+ oper="$sys-$unamer"
+ arch="$unamem"
+ syst=""
+ chip="`sysctl -n hw.model`" # hopefully
+ kern=""
+ ;;
+ linux)
+ # as in bsd 4.4, uname -v does not have useful info.
+ oper="$sys-$unamer"
+ syst=""
+ chip="$unamem"
+ kern=""
+ case "$chip" in
+ i386|i486|i586|i686)
+ arch="i386"
+ ;;
+ alpha)
+ arch="alpha"
+ ;;
+ esac
+ ;;
+ aix)
+ # naturally IBM has to get uname -r and uname -v backwards. dorks.
+ oper="$sys-$unamev-$unamer"
+ arch="`arch | tr /: ..`"
+ syst=""
+ chip="$unamem"
+ kern=""
+ ;;
+ sunos)
+ oper="$sys-$unamer-$unamev"
+ arch="`(uname -p || mach) | tr /: ..`"
+ syst="`arch | tr /: ..`"
+ chip="$unamem" # this is wrong; is there any way to get the real info?
+ kern="`arch -k | tr /: ..`"
+ ;;
+ unix_sv)
+ oper="$sys-$unamer-$unamev"
+ arch="`uname -m`"
+ syst=""
+ chip="$unamem"
+ kern=""
+ ;;
+ *)
+ oper="$sys-$unamer-$unamev"
+ arch="`arch | tr /: ..`"
+ syst=""
+ chip="$unamem"
+ kern=""
+ ;;
+ esac
+else
+ $CC -c trycpp.c
+ $LD -o trycpp trycpp.o
+ case `./trycpp` in
+ nextstep)
+ oper="nextstep-`hostinfo | sed -n 's/^[ ]*NeXT Mach \([^:]*\):.*$/\1/p'`"
+ arch="`hostinfo | sed -n 's/^Processor type: \(.*\) (.*)$/\1/p' | tr /: ..`"
+ syst=""
+ chip="`hostinfo | sed -n 's/^Processor type: .* (\(.*\))$/\1/p' | tr ' /:' '...'`"
+ kern=""
+ ;;
+ *)
+ oper="unknown"
+ arch=""
+ syst=""
+ chip=""
+ kern=""
+ ;;
+ esac
+ rm -f trycpp.o trycpp
+fi
+
+case "$chip" in
+80486)
+ # let's try to be consistent here. (BSD/OS)
+ chip=i486
+ ;;
+i486DX)
+ # respect the hyphen hierarchy. (FreeBSD)
+ chip=i486-dx
+ ;;
+i486.DX2)
+ # respect the hyphen hierarchy. (FreeBSD)
+ chip=i486-dx2
+ ;;
+Intel.586)
+ # no, you nitwits, there is no such chip. (NeXTStep)
+ chip=pentium
+ ;;
+i586)
+ # no, you nitwits, there is no such chip. (Linux)
+ chip=pentium
+ ;;
+i686)
+ # STOP SAYING THAT! (Linux)
+ chip=ppro
+esac
+
+echo "$oper-:$arch-:$syst-:$chip-:$kern-" | tr ' [A-Z]' '.[a-z]'
--- /dev/null
+#ifndef FMT_H
+#define FMT_H
+
+#define FMT_ULONG 40 /* enough space to hold 2^128 - 1 in decimal, plus \0 */
+#define FMT_LEN ((char *) 0) /* convenient abbreviation */
+
+extern unsigned int fmt_uint();
+extern unsigned int fmt_uint0();
+extern unsigned int fmt_xint();
+extern unsigned int fmt_nbbint();
+extern unsigned int fmt_ushort();
+extern unsigned int fmt_xshort();
+extern unsigned int fmt_nbbshort();
+extern unsigned int fmt_ulong();
+extern unsigned int fmt_xlong();
+extern unsigned int fmt_nbblong();
+
+extern unsigned int fmt_plusminus();
+extern unsigned int fmt_minus();
+extern unsigned int fmt_0x();
+
+extern unsigned int fmt_str();
+extern unsigned int fmt_strn();
+
+#endif
--- /dev/null
+#include "fmt.h"
+
+unsigned int fmt_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;
+}
--- /dev/null
+#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);
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#include "fmt.h"
+
+unsigned int fmt_ulong(s,u) register char *s; register unsigned long u;
+{
+ register unsigned int len; register unsigned long q;
+ len = 1; q = u;
+ while (q > 9) { ++len; q /= 10; }
+ if (s) {
+ s += len;
+ do { *--s = '0' + (u % 10); u /= 10; } while(u); /* handles u == 0 */
+ }
+ return len;
+}
--- /dev/null
+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'
--- /dev/null
+#ifndef FORK_H
+#define FORK_H
+
+extern int fork();
+#define vfork fork
+
+#endif
--- /dev/null
+#ifndef FORK_H
+#define FORK_H
+
+extern int fork();
+extern int vfork();
+
+#endif
--- /dev/null
+fmt_str.o
+fmt_uint.o
+fmt_uint0.o
+fmt_ulong.o
+scan_ulong.o
+scan_8long.o
--- /dev/null
+#ifndef GEN_ALLOC_H
+#define GEN_ALLOC_H
+
+#define GEN_ALLOC_typedef(ta,type,field,len,a) \
+ typedef struct ta { type *field; unsigned int len; unsigned int a; } ta;
+
+#endif
--- /dev/null
+#ifndef GEN_ALLOC_DEFS_H
+#define GEN_ALLOC_DEFS_H
+
+#define GEN_ALLOC_ready(ta,type,field,len,a,i,n,x,base,ta_ready) \
+int ta_ready(x,n) register ta *x; register unsigned int n; \
+{ register unsigned int i; \
+ if (x->field) { \
+ i = x->a; \
+ if (n > i) { \
+ x->a = base + n + (n >> 3); \
+ if (alloc_re(&x->field,i * sizeof(type),x->a * sizeof(type))) return 1; \
+ x->a = i; return 0; } \
+ return 1; } \
+ x->len = 0; \
+ return !!(x->field = (type *) alloc((x->a = n) * sizeof(type))); }
+
+#define GEN_ALLOC_readyplus(ta,type,field,len,a,i,n,x,base,ta_rplus) \
+int ta_rplus(x,n) register ta *x; register unsigned int n; \
+{ register unsigned int i; \
+ if (x->field) { \
+ i = x->a; n += x->len; \
+ if (n > i) { \
+ x->a = base + n + (n >> 3); \
+ if (alloc_re(&x->field,i * sizeof(type),x->a * sizeof(type))) return 1; \
+ x->a = i; return 0; } \
+ return 1; } \
+ x->len = 0; \
+ return !!(x->field = (type *) alloc((x->a = n) * sizeof(type))); }
+
+#define GEN_ALLOC_append(ta,type,field,len,a,i,n,x,base,ta_rplus,ta_append) \
+int ta_append(x,i) register ta *x; register type *i; \
+{ if (!ta_rplus(x,1)) return 0; x->field[x->len++] = *i; return 1; }
+
+#endif
--- /dev/null
+#include "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;
+}
--- /dev/null
+#ifndef GETCONF_H
+#define GETCONF_H
+
+extern int getconf();
+extern int getconf_line();
+
+#endif
--- /dev/null
+.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)
--- /dev/null
+#include "substdio.h"
+#include "byte.h"
+#include "stralloc.h"
+#include "getln.h"
+
+int getln(ss,sa,match,sep)
+register substdio *ss;
+register stralloc *sa;
+int *match;
+int sep;
+{
+ char *cont;
+ unsigned int clen;
+
+ if (getln2(ss,sa,&cont,&clen,sep) == -1) return -1;
+ if (!clen) { *match = 0; return 0; }
+ if (!stralloc_catb(sa,cont,clen)) return -1;
+ *match = 1;
+ return 0;
+}
--- /dev/null
+#ifndef GETLN_H
+#define GETLN_H
+
+extern int getln();
+extern int getln2();
+
+#endif
--- /dev/null
+.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)
--- /dev/null
+#include "substdio.h"
+#include "stralloc.h"
+#include "byte.h"
+#include "getln.h"
+
+int getln2(ss,sa,cont,clen,sep)
+register substdio *ss;
+register stralloc *sa;
+/*@out@*/char **cont;
+/*@out@*/unsigned int *clen;
+int sep;
+{
+ register char *x;
+ register unsigned int i;
+ int n;
+
+ if (!stralloc_ready(sa,0)) return -1;
+ sa->len = 0;
+
+ for (;;) {
+ n = substdio_feed(ss);
+ if (n < 0) return -1;
+ if (n == 0) { *clen = 0; return 0; }
+ x = substdio_PEEK(ss);
+ i = byte_chr(x,n,sep);
+ if (i < n) { substdio_SEEK(ss,*clen = i + 1); *cont = x; return 0; }
+ if (!stralloc_readyplus(sa,n)) return -1;
+ i = sa->len;
+ sa->len = i + substdio_get(ss,sa->s + i,n);
+ }
+}
--- /dev/null
+getln.o
+getln2.o
--- /dev/null
+.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.
--- /dev/null
+subgetopt.o
+sgetopt.o
--- /dev/null
+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'
--- /dev/null
+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'
--- /dev/null
+#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);
+ }
+}
--- /dev/null
+getln.a
+strerr.a
+substdio.a
+stralloc.a
+alloc.a
+open.a
+error.a
+str.a
+fs.a
--- /dev/null
+#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;
+}
--- /dev/null
+#ifndef ISSUB_H
+#define ISSUB_H
+
+#include "strerr.h"
+
+extern struct strerr issub_err;
+
+extern int issub();
+
+#endif
--- /dev/null
+dependon \
+ezmlm-make ezmlm-manage ezmlm-send ezmlm-reject \
+ezmlm-return ezmlm-warn ezmlm-weed \
+ezmlm-list ezmlm-sub ezmlm-unsub
--- /dev/null
+#ifndef LOCK_H
+#define LOCK_H
+
+extern int lock_ex();
+extern int lock_un();
+extern int lock_exnb();
+
+#endif
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#ifndef LOG_H
+#define LOG_H
+
+extern void log();
+
+#endif
--- /dev/null
+echo exec "$CC" -c '${1+"$@"}'
--- /dev/null
+echo 'main="$1"; shift'
+echo exec "$LD" '-o "$main" "$main".o ${1+"$@"}'
--- /dev/null
+echo 'main="$1"; shift'
+echo 'rm -f "$main"'
+echo 'ar cr "$main" ${1+"$@"}'
+
+case "$1" in
+sunos-5.*) ;;
+unix_sv*) ;;
+irix64-*) ;;
+irix-*) ;;
+dgux-*) ;;
+hp-ux-*) ;;
+sco*) ;;
+*)
+ echo 'ranlib "$main"'
+ ;;
+esac
--- /dev/null
+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
--- /dev/null
+.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)
--- /dev/null
+#include <time.h>
+#include "datetime.h"
+#include "now.h"
+
+datetime_sec now()
+{
+ return time((long *) 0);
+}
--- /dev/null
+#ifndef NOW_H
+#define NOW_H
+
+#include "datetime.h"
+
+extern datetime_sec now();
+
+#endif
--- /dev/null
+#ifndef OPEN_H
+#define OPEN_H
+
+extern int open_read();
+extern int open_excl();
+extern int open_append();
+extern int open_trunc();
+extern int open_write();
+
+#endif
--- /dev/null
+open_append.o
+open_read.o
+open_trunc.o
--- /dev/null
+#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); }
--- /dev/null
+#include <sys/types.h>
+#include <fcntl.h>
+#include "open.h"
+
+int open_read(fn) char *fn;
+{ return open(fn,O_RDONLY | O_NDELAY); }
--- /dev/null
+#include <sys/types.h>
+#include <fcntl.h>
+#include "open.h"
+
+int open_trunc(fn) char *fn;
+{ return open(fn,O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT,0644); }
--- /dev/null
+#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;
+ }
+}
--- /dev/null
+#ifndef QMAIL_H
+#define QMAIL_H
+
+#include "substdio.h"
+
+struct qmail {
+ int flagerr;
+ unsigned long pid;
+ int fdm;
+ int fde;
+ substdio ss;
+ char buf[1024];
+} ;
+
+extern int qmail_open();
+extern void qmail_put();
+extern void qmail_puts();
+extern void qmail_from();
+extern void qmail_to();
+extern void qmail_fail();
+extern 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
--- /dev/null
+#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);
+}
--- /dev/null
+#ifndef QUOTE_H
+#define QUOTE_H
+
+extern int quote_need();
+extern int quote();
+extern int quote2();
+
+#endif
--- /dev/null
+#ifndef READWRITE_H
+#define READWRITE_H
+
+extern int read();
+extern int write();
+
+#endif
--- /dev/null
+#ifndef SCAN_H
+#define SCAN_H
+
+extern unsigned int scan_uint();
+extern unsigned int scan_xint();
+extern unsigned int scan_nbbint();
+extern unsigned int scan_ushort();
+extern unsigned int scan_xshort();
+extern unsigned int scan_nbbshort();
+extern unsigned int scan_ulong();
+extern unsigned int scan_xlong();
+extern unsigned int scan_nbblong();
+
+extern unsigned int scan_plusminus();
+extern unsigned int scan_0x();
+
+extern unsigned int scan_whitenskip();
+extern unsigned int scan_nonwhitenskip();
+extern unsigned int scan_charsetnskip();
+extern unsigned int scan_noncharsetnskip();
+
+extern unsigned int scan_strncmp();
+extern unsigned int scan_memcmp();
+
+extern unsigned int scan_long();
+
+#endif
--- /dev/null
+#include "scan.h"
+
+unsigned int scan_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;
+}
--- /dev/null
+#include "scan.h"
+
+unsigned int scan_ulong(s,u) register char *s; register unsigned long *u;
+{
+ register unsigned int pos; register unsigned long result;
+ register unsigned long c;
+ pos = 0; result = 0;
+ while ((c = (unsigned long) (unsigned char) (s[pos] - '0')) < 10)
+ { result = result * 10 + c; ++pos; }
+ *u = result; return pos;
+}
--- /dev/null
+#ifndef SEEK_H
+#define SEEK_H
+
+typedef unsigned long seek_pos;
+
+extern seek_pos seek_cur();
+
+extern int seek_set();
+extern int seek_end();
+
+extern int seek_trunc();
+
+#define seek_begin(fd) (seek_set((fd),(seek_pos) 0))
+
+#endif
--- /dev/null
+seek_set.o
--- /dev/null
+#include <sys/types.h>
+#include "seek.h"
+
+#define SET 0 /* sigh */
+
+int seek_set(fd,pos) int fd; seek_pos pos;
+{ if (lseek(fd,(off_t) pos,SET) == -1) return -1; return 0; }
--- /dev/null
+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
--- /dev/null
+.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)
--- /dev/null
+/* sgetopt.c, sgetopt.h: (yet another) improved getopt clone, outer layer
+D. J. Bernstein, djb@pobox.com.
+Depends on subgetopt.h, substdio.h, subfd.h.
+No system requirements.
+19970208: Cleanups.
+931201: Baseline.
+No known patent problems.
+
+Documentation in sgetopt.3.
+*/
+
+#include "substdio.h"
+#include "subfd.h"
+#define SGETOPTNOSHORT
+#include "sgetopt.h"
+#define SUBGETOPTNOSHORT
+#include "subgetopt.h"
+
+#define getopt sgetoptmine
+#define optind subgetoptind
+#define opterr sgetopterr
+#define optproblem subgetoptproblem
+#define optprogname sgetoptprogname
+
+int opterr = 1;
+char *optprogname = 0;
+
+int getopt(argc,argv,opts)
+int argc;
+char **argv;
+char *opts;
+{
+ int c;
+ char *s;
+
+ if (!optprogname) {
+ optprogname = *argv;
+ if (!optprogname) optprogname = "";
+ for (s = optprogname;*s;++s) if (*s == '/') optprogname = s + 1;
+ }
+ c = subgetopt(argc,argv,opts);
+ if (opterr)
+ if (c == '?') {
+ char chp[2]; chp[0] = optproblem; chp[1] = '\n';
+ substdio_puts(subfderr,optprogname);
+ if (argv[optind] && (optind < argc))
+ substdio_puts(subfderr,": illegal option -- ");
+ else
+ substdio_puts(subfderr,": option requires an argument -- ");
+ substdio_put(subfderr,chp,2);
+ substdio_flush(subfderr);
+ }
+ return c;
+}
--- /dev/null
+#ifndef SGETOPT_H
+#define SGETOPT_H
+
+#ifndef SGETOPTNOSHORT
+#define getopt sgetoptmine
+#define optarg subgetoptarg
+#define optind subgetoptind
+#define optpos subgetoptpos
+#define opterr sgetopterr
+#define optproblem subgetoptproblem
+#define optprogname sgetoptprogname
+#define opteof subgetoptdone
+#endif
+
+#include "subgetopt.h"
+
+extern int sgetoptmine();
+extern int sgetopterr;
+extern char *sgetoptprogname;
+
+#endif
--- /dev/null
+#ifndef SIG_H
+#define SIG_H
+
+extern void sig_catch();
+extern void sig_block();
+extern void sig_unblock();
+extern void sig_blocknone();
+extern void sig_pause();
+
+extern void sig_dfl();
+
+extern void sig_miscignore();
+extern void sig_bugcatch();
+
+extern void sig_pipeignore();
+extern void sig_pipedefault();
+
+extern void sig_contblock();
+extern void sig_contunblock();
+extern void sig_contcatch();
+extern void sig_contdefault();
+
+extern void sig_termblock();
+extern void sig_termunblock();
+extern void sig_termcatch();
+extern void sig_termdefault();
+
+extern void sig_alarmblock();
+extern void sig_alarmunblock();
+extern void sig_alarmcatch();
+extern void sig_alarmdefault();
+
+extern void sig_childblock();
+extern void sig_childunblock();
+extern void sig_childcatch();
+extern void sig_childdefault();
+
+extern void sig_hangupblock();
+extern void sig_hangupunblock();
+extern void sig_hangupcatch();
+extern void sig_hangupdefault();
+
+#endif
--- /dev/null
+sig_catch.o
+sig_pipe.o
--- /dev/null
+#include <signal.h>
+#include "sig.h"
+#include "hassgact.h"
+
+void sig_catch(sig,f)
+int sig;
+void (*f)();
+{
+#ifdef HASSIGACTION
+ struct sigaction sa;
+ sa.sa_handler = f;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaction(sig,&sa,(struct sigaction *) 0);
+#else
+ signal(sig,f); /* won't work under System V, even nowadays---dorks */
+#endif
+}
--- /dev/null
+#include <signal.h>
+#include "sig.h"
+
+void sig_pipeignore() { sig_catch(SIGPIPE,SIG_IGN); }
+void sig_pipedefault() { sig_catch(SIGPIPE,SIG_DFL); }
--- /dev/null
+#include "stralloc.h"
+#include "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;
+}
--- /dev/null
+#ifndef SLURP_H
+#define SLURP_H
+
+extern int slurp();
+
+#endif
--- /dev/null
+#include "stralloc.h"
+#include "readwrite.h"
+#include "slurpclose.h"
+#include "error.h"
+
+int slurpclose(fd,sa,bufsize)
+int fd;
+stralloc *sa;
+int bufsize;
+{
+ int r;
+ for (;;) {
+ if (!stralloc_readyplus(sa,bufsize)) { close(fd); return -1; }
+ r = read(fd,sa->s + sa->len,bufsize);
+ if (r == -1) if (errno == error_intr) continue;
+ if (r <= 0) { close(fd); return r; }
+ sa->len += r;
+ }
+}
--- /dev/null
+#ifndef SLURPCLOSE_H
+#define SLURPCLOSE_H
+
+extern int slurpclose();
+
+#endif
--- /dev/null
+#ifndef STR_H
+#define STR_H
+
+extern unsigned int str_copy();
+extern int str_diff();
+extern int str_diffn();
+extern unsigned int str_len();
+extern unsigned int str_chr();
+extern unsigned int str_rchr();
+extern int str_start();
+
+#define str_equal(s,t) (!str_diff((s),(t)))
+
+#endif
--- /dev/null
+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
--- /dev/null
+#include "str.h"
+
+unsigned int str_chr(s,c)
+register char *s;
+int c;
+{
+ register char ch;
+ register char *t;
+
+ ch = c;
+ t = s;
+ for (;;) {
+ if (!*t) break; if (*t == ch) break; ++t;
+ if (!*t) break; if (*t == ch) break; ++t;
+ if (!*t) break; if (*t == ch) break; ++t;
+ if (!*t) break; if (*t == ch) break; ++t;
+ }
+ return t - s;
+}
--- /dev/null
+#include "str.h"
+
+unsigned int str_copy(s,t)
+register char *s;
+register char *t;
+{
+ register int len;
+
+ len = 0;
+ for (;;) {
+ if (!(*s = *t)) return len; ++s; ++t; ++len;
+ if (!(*s = *t)) return len; ++s; ++t; ++len;
+ if (!(*s = *t)) return len; ++s; ++t; ++len;
+ if (!(*s = *t)) return len; ++s; ++t; ++len;
+ }
+}
--- /dev/null
+#include "str.h"
+
+int str_diff(s,t)
+register char *s;
+register char *t;
+{
+ register char x;
+
+ for (;;) {
+ x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+ x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+ x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+ x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+ }
+ return ((int)(unsigned int)(unsigned char) x)
+ - ((int)(unsigned int)(unsigned char) *t);
+}
--- /dev/null
+#include "str.h"
+
+int str_diffn(s,t,len)
+register char *s;
+register char *t;
+unsigned int len;
+{
+ register char x;
+
+ for (;;) {
+ if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+ if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+ if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+ if (!len--) return 0; x = *s; if (x != *t) break; if (!x) break; ++s; ++t;
+ }
+ return ((int)(unsigned int)(unsigned char) x)
+ - ((int)(unsigned int)(unsigned char) *t);
+}
--- /dev/null
+#include "str.h"
+
+unsigned int str_len(s)
+register char *s;
+{
+ register char *t;
+
+ t = s;
+ for (;;) {
+ if (!*t) return t - s; ++t;
+ if (!*t) return t - s; ++t;
+ if (!*t) return t - s; ++t;
+ if (!*t) return t - s; ++t;
+ }
+}
--- /dev/null
+#include "str.h"
+
+unsigned int str_rchr(s,c)
+register char *s;
+int c;
+{
+ register char ch;
+ register char *t;
+ register char *u;
+
+ ch = c;
+ t = s;
+ u = 0;
+ for (;;) {
+ if (!*t) break; if (*t == ch) u = t; ++t;
+ if (!*t) break; if (*t == ch) u = t; ++t;
+ if (!*t) break; if (*t == ch) u = t; ++t;
+ if (!*t) break; if (*t == ch) u = t; ++t;
+ }
+ if (!u) u = t;
+ return u - s;
+}
--- /dev/null
+#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;
+ }
+}
--- /dev/null
+.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)
--- /dev/null
+#ifndef STRALLOC_H
+#define STRALLOC_H
+
+#include "gen_alloc.h"
+
+GEN_ALLOC_typedef(stralloc,char,s,len,a)
+
+extern int stralloc_ready();
+extern int stralloc_readyplus();
+extern int stralloc_copy();
+extern int stralloc_cat();
+extern int stralloc_copys();
+extern int stralloc_cats();
+extern int stralloc_copyb();
+extern int stralloc_catb();
+extern int stralloc_append(); /* beware: this takes a pointer to 1 char */
+extern int stralloc_starts();
+
+#define stralloc_0(sa) stralloc_append(sa,"")
+
+#endif
--- /dev/null
+stralloc.3
--- /dev/null
+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
--- /dev/null
+#include "byte.h"
+#include "str.h"
+#include "stralloc.h"
+
+int stralloc_starts(sa,s)
+stralloc *sa;
+char *s;
+{
+ int len;
+ len = str_len(s);
+ return (sa->len >= len) && byte_equal(s,len,sa->s);
+}
--- /dev/null
+#include "byte.h"
+#include "stralloc.h"
+
+int stralloc_cat(sato,safrom)
+stralloc *sato;
+stralloc *safrom;
+{
+ return stralloc_catb(sato,safrom->s,safrom->len);
+}
--- /dev/null
+#include "stralloc.h"
+#include "byte.h"
+
+int stralloc_catb(sa,s,n)
+stralloc *sa;
+char *s;
+unsigned int n;
+{
+ if (!sa->s) return stralloc_copyb(sa,s,n);
+ if (!stralloc_readyplus(sa,n + 1)) return 0;
+ byte_copy(sa->s + sa->len,n,s);
+ sa->len += n;
+ sa->s[sa->len] = 'Z'; /* ``offensive programming'' */
+ return 1;
+}
--- /dev/null
+#include "byte.h"
+#include "str.h"
+#include "stralloc.h"
+
+int stralloc_cats(sa,s)
+stralloc *sa;
+char *s;
+{
+ return stralloc_catb(sa,s,str_len(s));
+}
--- /dev/null
+#include "byte.h"
+#include "stralloc.h"
+
+int stralloc_copy(sato,safrom)
+stralloc *sato;
+stralloc *safrom;
+{
+ return stralloc_copyb(sato,safrom->s,safrom->len);
+}
--- /dev/null
+#include "alloc.h"
+#include "stralloc.h"
+#include "gen_allocdefs.h"
+
+GEN_ALLOC_ready(stralloc,char,s,len,a,i,n,x,30,stralloc_ready)
+GEN_ALLOC_readyplus(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus)
--- /dev/null
+#include "stralloc.h"
+#include "byte.h"
+
+int stralloc_copyb(sa,s,n)
+stralloc *sa;
+char *s;
+unsigned int n;
+{
+ if (!stralloc_ready(sa,n + 1)) return 0;
+ byte_copy(sa->s,n,s);
+ sa->len = n;
+ sa->s[n] = 'Z'; /* ``offensive programming'' */
+ return 1;
+}
--- /dev/null
+#include "byte.h"
+#include "str.h"
+#include "stralloc.h"
+
+int stralloc_copys(sa,s)
+stralloc *sa;
+char *s;
+{
+ return stralloc_copyb(sa,s,str_len(s));
+}
--- /dev/null
+#include "alloc.h"
+#include "stralloc.h"
+#include "gen_allocdefs.h"
+
+GEN_ALLOC_append(stralloc,char,s,len,a,i,n,x,30,stralloc_readyplus,stralloc_append)
--- /dev/null
+#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;
+}
--- /dev/null
+#ifndef STRERR_H
+#define STRERR_H
+
+struct strerr
+ {
+ struct strerr *who;
+ char *x;
+ char *y;
+ char *z;
+ }
+;
+
+extern struct strerr strerr_sys;
+extern void strerr_sysinit();
+
+extern char *strerr();
+extern void strerr_warn();
+extern void strerr_die();
+
+#define STRERR(r,se,a) \
+{ se.who = 0; se.x = a; se.y = 0; se.z = 0; return r; }
+
+#define STRERR_SYS(r,se,a) \
+{ se.who = &strerr_sys; se.x = a; se.y = 0; se.z = 0; return r; }
+#define STRERR_SYS3(r,se,a,b,c) \
+{ se.who = &strerr_sys; se.x = a; se.y = b; se.z = c; return r; }
+
+#define strerr_warn6(x1,x2,x3,x4,x5,x6,se) \
+strerr_warn((x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) (se))
+#define strerr_warn5(x1,x2,x3,x4,x5,se) \
+strerr_warn((x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) (se))
+#define strerr_warn4(x1,x2,x3,x4,se) \
+strerr_warn((x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_warn3(x1,x2,x3,se) \
+strerr_warn((x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_warn2(x1,x2,se) \
+strerr_warn((x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_warn1(x1,se) \
+strerr_warn((x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+
+#define strerr_die6(e,x1,x2,x3,x4,x5,x6,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) (se))
+#define strerr_die5(e,x1,x2,x3,x4,x5,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) (se))
+#define strerr_die4(e,x1,x2,x3,x4,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_die3(e,x1,x2,x3,se) \
+strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_die2(e,x1,x2,se) \
+strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+#define strerr_die1(e,x1,se) \
+strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+
+#define strerr_die6sys(e,x1,x2,x3,x4,x5,x6) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),&strerr_sys)
+#define strerr_die5sys(e,x1,x2,x3,x4,x5) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,&strerr_sys)
+#define strerr_die4sys(e,x1,x2,x3,x4) \
+strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,&strerr_sys)
+#define strerr_die3sys(e,x1,x2,x3) \
+strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
+#define strerr_die2sys(e,x1,x2) \
+strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
+#define strerr_die1sys(e,x1) \
+strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
+
+#define strerr_die6x(e,x1,x2,x3,x4,x5,x6) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) 0)
+#define strerr_die5x(e,x1,x2,x3,x4,x5) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) 0)
+#define strerr_die4x(e,x1,x2,x3,x4) \
+strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) 0)
+#define strerr_die3x(e,x1,x2,x3) \
+strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
+#define strerr_die2x(e,x1,x2) \
+strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
+#define strerr_die1x(e,x1) \
+strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
+
+#endif
--- /dev/null
+strerr.o
+strerr_sys.o
+strerr_die.o
--- /dev/null
+#include "substdio.h"
+#include "subfd.h"
+#include "exit.h"
+#include "strerr.h"
+
+void strerr_warn(x1,x2,x3,x4,x5,x6,se)
+char *x1; char *x2; char *x3; char *x4; char *x5; char *x6;
+struct strerr *se;
+{
+ strerr_sysinit();
+
+ if (x1) substdio_puts(subfderr,x1);
+ if (x2) substdio_puts(subfderr,x2);
+ if (x3) substdio_puts(subfderr,x3);
+ if (x4) substdio_puts(subfderr,x4);
+ if (x5) substdio_puts(subfderr,x5);
+ if (x6) substdio_puts(subfderr,x6);
+
+ while(se) {
+ if (se->x) substdio_puts(subfderr,se->x);
+ if (se->y) substdio_puts(subfderr,se->y);
+ if (se->z) substdio_puts(subfderr,se->z);
+ se = se->who;
+ }
+
+ substdio_puts(subfderr,"\n");
+ substdio_flush(subfderr);
+}
+
+void strerr_die(e,x1,x2,x3,x4,x5,x6,se)
+int e;
+char *x1; char *x2; char *x3; char *x4; char *x5; char *x6;
+struct strerr *se;
+{
+ strerr_warn(x1,x2,x3,x4,x5,x6,se);
+ _exit(e);
+}
--- /dev/null
+#include "error.h"
+#include "strerr.h"
+
+struct strerr strerr_sys;
+
+void strerr_sysinit()
+{
+ strerr_sys.who = 0;
+ strerr_sys.x = error_str(errno);
+ strerr_sys.y = "";
+ strerr_sys.z = "";
+}
--- /dev/null
+.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)
--- /dev/null
+#ifndef SUBFD_H
+#define SUBFD_H
+
+#include "substdio.h"
+
+extern substdio *subfdin;
+extern substdio *subfdinsmall;
+extern substdio *subfdout;
+extern substdio *subfdoutsmall;
+extern substdio *subfderr;
+
+extern int subfd_read();
+extern int subfd_readsmall();
+
+#endif
--- /dev/null
+#include "readwrite.h"
+#include "substdio.h"
+#include "subfd.h"
+
+char subfd_errbuf[256];
+static substdio it = SUBSTDIO_FDBUF(write,2,subfd_errbuf,256);
+substdio *subfderr = ⁢
--- /dev/null
+.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.
--- /dev/null
+/* subgetopt.c, subgetopt.h: (yet another) improved getopt clone, inner layer
+D. J. Bernstein, djb@pobox.com.
+No dependencies.
+No system requirements.
+19970228: Cleanups.
+931129: Adapted from getopt.c.
+No known patent problems.
+
+Documentation in subgetopt.3.
+*/
+
+#define SUBGETOPTNOSHORT
+#include "subgetopt.h"
+
+#define sgopt subgetopt
+#define optind subgetoptind
+#define optpos subgetoptpos
+#define optarg subgetoptarg
+#define optproblem subgetoptproblem
+#define optdone subgetoptdone
+
+int optind = 1;
+int optpos = 0;
+char *optarg = 0;
+int optproblem = 0;
+int optdone = SUBGETOPTDONE;
+
+int sgopt(argc,argv,opts)
+int argc;
+char **argv;
+char *opts;
+{
+ int c;
+ char *s;
+
+ optarg = 0;
+ if (!argv || (optind >= argc) || !argv[optind]) return optdone;
+ if (optpos && !argv[optind][optpos]) {
+ ++optind;
+ optpos = 0;
+ if ((optind >= argc) || !argv[optind]) return optdone;
+ }
+ if (!optpos) {
+ if (argv[optind][0] != '-') return optdone;
+ ++optpos;
+ c = argv[optind][1];
+ if ((c == '-') || (c == 0)) {
+ if (c) ++optind;
+ optpos = 0;
+ return optdone;
+ }
+ /* otherwise c is reassigned below */
+ }
+ c = argv[optind][optpos];
+ ++optpos;
+ s = opts;
+ while (*s) {
+ if (c == *s) {
+ if (s[1] == ':') {
+ optarg = argv[optind] + optpos;
+ ++optind;
+ optpos = 0;
+ if (!*optarg) {
+ optarg = argv[optind];
+ if ((optind >= argc) || !optarg) { /* argument past end */
+ optproblem = c;
+ return '?';
+ }
+ ++optind;
+ }
+ }
+ return c;
+ }
+ ++s;
+ if (*s == ':') ++s;
+ }
+ optproblem = c;
+ return '?';
+}
--- /dev/null
+#ifndef SUBGETOPT_H
+#define SUBGETOPT_H
+
+#ifndef SUBGETOPTNOSHORT
+#define sgopt subgetopt
+#define sgoptarg subgetoptarg
+#define sgoptind subgetoptind
+#define sgoptpos subgetoptpos
+#define sgoptproblem subgetoptproblem
+#define sgoptprogname subgetoptprogname
+#define sgoptdone subgetoptdone
+#endif
+
+#define SUBGETOPTDONE -1
+
+extern int subgetopt();
+extern char *subgetoptarg;
+extern int subgetoptind;
+extern int subgetoptpos;
+extern int subgetoptproblem;
+extern char *subgetoptprogname;
+extern int subgetoptdone;
+
+#endif
--- /dev/null
+subgetopt.3
--- /dev/null
+#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;
+}
--- /dev/null
+#ifndef SUBSCRIBE_H
+#define SUBSCRIBE_H
+
+#include "strerr.h"
+
+extern struct strerr subscribe_err;
+
+extern int subscribe();
+
+#endif
--- /dev/null
+#include "substdio.h"
+#include "byte.h"
+#include "error.h"
+
+static int oneread(op,fd,buf,len)
+register int (*op)();
+register int fd;
+register char *buf;
+register int len;
+{
+ register int r;
+
+ for (;;) {
+ r = op(fd,buf,len);
+ if (r == -1) if (errno == error_intr) continue;
+ return r;
+ }
+}
+
+static int getthis(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+ register int r;
+ register int q;
+
+ r = s->p;
+ q = r - len;
+ if (q > 0) { r = len; s->p = q; } else s->p = 0;
+ byte_copy(buf,r,s->x + s->n);
+ s->n += r;
+ return r;
+}
+
+int substdio_feed(s)
+register substdio *s;
+{
+ register int r;
+ register int q;
+
+ if (s->p) return s->p;
+ q = s->n;
+ r = oneread(s->op,s->fd,s->x,q);
+ if (r <= 0) return r;
+ s->p = r;
+ q -= r;
+ s->n = q;
+ if (q > 0) /* damn, gotta shift */ byte_copyr(s->x + q,r,s->x);
+ return r;
+}
+
+int substdio_bget(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+ register int r;
+
+ if (s->p > 0) return getthis(s,buf,len);
+ r = s->n; if (r <= len) return oneread(s->op,s->fd,buf,r);
+ r = substdio_feed(s); if (r <= 0) return r;
+ return getthis(s,buf,len);
+}
+
+int substdio_get(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+ register int r;
+
+ if (s->p > 0) return getthis(s,buf,len);
+ if (s->n <= len) return oneread(s->op,s->fd,buf,len);
+ r = substdio_feed(s); if (r <= 0) return r;
+ return getthis(s,buf,len);
+}
+
+char *substdio_peek(s)
+register substdio *s;
+{
+ return s->x + s->n;
+}
+
+void substdio_seek(s,len)
+register substdio *s;
+register int len;
+{
+ s->n += len;
+ s->p -= len;
+}
--- /dev/null
+.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)
--- /dev/null
+#include "substdio.h"
+
+void substdio_fdbuf(s,op,fd,buf,len)
+register substdio *s;
+register int (*op)();
+register int fd;
+register char *buf;
+register int len;
+{
+ s->x = buf;
+ s->fd = fd;
+ s->op = op;
+ s->p = 0;
+ s->n = len;
+}
--- /dev/null
+#ifndef SUBSTDIO_H
+#define SUBSTDIO_H
+
+typedef struct substdio {
+ char *x;
+ int p;
+ int n;
+ int fd;
+ int (*op)();
+} substdio;
+
+#define SUBSTDIO_FDBUF(op,fd,buf,len) { (buf), 0, (len), (fd), (op) }
+
+extern void substdio_fdbuf();
+
+extern int substdio_flush();
+extern int substdio_put();
+extern int substdio_bput();
+extern int substdio_putflush();
+extern int substdio_puts();
+extern int substdio_bputs();
+extern int substdio_putsflush();
+
+extern int substdio_get();
+extern int substdio_bget();
+extern int substdio_feed();
+
+extern char *substdio_peek();
+extern void substdio_seek();
+
+#define substdio_fileno(s) ((s)->fd)
+
+#define SUBSTDIO_INSIZE 8192
+#define SUBSTDIO_OUTSIZE 8192
+
+#define substdio_PEEK(s) ( (s)->x + (s)->n )
+#define substdio_SEEK(s,len) ( ( (s)->p -= (len) ) , ( (s)->n += (len) ) )
+
+#define substdio_BPUTC(s,c) \
+ ( ((s)->n != (s)->p) \
+ ? ( (s)->x[(s)->p++] = (c), 0 ) \
+ : substdio_bput((s),&(c),1) \
+ )
+
+extern int substdio_copy();
+
+#endif
--- /dev/null
+substdio.3
--- /dev/null
+substdio.o
+substdi.o
+substdo.o
+subfderr.o
+substdio_copy.o
--- /dev/null
+.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)
--- /dev/null
+#include "substdio.h"
+
+int substdio_copy(ssout,ssin)
+register substdio *ssout;
+register substdio *ssin;
+{
+ register int n;
+ register char *x;
+
+ for (;;) {
+ n = substdio_feed(ssin);
+ if (n < 0) return -2;
+ if (!n) return 0;
+ x = substdio_PEEK(ssin);
+ if (substdio_put(ssout,x,n) == -1) return -3;
+ substdio_SEEK(ssin,n);
+ }
+}
--- /dev/null
+substdio_copy.3
--- /dev/null
+.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)
--- /dev/null
+substdio_in.3
--- /dev/null
+.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)
--- /dev/null
+substdio_out.3
--- /dev/null
+#include "substdio.h"
+#include "str.h"
+#include "byte.h"
+#include "error.h"
+
+static int allwrite(op,fd,buf,len)
+register int (*op)();
+register int fd;
+register char *buf;
+register int len;
+{
+ register int w;
+
+ while (len) {
+ w = op(fd,buf,len);
+ if (w == -1) {
+ if (errno == error_intr) continue;
+ return -1; /* note that some data may have been written */
+ }
+ if (w == 0) ; /* luser's fault */
+ buf += w;
+ len -= w;
+ }
+ return 0;
+}
+
+int substdio_flush(s)
+register substdio *s;
+{
+ register int p;
+
+ p = s->p;
+ if (!p) return 0;
+ s->p = 0;
+ return allwrite(s->op,s->fd,s->x,p);
+}
+
+int substdio_bput(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+ register int n;
+
+ while (len > (n = s->n - s->p)) {
+ byte_copy(s->x + s->p,n,buf); s->p += n; buf += n; len -= n;
+ if (substdio_flush(s) == -1) return -1;
+ }
+ /* now len <= s->n - s->p */
+ byte_copy(s->x + s->p,len,buf);
+ s->p += len;
+ return 0;
+}
+
+int substdio_put(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+ register int n;
+
+ n = s->n;
+ if (len > n - s->p) {
+ if (substdio_flush(s) == -1) return -1;
+ /* now s->p == 0 */
+ if (n < SUBSTDIO_OUTSIZE) n = SUBSTDIO_OUTSIZE;
+ while (len > s->n) {
+ if (n > len) n = len;
+ if (allwrite(s->op,s->fd,buf,n) == -1) return -1;
+ buf += n;
+ len -= n;
+ }
+ }
+ /* now len <= s->n - s->p */
+ byte_copy(s->x + s->p,len,buf);
+ s->p += len;
+ return 0;
+}
+
+int substdio_putflush(s,buf,len)
+register substdio *s;
+register char *buf;
+register int len;
+{
+ if (substdio_flush(s) == -1) return -1;
+ return allwrite(s->op,s->fd,buf,len);
+}
+
+int substdio_bputs(s,buf)
+register substdio *s;
+register char *buf;
+{
+ return substdio_bput(s,buf,str_len(buf));
+}
+
+int substdio_puts(s,buf)
+register substdio *s;
+register char *buf;
+{
+ return substdio_put(s,buf,str_len(buf));
+}
+
+int substdio_putsflush(s,buf)
+register substdio *s;
+register char *buf;
+{
+ return substdio_putflush(s,buf,str_len(buf));
+}
--- /dev/null
+.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)
--- /dev/null
+/* 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];
+ }
+}
--- /dev/null
+#ifndef SURF_H
+#define SURF_H
+
+extern void surf();
+
+#endif
--- /dev/null
+surf.o
+surfpcs.o
--- /dev/null
+.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)
--- /dev/null
+/* 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]];
+}
--- /dev/null
+#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
--- /dev/null
+dependon it man install conf-bin conf-man
--- /dev/null
+void main()
+{
+#ifdef NeXT
+ printf("nextstep\n"); exit(0);
+#endif
+ printf("unknown\n"); exit(0);
+}
--- /dev/null
+#include <sys/types.h>
+#include <dirent.h>
+
+void foo()
+{
+ DIR *dir;
+ struct dirent *d;
+}
--- /dev/null
+#include <sys/types.h>
+#include <sys/file.h>
+#include <fcntl.h>
+
+void main()
+{
+ flock(0,LOCK_EX | LOCK_UN | LOCK_NB);
+}
--- /dev/null
+#include <signal.h>
+
+void main()
+{
+ struct sigaction sa;
+ sa.sa_handler = 0;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaction(0,&sa,(struct sigaction *) 0);
+}
--- /dev/null
+void main()
+{
+ unsigned long u;
+ u = 1;
+ u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+ u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+ u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+ u += u; u += u; u += u; u += u; u += u; u += u; u += u; u += u;
+ if (!u) _exit(0);
+ _exit(1);
+}
--- /dev/null
+void main()
+{
+ vfork();
+}
--- /dev/null
+#include <sys/types.h>
+#include <sys/wait.h>
+
+void main()
+{
+ waitpid(0,0,0);
+}
--- /dev/null
+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'
--- /dev/null
+#ifndef UINT32_H
+#define UINT32_H
+
+typedef unsigned int uint32;
+
+#endif
--- /dev/null
+#ifndef UINT32_H
+#define UINT32_H
+
+typedef unsigned long uint32;
+
+#endif
--- /dev/null
+.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)
--- /dev/null
+#ifndef WAIT_H
+#define WAIT_H
+
+extern int wait_pid();
+extern int wait_nohang();
+extern int wait_stop();
+extern int wait_stopnohang();
+
+#define wait_crashed(w) ((w) & 127)
+#define wait_exitcode(w) ((w) >> 8)
+#define wait_stopsig(w) ((w) >> 8)
+#define wait_stopped(w) (((w) & 127) == 127)
+
+#endif
--- /dev/null
+wait_pid.o
--- /dev/null
+#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;
+}
--- /dev/null
+#!/bin/sh
+# WARNING: This file was auto-generated. Do not edit!