about summary refs log tree commit diff stats
path: root/WWW
diff options
context:
space:
mode:
authorThomas E. Dickey <dickey@invisible-island.net>1996-09-02 19:39:24 -0400
committerThomas E. Dickey <dickey@invisible-island.net>1996-09-02 19:39:24 -0400
commite087f6d44e87f489fcb3056e86319ebba4218156 (patch)
treed045b58011bfbbf5186d34c4fed9e0dedb363275 /WWW
downloadlynx-snapshots-e087f6d44e87f489fcb3056e86319ebba4218156.tar.gz
snapshot of project "lynx", label v2_6
Diffstat (limited to 'WWW')
-rw-r--r--WWW/BUILD42
-rw-r--r--WWW/Copyright.txt22
-rw-r--r--WWW/Library/Implementation/BSDI_Makefile413
-rw-r--r--WWW/Library/Implementation/CommonMakefile376
-rw-r--r--WWW/Library/Implementation/HTAABrow.c1051
-rw-r--r--WWW/Library/Implementation/HTAABrow.h138
-rw-r--r--WWW/Library/Implementation/HTAAFile.c210
-rw-r--r--WWW/Library/Implementation/HTAAFile.h126
-rw-r--r--WWW/Library/Implementation/HTAAProt.c592
-rw-r--r--WWW/Library/Implementation/HTAAProt.h231
-rw-r--r--WWW/Library/Implementation/HTAAServ.c686
-rw-r--r--WWW/Library/Implementation/HTAAServ.h146
-rw-r--r--WWW/Library/Implementation/HTAAUtil.c640
-rw-r--r--WWW/Library/Implementation/HTAAUtil.h361
-rw-r--r--WWW/Library/Implementation/HTACL.c221
-rw-r--r--WWW/Library/Implementation/HTACL.h110
-rw-r--r--WWW/Library/Implementation/HTAccess.c1199
-rw-r--r--WWW/Library/Implementation/HTAccess.h306
-rw-r--r--WWW/Library/Implementation/HTAlert.c125
-rw-r--r--WWW/Library/Implementation/HTAlert.h86
-rw-r--r--WWW/Library/Implementation/HTAnchor.c999
-rw-r--r--WWW/Library/Implementation/HTAnchor.h358
-rw-r--r--WWW/Library/Implementation/HTAssoc.c87
-rw-r--r--WWW/Library/Implementation/HTAssoc.h44
-rw-r--r--WWW/Library/Implementation/HTAtom.c169
-rw-r--r--WWW/Library/Implementation/HTAtom.h49
-rw-r--r--WWW/Library/Implementation/HTAuth.c210
-rw-r--r--WWW/Library/Implementation/HTAuth.h66
-rw-r--r--WWW/Library/Implementation/HTBTree.c720
-rw-r--r--WWW/Library/Implementation/HTBTree.h104
-rw-r--r--WWW/Library/Implementation/HTCJK.h110
-rw-r--r--WWW/Library/Implementation/HTChunk.c100
-rw-r--r--WWW/Library/Implementation/HTChunk.h160
-rw-r--r--WWW/Library/Implementation/HTFTP.c3214
-rw-r--r--WWW/Library/Implementation/HTFTP.h72
-rw-r--r--WWW/Library/Implementation/HTFWriter.c358
-rw-r--r--WWW/Library/Implementation/HTFWriter.h37
-rw-r--r--WWW/Library/Implementation/HTFile.c1871
-rw-r--r--WWW/Library/Implementation/HTFile.h173
-rw-r--r--WWW/Library/Implementation/HTFinger.c438
-rw-r--r--WWW/Library/Implementation/HTFinger.h24
-rw-r--r--WWW/Library/Implementation/HTFormat.c836
-rw-r--r--WWW/Library/Implementation/HTFormat.h394
-rw-r--r--WWW/Library/Implementation/HTGopher.c2008
-rw-r--r--WWW/Library/Implementation/HTGopher.h27
-rw-r--r--WWW/Library/Implementation/HTGroup.c772
-rw-r--r--WWW/Library/Implementation/HTGroup.h189
-rw-r--r--WWW/Library/Implementation/HTHistory.c157
-rw-r--r--WWW/Library/Implementation/HTHistory.h112
-rw-r--r--WWW/Library/Implementation/HTInit.c176
-rw-r--r--WWW/Library/Implementation/HTInit.h22
-rw-r--r--WWW/Library/Implementation/HTLex.c142
-rw-r--r--WWW/Library/Implementation/HTLex.h64
-rw-r--r--WWW/Library/Implementation/HTList.c262
-rw-r--r--WWW/Library/Implementation/HTList.h136
-rw-r--r--WWW/Library/Implementation/HTMIME.c2009
-rw-r--r--WWW/Library/Implementation/HTMIME.h77
-rw-r--r--WWW/Library/Implementation/HTML.h79
-rw-r--r--WWW/Library/Implementation/HTMLDTD.c1122
-rw-r--r--WWW/Library/Implementation/HTMLDTD.h890
-rw-r--r--WWW/Library/Implementation/HTMLGen.c367
-rw-r--r--WWW/Library/Implementation/HTMLGen.h32
-rw-r--r--WWW/Library/Implementation/HTNews.c1646
-rw-r--r--WWW/Library/Implementation/HTNews.h35
-rw-r--r--WWW/Library/Implementation/HTParse.c606
-rw-r--r--WWW/Library/Implementation/HTParse.h159
-rw-r--r--WWW/Library/Implementation/HTPasswd.c301
-rw-r--r--WWW/Library/Implementation/HTPasswd.h129
-rw-r--r--WWW/Library/Implementation/HTPlain.c311
-rw-r--r--WWW/Library/Implementation/HTPlain.h21
-rw-r--r--WWW/Library/Implementation/HTRules.c418
-rw-r--r--WWW/Library/Implementation/HTRules.h165
-rw-r--r--WWW/Library/Implementation/HTStream.h61
-rw-r--r--WWW/Library/Implementation/HTString.c156
-rw-r--r--WWW/Library/Implementation/HTString.h51
-rw-r--r--WWW/Library/Implementation/HTStyle.c363
-rw-r--r--WWW/Library/Implementation/HTStyle.h183
-rw-r--r--WWW/Library/Implementation/HTTCP.c955
-rw-r--r--WWW/Library/Implementation/HTTCP.h118
-rw-r--r--WWW/Library/Implementation/HTTP.c1309
-rw-r--r--WWW/Library/Implementation/HTTP.h28
-rw-r--r--WWW/Library/Implementation/HTTelnet.c579
-rw-r--r--WWW/Library/Implementation/HTTelnet.h24
-rw-r--r--WWW/Library/Implementation/HTUU.c207
-rw-r--r--WWW/Library/Implementation/HTUU.h29
-rw-r--r--WWW/Library/Implementation/HTUtils.h311
-rw-r--r--WWW/Library/Implementation/HTVMSUtils.c1220
-rw-r--r--WWW/Library/Implementation/HTVMSUtils.h116
-rw-r--r--WWW/Library/Implementation/HTVMS_WaisProt.c2501
-rw-r--r--WWW/Library/Implementation/HTVMS_WaisProt.h376
-rw-r--r--WWW/Library/Implementation/HTVMS_WaisUI.c2448
-rw-r--r--WWW/Library/Implementation/HTVMS_WaisUI.h674
-rw-r--r--WWW/Library/Implementation/HTWAIS.c1103
-rw-r--r--WWW/Library/Implementation/HTWAIS.h44
-rw-r--r--WWW/Library/Implementation/HTWSRC.c478
-rw-r--r--WWW/Library/Implementation/HTWSRC.h46
-rw-r--r--WWW/Library/Implementation/HTWriter.c189
-rw-r--r--WWW/Library/Implementation/HTWriter.h28
-rw-r--r--WWW/Library/Implementation/HText.h262
-rw-r--r--WWW/Library/Implementation/LYLeaks.h149
-rw-r--r--WWW/Library/Implementation/LYexit.h54
-rw-r--r--WWW/Library/Implementation/Makefile489
-rw-r--r--WWW/Library/Implementation/SGML.c2145
-rw-r--r--WWW/Library/Implementation/SGML.h202
-rw-r--r--WWW/Library/Implementation/Version.make1
-rw-r--r--WWW/Library/Implementation/crypt.c129
-rw-r--r--WWW/Library/Implementation/crypt_util.c981
-rw-r--r--WWW/Library/Implementation/getline.c74
-rw-r--r--WWW/Library/Implementation/getpass.c64
-rw-r--r--WWW/Library/Implementation/patchlevel.h24
-rw-r--r--WWW/Library/Implementation/tcp.h524
-rw-r--r--WWW/Library/Implementation/ufc-crypt.h108
-rw-r--r--WWW/Library/apollo_m68k/Makefile38
-rw-r--r--WWW/Library/clix/Makefile30
-rw-r--r--WWW/Library/convex/Makefile32
-rw-r--r--WWW/Library/decstation/Makefile23
-rw-r--r--WWW/Library/duns/Makefile489
-rw-r--r--WWW/Library/freebsd/Makefile27
-rw-r--r--WWW/Library/isc/Makefile30
-rw-r--r--WWW/Library/mips/Makefile29
-rw-r--r--WWW/Library/netbsd/Makefile29
-rw-r--r--WWW/Library/next/Makefile40
-rw-r--r--WWW/Library/osf/Makefile23
-rw-r--r--WWW/Library/ptx/Makefile29
-rw-r--r--WWW/Library/rs6000/Makefile29
-rw-r--r--WWW/Library/sco/Makefile33
-rw-r--r--WWW/Library/sgi/Makefile30
-rw-r--r--WWW/Library/snake/Makefile33
-rw-r--r--WWW/Library/solaris2/Makefile29
-rw-r--r--WWW/Library/sun3/Makefile29
-rw-r--r--WWW/Library/sun4/Makefile29
-rw-r--r--WWW/Library/svr4/Makefile29
-rw-r--r--WWW/Library/unix/Makefile30
-rw-r--r--WWW/Library/unix_x/Makefile491
-rw-r--r--WWW/Library/vax_ultrix/Makefile33
-rw-r--r--WWW/Library/vms/COPYING.LIB481
-rw-r--r--WWW/Library/vms/descrip.mms258
-rw-r--r--WWW/Library/vms/libmake.com186
-rw-r--r--WWW/Makefile9
-rw-r--r--WWW/README.txt208
140 files changed, 51667 insertions, 0 deletions
diff --git a/WWW/BUILD b/WWW/BUILD
new file mode 100644
index 00000000..5fda9e0e
--- /dev/null
+++ b/WWW/BUILD
@@ -0,0 +1,42 @@
+#! /bin/csh
+#			Build all WWW Code for this platform
+#
+#	Figure out what sort of unix this is
+#	(NeXT machines don't have uname!)
+
+set UNAME=NeXT
+if (-e /usr/bin/uname) set UNAME=`/usr/bin/uname`
+if (-e /bin/uname) set UNAME=`/bin/uname`
+if (-e /usr/apollo/bin) set UNAME=`ver sys5.3 /bin/uname`
+if ( $UNAME == "" ) then
+    if (-r /NextApps ) set UNAME=next
+endif
+#
+setenv UNAME $UNAME
+# For apollo, must use bsd mode. Also, WWW_MACH not inherited through make!
+if ($UNAME == "DomainOS")    setenv WWW_MACH	apollo_m68k
+if ($UNAME == next)	setenv WWW_MACH 	next
+if ($UNAME == "HP-UX")	setenv WWW_MACH 	snake
+if ($UNAME == "IRIX")	setenv WWW_MACH 	sgi
+if ($UNAME == "SunOS")	setenv WWW_MACH		sun4
+if ($UNAME == "ULTRIX")	setenv WWW_MACH		decstation
+if ($UNAME == "AIX")    setenv WWW_MACH		rs6000
+if ($UNAME == "OSF1")   setenv WWW_MACH		osf1
+
+if ($WWW_MACH == "") then
+    echo "Please edit BUILD file to include your machine OS"
+    echo "and mail differences back to www-bug@info.cern.ch
+    exit -99
+endif
+echo "________________________________________________________________"
+echo "WWW build for machine type:                            " $WWW_MACH
+
+#	Now go do build
+
+#	We don't want SHELL set to something funny to screw up make
+
+(cd All/Implementation; unsetenv SHELL; make)
+set stat = $status
+echo
+echo "WWW build for " $WWW_MACH  " done. status = " $stat
+exit $stat
diff --git a/WWW/Copyright.txt b/WWW/Copyright.txt
new file mode 100644
index 00000000..3d7397bb
--- /dev/null
+++ b/WWW/Copyright.txt
@@ -0,0 +1,22 @@
+                                                        Copyright -- /hypertext
+                           COPYRIGHT CERN 1990-1993
+                                       
+   Except where specifically placed in the public domain, the information (of
+   all forms) in these directories is the intellectual property of the European
+   Laboratory for Particle Physics (known as CERN). No guarantee whatsoever is
+   provided by CERN. No liability whatsoever is accepted for any loss or damage
+   of any kind resulting from any defect or inaccuracy in this information or
+   code.
+   
+   The conditions for public domain and other access to the code are defined in
+    distribution conditions of WWW code[1]
+   
+   Tim Berners-Lee[2]
+   
+   CERN
+   
+   1211 Geneva 23, Switzerland
+   
+           Tel +41(22)767 3755, Fax +41(22)767 7155, Email: tbl@cernvax.cern.ch
+                                                                               
+   
diff --git a/WWW/Library/Implementation/BSDI_Makefile b/WWW/Library/Implementation/BSDI_Makefile
new file mode 100644
index 00000000..65653ca3
--- /dev/null
+++ b/WWW/Library/Implementation/BSDI_Makefile
@@ -0,0 +1,413 @@
+#  Make WWW under svr4
+#
+
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = svr4
+
+# The ASIS repository's name for the machine we are on
+ASIS_MACH = generic/svr4
+
+
+CFLAGS =  -O -DDEBUG -DUSE_DIRENT -DSVR4 -DNO_FILIO_H
+LFLAGS =
+CC = cc
+
+# Directory for installed binary:
+BINDIR = /usr/local/bin
+
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = ../..
+
+
+# Where is the W3 object library?
+LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH)
+
+#	Common Makefile for W3 Library Code
+#	-----------------------------------
+#
+#	(c) CERN 1990, 1991 -- see Copyright.html for conditions
+#
+# This file should be invariant between systems.
+#	DEPENDENCIES NOT COMPLETE @@
+#
+#	make		Compile and link the software (private version)
+#	make install	Copy it into the system (implies make)
+#	make update	Copy installed version into installed version
+#	make uninstall	Unlink installed version from the system
+#	make clean	Remove intermediate files
+#	make cleanall	Remove intremediate files and products
+#	
+# Macros required to be defined already for make:
+#
+# CC		The C compiler
+# CFLAGS	Flags for $(CC) -- except the -I which are below
+# LFLAGS	Flags for ld
+# LYFLAGS	Flags for Lynx
+#
+# WWW           The WWW source tree directory
+#
+# Macros needed for make install:
+#
+# LIBDIR	Directory for installed library
+#______________________________________________________________________
+
+#  If this env var is set to something else Some makes will use that instead
+SHELL = /bin/sh
+
+#	.h files are distributed but originally are made from the
+#	self-documenting hypertext files.
+
+.SUFFIXES: .h .html
+.html.h:
+#	- chmod +w $*.h
+	www -w90 -na -to text/x-c $*.html > $*.h
+#	chmod -w $*.h
+
+# If this is actually run in a subdirectory,
+#
+# WWW = ../../..
+# WWW = ../..	For [cernlib] build in this directory
+
+WC = $(WWW)/Library
+CMN = $(WWW)/Library/Implementation/
+VMS = $(CMN)vms
+# Where shall we put the objects and built library?
+
+LOB = $(WTMP)/Library/$(WWW_MACH)
+
+# Only needed if HTWAIS.c is to be compiled. Put into your Makefile.include
+#  uncomment these and fill in WAISINC for adding direct wais access
+#  to Lynx.
+#HTWAIS = $(LOB)/HTWAIS.o
+#WAIS = YES
+#WAISINC = -I../../../../freeWAIS-0.202/ir
+#WAISCFLAGS = -DDIRECT_WAIS 
+#
+
+# This path, if relative, is taken relative to the directory
+# in which this makefile is, not the pwd.  This screws up the 
+# recursive invocation
+# include $(CMN)Version.make
+VC = 2.14
+
+# XMOsAIC hack is only for server to cope with xmosaic kludge for mmedia
+#
+# add -DNEW_GATEWAY here for the new gateway config stuff
+CFLAGS2 = $(CFLAGS) $(LYFLAGS) $(WAISCFLAGS) -I$(CMN) -DXMOSAIC_HACK -DACCESS_AUTH 
+
+CERNLIBBIN = $(WWW)/bin
+
+COMMON = $(LOB)/HTParse.o $(LOB)/HTAccess.o $(LOB)/HTTP.o \
+	$(LOB)/HTFile.o	$(LOB)/HTBTree.o $(LOB)/HTFTP.o $(LOB)/HTTCP.o \
+	$(LOB)/SGML.o $(LOB)/HTMLDTD.o $(LOB)/HTChunk.o \
+	$(LOB)/HTPlain.o $(LOB)/HTWriter.o $(LOB)/HTFWriter.o \
+	$(LOB)/HTMLGen.o \
+	$(LOB)/HTAtom.o $(LOB)/HTAnchor.o $(LOB)/HTStyle.o \
+	$(LOB)/HTList.o $(LOB)/HTString.o $(LOB)/HTAlert.o \
+	$(LOB)/HTRules.o $(LOB)/HTFormat.o $(LOB)/HTInit.o $(LOB)/HTMIME.o \
+	$(LOB)/HTHistory.o $(LOB)/HTNews.o $(LOB)/HTGopher.o \
+	$(LOB)/HTTelnet.o $(LOB)/HTFinger.o $(LOB)/HTWSRC.o $(HTWAIS) \
+	$(LOB)/HTAAUtil.o $(LOB)/HTAAServ.o $(LOB)/HTAABrow.o \
+	$(LOB)/HTAAFile.o $(LOB)/HTPasswd.o $(LOB)/HTGroup.o  \
+	$(LOB)/HTACL.o    $(LOB)/HTAuth.o   $(LOB)/HTAAProt.o \
+	$(LOB)/HTAssoc.o  $(LOB)/HTLex.o    $(LOB)/HTUU.o
+
+CFILES = $(CMN)HTParse.c $(CMN)HTAccess.c $(CMN)HTTP.c $(CMN)HTFile.c \
+        $(CMN)HTBTree.c \
+	$(CMN)HTFTP.c   $(CMN)HTTCP.c     $(CMN)SGML.c 	\
+	$(CMN)HTMLDTD.c \
+	$(CMN)HTPlain.c	$(CMN)HTWriter.c  $(CMN)HTFWriter.c \
+        $(CMN)HTMLGen.c	\
+	$(CMN)HTChunk.c $(CMN)HTAtom.c   $(CMN)HTAnchor.c $(CMN)HTStyle.c \
+	$(CMN)HTList.c  $(CMN)HTString.c $(CMN)HTAlert.c $(CMN)HTRules.c \
+	$(CMN)HTFormat.c $(CMN)HTInit.c $(CMN)HTMIME.c $(CMN)HTHistory.c \
+	$(CMN)HTNews.c  $(CMN)HTGopher.c $(CMN)HTTelnet.c \
+	$(CMN)HTFinger.c $(CMN)HTWAIS.c  $(CMN)HTWSRC.c \
+	$(CMN)HTAAUtil.c $(CMN)HTAAServ.c $(CMN)HTAABrow.c \
+	$(CMN)HTAAFile.c $(CMN)HTPasswd.c $(CMN)HTGroup.c  \
+	$(CMN)HTACL.c    $(CMN)HTAuth.c   $(CMN)HTAAProt.c \
+	$(CMN)HTAssoc.c  $(CMN)HTLex.c    $(CMN)HTUU.c
+
+HFILES = $(CMN)HTParse.h $(CMN)HTAccess.h $(CMN)HTTP.h $(CMN)HTFile.h \
+	$(CMN)HTBTree.h $(CMN)HTFTP.h $(CMN)HTTCP.h \
+	$(CMN)SGML.h $(CMN)HTML.h $(CMN)HTMLDTD.h $(CMN)HTChunk.h \
+	$(CMN)HTPlain.h		$(CMN)HTWriter.h \
+	$(CMN)HTFWriter.h 	$(CMN)HTMLGen.h	\
+	$(CMN)HTStream.h \
+	$(CMN)HTAtom.h $(CMN)HTAnchor.h $(CMN)HTStyle.h \
+	$(CMN)HTList.h \
+	$(CMN)HTString.h $(CMN)HTAlert.h $(CMN)HTRules.h \
+	$(CMN)HTFormat.h $(CMN)HTInit.h \
+	$(CMN)HTMIME.h $(CMN)HTHistory.h $(CMN)HTNews.h \
+	$(CMN)HTGopher.h \
+	$(CMN)HTUtils.h $(CMN)tcp.h $(CMN)HText.h \
+	$(CMN)HTTelnet.h $(CMN)HTFinger.h \
+	$(CMN)HTWAIS.h  $(CMN)HTWSRC.h \
+	$(CMN)HTAAUtil.h $(CMN)HTAAServ.h $(CMN)HTAABrow.h \
+	$(CMN)HTAAFile.h $(CMN)HTPasswd.h $(CMN)HTGroup.h  \
+	$(CMN)HTACL.h    $(CMN)HTAuth.h   $(CMN)HTAAProt.h \
+	$(CMN)HTAssoc.h  $(CMN)HTLex.h    $(CMN)HTUU.h
+
+SOURCES = $(CFILES) $(HFILES) $(CMN)Version.make \
+	$(CMN)CommonMakefile $(CMN)Makefile \
+	$(WWW)/README.txt $(WWW)/Copyright.txt $(WWW)/BUILD $(WWW)/Makefile
+SPECIFIC = $(WWW)/All/*/Makefile.include $(WWW)/All/Implementation/Makefile* \
+	$(VMS)/descrip.mms $(VMS)/build_multinet.com \
+	$(VMS)/COPYING.LIB $(VMS)/setup.com $(VMS)/multinet.opt \
+	$(VMS)/patchlevel.h $(VMS)/ufc-crypt.h \
+	$(VMS)/crypt.c $(VMS)/crypt_util.c \
+	$(VMS)/getline.c $(VMS)/getpass.c \
+	$(VMS)/HTVMSUtils.h $(VMS)/HTVMSUtils.c
+
+
+#	Library
+#
+#  On SGI, ranlib is unnecessary and does not exist so we ignore errors
+# for that step
+$(LOB)/libwww.a : $(COMMON)
+	ar r $(LOB)/libwww.a $(COMMON)
+	-ranlib $(LOB)/libwww.a
+
+#	Clean up everything generatable except final products
+clean :
+	rm $(LOB)/*.o $(LOB)/.created
+	-rmdir $(LOB)
+
+#	Clean up everything generatable including final products
+
+cleanall : clean
+	rm $(LOB)/libwww.a
+
+#	Install W3 library into system space (not normally necessary)
+
+install : libwww.a
+	if [ ! -r $(LIBDIR) ] mkdir $(LIBDIR)
+	cp libwww.a $(LIBDIR)/libwww.a
+	
+uninstall :
+	rm $(LIBDIR)/libwww.a
+
+#		Distribution use only:
+#		----------------------
+
+#	Needs www version 2.4 or later to do this
+inc : $(HFILES)
+	echo Include files generated from hypertext.
+
+binary : /pub/www/bin/$(WWW_MACH)/libwww_$(VC).a
+	echo FTP archive binary Libray $(VC) for $(WWW_MACH) up to date.
+
+
+/pub/www/bin/$(WWW_MACH)/libwww_$(VC).a : libwww.a
+	-mkdir /pub/www/bin/$(WWW_MACH)
+	cp libwww.a /pub/www/bin/$(WWW_MACH)/libwww_$(VC).a
+
+#	Source Distribution:
+
+distribute :  /pub/www/README.txt /pub/www/Copyright.txt
+	(cd $(WWW)/..; WWW=WWW ABS=`pwd`/ make $(MFLAGS) \
+		-f WWW/Library/Implementation/CommonMakefile \
+		/pub/www/src/WWWLibrary_$(VC).tar.Z)
+	(cd ../Implementation; cvs tag \
+	    `sed -e 's/VC = /v/' Version.make | sed -e 's?\.?/?'` )
+	echo Distribution of Library version $(VC) up to date.
+
+/pub/www/src/WWWLibrary_$(VC).tar.Z : $(SOURCES)
+	tar cf /pub/www/src/WWWLibrary_$(VC).tar \
+	    $(SOURCES)  $(SPECIFIC) $(WC)/*/Makefile
+	compress /pub/www/src/WWWLibrary_$(VC).tar
+
+
+#	Hypertext supplied in text format
+#	---------------------------------
+
+$(WWW)/README.txt : $(WWW)/../README.html
+	www -n -p66 http://www.w3.org/hypertext/README.html \
+		> $(WWW)/README.txt
+/pub/www/README.txt : $(WWW)/README.txt
+	cp $(WWW)/README.txt /pub/www/README.txt
+
+$(WWW)/Copyright.txt : $(WWW)/../Copyright.html
+	www -n -p66 http://www.w3.org/hypertext/Copyright.html \
+		> $(WWW)/Copyright.txt
+/pub/www/Copyright.txt : $(WWW)/Copyright.txt
+	cp $(WWW)/Copyright.txt /pub/www/Copyright.txt
+
+# 			Common code
+#			-----------
+
+#	Directory for object files - .created checks it exists
+
+OE = $(LOB)/.created
+$(OE) :
+	if [ ! -r $(WTMP) ] ; then mkdir $(WTMP); else echo OK ; fi
+	if [ ! -r $(WTMP)/Library ] ; then mkdir $(WTMP)/Library; else echo OK ; fi
+	if [ ! -r $(WTMP)/Library/$(WWW_MACH) ] ; \
+		then mkdir $(WTMP)/Library/$(WWW_MACH); else echo OK ; fi
+	touch $@
+
+$(LOB)/HTList.o : $(OE) $(CMN)HTList.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTList.c
+
+$(LOB)/HTAnchor.o : $(OE) $(CMN)HTAnchor.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAnchor.c
+
+$(LOB)/HTFormat.o : $(OE) $(CMN)HTFormat.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFormat.c
+
+$(LOB)/HTInit.o : $(OE) $(CMN)HTInit.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTInit.c
+
+$(LOB)/HTMIME.o : $(OE) $(CMN)HTMIME.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMIME.c
+
+$(LOB)/HTHistory.o : $(OE) $(CMN)HTHistory.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTHistory.c
+
+$(LOB)/HTNews.o : $(OE) $(CMN)HTNews.c $(CMN)HTUtils.h $(CMN)HTList.h\
+	 $(CMN)HTMLDTD.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTNews.c
+
+$(LOB)/HTGopher.o : $(OE) $(CMN)HTGopher.c $(CMN)HTUtils.h $(CMN)HTList.h \
+	 $(CMN)HTMLDTD.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTGopher.c
+
+$(LOB)/HTTelnet.o : $(OE) $(CMN)HTTelnet.c $(CMN)HTUtils.h $(CMN)HTTelnet.h $(CMN)../../../userdefs.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTelnet.c
+
+$(LOB)/HTFinger.o : $(OE) $(CMN)HTFinger.c $(CMN)HTUtils.h $(CMN)HTList.h \
+	$(CMN)HTMLDTD.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFinger.c
+
+$(LOB)/HTStyle.o : $(OE) $(CMN)HTStyle.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTStyle.c
+
+$(LOB)/HTAtom.o : $(OE) $(CMN)HTAtom.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAtom.c
+
+$(LOB)/HTChunk.o : $(OE) $(CMN)HTChunk.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTChunk.c
+
+$(LOB)/HTString.o : $(OE) $(CMN)HTString.c $(CMN)HTUtils.h $(CMN)Version.make
+	$(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTString.c
+
+$(LOB)/HTAlert.o : $(OE) $(CMN)HTAlert.c $(CMN)HTUtils.h $(CMN)Version.make
+	$(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTAlert.c
+
+$(LOB)/HTRules.o : $(OE) $(CMN)HTRules.c $(CMN)HTUtils.h $(CMN)Version.make \
+	 $(CMN)HTAAServ.h $(CMN)HTAAProt.h
+	$(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTRules.c
+
+$(LOB)/SGML.o : $(OE) $(CMN)SGML.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)SGML.c
+
+$(LOB)/HTMLGen.o : $(OE) $(CMN)HTMLGen.c $(CMN)HTUtils.h $(CMN)HTMLDTD.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMLGen.c
+
+$(LOB)/HTMLDTD.o : $(OE) $(CMN)HTMLDTD.c $(CMN)SGML.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMLDTD.c
+
+$(LOB)/HTPlain.o : $(OE) $(CMN)HTPlain.c $(CMN)HTPlain.h $(CMN)HTStream.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTPlain.c
+
+$(LOB)/HTWAIS.o : $(OE) $(CMN)HTWAIS.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(WAISINC) $(CMN)HTWAIS.c
+
+$(LOB)/HTWSRC.o : $(OE) $(CMN)HTWSRC.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTWSRC.c
+
+$(LOB)/HTWriter.o : $(OE) $(CMN)HTWriter.c $(CMN)HTWriter.h $(CMN)HTStream.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTWriter.c
+
+$(LOB)/HTFWriter.o : $(OE) $(CMN)HTFWriter.c $(CMN)HTFWriter.h $(CMN)HTStream.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFWriter.c
+
+
+#	Access Authorization
+
+$(LOB)/HTAAUtil.o : $(OE) $(CMN)HTAAUtil.c $(CMN)HTAAUtil.h \
+	 $(CMN)HTUtils.h $(CMN)HTString.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAAUtil.c
+
+$(LOB)/HTAAFile.o : $(OE) $(CMN)HTAAFile.c $(CMN)HTAAFile.h \
+	 $(CMN)HTAAUtil.h $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAAFile.c
+
+$(LOB)/HTPasswd.o : $(OE) $(CMN)HTPasswd.c $(CMN)HTPasswd.h \
+	 $(CMN)HTAAUtil.h $(CMN)HTAAFile.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTPasswd.c
+
+$(LOB)/HTGroup.o : $(OE) $(CMN)HTGroup.c $(CMN)HTGroup.h \
+	 $(CMN)HTAAUtil.h $(CMN)HTAAFile.h \
+	 $(CMN)HTAssoc.h $(CMN)HTLex.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTGroup.c
+
+$(LOB)/HTACL.o : $(OE) $(CMN)HTACL.c $(CMN)HTACL.h \
+	 $(CMN)HTAAUtil.h $(CMN)HTAAFile.h $(CMN)HTGroup.h \
+	 $(CMN)HTAssoc.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTACL.c
+
+$(LOB)/HTAuth.o : $(OE) $(CMN)HTAuth.c $(CMN)HTAuth.h \
+	 $(CMN)HTAAUtil.h $(CMN)HTPasswd.h $(CMN)HTAAFile.h \
+	 $(CMN)HTAssoc.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAuth.c
+
+$(LOB)/HTAAServ.o : $(OE) $(CMN)HTAAServ.c $(CMN)HTAAServ.h \
+	$(CMN)HTAAUtil.h $(CMN)HTAAFile.h $(CMN)HTPasswd.h \
+	 $(CMN)HTGroup.h $(CMN)HTACL.h $(CMN)HTAuth.h \
+	 $(CMN)HTUU.h $(CMN)HTParse.h $(CMN)HTList.h \
+	 $(CMN)HTUtils.h $(CMN)HTString.h $(CMN)HTRules.h \
+	 $(CMN)HTAAProt.h $(CMN)HTAssoc.h $(CMN)HTLex.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAAServ.c
+
+$(LOB)/HTAABrow.o : $(OE) $(CMN)HTAABrow.c $(CMN)HTAABrow.h \
+	 $(CMN)HTAAUtil.h $(CMN)HTUU.h \
+	 $(CMN)HTUtils.h $(CMN)HTString.h \
+	 $(CMN)HTParse.h $(CMN)HTList.h $(CMN)HTAlert.h \
+	 $(CMN)HTAssoc.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAABrow.c
+
+$(LOB)/HTAAProt.o : $(OE) $(CMN)HTAAProt.c $(CMN)HTAAProt.h \
+	 $(CMN)HTUtils.h $(CMN)HTAAUtil.h $(CMN)HTAAFile.h \
+	 $(CMN)HTAssoc.h $(CMN)HTLex.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAAProt.c
+
+$(LOB)/HTAssoc.o : $(OE) $(CMN)HTAssoc.c $(CMN)HTAssoc.h \
+	$(CMN)HTUtils.h $(CMN)HTString.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAssoc.c
+
+$(LOB)/HTLex.o : $(OE) $(CMN)HTLex.c $(CMN)HTLex.h $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTLex.c
+
+$(LOB)/HTUU.o : $(OE) $(CMN)HTUU.c $(CMN)HTUU.h $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTUU.c
+
+
+#	Communications & Files
+
+$(LOB)/HTTP.o : $(OE) $(CMN)HTTP.c $(CMN)HTUtils.h $(CMN)HTAABrow.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTP.c
+
+$(LOB)/HTTCP.o : $(OE) $(CMN)HTTCP.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTCP.c
+
+$(LOB)/HTFile.o : $(OE) $(CMN)HTFile.c $(CMN)HTUtils.h \
+	 $(CMN)HTMLDTD.h $(CMN)HTAAServ.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFile.c
+
+$(LOB)/HTBTree.o : $(OE) $(CMN)HTBTree.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTBTree.c
+
+$(LOB)/HTFTP.o : $(OE) $(CMN)HTFTP.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFTP.c
+
+$(LOB)/HTAccess.o : $(OE)  $(CMN)HTAccess.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAccess.c
+
+$(LOB)/HTParse.o : $(OE) $(CMN)HTParse.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTParse.c
+
diff --git a/WWW/Library/Implementation/CommonMakefile b/WWW/Library/Implementation/CommonMakefile
new file mode 100644
index 00000000..bd32f131
--- /dev/null
+++ b/WWW/Library/Implementation/CommonMakefile
@@ -0,0 +1,376 @@
+#	Common Makefile for W3 Library Code
+#	-----------------------------------
+#
+#	(c) CERN 1990, 1991 -- see Copyright.html for conditions
+#
+# This file should be invariant between systems.
+#	DEPENDENCIES NOT COMPLETE @@
+#
+#	make		Compile and link the software (private version)
+#	make install	Copy it into the system (implies make)
+#	make update	Copy installed version into installed version
+#	make uninstall	Unlink installed version from the system
+#	make clean	Remove intermediate files
+#	make cleanall	Remove intremediate files and products
+#	
+# Macros required to be defined already for make:
+#
+# CC		The C compiler
+# CFLAGS	Flags for $(CC) -- except the -I which are below
+# LFLAGS	Flags for ld
+# LYFLAGS	Flags for Lynx
+#
+# WWW           The WWW source tree directory
+#
+# Macros needed for make install:
+#
+# LIBDIR	Directory for installed library
+#______________________________________________________________________
+
+#  If this env var is set to something else Some makes will use that instead
+SHELL = /bin/sh
+
+#	.h files are distributed but originally are made from the
+#	self-documenting hypertext files.
+
+.SUFFIXES: .h .html
+.html.h:
+#	- chmod +w $*.h
+	www -w90 -na -to text/x-c $*.html > $*.h
+#	chmod -w $*.h
+
+# If this is actually run in a subdirectory,
+#
+# WWW = ../../..
+# WWW = ../..	For [cernlib] build in this directory
+
+WC = $(WWW)/Library
+CMN = $(WWW)/Library/Implementation/
+VMS = $(CMN)vms
+# Where shall we put the objects and built library?
+
+LOB = $(WTMP)/Library/$(WWW_MACH)
+
+# Only needed if HTWAIS.c is to be compiled. Put into your Makefile.include
+#  uncomment these and fill in WAISINC for adding direct wais access
+#  to Lynx.
+#HTWAIS = $(LOB)/HTWAIS.o
+#WAIS = YES
+#WAISINC = -I../../../../freeWAIS-0.202/ir
+#WAISCFLAGS = -DDIRECT_WAIS 
+#
+
+# This path, if relative, is taken relative to the directory
+# in which this makefile is, not the pwd.  This screws up the 
+# recursive invocation
+# include $(CMN)Version.make
+include $(ABS)$(WWW)/Library/Implementation/Version.make
+
+# XMOsAIC hack is only for server to cope with xmosaic kludge for mmedia
+#
+# add -DNEW_GATEWAY here for the new gateway config stuff
+CFLAGS2 = $(CFLAGS) $(LYFLAGS) $(WAISCFLAGS) -I$(CMN) -DXMOSAIC_HACK -DACCESS_AUTH 
+
+CERNLIBBIN = $(WWW)/bin
+
+COMMON = $(LOB)/HTParse.o $(LOB)/HTAccess.o $(LOB)/HTTP.o \
+	$(LOB)/HTFile.o	$(LOB)/HTBTree.o $(LOB)/HTFTP.o $(LOB)/HTTCP.o \
+	$(LOB)/SGML.o $(LOB)/HTMLDTD.o $(LOB)/HTChunk.o \
+	$(LOB)/HTPlain.o $(LOB)/HTWriter.o \
+	$(LOB)/HTMLGen.o \
+	$(LOB)/HTAtom.o $(LOB)/HTAnchor.o $(LOB)/HTStyle.o \
+	$(LOB)/HTList.o $(LOB)/HTString.o \
+	$(LOB)/HTRules.o $(LOB)/HTFormat.o  $(LOB)/HTMIME.o \
+	$(LOB)/HTHistory.o $(LOB)/HTNews.o $(LOB)/HTGopher.o \
+	$(LOB)/HTTelnet.o $(LOB)/HTFinger.o $(LOB)/HTWSRC.o $(HTWAIS) \
+	$(LOB)/HTAAUtil.o $(LOB)/HTAAServ.o $(LOB)/HTAABrow.o \
+	$(LOB)/HTAAFile.o $(LOB)/HTPasswd.o $(LOB)/HTGroup.o  \
+	$(LOB)/HTACL.o    $(LOB)/HTAuth.o   $(LOB)/HTAAProt.o \
+	$(LOB)/HTAssoc.o  $(LOB)/HTLex.o    $(LOB)/HTUU.o
+
+CFILES = $(CMN)HTParse.c $(CMN)HTAccess.c $(CMN)HTTP.c $(CMN)HTFile.c \
+        $(CMN)HTBTree.c \
+	$(CMN)HTFTP.c   $(CMN)HTTCP.c     $(CMN)SGML.c \
+	$(CMN)HTMLDTD.c \
+	$(CMN)HTPlain.c	$(CMN)HTWriter.c  \
+        $(CMN)HTMLGen.c	\
+	$(CMN)HTChunk.c $(CMN)HTAtom.c   $(CMN)HTAnchor.c $(CMN)HTStyle.c \
+	$(CMN)HTList.c  $(CMN)HTString.c $(CMN)HTRules.c \
+	$(CMN)HTFormat.c $(CMN)HTMIME.c $(CMN)HTHistory.c \
+	$(CMN)HTNews.c  $(CMN)HTGopher.c $(CMN)HTTelnet.c \
+	$(CMN)HTFinger.c $(CMN)HTWAIS.c  $(CMN)HTWSRC.c \
+	$(CMN)HTAAUtil.c $(CMN)HTAAServ.c $(CMN)HTAABrow.c \
+	$(CMN)HTAAFile.c $(CMN)HTPasswd.c $(CMN)HTGroup.c  \
+	$(CMN)HTACL.c    $(CMN)HTAuth.c   $(CMN)HTAAProt.c \
+	$(CMN)HTAssoc.c  $(CMN)HTLex.c    $(CMN)HTUU.c
+
+HFILES = $(CMN)HTParse.h $(CMN)HTAccess.h $(CMN)HTTP.h $(CMN)HTFile.h \
+	$(CMN)HTBTree.h $(CMN)HTFTP.h $(CMN)HTTCP.h \
+	$(CMN)SGML.h $(CMN)HTML.h $(CMN)HTMLDTD.h $(CMN)HTChunk.h \
+	$(CMN)HTPlain.h		$(CMN)HTWriter.h \
+	$(CMN)HTFWriter.h 	$(CMN)HTMLGen.h	\
+	$(CMN)HTStream.h \
+	$(CMN)HTAtom.h $(CMN)HTAnchor.h $(CMN)HTStyle.h \
+	$(CMN)HTList.h \
+	$(CMN)HTString.h $(CMN)HTAlert.h $(CMN)HTRules.h \
+	$(CMN)HTFormat.h $(CMN)HTInit.h \
+	$(CMN)HTMIME.h $(CMN)HTHistory.h $(CMN)HTNews.h \
+	$(CMN)HTGopher.h \
+	$(CMN)HTUtils.h $(CMN)tcp.h $(CMN)HText.h \
+	$(CMN)HTTelnet.h $(CMN)HTFinger.h \
+	$(CMN)HTWAIS.h  $(CMN)HTWSRC.h \
+	$(CMN)HTAAUtil.h $(CMN)HTAAServ.h $(CMN)HTAABrow.h \
+	$(CMN)HTAAFile.h $(CMN)HTPasswd.h $(CMN)HTGroup.h  \
+	$(CMN)HTACL.h    $(CMN)HTAuth.h   $(CMN)HTAAProt.h \
+	$(CMN)HTAssoc.h  $(CMN)HTLex.h    $(CMN)HTUU.h
+
+SOURCES = $(CFILES) $(HFILES) $(CMN)Version.make \
+	$(CMN)CommonMakefile $(CMN)Makefile \
+	$(WWW)/README.txt $(WWW)/Copyright.txt $(WWW)/BUILD $(WWW)/Makefile
+SPECIFIC = $(WWW)/All/*/Makefile.include $(WWW)/All/Implementation/Makefile* \
+	$(VMS)/descrip.mms $(VMS)/build_multinet.com \
+	$(VMS)/COPYING.LIB $(VMS)/setup.com $(VMS)/multinet.opt \
+	$(VMS)/patchlevel.h $(VMS)/ufc-crypt.h \
+	$(VMS)/crypt.c $(VMS)/crypt_util.c \
+	$(VMS)/getline.c $(VMS)/getpass.c \
+	$(VMS)/HTVMSUtils.h $(VMS)/HTVMSUtils.c
+
+
+#	Library
+#
+#  On SGI, ranlib is unnecessary and does not exist so we ignore errors
+# for that step
+$(LOB)/libwww.a : $(COMMON)
+	ar r $(LOB)/libwww.a $(COMMON)
+	-ranlib $(LOB)/libwww.a
+
+#	Clean up everything generatable except final products
+clean :
+	rm $(LOB)/*.o $(LOB)/.created
+	-rmdir $(LOB)
+
+#	Clean up everything generatable including final products
+
+cleanall : clean
+	rm $(LOB)/libwww.a
+
+#	Install W3 library into system space (not normally necessary)
+
+install : libwww.a
+	if [ ! -r $(LIBDIR) ] mkdir $(LIBDIR)
+	cp libwww.a $(LIBDIR)/libwww.a
+	
+uninstall :
+	rm $(LIBDIR)/libwww.a
+
+#		Distribution use only:
+#		----------------------
+
+#	Needs www version 2.4 or later to do this
+inc : $(HFILES)
+	echo Include files generated from hypertext.
+
+binary : /pub/www/bin/$(WWW_MACH)/libwww_$(VC).a
+	echo FTP archive binary Libray $(VC) for $(WWW_MACH) up to date.
+
+
+/pub/www/bin/$(WWW_MACH)/libwww_$(VC).a : libwww.a
+	-mkdir /pub/www/bin/$(WWW_MACH)
+	cp libwww.a /pub/www/bin/$(WWW_MACH)/libwww_$(VC).a
+
+#	Source Distribution:
+
+distribute :  /pub/www/README.txt /pub/www/Copyright.txt
+	(cd $(WWW)/..; WWW=WWW ABS=`pwd`/ make $(MFLAGS) \
+		-f WWW/Library/Implementation/CommonMakefile \
+		/pub/www/src/WWWLibrary_$(VC).tar.Z)
+	(cd ../Implementation; cvs tag \
+	    `sed -e 's/VC = /v/' Version.make | sed -e 's?\.?/?'` )
+	echo Distribution of Library version $(VC) up to date.
+
+/pub/www/src/WWWLibrary_$(VC).tar.Z : $(SOURCES)
+	tar cf /pub/www/src/WWWLibrary_$(VC).tar \
+	    $(SOURCES)  $(SPECIFIC) $(WC)/*/Makefile
+	compress /pub/www/src/WWWLibrary_$(VC).tar
+
+
+#	Hypertext supplied in text format
+#	---------------------------------
+
+$(WWW)/README.txt : $(WWW)/../README.html
+	www -n -p66 http://www.w3.org/hypertext/README.html \
+		> $(WWW)/README.txt
+/pub/www/README.txt : $(WWW)/README.txt
+	cp $(WWW)/README.txt /pub/www/README.txt
+
+$(WWW)/Copyright.txt : $(WWW)/../Copyright.html
+	www -n -p66 http://www.w3.org/hypertext/Copyright.html \
+		> $(WWW)/Copyright.txt
+/pub/www/Copyright.txt : $(WWW)/Copyright.txt
+	cp $(WWW)/Copyright.txt /pub/www/Copyright.txt
+
+# 			Common code
+#			-----------
+
+#	Directory for object files - .created checks it exists
+
+OE = $(LOB)/.created
+$(OE) :
+	if [ ! -r $(WTMP) ] ; then mkdir $(WTMP); else echo OK ; fi
+	if [ ! -r $(WTMP)/Library ] ; then mkdir $(WTMP)/Library; else echo OK ; fi
+	if [ ! -r $(WTMP)/Library/$(WWW_MACH) ] ; \
+		then mkdir $(WTMP)/Library/$(WWW_MACH); else echo OK ; fi
+	touch $@
+
+$(LOB)/HTList.o : $(OE) $(CMN)HTList.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTList.c
+
+$(LOB)/HTAnchor.o : $(OE) $(CMN)HTAnchor.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAnchor.c
+
+$(LOB)/HTFormat.o : $(OE) $(CMN)HTFormat.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFormat.c
+
+$(LOB)/HTMIME.o : $(OE) $(CMN)HTMIME.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMIME.c
+
+$(LOB)/HTHistory.o : $(OE) $(CMN)HTHistory.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTHistory.c
+
+$(LOB)/HTNews.o : $(OE) $(CMN)HTNews.c $(CMN)HTUtils.h $(CMN)HTList.h\
+	 $(CMN)HTMLDTD.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTNews.c
+
+$(LOB)/HTGopher.o : $(OE) $(CMN)HTGopher.c $(CMN)HTUtils.h $(CMN)HTList.h \
+	 $(CMN)HTMLDTD.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTGopher.c
+
+$(LOB)/HTTelnet.o : $(OE) $(CMN)HTTelnet.c $(CMN)HTUtils.h $(CMN)HTTelnet.h $(CMN)../../../userdefs.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTelnet.c
+
+$(LOB)/HTFinger.o : $(OE) $(CMN)HTFinger.c $(CMN)HTUtils.h $(CMN)HTList.h \
+	$(CMN)HTMLDTD.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFinger.c
+
+$(LOB)/HTStyle.o : $(OE) $(CMN)HTStyle.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTStyle.c
+
+$(LOB)/HTAtom.o : $(OE) $(CMN)HTAtom.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAtom.c
+
+$(LOB)/HTChunk.o : $(OE) $(CMN)HTChunk.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTChunk.c
+
+$(LOB)/HTString.o : $(OE) $(CMN)HTString.c $(CMN)HTUtils.h $(CMN)Version.make
+	$(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTString.c
+
+$(LOB)/HTRules.o : $(OE) $(CMN)HTRules.c $(CMN)HTUtils.h $(CMN)Version.make \
+	 $(CMN)HTAAServ.h $(CMN)HTAAProt.h
+	$(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTRules.c
+
+$(LOB)/SGML.o : $(OE) $(CMN)SGML.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)SGML.c
+
+$(LOB)/HTMLGen.o : $(OE) $(CMN)HTMLGen.c $(CMN)HTUtils.h $(CMN)HTMLDTD.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMLGen.c
+
+$(LOB)/HTMLDTD.o : $(OE) $(CMN)HTMLDTD.c $(CMN)SGML.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMLDTD.c
+
+$(LOB)/HTPlain.o : $(OE) $(CMN)HTPlain.c $(CMN)HTPlain.h $(CMN)HTStream.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTPlain.c
+
+$(LOB)/HTWAIS.o : $(OE) $(CMN)HTWAIS.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(WAISINC) $(CMN)HTWAIS.c
+
+$(LOB)/HTWSRC.o : $(OE) $(CMN)HTWSRC.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTWSRC.c
+
+$(LOB)/HTWriter.o : $(OE) $(CMN)HTWriter.c $(CMN)HTWriter.h $(CMN)HTStream.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTWriter.c
+
+
+#	Access Authorization
+
+$(LOB)/HTAAUtil.o : $(OE) $(CMN)HTAAUtil.c $(CMN)HTAAUtil.h \
+	 $(CMN)HTUtils.h $(CMN)HTString.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAAUtil.c
+
+$(LOB)/HTAAFile.o : $(OE) $(CMN)HTAAFile.c $(CMN)HTAAFile.h \
+	 $(CMN)HTAAUtil.h $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAAFile.c
+
+$(LOB)/HTPasswd.o : $(OE) $(CMN)HTPasswd.c $(CMN)HTPasswd.h \
+	 $(CMN)HTAAUtil.h $(CMN)HTAAFile.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTPasswd.c
+
+$(LOB)/HTGroup.o : $(OE) $(CMN)HTGroup.c $(CMN)HTGroup.h \
+	 $(CMN)HTAAUtil.h $(CMN)HTAAFile.h \
+	 $(CMN)HTAssoc.h $(CMN)HTLex.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTGroup.c
+
+$(LOB)/HTACL.o : $(OE) $(CMN)HTACL.c $(CMN)HTACL.h \
+	 $(CMN)HTAAUtil.h $(CMN)HTAAFile.h $(CMN)HTGroup.h \
+	 $(CMN)HTAssoc.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTACL.c
+
+$(LOB)/HTAuth.o : $(OE) $(CMN)HTAuth.c $(CMN)HTAuth.h \
+	 $(CMN)HTAAUtil.h $(CMN)HTPasswd.h $(CMN)HTAAFile.h \
+	 $(CMN)HTAssoc.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAuth.c
+
+$(LOB)/HTAAServ.o : $(OE) $(CMN)HTAAServ.c $(CMN)HTAAServ.h \
+	$(CMN)HTAAUtil.h $(CMN)HTAAFile.h $(CMN)HTPasswd.h \
+	 $(CMN)HTGroup.h $(CMN)HTACL.h $(CMN)HTAuth.h \
+	 $(CMN)HTUU.h $(CMN)HTParse.h $(CMN)HTList.h \
+	 $(CMN)HTUtils.h $(CMN)HTString.h $(CMN)HTRules.h \
+	 $(CMN)HTAAProt.h $(CMN)HTAssoc.h $(CMN)HTLex.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAAServ.c
+
+$(LOB)/HTAABrow.o : $(OE) $(CMN)HTAABrow.c $(CMN)HTAABrow.h \
+	 $(CMN)HTAAUtil.h $(CMN)HTUU.h \
+	 $(CMN)HTUtils.h $(CMN)HTString.h \
+	 $(CMN)HTParse.h $(CMN)HTList.h $(CMN)HTAlert.h \
+	 $(CMN)HTAssoc.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAABrow.c
+
+$(LOB)/HTAAProt.o : $(OE) $(CMN)HTAAProt.c $(CMN)HTAAProt.h \
+	 $(CMN)HTUtils.h $(CMN)HTAAUtil.h $(CMN)HTAAFile.h \
+	 $(CMN)HTAssoc.h $(CMN)HTLex.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAAProt.c
+
+$(LOB)/HTAssoc.o : $(OE) $(CMN)HTAssoc.c $(CMN)HTAssoc.h \
+	$(CMN)HTUtils.h $(CMN)HTString.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAssoc.c
+
+$(LOB)/HTLex.o : $(OE) $(CMN)HTLex.c $(CMN)HTLex.h $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTLex.c
+
+$(LOB)/HTUU.o : $(OE) $(CMN)HTUU.c $(CMN)HTUU.h $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTUU.c
+
+
+#	Communications & Files
+
+$(LOB)/HTTP.o : $(OE) $(CMN)HTTP.c $(CMN)HTUtils.h $(CMN)HTAABrow.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTP.c
+
+$(LOB)/HTTCP.o : $(OE) $(CMN)HTTCP.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTCP.c
+
+$(LOB)/HTFile.o : $(OE) $(CMN)HTFile.c $(CMN)HTUtils.h \
+	 $(CMN)HTMLDTD.h $(CMN)HTAAServ.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFile.c
+
+$(LOB)/HTBTree.o : $(OE) $(CMN)HTBTree.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTBTree.c
+
+$(LOB)/HTFTP.o : $(OE) $(CMN)HTFTP.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFTP.c
+
+$(LOB)/HTAccess.o : $(OE)  $(CMN)HTAccess.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAccess.c
+
+$(LOB)/HTParse.o : $(OE) $(CMN)HTParse.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTParse.c
+
diff --git a/WWW/Library/Implementation/HTAABrow.c b/WWW/Library/Implementation/HTAABrow.c
new file mode 100644
index 00000000..caf1cbd8
--- /dev/null
+++ b/WWW/Library/Implementation/HTAABrow.c
@@ -0,0 +1,1051 @@
+
+/* MODULE							HTAABrow.c
+**		BROWSER SIDE ACCESS AUTHORIZATION MODULE
+**
+**	Containts the code for keeping track on server hostnames,
+**	port numbers, scheme names, usernames, passwords
+**	(and servers' public keys).
+**
+** IMPORTANT:
+**	Routines in this module use dynamic allocation, but free
+**	automatically all the memory reserved by them.
+**
+**	Therefore the caller never has to (and never should)
+**	free() any object returned by these functions.
+**
+**	Therefore also all the strings returned by this package
+**	are only valid until the next call to the same function
+**	is made. This approach is selected, because of the nature
+**	of access authorization: no string returned by the package
+**	needs to be valid longer than until the next call.
+**
+**	This also makes it easy to plug the AA package in:
+**	you don't have to ponder whether to free() something
+**	here or is it done somewhere else (because it is always
+**	done somewhere else).
+**
+**	The strings that the package needs to store are copied
+**	so the original strings given as parameters to AA
+**	functions may be freed or modified with no side effects.
+**
+**	The AA package does not free() anything else than what
+**	it has itself allocated.
+**
+** AUTHORS:
+**	AL	Ari Luotonen	luotonen@dxcern.cern.ch
+**
+** HISTORY:
+**	Oct 17	AL	Made corrections suggested by marca:
+**			Added  if (!realm->username) return NULL;
+**			Changed some ""s to NULLs.
+**			Now doing calloc() to init uuencode source;
+**			otherwise HTUU_encode() reads uninitialized memory
+**			every now and then (not a real bug but not pretty).
+**			Corrected the formula for uuencode destination size.
+** BUGS:
+**
+**
+*/
+
+#include "HTUtils.h"
+#include <string.h>		/* strchr() */
+#include "HTString.h"
+#include "HTParse.h"		/* URL parsing function		*/
+#include "HTList.h"		/* HTList object		*/
+#include "HTAlert.h"		/* HTConfirm(), HTPrompt()	*/
+#include "HTAAUtil.h"		/* AA common to both sides	*/
+#include "HTAssoc.h"		/* Assoc list			*/
+#include "HTAABrow.h"		/* Implemented here		*/
+#include "HTUU.h"		/* Uuencoding and uudecoding	*/
+
+#include "LYLeaks.h"
+
+extern BOOL using_proxy;	/* Are we using an HTTP gateway? */
+
+/*
+**  Local datatype definitions
+**
+**  HTAAServer contains all the information about one server.
+*/
+typedef struct {
+
+    char *	hostname;	/* Host's name			*/
+    int		portnumber;	/* Port number			*/
+    HTList *	setups;		/* List of protection setups	*/
+                                /* on this server; i.e. valid	*/
+                                /* authentication schemes and	*/
+                                /* templates when to use them.	*/
+                                /* This is actually a list of	*/
+                                /* HTAASetup objects.		*/
+    HTList *	realms;		/* Information about passwords	*/
+} HTAAServer;
+
+/*
+**  HTAASetup contains information about one server's one
+**  protected tree of documents.
+*/
+typedef struct {
+    HTAAServer *server;		/* Which server serves this tree	     */
+    char *	template;	/* Template for this tree		     */
+    HTList *	valid_schemes;	/* Valid authentic.schemes   		     */
+    HTAssocList**scheme_specifics;/* Scheme specific params		     */
+    BOOL	retry;		/* Failed last time -- reprompt (or whatever)*/
+} HTAASetup;
+
+/*
+**  Information about usernames and passwords in
+**  Basic and Pubkey authentication schemes;
+*/
+typedef struct {
+    char *	realmname;	/* Password domain name		*/
+    char *	username;	/* Username in that domain	*/
+    char *	password;	/* Corresponding password	*/
+} HTAARealm;
+
+/*
+**  To free off all globals. - FM
+*/
+PRIVATE void free_HTAAGlobals NOPARAMS;
+PRIVATE BOOL free_HTAAGlobalsSet = FALSE;
+PRIVATE char *HTAA_composeAuthResult = NULL;
+PRIVATE char *compose_auth_stringResult = NULL;	/* Uuencoded presentation */
+
+/*
+**  Module-wide global variables
+*/
+PRIVATE HTList *server_table	= NULL;	/* Browser's info about servers	     */
+PRIVATE char *secret_key	= NULL;	/* Browser's latest secret key       */
+PRIVATE HTAASetup *current_setup= NULL;	/* The server setup we are currently */
+                                        /* talking to			     */
+PRIVATE char *current_hostname	= NULL;	/* The server's name and portnumber  */
+PRIVATE int current_portnumber	= 80;	/* where we are currently trying to  */
+                                        /* connect.			     */
+PRIVATE char *current_docname	= NULL;	/* The document's name we are        */
+                                        /* trying to access.		     */
+PRIVATE char *HTAAForwardAuth	= NULL;	/* Authorization: line to forward    */
+                                        /* (used by gateway httpds)	     */
+
+
+/*** HTAAForwardAuth for enabling gateway-httpds to forward Authorization ***/
+
+PUBLIC void HTAAForwardAuth_set ARGS2(CONST char *, scheme_name,
+				      CONST char *, scheme_specifics)
+{
+    int len = 20 + (scheme_name      ? strlen(scheme_name)      : 0) 
+	         + (scheme_specifics ? strlen(scheme_specifics) : 0);
+
+    FREE(HTAAForwardAuth);
+    if (!(HTAAForwardAuth = (char*)calloc(1, sizeof(char) * len)))
+	outofmem(__FILE__, "HTAAForwardAuth_set");
+
+    strcpy(HTAAForwardAuth, "Authorization: ");
+    if (scheme_name) {
+	strcat(HTAAForwardAuth, scheme_name);
+	strcat(HTAAForwardAuth, " ");
+	if (scheme_specifics) {
+	    strcat(HTAAForwardAuth, scheme_specifics);
+	}
+    }
+}
+
+PUBLIC void HTAAForwardAuth_reset NOARGS
+{
+    FREE(HTAAForwardAuth);
+}
+
+
+/**************************** HTAAServer ***********************************/
+
+PRIVATE void HTAASetup_delete PARAMS((HTAASetup * killme));	/* Forward */
+
+/* PRIVATE						HTAAServer_new()
+**		ALLOCATE A NEW NODE TO HOLD SERVER INFO
+**		AND ADD IT TO THE LIST OF SERVERS
+** ON ENTRY:
+**	hostname	is the name of the host that the server
+**			is running in.
+**	portnumber	is the portnumber which the server listens.
+**
+** ON EXIT:
+**	returns		the newly-allocated node with all the strings
+**			duplicated.
+**			Strings will be automatically freed by
+**			the function HTAAServer_delete(), which also
+**			frees the node itself.
+*/
+PRIVATE HTAAServer *HTAAServer_new ARGS2(CONST char*,	hostname,
+					 int,		portnumber)
+{
+    HTAAServer *server;
+
+    if (!(server = (HTAAServer *)calloc(1, sizeof(HTAAServer))))
+	outofmem(__FILE__, "HTAAServer_new");
+
+    server->hostname	= NULL;
+    server->portnumber	= (portnumber > 0 ? portnumber : 80);
+    server->setups	= HTList_new();
+    server->realms	= HTList_new();
+
+    if (hostname)
+        StrAllocCopy(server->hostname, hostname);
+
+    if (!server_table)
+        server_table = HTList_new();
+    
+    HTList_addObject(server_table, (void*)server);
+
+    return server;
+}
+
+
+/* PRIVATE						HTAAServer_delete()
+**
+**	DELETE THE ENTRY FOR THE SERVER FROM THE HOST TABLE,
+**	AND FREE THE MEMORY USED BY IT.
+**
+** ON ENTRY:
+**	killme		points to the HTAAServer to be freed.
+**
+** ON EXIT:
+**	returns		nothing.
+*/
+PRIVATE void HTAAServer_delete ARGS1(HTAAServer *, killme)
+{
+    int n, i;
+    HTAASetup *setup;
+    HTAARealm *realm;
+    HTList *cur;
+
+    if (killme) {
+	if (killme->setups != NULL) {
+	    n = HTList_count(killme->setups);
+	    for (i = (n - 1); i >= 0; i--) {
+	        if ((setup = (HTAASetup*)HTList_objectAt(killme->setups, 
+	     						 i)) != NULL) {
+		    HTAASetup_delete(setup);
+		    setup = NULL;
+		}
+	    }
+	    HTList_delete(killme->setups);
+	    killme->setups = NULL;
+	}
+
+	cur = killme->realms;
+	while (NULL != (realm = (HTAARealm*)HTList_nextObject(cur))) {
+	    FREE(realm->realmname);
+	    FREE(realm->username);
+	    FREE(realm->password);
+	    FREE(realm);
+	}
+	HTList_delete(killme->realms);
+	killme->realms = NULL;
+
+	FREE(killme->hostname);
+
+	HTList_removeObject(server_table, (void*)killme);
+	FREE(killme);
+    }
+}
+
+/* PRIVATE						HTAAServer_lookup()
+**		LOOK UP SERVER BY HOSTNAME AND PORTNUMBER
+** ON ENTRY:
+**	hostname	obvious.
+**	portnumber	if non-positive defaults to 80.
+**
+**	Looks up the server in the module-global server_table.
+**
+** ON EXIT:
+**	returns		pointer to a HTAAServer structure
+**			representing the looked-up server.
+**			NULL, if not found.
+*/
+PRIVATE HTAAServer *HTAAServer_lookup ARGS2(CONST char *, hostname,
+					    int,	  portnumber)
+{
+    if (hostname) {
+	HTList *cur = server_table;
+	HTAAServer *server;
+
+	if (portnumber <= 0)
+	    portnumber = 80;
+
+	while (NULL != (server = (HTAAServer*)HTList_nextObject(cur))) {
+	    if (server->portnumber == portnumber  &&
+		0==strcmp(server->hostname, hostname))
+		return server;
+	}
+    }
+    return NULL;	/* NULL parameter, or not found */
+}
+
+
+/*************************** HTAASetup *******************************/    
+
+/* PRIVATE						HTAASetup_lookup()
+**	FIGURE OUT WHICH AUTHENTICATION SETUP THE SERVER
+**	IS USING FOR A GIVEN FILE ON A GIVEN HOST AND PORT
+**
+** ON ENTRY:
+**	hostname	is the name of the server host machine.
+**	portnumber	is the port that the server is running in.
+**	docname		is the (URL-)pathname of the document we
+**			are trying to access.
+**
+** 	This function goes through the information known about
+**	all the setups of the server, and finds out if the given
+**	filename resides in one of the protected directories.
+**
+** ON EXIT:
+**	returns		NULL if no match.
+**			Otherwise, a HTAASetup structure representing
+**			the protected server setup on the corresponding
+**			document tree.
+**			
+*/
+PRIVATE HTAASetup *HTAASetup_lookup ARGS3(CONST char *, hostname,
+					  int,		portnumber,
+					  CONST char *, docname)
+{
+    HTAAServer *server;
+    HTAASetup *setup;
+
+    if (portnumber <= 0)
+        portnumber = 80;
+
+    if (hostname && docname && *hostname && *docname &&
+	NULL != (server = HTAAServer_lookup(hostname, portnumber))) {
+
+	HTList *cur = server->setups;
+
+	if (TRACE)
+	    fprintf(stderr, "%s (%s:%d:%s)\n",
+			    "HTAASetup_lookup: resolving setup for",
+			    hostname, portnumber, docname);
+
+	while (NULL != (setup = (HTAASetup*)HTList_nextObject(cur))) {
+	    if (HTAA_templateMatch(setup->template, docname)) {
+		if (TRACE)
+		    fprintf(stderr, "%s `%s' %s `%s'\n",
+				    "HTAASetup_lookup:", docname,
+				    "matched template", setup->template);
+		return setup;
+	    }
+	    else if (TRACE)
+	        fprintf(stderr, "%s `%s' %s `%s'\n",
+				"HTAASetup_lookup:", docname,
+				"did NOT match template", setup->template);
+	} /* while setups remain */
+    } /* if valid parameters and server found */
+
+    if (TRACE)
+        fprintf(stderr, "%s `%s' %s\n",
+		        "HTAASetup_lookup: No template matched",
+		        (docname ? docname : "(null)"),
+		        "(so probably not protected)");
+
+    return NULL;	/* NULL in parameters, or not found */
+}
+
+/* PRIVATE						HTAASetup_new()
+**			CREATE A NEW SETUP NODE
+** ON ENTRY:
+**	server		is a pointer to a HTAAServer structure
+**			to which this setup belongs.
+**	template	documents matching this template
+**			are protected according to this setup.
+**	valid_schemes	a list containing all valid authentication
+**			schemes for this setup.
+**			If NULL, all schemes are disallowed.
+**	scheme_specifics is an array of assoc lists, which
+**			contain scheme specific parameters given
+**			by server in Authenticate: fields.
+**			If NULL, all scheme specifics are
+**			set to NULL.
+** ON EXIT:
+**	returns		a new HTAASetup node, and also adds it as
+**			part of the HTAAServer given as parameter.
+*/
+PRIVATE HTAASetup *HTAASetup_new ARGS4(HTAAServer *,	server,
+				       char *,		template,
+				       HTList *,	valid_schemes,
+				       HTAssocList **,	scheme_specifics)
+{
+    HTAASetup *setup;
+
+    if (!server || !template || !*template)
+        return NULL;
+
+    if (!(setup = (HTAASetup*)calloc(1, sizeof(HTAASetup))))
+	outofmem(__FILE__, "HTAASetup_new");
+
+    setup->retry = NO;
+    setup->server = server;
+    setup->template = NULL;
+    if (template)
+        StrAllocCopy(setup->template, template);
+    setup->valid_schemes = valid_schemes;
+    setup->scheme_specifics = scheme_specifics;
+
+    HTList_addObject(server->setups, (void*)setup);
+
+    return setup;
+}
+
+/* PRIVATE						HTAASetup_delete()
+**			FREE A HTAASetup STRUCTURE
+** ON ENTRY:
+**	killme		is a pointer to the structure to free().
+**
+** ON EXIT:
+**	returns		nothing.
+*/
+PRIVATE void HTAASetup_delete ARGS1(HTAASetup *, killme)
+{
+    int scheme;
+
+    if (killme) {
+	FREE(killme->template);
+	if (killme->valid_schemes)
+	    HTList_delete(killme->valid_schemes);
+	    killme->valid_schemes = NULL;
+	for (scheme = 0; scheme < HTAA_MAX_SCHEMES; scheme++)
+	    if (killme->scheme_specifics[scheme])
+		HTAssocList_delete(killme->scheme_specifics[scheme]);
+	FREE(killme->scheme_specifics);
+	FREE(killme);
+    }
+}
+
+/* PRIVATE					HTAASetup_updateSpecifics()
+*		COPY SCHEME SPECIFIC PARAMETERS
+**		TO HTAASetup STRUCTURE
+** ON ENTRY:
+**	setup		destination setup structure.
+**	specifics	string array containing scheme
+**			specific parameters for each scheme.
+**			If NULL, all the scheme specific
+**			parameters are set to NULL.
+**
+** ON EXIT:
+**	returns		nothing.
+*/
+PRIVATE void HTAASetup_updateSpecifics ARGS2(HTAASetup *,	setup,
+					     HTAssocList **,	specifics)
+{
+    int scheme;
+
+    if (setup) {
+	if (setup->scheme_specifics) {
+	    for (scheme=0; scheme < HTAA_MAX_SCHEMES; scheme++) {
+		if (setup->scheme_specifics[scheme])
+		    HTAssocList_delete(setup->scheme_specifics[scheme]);
+	    }
+	    FREE(setup->scheme_specifics);
+	}
+	setup->scheme_specifics = specifics;
+    }
+}
+
+
+/*************************** HTAARealm **********************************/
+
+/* PRIVATE 						HTAARealm_lookup()
+**		LOOKUP HTAARealm STRUCTURE BY REALM NAME
+** ON ENTRY:
+**	realm_table	a list of realm objects.
+**	realmname	is the name of realm to look for.
+**
+** ON EXIT:
+**	returns		the realm.  NULL, if not found.
+*/
+PRIVATE HTAARealm *HTAARealm_lookup ARGS2(HTList *,	realm_table,
+					  CONST char *, realmname)
+{
+    if (realm_table && realmname) {
+	HTList *cur = realm_table;
+	HTAARealm *realm;
+	
+	while (NULL != (realm = (HTAARealm*)HTList_nextObject(cur))) {
+	    if (0==strcmp(realm->realmname, realmname))
+		return realm;
+	}
+    }
+    return NULL;	/* No table, NULL param, or not found */
+}
+
+/* PRIVATE						HTAARealm_new()
+**		CREATE A NODE CONTAINING USERNAME AND
+**		PASSWORD USED FOR THE GIVEN REALM.
+**		IF REALM ALREADY EXISTS, CHANGE
+**		USERNAME/PASSWORD.
+** ON ENTRY:
+**	realm_table	a list of realms to where to add
+**			the new one, too.
+**	realmname	is the name of the password domain.
+**	username	and
+**	password	are what you can expect them to be.
+**
+** ON EXIT:
+**	returns		the created realm.
+*/
+PRIVATE HTAARealm *HTAARealm_new ARGS4(HTList *,	realm_table,
+				       CONST char *,	realmname,
+				       CONST char *,	username,
+				       CONST char *,	password)
+{
+    HTAARealm *realm;
+
+    realm = HTAARealm_lookup(realm_table, realmname);
+
+    if (!realm) {
+	if (!(realm = (HTAARealm*)calloc(1, sizeof(HTAARealm))))
+	    outofmem(__FILE__, "HTAARealm_new");
+	realm->realmname = NULL;
+	realm->username = NULL;
+	realm->password = NULL;
+	StrAllocCopy(realm->realmname, realmname);
+	if (realm_table)
+	    HTList_addObject(realm_table, (void*)realm);
+    }
+    if (username)
+        StrAllocCopy(realm->username, username);
+    if (password)
+        StrAllocCopy(realm->password, password);
+
+    return realm;
+}
+
+
+/***************** Basic and Pubkey Authentication ************************/
+
+/* PRIVATE						compose_auth_string()
+**
+**		COMPOSE Basic OR Pubkey AUTHENTICATION STRING;
+**		PROMPTS FOR USERNAME AND PASSWORD IF NEEDED
+**
+** ON ENTRY:
+**	scheme		is either HTAA_BASIC or HTAA_PUBKEY.
+**	realmname	is the password domain name.
+**
+** ON EXIT:
+**	returns		a newly composed authorization string,
+**			(with, of course, a newly generated secret
+**			key and fresh timestamp, if Pubkey-scheme
+**			is being used).
+**			NULL, if something fails.
+** NOTE:
+**	Like throughout the entire AA package, no string or structure
+**	returned by AA package needs to (or should) be freed.
+**
+*/
+PRIVATE char *compose_auth_string ARGS2(HTAAScheme,	scheme,
+					HTAASetup *,	setup)
+{
+    char *cleartext = NULL;	/* Cleartext presentation */
+    char *ciphertext = NULL;	/* Encrypted presentation */
+    int len;
+    char *msg = NULL;
+    char *username = NULL;
+    char *password = NULL;
+    char *realmname = NULL;
+    char *theHost = NULL;
+    char *proxiedHost = NULL;
+    HTAARealm *realm;
+    char *inet_addr = "0.0.0.0";	/* Change... @@@@ */
+    char *timestamp = "42";		/* ... these @@@@ */
+    
+
+    FREE(compose_auth_stringResult);	/* From previous call */
+
+    if ((scheme != HTAA_BASIC && scheme != HTAA_PUBKEY) || !setup ||
+	!setup->scheme_specifics || !setup->scheme_specifics[scheme] ||
+	!setup->server  ||  !setup->server->realms)
+	return NULL;
+
+    realmname = HTAssocList_lookup(setup->scheme_specifics[scheme], "realm");
+    if (!realmname)
+        return NULL;
+
+    realm = HTAARealm_lookup(setup->server->realms, realmname);
+    if (!(realm &&
+    	  realm->username && *realm->username &&
+	  realm->password && *realm->password) || setup->retry) {
+	if (!realm) {
+	    if (TRACE)
+	        fprintf(stderr, "%s `%s' %s\n",
+			        "compose_auth_string: realm:", realmname,
+			        "not found -- creating");
+	    realm = HTAARealm_new(setup->server->realms, realmname, NULL,NULL);
+	}
+	len = strlen(realm->realmname) +
+	      strlen(setup->server->hostname ?
+	      	     setup->server->hostname : "??") + 40;
+	if (!(msg = (char*)calloc(1, sizeof(char) * len)))
+	    outofmem(__FILE__, "compose_auth_string");
+	if (using_proxy && setup->template) {
+	    proxiedHost = HTParse(setup->template, "", PARSE_HOST);
+	    if (proxiedHost && *proxiedHost != '\0') {
+	        theHost = proxiedHost;
+	    }
+	}
+	if (!theHost)
+	    theHost = setup->server->hostname;
+	sprintf(msg, "Enter username for %s at %s:",
+		     realm->realmname,
+		     theHost ? theHost : "??");
+	FREE(proxiedHost);
+	username = realm->username;
+	password = NULL;
+	HTPromptUsernameAndPassword(msg, &username, &password);
+
+	FREE(msg);
+	FREE(realm->username);
+	FREE(realm->password);
+	realm->username = username;
+	realm->password = password;
+
+	if (!realm->username || !realm->password) {
+	    /*
+	     *  Signals to retry. - FM
+	     */
+	    return NULL;
+	} else if (*realm->username == '\0' || *realm->password == '\0') {
+	    /*
+	     *  Signals to abort. - FM
+	     */
+	    StrAllocCopy(compose_auth_stringResult, "");
+	    return compose_auth_stringResult;
+	}
+    }
+    
+    len = strlen(realm->username ? realm->username : "") +
+	  strlen(realm->password ? realm->password : "") + 3;
+
+    if (scheme == HTAA_PUBKEY) {
+#ifdef PUBKEY
+	/* Generate new secret key */
+	StrAllocCopy(secret_key, HTAA_generateRandomKey());
+#endif /* PUBKEY */
+	/* Room for secret key, timestamp and inet address */
+	len += strlen(secret_key ? secret_key : "") + 30;
+    } else {
+	FREE(secret_key);
+    }
+
+    if (!(cleartext  = (char*)calloc(1, sizeof(char) * len)))
+	outofmem(__FILE__, "compose_auth_string");
+
+    if (realm->username)
+        strcpy(cleartext, realm->username);
+    else
+        *cleartext = '\0';
+
+    strcat(cleartext, ":");
+
+    if (realm->password)
+        strcat(cleartext, realm->password);
+
+    if (scheme == HTAA_PUBKEY) {
+	strcat(cleartext, ":");
+	strcat(cleartext, inet_addr);
+	strcat(cleartext, ":");
+	strcat(cleartext, timestamp);
+	strcat(cleartext, ":");
+	if (secret_key)
+	    strcat(cleartext, secret_key);
+
+	if (!((ciphertext = (char *)calloc(1, (sizeof(char) * 2) * len)) &&
+	      (compose_auth_stringResult =
+	      		    (char *)calloc(1, (sizeof(char) * 3) * len))))
+	    outofmem(__FILE__, "compose_auth_string");
+#ifdef PUBKEY
+	HTPK_encrypt(cleartext, ciphertext, server->public_key);
+	HTUU_encode((unsigned char *)ciphertext, strlen(ciphertext),
+		    compose_auth_stringResult);
+#endif /* PUBKEY */
+	FREE(cleartext);
+	FREE(ciphertext);
+    }
+    else { /* scheme == HTAA_BASIC */
+	if (!(compose_auth_stringResult =
+				(char*)calloc(1, (4 * ((len+2)/3)) + 1)))
+	    outofmem(__FILE__, "compose_auth_string");
+	HTUU_encode((unsigned char *)cleartext, strlen(cleartext),
+		    compose_auth_stringResult);
+	FREE(cleartext);
+    }
+    return compose_auth_stringResult;
+}
+
+/* BROWSER PRIVATE					HTAA_selectScheme()
+**		SELECT THE AUTHENTICATION SCHEME TO USE
+** ON ENTRY:
+**	setup	is the server setup structure which can
+**		be used to make the decision about the
+**		used scheme.
+**
+**	When new authentication methods are added to library
+**	this function makes the decision about which one to
+**	use at a given time.  This can be done by inspecting
+**	environment variables etc.
+**
+**	Currently only searches for the first valid scheme,
+**	and if nothing found suggests Basic scheme;
+**
+** ON EXIT:
+**	returns	the authentication scheme to use.
+*/
+PRIVATE HTAAScheme HTAA_selectScheme ARGS1(HTAASetup *, setup)
+{
+    HTAAScheme scheme;
+
+    if (setup && setup->valid_schemes) {
+	for (scheme=HTAA_BASIC; scheme < HTAA_MAX_SCHEMES; scheme++)
+	    if (-1 < HTList_indexOf(setup->valid_schemes, (void*)scheme))
+		return scheme;
+    }
+    return HTAA_BASIC;
+}
+
+/*
+**  Purpose:	Free off all module globals.
+**  Arguments:	void
+**  Return Value:	void
+**  Remarks/Portability/Dependencies/Restrictions:
+**	To be used at program exit.
+**  Revision History:
+**	06-19-96	created - FM
+*/
+PRIVATE void free_HTAAGlobals NOARGS
+{
+    HTAAServer * server;
+    int n, i;
+
+    if (server_table != NULL) {
+        n = HTList_count(server_table);
+	for (i = (n - 1); i >= 0; i--) {
+	    if ((server = (HTAAServer*)HTList_objectAt(server_table, 
+	     						i)) != NULL) {
+	        HTAAServer_delete(server);
+		server = NULL;
+	    }
+	}
+	HTList_delete(server_table);
+	server_table = NULL;
+    }
+
+    HTAAForwardAuth_reset();
+    FREE(HTAA_composeAuthResult);
+    FREE(current_hostname);
+    FREE(current_docname);
+    FREE(compose_auth_stringResult);
+    FREE(secret_key);
+}
+
+/* BROWSER PUBLIC					HTAA_composeAuth()
+**
+**	SELECT THE AUTHENTICATION SCHEME AND
+**	COMPOSE THE ENTIRE AUTHORIZATION HEADER LINE
+**	IF WE ALREADY KNOW THAT THE HOST REQUIRES AUTHENTICATION
+**
+** ON ENTRY:
+**	hostname	is the hostname of the server.
+**	portnumber	is the portnumber in which the server runs.
+**	docname		is the pathname of the document (as in URL)
+**
+** ON EXIT:
+**	returns	NULL, if no authorization seems to be needed, or
+**		if it is the entire Authorization: line, e.g.
+**
+**		   "Authorization: Basic username:password"
+**
+**		As usual, this string is automatically freed.
+*/
+PUBLIC char *HTAA_composeAuth ARGS3(CONST char *,	hostname,
+				    CONST int,		portnumber,
+				    CONST char *,	docname)
+{
+    char *auth_string;
+    BOOL retry;
+    HTAAScheme scheme;
+    int len;
+
+    /*
+    **  Setup atexit() freeing if not done already. - FM
+    */
+    if (!free_HTAAGlobalsSet) {
+        atexit(free_HTAAGlobals);
+	free_HTAAGlobalsSet = TRUE;
+    }
+
+    /*
+    ** Make gateway httpds pass authorization field as it was received.
+    ** (This still doesn't really work because Authenticate: headers
+    **  from remote server are not forwarded to client yet so it cannot
+    **  really know that it should send authorization;  I will not
+    **  implement it yet because I feel we will soon change radically
+    **  the way requests are represented to allow multithreading
+    **  on server-side.  Life is hard.)
+    */
+    if (HTAAForwardAuth) {
+	if (TRACE)
+	    fprintf(stderr, "HTAA_composeAuth: %s\n",
+			    "Forwarding received authorization");
+	StrAllocCopy(HTAA_composeAuthResult, HTAAForwardAuth);
+	HTAAForwardAuth_reset();	/* Just a precaution */
+	return HTAA_composeAuthResult;
+    }
+
+    FREE(HTAA_composeAuthResult);	/* From previous call */
+
+    if (TRACE)
+	fprintf(stderr, 
+		"Composing Authorization for %s:%d/%s\n",
+		hostname, portnumber, docname);
+
+    if (current_portnumber != portnumber ||
+	!current_hostname || !current_docname ||
+	!hostname         || !docname         ||
+	0 != strcmp(current_hostname, hostname) ||
+	0 != strcmp(current_docname, docname)) {
+
+	retry = NO;
+
+	current_portnumber = portnumber;
+	
+	if (hostname)
+	    StrAllocCopy(current_hostname, hostname);
+	else
+	    FREE(current_hostname);
+
+	if (docname)
+	    StrAllocCopy(current_docname, docname);
+	else
+	    FREE(current_docname);
+    } else {
+        retry = YES;
+    }
+    
+    if (!current_setup || !retry)
+	current_setup = HTAASetup_lookup(hostname, portnumber, docname);
+
+    if (!current_setup)
+	return NULL;
+
+
+    switch (scheme = HTAA_selectScheme(current_setup)) {
+      case HTAA_BASIC:
+      case HTAA_PUBKEY:
+	auth_string = compose_auth_string(scheme, current_setup);
+	break;
+      case HTAA_KERBEROS_V4:
+	/* OTHER AUTHENTICATION ROUTINES ARE CALLED HERE */
+      default:
+	{
+	    char msg[100];
+	    sprintf(msg, "%s %s `%s'",
+		    "This client doesn't know how to compose authentication",
+		    "information for scheme", HTAAScheme_name(scheme));
+	    HTAlert(msg);
+	    auth_string = NULL;
+	}
+    } /* switch scheme */
+
+    current_setup->retry = NO;
+
+    if (!auth_string)
+        /*
+	 *  Signal a failure. - FM
+	 */
+	return NULL;  /* Added by marca. */
+    if (*auth_string == '\0') {
+        /*
+	 *  Signal an abort. - FM
+	 */
+        StrAllocCopy(HTAA_composeAuthResult, "");
+	return(HTAA_composeAuthResult);
+    }
+    
+    len = strlen(auth_string) + strlen((char *)HTAAScheme_name(scheme)) + 20;
+    if (!(HTAA_composeAuthResult =
+    		(char*)calloc(1, sizeof(char) * len)))
+	outofmem(__FILE__, "HTAA_composeAuth");
+    strcpy(HTAA_composeAuthResult, "Authorization: ");
+    strcat(HTAA_composeAuthResult, HTAAScheme_name(scheme));
+    strcat(HTAA_composeAuthResult, " ");
+    strcat(HTAA_composeAuthResult, auth_string);
+    return HTAA_composeAuthResult;
+}
+
+/* BROWSER PUBLIC				HTAA_shouldRetryWithAuth()
+**
+**		DETERMINES IF WE SHOULD RETRY THE SERVER
+**		WITH AUTHORIZATION
+**		(OR IF ALREADY RETRIED, WITH A DIFFERENT
+**		USERNAME AND/OR PASSWORD (IF MISSPELLED))
+** ON ENTRY:
+**	start_of_headers is the first block already read from socket,
+**			but status line skipped; i.e. points to the
+**			start of the header section.
+**	length		is the remaining length of the first block.
+**	soc		is the socket to read the rest of server reply.
+**
+**			This function should only be called when
+**			server has replied with a 401 (Unauthorized)
+**			status code.
+** ON EXIT:
+**	returns		YES, if connection should be retried.
+**			     The node containing all the necessary
+**			     information is
+**				* either constructed if it does not exist
+**				* or password is reset to NULL to indicate
+**				  that username and password should be
+**				  reprompted when composing Authorization:
+**				  field (in function HTAA_composeAuth()).
+**			NO, otherwise.
+*/
+PUBLIC BOOL HTAA_shouldRetryWithAuth ARGS4(char *, start_of_headers,
+					   int,	   length,
+					   void *, handle,
+					   int,	   soc)
+{
+    HTAAScheme scheme;
+    char *line = NULL;
+    int num_schemes = 0;
+    HTList *valid_schemes = HTList_new();
+    HTAssocList **scheme_specifics = NULL;
+    char *template = NULL;
+    char *temp = NULL;
+
+    /*
+    **  Setup atexit() freeing if not done already. - FM
+    */
+    if (!free_HTAAGlobalsSet) {
+        atexit(free_HTAAGlobals);
+	free_HTAAGlobalsSet = TRUE;
+    }
+
+    /*
+    **  Read server reply header lines
+    */
+    if (TRACE)
+	fprintf(stderr, "Server reply header lines:\n");
+
+    HTAA_setupReader(start_of_headers, length, handle, soc);
+    while (NULL != (line = HTAA_getUnfoldedLine())  &&  *line != '\0') {
+	if (TRACE)
+	    fprintf(stderr, "%s\n", line);
+
+	if (strchr(line, ':')) {	/* Valid header line */
+
+	    char *p = line;
+	    char *fieldname = HTNextField(&p);
+	    char *arg1 = HTNextField(&p);
+	    char *args = p;
+	    
+	    if (0==strcasecomp(fieldname, "WWW-Authenticate:")) {
+	        if (!(arg1 && *arg1 && args && *args)) {
+		    temp = (char *)calloc(1, strlen(line) +
+		    			     (arg1 ? strlen(arg1) : 0) +
+					     (args ? strlen(args) : 0) + 24);
+		    if (!temp)
+		        outofmem(__FILE__, "HTAA_shouldRetryWithAuth");
+		    sprintf(temp, "Invalid header '%s%s%s%s%s'", line,
+				  ((arg1 && *arg1) ? " "  : ""),
+				  ((arg1 && *arg1) ? arg1 : ""),
+				  ((args && *args) ? " "  : ""),
+				  ((args && *args) ? args : ""));
+		    HTAlert(temp);
+		    FREE(temp);
+		}
+		else if (HTAA_UNKNOWN != (scheme = HTAAScheme_enum(arg1))) {
+		    HTList_addObject(valid_schemes, (void*)scheme);
+		    if (!scheme_specifics) {
+			int i;
+			scheme_specifics = (HTAssocList**)
+			    calloc(1, HTAA_MAX_SCHEMES * sizeof(HTAssocList*));
+			if (!scheme_specifics)
+			    outofmem(__FILE__, "HTAA_shouldRetryWithAuth");
+			for (i=0; i < HTAA_MAX_SCHEMES; i++)
+			    scheme_specifics[i] = NULL;
+		    }
+		    scheme_specifics[scheme] = HTAA_parseArgList(args);
+		    num_schemes++;
+		}
+		else if (TRACE) {
+		    fprintf(stderr, "Unknown scheme `%s' %s\n",
+			    (arg1 ? arg1 : "(null)"),
+			    "in WWW-Authenticate: field");
+		}
+	    }
+
+	    else if (0==strcasecomp(fieldname, "WWW-Protection-Template:")) {
+		if (TRACE)
+		    fprintf(stderr, "Protection template set to `%s'\n", arg1);
+		StrAllocCopy(template, arg1);
+	    }
+
+	} /* if a valid header line */
+	else if (TRACE) {
+	    fprintf(stderr, "Invalid header line `%s' ignored\n", line);
+	} /* else invalid header line */
+
+	FREE(line);
+    } /* while header lines remain */
+    FREE(line);
+
+
+    /*
+    **  So should we retry with authorization
+    */
+    if (num_schemes == 0) {		/* No authentication valid */
+	current_setup = NULL;
+	return NO;
+    }
+
+    if (current_setup && current_setup->server) {
+	/* So we have already tried with authorization.	*/
+	/* Either we don't have access or username or	*/
+	/* password was misspelled.			*/
+	    
+	/* Update scheme-specific parameters	*/
+	/* (in case they have expired by chance).	*/
+	HTAASetup_updateSpecifics(current_setup, scheme_specifics);
+
+	if (NO == HTConfirm("Authorization failed.  Retry?")) {
+	    current_setup = NULL;
+	    return NO;
+	} /* HTConfirm(...) == NO */
+	else { /* re-ask username+password (if misspelled) */
+	    current_setup->retry = YES;
+	    return YES;
+	} /* HTConfirm(...) == YES */
+    } /* if current_setup != NULL */
+
+    else { /* current_setup == NULL, i.e. we have a	 */
+	   /* first connection to a protected server or  */
+	   /* the server serves a wider set of documents */
+	   /* than we expected so far.                   */
+
+	HTAAServer *server = HTAAServer_lookup(current_hostname,
+					       current_portnumber);
+	if (!server) {
+	    server = HTAAServer_new(current_hostname,
+				    current_portnumber);
+	}
+	if (!template)
+	    template = HTAA_makeProtectionTemplate(current_docname);
+	current_setup = HTAASetup_new(server, 
+				      template,
+				      valid_schemes,
+				      scheme_specifics);
+	FREE(template);
+
+        HTAlert("Access without authorization denied -- retrying");
+	return YES;
+    } /* else current_setup == NULL */
+
+    /* Never reached */
+}
+
diff --git a/WWW/Library/Implementation/HTAABrow.h b/WWW/Library/Implementation/HTAABrow.h
new file mode 100644
index 00000000..cfe155dc
--- /dev/null
+++ b/WWW/Library/Implementation/HTAABrow.h
@@ -0,0 +1,138 @@
+/*                          BROWSER SIDE ACCESS AUTHORIZATION MODULE
+                                             
+   This module is the browser side interface to Access Authorization (AA) package.  It
+   contains code only for browser.
+   
+   Important to know about memory allocation:
+   
+   Routines in this module use dynamic allocation, but free automatically all the memory
+   reserved by them.
+   
+   Therefore the caller never has to (and never should) free() any object returned by
+   these functions.
+   
+   Therefore also all the strings returned by this package are only valid until the next
+   call to the same function is made. This approach is selected, because of the nature of
+   access authorization: no string returned by the package needs to be valid longer than
+   until the next call.
+   
+   This also makes it easy to plug the AA package in: you don't have to ponder whether to
+   free()something here or is it done somewhere else (because it is always done somewhere
+   else).
+   
+   The strings that the package needs to store are copied so the original strings given as
+   parameters to AA functions may be freed or modified with no side effects.
+   
+   Also note:The AA package does not free() anything else than what it has itself
+   allocated.
+   
+ */
+
+#ifndef HTAABROW_H
+#define HTAABROW_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"            /* BOOL, PARAMS, ARGS */
+#endif /* HTUTILS_H */
+#include "HTAAUtil.h"           /* Common parts of AA */
+
+
+#ifdef SHORT_NAMES
+#define HTAAcoAu        HTAA_composeAuth
+#define HTAAsRWA        HTAA_shouldRetryWithAuth
+#endif /*SHORT_NAMES*/
+
+/*
+
+Routines for Browser Side Recording of AA Info
+
+   Most of the browser-side AA is done by the following two functions (which are called
+   from file HTTP.c so the browsers using libwww only need to be linked with the new
+   library and not be changed at all):
+   
+      HTAA_composeAuth() composes the Authorization: line contents, if the AA package
+      thinks that the given document is protected. Otherwise this function returns NULL.
+      This function also calls the functions HTPrompt(),HTPromptPassword() and HTConfirm()
+      to get the username, password and some confirmation from the user.
+      
+      HTAA_shouldRetryWithAuth() determines whether to retry the request with AA or with a
+      new AA (in case username or password was misspelled).
+      
+ */
+
+/* PUBLIC                                               HTAA_composeAuth()
+**
+**      COMPOSE THE ENTIRE AUTHORIZATION HEADER LINE IF WE
+**      ALREADY KNOW, THAT THE HOST MIGHT REQUIRE AUTHORIZATION
+**
+** ON ENTRY:
+**      hostname        is the hostname of the server.
+**      portnumber      is the portnumber in which the server runs.
+**      docname         is the pathname of the document (as in URL)
+**
+** ON EXIT:
+**      returns NULL, if no authorization seems to be needed, or
+**              if it is the entire Authorization: line, e.g.
+**
+**                 "Authorization: basic username:password"
+**
+**              As usual, this string is automatically freed.
+*/
+PUBLIC char *HTAA_composeAuth PARAMS((CONST char * hostname,
+                                      CONST int   portnumber,
+                                      CONST char * docname));
+
+
+/* BROWSER PUBLIC                               HTAA_shouldRetryWithAuth()
+**
+**              DETERMINES IF WE SHOULD RETRY THE SERVER
+**              WITH AUTHORIZATION
+**              (OR IF ALREADY RETRIED, WITH A DIFFERENT
+**              USERNAME AND/OR PASSWORD (IF MISSPELLED))
+** ON ENTRY:
+**      start_of_headers is the first block already read from socket,
+**                      but status line skipped; i.e. points to the
+**                      start of the header section.
+**      length          is the remaining length of the first block.
+**      soc             is the socket to read the rest of server reply.
+**
+**                      This function should only be called when
+**                      server has replied with a 401 (Unauthorized)
+**                      status code.
+** ON EXIT:
+**      returns         YES, if connection should be retried.
+**                           The node containing all the necessary
+**                           information is
+**                              * either constructed if it does not exist
+**                              * or password is reset to NULL to indicate
+**                                that username and password should be
+**                                reprompted when composing Authorization:
+**                                field (in function HTAA_composeAuth()).
+**                      NO, otherwise.
+*/
+PUBLIC BOOL HTAA_shouldRetryWithAuth PARAMS((char *     start_of_headers,
+                                             int        length,
+					     void *	handle,
+                                             int        soc));
+/*
+
+Enabling Gateway httpds to Forward Authorization
+
+   These functions should only be called from daemon code, and HTAAForwardAuth_reset()
+   must be called before the next request is handled to make sure that authorization
+   string isn't cached in daemon so that other people can access private files using
+   somebody elses previous authorization information.
+   
+ */
+
+PUBLIC void HTAAForwardAuth_set PARAMS((CONST char * scheme_name,
+                                        CONST char * scheme_specifics));
+PUBLIC void HTAAForwardAuth_reset NOPARAMS;
+/*
+
+ */
+
+#endif  /* NOT HTAABROW_H */
+/*
+
+   End of file HTAABrow.h.  */
diff --git a/WWW/Library/Implementation/HTAAFile.c b/WWW/Library/Implementation/HTAAFile.c
new file mode 100644
index 00000000..15c2d547
--- /dev/null
+++ b/WWW/Library/Implementation/HTAAFile.c
@@ -0,0 +1,210 @@
+
+/* MODULE							HTAAFile.c
+**		FILE ROUTINES FOR AUTHENTICATION
+**		(PASSWD AND GROUP FILES) AND
+**		ACCESS CONTROL LIST (.www_acl)
+** AUTHORS:
+**	AL	Ari Luotonen	luotonen@dxcern.cern.ch
+**
+** HISTORY:
+**
+**
+** BUGS:
+**
+**
+*/
+
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+
+#include "tcp.h"		/* Macro FROMASCII() */
+/*#include <stdio.h> included by HTUtils.h -- FM *//* FILE */
+#include <string.h>
+#include "HTAAUtil.h"		/* Common utilities used in AA */
+#include "HTAAFile.h"		/* Implemented here */
+
+#include "LYLeaks.h"
+
+#define SPACE			' '
+#define TAB			'\t'
+
+
+
+/* PUBLIC						HTAAFile_nextRec()
+**		GO TO THE BEGINNING OF THE NEXT RECORD
+** ON ENTRY:
+**	fp	is the file from which records are read from.
+**
+** ON EXIT:
+**	returns	nothing. File read pointer is located at the beginning
+**		of the next record. Handles continuation lines
+**		(lines ending in comma indicate a following
+**		continuation line).
+**
+*/
+PUBLIC void HTAAFile_nextRec ARGS1(FILE *, fp)
+{
+    int ch = getc(fp);
+    int last = (char)0;
+
+    do {
+	while (ch != EOF  &&  ch != CR  &&  ch != LF) {
+	    if (ch != ' '  && ch != '\t')
+		last = ch;		/* Last non-whitespace */
+	    ch = getc(fp);		/* Skip until end-of-line */
+	}
+	while (ch != EOF &&
+	       (ch == CR  ||  ch == LF))/*Skip carriage returns and linefeeds*/
+	    ch = getc(fp);
+	if (ch != EOF)
+	    ungetc(ch, fp);
+    } while (last == ',' && ch != EOF);	/* Skip also continuation lines */
+}
+
+
+/* PRIVATE							read_item()
+**		READ AN ITEM FROM A PASSWORD, GROUP
+**		OR ACCESS CONTROL LIST FILE
+**		i.e. either a field, or a list item.
+** ON ENTRY:
+**	fp		is the file to read the characters from
+**	contents	is the character array to put the characters
+**	reading_list	if TRUE, read a list item (ends either in
+**			acomma or acolon),
+**			if FALSE, read a field (ends in acolon).
+**	max_len		is the maximum number of characters that may
+**			be read (i.e. the size of dest minus one for
+**			terminating null).
+** ON EXIT:
+**	returns		the terminating character
+**			(i.e. either separator or CR or LF or EOF).
+**	contents	contains a null-terminated string representing
+**			the read field.
+** NOTE 1:
+**			Ignores leading and trailing blanks and tabs.
+** NOTE 2:
+**			If the item is more than max_len characters
+**			long, the rest of the characters in that item
+**			are ignored.  However, contents is always
+**			null-terminated!
+*/
+PRIVATE int read_item ARGS4(FILE *,	fp,
+			    char *,	contents,
+			    BOOL,	reading_list,
+			    int,	max_len)
+{
+    char * dest = contents;
+    char * end = contents;
+    int cnt = 0;
+    int ch = getc(fp);
+
+    while (SPACE == ch || TAB == ch)	/* Skip spaces and tabs */
+	ch = getc(fp);
+
+    while (ch != FIELD_SEPARATOR &&
+	   (!reading_list || ch != LIST_SEPARATOR) &&
+	   ch != CR  &&  ch != LF  &&  ch != EOF  &&  cnt < max_len) {
+	*(dest++) = ch;
+	cnt++;
+	if (ch != SPACE && ch != TAB)
+	    end = dest;
+	ch = getc(fp);
+    } /* while not eol or eof or too many read */
+
+    if (cnt == max_len)	{
+	/* If the field was too long (or exactly maximum) ignore the rest */
+	while (ch != FIELD_SEPARATOR &&
+	       (!reading_list || ch != LIST_SEPARATOR) &&
+	       ch != CR  &&  ch != LF  &&  ch != EOF)
+	    ch = getc(fp);
+    }
+
+    if (ch == CR || ch == LF)
+	ungetc(ch, fp);	/* Push back the record separator (NL or LF) */
+
+    /* Terminate the string, truncating trailing whitespace off.
+    ** Otherwise (if whitespace would be included), here would
+    ** be *dest='\0'; and  cnt -= ... would be left out.
+    */
+    *end = '\0';
+    cnt -= dest-end;
+
+    return ch;		/* Return the terminating character */
+}
+
+
+
+/* PUBLIC						HTAAFile_readField()
+**		READ A FIELD FROM A PASSWORD, GROUP
+**		OR ACCESS CONTROL LIST FILE
+**		i.e. an item terminated by colon,
+**		end-of-line, or end-of-file. 
+** ON ENTRY:
+**	fp		is the file to read the characters from
+**	contents	is the character array to put the characters
+**	max_len		is the maximum number of characters that may
+**			be read (i.e. the size of dest minus one for
+**			terminating null).
+** ON EXIT:
+**	returns		the terminating character
+**			(i.e. either separator or CR or LF or EOF).
+**	contents	contains a null-terminated string representing
+**			the read field.
+** NOTE 1:
+**			Ignores leading and trailing blanks and tabs.
+** NOTE 2:
+**			If the field is more than max_len characters
+**			long, the rest of the characters in that item
+**			are ignored.  However, contents is always
+**			null-terminated!
+*/
+PUBLIC int HTAAFile_readField ARGS3(FILE *, fp,
+				    char *, contents,
+				    int,    max_len)
+{
+    return read_item(fp, contents, NO, max_len);
+}
+
+
+
+
+/* PUBLIC						HTAAFile_readList()
+**
+**			READ A LIST OF STRINGS SEPARATED BY COMMAS
+**			(FROM A GROUP OR ACCESS CONTROL LIST FILE)
+** ON ENTRY:
+**	fp		is a pointer to the input file.
+**	result		is the list to which append the read items.
+**	max_len		is the maximum number of characters in each
+**			list entry (extra characters are ignored).
+** ON EXIT:
+**	returns		the number of items read.
+**
+*/
+PUBLIC int HTAAFile_readList ARGS3(FILE *,	fp,
+				   HTList *,	result,
+				   int,		max_len)
+{
+    char *item = NULL;
+    char terminator;
+    int cnt = 0;
+
+    do {
+	if (!item  &&  !(item = (char*)malloc(max_len+1)))
+	    outofmem(__FILE__, "HTAAFile_readList");
+	terminator = read_item(fp, item, YES, max_len);
+	if (strlen(item) > 0) {
+	    cnt++;
+	    HTList_addObject(result, (void*)item);
+	    item = NULL;
+	}
+    } while (terminator != FIELD_SEPARATOR  &&
+	     terminator != CR  &&  terminator != LF  &&
+	     terminator != EOF);
+
+    FREE(item);	/* This was not needed */
+    return cnt;
+}
+
diff --git a/WWW/Library/Implementation/HTAAFile.h b/WWW/Library/Implementation/HTAAFile.h
new file mode 100644
index 00000000..f6f8ac50
--- /dev/null
+++ b/WWW/Library/Implementation/HTAAFile.h
@@ -0,0 +1,126 @@
+/*                       FILE ROUTINES FOR ACCESS AUTHORIZATION PACKAGE
+                                             
+   This module implements the routines used for accessing (and parsing) the files used in
+   the access authorization:
+   
+      password file
+      
+      group file
+      
+      access control list (ACL) file
+      
+ */
+
+
+#ifndef HTAAFILE_H
+#define HTAAFILE_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"            /* BOOL, PARAMS, ARGS */
+#endif /* HTUTILS_H */
+/*#include <stdio.h> included by HTUtils.h -- FM *//* FILE */
+#include "HTList.h"             /* HTList */
+
+#ifdef SHORT_NAMES
+#define HTAAFnRe        HTAAFile_nextRec
+#define HTAAFrFi        HTAAFile_readField
+#define HTAAFrLi        HTAAFile_readList
+#endif /*SHORT_NAMES*/
+
+
+/* Used field separators */
+
+#define FIELD_SEPARATOR ':'     /* Used to separate fields              */
+#define LIST_SEPARATOR  ','     /* Used to separate items in a list     */
+                                /* in group and ALC files.              */
+
+/*
+
+Naming conventions
+
+  Record                 is an entire line in file.
+                         
+  Field                  is an entity separated by colons and/or by end-of-line.
+                         
+  List                   is a field in which there are items separated by commas.
+                         
+Record-oriented Read Routines
+
+   Password, group and ACL are internally read in by the following functions:
+   
+  HTAAFile_nextRec()      skips to the beginning of the next record (must be called even
+                         after the last field of a record is read to proceed to the next
+                         record).
+                         
+  HTAAFile_readField()    reads a field (separated by colons).
+                         
+  HTAAFile_readList()     reads a field containing a comma-separated list of items.
+                         
+ */
+
+/* PUBLIC                                               HTAAFile_nextRec()
+**                      GO TO THE BEGINNING OF THE NEXT RECORD
+** ON ENTRY:
+**      fp      is the file from which records are read from.
+**
+** ON EXIT:
+**      returns nothing. File read pointer is located at the beginning
+**              of the next record.
+**
+*/
+PUBLIC void HTAAFile_nextRec PARAMS((FILE * fp));
+
+
+/* PUBLIC                                               HTAAFile_readField()
+**              READ A FIELD FROM A PASSWORD, GROUP
+**              OR ACCESS CONTROL LIST FILE
+**              i.e. an item terminated by colon,
+**              end-of-line, or end-of-file.
+** ON ENTRY:
+**      fp              is the file to read the characters from
+**      contents        is the character array to put the characters
+**      max_len         is the maximum number of characters that may
+**                      be read (i.e. the size of dest minus one for
+**                      terminating null).
+** ON EXIT:
+**      returns         the terminating character
+**                      (i.e. either separator or CR or LF or EOF).
+**      contents        contains a null-terminated string representing
+**                      the read field.
+** NOTE 1:
+**                      Ignores leading and trailing blanks and tabs.
+** NOTE 2:
+**                      If the field is more than max_len characters
+**                      long, the rest of the characters in that item
+**                      are ignored.  However, contents is always
+**                      null-terminated!
+*/
+PUBLIC int HTAAFile_readField PARAMS((FILE * fp,
+                                      char * contents,
+                                      int    max_len));
+
+
+/* PUBLIC                                               HTAAFile_readList()
+**
+**                      READ A LIST OF STRINGS SEPARATED BY COMMAS
+**                      (FROM A GROUP OR ACCESS CONTROL LIST FILE)
+** ON ENTRY:
+**      fp              is a pointer to the input file.
+**      result          is the list to which append the read items.
+**      max_len         is the maximum number of characters in each
+**                      list entry (extra characters are ignored).
+** ON EXIT:
+**      returns         the number of items read.
+**
+*/
+PUBLIC int HTAAFile_readList PARAMS((FILE *     fp,
+                                     HTList *   result,
+                                     int        max_len));
+/*
+
+ */
+
+#endif /* not HTAAFILE_H */
+/*
+
+   End of file HTAAFile.h.  */
diff --git a/WWW/Library/Implementation/HTAAProt.c b/WWW/Library/Implementation/HTAAProt.c
new file mode 100644
index 00000000..751b51fc
--- /dev/null
+++ b/WWW/Library/Implementation/HTAAProt.c
@@ -0,0 +1,592 @@
+
+/* MODULE							HTAAProt.c
+**		PROTECTION FILE PARSING MODULE
+**
+** AUTHORS:
+**	AL	Ari Luotonen	luotonen@dxcern.cern.ch
+**	MD	Mark Donszelmann    duns@vxdeop.cern.ch
+**
+** HISTORY:
+**	20 Oct 93  AL	Now finds uid/gid for nobody/nogroup by name
+**			(doesn't use default 65534 right away).
+**			Also understands negative uids/gids.
+**	14 Nov 93  MD	Added VMS compatibility
+**
+** BUGS:
+**
+**
+*/
+
+#include "HTUtils.h"
+
+#include <string.h>
+#ifndef VMS
+#include <pwd.h>	/* Unix password file routine: getpwnam()	*/
+#include <grp.h>	/* Unix group file routine: getgrnam()		*/
+#endif /* not VMS */
+
+#include "HTAAUtil.h"
+#include "HTAAFile.h"
+#include "HTLex.h"	/* Lexical analysor	*/
+#include "HTAssoc.h"	/* Association list	*/
+#include "HTAAProt.h"	/* Implemented here	*/
+
+#include "LYLeaks.h"
+
+/*
+** Protection setup caching
+*/
+typedef struct {
+    char *	prot_filename;
+    HTAAProt *	prot;
+} HTAAProtCache;
+
+PRIVATE HTList *  prot_cache	= NULL;	/* Protection setup cache.	*/
+PRIVATE HTAAProt *default_prot	= NULL;	/* Default protection.		*/
+PRIVATE HTAAProt *current_prot	= NULL;	/* Current protection mode	*/
+                                        /* which is set up by callbacks	*/
+                                        /* from the rule system when	*/
+                                        /* a "protect" rule is matched.	*/
+
+
+/* PRIVATE							isNumber()
+**		DOES A CHARACTER STRING REPRESENT A NUMBER
+*/
+PRIVATE BOOL isNumber ARGS1(CONST char *, s)
+{
+    CONST char *cur = s;
+
+    if (!s || !*s) return NO;
+
+    if (*cur == '-')
+	cur++;		/* Allow initial minus sign in a number */
+
+    while (*cur) {
+	if (*cur < '0' || *cur > '9')
+	    return NO;
+	cur++;
+    }
+    return YES;
+}
+
+
+#ifdef VMS
+/* PUBLIC							HTAA_getUidName()
+**		GET THE USER ID NAME (VMS ONLY)
+** ON ENTRY:
+**	No arguments.
+**
+** ON EXIT:
+**	returns	the user name 
+**		Default is "" (nobody).
+*/
+PUBLIC char * HTAA_getUidName NOARGS
+{
+    if (current_prot && current_prot->uid_name 
+                  && (0 != strcmp(current_prot->uid_name,"nobody")) )
+       return(current_prot->uid_name);
+    else
+       return("");
+}
+
+/* PUBLIC							HTAA_getFileName
+**		GET THE FILENAME (VMS ONLY)
+** ON ENTRY:
+**	No arguments.
+**
+** ON EXIT:
+**	returns	the filename
+*/
+PUBLIC char * HTAA_getFileName NOARGS
+{
+    if (current_prot && current_prot->filename)
+       return(current_prot->filename);
+    else
+       return("");
+}
+
+#else /* not VMS */
+
+/* PUBLIC							HTAA_getUid()
+**		GET THE USER ID TO CHANGE THE PROCESS UID TO
+** ON ENTRY:
+**	No arguments.
+**
+** ON EXIT:
+**	returns	the uid number to give to setuid() system call.
+**		Default is 65534 (nobody).
+*/
+PUBLIC int HTAA_getUid NOARGS
+{
+    struct passwd *pw = NULL;
+
+    if (current_prot  &&  current_prot->uid_name) {
+	if (isNumber(current_prot->uid_name)) {
+	    if (NULL != (pw = getpwuid(atoi(current_prot->uid_name)))) {
+		if (TRACE) fprintf(stderr, 
+				   "%s(%s) returned (%s:%s:%d:%d:...)\n",
+				   "HTAA_getUid: getpwuid",
+				   current_prot->uid_name,
+				   pw->pw_name, pw->pw_passwd,
+				   pw->pw_uid, pw->pw_gid);
+		return pw->pw_uid;	
+	    }
+	}
+	else {	/* User name (not a number) */
+	    if (NULL != (pw = getpwnam(current_prot->uid_name))) {
+		if (TRACE) fprintf(stderr, "%s(\"%s\") %s (%s:%s:%d:%d:...)\n",
+				   "HTAA_getUid: getpwnam",
+				   current_prot->uid_name, "returned",
+				   pw->pw_name, pw->pw_passwd,
+				   pw->pw_uid, pw->pw_gid);
+		return pw->pw_uid;
+	    }
+	}
+    }
+    /*
+    ** Ok, then let's get uid for nobody.
+    */
+    if (NULL != (pw = getpwnam("nobody"))) {
+	if (TRACE) fprintf(stderr, "HTAA_getUid: Uid for `nobody' is %d\n",
+			   pw->pw_uid);
+	return pw->pw_uid;
+    }
+    /*
+    ** Ok, then use default.
+    */
+    return 65534;	/* nobody */
+}
+
+
+/* PUBLIC							HTAA_getGid()
+**		GET THE GROUP ID TO CHANGE THE PROCESS GID TO
+** ON ENTRY:
+**	No arguments.
+**
+** ON EXIT:
+**	returns	the uid number to give to setgid() system call.
+**		Default is 65534 (nogroup).
+*/
+PUBLIC int HTAA_getGid NOARGS
+{    
+    struct group *gr = NULL;
+    
+    if (current_prot  &&  current_prot->gid_name) {
+	if (isNumber(current_prot->gid_name)) {
+	    if (NULL != (gr = getgrgid(atoi(current_prot->gid_name)))) {
+		if (TRACE) fprintf(stderr,
+				   "%s(%s) returned (%s:%s:%d:...)\n",
+				   "HTAA_getGid: getgrgid",
+				   current_prot->gid_name,
+				   gr->gr_name, gr->gr_passwd, gr->gr_gid);
+		return gr->gr_gid;
+	    }
+	}
+	else {	/* Group name (not number) */
+	    if (NULL != (gr = getgrnam(current_prot->gid_name))) {
+		if (TRACE) fprintf(stderr, 
+				   "%s(\"%s\") returned (%s:%s:%d:...)\n",
+				   "HTAA_getGid: getgrnam",
+				   current_prot->gid_name,
+				   gr->gr_name, gr->gr_passwd, gr->gr_gid);
+		return gr->gr_gid;
+	    }
+	}
+    }
+    /*
+    ** Ok, then let's get gid for nogroup.
+    */
+    if (NULL != (gr = getgrnam("nogroup"))) {
+	if (TRACE) fprintf(stderr, "HTAA_getGid: Gid for `nogroup' is %d\n",
+			   gr->gr_gid);
+	return gr->gr_gid;
+    }
+    /*
+    ** Ok, then use default.
+    */
+    return 65534;	/* nogroup */
+}
+#endif /* not VMS */
+
+
+/* PRIVATE							HTAA_setIds()
+**		SET UID AND GID (AS NAMES OR NUMBERS)
+**		TO HTAAProt STRUCTURE
+** ON ENTRY:
+**	prot		destination.
+**	ids		is a string like "james.www" or "1422.69" etc.
+**			giving uid and gid.
+**
+** ON EXIT:
+**	returns		nothing.
+*/
+PRIVATE void HTAA_setIds ARGS2(HTAAProt *,	prot,
+			       CONST char *,	ids)
+{
+    if (ids) {
+	char *local_copy = NULL;
+	char *point;
+
+	StrAllocCopy(local_copy, ids);
+	point = strchr(local_copy, '.');
+	if (point) {
+	    *(point++) = (char)0;
+	    StrAllocCopy(prot->gid_name, point);
+	}
+	else {
+	    StrAllocCopy(prot->gid_name, "nogroup");
+	}
+	StrAllocCopy(prot->uid_name, local_copy);
+	FREE(local_copy);
+    }
+    else {
+	StrAllocCopy(prot->uid_name, "nobody");
+	StrAllocCopy(prot->gid_name, "nogroup");
+    }
+}
+
+
+/* PRIVATE						HTAA_parseProtFile()
+**		PARSE A PROTECTION SETUP FILE AND
+**		PUT THE RESULT IN A HTAAProt STRUCTURE
+** ON ENTRY:
+**	prot		destination structure.
+**	fp		open protection file.
+**
+** ON EXIT:
+**	returns		nothing.
+*/
+PRIVATE void HTAA_parseProtFile ARGS2(HTAAProt *, prot,
+				      FILE *,	  fp)
+{
+    if (prot && fp) {
+	LexItem lex_item;
+	char *fieldname = NULL;
+
+	while (LEX_EOF != (lex_item = lex(fp))) {
+
+	    while (lex_item == LEX_REC_SEP)	/* Ignore empty lines */
+		lex_item = lex(fp);
+
+	    if (lex_item == LEX_EOF)		/* End of file */
+		break;
+
+	    if (lex_item == LEX_ALPH_STR) {	/* Valid setup record */
+		
+		StrAllocCopy(fieldname, HTlex_buffer);
+		
+		if (LEX_FIELD_SEP != (lex_item = lex(fp)))
+		    unlex(lex_item);	/* If someone wants to use colon */
+		                        /* after field name it's ok, but */
+		                        /* not required. Here we read it.*/
+
+		if (0==strncasecomp(fieldname, "Auth", 4)) {
+		    lex_item = lex(fp);
+		    while (lex_item == LEX_ALPH_STR) {
+			HTAAScheme scheme = HTAAScheme_enum(HTlex_buffer);
+			if (scheme != HTAA_UNKNOWN) {
+			    if (!prot->valid_schemes)
+				prot->valid_schemes = HTList_new();
+			    HTList_addObject(prot->valid_schemes,(void*)scheme);
+			    if (TRACE) fprintf(stderr, "%s %s `%s'\n",
+					       "HTAA_parseProtFile: valid",
+					       "authentication scheme:",
+					       HTAAScheme_name(scheme));
+			}
+			else if (TRACE) fprintf(stderr, "%s %s `%s'\n",
+						"HTAA_parseProtFile: unknown",
+						"authentication scheme:",
+						HTlex_buffer);
+			
+			if (LEX_ITEM_SEP != (lex_item = lex(fp)))
+			    break;
+			/*
+			** Here lex_item == LEX_ITEM_SEP; after item separator
+			** it is ok to have one or more newlines (LEX_REC_SEP)
+			** and they are ignored (continuation line).
+			*/
+			do {
+			    lex_item = lex(fp);
+			} while (lex_item == LEX_REC_SEP);
+		    } /* while items in list */
+		} /* if "Authenticate" */
+
+		else if (0==strncasecomp(fieldname, "mask", 4)) {
+		    prot->mask_group = HTAA_parseGroupDef(fp);
+		    lex_item=LEX_REC_SEP; /*groupdef parser read this already*/
+		    if (TRACE) {
+			if (prot->mask_group) {
+			    fprintf(stderr,
+				    "HTAA_parseProtFile: Mask group:\n");
+			    HTAA_printGroupDef(prot->mask_group);
+			} else fprintf(stderr, "HTAA_parseProtFile: %s\n",
+				       "Mask group syntax error");
+		    }
+		} /* if "Mask" */
+
+		else {	/* Just a name-value pair, put it to assoclist */
+
+		    if (LEX_ALPH_STR == (lex_item = lex(fp))) {
+			if (!prot->values)
+			    prot->values = HTAssocList_new();
+			HTAssocList_add(prot->values, fieldname, HTlex_buffer);
+			lex_item = lex(fp);  /* Read record separator */
+			if (TRACE) fprintf(stderr, 
+					   "%s `%s' bound to value `%s'\n",
+					   "HTAA_parseProtFile: Name",
+					   fieldname, HTlex_buffer);
+		    }
+		} /* else name-value pair */
+
+	    } /* if valid field */
+
+	    if (lex_item != LEX_EOF  &&  lex_item != LEX_REC_SEP) {
+		if (TRACE) fprintf(stderr, "%s %s %d (that line ignored)\n",
+				   "HTAA_parseProtFile: Syntax error",
+				   "in protection setup file at line",
+				   HTlex_line);
+		do {
+		    lex_item = lex(fp);
+		} while (lex_item != LEX_EOF && lex_item != LEX_REC_SEP);
+	    } /* if syntax error */
+	} /* while not end-of-file */
+	FREE(fieldname);
+    } /* if valid parameters */
+}
+
+
+/* PRIVATE						HTAAProt_new()
+**		ALLOCATE A NEW HTAAProt STRUCTURE AND
+**		INITIALIZE IT FROM PROTECTION SETUP FILE
+** ON ENTRY:
+**	cur_docname	current filename after rule translations.
+**	prot_filename	protection setup file name.
+**			If NULL, not an error.
+**	ids		Uid and gid names or numbers,
+**			examples:
+**				james	( <=> james.nogroup)
+**				.www	( <=> nobody.www)
+**				james.www
+**				james.69
+**				1422.69
+**				1422.www
+**
+**			May be NULL, defaults to nobody.nogroup.
+**			Should be NULL, if prot_file is NULL.
+**
+** ON EXIT:
+**	returns		returns a new and initialized protection
+**			setup structure.
+**			If setup file is already read in (found
+**			in cache), only sets uid_name and gid
+**			fields, and returns that.
+*/
+PRIVATE HTAAProt *HTAAProt_new ARGS3(CONST char *,	cur_docname,
+				     CONST char *,	prot_filename,
+				     CONST char *,	ids)
+{
+    HTList *cur = prot_cache;
+    HTAAProtCache *cache_item = NULL;
+    HTAAProt *prot;
+    FILE *fp;
+
+    if (!prot_cache)
+	prot_cache = HTList_new();
+    
+    while (NULL != (cache_item = (HTAAProtCache*)HTList_nextObject(cur))) {
+	if (!strcmp(cache_item->prot_filename, prot_filename))
+	    break;
+    }
+    if (cache_item) {
+	prot = cache_item->prot;
+	if (TRACE) fprintf(stderr, "%s `%s' already in cache\n",
+			   "HTAAProt_new: Protection file", prot_filename);
+    } else {
+	if (TRACE) fprintf(stderr,
+			   "HTAAProt_new: Loading protection file `%s'\n",
+			   prot_filename);
+
+	if (!(prot = (HTAAProt*)calloc(1, sizeof(HTAAProt))))
+	    outofmem(__FILE__, "HTAAProt_new");
+
+	prot->template	= NULL;
+	prot->filename	= NULL;
+	prot->uid_name	= NULL;
+	prot->gid_name	= NULL;
+	prot->valid_schemes = HTList_new();
+	prot->mask_group= NULL;		/* Masking disabled by defaults */
+	prot->values	= HTAssocList_new();
+
+	if (prot_filename && NULL != (fp = fopen(prot_filename, "r"))) {
+	    HTAA_parseProtFile(prot, fp);
+	    fclose(fp);
+	    if (!(cache_item =
+	    		(HTAAProtCache*)calloc(1, sizeof(HTAAProtCache))))
+		outofmem(__FILE__, "HTAAProt_new");
+	    cache_item->prot = prot;
+	    cache_item->prot_filename = NULL;
+	    StrAllocCopy(cache_item->prot_filename, prot_filename);
+	    HTList_addObject(prot_cache, (void*)cache_item);
+	}
+	else if (TRACE) fprintf(stderr, "HTAAProt_new: %s `%s'\n",
+				"Unable to open protection setup file",
+				(prot_filename ? prot_filename : "(null)"));
+    }
+
+    if (cur_docname)
+	StrAllocCopy(prot->filename, cur_docname);
+    HTAA_setIds(prot, ids);
+
+    return prot;
+}
+
+
+/* PUBLIC					HTAA_setDefaultProtection()
+**		SET THE DEFAULT PROTECTION MODE
+**		(called by rule system when a
+**		"defprot" rule is matched)
+** ON ENTRY:
+**	cur_docname	is the current result of rule translations.
+**	prot_filename	is the protection setup file (second argument
+**			for "defprot" rule, optional)
+**	ids		contains user and group names separated by
+**			a dot, corresponding to the uid
+**			gid under which the server should run,
+**			default is "nobody.nogroup" (third argument
+**			for "defprot" rule, optional; can be given
+**			only if protection setup file is also given).
+**
+** ON EXIT:
+**	returns		nothing.
+**			Sets the module-wide variable default_prot.
+*/
+PUBLIC void HTAA_setDefaultProtection ARGS3(CONST char *,	cur_docname,
+					    CONST char *,	prot_filename,
+					    CONST char *,	ids)
+{
+    default_prot = NULL;	/* Not free()'d because this is in cache */
+
+    if (prot_filename) {
+	default_prot = HTAAProt_new(cur_docname, prot_filename, ids);
+    } else {
+	if (TRACE) fprintf(stderr, "%s %s\n",
+			   "HTAA_setDefaultProtection: ERROR: Protection file",
+			   "not specified (obligatory for DefProt rule)!!\n");
+    }
+}
+
+
+/* PUBLIC					HTAA_setCurrentProtection()
+**		SET THE CURRENT PROTECTION MODE
+**		(called by rule system when a
+**		"protect" rule is matched)
+** ON ENTRY:
+**	cur_docname	is the current result of rule translations.
+**	prot_filename	is the protection setup file (second argument
+**			for "protect" rule, optional)
+**	ids		contains user and group names separated by
+**			a dot, corresponding to the uid
+**			gid under which the server should run,
+**			default is "nobody.nogroup" (third argument
+**			for "protect" rule, optional; can be given
+**			only if protection setup file is also given).
+**
+** ON EXIT:
+**	returns		nothing.
+**			Sets the module-wide variable current_prot.
+*/
+PUBLIC void HTAA_setCurrentProtection ARGS3(CONST char *,	cur_docname,
+					    CONST char *,	prot_filename,
+					    CONST char *,	ids)
+{
+    current_prot = NULL;	/* Not free()'d because this is in cache */
+
+    if (prot_filename) {
+	current_prot = HTAAProt_new(cur_docname, prot_filename, ids);
+    } else {
+	if (default_prot) {
+	    current_prot = default_prot;
+	    HTAA_setIds(current_prot, ids);
+	    if (TRACE) fprintf(stderr, "%s %s %s\n",
+			       "HTAA_setCurrentProtection: Protection file",
+			       "not specified for Protect rule",
+			       "-- using default protection");
+	} else {
+	    if (TRACE) fprintf(stderr, "%s %s %s\n",
+			       "HTAA_setCurrentProtection: ERROR: Protection",
+			       "file not specified for Protect rule, and",
+			       "default protection is not set!!");
+	}
+    }
+}
+
+
+/* PUBLIC					HTAA_getCurrentProtection()
+**		GET CURRENT PROTECTION SETUP STRUCTURE
+**		(this is set up by callbacks made from
+**		 the rule system when matching "protect"
+**		 (and "defprot") rules)
+** ON ENTRY:
+**	HTTranslate() must have been called before calling
+**	this function.
+**
+** ON EXIT:
+**	returns	a HTAAProt structure representing the
+**		protection setup of the HTTranslate()'d file.
+**		This must not be free()'d.
+*/
+PUBLIC HTAAProt *HTAA_getCurrentProtection NOARGS
+{
+    return current_prot;
+}
+
+
+/* PUBLIC					HTAA_getDefaultProtection()
+**		GET DEFAULT PROTECTION SETUP STRUCTURE
+**		AND SET IT TO CURRENT PROTECTION
+**		(this is set up by callbacks made from
+**		 the rule system when matching "defprot"
+**		 rules)
+** ON ENTRY:
+**	HTTranslate() must have been called before calling
+**	this function.
+**
+** ON EXIT:
+**	returns	a HTAAProt structure representing the
+**		default protection setup of the HTTranslate()'d
+**		file (if HTAA_getCurrentProtection() returned
+**		NULL, i.e. if there is no "protect" rule
+**		but ACL exists, and we need to know default
+**		protection settings).
+**		This must not be free()'d.
+** IMPORTANT:
+**	As a side-effect this tells the protection system that
+**	the file is in fact protected and sets the current
+**	protection mode to default.
+*/
+PUBLIC HTAAProt *HTAA_getDefaultProtection NOARGS
+{
+    if (!current_prot) {
+	current_prot = default_prot;
+	default_prot = NULL;
+    }	
+    return current_prot;
+}
+
+
+/* SERVER INTERNAL					HTAA_clearProtections()
+**		CLEAR DOCUMENT PROTECTION MODE
+**		(ALSO DEFAULT PROTECTION)
+**		(called by the rule system)
+** ON ENTRY:
+**	No arguments.
+**
+** ON EXIT:
+**	returns	nothing.
+**		Frees the memory used by protection information.
+*/
+PUBLIC void HTAA_clearProtections NOARGS
+{
+    current_prot = NULL;	/* These are not freed because	*/
+    default_prot = NULL;	/* they are actually in cache.	*/
+}
diff --git a/WWW/Library/Implementation/HTAAProt.h b/WWW/Library/Implementation/HTAAProt.h
new file mode 100644
index 00000000..73bb7c45
--- /dev/null
+++ b/WWW/Library/Implementation/HTAAProt.h
@@ -0,0 +1,231 @@
+/*                                   PROTECTION SETUP FILE
+                                             
+ */
+
+#ifndef HTAAPROT_H
+#define HTAAPROT_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+#include "HTGroup.h"
+#include "HTAssoc.h"
+
+#ifdef SHORT_NAMES
+#define HTAAgUid        HTAA_getUid
+#define HTAAgGid        HTAA_getGid
+#define HTAAgDPr        HTAA_setDefaultProtection
+#define HTAAsCPr        HTAA_setCurrentProtection
+#define HTAAgCPr        HTAA_getCurrentProtection
+#define HTAAgDPr        HTAA_getDefaultProtection
+#define HTAAclPr        HTAA_clearProtections
+#endif /*SHORT_NAMES*/
+/*
+
+Server's Representation of Document (Tree) Protections
+
+ */
+
+typedef struct {
+    char *        template;     /* Template for this protection         */
+    char *        filename;     /* Current document file                */
+    char *        uid_name;     /* Effective uid (name of it)           */
+    char *        gid_name;     /* Effective gid (name of it)           */
+    GroupDef *    mask_group;   /* Allowed users and IP addresses       */
+    HTList *      valid_schemes;/* Valid authentication schemes         */
+    HTAssocList * values;       /* Association list for scheme specific */
+                                /* parameters.                          */
+} HTAAProt;
+/*
+
+Callbacks for rule system
+
+   The following three functioncs are called by the rule system:
+   
+      HTAA_clearProtections() when starting to translate a filename
+      
+      HTAA_setDefaultProtection() when "defprot" rule is matched
+      
+      HTAA_setCurrentProtection() when "protect" rule is matched
+      
+   Protection setup files are cached by these functions.
+   
+ */
+
+/* PUBLIC                                       HTAA_setDefaultProtection()
+**              SET THE DEFAULT PROTECTION MODE
+**              (called by rule system when a
+**              "defprot" rule is matched)
+** ON ENTRY:
+**      cur_docname     is the current result of rule translations.
+**      prot_filename   is the protection setup file (second argument
+**                      for "defprot" rule, optional)
+**      eff_ids         contains user and group names separated by
+**                      a dot, corresponding to the effective uid
+**                      gid under which the server should run,
+**                      default is "nobody.nogroup" (third argument
+**                      for "defprot" rule, optional; can be given
+**                      only if protection setup file is also given).
+**
+** ON EXIT:
+**      returns         nothing.
+**                      Sets the module-wide variable default_prot.
+*/
+PUBLIC void HTAA_setDefaultProtection PARAMS((CONST char *      cur_docname,
+                                              CONST char *      prot_filename,
+                                              CONST char *      eff_ids));
+
+
+
+/* PUBLIC                                       HTAA_setCurrentProtection()
+**              SET THE CURRENT PROTECTION MODE
+**              (called by rule system when a
+**              "protect" rule is matched)
+** ON ENTRY:
+**      cur_docname     is the current result of rule translations.
+**      prot_filename   is the protection setup file (second argument
+**                      for "protect" rule, optional)
+**      eff_ids         contains user and group names separated by
+**                      a dot, corresponding to the effective uid
+**                      gid under which the server should run,
+**                      default is "nobody.nogroup" (third argument
+**                      for "protect" rule, optional; can be given
+**                      only if protection setup file is also given).
+**
+** ON EXIT:
+**      returns         nothing.
+**                      Sets the module-wide variable current_prot.
+*/
+PUBLIC void HTAA_setCurrentProtection PARAMS((CONST char *      cur_docname,
+                                              CONST char *      prot_filename,
+                                              CONST char *      eff_ids));
+
+
+/* SERVER INTERNAL                                      HTAA_clearProtections()
+**              CLEAR DOCUMENT PROTECTION MODE
+**              (ALSO DEFAULT PROTECTION)
+**              (called by the rule system)
+** ON ENTRY:
+**      No arguments.
+**
+** ON EXIT:
+**      returns nothing.
+**              Frees the memory used by protection information.
+*/
+PUBLIC void HTAA_clearProtections NOPARAMS;
+/*
+
+Getting Protection Settings
+
+      HTAA_getCurrentProtection() returns the current protection mode (if there was a
+      "protect" rule). NULL, if no "protect" rule has been matched.
+      
+      HTAA_getDefaultProtection() sets the current protection mode to what it was set to
+      by "defprot" rule and also returns it (therefore after this call also
+      HTAA_getCurrentProtection() returns the same structure.
+      
+ */
+
+/* PUBLIC                                       HTAA_getCurrentProtection()
+**              GET CURRENT PROTECTION SETUP STRUCTURE
+**              (this is set up by callbacks made from
+**               the rule system when matching "protect"
+**               (and "defprot") rules)
+** ON ENTRY:
+**      HTTranslate() must have been called before calling
+**      this function.
+**
+** ON EXIT:
+**      returns a HTAAProt structure representing the
+**              protection setup of the HTTranslate()'d file.
+**              This must not be free()'d.
+*/
+PUBLIC HTAAProt *HTAA_getCurrentProtection NOPARAMS;
+
+
+
+/* PUBLIC                                       HTAA_getDefaultProtection()
+**              GET DEFAULT PROTECTION SETUP STRUCTURE
+**              (this is set up by callbacks made from
+**               the rule system when matching "defprot"
+**               rules)
+** ON ENTRY:
+**      HTTranslate() must have been called before calling
+**      this function.
+**
+** ON EXIT:
+**      returns a HTAAProt structure representing the
+**              default protection setup of the HTTranslate()'d
+**              file (if HTAA_getCurrentProtection() returned
+**              NULL, i.e. if there is no "protect" rule
+**              but ACL exists, and we need to know default
+**              protection settings).
+**              This must not be free()'d.
+*/
+PUBLIC HTAAProt *HTAA_getDefaultProtection NOPARAMS;
+/*
+
+Get User and Group IDs to Which Set to
+
+ */
+
+#ifndef VMS
+/* PUBLIC                                                       HTAA_getUid()
+**              GET THE USER ID TO CHANGE THE PROCESS UID TO
+** ON ENTRY:
+**      No arguments.
+**
+** ON EXIT:
+**      returns the uid number to give to setuid() system call.
+**              Default is 65534 (nobody).
+*/
+PUBLIC int HTAA_getUid NOPARAMS;
+
+
+/* PUBLIC                                                       HTAA_getGid()
+**              GET THE GROUP ID TO CHANGE THE PROCESS GID TO
+** ON ENTRY:
+**      No arguments.
+**
+** ON EXIT:
+**      returns the uid number to give to setgid() system call.
+**              Default is 65534 (nogroup).
+*/
+PUBLIC int HTAA_getGid NOPARAMS;
+#endif /* not VMS */
+/*
+
+   For VMS:
+   
+ */
+
+#ifdef VMS
+/* PUBLIC                                                       HTAA_getUidName()
+**              GET THE USER ID NAME (VMS ONLY)
+** ON ENTRY:
+**      No arguments.
+**
+** ON EXIT:
+**      returns the user name
+**              Default is "" (nobody).
+*/
+PUBLIC char * HTAA_getUidName NOPARAMS;
+
+/* PUBLIC                                                       HTAA_getFileName
+**              GET THE FILENAME (VMS ONLY)
+** ON ENTRY:
+**      No arguments.
+**
+** ON EXIT:
+**      returns the filename
+*/
+PUBLIC char * HTAA_getFileName NOPARAMS;
+#endif /* VMS */
+/*
+
+ */
+
+#endif /* not HTAAPROT_H */
+/*
+
+   End of file HTAAProt.h.  */
diff --git a/WWW/Library/Implementation/HTAAServ.c b/WWW/Library/Implementation/HTAAServ.c
new file mode 100644
index 00000000..7a0cdef6
--- /dev/null
+++ b/WWW/Library/Implementation/HTAAServ.c
@@ -0,0 +1,686 @@
+
+/* MODULE							HTAAServ.c
+**		SERVER SIDE ACCESS AUTHORIZATION MODULE
+**
+**	Contains the means for checking the user access
+**	authorization for a file.
+**
+** IMPORTANT:
+**	Routines in this module use dynamic allocation, but free
+**	automatically all the memory reserved by them.
+**
+**	Therefore the caller never has to (and never should)
+**	free() any object returned by these functions.
+**
+**	Therefore also all the strings returned by this package
+**	are only valid until the next call to the same function
+**	is made. This approach is selected, because of the nature
+**	of access authorization: no string returned by the package
+**	needs to be valid longer than until the next call.
+**
+**	This also makes it easy to plug the AA package in:
+**	you don't have to ponder whether to free() something
+**	here or is it done somewhere else (because it is always
+**	done somewhere else).
+**
+**	The strings that the package needs to store are copied
+**	so the original strings given as parameters to AA
+**	functions may be freed or modified with no side effects.
+**
+**	The AA package does not free() anything else than what
+**	it has itself allocated.
+**
+** AUTHORS:
+**	AL	Ari Luotonen	luotonen@dxcern.cern.ch
+**
+** HISTORY:
+**
+**
+** BUGS:
+**
+**
+*/
+
+#include "HTUtils.h"
+
+/*#include <stdio.h> included by HTUtils.h -- FM *//* FILE */
+#include <string.h>		/* strchr() */
+
+#include "HTString.h"
+#include "HTAccess.h"		/* HTSecure			*/
+#include "HTFile.h"		/* HTLocalName			*/
+#include "HTRules.h"		/* 				*/
+#include "HTParse.h"		/* URL parsing function		*/
+#include "HTList.h"		/* HTList object		*/
+
+#include "HTAAUtil.h"		/* AA common parts		*/
+#include "HTAuth.h"		/* Authentication		*/
+#include "HTACL.h"		/* Access Control List		*/
+#include "HTGroup.h"		/* Group handling		*/
+#include "HTAAProt.h"		/* Protection file parsing	*/
+#include "HTAAServ.h"		/* Implemented here		*/
+
+#include "LYLeaks.h"
+
+/*
+** Global variables
+*/
+PUBLIC time_t theTime;
+
+
+/*
+** Module-wide global variables
+*/
+PRIVATE FILE *  htaa_logfile	    = NULL; 		/* Log file	      */
+PRIVATE HTAAUser *htaa_user = NULL;			/* Authenticated user */
+PRIVATE HTAAFailReasonType HTAAFailReason = HTAA_OK;	/* AA fail reason     */
+
+
+/* SERVER PUBLIC					HTAA_statusMessage()
+**		RETURN A STRING EXPLAINING ACCESS
+**		AUTHORIZATION FAILURE
+**		(Can be used in server reply status line
+**		 with 401/403 replies.)
+** ON EXIT:
+**	returns	a string containing the error message
+**		corresponding to internal HTAAFailReason.
+*/
+PUBLIC char *HTAA_statusMessage NOARGS
+{
+    switch (HTAAFailReason) {
+
+    /* 401 cases */
+      case HTAA_NO_AUTH:
+	return "Unauthorized -- authentication failed";
+	break;
+      case HTAA_NOT_MEMBER:
+	return "Unauthorized to access the document";
+	break;
+
+    /* 403 cases */
+      case HTAA_BY_RULE:
+	return "Forbidden -- by rule";
+	break;
+      case HTAA_IP_MASK:
+	return "Forbidden -- server refuses to serve to your IP address";
+	break;
+      case HTAA_NO_ACL:
+      case HTAA_NO_ENTRY:
+	return "Forbidden -- access to file is never allowed";
+	break;
+      case HTAA_SETUP_ERROR:
+	return "Forbidden -- server protection setup error";
+	break;
+      case HTAA_DOTDOT:
+	return "Forbidden -- URL containing /../ disallowed";
+	break;
+      case HTAA_HTBIN:
+	return "Forbidden -- /htbin feature not enabled on this server";
+	break;
+
+    /* 404 cases */
+      case HTAA_NOT_FOUND:
+	return "Not found -- file doesn't exist or is read protected";
+	break;
+
+    /* Success */
+      case HTAA_OK:
+	return "AA: Access should be ok but something went wrong"; 
+	break;
+
+      case HTAA_OK_GATEWAY:
+	return "AA check bypassed (gatewaying) but something went wrong";
+	break;
+
+    /* Others */
+      default:
+	return "Access denied -- unable to specify reason (bug)";
+
+    } /* switch */
+}
+
+
+PRIVATE char *status_name ARGS1(HTAAFailReasonType, reason)
+{
+    switch (HTAAFailReason) {
+
+    /* 401 cases */
+      case HTAA_NO_AUTH:
+	return "NO-AUTHENTICATION";
+	break;
+      case HTAA_NOT_MEMBER:
+	return "NOT-AUTHORIZED";
+	break;
+
+    /* 403 cases */
+      case HTAA_BY_RULE:
+	return "FORB-RULE";
+	break;
+      case HTAA_IP_MASK:
+	return "FORB-IP";
+	break;
+      case HTAA_NO_ACL:
+	return "NO-ACL-FILE";
+	break;
+      case HTAA_NO_ENTRY:
+	return "NO-ACL-ENTRY";
+	break;
+      case HTAA_SETUP_ERROR:
+	return "SETUP-ERROR";
+	break;
+      case HTAA_DOTDOT:
+	return "SLASH-DOT-DOT";
+	break;
+      case HTAA_HTBIN:
+	return "HTBIN-OFF";
+	break;
+
+    /* 404 cases */
+      case HTAA_NOT_FOUND:
+	return "NOT-FOUND";
+	break;
+
+    /* Success */
+      case HTAA_OK:
+	return "OK";
+	break;
+      case HTAA_OK_GATEWAY:
+	return "OK-GATEWAY";
+	break;
+
+    /* Others */
+      default:
+	return "SERVER-BUG";
+    } /* switch */
+}
+
+
+/* PRIVATE						check_uthorization()
+**		CHECK IF USER IS AUTHORIZED TO ACCESS A FILE
+** ON ENTRY:
+**	pathname	is the physical file pathname
+**			to access.
+**	method		method, e.g. METHOD_GET, METHOD_PUT, ...
+**	scheme		authentication scheme.
+**	scheme_specifics authentication string (or other
+**			scheme specific parameters, like
+**			Kerberos-ticket).
+**
+** ON EXIT:
+**	returns		HTAA_OK on success.
+**			Otherwise the reason for failing.
+** NOTE:
+**	This function does not check whether the file
+**	exists or not -- so the status  404 Not found
+**	must be returned from somewhere else (this is
+**	to avoid unnecessary overhead of opening the
+**	file twice).
+*/
+PRIVATE HTAAFailReasonType check_authorization ARGS4(CONST char *,  pathname,
+						     HTAAMethod,    method,
+						     HTAAScheme,    scheme,
+						     char *, scheme_specifics)
+{
+    HTAAFailReasonType reason;
+    GroupDef *allowed_groups;
+    FILE *acl_file = NULL;
+    HTAAProt *prot = NULL;	/* Protection mode */
+
+    htaa_user = NULL;
+
+    if (!pathname) {
+	if (TRACE)
+	    fprintf(stderr, "HTAA_checkAuthorization: Forbidden by rule\n");
+	return HTAA_BY_RULE;
+    }
+    if (TRACE)
+        fprintf(stderr, "%s `%s' %s %s\n",
+		        "HTAA_checkAuthorization: translated path:",
+		        pathname, "method:", HTAAMethod_name(method));
+
+    /*
+    ** Get protection setting (set up by callbacks from rule system)
+    ** NULL, if not protected by a "protect" rule.
+    */
+    prot = HTAA_getCurrentProtection();
+
+    /*
+    ** Check ACL existence
+    */
+    if (!(acl_file = HTAA_openAcl(pathname))) {
+	if (prot) { /* protect rule, but no ACL */
+	    if (prot->mask_group) {
+		/*
+		** Only mask enabled, check that
+		*/
+		GroupDefList *group_def_list =
+		    HTAA_readGroupFile(HTAssocList_lookup(prot->values,
+							  "group"));
+		/*
+		** Authenticate if authentication info given
+		*/
+		if (scheme != HTAA_UNKNOWN  &&  scheme != HTAA_NONE) {
+		    htaa_user = HTAA_authenticate(scheme,
+						  scheme_specifics,
+						  prot);
+		    if (TRACE)
+		        fprintf(stderr, "Authentication returned: %s\n",
+				        (htaa_user ? htaa_user->username
+					           : "NOT-AUTHENTICATED"));
+		}
+		HTAA_resolveGroupReferences(prot->mask_group, group_def_list);
+		reason = HTAA_userAndInetInGroup(prot->mask_group,
+						 htaa_user
+						  ? htaa_user->username : "",
+						 HTClientHost,
+						 NULL);
+		if (TRACE) {
+		    if (reason != HTAA_OK)
+			fprintf(stderr, "%s %s %s %s\n",
+				"HTAA_checkAuthorization: access denied",
+				"by mask (no ACL, only Protect rule)",
+				"host", HTClientHost);
+		    else
+		        fprintf(stderr, "%s %s %s %s\n",
+				"HTAA_checkAuthorization: request from",
+				HTClientHost, 
+				"accepted by only mask match (no ACL, only",
+				"Protect rule, and only mask enabled)");
+		}
+		return reason;
+	    }
+	    else {	/* 403 Forbidden */
+		if (TRACE)
+		    fprintf(stderr, "%s %s\n",
+				    "HTAA_checkAuthorization: Protected, but",
+				    "no mask group nor ACL -- forbidden");
+		return HTAA_NO_ACL;
+	    }
+	}
+	else { /* No protect rule and no ACL => OK 200 */
+	    if (TRACE)
+	        fprintf(stderr, "HTAA_checkAuthorization: %s\n",
+			        "no protect rule nor ACL -- ok\n");
+	    return HTAA_OK;
+	}
+    }
+
+    /*
+    ** Now we know that ACL exists
+    */
+    if (!prot) {		/* Not protected by "protect" rule */
+	if (TRACE)
+	    fprintf(stderr, "HTAA_checkAuthorization: default protection\n");
+	prot = HTAA_getDefaultProtection(); /* Also sets current protection */
+
+	if (!prot) {		/* @@ Default protection not set ?? */
+	    if (TRACE)
+	        fprintf(stderr, "%s %s\n",
+			        "HTAA_checkAuthorization: default protection",
+			        "not set (internal server error)!!");
+	    return HTAA_SETUP_ERROR;
+	}
+    }
+
+    /*
+    ** Now we know that document is protected and ACL exists.
+    ** Check against ACL entry.
+    */
+    {
+	GroupDefList *group_def_list =
+	    HTAA_readGroupFile(HTAssocList_lookup(prot->values, "group"));
+
+	/*
+	** Authenticate now that we know protection mode
+	*/
+	if (scheme != HTAA_UNKNOWN  &&  scheme != HTAA_NONE) {
+	    htaa_user = HTAA_authenticate(scheme,
+					  scheme_specifics,
+					  prot);
+	    if (TRACE)
+	        fprintf(stderr, "Authentication returned: %s\n",
+			        (htaa_user
+				 ? htaa_user->username : "NOT-AUTHENTICATED"));
+	}
+	/* 
+	** Check mask group
+	*/
+	if (prot->mask_group) {
+	    HTAA_resolveGroupReferences(prot->mask_group, group_def_list);
+	    reason=HTAA_userAndInetInGroup(prot->mask_group,
+					   htaa_user ? htaa_user->username : "",
+					   HTClientHost,
+					   NULL);
+	    if (reason != HTAA_OK) {
+		if (TRACE)
+		    fprintf(stderr, "%s %s %s\n",
+				    "HTAA_checkAuthorization: access denied",
+				    "by mask, host:", HTClientHost);
+		return reason;
+	    }
+	    else {
+		if (TRACE)
+		    fprintf(stderr, "%s %s %s %s %s\n",
+				    "HTAA_checkAuthorization: request from",
+				    HTClientHost, 
+				    "accepted by just mask group match",
+				    "(no ACL, only Protect rule, and only",
+				    "mask enabled)");
+		/* And continue authorization checking */
+	    }
+	}
+	/*
+        ** Get ACL entries; get first one first, the loop others
+	** Remember, allowed_groups is automatically freed by
+	** HTAA_getAclEntry().
+	*/
+	allowed_groups = HTAA_getAclEntry(acl_file, pathname, method);
+	if (!allowed_groups) {
+	    if (TRACE)
+	        fprintf(stderr, "%s `%s' %s\n",
+			        "No entry for file", pathname, "in ACL");
+	    HTAA_closeAcl(acl_file);
+	    return HTAA_NO_ENTRY;  /* Forbidden -- no entry in the ACL */
+	}
+	else {
+	    do {
+		HTAA_resolveGroupReferences(allowed_groups, group_def_list);
+		reason = HTAA_userAndInetInGroup(allowed_groups,
+						 htaa_user
+						 ? htaa_user->username : "",
+						 HTClientHost,
+						 NULL);
+		if (reason == HTAA_OK) {
+		    HTAA_closeAcl(acl_file);
+		    return HTAA_OK;	/* OK */
+		}
+		allowed_groups = HTAA_getAclEntry(acl_file, pathname, method);
+	    } while (allowed_groups);
+	    HTAA_closeAcl(acl_file);
+	    return HTAA_NOT_MEMBER;	/* Unauthorized */
+	}
+    }
+}
+
+
+/* PUBLIC					      HTAA_checkAuthorization()
+**		CHECK IF USER IS AUTHORIZED TO ACCESS A FILE
+** ON ENTRY:
+**	url		is the document to be accessed.
+**	method_name	name of the method, e.g. "GET"
+**	scheme_name	authentication scheme name.
+**	scheme_specifics authentication string (or other
+**			scheme specific parameters, like
+**			Kerberos-ticket).
+**
+** ON EXIT:
+**	returns	status codes uniform with those of HTTP:
+**	  200 OK	   if file access is ok.
+**	  401 Unauthorized if user is not authorized to
+**			   access the file.
+**	  403 Forbidden	   if there is no entry for the
+**			   requested file in the ACL.
+**
+** NOTE:
+**	This function does not check whether the file
+**	exists or not -- so the status  404 Not found
+**	must be returned from somewhere else (this is
+**	to avoid unnecessary overhead of opening the
+**	file twice).
+**
+*/
+PUBLIC int HTAA_checkAuthorization ARGS4(CONST char *,	url,
+					 CONST char *,	method_name,
+					 CONST char *,	scheme_name,
+                                         char *,	scheme_specifics)
+{
+    static char *pathname = NULL;
+    char *local_copy = NULL;
+    HTAAMethod method = HTAAMethod_enum(method_name);
+    HTAAScheme scheme = HTAAScheme_enum(scheme_name);
+
+    HTAAFailReason = HTAA_OK;
+
+    /*
+    ** Translate into absolute pathname, and
+    ** check for "protect" and "defprot" rules.
+    */
+    FREE(pathname);		/* From previous call	*/
+    StrAllocCopy(local_copy, url);
+    {
+	char *keywords = strchr(local_copy, '?');
+	if (keywords)
+	    *keywords = '\0';	/* Chop off keywords */
+    }
+    HTSimplify(local_copy);	/* Remove ".." etc. */
+
+    /* HTSimplify will leave in a "/../" at the top, which can
+    ** be a security hole.
+    */
+    if (strstr(local_copy, "/../")) {
+	if (TRACE)
+	    fprintf(stderr, "HTAA_checkAuthorization: %s (`%s')\n",
+			    "Illegal attempt to use /../", url);
+	HTAAFailReason = HTAA_DOTDOT;
+    }
+    else {
+	pathname = HTTranslate(local_copy); /* Translate rules even if */
+	                                    /* a /htbin call to set up */
+	                                    /* protections.	       */
+	if (0 == strncmp(local_copy, "/htbin/", 7)) {
+	    if (!HTBinDir)
+		HTAAFailReason = HTAA_HTBIN;
+	    else {
+		char *end = strchr(local_copy+7, '/');
+		if (end)
+		    *end = '\0';
+		FREE(pathname);
+		pathname=(char*)malloc(strlen(HTBinDir)+strlen(local_copy)+1);
+		strcpy(pathname, HTBinDir);
+		strcat(pathname, local_copy+6);
+	    }
+	}
+
+	if (!pathname) {		/* Forbidden by rule */
+	    if (TRACE)
+	        fprintf(stderr, "HTAA_checkAuthorization: Forbidden by rule\n");
+	    HTAAFailReason = HTAA_BY_RULE;
+	}
+	else if (HTAAFailReason != HTAA_HTBIN) {
+	    /* pathname != NULL */
+	    char *access = HTParse(pathname, "", PARSE_ACCESS);
+	    if (!*access || 0 == strcmp(access,"file")) { /*Local file, do AA*/
+		if (!HTSecure && 0 != strncmp(local_copy, "/htbin/", 7)) {
+		    char *localname = HTLocalName(pathname);
+		    FREE(pathname);
+		    pathname = localname;
+		}
+		HTAAFailReason = check_authorization(pathname, method,
+						     scheme, scheme_specifics);
+	    }
+	    else {  /* Not local access */
+		HTAAFailReason = HTAA_OK_GATEWAY;
+		if (TRACE)
+		    fprintf(stderr,
+			    "HTAA_checkAuthorization: %s (%s access)\n",
+			    "Gatewaying -- skipping authorization check",
+			    access);
+	    }
+	} /* pathname */
+    }
+    FREE(local_copy);
+
+    if (htaa_logfile) {
+	time(&theTime);
+	fprintf(htaa_logfile, "%24.24s %s %s %s %s %s\n",
+		ctime(&theTime),
+		HTClientHost ? HTClientHost : "local",
+		method_name,
+		url,
+		status_name(HTAAFailReason),
+		htaa_user && htaa_user->username
+		? htaa_user->username : "");
+	fflush(htaa_logfile);	/* Actually update it on disk */
+	if (TRACE)
+	    fprintf(stderr, "Log: %24.24s %s %s %s %s %s\n",
+			    ctime(&theTime),
+			    HTClientHost ? HTClientHost : "local",
+			    method_name,
+			    url,
+			    status_name(HTAAFailReason),
+			    htaa_user && htaa_user->username
+			    ? htaa_user->username : "");
+    }
+
+    switch (HTAAFailReason) {
+
+      case HTAA_NO_AUTH:
+      case HTAA_NOT_MEMBER:
+	return 401;
+	break;
+
+      case HTAA_BY_RULE:
+      case HTAA_IP_MASK:
+      case HTAA_NO_ACL:
+      case HTAA_NO_ENTRY:
+      case HTAA_SETUP_ERROR:
+      case HTAA_DOTDOT:
+      case HTAA_HTBIN:
+	return 403;
+	break;
+
+      case HTAA_NOT_FOUND:
+	return 404;
+	break;
+
+      case HTAA_OK:
+      case HTAA_OK_GATEWAY:
+	return 200;
+	break;
+
+      default:
+	return 500;
+    } /* switch */
+}
+
+
+/* PRIVATE					compose_scheme_specifics()
+**		COMPOSE SCHEME-SPECIFIC PARAMETERS
+**		TO BE SENT ALONG WITH SERVER REPLY
+**		IN THE WWW-Authenticate: FIELD.
+** ON ENTRY:
+**	scheme		is the authentication scheme for which
+**			parameters are asked for.
+**	prot		protection setup structure.
+**
+** ON EXIT:
+**	returns		scheme specific parameters in an
+**			auto-freed string.
+*/
+PRIVATE char *compose_scheme_specifics ARGS2(HTAAScheme,	scheme,
+					     HTAAProt *,	prot)
+{
+    static char *result = NULL;
+
+    FREE(result);	/* From previous call */
+
+    switch (scheme) {
+      case HTAA_BASIC:
+	{
+	    char *realm = HTAssocList_lookup(prot->values, "server");
+	    result = (char*)malloc(60);
+	    sprintf(result, "realm=\"%s\"",
+		    (realm ? realm : "UNKNOWN"));
+	    return result;
+	}
+	break;
+
+      case HTAA_PUBKEY:
+	{
+	    char *realm = HTAssocList_lookup(prot->values, "server");
+	    result = (char*)malloc(200);
+	    sprintf(result, "realm=\"%s\", key=\"%s\"",
+		    (realm ? realm : "UNKNOWN"),
+		    "PUBKEY-NOT-IMPLEMENTED");
+	    return result;
+	}
+	break;
+      default:
+	return NULL;
+    }
+}
+
+
+/* SERVER PUBLIC				    HTAA_composeAuthHeaders()
+**		COMPOSE WWW-Authenticate: HEADER LINES
+**		INDICATING VALID AUTHENTICATION SCHEMES
+**		FOR THE REQUESTED DOCUMENT
+** ON ENTRY:
+**	No parameters, but HTAA_checkAuthorization() must
+**	just before have failed because a wrong (or none)
+**	authentication scheme was used.
+**
+** ON EXIT:
+**	returns	a buffer containing all the WWW-Authenticate:
+**		fields including CRLFs (this buffer is auto-freed).
+**		NULL, if authentication won't help in accessing
+**		the requested document.
+**
+*/
+PUBLIC char *HTAA_composeAuthHeaders NOARGS
+{
+    static char *result = NULL;
+    HTAAScheme scheme;
+    char *scheme_name;
+    char *scheme_params;
+    HTAAProt *prot = HTAA_getCurrentProtection();
+
+    if (!prot) {
+	if (TRACE)
+	    fprintf(stderr, "%s %s\n",
+			    "HTAA_composeAuthHeaders: Document not protected",
+			    "-- why was this function called??");
+	return NULL;
+    }
+    else if (TRACE)
+        fprintf(stderr, "HTAA_composeAuthHeaders: for file `%s'\n",
+			prot->filename);
+
+    FREE(result);	/* From previous call */
+    if (!(result = (char*)malloc(4096)))	/* @@ */
+	outofmem(__FILE__, "HTAA_composeAuthHeaders");
+    *result = '\0';
+
+    for (scheme=0; scheme < HTAA_MAX_SCHEMES; scheme++) {
+	if (-1 < HTList_indexOf(prot->valid_schemes, (void*)scheme)) {
+	    if ((scheme_name = HTAAScheme_name(scheme))) {
+		scheme_params = compose_scheme_specifics(scheme,prot);
+		strcat(result, "WWW-Authenticate: ");
+		strcat(result, scheme_name);
+		if (scheme_params) {
+		    strcat(result, " ");
+		    strcat(result, scheme_params);
+		}
+		strcat(result, "\r\n");
+	    } /* scheme name found */
+	    else if (TRACE)
+	        fprintf(stderr, "HTAA_composeAuthHeaders: %s %d\n",
+				"No name found for scheme number", scheme);
+	} /* scheme valid for requested document */
+    } /* for every scheme */
+    
+    return result;
+}
+
+
+/* PUBLIC						HTAA_startLogging()
+**		START UP ACCESS AUTHORIZATION LOGGING
+** ON ENTRY:
+**	fp	is the open log file.
+**
+*/
+PUBLIC void HTAA_startLogging ARGS1(FILE *, fp)
+{
+    htaa_logfile = fp;
+}
+
diff --git a/WWW/Library/Implementation/HTAAServ.h b/WWW/Library/Implementation/HTAAServ.h
new file mode 100644
index 00000000..aa350a01
--- /dev/null
+++ b/WWW/Library/Implementation/HTAAServ.h
@@ -0,0 +1,146 @@
+/*                          SERVER SIDE ACCESS AUTHORIZATION MODULE
+                                             
+   This module is the server side interface to Access Authorization (AA) package. It
+   contains code only for server.
+   
+   Important to know about memory allocation:
+   
+   Routines in this module use dynamic allocation, but free automatically all the memory
+   reserved by them.
+   
+   Therefore the caller never has to (and never should) free() any object returned by
+   these functions.
+   
+   Therefore also all the strings returned by this package are only valid until the next
+   call to the same function is made. This approach is selected, because of the nature of
+   access authorization: no string returned by the package needs to be valid longer than
+   until the next call.
+   
+   This also makes it easy to plug the AA package in: you don't have to ponder whether to
+   free()something here or is it done somewhere else (because it is always done somewhere
+   else).
+   
+   The strings that the package needs to store are copied so the original strings given as
+   parameters to AA functions may be freed or modified with no side effects.
+   
+   Also note:The AA package does not free() anything else than what it has itself
+   allocated.
+   
+ */
+
+#ifndef HTAASERV_H
+#define HTAASERV_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"            /* BOOL, PARAMS, ARGS   */
+#endif /* HTUTILS_H */
+/*#include <stdio.h> included by HTUtils.h -- FM *//* FILE                */
+#include "HTRules.h"            /* This module interacts with rule system */
+#include "HTAAUtil.h"           /* Common parts of AA   */
+#include "HTAuth.h"             /* Authentication       */
+
+
+#ifdef SHORT_NAMES
+#define HTAAstMs        HTAA_statusMessage
+#define HTAAchAu        HTAA_checkAuthorization
+#define HTAAcoAH        HTAA_composeAuthHeaders
+#define HTAAsLog        HTAA_startLogging
+#endif /*SHORT_NAMES*/
+
+/*
+
+Check Access Authorization
+
+   HTAA_checkAuthorization() is the main access authorization function.
+   
+ */
+
+/* PUBLIC                                             HTAA_checkAuthorization()
+**              CHECK IF USER IS AUTHORIZED TO ACCESS A FILE
+** ON ENTRY:
+**      url             is the document to be accessed.
+**      method_name     name of the method, e.g. "GET"
+**      scheme_name     authentication scheme name.
+**      scheme_specifics authentication string (or other
+**                      scheme specific parameters, like
+**                      Kerberos-ticket).
+**
+** ON EXIT:
+**      returns status codes uniform with those of HTTP:
+**        200 OK           if file access is ok.
+**        401 Unauthorized if user is not authorized to
+**                         access the file.
+**        403 Forbidden    if there is no entry for the
+**                         requested file in the ACL.
+**
+** NOTE:
+**      This function does not check whether the file
+**      exists or not -- so the status  404 Not found
+**      must be returned from somewhere else (this is
+**      to avoid unnecessary overhead of opening the
+**      file twice).
+**
+*/
+PUBLIC int HTAA_checkAuthorization PARAMS((CONST char * url,
+                                           CONST char * method_name,
+                                           CONST char * scheme_name,
+                                           char *       scheme_specifics));
+/*
+
+Compose Status Line Message
+
+ */
+
+/* SERVER PUBLIC                                        HTAA_statusMessage()
+**              RETURN A STRING EXPLAINING ACCESS
+**              AUTHORIZATION FAILURE
+**              (Can be used in server reply status line
+**               with 401/403 replies.)
+** ON EXIT:
+**      returns a string containing the error message
+**              corresponding to internal HTAAFailReason.
+*/
+PUBLIC char *HTAA_statusMessage NOPARAMS;
+/*
+
+Compose "Authenticate:" Header Lines for Server Reply
+
+ */
+
+/* SERVER PUBLIC                                    HTAA_composeAuthHeaders()
+**              COMPOSE WWW-Authenticate: HEADER LINES
+**              INDICATING VALID AUTHENTICATION SCHEMES
+**              FOR THE REQUESTED DOCUMENT
+** ON ENTRY:
+**      No parameters, but HTAA_checkAuthorization() must
+**      just before have failed because a wrong (or none)
+**      authentication scheme was used.
+**
+** ON EXIT:
+**      returns a buffer containing all the WWW-Authenticate:
+**              fields including CRLFs (this buffer is auto-freed).
+**              NULL, if authentication won't help in accessing
+**              the requested document.
+*/
+PUBLIC char *HTAA_composeAuthHeaders NOPARAMS;
+/*
+
+Start Access Authorization Logging
+
+ */
+
+/* PUBLIC                                               HTAA_startLogging()
+**              START UP ACCESS AUTHORIZATION LOGGING
+** ON ENTRY:
+**      fp      is the open log file.
+**
+*/
+PUBLIC void HTAA_startLogging PARAMS((FILE * fp));
+/*
+
+ */
+
+#endif  /* NOT HTAASERV_H */
+/*
+
+   End of file HTAAServ.h.  */
diff --git a/WWW/Library/Implementation/HTAAUtil.c b/WWW/Library/Implementation/HTAAUtil.c
new file mode 100644
index 00000000..c1af8c24
--- /dev/null
+++ b/WWW/Library/Implementation/HTAAUtil.c
@@ -0,0 +1,640 @@
+
+/* MODULE							HTAAUtil.c
+**		COMMON PARTS OF ACCESS AUTHORIZATION MODULE
+**			FOR BOTH SERVER AND BROWSER
+**
+** IMPORTANT:
+**	Routines in this module use dynamic allocation, but free
+**	automatically all the memory reserved by them.
+**
+**	Therefore the caller never has to (and never should)
+**	free() any object returned by these functions.
+**
+**	Therefore also all the strings returned by this package
+**	are only valid until the next call to the same function
+**	is made. This approach is selected, because of the nature
+**	of access authorization: no string returned by the package
+**	needs to be valid longer than until the next call.
+**
+**	This also makes it easy to plug the AA package in:
+**	you don't have to ponder whether to free() something
+**	here or is it done somewhere else (because it is always
+**	done somewhere else).
+**
+**	The strings that the package needs to store are copied
+**	so the original strings given as parameters to AA
+**	functions may be freed or modified with no side effects.
+**
+**	The AA package does not free() anything else than what
+**	it has itself allocated.
+**
+**	AA (Access Authorization) package means modules which
+**	names start with HTAA.
+**
+** AUTHORS:
+**	AL	Ari Luotonen	luotonen@dxcern.cern.ch
+**	MD 	Mark Donszelmann    duns@vxdeop.cern.ch
+**
+** HISTORY:
+**	 8 Nov 93  MD	(VMS only) Added case insensitive comparison in HTAA_templateCaseMatch
+**
+**
+** BUGS:
+**
+**
+*/
+
+#include "HTUtils.h"
+#include "tcp.h"	/* NETREAD() etc.	*/
+#include <string.h>
+#include "HTAAUtil.h"	/* Implemented here	*/
+#include "HTAssoc.h"	/* Assoc list		*/
+#include "HTTCP.h"
+#include "HTAlert.h"
+
+#ifdef USE_SSL
+#ifdef VMS
+#include "[-.-.-.refssl]ssl.h"
+#else
+#include "../../../refssl/ssl.h"
+#endif /* VMS */
+PRIVATE SSLHandle * ssl_handle=NULL;	/* The SSL handle */
+#endif /* USE_SSL */
+
+#include "LYLeaks.h"
+
+/* PUBLIC						HTAAScheme_enum()
+**		TRANSLATE SCHEME NAME INTO
+**		A SCHEME ENUMERATION
+**
+** ON ENTRY:
+**	name		is a string representing the scheme name.
+**
+** ON EXIT:
+**	returns		the enumerated constant for that scheme.
+*/
+PUBLIC HTAAScheme HTAAScheme_enum ARGS1(CONST char*, name)
+{
+    char *upcased = NULL;
+    char *cur;
+
+    if (!name)
+        return HTAA_UNKNOWN;
+
+    StrAllocCopy(upcased, name);
+    cur = upcased;
+    while (*cur) {
+	*cur = TOUPPER(*cur);
+	cur++;
+    }
+    
+    if (!strncmp(upcased, "NONE", 4)) {
+        FREE(upcased);
+	return HTAA_NONE;
+    } else if (!strncmp(upcased, "BASIC", 5)) {
+        FREE(upcased);
+	return HTAA_BASIC;
+    } else if (!strncmp(upcased, "PUBKEY", 6)) {
+        FREE(upcased);
+	return HTAA_PUBKEY;
+    } else if (!strncmp(upcased, "KERBEROSV4", 10)) {
+        FREE(upcased);
+	return HTAA_KERBEROS_V4;
+    } else if (!strncmp(upcased, "KERBEROSV5", 10)) {
+        FREE(upcased);
+	return HTAA_KERBEROS_V5;
+    } else {
+        FREE(upcased);
+	return HTAA_UNKNOWN;
+    }
+}
+
+
+/* PUBLIC						HTAAScheme_name()
+**			GET THE NAME OF A GIVEN SCHEME
+** ON ENTRY:
+**	scheme		is one of the scheme enum values:
+**			HTAA_NONE, HTAA_BASIC, HTAA_PUBKEY, ...
+**
+** ON EXIT:
+**	returns		the name of the scheme, i.e.
+**			"None", "Basic", "Pubkey", ...
+*/
+PUBLIC char *HTAAScheme_name ARGS1(HTAAScheme, scheme)
+{
+    switch (scheme) {
+        case HTAA_NONE:
+	    return "None";
+	    break;
+	case HTAA_BASIC:
+	    return "Basic";
+	    break;
+	case HTAA_PUBKEY:
+	    return "Pubkey";
+	    break;
+	case HTAA_KERBEROS_V4:
+	    return "KerberosV4";
+	    break;
+	case HTAA_KERBEROS_V5:
+	    return "KerberosV5";
+	    break;
+	case HTAA_UNKNOWN:
+	    return "UNKNOWN";
+	    break;
+	default:
+	    return "THIS-IS-A-BUG";
+    }
+}
+
+
+/* PUBLIC						    HTAAMethod_enum()
+**		TRANSLATE METHOD NAME INTO AN ENUMERATED VALUE
+** ON ENTRY:
+**	name		is the method name to translate.
+**
+** ON EXIT:
+**	returns		HTAAMethod enumerated value corresponding
+**			to the given name.
+*/
+PUBLIC HTAAMethod HTAAMethod_enum ARGS1(CONST char *, name)
+{
+    char tmp[MAX_METHODNAME_LEN+1];
+    CONST char *src = name;
+    char *dest = tmp;
+
+    if (!name)
+        return METHOD_UNKNOWN;
+
+    while (*src) {
+	*dest = TOUPPER(*src);
+	dest++;
+	src++;
+    }
+    *dest = 0;
+
+    if (0==strcmp(tmp, "GET"))
+	return METHOD_GET;
+    else if (0==strcmp(tmp, "PUT"))
+	return METHOD_PUT;
+    else
+	return METHOD_UNKNOWN;
+}
+
+
+/* PUBLIC						HTAAMethod_name()
+**			GET THE NAME OF A GIVEN METHOD
+** ON ENTRY:
+**	method		is one of the method enum values:
+**			METHOD_GET, METHOD_PUT, ...
+**
+** ON EXIT:
+**	returns		the name of the scheme, i.e.
+**			"GET", "PUT", ...
+*/
+PUBLIC char *HTAAMethod_name ARGS1(HTAAMethod, method)
+{
+    switch (method) {
+      case METHOD_GET:
+          return "GET";
+	  break;
+      case METHOD_PUT:
+          return "PUT";
+	  break;
+      case METHOD_UNKNOWN:
+          return "UNKNOWN";
+	  break;
+      default:
+          return "THIS-IS-A-BUG";
+    }
+}
+
+
+/* PUBLIC						HTAAMethod_inList()
+**		IS A METHOD IN A LIST OF METHOD NAMES
+** ON ENTRY:
+**	method		is the method to look for.
+**	list		is a list of method names.
+**
+** ON EXIT:
+**	returns		YES, if method was found.
+**			NO, if not found.
+*/
+PUBLIC BOOL HTAAMethod_inList ARGS2(HTAAMethod,	method,
+				    HTList *,	list)
+{
+    HTList *cur = list;
+    char *item;
+
+    while (NULL != (item = (char*)HTList_nextObject(cur))) {
+	if (TRACE)
+	    fprintf(stderr, " %s", item);
+	if (method == HTAAMethod_enum(item))
+	    return YES;
+    }
+
+    return NO;	/* Not found */
+}
+
+
+/* PUBLIC						HTAA_templateMatch()
+**		STRING COMPARISON FUNCTION FOR FILE NAMES
+**		   WITH ONE WILDCARD * IN THE TEMPLATE
+** NOTE:
+**	This is essentially the same code as in HTRules.c, but it
+**	cannot be used because it is embedded in between other code.
+**	(In fact, HTRules.c should use this routine, but then this
+**	 routine would have to be more sophisticated... why is life
+**	 sometimes so hard...)
+**
+** ON ENTRY:
+**	template	is a template string to match the file name
+**			agaist, may contain a single wildcard
+**			character * which matches zero or more
+**			arbitrary characters.
+**	filename	is the filename (or pathname) to be matched
+**			agaist the template.
+**
+** ON EXIT:
+**	returns		YES, if filename matches the template.
+**			NO, otherwise.
+*/
+PUBLIC BOOL HTAA_templateMatch ARGS2(CONST char *, template, 
+				     CONST char *, filename)
+{
+    CONST char *p = template;
+    CONST char *q = filename;
+    int m;
+
+    for (; *p  &&  *q  &&  *p == *q; p++, q++)	/* Find first mismatch */
+	; /* do nothing else */
+
+    if (!*p && !*q)
+        return YES;			/* Equally long equal strings */
+    else if ('*' == *p) {		/* Wildcard */
+	p++;				/* Skip wildcard character */
+	m = strlen(q) - strlen(p);	/* Amount to match to wildcard */
+	if (m < 0)
+	    return NO;			/* No match, filename too short */
+	else {			/* Skip the matched characters and compare */
+	    if (strcmp(p, q+m))
+	        return NO;	/* Tail mismatch */
+	    else
+	        return YES;	/* Tail match */
+	}
+    }	/* if wildcard */
+    else
+        return NO;		/* Length or character mismatch */
+}    
+
+
+/* PUBLIC						HTAA_templateCaseMatch()
+**		STRING COMPARISON FUNCTION FOR FILE NAMES
+**		   WITH ONE WILDCARD * IN THE TEMPLATE (Case Insensitive)
+** NOTE:
+**	This is essentially the same code as in HTAA_templateMatch, but
+**	it compares case insensitive (for VMS). Reason for this routine
+**	is that HTAA_templateMatch gets called from several places, also 
+**	there where a case sensitive match is needed, so one cannot just
+**	change the HTAA_templateMatch routine for VMS.
+**
+** ON ENTRY:
+**	template	is a template string to match the file name
+**			agaist, may contain a single wildcard
+**			character * which matches zero or more
+**			arbitrary characters.
+**	filename	is the filename (or pathname) to be matched
+**			agaist the template.
+**
+** ON EXIT:
+**	returns		YES, if filename matches the template.
+**			NO, otherwise.
+*/
+PUBLIC BOOL HTAA_templateCaseMatch ARGS2(CONST char *, template, 
+			        	 CONST char *, filename)
+{
+    CONST char *p = template;
+    CONST char *q = filename;
+    int m;
+
+    /* Find first mismatch */
+    for (; *p  &&  *q  &&  TOUPPER(*p) == TOUPPER(*q); p++, q++)
+	; /* do nothing else */
+
+    if (!*p && !*q)
+        return YES;			/* Equally long equal strings */
+    else if ('*' == *p) {		/* Wildcard */
+	p++;				/* Skip wildcard character */
+	m = strlen(q) - strlen(p);	/* Amount to match to wildcard */
+	if (m < 0)
+	    return NO;			/* No match, filename too short */
+	else {			/* Skip the matched characters and compare */
+	    if (strcasecomp(p, q+m))
+	        return NO;	/* Tail mismatch */
+	    else
+	        return YES;	/* Tail match */
+	}
+    }	/* if wildcard */
+    else
+        return NO;		/* Length or character mismatch */
+}    
+
+
+/* PUBLIC					HTAA_makeProtectionTemplate()
+**		CREATE A PROTECTION TEMPLATE FOR THE FILES
+**		IN THE SAME DIRECTORY AS THE GIVEN FILE
+**		(Used by server if there is no fancier way for
+**		it to tell the client, and by browser if server
+**		didn't send WWW-ProtectionTemplate: field)
+** ON ENTRY:
+**	docname	is the document pathname (from URL).
+**
+** ON EXIT:
+**	returns	a template matching docname, and other files
+**		files in that directory.
+**
+**		E.g.  /foo/bar/x.html  =>  /foo/bar/ *
+**						    ^
+**				Space only to prevent it from
+**				being a comment marker here,
+**				there really isn't any space.
+*/
+PUBLIC char *HTAA_makeProtectionTemplate ARGS1(CONST char *, docname)
+{
+    char *template = NULL;
+    char *slash = NULL;
+
+    if (docname) {
+	StrAllocCopy(template, docname);
+	slash = strrchr(template, '/');
+	if (slash)
+	    slash++;
+	else
+	    slash = template;
+	*slash = '\0';
+	StrAllocCat(template, "*");
+    }
+    else
+        StrAllocCopy(template, "*");
+
+    if (TRACE)
+        fprintf(stderr, "make_template: made template `%s' for file `%s'\n",
+		        template, docname);
+
+    return template;
+}
+
+
+/*
+** Skip leading whitespace from *s forward
+*/
+#define SKIPWS(s) while (*s==' ' || *s=='\t') s++;
+
+/*
+** Kill trailing whitespace starting from *(s-1) backwords
+*/
+#define KILLWS(s) {char *c=s-1; while (*c==' ' || *c=='\t') *(c--)='\0';}
+
+
+/* PUBLIC						HTAA_parseArgList()
+**		PARSE AN ARGUMENT LIST GIVEN IN A HEADER FIELD
+** ON ENTRY:
+**	str	is a comma-separated list:
+**
+**			item, item, item
+**		where
+**			item ::= value
+**			       | name=value
+**			       | name="value"
+**
+**		Leading and trailing whitespace is ignored
+**		everywhere except inside quotes, so the following
+**		examples are equal:
+**
+**			name=value,foo=bar
+**			 name="value",foo="bar"
+**			  name = value ,  foo = bar
+**			   name = "value" ,  foo = "bar"
+**
+** ON EXIT:
+**	returns	a list of name-value pairs (actually HTAssocList*).
+**		For items with no name, just value, the name is
+**		the number of order number of that item. E.g.
+**		"1" for the first, etc.
+*/
+PUBLIC HTAssocList *HTAA_parseArgList ARGS1(char *, str)
+{
+    HTAssocList *assoc_list = HTAssocList_new();
+    char *cur = NULL;
+    char *name = NULL;
+    int index = 0;
+
+    if (!str)
+        return assoc_list;
+
+    while (*str) {
+	SKIPWS(str);				/* Skip leading whitespace */
+	cur = str;
+	index++;
+
+	while (*cur  &&  *cur != '='  &&  *cur != ',')
+	    cur++;	/* Find end of name (or lonely value without a name) */
+	KILLWS(cur);	/* Kill trailing whitespace */
+
+	if (*cur == '=') {			/* Name followed by a value */
+	    *(cur++) = '\0';			/* Terminate name */
+	    StrAllocCopy(name, str);
+	    SKIPWS(cur);			/* Skip WS leading the value */
+	    str = cur;
+	    if (*str == '"') {			/* Quoted value */
+		str++;
+		cur = str;
+		while (*cur  &&  *cur != '"')
+		    cur++;
+		if (*cur == '"')
+		    *(cur++) = '\0';		/* Terminate value */
+		/* else it is lacking terminating quote */
+		SKIPWS(cur);			/* Skip WS leading comma */
+		if (*cur == ',')
+		    cur++;			/* Skip separating colon */
+	    }
+	    else {				/* Unquoted value */
+		while (*cur  &&  *cur != ',')
+		    cur++;
+		KILLWS(cur);			/* Kill trailing whitespace */
+		if (*cur == ',')
+		    *(cur++) = '\0';
+		/* else *cur already NULL */
+	    }
+	}
+	else {	/* No name, just a value */
+	    if (*cur == ',') 
+		*(cur++) = '\0';		/* Terminate value */
+	    /* else last value on line (already terminated by NULL) */
+	    StrAllocCopy(name, "nnn");	/* Room for item order number */
+	    sprintf(name, "%d", index); /* Item order number for name */
+	}
+	HTAssocList_add(assoc_list, name, str);
+	str = cur;
+    } /* while *str */
+
+    FREE(name);
+    return assoc_list;
+}
+
+
+/************** HEADER LINE READER -- DOES UNFOLDING *************************/
+
+#define BUFFER_SIZE	1024
+
+PRIVATE char buffer[BUFFER_SIZE + 1];
+PRIVATE char *start_pointer = buffer;
+PRIVATE char *end_pointer = buffer;
+PRIVATE int in_soc = -1;
+
+/* PUBLIC						HTAA_setupReader()
+**		SET UP HEADER LINE READER, i.e. give
+**		the already-read-but-not-yet-processed
+**		buffer of text to be read before more
+**		is read from the socket.
+** ON ENTRY:
+**	start_of_headers is a pointer to a buffer containing
+**			the beginning of the header lines
+**			(rest will be read from a socket).
+**	length		is the number of valid characters in
+**			'start_of_headers' buffer.
+**	soc		is the socket to use when start_of_headers
+**			buffer is used up.
+** ON EXIT:
+**	returns		nothing.
+**			Subsequent calls to HTAA_getUnfoldedLine()
+**			will use this buffer first and then
+**			proceed to read from socket.
+*/
+PUBLIC void HTAA_setupReader ARGS4(char *,	start_of_headers,
+				   int,		length,
+				   void *,	handle,
+				   int,		soc)
+{
+    start_pointer = buffer;
+    if (start_of_headers) {
+	strncpy(buffer, start_of_headers, length);
+	buffer[length] = '\0';
+	end_pointer = buffer + length;
+    }
+    else {
+	*start_pointer = '\0';
+	end_pointer = start_pointer;
+    }
+    in_soc = soc;
+#ifdef USE_SSL
+    ssl_handle = (SSLHandle *)handle;
+#endif /* USE_SSL */
+}
+
+
+/* PUBLIC						HTAA_getUnfoldedLine()
+**		READ AN UNFOLDED HEADER LINE FROM SOCKET
+** ON ENTRY:
+**	HTAA_setupReader must absolutely be called before
+**	this function to set up internal buffer.
+**
+** ON EXIT:
+**	returns	a newly-allocated character string representing
+**		the read line.  The line is unfolded, i.e.
+**		lines that begin with whitespace are appended
+**		to current line.  E.g.
+**
+**			Field-Name: Blaa-Blaa
+**			 This-Is-A-Continuation-Line
+**			 Here-Is_Another
+**
+**		is seen by the caller as:
+**
+**	Field-Name: Blaa-Blaa This-Is-A-Continuation-Line Here-Is_Another
+**
+*/
+PUBLIC char *HTAA_getUnfoldedLine NOARGS
+{
+    char *line = NULL;
+    char *cur;
+    int count;
+    BOOL peek_for_folding = NO;
+
+    if (in_soc < 0) {
+        if (TRACE)
+	    fprintf(stderr, "%s %s\n",
+			    "HTAA_getUnfoldedLine: buffer not initialized",
+			    "with function HTAA_setupReader()");
+	return NULL;
+    }
+
+    for(;;) {
+
+	/* Reading from socket */
+
+	if (start_pointer >= end_pointer) {/*Read the next block and continue*/
+#ifdef USE_SSL
+	    if (ssl_handle)
+	        count = SSL_Read(ssl_handle, buffer, BUFFER_SIZE);
+	    else
+	        count = NETREAD(in_soc, buffer, BUFFER_SIZE);
+#else
+	    count = NETREAD(in_soc, buffer, BUFFER_SIZE);
+#endif /* USE_SSL */
+	    if (count <= 0) {
+		in_soc = -1;
+		return line;
+	    }
+	    start_pointer = buffer;
+	    end_pointer = buffer + count;
+	    *end_pointer = '\0';
+#ifdef NOT_ASCII
+	    cur = start_pointer;
+	    while (cur < end_pointer) {
+		*cur = TOASCII(*cur);
+		cur++;
+	    }
+#endif /*NOT_ASCII*/
+	}
+	cur = start_pointer;
+
+
+	/* Unfolding */
+	
+	if (peek_for_folding) {
+	    if (*cur != ' '  &&  *cur != '\t')
+		return line;	/* Ok, no continuation line */
+	    else		/* So this is a continuation line, continue */
+		peek_for_folding = NO;
+	}
+
+
+	/* Finding end-of-line */
+
+	while (cur < end_pointer && *cur != '\n') /* Find the end-of-line */
+	    cur++;				  /* (or end-of-buffer).  */
+
+	
+	/* Terminating line */
+
+	if (cur < end_pointer) {	/* So *cur==LF, terminate line */
+	    *cur = '\0';		/* Overwrite LF */
+	    if (*(cur-1) == '\r')
+		*(cur-1) = '\0';	/* Overwrite CR */
+	    peek_for_folding = YES;	/* Check for a continuation line */
+	}
+
+
+	/* Copying the result */
+
+	if (line)
+	    StrAllocCat(line, start_pointer);	/* Append */
+	else
+	    StrAllocCopy(line, start_pointer);	/* A new line */
+
+	start_pointer = cur+1;	/* Skip the read line */
+
+    } /* forever */
+}
+
+
diff --git a/WWW/Library/Implementation/HTAAUtil.h b/WWW/Library/Implementation/HTAAUtil.h
new file mode 100644
index 00000000..226f1547
--- /dev/null
+++ b/WWW/Library/Implementation/HTAAUtil.h
@@ -0,0 +1,361 @@
+/*                                            Utilities for the Authorization parts of libwww
+             COMMON PARTS OF AUTHORIZATION MODULE TO BOTH SERVER AND BROWSER
+                                             
+   This module is the interface to the common parts of Access Authorization (AA) package
+   for both server and browser. Important to know about memory allocation:
+   
+   Routines in this module use dynamic allocation, but free automatically all the memory
+   reserved by them.
+   
+   Therefore the caller never has to (and never should) free() any object returned by
+   these functions.
+   
+   Therefore also all the strings returned by this package are only valid until the next
+   call to the same function is made. This approach is selected, because of the nature of
+   access authorization: no string returned by the package needs to be valid longer than
+   until the next call.
+   
+   This also makes it easy to plug the AA package in: you don't have to ponder whether to
+   free() something here or is it done somewhere else (because it is always done somewhere
+   else).
+   
+   The strings that the package needs to store are copied so the original strings given as
+   parameters to AA functions may be freed or modified with no side effects.
+   
+   Also note: The AA package does not free() anything else than what it has itself
+   allocated.
+   
+ */
+
+#ifndef HTAAUTIL_H
+#define HTAAUTIL_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"            /* BOOL, PARAMS, ARGS */
+#endif /* HTUTILS_H */
+#include "tcp.h"
+#include "HTList.h"
+
+#ifdef SHORT_NAMES
+#define HTAASenu        HTAAScheme_enum
+#define HTAASnam        HTAAScheme_name
+#define HTAAMenu        HTAAMethod_enum
+#define HTAAMnam        HTAAMethod_name
+#define HTAAMinL        HTAAMethod_inList
+#define HTAAteMa        HTAA_templateMatch
+#define HTAAmaPT        HTAA_makeProtectionTemplate
+#define HTAApALi        HTAA_parseArgList
+#define HTAAsuRe        HTAA_setupReader
+#define HTAAgUfL        HTAA_getUnfoldedLine
+#endif /*SHORT_NAMES*/
+
+
+/*
+
+Default filenames
+
+ */
+#ifndef PASSWD_FILE
+#define PASSWD_FILE     "/home2/luotonen/passwd"
+#endif
+
+#ifndef GROUP_FILE
+#define GROUP_FILE      "/home2/luotonen/group"
+#endif
+
+#define ACL_FILE_NAME   ".www_acl"
+
+
+/*
+** Numeric constants
+*/
+#define MAX_USERNAME_LEN        16      /* @@ Longest allowed username    */
+#define MAX_PASSWORD_LEN        3*13    /* @@ Longest allowed password    */
+                                        /* (encrypted, so really only 3*8)*/
+#define MAX_METHODNAME_LEN      12      /* @@ Longest allowed method name */
+#define MAX_FIELDNAME_LEN       16      /* @@ Longest field name in       */
+                                        /* protection setup file          */
+#define MAX_PATHNAME_LEN        80      /* @@ Longest passwd/group file   */
+                                        /* patname to allow               */
+
+/*
+** Helpful macros
+*/
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+/*
+
+Datatype definitions
+
+  HTAASCHEME
+  
+   The enumeration HTAAScheme represents the possible authentication schemes used by the
+   WWW Access Authorization.
+   
+ */
+
+typedef enum {
+    HTAA_UNKNOWN,
+    HTAA_NONE,
+    HTAA_BASIC,
+    HTAA_PUBKEY,
+    HTAA_KERBEROS_V4,
+    HTAA_KERBEROS_V5,
+    HTAA_MAX_SCHEMES /* THIS MUST ALWAYS BE LAST! Number of schemes */
+} HTAAScheme;
+
+/*
+
+  ENUMERATION TO REPRESENT HTTP METHODS
+  
+ */
+
+typedef enum {
+    METHOD_UNKNOWN,
+    METHOD_GET,
+    METHOD_PUT
+} HTAAMethod;
+
+/*
+
+Authentication Schemes
+
+ */
+
+/* PUBLIC                                               HTAAScheme_enum()
+**              TRANSLATE SCHEME NAME TO A SCHEME ENUMERATION
+** ON ENTRY:
+**      name            is a string representing the scheme name.
+**
+** ON EXIT:
+**      returns         the enumerated constant for that scheme.
+*/
+PUBLIC HTAAScheme HTAAScheme_enum PARAMS((CONST char* name));
+
+
+/* PUBLIC                                               HTAAScheme_name()
+**                      GET THE NAME OF A GIVEN SCHEME
+** ON ENTRY:
+**      scheme          is one of the scheme enum values:
+**                      HTAA_NONE, HTAA_BASIC, HTAA_PUBKEY, ...
+**
+** ON EXIT:
+**      returns         the name of the scheme, i.e.
+**                      "none", "basic", "pubkey", ...
+*/
+PUBLIC char *HTAAScheme_name PARAMS((HTAAScheme scheme));
+
+/*
+
+Methods
+
+ */
+
+/* PUBLIC                                                   HTAAMethod_enum()
+**              TRANSLATE METHOD NAME INTO AN ENUMERATED VALUE
+** ON ENTRY:
+**      name            is the method name to translate.
+**
+** ON EXIT:
+**      returns         HTAAMethod enumerated value corresponding
+**                      to the given name.
+*/
+PUBLIC HTAAMethod HTAAMethod_enum PARAMS((CONST char * name));
+
+
+/* PUBLIC                                               HTAAMethod_name()
+**                      GET THE NAME OF A GIVEN METHOD
+** ON ENTRY:
+**      method          is one of the method enum values:
+**                      METHOD_GET, METHOD_PUT, ...
+**
+** ON EXIT:
+**      returns         the name of the scheme, i.e.
+**                      "GET", "PUT", ...
+*/
+PUBLIC char *HTAAMethod_name PARAMS((HTAAMethod method));
+
+
+/* PUBLIC                                               HTAAMethod_inList()
+**              IS A METHOD IN A LIST OF METHOD NAMES
+** ON ENTRY:
+**      method          is the method to look for.
+**      list            is a list of method names.
+**
+** ON EXIT:
+**      returns         YES, if method was found.
+**                      NO, if not found.
+*/
+PUBLIC BOOL HTAAMethod_inList PARAMS((HTAAMethod        method,
+                                     HTList *           list));
+/*
+
+Match Template Against Filename
+
+ */
+
+/* PUBLIC                                               HTAA_templateMatch()
+**              STRING COMPARISON FUNCTION FOR FILE NAMES
+**                 WITH ONE WILDCARD * IN THE TEMPLATE
+** NOTE:
+**      This is essentially the same code as in HTRules.c, but it
+**      cannot be used because it is embedded in between other code.
+**      (In fact, HTRules.c should use this routine, but then this
+**       routine would have to be more sophisticated... why is life
+**       sometimes so hard...)
+**
+** ON ENTRY:
+**      template        is a template string to match the file name
+**                      agaist, may contain a single wildcard
+**                      character * which matches zero or more
+**                      arbitrary characters.
+**      filename        is the filename (or pathname) to be matched
+**                      agaist the template.
+**
+** ON EXIT:
+**      returns         YES, if filename matches the template.
+**                      NO, otherwise.
+*/
+PUBLIC BOOL HTAA_templateMatch PARAMS((CONST char * template,
+                                       CONST char * filename));
+
+
+/* PUBLIC                                               HTAA_templateCaseMatch()
+**              STRING COMPARISON FUNCTION FOR FILE NAMES
+**                 WITH ONE WILDCARD * IN THE TEMPLATE (Case Insensitive)
+** NOTE:
+**      This is essentially the same code as in HTAA_templateMatch, but
+**      it compares case insensitive (for VMS). Reason for this routine
+**      is that HTAA_templateMatch gets called from several places, also
+**      there where a case sensitive match is needed, so one cannot just
+**      change the HTAA_templateMatch routine for VMS.
+**
+** ON ENTRY:
+**      template        is a template string to match the file name
+**                      agaist, may contain a single wildcard
+**                      character * which matches zero or more
+**                      arbitrary characters.
+**      filename        is the filename (or pathname) to be matched
+**                      agaist the template.
+**
+** ON EXIT:
+**      returns         YES, if filename matches the template.
+**                      NO, otherwise.
+*/
+PUBLIC BOOL HTAA_templateCaseMatch PARAMS((CONST char * template,
+                                         CONST char * filename));
+
+
+/* PUBLIC                                       HTAA_makeProtectionTemplate()
+**              CREATE A PROTECTION TEMPLATE FOR THE FILES
+**              IN THE SAME DIRECTORY AS THE GIVEN FILE
+**              (Used by server if there is no fancier way for
+**              it to tell the client, and by browser if server
+**              didn't send WWW-ProtectionTemplate: field)
+** ON ENTRY:
+**      docname is the document pathname (from URL).
+**
+** ON EXIT:
+**      returns a template matching docname, and other files
+**              files in that directory.
+**
+**              E.g.  /foo/bar/x.html  =>  /foo/bar/ *
+**                                                  ^
+**                              Space only to prevent it from
+**                              being a comment marker here,
+**                              there really isn't any space.
+*/
+PUBLIC char *HTAA_makeProtectionTemplate PARAMS((CONST char * docname));
+/*
+
+MIME Argument List Parser
+
+ */
+
+
+/* PUBLIC                                               HTAA_parseArgList()
+**              PARSE AN ARGUMENT LIST GIVEN IN A HEADER FIELD
+** ON ENTRY:
+**      str     is a comma-separated list:
+**
+**                      item, item, item
+**              where
+**                      item ::= value
+**                             | name=value
+**                             | name="value"
+**
+**              Leading and trailing whitespace is ignored
+**              everywhere except inside quotes, so the following
+**              examples are equal:
+**
+**                      name=value,foo=bar
+**                       name="value",foo="bar"
+**                        name = value ,  foo = bar
+**                         name = "value" ,  foo = "bar"
+**
+** ON EXIT:
+**      returns a list of name-value pairs (actually HTAssocList*).
+**              For items with no name, just value, the name is
+**              the number of order number of that item. E.g.
+**              "1" for the first, etc.
+*/
+PUBLIC HTList *HTAA_parseArgList PARAMS((char * str));
+
+/*
+
+Header Line Reader
+
+ */
+
+/* PUBLIC                                               HTAA_setupReader()
+**              SET UP HEADER LINE READER, i.e. give
+**              the already-read-but-not-yet-processed
+**              buffer of text to be read before more
+**              is read from the socket.
+** ON ENTRY:
+**      start_of_headers is a pointer to a buffer containing
+**                      the beginning of the header lines
+**                      (rest will be read from a socket).
+**      length          is the number of valid characters in
+**                      'start_of_headers' buffer.
+**      soc             is the socket to use when start_of_headers
+**                      buffer is used up.
+** ON EXIT:
+**      returns         nothing.
+**                      Subsequent calls to HTAA_getUnfoldedLine()
+**                      will use this buffer first and then
+**                      proceed to read from socket.
+*/
+PUBLIC void HTAA_setupReader PARAMS((char *     start_of_headers,
+                                     int        length,
+				     void *	handle,
+                                     int        soc));
+
+
+/* PUBLIC                                               HTAA_getUnfoldedLine()
+**              READ AN UNFOLDED HEADER LINE FROM SOCKET
+** ON ENTRY:
+**      HTAA_setupReader must absolutely be called before
+**      this function to set up internal buffer.
+**
+** ON EXIT:
+**      returns a newly-allocated character string representing
+**              the read line.  The line is unfolded, i.e.
+**              lines that begin with whitespace are appended
+**              to current line.  E.g.
+**
+**                      Field-Name: Blaa-Blaa
+**                       This-Is-A-Continuation-Line
+**                       Here-Is_Another
+**
+**              is seen by the caller as:
+**
+**      Field-Name: Blaa-Blaa This-Is-A-Continuation-Line Here-Is_Another
+**
+*/
+PUBLIC char *HTAA_getUnfoldedLine NOPARAMS;
+
+#endif  /* NOT HTAAUTIL_H */
+/*
+
+   End of file HTAAUtil.h. */
diff --git a/WWW/Library/Implementation/HTACL.c b/WWW/Library/Implementation/HTACL.c
new file mode 100644
index 00000000..aeafcafd
--- /dev/null
+++ b/WWW/Library/Implementation/HTACL.c
@@ -0,0 +1,221 @@
+
+/* MODULE							HTACL.c
+**		ACCESS CONTROL LIST ROUTINES
+**
+** AUTHORS:
+**	AL	Ari Luotonen	luotonen@dxcern.cern.ch
+**	MD 	Mark Donszelmann    duns@vxdeop.cern.ch
+**
+** HISTORY:
+**	 8 Nov 93  MD	(VMS only) case insensitive compare reading acl entry, filename
+**
+**
+** BUGS:
+**
+**
+*/
+
+
+#include "HTUtils.h"
+
+/*#include <stdio.h> included by HTUtils.h -- FM *//* FILE */
+#include <string.h>
+
+#include "HTAAFile.h"	/* File routines	*/
+#include "HTGroup.h"	/* GroupDef		*/
+#include "HTACL.h"	/* Implemented here	*/
+
+#include "LYLeaks.h"
+
+/* PUBLIC						HTAA_getAclFilename()
+**	    RESOLVE THE FULL PATHNAME OF ACL FILE FOR A GIVEN FILE
+** ON ENTRY:
+**	path	is the pathname of the file for which to
+**		ACL file should be found.
+**
+**		ACL filename is computed by replacing
+**		the filename by .www_acl in the pathname
+**		(this is done to a local copy, of course).
+**
+** ON EXIT:
+**	returns	the absolute pathname of ACL file
+**		(which is automatically freed next time
+**		this fuction is called).
+*/
+PUBLIC char *HTAA_getAclFilename ARGS1(CONST char *, pathname)
+{
+    static char * local_copy = NULL;
+    static char * acl_path = NULL;
+    char * directory = NULL;
+    char * filename = NULL;
+
+    StrAllocCopy(local_copy, pathname);	/* Also frees local_copy */
+                                        /* from previous call.   */
+
+    directory = local_copy;
+    filename = strrchr(directory, '/');
+    if (!filename) {		/* No path in front of filename */
+	directory = ".";	/* So use current directory */
+	filename = local_copy;	/* and the pathname itself is the filename */
+    }
+    else {
+	*filename = '\0'; /* Truncate filename off from directory path */
+	filename++;	  /* and the filename begins from the next character */
+    }
+    
+    StrAllocCopy(acl_path, directory);	/* Also frees acl_path */
+                                        /* from previous call. */
+    StrAllocCat(acl_path, "/");
+    StrAllocCat(acl_path, ACL_FILE_NAME);
+
+    return acl_path;
+}
+
+
+/* PUBLIC						HTAA_openAcl()
+**		OPEN THE ACL FILE FOR THE GIVEN DOCUMENT
+** ON ENTRY:
+**	pathname	is the absolute pathname of
+**			the file to be accessed.
+**
+** ON EXIT:
+**	returns		the FILE* to open ACL.
+**			NULL, if ACL not found.
+*/
+PUBLIC FILE *HTAA_openAcl ARGS1(CONST char *, pathname)
+{
+    return fopen(HTAA_getAclFilename(pathname), "r");
+}
+
+
+/* PUBLIC						HTAA_closeAcl()
+**			CLOSE ACL FILE
+** ON ENTRY:
+**	acl_file is Access Control List file to close.
+**
+** ON EXIT:
+**	returns	nothing.
+*/
+PUBLIC void HTAA_closeAcl ARGS1(FILE *, acl_file)
+{
+    if (acl_file)  fclose(acl_file);
+}
+
+
+/* PUBLIC						HTAA_getAclEntry()
+**			CONSULT THE ACCESS CONTROL LIST AND
+**			GIVE A LIST OF GROUPS (AND USERS)
+**			AUTHORIZED TO ACCESS A GIVEN FILE
+** ON ENTRY:
+**	acl_file	is an open ACL file.
+**	pathname	is the absolute pathname of
+**			the file to be accessed.
+**	method		is the method for which access is wanted.
+**
+** ALC FILE FORMAT:
+**
+**	template : method, method, ... : group@addr, user, group, ...
+**
+**	The last item is in fact in exactly the same format as
+**	group definition in group file, i.e. everything that
+**	follows the 'groupname:' part,
+**	e.g.
+**		user, group, user@address, group@address,
+**		(user,group,...)@(address, address, ...)
+**
+** ON EXIT:
+**	returns		NULL, if there is no entry for the file in the ACL,
+**			or ACL doesn't exist.
+**			If there is, a GroupDef object containing the
+**			group and user names allowed to access the file
+**			is returned (this is automatically freed
+**			next time this function is called).
+** IMPORTANT:
+**	Returns the first entry with matching template and
+**	method. This function should be called multiple times
+**	to process all the valid entries (until it returns NULL).
+**	This is because there can be multiple entries like:
+**
+**		*.html : get,put : ari,timbl,robert
+**		*.html : get	 : jim,james,jonathan,jojo
+**
+** NOTE:
+**	The returned group definition may well contain references
+**	to groups defined in group file. Therefore these references
+**	must be resolved according to that rule file by function
+**	HTAA_resolveGroupReferences() (group file is read in by
+**	HTAA_readGroupFile()) and after that access authorization
+**	can be checked with function HTAA_userAndInetGroup().
+*/
+PUBLIC GroupDef *HTAA_getAclEntry ARGS3(FILE *,		acl_file,
+					CONST char *,	pathname,
+					HTAAMethod,	method)
+{
+    static GroupDef * group_def = NULL;
+    CONST char * filename;
+    int len;
+    char *buf;
+
+    if (!acl_file) return NULL;		/* ACL doesn't exist */
+    
+    if (group_def) {
+	GroupDef_delete(group_def);	/* From previous call */
+	group_def = NULL;
+    }
+
+    if (!(filename = strrchr(pathname, '/')))
+	filename = pathname;
+    else filename++;	/* Skip slash */
+
+    len = strlen(filename);
+
+    if (!(buf = (char*)malloc((strlen(filename)+2)*sizeof(char))))
+	outofmem(__FILE__, "HTAA_getAuthorizedGroups");
+    
+    while (EOF != HTAAFile_readField(acl_file, buf, len+1)) {
+#ifdef VMS
+	if (HTAA_templateCaseMatch(buf, filename)) {
+#else /* not VMS */
+	if (HTAA_templateMatch(buf, filename)) {
+#endif /* not VMS */
+	    HTList *methods = HTList_new();
+	    HTAAFile_readList(acl_file, methods, MAX_METHODNAME_LEN);
+	    if (TRACE) {
+		fprintf(stderr,
+			"Filename '%s' matched template '%s', allowed methods:",
+			filename, buf);
+	    }	
+	    if (HTAAMethod_inList(method, methods)) {	/* right method? */
+		if (TRACE)
+		    fprintf(stderr, " METHOD OK\n");
+		HTList_delete(methods);
+		methods = NULL;
+		FREE(buf);
+		group_def = HTAA_parseGroupDef(acl_file);
+		/*
+		** HTAA_parseGroupDef() already reads the record
+		** separator so we don't call HTAAFile_nextRec().
+		*/
+		return group_def;
+	    } else if (TRACE) {
+	        fprintf(stderr, " METHOD NOT FOUND\n");
+	    }
+	    HTList_delete(methods);
+	    methods = NULL;
+	}	/* if template match */
+	else {
+	    if (TRACE) {
+		fprintf(stderr,
+			"Filename '%s' didn't match template '%s'\n",
+			filename, buf);
+	    }
+	}
+
+	HTAAFile_nextRec(acl_file);
+    }	/* while not eof */
+    FREE(buf);
+
+    return NULL;	/* No entry for requested file */
+                        /* (or an empty entry).        */
+}
+
diff --git a/WWW/Library/Implementation/HTACL.h b/WWW/Library/Implementation/HTACL.h
new file mode 100644
index 00000000..d6ae3c6c
--- /dev/null
+++ b/WWW/Library/Implementation/HTACL.h
@@ -0,0 +1,110 @@
+/*                                ACCESS CONTROL LIST ROUTINES
+                                             
+ */
+
+#ifndef HTACL_H
+#define HTACL_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+#include "HTAAUtil.h"
+#include "HTGroup.h"
+
+#ifdef SHORT_NAMES
+#define HTAAgAFn        HTAA_getAclFilename
+#define HTAAoACL        HTAA_openAcl
+#define HTAAcACL        HTAA_closeAcl
+#define HTAAgAEn        HTAA_getAclEntry
+#endif /* SHORT_NAMES */
+
+/*
+
+Opening Access Control List File
+
+ */
+
+/* PUBLIC                                               HTAA_openAcl()
+**              OPEN THE ACL FILE FOR THE GIVEN DOCUMENT
+** ON ENTRY:
+**      pathname        is the absolute pathname of
+**                      the file to be accessed.
+**
+** ON EXIT:
+**      returns         the FILE* to open ACL.
+**                      NULL, if ACL not found.
+*/
+PUBLIC FILE *HTAA_openAcl PARAMS((CONST char * pathname));
+
+
+/* PUBLIC                                               HTAA_closeAcl()
+**                      CLOSE ACL FILE
+** ON ENTRY:
+**      acl_file is Access Control List file to close.
+**
+** ON EXIT:
+**      returns nothing.
+*/
+PUBLIC void HTAA_closeAcl PARAMS((FILE * acl_file));
+/*
+
+Getting ACL Entry
+
+ */
+
+/* PUBLIC                                               HTAA_getAclEntry()
+**                      CONSULT THE ACCESS CONTROL LIST AND
+**                      GIVE A LIST OF GROUPS (AND USERS)
+**                      AUTHORIZED TO ACCESS A GIVEN FILE
+** ON ENTRY:
+**      acl_file        is an open ACL file.
+**      pathname        is the absolute pathname of
+**                      the file to be accessed.
+**      method          is the method for which access is wanted.
+**
+** ALC FILE FORMAT:
+**
+**      template : method, method, ... : group@addr, user, group, ...
+**
+**      The last item is in fact in exactly the same format as
+**      group definition in group file, i.e. everything that
+**      follows the 'groupname:' part,
+**      e.g.
+**              user, group, user@address, group@address,
+**              (user,group,...)@(address, address, ...)
+**
+** ON EXIT:
+**      returns         NULL, if there is no entry for the file in the ACL,
+**                      or ACL doesn't exist.
+**                      If there is, a GroupDef object containing the
+**                      group and user names allowed to access the file
+**                      is returned (this is automatically freed
+**                      next time this function is called).
+** IMPORTANT:
+**      Returns the first entry with matching template and
+**      method. This function should be called multiple times
+**      to process all the valid entries (until it returns NULL).
+**      This is because there can be multiple entries like:
+**
+**              *.html : get,put : ari,timbl,robert
+**              *.html : get     : jim,james,jonathan,jojo
+**
+** NOTE:
+**      The returned group definition may well contain references
+**      to groups defined in group file. Therefore these references
+**      must be resolved according to that rule file by function
+**      HTAA_resolveGroupReferences() (group file is read in by
+**      HTAA_readGroupFile()) and after that access authorization
+**      can be checked with function HTAA_userAndInetGroup().
+*/
+PUBLIC GroupDef *HTAA_getAclEntry PARAMS((FILE *        acl_file,
+                                          CONST char *  pathname,
+                                          HTAAMethod    method));
+/*
+
+ */
+
+#endif /* not HTACL_H */
+/*
+
+   End of file HTACL.h.  */
diff --git a/WWW/Library/Implementation/HTAccess.c b/WWW/Library/Implementation/HTAccess.c
new file mode 100644
index 00000000..1d9725f5
--- /dev/null
+++ b/WWW/Library/Implementation/HTAccess.c
@@ -0,0 +1,1199 @@
+/*		Access Manager					HTAccess.c
+**		==============
+**
+** Authors
+**	TBL	Tim Berners-Lee timbl@info.cern.ch
+**	JFG	Jean-Francois Groff jfg@dxcern.cern.ch
+**	DD	Denis DeLaRoca (310) 825-4580  <CSP1DWD@mvs.oac.ucla.edu>
+**	FM	Foteos Macrides macrides@sci.wfeb.edu
+**      PDM     Danny Mayer mayer@ljo.dec.com
+**
+** History
+**       8 Jun 92 Telnet hopping prohibited as telnet is not secure TBL
+**	26 Jun 92 When over DECnet, suppressed FTP, Gopher and News. JFG
+**	 6 Oct 92 Moved HTClientHost and logfile into here. TBL
+**	17 Dec 92 Tn3270 added, bug fix. DD
+**	 4 Feb 93 Access registration, Search escapes bad chars TBL
+**		  PARAMETERS TO HTSEARCH AND HTLOADRELATIVE CHANGED
+**	28 May 93 WAIS gateway explicit if no WAIS library linked in.
+**	31 May 94 Added DIRECT_WAIS support for VMS. FM
+**      27 Jan 95 Fixed proxy support to use NNTPSERVER for checking
+**                whether or not to use the proxy server. PDM
+**      27 Jan 95 Ensured that proxy service will be overridden for files
+**		  on the local host (because HTLoadFile() doesn't try ftp
+**		  for those) and will substitute ftp for remote files. FM
+**      28 Jan 95 Tweeked PDM's proxy override mods to handle port info
+**		  for news and wais URL's. FM
+**
+** Bugs
+**	This module assumes that that the graphic object is hypertext, as it
+**	needs to select it when it has been loaded.  A superclass needs to be
+**	defined which accepts select and select_anchor.
+*/
+
+#ifdef VMS
+#define DIRECT_WAIS
+#endif /* VMS */
+
+#ifndef DEFAULT_WAIS_GATEWAY
+#define DEFAULT_WAIS_GATEWAY "http://www.w3.org:8001"
+#endif
+
+#include "HTUtils.h"
+#include "HTAlert.h"
+/* Implements:
+*/
+#include "HTAccess.h"
+
+/* Uses:
+*/
+#include "HTParse.h"
+#include "HTML.h"		/* SCW */
+
+#ifndef NO_RULES
+#include "HTRules.h"
+#endif
+
+/*#include <stdio.h> included by HTUtils.h -- FM */
+
+#include "HTList.h"
+#include "HText.h"	/* See bugs above */
+#include "HTAlert.h"
+#include "HTCJK.h"
+
+#include "LYexit.h"
+#include "LYLeaks.h"
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+extern HTCJKlang HTCJK;
+
+/*
+**  These flags may be set to modify the operation of this module
+*/
+PUBLIC char * HTClientHost = NULL; /* Name of remote login host if any */
+PUBLIC FILE * HTlogfile = NULL;	   /* File to which to output one-liners */
+PUBLIC BOOL HTSecure = NO;	   /* Disable access for telnet users? */
+
+PUBLIC BOOL using_proxy = NO; /* are we using a proxy gateway? */
+
+/*
+**  To generate other things, play with these:
+*/
+PUBLIC HTFormat HTOutputFormat = NULL;
+PUBLIC HTStream* HTOutputStream = NULL;	/* For non-interactive, set this */ 
+
+PRIVATE HTList * protocols = NULL;   /* List of registered protocol descriptors */
+
+PUBLIC char *use_this_url_instead = NULL;
+
+
+PRIVATE void free_protocols NOARGS
+{
+    HTList_delete(protocols);
+    protocols = NULL;
+}
+
+/*	Register a Protocol				HTRegisterProtocol
+**	-------------------
+*/
+PUBLIC BOOL HTRegisterProtocol ARGS1(
+	HTProtocol *,	protocol)
+{
+    if (!protocols) {
+        protocols = HTList_new();
+	atexit(free_protocols);
+    }
+    HTList_addObject(protocols, protocol);
+    return YES;
+}
+
+
+/*	Register all known protocols
+**	----------------------------
+**
+**	Add to or subtract from this list if you add or remove protocol modules.
+**	This routine is called the first time the protocol list is needed,
+**	unless any protocols are already registered, in which case it is not called.
+**	Therefore the application can override this list.
+**
+**	Compiling with NO_INIT prevents all known protocols from being forced
+**	in at link time.
+*/
+#ifndef NO_INIT
+PRIVATE void HTAccessInit NOARGS			/* Call me once */
+{
+#ifdef GLOBALREF_IS_MACRO
+extern GLOBALREF (HTProtocol, HTTP);
+extern GLOBALREF (HTProtocol, HTTPS);
+extern GLOBALREF (HTProtocol, HTFile);
+extern GLOBALREF (HTProtocol, HTTelnet);
+extern GLOBALREF (HTProtocol, HTTn3270);
+extern GLOBALREF (HTProtocol, HTRlogin);
+#ifndef DECNET
+extern GLOBALREF (HTProtocol, HTFTP);
+extern GLOBALREF (HTProtocol, HTNews);
+extern GLOBALREF (HTProtocol, HTNNTP);
+extern GLOBALREF (HTProtocol, HTSNews);
+extern GLOBALREF (HTProtocol, HTGopher);
+extern GLOBALREF (HTProtocol, HTCSO);
+extern GLOBALREF (HTProtocol, HTFinger);
+#ifdef DIRECT_WAIS
+extern GLOBALREF (HTProtocol, HTWAIS);
+#endif /* DIRECT_WAIS */
+#endif /* !DECNET */
+#else
+GLOBALREF HTProtocol HTTP, HTTPS, HTFile, HTTelnet, HTTn3270, HTRlogin;
+#ifndef DECNET
+GLOBALREF  HTProtocol HTFTP, HTNews, HTNNTP, HTSNews, HTGopher, HTCSO;
+GLOBALREF HTProtocol HTFinger;
+#ifdef DIRECT_WAIS
+GLOBALREF  HTProtocol HTWAIS;
+#endif /* DIRECT_WAIS */
+#endif /* !DECNET */
+#endif /* GLOBALREF_IS_MACRO */
+    HTRegisterProtocol(&HTTP);
+    HTRegisterProtocol(&HTTPS);
+    HTRegisterProtocol(&HTFile);
+    HTRegisterProtocol(&HTTelnet);
+    HTRegisterProtocol(&HTTn3270);
+    HTRegisterProtocol(&HTRlogin);
+#ifndef DECNET
+    HTRegisterProtocol(&HTFTP);
+    HTRegisterProtocol(&HTNews);
+    HTRegisterProtocol(&HTNNTP);
+    HTRegisterProtocol(&HTSNews);
+    HTRegisterProtocol(&HTGopher);
+    HTRegisterProtocol(&HTCSO);
+    HTRegisterProtocol(&HTFinger);
+#ifdef DIRECT_WAIS
+    HTRegisterProtocol(&HTWAIS);
+#endif /* DIRECT_WAIS */
+#endif /* !DECNET */
+    LYRegisterLynxProtocols();
+}
+#endif /* !NO_INIT */
+
+/*                                                  override_proxy()
+**
+**  Check the no_proxy environment variable to get the list
+**  of hosts for which proxy server is not consulted.
+**
+**  no_proxy is a comma- or space-separated list of machine
+**  or domain names, with optional :port part.  If no :port
+**  part is present, it applies to all ports on that domain.
+**
+**  Example:
+**          no_proxy="cern.ch,some.domain:8001"
+**
+**  Use "*" to override all proxy service:
+**	     no_proxy="*"
+*/
+PRIVATE BOOL override_proxy ARGS1(CONST char *, addr)
+{
+    CONST char * no_proxy = getenv("no_proxy");
+    char * p = NULL;
+    char * host = NULL;
+    char * access = NULL;
+    int port = 0;
+    int h_len = 0;
+
+    /*
+     * Check for global override.
+     */
+    if (no_proxy) {
+        if (!strcmp(no_proxy, "*"))
+            return YES;
+    }
+
+    /*
+     *  Never proxy file:// URLs if they are on the local host.
+     *  HTLoadFile() will not attempt ftp for those if direct
+     *  access fails.  We'll check that first, in case no_proxy
+     *  hasn't been defined. - FM
+     */
+    if (!addr)
+        return NO;
+    if (!(host = HTParse(addr, "", PARSE_HOST)))
+        return NO;
+    if (!*host) {
+        FREE(host);
+	return NO;
+    }
+
+    if((access = HTParse(addr, "", PARSE_ACCESS))) {
+        if (0==strcmp("file", access) &&
+	    (0==strcmp(host, "localhost") ||
+#ifdef VMS
+             0==strcasecomp(host, HTHostName())))
+#else
+             0==strcmp(host, HTHostName())))
+#endif /* VMS */
+        {
+	    FREE(host);
+	    FREE(access);
+	    return YES;
+	}
+	FREE(access);
+    }
+
+    if (!no_proxy) {
+        FREE(host);
+        return NO;
+    }
+
+    if (NULL != (p = strchr(host, ':'))) {	/* Port specified */
+        *p++ = 0;                   		/* Chop off port */
+        port = atoi(p);
+    } else {					/* Use default port */
+        access = HTParse(addr, "", PARSE_ACCESS);
+        if (access) {
+            if      (!strcmp(access,"http"))    port = 80;
+            else if (!strcmp(access,"https"))   port = 443;
+            else if (!strcmp(access,"ftp"))     port = 21;
+            else if (!strcmp(access,"gopher"))  port = 70;
+            else if (!strcmp(access,"cso"))     port = 105;
+	    else if (!strcmp(access,"news"))	port = 119;
+	    else if (!strcmp(access,"snews"))	port = 563;
+	    else if (!strcmp(access,"nntp"))	port = 119;
+	    else if (!strcmp(access,"wais"))	port = 210;
+	    else if (!strcmp(access,"finger"))	port = 79;
+            FREE(access);
+        }
+    }
+    if (!port)
+        port = 80;                  /* Default */
+    h_len = strlen(host);
+
+    while (*no_proxy) {
+        CONST char * end;
+        CONST char * colon = NULL;
+        int templ_port = 0;
+        int t_len;
+
+        while (*no_proxy && (WHITE(*no_proxy) || *no_proxy==','))
+            no_proxy++;             /* Skip whitespace and separators */
+
+        end = no_proxy;
+        while (*end && !WHITE(*end) && *end != ',') {  /* Find separator */
+            if (*end==':') colon = end;                /* Port number given */
+            end++;
+        }
+
+        if (colon) {
+            templ_port = atoi(colon+1);
+            t_len = colon - no_proxy;
+        }
+        else {
+            t_len = end - no_proxy;
+        }
+
+        if ((!templ_port || templ_port == port)  &&
+            (t_len > 0  &&  t_len <= h_len  &&
+             !strncmp(host + h_len - t_len, no_proxy, t_len))) {
+            FREE(host);
+            return YES;
+        }
+        if (*end)
+	    no_proxy = end+1;
+        else
+	    break;
+    }
+
+    FREE(host);
+    return NO;
+}
+
+
+/*		Find physical name and access protocol
+**		--------------------------------------
+**
+**
+** On entry,
+**	addr		must point to the fully qualified hypertext reference.
+**	anchor		a pareent anchor with whose address is addr
+**
+** On exit,
+**	returns		HT_NO_ACCESS		Error has occured.
+**			HT_OK			Success
+**
+*/
+PRIVATE int get_physical ARGS2(
+	CONST char *,		addr,
+	HTParentAnchor *,	anchor)
+{
+    char * access=NULL;		/* Name of access method */
+    char * physical=NULL;
+    char * Server_addr=NULL;
+
+#ifndef NO_RULES
+    physical = HTTranslate(addr);
+    if (!physical) {
+	return HT_FORBIDDEN;
+    }
+    if (anchor->isISMAPScript == TRUE) {
+        StrAllocCat(physical, "?0,0");
+	if(TRACE)
+	    fprintf(stderr,"HTAccess: Appending '?0,0' coordinate pair.\n");
+    }
+    HTAnchor_setPhysical(anchor, physical);
+    FREE(physical);			/* free our copy */
+#else
+    if (anchor->isISMAPScript == TRUE) {
+        StrAllocCopy(physical, addr);
+	StrAllocCat(physical, "?0,0");
+	if(TRACE)
+	    fprintf(stderr,"HTAccess: Appending '?0,0' coordinate pair.\n");
+	HTAnchor_setPhysical(anchor, physical);
+	FREE(physical);			/* free our copy */
+    } else {
+        HTAnchor_setPhysical(anchor, addr);
+    }
+#endif /* NO_RULES */
+
+    access =  HTParse(HTAnchor_physical(anchor),
+    		"file:", PARSE_ACCESS);
+
+/*	Check whether gateway access has been set up for this
+**
+**	This function can be replaced by the rule system above.
+*/
+#define USE_GATEWAYS
+#ifdef USE_GATEWAYS
+    /*
+     *	Make sure the using_proxy variable is false.
+     */
+    using_proxy = NO;
+
+    /*
+     *  News is different, so we need to check the name of the server,
+     *  as well as the default port for selective exclusions.
+     */
+    if (strcasecomp(access, "news") == 0) {
+	char *host = NULL;
+	if ((host = HTParse(addr, "", PARSE_HOST))) {
+	    if (!(strchr(host, ':'))) {
+	        StrAllocCopy(Server_addr, "news://");
+	        StrAllocCat(Server_addr, host);
+		StrAllocCat(Server_addr, ":119/");
+	    }
+	    FREE(host);
+        } else if (getenv("NNTPSERVER")) {
+            StrAllocCopy(Server_addr, "news://");
+            StrAllocCat(Server_addr, (char *)getenv("NNTPSERVER"));
+            StrAllocCat(Server_addr, ":119/");
+         }
+    }
+    /*
+     * Wais also needs checking of the default port for selective exclusions.
+     */
+    else if (strcasecomp(access, "wais") == 0) {
+	char *host = NULL;
+	if ((host = HTParse(addr, "", PARSE_HOST))) {
+	    if (!(strchr(host, ':'))) {
+	        StrAllocCopy(Server_addr, "wais://");
+	        StrAllocCat(Server_addr, host);
+		StrAllocCat(Server_addr, ":210/");
+	    }
+	    FREE(host);
+	}
+	else
+            StrAllocCopy(Server_addr, addr);
+    }
+    else {
+        StrAllocCopy(Server_addr, addr);
+    }
+
+    if(!override_proxy(Server_addr)) {
+	char * gateway_parameter, *gateway, *proxy;
+
+	/* search for gateways */
+	gateway_parameter = (char *)malloc(strlen(access)+20);
+	if (gateway_parameter == NULL)
+	    outofmem(__FILE__, "HTLoad");
+	strcpy(gateway_parameter, "WWW_");
+	strcat(gateway_parameter, access);
+	strcat(gateway_parameter, "_GATEWAY");
+	gateway = (char *)getenv(gateway_parameter); /* coerce for decstation */
+
+	/* search for proxy servers */
+	/*
+	 * If we got to here, a file URL is for ftp on a remote host. - FM
+	 */
+	if (0==strcmp(access, "file"))
+	    strcpy(gateway_parameter, "ftp");
+	else
+	    strcpy(gateway_parameter, access);
+        strcat(gateway_parameter, "_proxy");
+	proxy = (char *)getenv(gateway_parameter);
+	FREE(gateway_parameter);
+
+	if(TRACE && gateway)
+	    fprintf(stderr,"Gateway found: %s\n",gateway);
+	if(TRACE && proxy)
+	    fprintf(stderr,"proxy server found: %s\n",proxy);
+	
+#ifndef DIRECT_WAIS
+	if (!gateway && 0==strcmp(access, "wais")) {
+	    gateway = DEFAULT_WAIS_GATEWAY;
+	}
+#endif /* direct wais */
+
+	/* proxy servers have precedence over gateway servers */
+	if(proxy) {
+	    char * gatewayed=0;
+            StrAllocCopy(gatewayed,proxy);
+	    /*
+	     * Ensure that the proxy server uses ftp for file URLs. - FM
+	     */
+	    if (0==strncmp(addr, "file", 4)) {
+                StrAllocCat(gatewayed, "ftp");
+                StrAllocCat(gatewayed, addr+4);
+	    } else
+                StrAllocCat(gatewayed, addr);
+            using_proxy = YES;
+	    if (anchor->isISMAPScript == TRUE)
+	        StrAllocCat(gatewayed, "?0,0");
+            HTAnchor_setPhysical(anchor, gatewayed);
+	    FREE(gatewayed);
+	    FREE(access);
+
+    	    access =  HTParse(HTAnchor_physical(anchor),
+    		"http:", PARSE_ACCESS);
+
+	} else if (gateway) {
+	    char * path = HTParse(addr, "",
+	    	PARSE_HOST + PARSE_PATH + PARSE_PUNCTUATION);
+		/* Chop leading / off to make host into part of path */
+	    char * gatewayed = HTParse(path+1, gateway, PARSE_ALL);
+	    FREE(path);
+            HTAnchor_setPhysical(anchor, gatewayed);
+	    FREE(gatewayed);
+	    FREE(access);
+	    
+    	    access =  HTParse(HTAnchor_physical(anchor),
+    		"http:", PARSE_ACCESS);
+	} 
+    }
+    FREE(Server_addr);
+#endif /* use gateways */
+
+
+    /*
+     *  Search registered protocols to find suitable one.
+     */
+    {
+	int i, n;
+#ifndef NO_INIT
+        if (!protocols) HTAccessInit();
+#endif
+	n = HTList_count(protocols);
+	for (i=0; i<n; i++) {
+	    HTProtocol *p = (HTProtocol *)HTList_objectAt(protocols, i);
+	    if (strcmp(p->name, access)==0) {
+		HTAnchor_setProtocol(anchor, p);
+		FREE(access);
+		return (HT_OK);
+	    }
+	}
+    }
+
+    FREE(access);
+    return HT_NO_ACCESS;
+}
+
+
+/*		Load a document
+**		---------------
+**
+**	This is an internal routine, which has an address AND a matching
+**	anchor.  (The public routines are called with one OR the other.)
+**
+** On entry,
+**	addr		must point to the fully qualified hypertext reference.
+**	anchor		a pareent anchor with whose address is addr
+**
+** On exit,
+**	returns		<0		Error has occured.
+**			HT_LOADED	Success
+**			HT_NO_DATA	Success, but no document loaded.
+**					(telnet sesssion started etc)
+**
+*/
+PRIVATE int HTLoad ARGS4(
+	CONST char *,		addr,
+	HTParentAnchor *,	anchor,
+	HTFormat,		format_out,
+	HTStream *,		sink)
+{
+    HTProtocol *p;
+    int status = get_physical(addr, anchor);
+    if (status == HT_FORBIDDEN) {
+        return HTLoadError(sink, 500, "Access forbidden by rule");
+    }
+    if (status < 0)
+        return status;	/* Can't resolve or forbidden */
+    
+    p = (HTProtocol *)HTAnchor_protocol(anchor);
+    anchor->underway = TRUE;            /* Hack to deal with caching */
+    status= (*(p->load))(HTAnchor_physical(anchor),
+    			anchor, format_out, sink);
+    anchor->underway = FALSE;
+    return status;
+}
+
+
+/*		Get a save stream for a document
+**		--------------------------------
+*/
+PUBLIC HTStream *HTSaveStream ARGS1(HTParentAnchor *, anchor)
+{
+    HTProtocol *p = (HTProtocol *)HTAnchor_protocol(anchor);
+    if (!p)
+        return NULL;
+    
+    return (*p->saveStream)(anchor);
+    
+}
+
+
+/*		Load a document - with logging etc
+**		----------------------------------
+**
+**	- Checks or documents already loaded
+**	- Logs the access
+**	- Allows stdin filter option
+**	- Trace ouput and error messages
+**
+**    On Entry,
+**	  anchor	    is the node_anchor for the document
+**        full_address      The address of the document to be accessed.
+**        filter            if YES, treat stdin as HTML
+**
+**    On Exit,
+**        returns    YES     Success in opening document
+**                   NO      Failure 
+**
+*/
+PRIVATE BOOL HTLoadDocument ARGS4(
+	CONST char *,		full_address,
+	HTParentAnchor *,	anchor,
+	HTFormat,		format_out,
+	HTStream*,		sink)
+
+{
+    int	        status;
+    HText *	text;
+    char * address_to_load =  (char *)full_address;
+    extern char LYforce_no_cache;		       /* from   GridText.c */
+    extern char LYoverride_no_cache;		       /* from LYMainLoop.c */
+    extern BOOL HText_hasNoCacheSet PARAMS((HText *text)); /* in GridText.c */
+    extern BOOL reloading;
+    extern char *redirecting_url;
+    extern BOOL permanent_redirection;
+    char *cp;
+    BOOL ForcingNoCache = LYforce_no_cache;
+    static int redirection_attempts = 0;
+
+#ifdef DIRED_SUPPORT
+    extern BOOLEAN lynx_edit_mode;
+#endif
+
+    if (TRACE)
+        fprintf (stderr, "HTAccess: loading document %s\n", address_to_load);
+
+    /*
+     *  Free use_this_url_instead and reset permanent_redirection
+     *  if not done elsewhere. - FM
+     */
+    FREE(use_this_url_instead);
+    permanent_redirection = FALSE;
+
+    /*
+     *  Make sure some yoyo doesn't send us 'round in circles
+     *  with redirecting URLs that point back to themselves.
+     *  We'll set the HTTP/1.1 limit of 5 redirections per
+     *  requested URL from a user.  - FM
+     */
+    if (redirection_attempts > 5) {
+        redirection_attempts = 0;
+	HTAlert("Redirection limit of 5 URL's reached.");
+        return NO;
+    }
+
+    /*
+     *  If we don't have POST content, check whether this is a previous
+     *  redirecting URL, and keep re-checking until we get to the final
+     *  destination or redirection limit.  If we do have POST content,
+     *  we didn't allow permanent redirection, and an interactive user
+     *  will be deciding whether to keep redirecting. - FM
+     */
+    if (!anchor->post_data) {
+        while ((cp = HTAnchor_physical(anchor)) &&
+	       !strncmp(cp, "Location=", 9)) {
+	    DocAddress NewDoc;
+
+	    if (TRACE) {
+		fprintf (stderr, "HTAccess: '%s' is a redirection URL.\n",
+				  anchor->address);
+		fprintf (stderr, "HTAccess: Redirecting to '%s'\n", cp+9);
+	    }
+
+	    /*
+	     *  Don't exceed the redirection_attempts limit. - FM
+	     */
+	    if (++redirection_attempts > 5) {
+		HTAlert("Redirection limit of 5 URL's reached.");
+		redirection_attempts = 0;
+		FREE(use_this_url_instead);
+ 		return NO;
+	    }
+
+	    /*
+	     * Set up the redirection. - FM
+	     */
+	    StrAllocCopy(use_this_url_instead, cp+9);
+	    NewDoc.address = use_this_url_instead;
+	    NewDoc.post_data = NULL;
+	    NewDoc.post_content_type = NULL;
+	    NewDoc.isHEAD = anchor->isHEAD;
+	    anchor = (HTParentAnchor *)HTAnchor_findAddress(&NewDoc);
+	}
+    }
+    /*
+     *  If we had previous redirection, go back and check out
+     *  that the URL under the current restrictions. - FM
+     */
+    if (use_this_url_instead) {
+	FREE(redirecting_url);
+        return(NO);
+    }
+
+    /*
+     *  See if we can use an already loaded document.
+     */
+    if (!LYforce_no_cache && (text = (HText *)HTAnchor_document(anchor))) {	
+	/*
+	 *  Already loaded.  Check it it's OK to use it. - FM
+	 */
+	if (LYoverride_no_cache || !HText_hasNoCacheSet(text)) {
+            if (TRACE)
+	        fprintf(stderr, "HTAccess: Document already in memory.\n");
+            HText_select(text);
+
+#ifdef DIRED_SUPPORT
+	    if (HTAnchor_format(anchor) == WWW_DIRED)
+	        lynx_edit_mode = TRUE;
+#endif
+	    redirection_attempts = 0;
+	    return YES;
+	} else {
+	    reloading = TRUE;
+	    ForcingNoCache = YES;
+            if (TRACE)
+	        fprintf(stderr, "HTAccess: Auto-reloading document.\n");
+	}
+    }
+
+    /*
+     *  Get the document from the net.  If we are auto-reloading,
+     *  the previous rendition will be freed in conjunction with
+     *  loading of the new rendition. - FM
+     */
+    LYforce_no_cache = NO;  /* reset after each time through */
+    status = HTLoad(address_to_load, anchor, format_out, sink);
+    if (TRACE) {
+	fprintf(stderr, "HTAccess:  status=%d\n", status);
+    }
+    
+    /*
+     *  Log the access if necessary.
+     */
+    if (HTlogfile) {
+	time_t theTime;
+	time(&theTime);
+	fprintf(HTlogfile, "%24.24s %s %s %s\n",
+	    ctime(&theTime),
+	    HTClientHost ? HTClientHost : "local",
+	    status<0 ? "FAIL" : "GET",
+	    full_address);
+	fflush(HTlogfile);	/* Actually update it on disk */
+	if (TRACE) fprintf(stderr, "Log: %24.24s %s %s %s\n",
+	    ctime(&theTime),
+	    HTClientHost ? HTClientHost : "local",
+	    status<0 ? "FAIL" : "GET",
+	    full_address);
+    }
+    
+    /*
+     *  Check out what we received from the net.
+     */
+    if (status == HT_REDIRECTING) {
+        /*  Exported from HTMIME.c, of all places. *//** NO!! - FM **/
+	/*
+	 *  Doing this via HTMIME.c meant that the redirection cover
+	 *  page was already loaded before we learned that we want a
+	 *  different URL.  Also, changing anchor->address, as Lynx
+	 *  was doing, meant we could never again access its hash
+	 *  table entry, creating an insolvable memory leak.  Instead,
+	 *  if we had a 301 status and set permanent_redirection,
+	 *  we'll load the new URL in anchor->physical, preceded by a
+	 *  token, which we can check to make replacements on subsequent
+	 *  access attempts.  We'll check recursively, and retrieve the
+	 *  final URL if we had multiple redirections to it.  If we just
+	 *  went to HTLoad now, as Lou originally had this, we couldn't do
+	 *  Lynx's security checks and alternate handling of some URL types.
+	 *  So, instead, we'll go all the way back to the top of getfile
+	 *  in LYGetFile.c when the status is HT_REDIRECTING.  This may
+	 *  seem bizarre, but it works like a charm! - FM
+	 */
+        if (TRACE) {
+            fprintf(stderr, "HTAccess: '%s' is a redirection URL.\n",
+			    address_to_load);
+            fprintf(stderr, "HTAccess: Redirecting to '%s'\n",
+			     redirecting_url);
+        }
+	/*
+	 *  Prevent circular references. 
+	 */
+	if (strcmp(address_to_load, redirecting_url)) { /* if different */
+	    /*
+	     *  Load token and redirecting url into anchor->physical
+	     *  if we had 301 Permanent redirection.  HTTP.c does not
+	     *  allow this if we have POST content. - FM
+	     */
+	    if (permanent_redirection) {
+	        StrAllocCopy(anchor->physical, "Location=");
+	        StrAllocCat(anchor->physical, redirecting_url);
+	    }
+
+            /*
+	     *  Set up flags before return to getfile. - FM
+	     */
+            StrAllocCopy(use_this_url_instead, redirecting_url);
+	    if (ForcingNoCache)
+	        LYforce_no_cache = YES;
+	    ++redirection_attempts;
+	    FREE(redirecting_url);
+	    permanent_redirection = FALSE;
+	    return(NO);
+	}
+	++redirection_attempts;
+	FREE(redirecting_url);
+	permanent_redirection = FALSE;
+	return(YES);
+    }
+
+    /*
+     *  We did not receive a redirecting URL. - FM
+     */
+    redirection_attempts = 0;
+    FREE(redirecting_url);
+    permanent_redirection = FALSE;
+
+    if (status == HT_LOADED) {
+	if (TRACE) {
+	    fprintf(stderr, "HTAccess: `%s' has been accessed.\n",
+	    full_address);
+	}
+	return YES;
+    }
+
+    if (status == HT_NO_DATA) {
+	if (TRACE) {
+	    fprintf(stderr, 
+	    "HTAccess: `%s' has been accessed, No data left.\n",
+	    full_address);
+	}
+	return NO;
+    }
+    
+    if (status == HT_NOT_LOADED) {
+	if (TRACE) {
+	    fprintf(stderr, 
+	    "HTAccess: `%s' has been accessed, No data loaded.\n",
+	    full_address);
+	}
+	return NO;
+    }
+    
+    if (status == HT_INTERRUPTED) {
+	if (TRACE) {
+	    fprintf(stderr, 
+	    "HTAccess: `%s' has been accessed, transfer interrupted.\n",
+	    full_address);
+	}
+/*	_HTProgress("Data transfer interrupted."); */
+	return NO;
+    }
+
+    if (status <= 0) {		/* Failure in accessing a document */
+	char *temp = NULL;
+	StrAllocCopy(temp, "Can't Access `");
+	StrAllocCat(temp, full_address);
+	StrAllocCat(temp, "'");
+	_HTProgress(temp);
+	FREE(temp);
+	if (TRACE) fprintf(stderr, 
+		"HTAccess: Can't access `%s'\n", full_address);
+	HTLoadError(sink, 500, "Unable to access document.");
+	return NO;
+    }
+ 
+    /*
+     *  If you get this, then please find which routine is returning
+     *  a positive unrecognised error code!
+     */
+    fprintf(stderr,
+ "**** HTAccess: socket or file number returned by obsolete load routine!\n");
+    fprintf(stderr,
+ "**** HTAccess: Internal software error. Please mail lynx_dev@sig.net!\n");
+    fprintf(stderr, "**** HTAccess: Status returned was: %d\n",status);
+    exit(-1);
+
+} /* HTLoadDocument */
+
+
+/*		Load a document from absolute name
+**		---------------
+**
+**    On Entry,
+**        addr     The absolute address of the document to be accessed.
+**        filter   if YES, treat document as HTML
+**
+**    On Exit,
+**        returns    YES     Success in opening document
+**                   NO      Failure 
+**
+**
+*/
+
+PUBLIC BOOL HTLoadAbsolute ARGS1(CONST DocAddress *,docaddr)
+{
+   return HTLoadDocument(docaddr->address,
+       		HTAnchor_parent(HTAnchor_findAddress(docaddr)),
+       			HTOutputFormat ? HTOutputFormat : WWW_PRESENT,
+			HTOutputStream);
+}
+
+
+#ifdef NOT_USED_CODE
+/*		Load a document from absolute name to stream
+**		--------------------------------------------
+**
+**    On Entry,
+**        addr     The absolute address of the document to be accessed.
+**        sink     if non-NULL, send data down this stream
+**
+**    On Exit,
+**        returns    YES     Success in opening document
+**                   NO      Failure 
+**
+**
+*/
+PUBLIC BOOL HTLoadToStream ARGS3(
+		CONST char *,	addr,
+		BOOL, 		filter,
+		HTStream *, 	sink)
+{
+   return HTLoadDocument(addr,
+       		HTAnchor_parent(HTAnchor_findAddress(addr)),
+       			HTOutputFormat ? HTOutputFormat : WWW_PRESENT,
+			sink);
+}
+#endif /* NOT_USED_CODE */
+
+
+/*		Load a document from relative name
+**		---------------
+**
+**    On Entry,
+**        relative_name     The relative address of the document
+**	  		    to be accessed.
+**
+**    On Exit,
+**        returns    YES     Success in opening document
+**                   NO      Failure 
+**
+**
+*/
+PUBLIC BOOL HTLoadRelative ARGS2(
+		CONST char *,		relative_name,
+		HTParentAnchor *,	here)
+{
+    DocAddress 		full_address;
+    BOOL       		result;
+    char * 		mycopy = 0;
+    char * 		stripped = 0;
+    char *		current_address =
+    				HTAnchor_address((HTAnchor*)here);
+
+    full_address.address = 0;
+    full_address.post_data = 0;
+    full_address.post_content_type = 0;
+    full_address.isHEAD = here->isHEAD;
+
+    StrAllocCopy(mycopy, relative_name);
+
+    stripped = HTStrip(mycopy);
+    full_address.address = HTParse(stripped,
+	           current_address,
+		   PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
+    result = HTLoadAbsolute(&full_address);
+    /*
+    **  If we got redirection, result will be NO, but use_this_url_instead
+    **  will be set.  The calling routine should check both and do whatever
+    **  is appropriate. - FM
+    */
+    FREE(full_address.address);
+    FREE(current_address);
+    FREE(mycopy);  /* Memory leak fixed 10/7/92 -- JFG */
+    return result;
+}
+
+
+/*		Load if necessary, and select an anchor
+**		--------------------------------------
+**
+**    On Entry,
+**        destination      	    The child or parenet anchor to be loaded.
+**
+**    On Exit,
+**        returns    YES     Success
+**                   NO      Failure 
+**
+*/
+PUBLIC BOOL HTLoadAnchor ARGS1(HTAnchor *,destination)
+{
+    HTParentAnchor * parent;
+    BOOL loaded = NO;
+    if (!destination) return NO;	/* No link */
+    
+    parent  = HTAnchor_parent(destination);
+    
+    if (HTAnchor_document(parent) == NULL) {	/* If not alread loaded */
+    						/* TBL 921202 */
+
+        BOOL result;
+        char * address = HTAnchor_address((HTAnchor*) parent);
+	result = HTLoadDocument(address, parent,
+		HTOutputFormat ? HTOutputFormat : WWW_PRESENT,
+		HTOutputStream);
+	FREE(address);
+	if (!result) return NO;
+	loaded = YES;
+    }
+    
+    {
+	HText *text = (HText*)HTAnchor_document(parent);
+	if (destination != (HTAnchor *)parent) {  /* If child anchor */
+	    HText_selectAnchor(text, 
+		    (HTChildAnchor*)destination); /* Double display? @@ */
+	} else {
+	    if (!loaded) HText_select(text);
+	}
+    }
+    return YES;
+	
+} /* HTLoadAnchor */
+
+
+/*		Search
+**		------
+**  Performs a keyword search on word given by the user. Adds the keyword to 
+**  the end of the current address and attempts to open the new address.
+**
+**  On Entry,
+**       *keywords  	space-separated keyword list or similar search list
+**	here		is anchor search is to be done on.
+*/
+PRIVATE char hex(i)
+    int i;
+{
+    char * hexchars = "0123456789ABCDEF";
+    return hexchars[i];
+}
+
+PUBLIC BOOL HTSearch ARGS2(
+	CONST char *, 		keywords,
+	HTParentAnchor *, 	here)
+{
+
+#define acceptable \
+"1234567890abcdefghijlkmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_"
+
+    char *q, *u;
+    CONST char * p, *s, *e;		/* Pointers into keywords */
+    char * address = 0;
+    BOOL result;
+    char * escaped = malloc(strlen(keywords)*3+1);
+    static CONST BOOL isAcceptable[96] =
+
+    /*   0 1 2 3 4 5 6 7 8 9 A B C D E F */
+    {    0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,0,	/* 2x   !"#$%&'()*+,-./	 */
+         1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,	/* 3x  0123456789:;<=>?	 */
+	 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,	/* 4x  @ABCDEFGHIJKLMNO  */
+	 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,	/* 5X  PQRSTUVWXYZ[\]^_	 */
+	 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,	/* 6x  `abcdefghijklmno	 */
+	 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0 };	/* 7X  pqrstuvwxyz{\}~	DEL */
+
+    if (escaped == NULL)
+        outofmem(__FILE__, "HTSearch");
+    
+    StrAllocCopy(address, here->isIndexAction);
+
+    /*
+     *  Convert spaces to + and hex escape unacceptable characters.
+     */
+    for(s=keywords; *s && WHITE(*s); s++) /*scan */ ;	/* Skip white space */
+    for(e = s + strlen(s); e>s && WHITE(*(e-1)) ; e--); /* Skip trailers */
+    for(q=escaped, p=s; p<e; p++) {			/* scan stripped field */
+        unsigned char c = (unsigned char)TOASCII(*p);
+        if (WHITE(*p)) {
+	    *q++ = '+';
+	} else if (HTCJK != NOCJK) {
+	    *q++ = *p;
+	} else if (c>=32 && c<=(unsigned char)127 && isAcceptable[c-32]) {
+	    *q++ = *p;				/* 930706 TBL for MVS bug */
+	} else {
+	    *q++ = '%';
+	    *q++ = hex((int)(c >> 4));
+	    *q++ = hex((int)(c & 15));
+	}
+    } /* Loop over string */
+    
+    *q=0;
+    				/* terminate escaped sctring */
+    u=strchr(address, '?');		/* Find old search string */
+    if (u) *u = 0;			        /* Chop old search off */
+
+    StrAllocCat(address, "?");
+    StrAllocCat(address, escaped);
+    FREE(escaped);
+    result = HTLoadRelative(address, here);
+    FREE(address);
+    
+    /*
+    **  If we got redirection, result will be NO, but use_this_url_instead
+    **  will be set.  The calling routine should check both and do whatever
+    **  is appropriate.  Only an http server (not a gopher or wais server)
+    **  could return redirection.  Lynx will go all the way back to its
+    **  mainloop() and subject a redirecting URL to all of its security and
+    **  restrictions checks. - FM
+    */
+    return result;
+}
+
+
+/*		Search Given Indexname
+**		------
+**  Performs a keyword search on word given by the user. Adds the keyword to 
+**  the end of the current address and attempts to open the new address.
+**
+**  On Entry,
+**       *keywords  	space-separated keyword list or similar search list
+**	*addres		is name of object search is to be done on.
+*/
+PUBLIC BOOL HTSearchAbsolute ARGS2(
+	CONST char *, 	keywords,
+	CONST char *, 	indexname)
+{
+    DocAddress abs_doc;
+    HTParentAnchor * anchor;
+    abs_doc.address = (char *)indexname;
+    abs_doc.post_data = 0;
+    abs_doc.post_content_type = 0;
+    abs_doc.isHEAD = FALSE;
+
+    anchor = (HTParentAnchor*) HTAnchor_findAddress(&abs_doc);
+    return HTSearch(keywords, anchor);
+}
+
+#ifdef NOT_USED_CODE
+/*		Generate the anchor for the home page
+**		-------------------------------------
+**
+**	As it involves file access, this should only be done once
+**	when the program first runs.
+**	This is a default algorithm -- browser don't HAVE to use this.
+**	But consistency betwen browsers is STRONGLY recommended!
+**
+**	Priority order is:
+**
+**		1	WWW_HOME environment variable (logical name, etc)
+**		2	~/WWW/default.html
+**		3	/usr/local/bin/default.html
+**		4	http://www.w3.org/default.html
+**
+*/
+PUBLIC HTParentAnchor * HTHomeAnchor NOARGS
+{
+    char * my_home_document = NULL;
+    char * home = (char *)getenv(LOGICAL_DEFAULT);
+    char * ref;
+    HTParentAnchor * anchor;
+    
+    if (home) {
+        StrAllocCopy(my_home_document, home);
+    
+/* 	Someone telnets in, they get a special home.
+*/
+#define MAX_FILE_NAME 1024					/* @@@ */
+    } else  if (HTClientHost) {			/* Telnet server */
+    	FILE * fp = fopen(REMOTE_POINTER, "r");
+	char * status;
+	if (fp) {
+	    my_home_document = (char*) malloc(MAX_FILE_NAME);
+	    status = fgets(my_home_document, MAX_FILE_NAME, fp);
+	    if (!status) {
+	        FREE(my_home_document);
+	    }
+	    fclose(fp);
+	}
+	if (!my_home_document) StrAllocCopy(my_home_document, REMOTE_ADDRESS);
+    }
+
+#ifdef unix
+    if (!my_home_document) {
+	FILE * fp = NULL;
+	CONST char * home =  (CONST char*)getenv("HOME");
+	if (home) { 
+	    my_home_document = (char *)malloc(
+		strlen(home)+1+ strlen(PERSONAL_DEFAULT)+1);
+	    if (my_home_document == NULL)
+	        outofmem(__FILE__, "HTLocalName");
+	    sprintf(my_home_document, "%s/%s", home, PERSONAL_DEFAULT);
+	    fp = fopen(my_home_document, "r");
+	}
+	
+	if (!fp) {
+	    StrAllocCopy(my_home_document, LOCAL_DEFAULT_FILE);
+	    fp = fopen(my_home_document, "r");
+	}
+	if (fp) {
+	    fclose(fp);
+	} else {
+	if (TRACE) fprintf(stderr,
+	    "HTBrowse: No local home document ~/%s or %s\n",
+	    PERSONAL_DEFAULT, LOCAL_DEFAULT_FILE);
+	    FREE(my_home_document);
+	}
+    }
+#endif /* unix */
+    ref = HTParse( my_home_document ?	my_home_document :
+				HTClientHost ? REMOTE_ADDRESS
+				: LAST_RESORT,
+		    "file:",
+		    PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
+    if (my_home_document) {
+	if (TRACE) fprintf(stderr,
+	    "HTAccess: Using custom home page %s i.e. address %s\n",
+	    my_home_document, ref);
+	FREE(my_home_document);
+    }
+    anchor = (HTParentAnchor*) HTAnchor_findAddress(ref);
+    FREE(ref);
+    return anchor;
+}
+#endif /* NOT_USED_CODE */
+
diff --git a/WWW/Library/Implementation/HTAccess.h b/WWW/Library/Implementation/HTAccess.h
new file mode 100644
index 00000000..f0c7e794
--- /dev/null
+++ b/WWW/Library/Implementation/HTAccess.h
@@ -0,0 +1,306 @@
+/*                                                      HTAccess:  Access manager  for libwww
+                                      ACCESS MANAGER
+                                             
+   This module keeps a list of valid protocol (naming scheme) specifiers with associated
+   access code.  It allows documents to be loaded given various combinations of
+   parameters. New access protocols may be registered at any time.
+   
+   Part of the libwww library .
+   
+ */
+#ifndef HTACCESS_H
+#define HTACCESS_H
+
+extern char * use_this_url_instead;
+
+/*      Definition uses:
+*/
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+#include "tcp.h"
+#include "HTAnchor.h"
+#include "HTFormat.h"
+
+#ifdef SHORT_NAMES
+#define HTClientHost            HTClHost
+#define HTSearchAbsolute        HTSeAbso
+#define HTOutputStream          HTOuStre
+#define HTOutputFormat          HTOuForm
+#endif
+
+/*      Return codes from load routines:
+**
+**      These codes may be returned by the protocol modules,
+**      and by the HTLoad routines.
+**      In general, positive codes are OK and negative ones are bad.
+*/
+
+#define HT_NO_DATA -9999        /* return code: OK but no data was loaded */
+                                /* Typically, other app started or forked */
+
+/*
+
+Default Addresses
+
+   These control the home page selection. To mess with these for normal browses is asking
+   for user confusion.
+   
+ */
+#define LOGICAL_DEFAULT "WWW_HOME"  /* Defined to be the home page */
+
+#ifndef PERSONAL_DEFAULT
+#define PERSONAL_DEFAULT "WWW/default.html"     /* in home directory */
+#endif
+#ifndef LOCAL_DEFAULT_FILE
+#define LOCAL_DEFAULT_FILE "/usr/local/lib/WWW/default.html"
+#endif
+/*  If one telnets to a www access point,
+    it will look in this file for home page */
+#ifndef REMOTE_POINTER
+#define REMOTE_POINTER  "/etc/www-remote.url"  /* can't be file */
+#endif
+/* and if that fails it will use this. */
+#ifndef REMOTE_ADDRESS
+#define REMOTE_ADDRESS  "http://www.w3.org/remote.html"  /* can't be file */
+#endif
+
+/* If run from telnet daemon and no -l specified, use this file:
+*/
+#ifndef DEFAULT_LOGFILE
+#define DEFAULT_LOGFILE "/usr/adm/www-log/www-log"
+#endif
+
+/*      If the home page isn't found, use this file:
+*/
+#ifndef LAST_RESORT
+#define LAST_RESORT     "http://www.w3.org/default.html"
+#endif
+
+
+/*
+
+Flags which may be set to control this module
+
+ */
+#ifdef NOT
+extern int HTDiag;                      /* Flag: load source as plain text */
+#endif /* NOT */
+extern char * HTClientHost;             /* Name or number of telnetting host */
+extern FILE * HTlogfile;                /* File to output one-liners to */
+extern BOOL HTSecure;                   /* Disable security holes? */
+extern HTStream* HTOutputStream;        /* For non-interactive, set this */
+extern HTFormat HTOutputFormat;         /* To convert on load, set this */
+
+
+
+/*
+
+Load a document from relative name
+
+  ON ENTRY,
+  
+  relative_name           The relative address of the file to be accessed.
+                         
+  here                    The anchor of the object being searched
+                         
+  ON EXIT,
+  
+  returns    YES          Success in opening file
+                         
+  NO                      Failure
+                         
+ */
+extern  BOOL HTLoadRelative PARAMS((
+                CONST char *            relative_name,
+                HTParentAnchor *        here));
+
+
+/*
+
+Load a document from absolute name
+
+  ON ENTRY,
+  
+  addr                    The absolute address of the document to be accessed.
+                         
+  filter                  if YES, treat document as HTML
+                         
+ */
+
+/*
+
+  ON EXIT,
+  
+ */
+
+/*
+
+  returns YES             Success in opening document
+                         
+  NO                      Failure
+                         
+ */
+extern BOOL HTLoadAbsolute PARAMS((CONST DocAddress * addr));
+
+
+/*
+
+Load a document from absolute name to a stream
+
+  ON ENTRY,
+  
+  addr                    The absolute address of the document to be accessed.
+                         
+  filter                  if YES, treat document as HTML
+                         
+  ON EXIT,
+  
+  returns YES             Success in opening document
+                         
+  NO                      Failure
+                         
+   Note: This is equivalent to HTLoadDocument
+   
+ */
+extern BOOL HTLoadToStream PARAMS((CONST char * addr, BOOL filter,
+                                HTStream * sink));
+
+
+/*
+
+Load if necessary, and select an anchor
+
+  ON ENTRY,
+  
+  destination                The child or parenet anchor to be loaded.
+                         
+ */
+
+/*
+
+  ON EXIT,
+  
+ */
+
+/*
+
+  returns YES             Success
+                         
+  returns NO              Failure
+                         
+ */
+
+
+
+extern BOOL HTLoadAnchor PARAMS((HTAnchor * destination));
+
+
+/*
+
+Make a stream for Saving object back
+
+  ON ENTRY,
+  
+  anchor                  is valid anchor which has previously beeing loaded
+                         
+  ON EXIT,
+  
+  returns                 0 if error else a stream to save the object to.
+                         
+ */
+
+
+extern HTStream * HTSaveStream PARAMS((HTParentAnchor * anchor));
+
+
+/*
+
+Search
+
+   Performs a search on word given by the user. Adds the search words to the end of the
+   current address and attempts to open the new address.
+   
+  ON ENTRY,
+  
+  *keywords               space-separated keyword list or similar search list
+                         
+  here                    The anchor of the object being searched
+                         
+ */
+extern BOOL HTSearch PARAMS((CONST char * keywords, HTParentAnchor* here));
+
+
+/*
+
+Search Given Indexname
+
+   Performs a keyword search on word given by the user. Adds the keyword to  the end of
+   the current address and attempts to open the new address.
+   
+  ON ENTRY,
+  
+  *keywords               space-separated keyword list or similar search list
+                         
+  *indexname              is name of object search is to be done on.
+                         
+ */
+extern BOOL HTSearchAbsolute PARAMS((
+        CONST char *    keywords,
+        CONST char *    indexname));
+
+
+/*
+
+Register an access method
+
+ */
+
+typedef struct _HTProtocol {
+        char * name;
+        
+        int (*load)PARAMS((
+                CONST char *    full_address,
+                HTParentAnchor * anchor,
+                HTFormat        format_out,
+                HTStream*       sink));
+                
+        HTStream* (*saveStream)PARAMS((HTParentAnchor * anchor));
+
+} HTProtocol;
+
+extern BOOL HTRegisterProtocol PARAMS((HTProtocol * protocol));
+
+
+/*
+
+Generate the anchor for the home page
+
+ */
+
+/*
+
+   As it involves file access, this should only be done once when the program first runs.
+   This is a default algorithm -- browser don't HAVE to use this.
+   
+ */
+extern HTParentAnchor * HTHomeAnchor NOPARAMS;
+
+/*
+
+Return Host Name
+
+ */
+extern CONST char * HTHostName NOPARAMS;
+
+/*
+
+For registering protocols supported by Lynx
+
+*/
+extern void LYRegisterLynxProtocols NOARGS;
+
+#endif /* HTACCESS_H */
+/*
+
+   end of HTAccess  */
diff --git a/WWW/Library/Implementation/HTAlert.c b/WWW/Library/Implementation/HTAlert.c
new file mode 100644
index 00000000..769db178
--- /dev/null
+++ b/WWW/Library/Implementation/HTAlert.c
@@ -0,0 +1,125 @@
+/*	Displaying messages and getting input for LineMode Browser
+**	==========================================================
+**
+**	REPLACE THIS MODULE with a GUI version in a GUI environment!
+**
+** History:
+**	   Jun 92 Created May 1992 By C.T. Barker
+**	   Feb 93 Simplified, portablised TBL
+**	   Sep 93 Corrected 3 bugs in HTConfirm() :-( AL
+*/
+
+
+#include "HTUtils.h"
+#include "tcp.h"		/* for TOUPPER */
+
+#include "HTAlert.h"
+
+#include <ctype.h> 		/* for toupper - should be in tcp.h */
+#ifdef VMS
+extern char * getpass PARAMS((CONST char *prompt));
+#endif /* VMS */
+
+#include "LYLeaks.h"
+
+PUBLIC void HTAlert ARGS1(CONST char *, Msg)
+{
+#ifdef NeXTStep
+    NXRunAlertPanel(NULL, "%s", NULL, NULL, NULL, Msg);
+#else
+    fprintf(stderr, "WWW Alert:  %s\n", Msg);
+#endif
+}
+
+
+PUBLIC void HTProgress ARGS1(CONST char *, Msg)
+{
+    fprintf(stderr, "   %s ...\n", Msg);
+}
+
+
+PUBLIC BOOL HTConfirm ARGS1(CONST char *, Msg)
+{
+  char Reply[4];	/* One more for terminating NULL -- AL */
+  char *URep;
+  
+  fprintf(stderr, "WWW: %s (y/n) ", Msg);
+                       /* (y/n) came twice -- AL */
+
+  fgets(Reply, 4, stdin); /* get reply, max 3 characters */
+  URep=Reply;
+  while (*URep) {
+    if (*URep == '\n') {
+	*URep = (char)0;	/* Overwrite newline */
+	break;
+    }
+    *URep=TOUPPER(*URep);
+    URep++;	/* This was previously embedded in the TOUPPER */
+                /* call an it became evaluated twice because   */
+                /* TOUPPER is a macro -- AL */
+  }
+
+  if ((strcmp(Reply,"YES")==0) || (strcmp(Reply,"Y")==0))
+    return(YES);
+  else
+    return(NO);
+}
+
+/*	Prompt for answer and get text back
+*/
+PUBLIC char * HTPrompt ARGS2(CONST char *, Msg, CONST char *, deflt)
+{
+    char Tmp[200];
+    char * rep = 0;
+    fprintf(stderr, "WWW: %s", Msg);
+    if (deflt) fprintf(stderr, " (RETURN for [%s]) ", deflt);
+    
+    fgets(Tmp, 200, stdin);
+    Tmp[strlen(Tmp)-1] = (char)0;	/* Overwrite newline */
+   
+    StrAllocCopy(rep, *Tmp ? Tmp : deflt);
+    return rep;
+}
+
+
+/*	Prompt for password without echoing the reply
+*/
+PUBLIC char * HTPromptPassword ARGS1(CONST char *, Msg)
+{
+    char *result = NULL;
+    char *pw = (char*)getpass(Msg ? Msg : "Password: ");
+
+    StrAllocCopy(result, pw);
+    return result;
+}
+
+
+/*	Prompt both username and password	HTPromptUsernameAndPassword()
+**	---------------------------------
+** On entry,
+**	Msg		is the prompting message.
+**	*username and
+**	*password	are char pointers; they are changed
+**			to point to result strings.
+**
+**			If *username is not NULL, it is taken
+**			to point to  a default value.
+**			Initial value of *password is
+**			completely discarded.
+**
+** On exit,
+**	*username and *password point to newly allocated
+**	strings -- original strings pointed to by them
+**	are NOT freed.
+**	
+*/
+PUBLIC void HTPromptUsernameAndPassword ARGS3(CONST char *,	Msg,
+					      char **,		username,
+					      char **,		password)
+{
+    if (Msg)
+	fprintf(stderr, "WWW: %s\n", Msg);
+    *username = HTPrompt("Username: ", *username);
+    *password = HTPromptPassword("Password: ");
+}
+
diff --git a/WWW/Library/Implementation/HTAlert.h b/WWW/Library/Implementation/HTAlert.h
new file mode 100644
index 00000000..8ce0be53
--- /dev/null
+++ b/WWW/Library/Implementation/HTAlert.h
@@ -0,0 +1,86 @@
+/*  */
+
+/*      Displaying messages and getting input for WWW Library
+**      =====================================================
+**
+**         May 92 Created By C.T. Barker
+**         Feb 93 Portablized etc TBL
+*/
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+#include "tcp.h"
+
+/*      Display a message and get the input
+**
+**      On entry,
+**              Msg is the message.
+**
+**      On exit,
+**              Return value is malloc'd string which must be freed.
+*/
+extern char * HTPrompt PARAMS((CONST char * Msg, CONST char * deflt));
+
+
+/*      Display a message, don't wait for input
+**
+**      On entry,
+**              The input is a list of parameters for printf.
+*/
+extern void HTAlert PARAMS((CONST char * Msg));
+
+
+/*      Display a progress message for information (and diagnostics) only
+**
+**      On entry,
+**              The input is a list of parameters for printf.
+*/
+extern void HTProgress PARAMS((CONST char * Msg));
+extern BOOLEAN mustshow;
+#define _HTProgress(msg)	mustshow = TRUE, HTProgress(msg)
+
+
+/*      Display a message, then wait for 'yes' or 'no'.
+**
+**      On entry,
+**              Takes a list of parameters for printf.
+**
+**      On exit,
+**              If the user enters 'YES', returns TRUE, returns FALSE
+**              otherwise.
+*/
+extern BOOL HTConfirm PARAMS ((CONST char * Msg));
+
+
+/*      Prompt for password without echoing the reply
+*/
+extern char * HTPromptPassword PARAMS((CONST char * Msg));
+
+/*      Prompt both username and password       HTPromptUsernameAndPassword()
+**      ---------------------------------
+** On entry,
+**      Msg             is the prompting message.
+**      *username and
+**      *password       are char pointers; they are changed
+**                      to point to result strings.
+**
+**                      If *username is not NULL, it is taken
+**                      to point to  a default value.
+**                      Initial value of *password is
+**                      completely discarded.
+**
+** On exit,
+**      *username and *password point to newly allocated
+**      strings -- original strings pointed to by them
+**      are NOT freed.
+**
+*/
+extern void HTPromptUsernameAndPassword PARAMS((
+	CONST char *	Msg,
+	char **		username,
+	char **		password));
+
+/*
+
+    */
diff --git a/WWW/Library/Implementation/HTAnchor.c b/WWW/Library/Implementation/HTAnchor.c
new file mode 100644
index 00000000..3172f1a2
--- /dev/null
+++ b/WWW/Library/Implementation/HTAnchor.c
@@ -0,0 +1,999 @@
+/*	Hypertext "Anchor" Object				HTAnchor.c
+**	==========================
+**
+** An anchor represents a region of a hypertext document which is linked to
+** another anchor in the same or a different document.
+**
+** History
+**
+**         Nov 1990  Written in Objective-C for the NeXT browser (TBL)
+**	24-Oct-1991 (JFG), written in C, browser-independant 
+**	21-Nov-1991 (JFG), first complete version
+**
+**	(c) Copyright CERN 1991 - See Copyright.html
+*/
+
+#define HASH_SIZE 101		/* Arbitrary prime. Memory/speed tradeoff */
+
+#include "HTUtils.h"
+#include "tcp.h"
+#include <ctype.h>
+#include "HTAnchor.h"
+#include "HTParse.h"
+
+#include "LYLeaks.h"
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+#ifdef NOT_DEFINED
+/*
+ *	This is the hashing function used to determine which list in the
+ *		adult_table a parent anchor should be put in.  This is a
+ *		much simpler function than the original used.
+ */
+#define HASH_FUNCTION(cp_address) ((unsigned short int)strlen(cp_address) *\
+	(unsigned short int)TOUPPER(*cp_address) % HASH_SIZE)
+#endif /* NOT_DEFINED */
+/*
+ *	This is the original function.  We'll use it again. - FM
+ */ 
+PRIVATE int HASH_FUNCTION ARGS1(
+	CONST char *,	cp_address)
+{
+    int hash;
+    unsigned char *p;
+
+    for (p = (unsigned char *)cp_address, hash = 0; *p; p++)
+    	hash = (int) (hash * 3 + (*(unsigned char*)p)) % HASH_SIZE;
+
+    return hash;
+}
+
+typedef struct _HyperDoc Hyperdoc;
+#ifdef VMS
+struct _HyperDoc {
+	int junk;	/* VMS cannot handle pointers to undefined structs */
+};
+#endif /* VMS */
+
+PRIVATE HTList **adult_table = 0;  /* Point to table of lists of all parents */
+
+/*				Creation Methods
+**				================
+**
+**	Do not use "new" by itself outside this module. In order to enforce
+**	consistency, we insist that you furnish more information about the
+**	anchor you are creating : use newWithParent or newWithAddress.
+*/
+
+PRIVATE HTParentAnchor * HTParentAnchor_new NOARGS
+{
+    HTParentAnchor *newAnchor = 
+       (HTParentAnchor *)calloc(1, sizeof(HTParentAnchor));  /* zero-filled */
+    newAnchor->parent = newAnchor;
+    newAnchor->isISMAPScript = FALSE;	/* Lynx appends ?0,0 if TRUE. - FM */
+    newAnchor->isHEAD = FALSE;		/* HEAD request if TRUE. - FM */
+    newAnchor->FileCache = NULL;	/* Path to a disk-cached copy. - FM */
+    newAnchor->cache_control = NULL;	/* Cache-Control. - FM */
+    newAnchor->no_cache = FALSE;	/* no-cache? - FM */
+    newAnchor->content_type = NULL;	/* Content-Type. - FM */
+    newAnchor->content_language = NULL;	/* Content-Language. - FM */
+    newAnchor->content_encoding = NULL;	/* Compression algorith. - FM */
+    newAnchor->RevTitle = NULL;		/* TITLE for a LINK with REV. - FM */
+    return newAnchor;
+}
+
+PRIVATE HTChildAnchor * HTChildAnchor_new NOARGS
+{
+    return (HTChildAnchor *)calloc(1, sizeof(HTChildAnchor)); /* zero-filled */
+}
+
+
+#ifdef CASE_INSENSITIVE_ANCHORS
+/*	Case insensitive string comparison
+**	----------------------------------
+** On entry,
+**	s	Points to one string, null terminated
+**	t	points to the other.
+** On exit,
+**	returns	YES if the strings are equivalent ignoring case
+**		NO if they differ in more than  their case.
+*/
+
+PRIVATE BOOL HTEquivalent ARGS2(
+	CONST char *,	s,
+	CONST char *,	t)
+{
+    if (s && t) {  /* Make sure they point to something */
+        for (; *s && *t; s++, t++) {
+            if (TOUPPER(*s) != TOUPPER(*t)) {
+	        return NO;
+	    }
+        }
+        return TOUPPER(*s) == TOUPPER(*t);
+    } else {
+        return s == t;  /* Two NULLs are equivalent, aren't they ? */
+    }
+}
+
+#else
+
+/*	Case sensitive string comparison
+**	----------------------------------
+** On entry,
+**	s	Points to one string, null terminated
+**	t	points to the other.
+** On exit,
+**	returns	YES if the strings are identical or both NULL
+**		NO if they differ.
+*/
+
+PRIVATE BOOL HTIdentical ARGS2(
+	CONST char *,	s,
+	CONST char *,	t)
+{
+    if (s && t) {  /* Make sure they point to something */
+        for (; *s && *t; s++, t++) {
+            if (*s != *t) {
+	        return NO;
+	    }
+        }
+        return *s == *t;
+    } else {
+        return s == t;  /* Two NULLs are identical, aren't they ? */
+    }
+}
+#endif /* CASE_INSENSITIVE_ANCHORS */
+
+
+/*	Create new or find old sub-anchor
+**	---------------------------------
+**
+**	Me one is for a new anchor being edited into an existing
+**	document. The parent anchor must already exist.
+*/
+
+PUBLIC HTChildAnchor * HTAnchor_findChild ARGS2(
+	HTParentAnchor *,	parent,
+	CONST char *,		tag)
+{
+    HTChildAnchor *child;
+    HTList *kids;
+
+    if (!parent) {
+        if (TRACE)
+	    fprintf(stderr, "HTAnchor_findChild called with NULL parent.\n");
+        return NULL;
+    }
+    if (kids = parent->children) {  /* parent has children : search them */
+        if (tag && *tag) {		/* TBL */
+	    while (NULL != (child=(HTChildAnchor*)HTList_nextObject(kids))) {
+#ifdef CASE_INSENSITIVE_ANCHORS
+	        if (HTEquivalent(child->tag, tag)) { /* Case insensitive */
+#else
+	        if (HTIdentical(child->tag, tag)) {  /* Case sensitive - FM */
+#endif /* CASE_INSENSITIVE_ANCHORS */
+		    if (TRACE)
+		        fprintf(stderr,
+	      "Child anchor %p of parent %p with name `%s' already exists.\n",
+		    		(void*)child, (void*)parent, tag);
+		    return child;
+	        }
+	    }
+        }  /*  end if tag is void */
+    } else {  /* parent doesn't have any children yet : create family */
+        parent->children = HTList_new();
+    }
+
+    child = HTChildAnchor_new();
+    if (TRACE)
+        fprintf(stderr,
+		"new Anchor %p named `%s' is child of %p\n",
+       		(void*)child, 
+		(int)tag ? tag : (CONST char *)"" ,
+		(void*)parent); /* int for apollo */
+    HTList_addObject (parent->children, child);
+    child->parent = parent;
+    StrAllocCopy(child->tag, tag);
+    return child;
+}
+
+
+/*	Create or find a child anchor with a possible link
+**	--------------------------------------------------
+**
+**	Create new anchor with a given parent and possibly
+**	a name, and possibly a link to a _relatively_ named anchor.
+**	(Code originally in ParseHTML.h)
+*/
+PUBLIC HTChildAnchor * HTAnchor_findChildAndLink ARGS4(
+	HTParentAnchor *,	parent,	/* May not be 0   */
+	CONST char *,		tag,	/* May be "" or 0 */
+	CONST char *,		href,	/* May be "" or 0 */
+	HTLinkType *,		ltype)	/* May be 0	  */
+{
+    HTChildAnchor * child = HTAnchor_findChild(parent, tag);
+
+    if (TRACE)
+        fprintf(stderr,"Entered HTAnchor_findChildAndLink\n");
+
+    if (href && *href) {
+        char *relative_to = HTAnchor_address((HTAnchor *)parent);
+        DocAddress parsed_doc;
+        HTAnchor * dest;
+
+        parsed_doc.address = HTParse(href, relative_to, PARSE_ALL);
+        parsed_doc.post_data = NULL;
+        parsed_doc.post_content_type = NULL;
+        parsed_doc.isHEAD = FALSE;
+        dest = HTAnchor_findAddress(&parsed_doc);
+
+        HTAnchor_link((HTAnchor *) child, dest, ltype);
+        FREE(parsed_doc.address);
+        FREE(relative_to);
+    }
+    return child;
+}
+
+/*
+**  Function for freeing the adult hash table. - FM
+*/
+PRIVATE void free_adult_table NOARGS
+{
+    int i_counter;
+    HTList * HTAp_freeme;
+    HTParentAnchor * parent;
+    /*
+     *  Loop through all lists.
+     */
+    for (i_counter = 0; i_counter < HASH_SIZE; i_counter++) {
+	/*
+	**  Loop through the list.
+	*/
+	while (adult_table[i_counter] != NULL) {
+	    /*
+	    **  Free off items - FM
+	    */
+	    HTAp_freeme = adult_table[i_counter];
+	    adult_table[i_counter] = HTAp_freeme->next;
+	    if (HTAp_freeme->object) {
+		parent = (HTParentAnchor *)HTAp_freeme->object;
+		HTAnchor_delete(parent);
+	    }
+	    FREE(HTAp_freeme);
+	}
+    }
+    FREE(adult_table);
+}
+
+/*	Create new or find old named anchor
+**	-----------------------------------
+**
+**	Me one is for a reference which is found in a document, and might
+**	not be already loaded.
+**	Note: You are not guaranteed a new anchor -- you might get an old one,
+**	like with fonts.
+*/
+
+PUBLIC HTAnchor * HTAnchor_findAddress ARGS1(
+	CONST DocAddress *,	newdoc)
+{
+    /* Anchor tag specified ? */
+    char *tag = HTParse(newdoc->address, "", PARSE_ANCHOR);
+
+    if (TRACE)
+	fprintf(stderr,"Entered HTAnchor_findAddress\n");
+
+    /*
+    **  If the address represents a sub-anchor, we recursively load its
+    **  parent, then we create a child anchor within that document.
+    */
+    if (*tag) {
+        DocAddress parsed_doc;
+        HTParentAnchor * foundParent;
+        HTChildAnchor * foundAnchor;
+
+        parsed_doc.address = HTParse(newdoc->address, "", 
+		PARSE_ACCESS | PARSE_HOST | PARSE_PATH | PARSE_PUNCTUATION);
+        parsed_doc.post_data = newdoc->post_data;
+        parsed_doc.post_content_type = newdoc->post_content_type;
+        parsed_doc.isHEAD = newdoc->isHEAD;
+
+        foundParent = (HTParentAnchor *) HTAnchor_findAddress (&parsed_doc);
+        foundAnchor = HTAnchor_findChild (foundParent, tag);
+        FREE(parsed_doc.address);
+        FREE(tag);
+        return (HTAnchor *) foundAnchor;
+    } else {
+        /*
+	**  If the address has no anchor tag, 
+	**  check whether we have this node.
+	*/
+        int hash;
+        HTList * adults;
+        HTList *grownups;
+        HTParentAnchor * foundAnchor;
+
+        FREE(tag);
+
+        /*
+	**  Select list from hash table,
+	*/
+        hash = HASH_FUNCTION(newdoc->address);
+        if (!adult_table) {
+            adult_table = (HTList**) calloc(HASH_SIZE, sizeof(HTList*));
+	    atexit(free_adult_table);
+        }
+        if (!adult_table[hash])
+            adult_table[hash] = HTList_new();
+        adults = adult_table[hash];
+
+        /*
+	**  Search list for anchor.
+	*/
+        grownups = adults;
+        while (NULL != (foundAnchor =
+			(HTParentAnchor*)HTList_nextObject(grownups))) {
+#ifdef CASE_INSENSITIVE_ANCHORS
+            if (HTEquivalent(foundAnchor->address, newdoc->address) &&
+	        HTEquivalent(foundAnchor->post_data, newdoc->post_data) &&
+	        foundAnchor->isHEAD == newdoc->isHEAD) {
+#else
+            if (HTIdentical(foundAnchor->address, newdoc->address) &&
+	        HTIdentical(foundAnchor->post_data, newdoc->post_data) &&
+	        foundAnchor->isHEAD == newdoc->isHEAD) {
+#endif /* CASE_INSENSITIVE_ANCHORS */
+	        if (TRACE)
+		    fprintf(stderr,
+		    	    "Anchor %p with address `%s' already exists.\n",
+			    (void*)foundAnchor, newdoc->address);
+	         return (HTAnchor *) foundAnchor;
+             }
+        }
+    
+        /*
+	**  Node not found: create new anchor.
+	*/
+        foundAnchor = HTParentAnchor_new();
+        if (TRACE)
+	    fprintf(stderr,
+	    	    "New anchor %p has hash %d and address `%s'\n",
+    	    	    (void*)foundAnchor, hash, newdoc->address);
+        StrAllocCopy(foundAnchor->address, newdoc->address);
+        if (newdoc->post_data)
+	    StrAllocCopy(foundAnchor->post_data, newdoc->post_data);
+        if (newdoc->post_content_type)
+	    StrAllocCopy(foundAnchor->post_content_type,
+	    		 newdoc->post_content_type);
+        foundAnchor->isHEAD = newdoc->isHEAD;
+        HTList_addObject (adults, foundAnchor);
+        return (HTAnchor *) foundAnchor;
+    }
+}
+
+
+/*	Delete an anchor and possibly related things (auto garbage collection)
+**	--------------------------------------------
+**
+**	The anchor is only deleted if the corresponding document is not loaded.
+**	All outgoing links from parent and children are deleted, and this anchor
+**	is removed from the sources list of all its targets.
+**	We also try to delete the targets whose documents are not loaded.
+**	If this anchor's source list is empty, we delete it and its children.
+*/
+
+PRIVATE void deleteLinks ARGS1(
+	HTAnchor *,	me)
+{
+    /*
+     *  Memory leaks fixed.
+     *  05-27-94 Lynx 2-3-1 Garrett Arch Blythe
+     */
+
+    /*
+     *  Anchor is NULL, do nothing.
+     */
+    if (!me) {
+	return;
+    }
+
+    /*
+     *  Unregister me with our mainLink destination anchor's parent.
+     */
+    if (me->mainLink.dest) {
+	HTParentAnchor *parent = me->mainLink.dest->parent;
+
+	/*
+	 *  Remove me from the parent's sources so that the
+	 *  parent knows one less anchor is it's dest.
+	 */
+	if (!HTList_isEmpty(parent->sources)) {
+	    /*
+	     *  Really should only need to deregister once.
+	     */
+	    HTList_removeObject(parent->sources, (void *)me);
+	}
+
+	/*
+	 *  Test here to avoid calling overhead.
+	 *  If the parent has no loaded document, then we should
+	 *  tell it to attempt to delete itself.
+	 *  Don't do this jass if the anchor passed in is the same
+	 *  as the anchor to delete.
+	 *  Also, don't do this if the destination parent is our
+	 *  parent.
+	 */
+	if (!parent->document &&
+	    parent != (HTParentAnchor *)me &&
+	    me->parent != parent) {
+	    HTAnchor_delete(parent);
+	}
+
+	/*
+	 *  At this point, we haven't a mainLink.  Set it to be
+	 *  so.
+	 *  Leave the HTAtom pointed to by type up to other code to
+	 *  handle (reusable, near static).
+	 */
+	me->mainLink.dest = NULL;
+	me->mainLink.type = NULL;
+    }
+
+    /*
+     *  Check for extra destinations in our links list.
+     */
+    if (!HTList_isEmpty(me->links)) {
+	HTLink *target;
+	HTParentAnchor *parent;
+
+	/*
+	 *  Take out our extra non mainLinks one by one, calling
+	 *  their parents to know that they are no longer
+	 *  the destination of me's anchor.
+	 */
+	while (target = (HTLink *)HTList_removeLastObject(me->links)) {
+	    parent = target->dest->parent;
+	    if (!HTList_isEmpty(parent->sources)) {
+	        /*
+	         *  Only need to tell destination parent
+	         *  anchor once.
+	         */
+	        HTList_removeObject(parent->sources, (void *)me);
+	    }
+
+	    /*
+	     *  Avoid calling overhead.
+	     *  If the parent hasn't a loaded document, then
+	     *     we will attempt to have the parent
+	     *     delete itself.
+	     *  Don't call twice if this is the same anchor
+	     *     that we are trying to delete.
+	     *  Also, don't do this if we are trying to delete
+	     *     our parent.
+	     */
+	    if (!parent->document &&
+	        (HTParentAnchor *)me != parent &&
+	        me->parent != parent) {
+	        HTAnchor_delete(parent);
+	    }
+        }
+
+        /*
+         *  At this point, me no longer has any destination in
+         *  the links list.  Get rid of it.
+         */
+        if (me->links) {
+	    HTList_delete(me->links);
+	    me->links = NULL;
+        }
+    }
+
+    /*
+     *  Catch in case links list exists but nothing in it.
+     */
+    if (me->links) {
+	HTList_delete(me->links);
+	me->links = NULL;
+    }
+}
+
+PUBLIC BOOL HTAnchor_delete ARGS1(
+	HTParentAnchor *,	me)
+{
+    /*
+     *  Memory leaks fixed.
+     * 05-27-94 Lynx 2-3-1 Garrett Arch Blythe
+     */
+    HTList *cur;
+    HTChildAnchor *child;
+
+    /*
+     *  Do nothing if nothing to do.
+     */
+    if (!me) {
+	return(NO);
+    }
+
+    /*
+     *  Don't delete if document is loaded or being loaded.
+     */
+    if (me->document || me->underway) {
+	return(NO);
+    }
+
+    /*
+     *  Recursively try to delete destination anchors of this parent.
+     *  In any event, this will tell all destination anchors that we
+     *  no longer consider them a destination.
+     */
+    deleteLinks((HTAnchor *)me);
+
+    /*
+     *  There are still incoming links to this one (we are the
+     *  destination of another anchor).
+     *  Don't actually delete this anchor, but children are OK to
+     *  delete their links.
+     */
+    if (!HTList_isEmpty(me->sources)) {
+        /*
+	 *  Delete all outgoing links from children, do not
+	 *  delete the children, though.
+	 */
+	if (!HTList_isEmpty(me->children)) {
+	    cur = me->children; 
+	    while (child = (HTChildAnchor *)HTList_nextObject(cur)) {
+		if (child != NULL) {
+		    deleteLinks((HTAnchor *)child);
+		}
+	    }
+	}
+
+	/*
+	 *  Can't delete parent, still have sources.
+	 */
+	return(NO);
+    }
+
+    /*
+     *  No more incoming links : kill everything
+     *  First, recursively delete children and their links.
+     */
+    if (!HTList_isEmpty(me->children)) {
+	while (child = (HTChildAnchor *)HTList_removeLastObject(
+							me->children)) {
+	    if (child) {
+		deleteLinks((HTAnchor *)child);
+		if (child->tag) {
+		    FREE(child->tag);
+		}
+		FREE(child);
+	    }
+	}
+    }
+
+    /*
+     *  Delete our empty list of children.
+     */
+    if (me->children) {
+	HTList_delete(me->children);
+	me->children = NULL;
+    }
+
+    /*
+     * Delete our empty list of sources.
+     */
+    if (me->sources) {
+	HTList_delete(me->sources);
+	me->sources = NULL;
+    }
+
+    /*
+     *  Delete the methods list.
+     */
+    if (me->methods) {
+	/*
+	 *  Leave what the methods point to up in memory for
+	 *  other code (near static stuff).
+	 */
+	HTList_delete(me->methods);
+	me->methods = NULL;
+    }
+
+    /*
+     *  Free up all allocated members.
+     */
+    FREE(me->charset);
+    FREE(me->isIndexAction);
+    FREE(me->isIndexPrompt);
+    FREE(me->title);
+    FREE(me->physical);
+    FREE(me->post_data);
+    FREE(me->post_content_type);
+    FREE(me->owner);
+    FREE(me->RevTitle);
+    if (me->FileCache) {
+	FILE *fd;
+	if (fd = fopen(me->FileCache, "r")) {
+	    fclose(fd);
+	    remove(me->FileCache);
+	}
+	FREE(me->FileCache);
+    }
+    FREE(me->cache_control);
+    FREE(me->content_type);
+    FREE(me->content_language);
+    FREE(me->content_encoding);
+
+    /*
+     *  Remove ourselves from the hash table's list.
+     */
+    if (adult_table) {
+	unsigned short int usi_hash = HASH_FUNCTION(me->address);
+
+	if (adult_table[usi_hash])  {
+	    HTList_removeObject(adult_table[usi_hash], (void *)me);
+	}
+    }
+
+    /*
+     *  Original code wanted a way to clean out the HTFormat if no
+     *  longer needed (ref count?).  I'll leave it alone since
+     *  those HTAtom objects are a little harder to know where
+     *  they are being referenced all at one time. (near static)
+     */
+
+    /*
+     *  Free the address.
+     */
+    FREE(me->address);
+
+    /*
+     *  Finally, kill the parent anchor passed in.
+     */
+    FREE(me);
+
+    return(YES);
+}
+
+
+/*		Move an anchor to the head of the list of its siblings
+**		------------------------------------------------------
+**
+**	This is to ensure that an anchor which might have already existed
+**	is put in the correct order as we load the document.
+*/
+
+PUBLIC void HTAnchor_makeLastChild ARGS1(
+	HTChildAnchor *,	me)
+{
+    if (me->parent != (HTParentAnchor *) me) {  /* Make sure it's a child */
+        HTList * siblings = me->parent->children;
+	HTList_removeObject (siblings, me);
+	HTList_addObject (siblings, me);
+    }
+}
+
+/*	Data access functions
+**	---------------------
+*/
+
+PUBLIC HTParentAnchor * HTAnchor_parent ARGS1(
+	HTAnchor *,	me)
+{
+    return me ? me->parent : NULL;
+}
+
+PUBLIC void HTAnchor_setDocument ARGS2(
+	HTParentAnchor *,	me,
+	HyperDoc *,		doc)
+{
+    if (me)
+        me->document = doc;
+}
+
+PUBLIC HyperDoc * HTAnchor_document ARGS1(
+	HTParentAnchor *,	me)
+{
+    return me ? me->document : NULL;
+}
+
+
+/*  We don't want code to change an address after anchor creation... yet ?
+PUBLIC void HTAnchor_setAddress ARGS2(
+	HTAnchor *,	me,
+	char *,		addr)
+{
+    if (me)
+        StrAllocCopy (me->parent->address, addr);
+}
+*/
+
+PUBLIC char * HTAnchor_address ARGS1(
+	HTAnchor *,	me)
+{
+    char *addr = NULL;
+
+    if (me) {
+        if (((HTParentAnchor *)me == me->parent) ||
+    	    !((HTChildAnchor *)me)->tag) {  /* it's an adult or no tag */
+            StrAllocCopy(addr, me->parent->address);
+        } else {  /* it's a named child */
+            addr = malloc(2 +
+	    		  strlen(me->parent->address) +
+			  strlen(((HTChildAnchor *)me)->tag));
+            if (addr == NULL)
+	        outofmem(__FILE__, "HTAnchor_address");
+            sprintf(addr, "%s#%s",
+	    		   me->parent->address, ((HTChildAnchor *)me)->tag);
+        }
+    }
+    return addr;
+}
+
+PUBLIC void HTAnchor_setFormat ARGS2(
+	HTParentAnchor *,	me,
+	HTFormat,		form)
+{
+    if (me)
+        me->format = form;
+}
+
+PUBLIC HTFormat HTAnchor_format ARGS1(
+	HTParentAnchor *,	me)
+{
+    return me ? me->format : NULL;
+}
+
+PUBLIC void HTAnchor_setIndex ARGS2(
+	HTParentAnchor *,	me,
+	char *,			address)
+{
+    if (me) {
+        me->isIndex = YES;
+	StrAllocCopy(me->isIndexAction, address);
+    }
+}
+
+PUBLIC void HTAnchor_setPrompt ARGS2(
+	HTParentAnchor *,	me,
+	char *,			prompt)
+{
+    if (me) {
+        StrAllocCopy(me->isIndexPrompt, prompt);
+    }
+}
+
+PUBLIC BOOL HTAnchor_isIndex ARGS1(
+	HTParentAnchor *,	me)
+{
+    return me ? me->isIndex : NO;
+}
+
+PUBLIC BOOL HTAnchor_hasChildren ARGS1(
+	HTParentAnchor *,	me)
+{
+    return me ? ! HTList_isEmpty(me->children) : NO;
+}
+
+/*	Title handling
+*/
+PUBLIC CONST char * HTAnchor_title ARGS1(
+	HTParentAnchor *,	me)
+{
+    return me ? me->title : 0;
+}
+
+PUBLIC void HTAnchor_setTitle ARGS2(
+	HTParentAnchor *,	me,
+	CONST char *,		title)
+{
+    int i;
+
+    if (me) {
+        StrAllocCopy(me->title, title);
+        for (i = 0; me->title[i]; i++) {
+            if ((unsigned char)me->title[i] == 1 ||
+	        (unsigned char)me->title[i] == 2) {
+	        me->title[i] = ' ';
+	    }
+        }
+    }
+}
+
+PUBLIC void HTAnchor_appendTitle ARGS2(
+	HTParentAnchor *,	me,
+	CONST char *,		title)
+{
+    int i;
+
+    if (me) {
+        StrAllocCat(me->title, title);
+        for (i = 0; me->title[i]; i++) {
+            if ((unsigned char)me->title[i] == 1 ||
+	        (unsigned char)me->title[i] == 2) {
+	        me->title[i] = ' ';
+	    }
+        }
+    }
+}
+
+/*	Owner handling
+*/
+PUBLIC CONST char * HTAnchor_owner ARGS1(
+	HTParentAnchor *,	me)
+{
+    return (me ? me->owner : 0);
+}
+
+PUBLIC void HTAnchor_setOwner ARGS2(
+	HTParentAnchor *,	me,
+	CONST char *,		owner)
+{
+    if (me) {
+        StrAllocCopy(me->owner, owner);
+    }
+}
+
+/*      TITLE handling in LINKs with REV="made" or REV="owner"
+*/
+PUBLIC CONST char * HTAnchor_RevTitle ARGS1(
+	HTParentAnchor *,	me)
+{
+    return (me ? me->RevTitle : 0);
+}
+
+PUBLIC void HTAnchor_setRevTitle ARGS2(
+	HTParentAnchor *,	me,
+	CONST char *,		title)
+{
+    int i;
+
+    if (me) {
+        StrAllocCopy(me->RevTitle, title);
+        for (i = 0; me->RevTitle[i]; i++) {
+            if ((unsigned char)me->RevTitle[i] == 1 ||
+	        (unsigned char)me->RevTitle[i] == 2) {
+	        me->RevTitle[i] = ' ';
+	    }
+        }
+    }
+}
+
+/*	Link me Anchor to another given one
+**	-------------------------------------
+*/
+
+PUBLIC BOOL HTAnchor_link ARGS3(
+	HTAnchor *,	source,
+	HTAnchor *,	destination,
+	HTLinkType *,	type)
+{
+    if (!(source && destination))
+        return NO;  /* Can't link to/from non-existing anchor */
+    if (TRACE)
+        fprintf(stderr,
+  		"Linking anchor %p to anchor %p\n", source, destination);
+    if (!source->mainLink.dest) {
+        source->mainLink.dest = destination;
+        source->mainLink.type = type;
+    } else {
+        HTLink * newLink = (HTLink *) calloc (1, sizeof (HTLink));
+        if (newLink == NULL)
+	    outofmem(__FILE__, "HTAnchor_link");
+        newLink->dest = destination;
+        newLink->type = type;
+        if (!source->links)
+            source->links = HTList_new();
+        HTList_addObject (source->links, newLink);
+    }
+    if (!destination->parent->sources)
+        destination->parent->sources = HTList_new();
+    HTList_addObject (destination->parent->sources, source);
+    return YES;  /* Success */
+}
+
+
+/*	Manipulation of links
+**	---------------------
+*/
+
+PUBLIC HTAnchor * HTAnchor_followMainLink ARGS1(
+	HTAnchor *,	me)
+{
+    return me->mainLink.dest;
+}
+
+PUBLIC HTAnchor * HTAnchor_followTypedLink ARGS2(
+	HTAnchor *,	me,
+	HTLinkType *,	type)
+{
+    if (me->mainLink.type == type)
+        return me->mainLink.dest;
+    if (me->links) {
+        HTList *links = me->links;
+        HTLink *link;
+        while (NULL != (link=(HTLink*)HTList_nextObject(links))) {
+            if (link->type == type) {
+	        return link->dest;
+	    }
+	}
+    }
+    return NULL;  /* No link of me type */
+}
+
+
+/*	Make main link
+*/
+PUBLIC BOOL HTAnchor_makeMainLink ARGS2(
+	HTAnchor *,	me,
+	HTLink *,	movingLink)
+{
+    /* Check that everything's OK */
+    if (!(me && HTList_removeObject (me->links, movingLink))) {
+        return NO;  /* link not found or NULL anchor */
+    } else {
+        /* First push current main link onto top of links list */
+        HTLink *newLink = (HTLink*) calloc (1, sizeof (HTLink));
+        if (newLink == NULL)
+	    outofmem(__FILE__, "HTAnchor_makeMainLink");
+        memcpy((void *)newLink,
+	       (CONST char *)&me->mainLink, sizeof (HTLink));
+        HTList_addObject (me->links, newLink);
+
+        /* Now make movingLink the new main link, and free it */
+        memcpy((void *)&me->mainLink,
+	       (CONST void *)movingLink, sizeof (HTLink));
+        FREE(movingLink);
+        return YES;
+    }
+}
+
+
+/*	Methods List
+**	------------
+*/
+
+PUBLIC HTList * HTAnchor_methods ARGS1(
+	HTParentAnchor *,	me)
+{
+    if (!me->methods) {
+        me->methods = HTList_new();
+    }
+    return me->methods;
+}
+
+/*	Protocol
+**	--------
+*/
+
+PUBLIC void * HTAnchor_protocol ARGS1(
+	HTParentAnchor *,	me)
+{
+    return me->protocol;
+}
+
+PUBLIC void HTAnchor_setProtocol ARGS2(
+	HTParentAnchor *,	me,
+	void*,			protocol)
+{
+    me->protocol = protocol;
+}
+
+/*	Physical Address
+**	----------------
+*/
+
+PUBLIC char * HTAnchor_physical ARGS1(
+	HTParentAnchor *,	me)
+{
+    return me->physical;
+}
+
+PUBLIC void HTAnchor_setPhysical ARGS2(
+	HTParentAnchor *,	me,
+	char *,			physical)
+{
+    if (me) {
+        StrAllocCopy(me->physical, physical);
+    }
+}
diff --git a/WWW/Library/Implementation/HTAnchor.h b/WWW/Library/Implementation/HTAnchor.h
new file mode 100644
index 00000000..e9a137cb
--- /dev/null
+++ b/WWW/Library/Implementation/HTAnchor.h
@@ -0,0 +1,358 @@
+/*                 /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTAnchor.html
+ */
+
+/*      Hypertext "Anchor" Object                                    HTAnchor.h
+**      ==========================
+**
+**      An anchor represents a region of a hypertext document which is linked
+**      to another anchor in the same or a different document.
+*/
+
+#ifndef HTANCHOR_H
+#define HTANCHOR_H
+
+/* Version 0 (TBL) written in Objective-C for the NeXT browser */
+/* Version 1 of 24-Oct-1991 (JFG), written in C, browser-independant */
+
+#include "HTList.h"
+#include "HTAtom.h"
+
+#ifdef SHORT_NAMES
+#define HTAnchor_findChild                      HTAnFiCh
+#define HTAnchor_findChildAndLink               HTAnFiLi
+#define HTAnchor_findAddress                    HTAnFiAd
+#define HTAnchor_delete                         HTAnDele
+#define HTAnchor_makeLastChild                  HTAnMaLa
+#define HTAnchor_parent                         HTAnPare
+#define HTAnchor_setDocument                    HTAnSeDo
+#define HTAnchor_document                       HTAnDocu
+#define HTAnchor_setFormat                      HTAnSeFo
+#define HTAnchor_format                         HTAnForm
+#define HTAnchor_setIndex                       HTAnSeIn
+#define HTAnchor_setPrompt                      HTAnSePr
+#define HTAnchor_isIndex                        HTAnIsIn
+#define HTAnchor_address                        HTAnAddr
+#define HTAnchor_hasChildren                    HTAnHaCh
+#define HTAnchor_title                          HTAnTitl
+#define HTAnchor_setTitle                       HTAnSeTi
+#define HTAnchor_appendTitle                    HTAnApTi
+#define HTAnchor_link                           HTAnLink
+#define HTAnchor_followMainLink                 HTAnFoMa
+#define HTAnchor_followTypedLink                HTAnFoTy
+#define HTAnchor_makeMainLink                   HTAnMaMa
+#define HTAnchor_setProtocol                    HTAnSePr
+#define HTAnchor_protocol                       HTAnProt
+#define HTAnchor_physical                       HTAnPhys
+#define HTAnchor_setPhysical                    HTAnSePh
+#define HTAnchor_methods                        HtAnMeth
+#endif
+
+/*                      Main definition of anchor
+**                      =========================
+*/
+
+typedef struct _HyperDoc HyperDoc;  /* Ready for forward references */
+typedef struct _HTAnchor HTAnchor;
+typedef struct _HTParentAnchor HTParentAnchor;
+
+/*      After definition of HTFormat: */
+#include "HTFormat.h"
+
+typedef HTAtom HTLinkType;
+
+typedef struct {
+  HTAnchor *    dest;           /* The anchor to which this leads */
+  HTLinkType *  type;           /* Semantics of this link */
+} HTLink;
+
+struct _HTAnchor {              /* Generic anchor : just links */
+  HTLink        mainLink;       /* Main (or default) destination of this */
+  HTList *      links;          /* List of extra links from this, if any */
+  /* We separate the first link from the others to avoid too many small mallocs
+     involved by a list creation. Most anchors only point to one place. */
+  HTParentAnchor * parent;      /* Parent of this anchor (self for adults) */
+};
+
+struct _HTParentAnchor {
+  /* Common part from the generic anchor structure */
+  HTLink        mainLink;       /* Main (or default) destination of this */
+  HTList *      links;          /* List of extra links from this, if any */
+  HTParentAnchor * parent;      /* Parent of this anchor (self) */
+
+  /* ParentAnchor-specific information */
+  HTList *      children;       /* Subanchors of this, if any */
+  HTList *      sources;        /* List of anchors pointing to this, if any */
+  HyperDoc *    document;       /* The document within which this is an anchor */
+  char *        address;        /* Absolute address of this node */
+  char *	post_data;      /* posting data */
+  char * 	post_content_type;  /* type of post data */
+  HTFormat      format;         /* Pointer to node format descriptor */
+  char *	charset;	/* Pointer to character set (kludge, for now */
+  BOOL          isIndex;        /* Acceptance of a keyword search */
+  char *        isIndexAction;  /* URL of isIndex server */
+  char *        isIndexPrompt;  /* Prompt for isIndex query */
+  char *        title;          /* Title of document */
+  char *        owner;          /* Owner of document */
+  char *        RevTitle;       /* TITLE in REV="made" or REV="owner" LINK */
+
+  HTList*       methods;        /* Methods available as HTAtoms */
+  void *        protocol;       /* Protocol object */
+  char *        physical;       /* Physical address */
+  BOOL          underway;       /* Document about to be attached to it */
+  BOOL		isISMAPScript;	/* Script for clickable image map */
+  BOOL		isHEAD;		/* Document is headers from a HEAD request */
+  char *	FileCache;	/* Path to a disk-cached copy */
+  char *	cache_control;	/* Cache-Control */
+  BOOL		no_cache;	/* Cache-Control, Pragma or META "no-cache"? */
+  char *	content_type;	/* Content-Type */
+  char *	content_language;   /* Content-Language */
+  char *	content_encoding;   /* Compression algorithm */
+};
+
+typedef struct {
+  /* Common part from the generic anchor structure */
+  HTLink        mainLink;       /* Main (or default) destination of this */
+  HTList *      links;          /* List of extra links from this, if any */
+  HTParentAnchor * parent;      /* Parent of this anchor */
+
+  /* ChildAnchor-specific information */
+  char *        tag;            /* Address of this anchor relative to parent */
+} HTChildAnchor;
+
+
+/* DocAddress structure is used for loading an absolute anchor with all
+ * needed information including posting data and post content type.
+ */
+
+typedef struct _DocAddress {
+    char * address;
+    char * post_data;
+    char * post_content_type;
+    BOOL   isHEAD;
+} DocAddress;
+
+/*      Create new or find old sub-anchor
+**      ---------------------------------
+**
+**      This one is for a new anchor being edited into an existing
+**      document. The parent anchor must already exist.
+*/
+
+extern HTChildAnchor * HTAnchor_findChild
+  PARAMS(
+     (HTParentAnchor *parent,
+      CONST char *tag)
+  );
+
+/*      Create or find a child anchor with a possible link
+**      --------------------------------------------------
+**
+**      Create new anchor with a given parent and possibly
+**      a name, and possibly a link to a _relatively_ named anchor.
+**      (Code originally in ParseHTML.h)
+*/
+extern HTChildAnchor * HTAnchor_findChildAndLink
+  PARAMS((
+      HTParentAnchor * parent,  /* May not be 0 */
+      CONST char * tag,         /* May be "" or 0 */
+      CONST char * href,        /* May be "" or 0 */
+      HTLinkType * ltype        /* May be 0 */
+  ));
+
+
+/*      Create new or find old named anchor
+**      -----------------------------------
+**
+**      This one is for a reference which is found in a document, and might
+**      not be already loaded.
+**      Note: You are not guaranteed a new anchor -- you might get an old one,
+**      like with fonts.
+*/
+
+extern HTAnchor * HTAnchor_findAddress PARAMS((CONST DocAddress * address));
+
+
+/*      Delete an anchor and possibly related things (auto garbage collection)
+**      --------------------------------------------
+**
+**      The anchor is only deleted if the corresponding document is not loaded.
+**      All outgoing links from parent and children are deleted, and this anchor
+**      is removed from the sources list of all its targets.
+**      We also try to delete the targets whose documents are not loaded.
+**      If this anchor's source list is empty, we delete it and its children.
+*/
+
+extern BOOL HTAnchor_delete
+  PARAMS(
+     (HTParentAnchor *me)
+     );
+
+
+/*              Move an anchor to the head of the list of its siblings
+**              ------------------------------------------------------
+**
+**      This is to ensure that an anchor which might have already existed
+**      is put in the correct order as we load the document.
+*/
+
+extern void HTAnchor_makeLastChild
+  PARAMS(
+     (HTChildAnchor *me)
+     );
+
+/*      Data access functions
+**      ---------------------
+*/
+
+extern HTParentAnchor * HTAnchor_parent
+  PARAMS(
+     (HTAnchor *me)
+     );
+
+extern void HTAnchor_setDocument
+  PARAMS(
+     (HTParentAnchor *me, HyperDoc *doc)
+     );
+
+extern HyperDoc * HTAnchor_document
+  PARAMS(
+     (HTParentAnchor *me)
+     );
+/* We don't want code to change an address after anchor creation... yet ?
+extern void HTAnchor_setAddress
+  PARAMS(
+     (HTAnchor *me, char *addr)
+     );
+*/
+
+/*      Returns the full URI of the anchor, child or parent
+**      as a malloc'd string to be freed by the caller.
+*/
+extern char * HTAnchor_address
+  PARAMS(
+     (HTAnchor *me)
+     );
+
+extern void HTAnchor_setFormat
+  PARAMS(
+     (HTParentAnchor *me, HTFormat form)
+     );
+
+extern HTFormat HTAnchor_format
+  PARAMS(
+     (HTParentAnchor *me)
+     );
+
+extern void HTAnchor_setIndex
+  PARAMS(
+     (HTParentAnchor *me, char * address)
+     );
+
+extern void HTAnchor_setPrompt
+  PARAMS(
+     (HTParentAnchor *me, char * prompt)
+     );
+
+extern BOOL HTAnchor_isIndex
+  PARAMS(
+     (HTParentAnchor *me)
+     );
+
+extern BOOL HTAnchor_hasChildren
+  PARAMS(
+     (HTParentAnchor *me)
+     );
+
+/*      Title handling
+*/
+extern CONST char * HTAnchor_title
+  PARAMS(
+     (HTParentAnchor *me)
+     );
+
+extern void HTAnchor_setTitle
+  PARAMS(
+     (HTParentAnchor *me, CONST char * title)
+     );
+
+extern void HTAnchor_appendTitle
+  PARAMS(
+     (HTParentAnchor *me, CONST char * title)
+     );
+
+/*      Owner handling
+*/
+extern CONST char * HTAnchor_owner
+  PARAMS(
+     (HTParentAnchor *me)
+     );
+
+extern void HTAnchor_setOwner
+  PARAMS(
+     (HTParentAnchor *me, CONST char * owner)
+     );
+
+/*      TITLE handling in LINKs with REV="made" or REV="owner"
+*/
+extern CONST char * HTAnchor_RevTitle
+  PARAMS(
+     (HTParentAnchor *me)
+     );
+
+extern void HTAnchor_setRevTitle
+  PARAMS(
+     (HTParentAnchor *me, CONST char * title)
+     );
+
+/*      Link this Anchor to another given one
+**      -------------------------------------
+*/
+
+extern BOOL HTAnchor_link
+  PARAMS(
+     (HTAnchor *source, HTAnchor *destination, HTLinkType *type)
+     );
+
+/*      Manipulation of links
+**      ---------------------
+*/
+
+extern HTAnchor * HTAnchor_followMainLink
+  PARAMS(
+     (HTAnchor *me)
+     );
+
+extern HTAnchor * HTAnchor_followTypedLink
+  PARAMS(
+     (HTAnchor *me, HTLinkType *type)
+     );
+
+extern BOOL HTAnchor_makeMainLink
+  PARAMS(
+     (HTAnchor *me, HTLink *movingLink)
+     );
+
+/*      Read and write methods
+**      ----------------------
+*/
+extern HTList * HTAnchor_methods PARAMS((HTParentAnchor *me));
+
+/*      Protocol
+**      --------
+*/
+extern void * HTAnchor_protocol PARAMS((HTParentAnchor * me));
+extern void HTAnchor_setProtocol PARAMS((HTParentAnchor * me,
+                                        void* protocol));
+
+/*      Physical address
+**      ----------------
+*/
+extern char * HTAnchor_physical PARAMS((HTParentAnchor * me));
+extern void HTAnchor_setPhysical PARAMS((HTParentAnchor * me,
+                                        char * protocol));
+
+#endif /* HTANCHOR_H */
+
+/*
+
+    */
diff --git a/WWW/Library/Implementation/HTAssoc.c b/WWW/Library/Implementation/HTAssoc.c
new file mode 100644
index 00000000..e63c0213
--- /dev/null
+++ b/WWW/Library/Implementation/HTAssoc.c
@@ -0,0 +1,87 @@
+
+/* MODULE							HTAssoc.c
+**	    ASSOCIATION LIST FOR STORING NAME-VALUE PAIRS.
+**	    NAMES NOT CASE SENSITIVE, AND ONLY COMMON LENGTH
+**	    IS CHECKED (allows abbreviations; well, length is
+**	    taken from lookup-up name, so if table contains
+**	    a shorter abbrev it is not found).
+** AUTHORS:
+**	AL	Ari Luotonen	luotonen@dxcern.cern.ch
+**
+** HISTORY:
+**
+**
+** BUGS:
+**
+**
+*/
+
+
+#include "HTUtils.h"
+
+#include <string.h>
+
+#include "HTAAUtil.h"
+#include "HTAssoc.h"
+#include "HTString.h"
+
+#include "LYLeaks.h"
+
+PUBLIC HTAssocList *HTAssocList_new NOARGS
+{
+    return HTList_new();
+}
+
+
+PUBLIC void HTAssocList_delete ARGS1(HTAssocList *, alist)
+{
+    if (alist) {
+	HTAssocList *cur = alist;
+	HTAssoc *assoc;
+	while (NULL != (assoc = (HTAssoc*)HTList_nextObject(cur))) {
+	    FREE(assoc->name);
+	    FREE(assoc->value);
+	    FREE(assoc);
+	}
+	HTList_delete(alist);
+	alist = NULL;
+    }
+}
+
+
+PUBLIC void HTAssocList_add ARGS3(HTAssocList *,	alist,
+				  CONST char *,		name,
+				  CONST char *,		value)
+{
+    HTAssoc *assoc;
+
+    if (alist) {
+	if (!(assoc = (HTAssoc*)malloc(sizeof(HTAssoc))))
+	    outofmem(__FILE__, "HTAssoc_add");
+	assoc->name = NULL;
+	assoc->value = NULL;
+
+	if (name)
+	    StrAllocCopy(assoc->name, name);
+	if (value)
+	    StrAllocCopy(assoc->value, value);
+	HTList_addObject(alist, (void*)assoc);
+    } else if (TRACE) {
+        fprintf(stderr, "HTAssoc_add: ERROR: assoc list NULL!!\n");
+    }
+}
+
+
+PUBLIC char *HTAssocList_lookup ARGS2(HTAssocList *,	alist,
+				      CONST char *,	name)
+{
+    HTAssocList *cur = alist;
+    HTAssoc *assoc;
+
+    while (NULL != (assoc = (HTAssoc*)HTList_nextObject(cur))) {
+	if (!strncasecomp(assoc->name, name, strlen(name)))
+	    return assoc->value;
+    }
+    return NULL;
+}
+
diff --git a/WWW/Library/Implementation/HTAssoc.h b/WWW/Library/Implementation/HTAssoc.h
new file mode 100644
index 00000000..71b0c918
--- /dev/null
+++ b/WWW/Library/Implementation/HTAssoc.h
@@ -0,0 +1,44 @@
+/*                       ASSOCIATION LIST FOR STORING NAME-VALUE PAIRS
+                                             
+   Lookups from assosiation list are not case-sensitive.
+   
+ */
+
+#ifndef HTASSOC_H
+#define HTASSOC_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+#include "HTList.h"
+
+
+#ifdef SHORT_NAMES
+#define HTAL_new        HTAssocList_new
+#define HTAL_del        HTAssocList_delete
+#define HTAL_add        HTAssocList_add
+#define HTAL_lup        HTAssocList_lookup
+#endif /*SHORT_NAMES*/
+
+typedef HTList HTAssocList;
+
+typedef struct {
+    char * name;
+    char * value;
+} HTAssoc;
+
+
+PUBLIC HTAssocList *HTAssocList_new NOPARAMS;
+PUBLIC void HTAssocList_delete PARAMS((HTAssocList * alist));
+
+PUBLIC void HTAssocList_add PARAMS((HTAssocList *       alist,
+                                    CONST char *        name,
+                                    CONST char *        value));
+
+PUBLIC char *HTAssocList_lookup PARAMS((HTAssocList *   alist,
+                                        CONST char *    name));
+
+#endif /* not HTASSOC_H */
+/*
+
+   End of file HTAssoc.h.  */
diff --git a/WWW/Library/Implementation/HTAtom.c b/WWW/Library/Implementation/HTAtom.c
new file mode 100644
index 00000000..8286f49b
--- /dev/null
+++ b/WWW/Library/Implementation/HTAtom.c
@@ -0,0 +1,169 @@
+/*			Atoms: Names to numbers			HTAtom.c
+**			=======================
+**
+**	Atoms are names which are given representative pointer values
+**	so that they can be stored more efficiently, and comparisons
+**	for equality done more efficiently.
+**
+**	Atoms are kept in a hash table consisting of an array of linked lists.
+**
+** Authors:
+**	TBL	Tim Berners-Lee, WorldWideWeb project, CERN
+**	(c) Copyright CERN 1991 - See Copyright.html
+**
+*/
+#include "HTUtils.h"
+
+#define HASH_SIZE	101		/* Tunable */
+#include "HTAtom.h"
+
+/*#include <stdio.h> included by HTUtils.h -- FM *//* joe@athena, TBL 921019 */
+#include <string.h>
+
+#include "HTList.h"
+
+#include "LYexit.h"
+#include "LYLeaks.h"
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+PRIVATE HTAtom * hash_table[HASH_SIZE];
+PRIVATE BOOL initialised = NO;
+
+/*
+ *	To free off all atoms.
+ */
+PRIVATE void free_atoms NOPARAMS;
+
+/*
+ *	Alternate hashing function.
+ */
+#define HASH_FUNCTION(cp_hash) ((strlen(cp_hash) * *cp_hash) % HASH_SIZE)
+
+PUBLIC HTAtom * HTAtom_for ARGS1(CONST char *, string)
+{
+    int hash;
+    HTAtom * a;
+    
+    /*		First time around, clear hash table
+    */
+    /*
+     *	Memory leak fixed.
+     *  05-29-94 Lynx 2-3-1 Garrett Arch Blythe
+     */
+    if (!initialised) {
+        int i;
+	for (i = 0; i < HASH_SIZE; i++)
+	    hash_table[i] = (HTAtom *) 0;
+	initialised = YES;
+	atexit(free_atoms);
+    }
+    
+    /*		Generate hash function
+    */
+    hash = HASH_FUNCTION(string);
+    
+    /*		Search for the string in the list
+    */
+    for (a = hash_table[hash]; a; a = a->next) {
+	if (0 == strcasecomp(a->name, string)) {
+    	    /* if (TRACE) fprintf(stderr,
+	    	"HTAtom: Old atom %p for `%s'\n", a, string); */
+	    return a;				/* Found: return it */
+	}
+    }
+    
+    /*		Generate a new entry
+    */
+    a = (HTAtom *)malloc(sizeof(*a));
+    if (a == NULL)
+        outofmem(__FILE__, "HTAtom_for");
+    a->name = (char *)malloc(strlen(string)+1);
+    if (a->name == NULL)
+        outofmem(__FILE__, "HTAtom_for");
+    strcpy(a->name, string);
+    a->next = hash_table[hash];		/* Put onto the head of list */
+    hash_table[hash] = a;
+#ifdef NOT_DEFINED
+    if (TRACE)
+	fprintf(stderr, "HTAtom: New atom %p for `%s'\n", a, string);
+#endif /* NOT_DEFINED */
+    return a;
+}
+
+/*
+ *	Purpose:	Free off all atoms.
+ *	Arguments:	void
+ *	Return Value:	void
+ *	Remarks/Portability/Dependencies/Restrictions:
+ *		To be used at program exit.
+ *	Revision History:
+ *		05-29-94	created Lynx 2-3-1 Garrett Arch Blythe
+ */
+PRIVATE void free_atoms NOARGS
+{
+	auto int i_counter;
+	HTAtom *HTAp_freeme;
+	/*
+	 *	Loop through all lists of atoms.
+	 */
+	for (i_counter = 0; i_counter < HASH_SIZE; i_counter++)	{
+		/*
+		 *	Loop through the list.
+		 */
+		while (hash_table[i_counter] != NULL)	{
+			/*
+		 	 *	Free off atoms and any members.
+			 */
+			HTAp_freeme = hash_table[i_counter];
+			hash_table[i_counter] = HTAp_freeme->next;
+			FREE(HTAp_freeme->name);
+			FREE(HTAp_freeme);
+		}
+	}
+}
+
+PRIVATE BOOL mime_match ARGS2(CONST char *, name,
+			      CONST char *, templ)
+{
+    if (name && templ) {
+	static char *n1 = NULL;
+	static char *t1 = NULL;
+	char *n2;
+	char *t2;
+
+	StrAllocCopy(n1, name);		/* These also free the ones	*/
+	StrAllocCopy(t1, templ);	/* from previous call.		*/
+
+	if (!(n2 = strchr(n1, '/'))  ||  !(t2 = strchr(t1, '/')))
+	    return NO;
+
+	*(n2++) = (char)0;
+	*(t2++) = (char)0;
+
+	if ((0 == strcmp(t1, "*") || 0 == strcmp(t1, n1)) &&
+	    (0 == strcmp(t2, "*") || 0 == strcmp(t2, n2)))
+	    return YES;
+    }
+    return NO;
+}
+	
+
+PUBLIC HTList *HTAtom_templateMatches ARGS1(CONST char *, templ)
+{
+    HTList *matches = HTList_new();
+
+    if (initialised && templ) {
+	int i;
+	HTAtom *cur;
+
+	for (i = 0; i < HASH_SIZE; i++) {
+	    for (cur = hash_table[i];  cur;  cur = cur->next) {
+		if (mime_match(cur->name, templ))
+		    HTList_addObject(matches, (void*)cur);
+	    }
+	}
+    }
+    return matches;
+}
+
diff --git a/WWW/Library/Implementation/HTAtom.h b/WWW/Library/Implementation/HTAtom.h
new file mode 100644
index 00000000..b8dd10ec
--- /dev/null
+++ b/WWW/Library/Implementation/HTAtom.h
@@ -0,0 +1,49 @@
+/*  */
+
+/*                      Atoms: Names to numbers                 HTAtom.h
+**                      =======================
+**
+**      Atoms are names which are given representative pointer values
+**      so that they can be stored more efficiently, and compaisons
+**      for equality done more efficiently.
+**
+**      HTAtom_for(string) returns a representative value such that it
+**      will always (within one run of the program) return the same
+**      value for the same given string.
+**
+** Authors:
+**      TBL     Tim Berners-Lee, WorldWideWeb project, CERN
+**
+**      (c) Copyright CERN 1991 - See Copyright.html
+**
+*/
+
+#ifndef HTATOM_H
+#define HTATOM_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+#include "HTList.h"
+
+#ifdef SHORT_NAMES
+#define HTAt_for        HTAtom_for
+#define HTAt_tMa        HTAtom_templateMatches
+#endif /*SHORT_NAMES*/
+
+typedef struct _HTAtom HTAtom;
+struct _HTAtom {
+        HTAtom *        next;
+        char *          name;
+}; /* struct _HTAtom */
+
+
+PUBLIC HTAtom * HTAtom_for PARAMS((CONST char * string));
+PUBLIC HTList * HTAtom_templateMatches PARAMS((CONST char * templ));
+
+#define HTAtom_name(a) ((a)->name)
+
+#endif  /* HTATOM_H */
+/*
+
+    */
diff --git a/WWW/Library/Implementation/HTAuth.c b/WWW/Library/Implementation/HTAuth.c
new file mode 100644
index 00000000..7f7b363b
--- /dev/null
+++ b/WWW/Library/Implementation/HTAuth.c
@@ -0,0 +1,210 @@
+
+/* MODULE							HTAuth.c
+**			USER AUTHENTICATION
+**
+** AUTHORS:
+**	AL	Ari Luotonen	luotonen@dxcern.cern.ch
+**
+** HISTORY:
+**	AL 14.10.93 Fixed the colon-not-allowed-in-password-bug.
+**
+** BUGS:
+**
+**
+*/
+
+#include "HTUtils.h"
+#include <string.h>
+#include "HTPasswd.h"	/* Password file routines	*/
+#include "HTAssoc.h"
+#include "HTAuth.h"	/* Implemented here		*/
+#include "HTUU.h"	/* Uuencoding and uudecoding	*/
+
+#include "LYLeaks.h"
+
+/* PRIVATE					    decompose_auth_string()
+**		DECOMPOSE AUTHENTICATION STRING
+**		FOR BASIC OR PUBKEY SCHEME
+** ON ENTRY:
+**	authstring	is the authorization string received
+**			from browser.
+**
+** ON EXIT:
+**	returns		a node representing the user information
+**			(as always, this is automatically freed
+**			by AA package).
+*/
+PRIVATE HTAAUser *decompose_auth_string ARGS2(char *,		authstring,
+					      HTAAScheme,	scheme)
+{
+    static HTAAUser *user = NULL;
+    static char *cleartext = NULL;
+    char *username = NULL;
+    char *password = NULL;
+    char *inet_addr = NULL;
+    char *timestamp = NULL;
+    char *browsers_key = NULL;
+
+    if (!user && !(user = (HTAAUser*)malloc(sizeof(HTAAUser))))	/* Allocated */
+	outofmem(__FILE__, "decompose_auth_string");		/* only once */
+
+    user->scheme = scheme;
+    user->username = NULL;	/* Not freed, because freeing */
+    user->password = NULL;	/* cleartext also frees these */
+    user->inet_addr = NULL;	/* See below: ||              */
+    user->timestamp = NULL;	/*            ||              */
+    user->secret_key = NULL;	/*            ||              */
+                                /*            \/              */
+    FREE(cleartext);	/* From previous call.				*/
+                        /* NOTE: parts of this memory are pointed to by	*/
+                        /* pointers in HTAAUser structure. Therefore,	*/
+                        /* this also frees all the strings pointed to	*/
+			/* by the static 'user'.			*/
+
+    if (!authstring || !*authstring || 
+	scheme != HTAA_BASIC || scheme == HTAA_PUBKEY)
+	return NULL;
+
+    if (scheme == HTAA_PUBKEY) {    /* Decrypt authentication string */
+	int bytes_decoded;
+	char *ciphertext;
+	int len = strlen(authstring) + 1;
+
+	if (!(ciphertext = (char*)malloc(len)) ||
+	    !(cleartext  = (char*)malloc(len)))
+	    outofmem(__FILE__, "decompose_auth_string");
+
+	bytes_decoded = HTUU_decode(authstring,
+				    (unsigned char *)ciphertext, len);
+	ciphertext[bytes_decoded] = (char)0;
+#ifdef PUBKEY
+	HTPK_decrypt(ciphertext, cleartext, private_key);
+#endif
+	FREE(ciphertext);
+    }
+    else {   /* Just uudecode */
+	int bytes_decoded;
+	int len = strlen(authstring) + 1;
+	
+	if (!(cleartext = (char*)malloc(len)))
+	    outofmem(__FILE__, "decompose_auth_string");
+	bytes_decoded = HTUU_decode(authstring,
+				    (unsigned char *)cleartext, len);
+	cleartext[bytes_decoded] = (char)0;
+    }
+
+
+/*
+** Extract username and password (for both schemes)
+*/
+    username = cleartext;
+    if (!(password = strchr(cleartext, ':'))) {
+	if (TRACE)
+	    fprintf(stderr, "%s %s\n",
+		    "decompose_auth_string: password field",
+		    "missing in authentication string.\n");
+	return NULL;
+    }
+    *(password++) = '\0';
+
+/*
+** Extract rest of the fields
+*/
+    if (scheme == HTAA_PUBKEY) {
+	if (                          !(inet_addr   =strchr(password, ':')) || 
+	    (*(inet_addr++)   ='\0'), !(timestamp   =strchr(inet_addr,':')) ||
+	    (*(timestamp++)   ='\0'), !(browsers_key=strchr(timestamp,':')) ||
+	    (*(browsers_key++)='\0')) {
+
+	    if (TRACE) fprintf(stderr, "%s %s\n",
+			       "decompose_auth_string: Pubkey scheme",
+			       "fields missing in authentication string");
+	    return NULL;
+	}
+    }
+
+/*
+** Set the fields into the result
+*/
+    user->username   = username;
+    user->password   = password;
+    user->inet_addr  = inet_addr;
+    user->timestamp  = timestamp;
+    user->secret_key = browsers_key;
+
+    if (TRACE) {
+	if (scheme==HTAA_BASIC)
+	    fprintf(stderr, "decompose_auth_string: %s (%s,%s)\n",
+		    "Basic scheme authentication string:",
+		    username, password);
+	else
+	    fprintf(stderr, "decompose_auth_string: %s (%s,%s,%s,%s,%s)\n",
+		    "Pubkey scheme authentication string:",
+		    username, password, inet_addr, timestamp, browsers_key);
+    }
+    
+    return user;
+}
+
+
+
+PRIVATE BOOL HTAA_checkTimeStamp ARGS1(CONST char *, timestamp)
+{
+    return NO;		/* This is just a stub */
+}
+
+
+PRIVATE BOOL HTAA_checkInetAddress ARGS1(CONST char *, inet_addr)
+{
+    return NO;		/* This is just a stub */
+}
+
+
+/* SERVER PUBLIC					HTAA_authenticate()
+**			AUTHENTICATE USER
+** ON ENTRY:
+**	scheme		used authentication scheme.
+**	scheme_specifics the scheme specific parameters
+**			(authentication string for Basic and
+**			Pubkey schemes).
+**	prot		is the protection information structure
+**			for the file.
+**
+** ON EXIT:
+**	returns		NULL, if authentication failed.
+**			Otherwise a pointer to a structure
+**			representing authenticated user,
+**			which should not be freed.
+*/
+PUBLIC HTAAUser *HTAA_authenticate ARGS3(HTAAScheme,	scheme,
+					 char *,	scheme_specifics,
+					 HTAAProt *,	prot)
+{
+    if (HTAA_UNKNOWN == scheme || !prot ||
+	-1 == HTList_indexOf(prot->valid_schemes, (void*)scheme))
+	return NULL;
+
+    switch (scheme) {
+      case HTAA_BASIC:
+      case HTAA_PUBKEY:
+	{
+	    HTAAUser *user = decompose_auth_string(scheme_specifics, scheme);
+	                                   /* Remember, user is auto-freed */
+	    if (user &&
+		HTAA_checkPassword(user->username,
+				   user->password,
+				   HTAssocList_lookup(prot->values, "passw")) &&
+		(HTAA_BASIC == scheme ||
+		 (HTAA_checkTimeStamp(user->timestamp) &&
+		  HTAA_checkInetAddress(user->inet_addr))))
+		return user;
+	    else
+		return NULL;
+	}
+	break;
+      default:
+	/* Other authentication routines go here */
+	return NULL;
+    }
+}
+
diff --git a/WWW/Library/Implementation/HTAuth.h b/WWW/Library/Implementation/HTAuth.h
new file mode 100644
index 00000000..6dc4c516
--- /dev/null
+++ b/WWW/Library/Implementation/HTAuth.h
@@ -0,0 +1,66 @@
+/*                                   AUTHENTICATION MODULE
+                                             
+   This is the authentication module. By modifying the function HTAA_authenticate() it can
+   be made to support external authentication methods.
+   
+ */
+
+#ifndef HTAUTH_H
+#define HTAUTH_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+#include "HTAAUtil.h"
+#include "HTAAProt.h"
+
+
+#ifdef SHORT_NAMES
+#define HTAAauth        HTAA_authenticate
+#endif /* SHORT_NAMES */
+
+
+/*
+** Server's representation of a user (fields in authentication string)
+*/
+typedef struct {
+    HTAAScheme  scheme;         /* Scheme used to authenticate this user */
+    char *      username;
+    char *      password;
+    char *      inet_addr;
+    char *      timestamp;
+    char *      secret_key;
+} HTAAUser;
+/*
+
+User Authentication
+
+ */
+
+/* SERVER PUBLIC                                        HTAA_authenticate()
+**                      AUTHENTICATE USER
+** ON ENTRY:
+**      scheme          used authentication scheme.
+**      scheme_specifics the scheme specific parameters
+**                      (authentication string for Basic and
+**                      Pubkey schemes).
+**      prot            is the protection information structure
+**                      for the file.
+**
+** ON EXIT:
+**      returns         NULL, if authentication failed.
+**                      Otherwise a pointer to a structure
+**                      representing authenticated user,
+**                      which should not be freed.
+*/
+PUBLIC HTAAUser *HTAA_authenticate PARAMS((HTAAScheme   scheme,
+                                           char *       scheme_specifics,
+                                           HTAAProt *   prot));
+/*
+
+ */
+
+#endif /* not HTAUTH_H */
+/*
+
+   End of file HTAuth.h.  */
diff --git a/WWW/Library/Implementation/HTBTree.c b/WWW/Library/Implementation/HTBTree.c
new file mode 100644
index 00000000..cc90111a
--- /dev/null
+++ b/WWW/Library/Implementation/HTBTree.c
@@ -0,0 +1,720 @@
+/*                  Binary Tree for sorting things
+**                  ==============================
+**                      Author: Arthur Secret
+**
+**       4 March 94: Bug fixed in the balancing procedure
+**
+*/
+
+
+#include "HTUtils.h"
+#include "HTBTree.h"
+#ifndef __STRICT_BSD__
+#include <stdlib.h>
+#endif
+#include <string.h>
+
+#define MAXIMUM(a,b) ((a)>(b)?(a):(b))
+
+#include "LYLeaks.h"
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+
+PUBLIC HTBTree * HTBTree_new ARGS1(HTComparer, comp)
+    /*********************************************************
+    ** This function returns an HTBTree with memory allocated 
+    ** for it when given a mean to compare things
+    */
+{
+    HTBTree * tree = (HTBTree *)malloc(sizeof(HTBTree));
+    if (tree==NULL) outofmem(__FILE__, "HTBTree_new");
+
+    tree->compare = comp;
+    tree->top = NULL;
+
+    return tree;
+}
+
+
+
+
+PRIVATE void HTBTElement_free ARGS1(HTBTElement*, element)
+    /**********************************************************
+    ** This void will free the memory allocated for one element
+    */
+{
+    if (element) {
+        if (element->left != NULL)
+	    HTBTElement_free(element->left);
+	if (element->right != NULL)
+	    HTBTElement_free(element->right);
+	FREE(element);
+    }
+}
+
+PUBLIC void HTBTree_free ARGS1(HTBTree*, tree)
+    /**************************************************************
+    ** This void will free the memory allocated for the whole tree
+    */
+{
+    HTBTElement_free(tree->top);
+    FREE(tree);
+}
+
+
+
+
+PRIVATE void HTBTElementAndObject_free ARGS1(HTBTElement*, element)
+    /**********************************************************
+    ** This void will free the memory allocated for one element
+    */
+{
+    if (element) {     /* Just in case nothing was in the tree anyway */
+        if (element->left != NULL)
+	    HTBTElementAndObject_free(element->left);
+	if (element->right != NULL)    
+	    HTBTElementAndObject_free(element->right);
+	FREE(element->object);
+	FREE(element);
+    }
+}
+
+PUBLIC void HTBTreeAndObject_free ARGS1(HTBTree*, tree)
+    /**************************************************************
+    ** This void will free the memory allocated for the whole tree
+    */
+{
+    HTBTElementAndObject_free(tree->top);
+    FREE(tree);
+}
+
+
+
+
+PUBLIC void HTBTree_add ARGS2(
+		    HTBTree*,  tree,
+		    void*,     object)
+    /**********************************************************************
+    ** This void is the core of HTBTree.c . It will
+    **       1/ add a new element to the tree at the right place
+    **          so that the tree remains sorted
+    **       2/ balance the tree to be as fast as possible when reading it
+    */
+{
+    HTBTElement * father_of_element;
+    HTBTElement * added_element;
+    HTBTElement * forefather_of_element;
+    HTBTElement * father_of_forefather;
+    BOOL father_found,top_found;
+    int depth,depth2,corrections;
+        /* father_of_element is a pointer to the structure that is the father of the
+        ** new object "object".
+        ** added_element is a pointer to the structure that contains or will contain 
+        ** the new object "object".
+        ** father_of_forefather and forefather_of_element are pointers that are used
+        ** to modify the depths of upper elements, when needed.
+        **
+        ** father_found indicates by a value NO when the future father of "object" 
+        ** is found.
+        ** top_found indicates by a value NO when, in case of a difference of depths
+        **  < 2, the top of the tree is encountered and forbids any further try to
+        ** balance the tree.
+        ** corrections is an integer used to avoid infinite loops in cases
+        ** such as:
+        **
+        **             3                        3
+        **          4                              4
+        **           5                            5
+        **
+        ** 3 is used here to show that it need not be the top of the tree.
+        */
+
+    /*
+    ** 1/ Adding of the element to the binary tree
+    */
+
+    if (tree->top == NULL)
+    {
+        tree->top = (HTBTElement *)malloc(sizeof(HTBTElement));
+        if (tree->top == NULL) outofmem(__FILE__, "HTBTree_add");
+        tree->top->up = NULL;
+        tree->top->object = object;
+        tree->top->left = NULL;
+        tree->top->left_depth = 0;
+        tree->top->right = NULL;
+        tree->top->right_depth = 0;
+    }
+    else
+    {   
+        father_found = YES;
+        father_of_element = tree->top;
+        added_element = NULL;
+        father_of_forefather = NULL;
+        forefather_of_element = NULL;      
+        while (father_found)
+        {
+            if (tree->compare(object,father_of_element->object)<0)
+	    {
+                if (father_of_element->left != NULL)
+                    father_of_element = father_of_element->left;
+                else 
+	        {
+                    father_found = NO;
+                    father_of_element->left = 
+                        (HTBTElement *)malloc(sizeof(HTBTElement));
+                    if (father_of_element->left==NULL) 
+                        outofmem(__FILE__, "HTBTree_add");
+                    added_element = father_of_element->left;
+                    added_element->up = father_of_element;
+                    added_element->object = object;
+                    added_element->left = NULL;
+                    added_element->left_depth = 0;
+                    added_element->right = NULL;
+                    added_element->right_depth = 0;
+                }
+   	    }
+            if (tree->compare(object,father_of_element->object)>=0)
+            {
+                if (father_of_element->right != NULL) 
+                    father_of_element = father_of_element->right;
+                else 
+                {  
+                    father_found = NO;
+                    father_of_element->right = 
+                        (HTBTElement *)malloc(sizeof(HTBTElement));
+                    if (father_of_element->right==NULL) 
+                        outofmem(__FILE__, "HTBTree_add");
+                    added_element = father_of_element->right;
+                    added_element->up = father_of_element;
+                    added_element->object = object;
+                    added_element->left = NULL;
+                    added_element->left_depth = 0;
+                    added_element->right = NULL;
+                    added_element->right_depth = 0;       
+    	        }
+            }
+	}
+            /*
+            ** Changing of all depths that need to be changed
+            */
+        father_of_forefather = father_of_element;
+        forefather_of_element = added_element;
+        do
+        {
+            if (father_of_forefather->left == forefather_of_element)
+            {
+                depth = father_of_forefather->left_depth;
+                father_of_forefather->left_depth = 1 
+                            + MAXIMUM(forefather_of_element->right_depth,
+                                  forefather_of_element->left_depth);
+                depth2 = father_of_forefather->left_depth;
+            }
+            else
+	    {
+                depth = father_of_forefather->right_depth;
+                father_of_forefather->right_depth = 1
+                            + MAXIMUM(forefather_of_element->right_depth,
+                                  forefather_of_element->left_depth);
+                depth2 = father_of_forefather->right_depth;
+            }
+            forefather_of_element = father_of_forefather;
+            father_of_forefather = father_of_forefather->up;
+        } while ((depth != depth2) && (father_of_forefather != NULL));
+        
+
+
+            /*
+            ** 2/ Balancing the binary tree, if necessary
+            */
+        top_found = YES;
+        corrections = 0;
+        while ((top_found) && (corrections < 7))
+        {
+            if ((abs(father_of_element->left_depth
+                      - father_of_element->right_depth)) < 2)
+	    {
+                if (father_of_element->up != NULL) 
+                    father_of_element = father_of_element->up;
+                else top_found = NO;
+	    }
+            else
+ 	    {                /* We start the process of balancing */
+
+                corrections = corrections + 1;
+                    /* 
+                    ** corrections is an integer used to avoid infinite 
+                    ** loops in cases such as:
+                    **
+                    **             3                        3
+                    **          4                              4
+                    **           5                            5
+                    **
+                    ** 3 is used to show that it need not be the top of the tree
+		    ** But let's avoid these two exceptions anyhow 
+		    ** with the two following conditions (4 March 94 - AS)
+                    */
+
+		if ((father_of_element->left == NULL) 
+		    && (father_of_element->right->right == NULL) 
+		    && (father_of_element->right->left->left == NULL) 
+		    && (father_of_element->right->left->right == NULL)) 
+		    corrections = 7;
+
+		if ((father_of_element->right == NULL) 
+		    && (father_of_element->left->left == NULL) 
+		    && (father_of_element->left->right->right == NULL) 
+		    && (father_of_element->left->right->left == NULL))
+		    corrections = 7;
+ 
+
+                if (father_of_element->left_depth > father_of_element->right_depth)
+	        {
+                    added_element = father_of_element->left;
+                    father_of_element->left_depth = added_element->right_depth;
+                    added_element->right_depth = 1
+                                    + MAXIMUM(father_of_element->right_depth,
+                                          father_of_element->left_depth);
+                    if (father_of_element->up != NULL)
+		    {
+			/* Bug fixed in March 94  -  AS */
+			BOOL first_time;
+
+                        father_of_forefather = father_of_element->up;
+                        forefather_of_element = added_element;
+			first_time = YES;
+                        do 
+                        {
+                            if (father_of_forefather->left
+                                 == forefather_of_element->up)
+                              {
+				  depth = father_of_forefather->left_depth;
+				  if (first_time)
+				  {
+				      father_of_forefather->left_depth = 1
+					  + MAXIMUM(forefather_of_element->left_depth,
+						  forefather_of_element->right_depth);
+					first_time = NO;
+				   }
+				   else
+				       father_of_forefather->left_depth = 1
+					   + MAXIMUM(forefather_of_element->up->left_depth,
+					      forefather_of_element->up->right_depth);
+
+                                depth2 = father_of_forefather->left_depth;
+			    }
+                            else
+			    {
+                                depth = father_of_forefather->right_depth;
+				if (first_time)
+				{
+				    father_of_forefather->right_depth = 1
+				      + MAXIMUM(forefather_of_element->left_depth,
+					       forefather_of_element->right_depth);
+				    first_time = NO;
+				}				
+				else
+				    father_of_forefather->right_depth = 1
+				      + MAXIMUM(forefather_of_element->up->left_depth,
+					   forefather_of_element->up->right_depth);
+                                depth2 = father_of_forefather->right_depth;
+			    }
+                            forefather_of_element = forefather_of_element->up;
+                            father_of_forefather = father_of_forefather->up;
+			} while ((depth != depth2) && 
+				 (father_of_forefather != NULL));
+                        father_of_forefather = father_of_element->up;
+                        if (father_of_forefather->left == father_of_element)
+	                {
+                            /*
+                            **                   3                       3
+                            **               4                       5
+                            ** When tree   5   6        becomes    7    4
+                            **            7 8                          8 6
+                            **
+                            ** 3 is used to show that it may not be the top of the
+                            ** tree.
+                            */ 
+                            father_of_forefather->left = added_element;
+                            father_of_element->left = added_element->right;
+                            added_element->right = father_of_element;
+                        }
+                        if (father_of_forefather->right == father_of_element)
+		        {
+                            /*
+                            **          3                       3
+                            **               4                       5
+                            ** When tree   5   6        becomes    7    4
+                            **            7 8                          8 6
+                            **
+                            ** 3 is used to show that it may not be the top of the
+                            ** tree
+                            */
+                            father_of_forefather->right = added_element;
+                            father_of_element->left = added_element->right;
+                            added_element->right = father_of_element;
+                        }
+                        added_element->up = father_of_forefather;
+		    }
+                    else
+		    {
+                        /*
+                        **
+                        **               1                       2
+                        ** When tree   2   3        becomes    4    1
+                        **            4 5                          5 3
+                        **
+                        ** 1 is used to show that it is the top of the tree    
+                        */
+                        added_element->up = NULL;
+                        father_of_element->left = added_element->right;
+                        added_element->right = father_of_element;
+		    }
+                    father_of_element->up = added_element;
+                    if (father_of_element->left != NULL)
+                        father_of_element->left->up = father_of_element;
+	        }
+                else
+	        {
+                    added_element = father_of_element->right;
+                    father_of_element->right_depth = added_element->left_depth;
+                    added_element->left_depth = 1 + 
+                            MAXIMUM(father_of_element->right_depth,
+                                father_of_element->left_depth);
+                    if (father_of_element->up != NULL)
+			/* Bug fixed in March 94  -  AS */
+		    {
+			BOOL first_time;
+
+                        father_of_forefather = father_of_element->up;
+                        forefather_of_element = added_element;
+			first_time = YES;
+                        do 
+                        {
+                            if (father_of_forefather->left 
+				== forefather_of_element->up)
+                            {
+                                depth = father_of_forefather->left_depth;
+                                if (first_time)
+				{
+				    father_of_forefather->left_depth = 1
+				       + MAXIMUM(forefather_of_element->left_depth,
+					       forefather_of_element->right_depth);
+				    first_time = NO;
+				}
+                                else
+				    father_of_forefather->left_depth = 1
+				      + MAXIMUM(forefather_of_element->up->left_depth,
+				       	  forefather_of_element->up->right_depth);
+				depth2 = father_of_forefather->left_depth;
+			    }
+                            else
+			    {
+                                depth = father_of_forefather->right_depth;
+				if (first_time)
+				{
+				    father_of_forefather->right_depth = 1
+				       + MAXIMUM(forefather_of_element->left_depth,
+					       forefather_of_element->right_depth);
+				    first_time = NO;
+				}
+				else
+				    father_of_forefather->right_depth = 1
+				      + MAXIMUM(forefather_of_element->up->left_depth,
+					   forefather_of_element->up->right_depth);
+                                depth2 = father_of_forefather->right_depth;
+			    }
+                            father_of_forefather = father_of_forefather->up;
+                            forefather_of_element = forefather_of_element->up;
+			} while ((depth != depth2) && 
+				 (father_of_forefather != NULL));
+                        father_of_forefather = father_of_element->up;
+                        if (father_of_forefather->left == father_of_element)
+		        {
+                            /*
+                            **                    3                       3
+                            **               4                       6
+                            ** When tree   5   6        becomes    4    8
+                            **                7 8                 5 7
+                            **
+                            ** 3 is used to show that it may not be the top of the
+                            ** tree.
+                            */
+                            father_of_forefather->left = added_element;
+                            father_of_element->right = added_element->left;
+                            added_element->left = father_of_element;
+                        }
+                        if (father_of_forefather->right == father_of_element)
+		        {
+                            /*
+                            **           3                      3
+                            **               4                       6
+                            ** When tree   5   6        becomes    4    8
+                            **                7 8                 5 7
+                            **
+                            ** 3 is used to show that it may not be the top of the
+                            ** tree
+                            */
+                            father_of_forefather->right = added_element;
+                            father_of_element->right = added_element->left;
+                            added_element->left = father_of_element;
+                        }
+                        added_element->up = father_of_forefather;
+		    }
+                    else
+                    {
+                        /*
+                        **
+                        **               1                       3
+                        ** When tree   2   3        becomes    1    5
+                        **                4 5                 2 4
+                        **
+                        ** 1 is used to show that it is the top of the tree.
+                        */
+                        added_element->up = NULL;
+                        father_of_element->right = added_element->left;
+                        added_element->left = father_of_element;
+		    }
+                    father_of_element->up = added_element;
+                    if (father_of_element->right != NULL)
+		        father_of_element->right->up = father_of_element;
+		}
+	    }
+        }
+        while (father_of_element->up != NULL)
+	{
+            father_of_element = father_of_element->up;
+        }
+        tree->top = father_of_element;
+    }
+}
+
+
+
+PUBLIC HTBTElement * HTBTree_next ARGS2(
+                               HTBTree*,       tree,
+                               HTBTElement*,   ele)
+    /**************************************************************************
+    ** this function returns a pointer to the leftmost element if ele is NULL,
+    ** and to the next object to the right otherways.
+    ** If no elements left, returns a pointer to NULL.
+    */
+{
+    HTBTElement * father_of_element;
+    HTBTElement * father_of_forefather;
+
+    if (ele == NULL)
+    {
+        father_of_element = tree->top;
+        if (father_of_element != NULL)
+            while (father_of_element->left != NULL)
+                father_of_element = father_of_element->left;
+    }
+    else
+    {
+        father_of_element = ele;
+        if (father_of_element->right != NULL)
+	{
+            father_of_element = father_of_element->right;
+            while (father_of_element->left != NULL)
+                father_of_element = father_of_element->left;
+	}
+        else
+	{
+            father_of_forefather = father_of_element->up;
+	        while (father_of_forefather && 
+		       (father_of_forefather->right == father_of_element))
+      	        {
+                    father_of_element = father_of_forefather;
+		    father_of_forefather = father_of_element->up;
+		}
+            father_of_element = father_of_forefather;
+	}
+    }
+#ifdef BTREE_TRACE
+    /* The option -DBTREE_TRACE will give much more information
+    ** about the way the process is running, for debugging matters
+    */
+    if (father_of_element != NULL)
+    {
+        printf("\nObject = %s\t",(char *)father_of_element->object);
+        if (father_of_element->up != NULL)
+            printf("Objet du pere = %s\n",
+		   (char *)father_of_element->up->object);
+        else printf("Pas de Pere\n");
+        if (father_of_element->left != NULL)
+            printf("Objet du fils gauche = %s\t",
+		   (char *)father_of_element->left->object); 
+        else printf("Pas de fils gauche\t");
+        if (father_of_element->right != NULL)
+            printf("Objet du fils droit = %s\n",
+		   (char *)father_of_element->right->object);
+        else printf("Pas de fils droit\n");
+        printf("Profondeur gauche = %i\t",father_of_element->left_depth);
+        printf("Profondeur droite = %i\n",father_of_element->right_depth);
+        printf("      **************\n");
+    }
+#endif
+    return father_of_element;
+}
+
+
+#ifdef TEST
+main ()
+    /******************************************************
+    ** This is just a test to show how to handle HTBTree.c
+    */
+{
+    HTBTree * tree;
+    HTBTElement * next_element;
+    
+    tree = HTBTree_new((HTComparer)strcasecomp);
+    HTBTree_add(tree,"hypertext");
+    HTBTree_add(tree,"Addressing");
+    HTBTree_add(tree,"X11");
+    HTBTree_add(tree,"Tools");
+    HTBTree_add(tree,"Proposal.wn");
+    HTBTree_add(tree,"Protocols");
+    HTBTree_add(tree,"NeXT");
+    HTBTree_add(tree,"Daemon");
+    HTBTree_add(tree,"Test");
+    HTBTree_add(tree,"Administration");
+    HTBTree_add(tree,"LineMode");
+    HTBTree_add(tree,"DesignIssues");
+    HTBTree_add(tree,"MarkUp");
+    HTBTree_add(tree,"Macintosh");
+    HTBTree_add(tree,"Proposal.rtf.wn");
+    HTBTree_add(tree,"FIND");
+    HTBTree_add(tree,"Paper");
+    HTBTree_add(tree,"Tcl");
+    HTBTree_add(tree,"Talks");
+    HTBTree_add(tree,"Architecture");
+    HTBTree_add(tree,"VMSHelp");
+    HTBTree_add(tree,"Provider");
+    HTBTree_add(tree,"Archive");
+    HTBTree_add(tree,"SLAC");
+    HTBTree_add(tree,"Project");
+    HTBTree_add(tree,"News");
+    HTBTree_add(tree,"Viola");
+    HTBTree_add(tree,"Users");
+    HTBTree_add(tree,"FAQ");
+    HTBTree_add(tree,"WorkingNotes");
+    HTBTree_add(tree,"Windows");
+    HTBTree_add(tree,"FineWWW");
+    HTBTree_add(tree,"Frame");
+    HTBTree_add(tree,"XMosaic");
+    HTBTree_add(tree,"People");
+    HTBTree_add(tree,"All");
+    HTBTree_add(tree,"Curses");
+    HTBTree_add(tree,"Erwise");
+    HTBTree_add(tree,"Carl");
+    HTBTree_add(tree,"MidasWWW");
+    HTBTree_add(tree,"XPM");
+    HTBTree_add(tree,"MailRobot");
+    HTBTree_add(tree,"Illustrations");
+    HTBTree_add(tree,"VMClient");
+    HTBTree_add(tree,"XPA");
+    HTBTree_add(tree,"Clients.html");
+    HTBTree_add(tree,"Library");
+    HTBTree_add(tree,"CERNLIB_Distribution");
+    HTBTree_add(tree,"libHTML");
+    HTBTree_add(tree,"WindowsPC");
+    HTBTree_add(tree,"tkWWW");
+    HTBTree_add(tree,"tk2.3");
+    HTBTree_add(tree,"CVS-RCS");
+    HTBTree_add(tree,"DecnetSockets");
+    HTBTree_add(tree,"SGMLStream");
+    HTBTree_add(tree,"NextStep");
+    HTBTree_add(tree,"CVSRepository_old");
+    HTBTree_add(tree,"ArthurSecret");
+    HTBTree_add(tree,"CVSROOT");
+    HTBTree_add(tree,"HytelnetGate");
+    HTBTree_add(tree,"cern.www.new.src");
+    HTBTree_add(tree,"Conditions");
+    HTBTree_add(tree,"HTMLGate");
+    HTBTree_add(tree,"Makefile");
+    HTBTree_add(tree,"Newsgroups.html");
+    HTBTree_add(tree,"People.html");
+    HTBTree_add(tree,"Bugs.html");
+    HTBTree_add(tree,"Summary.html");
+    HTBTree_add(tree,"zDesignIssues.wn");
+    HTBTree_add(tree,"HT.draw");
+    HTBTree_add(tree,"HTandCERN.wn");
+    HTBTree_add(tree,"Ideas.wn");
+    HTBTree_add(tree,"MarkUp.wn");
+    HTBTree_add(tree,"Proposal.html");
+    HTBTree_add(tree,"SearchPanel.draw");
+    HTBTree_add(tree,"Comments.wn");
+    HTBTree_add(tree,"Xanadu.html");
+    HTBTree_add(tree,"Storinglinks.html");
+    HTBTree_add(tree,"TheW3Book.html");
+    HTBTree_add(tree,"Talk_Feb-91.html");
+    HTBTree_add(tree,"JFosterEntry.txt");
+    HTBTree_add(tree,"Summary.txt");
+    HTBTree_add(tree,"Bibliography.html");
+    HTBTree_add(tree,"HTandCern.txt");
+    HTBTree_add(tree,"Talk.draw");
+    HTBTree_add(tree,"zDesignNotes.html");
+    HTBTree_add(tree,"Link.html");
+    HTBTree_add(tree,"Status.html");
+    HTBTree_add(tree,"http.txt");
+    HTBTree_add(tree,"People.html~");
+    HTBTree_add(tree,"TAGS");
+    HTBTree_add(tree,"summary.txt");
+    HTBTree_add(tree,"Technical.html");
+    HTBTree_add(tree,"Terms.html");
+    HTBTree_add(tree,"JANETAccess.html");
+    HTBTree_add(tree,"People.txt");
+    HTBTree_add(tree,"README.txt");
+    HTBTree_add(tree,"CodingStandards.html");
+    HTBTree_add(tree,"Copyright.txt");
+    HTBTree_add(tree,"Status_old.html");
+    HTBTree_add(tree,"patches~");
+    HTBTree_add(tree,"RelatedProducts.html");
+    HTBTree_add(tree,"Implementation");
+    HTBTree_add(tree,"History.html");
+    HTBTree_add(tree,"Makefile.bak");
+    HTBTree_add(tree,"Makefile.old");
+    HTBTree_add(tree,"Policy.html");
+    HTBTree_add(tree,"WhatIs.html");
+    HTBTree_add(tree,"TheProject.html");
+    HTBTree_add(tree,"Notation.html");
+    HTBTree_add(tree,"Helping.html");
+    HTBTree_add(tree,"Cyber-WWW.sit.Hqx");
+    HTBTree_add(tree,"Glossary.html");
+    HTBTree_add(tree,"maketags.html");
+    HTBTree_add(tree,"IntroCS.html");
+    HTBTree_add(tree,"Contrib");
+    HTBTree_add(tree,"Help.html");
+    HTBTree_add(tree,"CodeManagExec");
+    HTBTree_add(tree,"HT-0.1draz");
+    HTBTree_add(tree,"Cello");
+    HTBTree_add(tree,"TOPUB");
+    HTBTree_add(tree,"BUILD");
+    HTBTree_add(tree,"BUILDALL");
+    HTBTree_add(tree,"Lynx");
+    HTBTree_add(tree,"ArthurLibrary");
+    HTBTree_add(tree,"RashtyClient");
+    HTBTree_add(tree,"#History.html#");
+    HTBTree_add(tree,"PerlServers");
+    HTBTree_add(tree,"modules");
+    HTBTree_add(tree,"NCSA_httpd");
+    HTBTree_add(tree,"MAIL2HTML");
+    HTBTree_add(tree,"core");
+    HTBTree_add(tree,"EmacsWWW");
+#ifdef BTREE_TRACE
+    printf("\nTreeTopObject=%s\n\n",tree->top->object);
+#endif
+    next_element = HTBTree_next(tree,NULL);
+    while (next_element != NULL)
+    {
+#ifndef BTREE_TRACE
+        printf("The next element is %s\n",next_element->object);
+#endif
+        next_element = HTBTree_next(tree,next_element);
+    }
+    HTBTree_free(tree);
+}
+
+
+#endif
diff --git a/WWW/Library/Implementation/HTBTree.h b/WWW/Library/Implementation/HTBTree.h
new file mode 100644
index 00000000..55bfe6ab
--- /dev/null
+++ b/WWW/Library/Implementation/HTBTree.h
@@ -0,0 +1,104 @@
+/*                  /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTBTree.html
+                         BALANCED BINARY TREE FOR SORTING THINGS
+                                             
+   Tree creation, traversal and freeing.  User-supplied comparison routine.
+   
+   Author: Arthur Secret, CERN. Public domain.  Please mail bugs and changes to
+   www-request@info.cern.ch
+   
+   part of libWWW
+   
+ */
+#ifdef SHORT_NAMES
+#define HTBTree_new             HTBTNew
+#define HTBTree_free            HTBTFree
+#define HTBTreeAndObject_free   HTBTAOFr
+#define HTBTree_add             HTBTAdd
+#define HTBTree_next            HTBTNext
+/* #define      HTBTree_object          HTBTObje already a macro */
+#endif
+
+
+/*
+
+Data structures
+
+ */
+typedef struct _HTBTree_element {
+    void                        *object;        /* User object */
+    struct _HTBTree_element     *up;
+    struct _HTBTree_element     *left;
+    int                         left_depth;
+    struct _HTBTree_element     *right;
+    int                         right_depth;
+} HTBTElement;
+
+typedef int (*HTComparer) PARAMS((void * a, void * b));
+
+typedef struct _HTBTree_top {
+    HTComparer                  compare;
+    struct _HTBTree_element     *top;
+} HTBTree;
+
+
+/*
+
+Create a binary tree given its discrimination routine
+
+ */
+extern HTBTree * HTBTree_new PARAMS((HTComparer comp));
+
+
+
+/*
+
+Free storage of the tree but not of the objects
+
+ */
+extern void HTBTree_free PARAMS((HTBTree* tree));
+
+
+
+/*
+
+Free storage of the tree and of the objects
+
+ */
+extern void HTBTreeAndObject_free PARAMS((HTBTree* tree));
+
+
+
+/*
+
+Add an object to a binary tree
+
+ */
+
+extern void HTBTree_add PARAMS((HTBTree* tree, void * object));
+
+
+/*
+
+Find user object for element
+
+ */
+#define HTBTree_object(element)  ((element)->object)
+
+
+/*
+
+Find next element in depth-first order
+
+  ON ENTRY,
+  
+  ele                    if NULL, start with leftmost element. if != 0 give next object to
+                         the right.
+                         
+  returns                Pointer to element ot NULL if none left.
+                         
+ */
+extern HTBTElement * HTBTree_next PARAMS((HTBTree* tree, HTBTElement * ele));
+
+/*
+
+   end  */
diff --git a/WWW/Library/Implementation/HTCJK.h b/WWW/Library/Implementation/HTCJK.h
new file mode 100644
index 00000000..8944cd42
--- /dev/null
+++ b/WWW/Library/Implementation/HTCJK.h
@@ -0,0 +1,110 @@
+/*			CJK character converter		HTCJK.h
+**			=======================
+**
+**	Added 11-Jun-96 by FM, based on jiscode.h for
+**	  Yutaka Sato's (ysato@etl.go.jp) SJIS.c, and
+**	  Takuya ASADA's (asada@three-a.co.jp) CJK patches.
+**	  (see SGML.c).
+**
+*/
+
+#ifndef HTCJK_H
+#define HTCJK_H
+
+/*
+**	STATUS CHANGE CODES
+*/
+#ifdef ESC
+#undef ESC
+#endif /* ESC */
+#define ESC		'\033'
+#define TO_2BCODE	'$'
+#define TO_1BCODE	'('
+
+#define TO_KANA		'\016'
+#define TO_KANAOUT	'\017'
+
+#define TO_KANJI	"\033$B"
+#define TO_HANJI	"\033$A"
+#define TO_HANGUL	"\033$(C"
+#define TO_ASCII	"\033(B"
+
+#define IS_SJIS_LO(lo)	((0x40<=lo)&&(lo!=0x7F)&&(lo<=0xFC))
+#define IS_SJIS_HI1(hi) ((0x81<=hi)&&(hi<=0x9F))	/* 1st lev. */
+#define IS_SJIS_HI2(hi) ((0xE0<=hi)&&(hi<=0xEF))	/* 2nd lev. */
+#define IS_SJIS(hi,lo,in_sjis) (!IS_SJIS_LO(lo)?0:IS_SJIS_HI1(hi)?(in_sjis=1):in_sjis&&IS_SJIS_HI2(hi))
+
+#define IS_EUC_LOS(lo)	((0x21<=lo)&&(lo<=0x7E))	/* standard */
+#define IS_EUC_LOX(lo)	((0xA1<=lo)&&(lo<=0xFE))	/* extended */
+#define IS_EUC_HI(hi)	((0xA1<=hi)&&(hi<=0xFE))
+#define IS_EUC(hi,lo) IS_EUC_HI(hi) && (IS_EUC_LOS(lo) || IS_EUC_LOX(lo))
+
+#define IS_BIG5_LOS(lo)	((0x40<=lo)&&(lo<=0x7E))	/* standard */
+#define IS_BIG5_LOX(lo)	((0xA1<=lo)&&(lo<=0xFE))	/* extended */
+#define IS_BIG5_HI(hi)	((0xA1<=hi)&&(hi<=0xFE))
+#define IS_BIG5(hi,lo) IS_BIG5_HI(hi) && (IS_BIG5_LOS(lo) || IS_BIG5_LOX(lo))
+
+typedef enum _HTkcode {NOKANJI, EUC, SJIS, JIS} HTkcode;
+typedef enum _HTCJKlang {NOCJK, JAPANESE, CHINESE, KOREAN, TAIPEI} HTCJKlang;
+
+/*
+**  Function prototypes.
+*/
+extern void JISx0201TO0208_EUC PARAMS((
+	register unsigned char		IHI,
+	register unsigned char		ILO,
+	register unsigned char *	OHI,
+	register unsigned char *	OLO));
+
+extern unsigned char * SJIS_TO_JIS1 PARAMS((
+	register unsigned char		HI,
+	register unsigned char		LO,
+	register unsigned char *	JCODE));
+
+extern unsigned char * JIS_TO_SJIS1 PARAMS((
+	register unsigned char		HI,
+	register unsigned char		LO,
+	register unsigned char *	SJCODE));
+
+extern unsigned char * EUC_TO_SJIS1 PARAMS((
+	unsigned char			HI,
+	unsigned char			LO,
+	register unsigned char *	SJCODE));
+
+extern void JISx0201TO0208_SJIS PARAMS((
+	register unsigned char		I,
+	register unsigned char *	OHI,
+	register unsigned char *	OLO));
+
+extern unsigned char * SJIS_TO_EUC1 PARAMS((
+	unsigned char		HI,
+	unsigned char		LO,
+	unsigned char *		EUC));
+
+extern unsigned char * SJIS_TO_EUC PARAMS((
+	unsigned char *		src,
+	unsigned char *		dst));
+
+extern unsigned char * EUC_TO_SJIS PARAMS((
+	unsigned char *		src,
+	unsigned char *		dst));
+
+extern unsigned char * EUC_TO_JIS PARAMS((
+	unsigned char *		src,
+	unsigned char *		dst,
+	CONST char *		toK,
+	CONST char *		toA));
+
+extern unsigned char * TO_EUC PARAMS((
+	unsigned char *		jis,
+	unsigned char *		euc));
+
+extern void TO_SJIS PARAMS((
+	unsigned char *		any,
+	unsigned char *		sjis));
+
+extern void TO_JIS PARAMS((
+	unsigned char *		any,
+	unsigned char *		jis));
+
+#endif /* HTCJK_H */
diff --git a/WWW/Library/Implementation/HTChunk.c b/WWW/Library/Implementation/HTChunk.c
new file mode 100644
index 00000000..fc46ac4a
--- /dev/null
+++ b/WWW/Library/Implementation/HTChunk.c
@@ -0,0 +1,100 @@
+/*		Chunk handling:	Flexible arrays
+**		===============================
+**
+*/
+
+#include "HTUtils.h"
+#include "HTChunk.h"
+/*#include <stdio.h> included by HTUtils.h -- FM */
+
+#include "LYLeaks.h"
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+/*	Create a chunk with a certain allocation unit
+**	--------------
+*/
+PUBLIC HTChunk * HTChunkCreate ARGS1 (int,grow)
+{
+    HTChunk * ch = (HTChunk *) calloc(1, sizeof(HTChunk));
+    if (ch == NULL)
+        outofmem(__FILE__, "creation of chunk");
+
+    ch->data = 0;
+    ch->growby = grow;
+    ch->size = 0;
+    ch->allocated = 0;
+    return ch;
+}
+
+
+/*	Clear a chunk of all data
+**	--------------------------
+*/
+PUBLIC void HTChunkClear ARGS1 (HTChunk *,ch)
+{
+    FREE(ch->data);
+    ch->size = 0;
+    ch->allocated = 0;
+}
+
+
+/*	Free a chunk
+**	------------
+*/
+PUBLIC void HTChunkFree ARGS1 (HTChunk *,ch)
+{
+    FREE(ch->data);
+    FREE(ch);
+}
+
+
+/*	Append a character
+**	------------------
+*/
+PUBLIC void HTChunkPutc ARGS2 (HTChunk *,ch, char,c)
+{
+    if (ch->size >= ch->allocated) {
+	ch->allocated = ch->allocated + ch->growby;
+        ch->data = ch->data ? (char *)realloc(ch->data, ch->allocated)
+			    : (char *)calloc(1, ch->allocated);
+      if (!ch->data)
+          outofmem(__FILE__, "HTChunkPutc");
+    }
+    ch->data[ch->size++] = c;
+}
+
+
+/*	Ensure a certain size
+**	---------------------
+*/
+PUBLIC void HTChunkEnsure ARGS2 (HTChunk *,ch, int,needed)
+{
+    if (needed <= ch->allocated) return;
+    ch->allocated = needed-1 - ((needed-1) % ch->growby)
+    			     + ch->growby; /* Round up */
+    ch->data = ch->data ? (char *)realloc(ch->data, ch->allocated)
+			: (char *)calloc(1, ch->allocated);
+    if (ch->data == NULL)
+        outofmem(__FILE__, "HTChunkEnsure");
+}
+
+
+/*	Terminate a chunk
+**	-----------------
+*/
+PUBLIC void HTChunkTerminate ARGS1 (HTChunk *,ch)
+{
+    HTChunkPutc(ch, (char)0);
+}
+
+
+/*	Append a string
+**	---------------
+*/
+PUBLIC void HTChunkPuts ARGS2 (HTChunk *,ch, CONST char *,s)
+{
+    CONST char * p;
+    for (p=s; *p; p++)
+        HTChunkPutc(ch, *p);
+}
diff --git a/WWW/Library/Implementation/HTChunk.h b/WWW/Library/Implementation/HTChunk.h
new file mode 100644
index 00000000..260f798a
--- /dev/null
+++ b/WWW/Library/Implementation/HTChunk.h
@@ -0,0 +1,160 @@
+/*                                                HTChunk: Flexible array handling for libwww
+                                     CHUNK HANDLING:
+                                     FLEXIBLE ARRAYS
+                                             
+   This module implements a flexible array. It is a general utility module. A chunk is a
+   structure which may be extended.  These routines create and append data to chunks,
+   automatically reallocating them as necessary.
+   
+ */
+typedef struct {
+        int     size;           /* In bytes                     */
+        int     growby;         /* Allocation unit in bytes     */
+        int     allocated;      /* Current size of *data        */
+        char *  data;           /* Pointer to malloced area or 0 */
+} HTChunk;
+
+
+#ifdef SHORT_NAMES
+#define HTChunkClear            HTChClea
+#define HTChunkPutc             HTChPutc
+#define HTChunkPuts             HTChPuts
+#define HTChunkCreate           HTChCrea
+#define HTChunkTerminate        HTChTerm
+#define HTChunkEnsure           HtChEnsu
+#endif
+
+
+/*
+
+Create new chunk
+
+  ON ENTRY,
+  
+  growby                  The number of bytes to allocate at a time when the chunk is
+                         later extended. Arbitrary but normally a trade-off time vs.
+                         memory
+                         
+  ON EXIT,
+  
+  returns                 A chunk pointer to the new chunk,
+                         
+ */
+
+extern HTChunk * HTChunkCreate PARAMS((int growby));
+
+
+/*
+
+Free a chunk
+
+  ON ENTRY,
+  
+  ch                      A valid chunk pointer made by HTChunkCreate()
+                         
+  ON EXIT,
+  
+  ch                      is invalid and may not be used.
+                         
+ */
+
+extern void HTChunkFree PARAMS((HTChunk * ch));
+
+
+/*
+
+Clear a chunk
+
+  ON ENTRY,
+  
+  ch                      A valid chunk pointer made by HTChunkCreate()
+                         
+  ON EXIT,
+  
+  *ch                     The size of the chunk is zero.
+                         
+ */
+
+extern void HTChunkClear PARAMS((HTChunk * ch));
+
+
+/*
+
+Ensure a chunk has a certain space in
+
+  ON ENTRY,
+  
+  ch                      A valid chunk pointer made by HTChunkCreate()
+                         
+  s                       The size required
+                         
+  ON EXIT,
+  
+  *ch                     Has size at least s
+                         
+ */
+
+extern void HTChunkEnsure PARAMS((HTChunk * ch, int s));
+
+
+/*
+
+Append a character to a  chunk
+
+  ON ENTRY,
+  
+  ch                      A valid chunk pointer made by HTChunkCreate()
+                         
+  c                       The character to be appended
+                         
+  ON EXIT,
+  
+  *ch                     Is one character bigger
+                         
+ */
+extern void HTChunkPutc PARAMS((HTChunk * ch, char c));
+
+/*
+
+Append a string to a  chunk
+
+  ON ENTRY,
+  
+  ch                      A valid chunk pointer made by HTChunkCreate()
+                         
+  str                     Tpoints to a zero-terminated string to be appended
+                         
+  ON EXIT,
+  
+  *ch                     Is bigger by strlen(str)
+                         
+ */
+
+
+extern void HTChunkPuts PARAMS((HTChunk * ch, const char *str));
+
+
+/*
+
+Append a zero character to a  chunk
+
+ */
+
+/*
+
+  ON ENTRY,
+  
+  ch                      A valid chunk pointer made by HTChunkCreate()
+                         
+  ON EXIT,
+  
+  *ch                     Is one character bigger
+                         
+ */
+
+
+extern void HTChunkTerminate PARAMS((HTChunk * ch));
+
+/*
+
+   end */
diff --git a/WWW/Library/Implementation/HTFTP.c b/WWW/Library/Implementation/HTFTP.c
new file mode 100644
index 00000000..9d19610f
--- /dev/null
+++ b/WWW/Library/Implementation/HTFTP.c
@@ -0,0 +1,3214 @@
+/*			File Transfer Protocol (FTP) Client
+**			for a WorldWideWeb browser
+**			===================================
+**
+**	A cache of control connections is kept.
+**
+** Note: Port allocation
+**
+**	It is essential that the port is allocated by the system, rather
+**	than chosen in rotation by us (POLL_PORTS), or the following
+**	problem occurs.
+**
+**	It seems that an attempt by the server to connect to a port which has
+**	been used recently by a listen on the same socket, or by another
+**	socket this or another process causes a hangup of (almost exactly)
+**	one minute. Therefore, we have to use a rotating port number.
+**	The problem remains that if the application is run twice in quick
+**	succession, it will hang for what remains of a minute.
+**
+** Authors
+**	TBL	Tim Berners-lee <timbl@info.cern.ch>
+**	DD	Denis DeLaRoca 310 825-4580 <CSP1DWD@mvs.oac.ucla.edu>
+**      LM      Lou Montulli <montulli@ukanaix.cc.ukans.edu>
+**      FM      Foteos Macrides <macrides@sci.wfeb.edu>
+** History:
+**	 2 May 91	Written TBL, as a part of the WorldWideWeb project.
+**	15 Jan 92	Bug fix: close() was used for NETCLOSE for control soc
+**	10 Feb 92	Retry if cached connection times out or breaks
+**	 8 Dec 92	Bug fix 921208 TBL after DD
+**	17 Dec 92	Anon FTP password now just WWWuser@ suggested by DD
+**			fails on princeton.edu!
+**	27 Dec 93 (FM)  Fixed up so FTP now works with VMS hosts.  Path
+**			must be Unix-style and cannot include the device
+**			or top directory.
+**      ?? ??? ?? (LM)  Added code to prompt and send passwords for non
+**			anonymous FTP
+**      25 Mar 94 (LM)  Added code to recognize different ftp server types
+**                      and code to parse dates and sizes on most hosts.
+**	27 Mar 93 (FM)  Added code for getting dates and sizes on VMS hosts.
+**
+** Options:
+**	LISTEN		We listen, the other guy connects for data.
+**			Otherwise, other way round, but problem finding our
+**			internet address!
+**
+** Notes:
+**     			Portions Copyright 1994 Trustees of Dartmouth College
+** 			Code for recognizing different FTP servers and
+**			parsing "ls -l" output taken from Macintosh Fetch
+**			program with permission from Jim Matthews,
+**			Dartmouth Software Development Team.
+*/
+
+/*
+** If LISTEN is not defined, PASV is used instead of PORT, and not
+** all FTP servers support PASV, so define it unless there is no
+** alternative for your system.
+*/ 
+#ifndef NOPORT
+#define LISTEN	 /* @@@@ Test LJM */
+#endif /* !NOPORT */
+
+/*
+BUGS:	@@@  	Limit connection cache size!
+		Error reporting to user.
+		400 & 500 errors are acked by user with windows.
+		Use configuration file for user names
+		
+**		Note for portablility this version does not use select() and
+**		so does not watch the control and data channels at the
+**		same time.
+*/		
+
+#include "HTUtils.h"
+#include "tcp.h"
+
+#include "HTAlert.h"
+
+#include "HTFTP.h"	/* Implemented here */
+
+/* this define should be in HTFont.h :( */
+#define HT_NON_BREAK_SPACE ((char)1)   /* For now */
+
+#define REPEAT_PORT	/* Give the port number for each file */
+#define REPEAT_LISTEN	/* Close each listen socket and open a new one */
+
+/* define POLL_PORTS		 If allocation does not work, poll ourselves.*/
+#define LISTEN_BACKLOG 2	/* Number of pending connect requests (TCP)*/
+
+#define FIRST_TCP_PORT	1024	/* Region to try for a listening port */
+#define LAST_TCP_PORT	5999	
+
+#define LINE_LENGTH 256
+#define COMMAND_LENGTH 256
+
+#define INFINITY 512
+
+#include "HTParse.h"
+#include "HTTCP.h"
+#include "HTAnchor.h"
+#include "HTFile.h"	/* For HTFileFormat() */
+#include "HTBTree.h"
+#include "HTChunk.h"
+#include "HTAlert.h"
+#ifndef IPPORT_FTP
+#define IPPORT_FTP	21
+#endif /* !IPORT_FTP */
+
+#include "LYLeaks.h"
+
+#ifdef REMOVED_CODE
+extern char *malloc();
+extern void free();
+extern char *strncpy();
+#endif /* REMOVED_CODE */
+
+typedef struct _connection {
+    struct _connection *	next;	/* Link on list 	*/
+    u_long			addr;	/* IP address		*/
+    int				socket;	/* Socket number for communication */
+    BOOL			binary; /* Binary mode? */
+} connection;
+
+#ifndef NIL
+#define NIL 0
+#endif /* !NIL */
+
+/*		Hypertext object building machinery
+*/
+#include "HTML.h"
+
+#define PUTC(c) (*targetClass.put_character)(target, c)
+#define PUTS(s) (*targetClass.put_string)(target, s)
+#define START(e) (*targetClass.start_element)(target, e, 0, 0, 0)
+#define END(e) (*targetClass.end_element)(target, e, 0)
+#define FREE_TARGET (*targetClass._free)(target)
+#define ABORT_TARGET (*targetClass._free)(target)
+struct _HTStructured {
+	CONST HTStructuredClass *	isa;
+	/* ... */
+};
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+extern int HTCheckForInterrupt NOPARAMS;
+
+
+/*	Global Variables
+**	---------------------
+*/
+PUBLIC BOOLEAN HTfileSortMethod = FILE_BY_NAME;
+PRIVATE char ThisYear[8];
+PRIVATE char LastYear[8];
+PRIVATE int TheDate;
+PRIVATE BOOLEAN HaveYears = FALSE; 
+#ifdef SOCKS
+extern BOOLEAN socks_flag;
+extern unsigned long socks_bind_remoteAddr;
+#endif /* SOCKS */
+extern char *personal_mail_address;
+
+/*	Module-Wide Variables
+**	---------------------
+*/
+PRIVATE connection * connections = 0;	/* Linked list of connections */
+PRIVATE char response_text[LINE_LENGTH+1];/* Last response from NewsHost */
+PRIVATE connection * control;		/* Current connection */
+PRIVATE int data_soc = -1;		/* Socket for data transfer =invalid */
+
+#define GENERIC_SERVER	   0
+#define MACHTEN_SERVER 	   1
+#define UNIX_SERVER 	   2
+#define VMS_SERVER 	   3
+#define CMS_SERVER 	   4
+#define DCTS_SERVER    	   5
+#define TCPC_SERVER	   6
+#define PETER_LEWIS_SERVER 7
+#define NCSA_SERVER	   8
+#define WINDOWS_NT_SERVER  9
+#define MS_WINDOWS_SERVER 10
+#define MSDOS_SERVER      11
+
+PRIVATE int     server_type = GENERIC_SERVER;   /* the type of ftp host */
+PRIVATE int     unsure_type = FALSE;            /* sure about the type? */
+PRIVATE BOOLEAN use_list = FALSE;		/* use the LIST command? */
+
+PRIVATE interrupted_in_next_data_char = FALSE;
+
+#ifdef POLL_PORTS
+PRIVATE	unsigned short	port_number = FIRST_TCP_PORT;
+#endif /* POLL_PORTS */
+
+#ifdef LISTEN
+PRIVATE int     master_socket = -1;	/* Listening socket = invalid	*/
+PRIVATE char	port_command[255];	/* Command for setting the port */
+PRIVATE fd_set	open_sockets; 		/* Mask of active channels */
+PRIVATE int	num_sockets;  		/* Number of sockets to scan */
+#else
+PRIVATE	unsigned short	passive_port;	/* Port server specified for data */
+#endif /* LISTEN */
+
+
+#define NEXT_CHAR HTGetCharacter()	/* Use function in HTFormat.c */
+
+#define DATA_BUFFER_SIZE 2048
+PRIVATE char data_buffer[DATA_BUFFER_SIZE];		/* Input data buffer */
+PRIVATE char * data_read_pointer;
+PRIVATE char * data_write_pointer;
+#define NEXT_DATA_CHAR next_data_char()
+
+
+/* PUBLIC						HTMake_VMS_name()
+**		CONVERTS WWW name into a VMS name
+** ON ENTRY:
+**	nn		Node Name (optional)
+**	fn		WWW file name
+**
+** ON EXIT:
+**	returns 	vms file specification
+**
+** Bug:	Returns pointer to static -- non-reentrant
+*/
+PUBLIC char * HTMake_VMS_name ARGS2(
+	CONST char *,	nn, 
+	CONST char *,	fn)
+{
+
+/*	We try converting the filename into Files-11 syntax. That is, we assume
+**	first that the file is, like us, on a VMS node. We try remote
+**	(or local) DECnet access. Files-11, VMS, VAX and DECnet
+**	are trademarks of Digital Equipment Corporation. 
+**	The node is assumed to be local if the hostname WITHOUT DOMAIN
+**	matches the local one. @@@
+*/
+    static char vmsname[INFINITY];	/* returned */
+    char * filename = (char*)malloc(strlen(fn)+1);
+    char * nodename = (char*)malloc(strlen(nn)+2+1);	/* Copies to hack */
+    char *second;		/* 2nd slash */
+    char *last;			/* last slash */
+    
+    char * hostname = (char *)HTHostName();
+
+    if (!filename || !nodename)
+        outofmem(__FILE__, "HTVMSname");
+    strcpy(filename, fn);
+    strcpy(nodename, "");	/* On same node? Yes if node names match */
+    if (strncmp(nn, "localhost", 9)) {
+        char *p, *q;
+        for (p = hostname, q = (char *)nn;
+	     *p && *p != '.' && *q && *q != '.'; p++, q++){
+	    if (TOUPPER(*p) != TOUPPER(*q)) {
+	        strcpy(nodename, nn);
+		q = strchr(nodename, '.');	/* Mismatch */
+		if (q)
+		    *q = '\0';			/* Chop domain */
+		strcat(nodename, "::");		/* Try decnet anyway */
+		break;
+	    }
+	}
+    }
+
+    second = strchr(filename+1, '/');		/* 2nd slash */
+    last = strrchr(filename, '/');	/* last slash */
+        
+    if (!second) {				/* Only one slash */
+	sprintf(vmsname, "%s%s", nodename, filename + 1);
+    } else if (second == last) {		/* Exactly two slashes */
+	*second = '\0';		/* Split filename from disk */
+	sprintf(vmsname, "%s%s:%s", nodename, filename+1, second+1);
+	*second = '/';	/* restore */
+    } else { 				/* More than two slashes */
+	char * p;
+	*second = '\0';		/* Split disk from directories */
+	*last = '\0';		/* Split dir from filename */
+	sprintf(vmsname, "%s%s:[%s]%s",
+		nodename, filename+1, second+1, last+1);
+	*second = *last = '/';	/* restore filename */
+	for (p = strchr(vmsname, '['); *p!=']'; p++)
+	    if (*p == '/')
+	        *p = '.';		/* Convert dir sep.  to dots */
+    }
+    FREE(nodename);
+    FREE(filename);
+    return vmsname;
+}
+
+/*	Procedure: Read a character from the data connection
+**	----------------------------------------------------
+*/
+PRIVATE char next_data_char NOARGS
+{
+    int status;
+    if (data_read_pointer >= data_write_pointer) {
+	status = NETREAD(data_soc, data_buffer, DATA_BUFFER_SIZE);
+      if (status == HT_INTERRUPTED)
+        interrupted_in_next_data_char = 1;
+      if (status <= 0)
+        return (char)-1;
+      data_write_pointer = data_buffer + status;
+      data_read_pointer = data_buffer;
+    }
+#ifdef NOT_ASCII
+    {
+        char c = *data_read_pointer++;
+	return FROMASCII(c);
+    }
+#else
+    return *data_read_pointer++;
+#endif /* NOT_ASCII */
+}
+
+
+/*	Close an individual connection
+**
+*/
+PRIVATE int close_connection ARGS1(
+	connection *,	con)
+{
+    connection * scan;
+    int status = NETCLOSE(con->socket);
+    if (TRACE)
+        fprintf(stderr, "HTFTP: Closing control socket %d\n", con->socket);
+    con->socket = -1;
+    if (connections == con) {
+        connections = con->next;
+	return status;
+    }
+    for (scan = connections; scan; scan = scan->next) {
+        if (scan->next == con) {
+	    scan->next = con->next;	/* Unlink */
+	    if (control == con)
+	        control = (connection*)0;
+	    return status;
+	} /*if */
+    } /* for */
+    return -1;		/* very strange -- was not on list. */
+}
+
+PRIVATE char *help_message_buffer = NULL;  /* global :( */
+
+PRIVATE void init_help_message_cache NOARGS
+{
+    FREE(help_message_buffer);
+}
+
+PRIVATE void help_message_cache_add ARGS1(
+	char *,		string)
+{
+    if (help_message_buffer)
+        StrAllocCat(help_message_buffer, string);
+    else	
+        StrAllocCopy(help_message_buffer, string);
+
+    if (TRACE)
+	fprintf(stderr,"Adding message to help cache: %s\n",string);
+}
+
+PRIVATE char *help_message_cache_non_empty NOARGS
+{
+  return(help_message_buffer);
+}
+PRIVATE char *help_message_cache_contents NOARGS
+{
+   return(help_message_buffer);
+}
+
+/*	Execute Command and get Response
+**	--------------------------------
+**
+**	See the state machine illustrated in RFC959, p57. This implements
+**	one command/reply sequence.  It also interprets lines which are to
+**	be continued, which are marked with a "-" immediately after the
+**	status code.
+**
+**	Continuation then goes on until a line with a matching reply code
+**	an a space after it.
+**
+** On entry,
+**	con	points to the connection which is established.
+**	cmd	points to a command, or is NIL to just get the response.
+**
+**	The command is terminated with the CRLF pair.
+**
+** On exit,
+**	returns:  The first digit of the reply type,
+**		  or negative for communication failure.
+*/
+PRIVATE int response ARGS1(
+	char *,		cmd)
+{
+    int result;				/* Three-digit decimal code */
+    int	continuation_response = -1;
+    int status;
+    extern int interrupted_in_htgetcharacter;
+    
+    if (!control) {
+          if (TRACE)
+	      fprintf(stderr, "HTFTP: No control connection set up!!\n");
+	  return -99;
+    }
+    
+    if (cmd) {
+	if (TRACE)
+	    fprintf(stderr, "  Tx: %s", cmd);
+#ifdef NOT_ASCII
+	{
+	    char * p;
+	    for (p = cmd; *p; p++) {
+	        *p = TOASCII(*p);
+	    }
+	}
+#endif /* NOT_ASCII */
+	status = NETWRITE(control->socket, cmd, (int)strlen(cmd));
+	if (status < 0) {
+	    if (TRACE)
+	        fprintf(stderr, 
+	    		"HTFTP: Error %d sending command: closing socket %d\n",
+			status, control->socket);
+	    close_connection(control);
+	    return status;
+	}
+    }
+
+    do {
+	char *p = response_text;
+	for (;;) {  
+	    if (((*p++ = NEXT_CHAR) == LF)
+			|| (p == &response_text[LINE_LENGTH])) {
+
+		char continuation;
+
+	        if (interrupted_in_htgetcharacter)
+                  {
+                    if (TRACE)
+                      fprintf (stderr,
+		        "HTFTP: Interrupted in HTGetCharacter, apparently.\n");
+                    NETCLOSE (control->socket);
+                    control->socket = -1;
+                    return HT_INTERRUPTED;
+                  }
+
+		*p = '\0';			/* Terminate the string */
+		if (TRACE)
+		    fprintf(stderr, "    Rx: %s", response_text);
+
+                /* Check for login or help messages */
+		if (!strncmp(response_text,"230-",4) ||
+		    !strncmp(response_text,"250-",4) ||
+		    !strncmp(response_text,"220-",4))
+		    help_message_cache_add(response_text+4);
+
+		sscanf(response_text, "%d%c", &result, &continuation);
+		if  (continuation_response == -1) {
+			if (continuation == '-')  /* start continuation */
+			    continuation_response = result;
+		} else { 	/* continuing */
+			if (continuation_response == result &&
+			    continuation == ' ')
+			    continuation_response = -1;	/* ended */
+		}	
+		break;	    
+	    } /* if end of line */
+	    
+	    if (interrupted_in_htgetcharacter)
+               {
+                    if (TRACE)
+                      fprintf (stderr,
+		        "HTFTP: Interrupted in HTGetCharacter, apparently.\n");
+                    NETCLOSE (control->socket);
+                    control->socket = -1;
+                    return HT_INTERRUPTED;
+               }
+
+	    if (*(p-1) == (char) EOF) {
+		if (TRACE)
+		    fprintf(stderr, "Error on rx: closing socket %d\n",
+		    	    control->socket);
+		strcpy(response_text, "000 *** TCP read error on response\n");
+	        close_connection(control);
+	    	return -1;	/* End of file on response */
+	    }
+	} /* Loop over characters */
+
+    } while (continuation_response != -1);
+    
+    if (result == 421) {
+	if (TRACE)
+	    fprintf(stderr, "HTFTP: They close so we close socket %d\n",
+	    	    control->socket);
+	close_connection(control);
+	return -1;
+    }
+    if ((result == 255 && server_type == CMS_SERVER) &&
+        (0 == strncasecomp(cmd, "CWD", 3) ||
+	 0 == strcasecomp(cmd, "CDUP"))) {
+        /*
+	**  Alas, CMS returns 255 on failure to CWD to parent of root. - PG
+	*/
+	result = 555;
+    }
+    return result/100;
+}
+
+/* this function should try to set the macintosh server into binary mode
+ */
+PRIVATE int set_mac_binary NOARGS
+{
+    /* try to set mac binary mode */
+    return(2 == response("MACB\r\n"));
+}
+
+/* This function gets the current working directory to help
+ * determine what kind of host it is
+ */
+
+PRIVATE void get_ftp_pwd ARGS2(
+	int *,		server_type,
+	BOOLEAN *,	use_list)
+{
+
+    char *cp;
+    /* get the working directory (to see what it looks like) */
+    int status = response("PWD\r\n");
+    if (status < 0) {
+        return;
+    } else {
+	cp = strchr(response_text+5,'"');
+	if (cp)
+	    *cp = '\0';
+        if (*server_type == TCPC_SERVER) {
+            *server_type = ((response_text[5] == '/') ?
+	    				  NCSA_SERVER : TCPC_SERVER);
+	     if (TRACE)
+	         fprintf(stderr, "HTFTP: Treating as %s server.\n",
+		 	 ((*server_type == NCSA_SERVER) ?
+			 			 "NCSA" : "TCPC"));
+        } else if (response_text[5] == '/') {
+            /* path names beginning with / imply Unix,
+	     * right? 
+	     */
+	    if (set_mac_binary()) {
+		*server_type = NCSA_SERVER;
+		if (TRACE)
+	            fprintf(stderr, "HTFTP: Treating as NCSA server.\n");
+	    } else {
+                 *server_type = UNIX_SERVER;
+                 *use_list = TRUE;
+		 if (TRACE)
+	             fprintf(stderr, "HTFTP: Treating as Unix server.\n");
+	    }
+	    return;
+        } else if (response_text[strlen(response_text)-1] == ']') {
+            /* path names ending with ] imply VMS, right? */
+            *server_type = VMS_SERVER;
+	    *use_list = TRUE;
+	    if (TRACE)
+	        fprintf(stderr, "HTFTP: Treating as VMS server.\n");
+        } else {
+            *server_type = GENERIC_SERVER;
+	    if (TRACE)
+	        fprintf(stderr, "HTFTP: Treating as Generic server.\n");
+	}
+
+        if ((*server_type == NCSA_SERVER) ||
+            (*server_type == TCPC_SERVER) ||
+            (*server_type == PETER_LEWIS_SERVER))
+            set_mac_binary();
+    }
+}
+
+/*	Get a valid connection to the host
+**	----------------------------------
+**
+** On entry,
+**	arg	points to the name of the host in a hypertext address
+** On exit,
+**	returns	<0 if error
+**		socket number if success
+**
+**	This routine takes care of managing timed-out connections, and
+**	limiting the number of connections in use at any one time.
+**
+**	It ensures that all connections are logged in if they exist.
+**	It ensures they have the port number transferred.
+*/
+PRIVATE int get_connection ARGS1(
+	CONST char *,	arg)
+{
+    int status;
+    char * command;
+    connection * con;
+    char * username=NULL;
+    char * password=NULL;
+    static char *user_entered_password=NULL;
+    static char *last_username_and_host=NULL;
+
+    /*
+    **  Allocate and init control struct.
+    */
+    con = (connection *)calloc(1, sizeof(connection));
+    
+    if (!arg) return -1;		/* Bad if no name sepcified	*/
+    if (!*arg) return -1;		/* Bad if name had zero length	*/
+
+/* Get node name:
+*/
+    {
+	char *p1 = HTParse(arg, "", PARSE_HOST);
+	char *p2 = strrchr(p1, '@');	/* user? */
+	char * pw = NULL;
+
+	if (p2 != NULL) {
+	    username = p1;
+	    *p2 = '\0';			/* terminate */
+	    p1 = p2+1;			/* point to host */
+	    pw = strchr(username, ':');
+	    if (pw != NULL) {
+	        *pw++ = '\0';
+		password = HTUnEscape(pw);
+	    }
+	    if (*username)
+	        HTUnEscape(username);
+
+	    /* if the password doesn't exist then we are going to have
+	     * to ask the user for it.  The only problem is that we
+	     * don't want to ask for it every time, so we will store
+	     * away in a primitive fashion.
+	     */
+	    if (!password) {
+		char tmp[256];
+
+		sprintf(tmp, "%s@%s", username, p1);
+		/* if the user@host is not equal to the last time through
+		 * or user_entered_password has no data then we need
+		 * to ask the user for the password
+		 */
+		if (!last_username_and_host ||
+		    strcmp(tmp, last_username_and_host) ||
+		    !user_entered_password) {
+
+		    StrAllocCopy(last_username_and_host, tmp);
+		    sprintf(tmp, "Enter password for user %s@%s:",
+		    		  username, p1);
+		    FREE(user_entered_password);
+		    user_entered_password = (char *)HTPromptPassword(tmp);
+
+		} /* else we already know the password */
+		password = user_entered_password;
+	    }
+	}
+
+        if (!username)
+	    FREE(p1);
+    } /* scope of p1 */
+
+        
+  con->socket = -1;
+  status = HTDoConnect (arg, "FTP", IPPORT_FTP, (int *)&con->socket);
+   
+  if (status < 0)
+    {
+      if (TRACE)
+        {
+          if (status == HT_INTERRUPTED)
+            fprintf (stderr,
+                     "HTFTP: Interrupted on connect\n");
+          else
+            fprintf(stderr,
+                    "HTFTP: Unable to connect to remote host for `%s'.\n",
+                    arg);
+        }
+      if (status == HT_INTERRUPTED)
+        _HTProgress ("Connection interrupted.");
+      else
+        HTAlert("Unable to connect to FTP host.");
+      if (con->socket != -1)
+        {
+          NETCLOSE(con->socket);
+        }
+
+      FREE(username);
+      FREE(con);
+      return status;                    /* Bad return */
+    }
+
+    if (TRACE) 
+ 	fprintf(stderr, "FTP connected, socket %ld\n", (long)con);
+    control = con;		/* Current control connection */
+
+    /* Initialise buffering for control connection */
+    HTInitInput(control->socket);
+    init_help_message_cache();  /* Clear the login message buffer. */
+
+
+/*	Now we log in		Look up username, prompt for pw.
+*/
+  {
+    int status = response((char *)0);	/* Get greeting */
+
+    if (status == HT_INTERRUPTED)
+      {
+        if (TRACE)
+          fprintf (stderr,
+                   "HTFTP: Interrupted at beginning of login.\n");
+        _HTProgress ("Connection interrupted.");
+        NETCLOSE(control->socket);
+        control->socket = -1;
+        return HT_INTERRUPTED;
+      }
+    if (status == 2) {		/* Send username */
+	if (username && *username) {
+	    command = (char*)malloc(10+strlen(username)+2+1);
+	    if (command == NULL)
+	        outofmem(__FILE__, "get_connection");
+	    sprintf(command, "USER %s%c%c", username, CR, LF);
+	} else {
+	    command = (char*)malloc(24);
+	    if (command == NULL)
+	        outofmem(__FILE__, "get_connection");
+	    sprintf(command, "USER anonymous%c%c", CR, LF);
+        }
+	status = response(command);
+	FREE(command);
+        if (status == HT_INTERRUPTED)
+          {
+            if (TRACE)
+              fprintf (stderr,
+                       "HTFTP: Interrupted while sending username.\n");
+            _HTProgress ("Connection interrupted.");
+            NETCLOSE(control->socket);
+            control->socket = -1;
+            return HT_INTERRUPTED;
+          }
+    }
+    if (status == 3) {		/* Send password */
+	if (password) {
+	    /*
+	     * We have non-zero length password, so send it. - FM
+	     */
+	    command = (char*)malloc(10+strlen(password)+2+1);
+	    if (command == NULL)
+	        outofmem(__FILE__, "get_connection");
+	    sprintf(command, "PASS %s%c%c", password, CR, LF);
+	} else {
+	    /*
+	     * Create and send a mail address as the password. - FM
+	     */
+	    char *user = NULL;
+	    char *host = NULL;
+	    char * cp;
+
+	    if (personal_mail_address && *personal_mail_address) {
+	        /*
+		 * We have a non-zero length personal
+		 * mail address, so use that. - FM
+		 */
+	        StrAllocCopy(user, personal_mail_address);
+		if ((cp=strchr(user, '@')) != NULL) {
+		    *cp++ = '\0';
+		    host = cp;
+		} else {
+		    host = (char *)HTHostName();
+		}
+	    } else {
+	        /*
+		 * Use an environment variable and the host global. - FM
+		 */
+		if ((cp=getenv("USER")) != NULL)
+		    StrAllocCopy(user, cp);
+		else
+		    StrAllocCopy(user, "WWWuser");
+		host = (char *)HTHostName();
+	    }
+
+	    /*
+	     * If host is not fully qualified, suppress it
+	     * as ftp.uu.net prefers a blank to a bad name
+	     */
+	    if (!(host) || strchr(host, '.') == NULL)
+	    	host = "";
+
+	    command = (char*)malloc(10+strlen(user)+1+strlen(host)+2+1);
+	    if (command == NULL)
+	        outofmem(__FILE__, "get_connection");
+	    sprintf(command, "PASS %s@%s%c%c", user, host, CR, LF);
+	    FREE(user);
+        }
+	status = response(command);
+	FREE(command);
+        if (status == HT_INTERRUPTED)
+          {
+            if (TRACE)
+              fprintf (stderr,
+                       "HTFTP: Interrupted while sending password.\n");
+            _HTProgress ("Connection interrupted.");
+            NETCLOSE(control->socket);
+            control->socket = -1;
+            return HT_INTERRUPTED;
+          }
+    }
+    FREE(username);
+
+    if (status == 3) {
+        char temp[80];
+	sprintf(temp, "ACCT noaccount%c%c", CR, LF);
+	status = response(temp);
+	if (status == HT_INTERRUPTED)
+          {
+            if (TRACE)
+              fprintf (stderr,
+                       "HTFTP: Interrupted while sending password.\n");
+            _HTProgress ("Connection interrupted.");
+            NETCLOSE(control->socket);
+            control->socket = -1;
+            return HT_INTERRUPTED;
+          }
+
+    }
+    if (status != 2) {
+        if (TRACE)
+	    fprintf(stderr, "HTFTP: Login fail: %s", response_text);
+    	/* if (control->socket > 0) close_connection(control->socket); */
+        return -1;		/* Bad return */
+    }
+    if (TRACE) fprintf(stderr, "HTFTP: Logged in.\n");
+
+    /** Check for host type **/
+    server_type = GENERIC_SERVER;	/* reset */
+    use_list = FALSE; 			/* reset */
+    if ((status=response("SYST\r\n")) == 2) {
+        /* we got a line -- what kind of server are we talking to? */
+        if (strncmp(response_text+4,
+	 	    "UNIX Type: L8 MAC-OS MachTen", 28) == 0) {
+            server_type = MACHTEN_SERVER;
+	    use_list = TRUE;
+	    if (TRACE)
+	        fprintf(stderr, "HTFTP: Treating as MachTen server.\n");
+
+        } else if (strstr(response_text+4, "UNIX") != NULL ||
+		   strstr(response_text+4, "Unix") != NULL) {
+            server_type = UNIX_SERVER;
+	    use_list = TRUE;
+	    if (TRACE)
+	        fprintf(stderr, "HTFTP: Treating as Unix server.\n");
+
+        } else if (strstr(response_text+4, "MSDOS") != NULL) {
+            server_type = MSDOS_SERVER;
+	    use_list = TRUE;
+	    if (TRACE)
+	        fprintf(stderr, 
+	 	 	"HTFTP: Treating as MSDOS (Unix emulation) server.\n");
+
+        } else if (strncmp(response_text+4, "VMS", 3) == 0) {
+            server_type = VMS_SERVER;
+	    use_list = TRUE;
+	    if (TRACE)
+	        fprintf(stderr, "HTFTP: Treating as VMS server.\n");
+
+	} else if ((strncmp(response_text+4, "VM/CMS", 6) == 0) ||
+	 	   (strncmp(response_text+4, "VM ", 3) == 0)) {
+            server_type = CMS_SERVER;
+	    use_list = TRUE;
+	    if (TRACE)
+	        fprintf(stderr, "HTFTP: Treating as CMS server.\n");
+
+	} else if (strncmp(response_text+4, "DCTS", 4) == 0) {
+            server_type = DCTS_SERVER;
+	    if (TRACE)
+	        fprintf(stderr, "HTFTP: Treating as DCTS server.\n");
+
+	} else if (strstr(response_text+4, "MAC-OS TCP/Connect II") != NULL) {
+            server_type = TCPC_SERVER;
+	    if (TRACE)
+	        fprintf(stderr, "HTFTP: Looks like a TCPC server.\n");
+            get_ftp_pwd(&server_type, &use_list);
+	    unsure_type = TRUE;
+
+        } else if (strncmp(response_text+4, "MACOS Peter's Server", 20) == 0) {
+            server_type = PETER_LEWIS_SERVER;
+            use_list = TRUE;
+            set_mac_binary();
+	    if (TRACE)
+	        fprintf(stderr,
+	 	 	"HTFTP: Treating as Peter Lewis (MACOS) server.\n");
+
+        } else if (strncmp(response_text+4, "Windows_NT", 10) == 0) {
+	    server_type = WINDOWS_NT_SERVER;
+	    use_list = TRUE;
+	    if (TRACE)
+	        fprintf(stderr, "HTFTP: Treating as Window_NT server.\n");
+
+	} else if (strncmp(response_text+4, "MS Windows", 10) == 0) {
+	    server_type = MS_WINDOWS_SERVER;
+	    use_list = TRUE;
+	    if (TRACE)
+	        fprintf(stderr, "HTFTP: Treating as MS Windows server.\n");
+
+	} else  {
+	    server_type = GENERIC_SERVER;
+	    if (TRACE)
+	        fprintf(stderr, "HTFTP: Ugh!  A Generic server.\n");
+            get_ftp_pwd(&server_type, &use_list);
+	    unsure_type = TRUE;   
+	 }
+    } else {
+	/* SYST fails :(  try to get the type from the PWD command */
+         get_ftp_pwd(&server_type, &use_list);
+    }
+
+/*  Now we inform the server of the port number we will listen on
+*/
+#ifdef NOTREPEAT_PORT
+    {
+	int status = response(port_command);
+	if (status != 2) {
+	    if (control->socket)
+	        close_connection(control->socket);
+	    return -status;		/* Bad return */
+	}
+	if (TRACE)
+	    fprintf(stderr, "HTFTP: Port defined.\n");
+    }
+#endif /* NOTREPEAT_PORT */
+    return con->socket;			/* Good return */
+  } /* Scope of con */
+}
+
+
+#ifdef LISTEN
+
+/*	Close Master (listening) socket
+**	-------------------------------
+**
+**
+*/
+PRIVATE int close_master_socket NOARGS
+{
+    int status;
+    FD_CLR(master_socket, &open_sockets);
+    status = NETCLOSE(master_socket);
+    if (TRACE)
+        fprintf(stderr, "HTFTP: Closed master socket %d\n", master_socket);
+    master_socket = -1;
+    if (status < 0)
+        return HTInetStatus("close master socket");
+    else
+        return status;
+}
+
+
+/*	Open a master socket for listening on
+**	-------------------------------------
+**
+**	When data is transferred, we open a port, and wait for the server to
+**	connect with the data.
+**
+** On entry,
+**	master_socket	Must be negative if not set up already.
+** On exit,
+**	Returns		socket number if good
+**			less than zero if error.
+**	master_socket	is socket number if good, else negative.
+**	port_number	is valid if good.
+*/
+PRIVATE int get_listen_socket NOARGS
+{
+    struct sockaddr_in soc_address;	/* Binary network address */
+    struct sockaddr_in* sin = &soc_address;
+    int new_socket;			/* Will be master_socket */
+    
+    
+    FD_ZERO(&open_sockets);	/* Clear our record of open sockets */
+    num_sockets = 0;
+    
+#ifndef REPEAT_LISTEN
+    if (master_socket >= 0)
+        return master_socket;  /* Done already */
+#endif /* !REPEAT_LISTEN */
+
+/*  Create internet socket
+*/
+    new_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+	
+    if (new_socket < 0)
+	return HTInetStatus("socket for master socket");
+    
+    if (TRACE)
+        fprintf(stderr, "HTFTP: Opened master socket number %d\n", new_socket);
+    
+/*  Search for a free port.
+*/
+    sin->sin_family = AF_INET;	    /* Family = internet, host order  */
+    sin->sin_addr.s_addr = INADDR_ANY; /* Any peer address */
+#ifdef POLL_PORTS
+    {
+        unsigned short old_port_number = port_number;
+	for (port_number = (old_port_number+1); ; port_number++) { 
+	    int status;
+	    if (port_number > LAST_TCP_PORT)
+		port_number = FIRST_TCP_PORT;
+	    if (port_number == old_port_number) {
+		return HTInetStatus("bind");
+	    }
+	    soc_address.sin_port = htons(port_number);
+#ifdef SOCKS
+	    if (socks_flag)
+	        if ((status=Rbind(new_socket,
+			(struct sockaddr*)&soc_address,
+			    /* Cast to generic sockaddr */
+			sizeof(soc_address)
+#ifndef SHORTENED_RBIND
+			,socks_bind_remoteAddr
+#endif /* !SHORTENED_RBIND */
+						)) == 0)
+		    break;
+	    else
+#endif /* SOCKS */
+	    if ((status=bind(new_socket,
+		    (struct sockaddr*)&soc_address,
+			    /* Cast to generic sockaddr */
+		    sizeof(soc_address))) == 0)
+		break;
+	    if (TRACE)
+	        fprintf(stderr, 
+	    		"TCP bind attempt to port %d yields %d, errno=%d\n",
+		port_number, status, SOCKET_ERRNO);
+	} /* for */
+    }
+#else
+    {
+        int status;
+	int address_length = sizeof(soc_address);
+#ifdef SOCKS
+	if (socks_flag)
+	    status = Rgetsockname(control->socket,
+				  (struct sockaddr *)&soc_address,
+			 	  (void *)&address_length);
+	else
+#endif /* SOCKS */
+	status = getsockname(control->socket,
+			     (struct sockaddr *)&soc_address,
+			     (void *)&address_length);
+	if (status<0) return HTInetStatus("getsockname");
+	CTRACE(tfp, "HTFTP: This host is %s\n",
+	    HTInetString(sin));
+	
+	soc_address.sin_port = 0;	/* Unspecified: please allocate */
+#ifdef SOCKS
+	if (socks_flag)
+	    status=Rbind(new_socket,
+			 (struct sockaddr*)&soc_address,
+			 /* Cast to generic sockaddr */
+			 sizeof(soc_address)
+#ifndef SHORTENED_RBIND
+			,socks_bind_remoteAddr
+#endif /* !SHORTENED_RBIND */
+						);
+	else
+#endif /* SOCKS */
+	status=bind(new_socket,
+		    (struct sockaddr*)&soc_address,
+		    /* Cast to generic sockaddr */
+		    sizeof(soc_address));
+	if (status<0) return HTInetStatus("bind");
+	
+	address_length = sizeof(soc_address);
+#ifdef SOCKS
+	if (socks_flag)
+	    status = Rgetsockname(new_socket,
+				  (struct sockaddr*)&soc_address,
+				  (void *)&address_length);
+	else
+#endif /* SOCKS */
+	status = getsockname(new_socket,
+			     (struct sockaddr*)&soc_address,
+			     (void *)&address_length);
+	if (status<0) return HTInetStatus("getsockname");
+    }
+#endif /* POLL_PORTS */
+
+    CTRACE(tfp, "HTFTP: bound to port %d on %s\n",
+    		(int)ntohs(sin->sin_port),
+		HTInetString(sin));
+
+#ifdef REPEAT_LISTEN
+    if (master_socket >= 0)
+        (void) close_master_socket();
+#endif /* REPEAD_LISTEN */
+    
+    master_socket = new_socket;
+    
+/*	Now we must find out who we are to tell the other guy
+*/
+    (void)HTHostName(); 	/* Make address valid - doesn't work*/
+    sprintf(port_command, "PORT %d,%d,%d,%d,%d,%d%c%c",
+		    (int)*((unsigned char *)(&sin->sin_addr)+0),
+		    (int)*((unsigned char *)(&sin->sin_addr)+1),
+		    (int)*((unsigned char *)(&sin->sin_addr)+2),
+		    (int)*((unsigned char *)(&sin->sin_addr)+3),
+		    (int)*((unsigned char *)(&sin->sin_port)+0),
+		    (int)*((unsigned char *)(&sin->sin_port)+1),
+		    CR, LF);
+
+
+/*	Inform TCP that we will accept connections
+*/
+  {
+    int status;
+#ifdef SOCKS
+    if (socks_flag)
+        status = Rlisten(master_socket, 1);
+    else
+#endif /* SOCKS */
+    status = listen(master_socket, 1);
+    if (status < 0) {
+	master_socket = -1;
+	return HTInetStatus("listen");
+    }
+  }
+    CTRACE(tfp, "TCP: Master socket(), bind() and listen() all OK\n");
+    FD_SET(master_socket, &open_sockets);
+    if ((master_socket+1) > num_sockets)
+        num_sockets = master_socket+1;
+
+    return master_socket;		/* Good */
+
+} /* get_listen_socket */
+#endif /* LISTEN */
+
+PRIVATE char * months[12] = {
+    "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
+};
+
+/*	Procedure: Set the current and last year strings and date integer
+**	-----------------------------------------------------------------
+**
+**	Bug:
+**		This code is for sorting listings by date, if that option
+**		is selected in Lynx, and doesn't take into account time
+**		zones or ensure resetting at midnight, so the sort may not
+**		be perfect, but the actual date isn't changed in the display,
+**		i.e., the date is still correct. - FM
+*/
+PRIVATE void set_years_and_date NOARGS
+{
+    char day[8], month[8], date[12];
+    time_t NowTime;
+    int i;
+
+    NowTime = time(NULL);
+    strncpy(day, (char *)ctime(&NowTime)+8, 2);
+    day[2] = '\0';
+    if (day[0] == ' ') {
+        day[0] = '0';
+    }
+    strncpy(month, (char *)ctime(&NowTime)+4, 3);
+    strncpy(month, (char *)ctime(&NowTime)+4, 3);
+    month[3] = '\0';
+    for (i = 0; i < 12; i++) {
+        if (!strcasecomp(month, months[i])) {
+	    break;
+	}
+    }
+    i++;
+    sprintf(month, "%s%d", (i < 10 ? "0" : ""), i);
+    sprintf(date, "9999%s%s", month, day);
+    TheDate = atoi(date);
+    strcpy(ThisYear, (char *)ctime(&NowTime)+20);
+    ThisYear[4] = '\0';
+    sprintf(LastYear, "%d", (atoi(ThisYear) - 1));
+    HaveYears = TRUE;
+}
+
+typedef struct _EntryInfo {
+    char *       filename;
+    char *       type;
+    char *       date;
+    unsigned int size;
+    BOOLEAN      display;  /* show this entry? */
+} EntryInfo;
+
+PRIVATE void free_entryinfo_struct_contents ARGS1(
+	EntryInfo *,	entry_info)
+{
+    if (entry_info) {
+        FREE(entry_info->filename);
+        FREE(entry_info->type);
+        FREE(entry_info->date);
+    }
+   /* dont free the struct */
+}
+
+/*
+ * is_ls_date() --
+ *      Return TRUE if s points to a string of the form:
+ *              "Sep  1  1990 " or
+ *              "Sep 11 11:59 " or
+ *              "Dec 12 1989  " or
+ *              "FCv 23 1990  " ...
+ */
+PRIVATE BOOLEAN is_ls_date ARGS1(
+	char *,		s)
+{
+    /* must start with three alpha characters */
+    if (!isalpha(*s++) || !isalpha(*s++) || !isalpha(*s++))
+        return FALSE;
+
+    /* space or HT_NON_BREAK_SPACE */
+    if (!(*s == ' ' || *s == HT_NON_BREAK_SPACE)) {
+	s++;
+	return FALSE;
+    }
+    s++;
+
+    /* space or digit */
+    if (!(*s == ' ' || isdigit(*s))) {
+	s++;
+	return FALSE;
+    }
+    s++;
+
+    /* digit */
+    if (!isdigit(*s++))
+        return FALSE;
+
+    /* space */
+    if (*s++ != ' ')
+        return FALSE;
+
+    /* space or digit */
+    if (!(*s == ' ' || isdigit(*s))) {
+	s++;
+	return FALSE;
+    }
+    s++;
+
+    /* digit */
+    if (!isdigit(*s++))
+        return FALSE;
+
+    /* colon or digit */
+    if (!(*s == ':' || isdigit(*s))) {
+	s++;
+	return FALSE;
+    }
+    s++;
+
+    /* digit */
+    if (!isdigit(*s++))
+        return FALSE;
+
+    /* space or digit */
+    if (!(*s == ' ' || isdigit(*s))) {
+	s++;
+	return FALSE;
+    }
+    s++;
+
+    /* space */
+    if (*s++ != ' ')
+        return FALSE;
+
+    return TRUE;
+} /* is_ls_date() */
+
+/*
+ *  parse_eplf_line() --
+ *      Extract the name, size, and date from an EPLF line. - 08-06-96 DJB
+ */
+PRIVATE void parse_eplf_line ARGS2(
+	char *,		line,
+	EntryInfo *,	info)
+{
+    char *cp = line;
+    char ct[26];
+    unsigned long size;
+    time_t secs;
+    static time_t base; /* time() value on this OS in 1970 */
+    static int flagbase = 0;
+
+    if (!flagbase) {
+	struct tm t;
+	t.tm_year = 70; t.tm_mon = 0; t.tm_mday = 0;
+	t.tm_hour = 0; t.tm_min = 0; t.tm_sec = 0;
+	t.tm_isdst = -1;
+	base = mktime(&t); /* could return -1 */
+	flagbase = 1;
+    }
+
+    while (*cp) {
+    	switch(*cp) {
+	    case '\t':
+	        StrAllocCopy(info->filename, cp + 1);
+		return;
+	    case 's':
+	        size = 0;
+		while (*(++cp) && (*cp != ','))
+		    size = (size * 10) + (*cp - '0');
+		info->size = size;
+		break;
+	    case 'm':
+		secs = 0;
+		while (*(++cp) && (*cp != ','))
+		    secs = (secs * 10) + (*cp - '0');
+		secs += base; /* assumes that time_t is #seconds */
+		strcpy(ct, ctime(&secs));
+		ct[24] = 0;
+		StrAllocCopy(info->date, ct);
+		break;
+	    case '/':
+		StrAllocCopy(info->type, "Directory");
+	    default:
+		while (*cp) {
+		    if (*cp++ == ',')
+		      break;
+		}
+		break;
+	}
+    }
+} /* parse_eplf_line */
+
+/*
+ * parse_ls_line() --
+ *      Extract the name, size, and date from an ls -l line.
+ */
+PRIVATE void parse_ls_line ARGS2(
+	char *,		line,
+	EntryInfo *,	entry_info)
+{
+    short  i, j;
+    int    base=1;
+    int    size_num=0;
+
+    for (i = strlen(line) - 1;
+         (i > 13) && (!isspace(line[i]) || !is_ls_date(&line[i-12])); i--)
+        ; /* null body */
+    line[i] = '\0';
+    if (i > 13) {
+        StrAllocCopy(entry_info->date, &line[i-12]);
+	/* replace the 4th location with nbsp if it is a space or zero */
+	if (entry_info->date[4] == ' ' || entry_info->date[4] == '0')
+	    entry_info->date[4] = HT_NON_BREAK_SPACE;
+	/* make sure year or time is flush right */
+	if (entry_info->date[11] == ' ') {
+	    for (j = 11; j > 6; j--) {
+	        entry_info->date[j] = entry_info->date[j-1];
+	    }
+	}
+    } 
+    j = i - 14;
+    while (isdigit(line[j])) {
+        size_num += (line[j] - '0') * base;
+        base *= 10;
+        j--;
+    }
+    entry_info->size = size_num;
+    StrAllocCopy(entry_info->filename, &line[i + 1]);
+} /* parse_ls_line() */
+
+/*
+ * parse_vms_dir_entry()
+ *      Format the name, date, and size from a VMS LIST line
+ *      into the EntryInfo structure - FM
+ */
+PRIVATE void parse_vms_dir_entry ARGS2(
+	char *,		line,
+	EntryInfo *,	entry_info)
+{
+    int i, j, ialloc;
+    char *cp, *cpd, *cps, date[16], *sp = " ";
+
+    /**  Get rid of blank lines, and information lines.  **/
+    /**  Valid lines have the ';' version number token.  **/
+    if (!strlen(line) || (cp=strchr(line, ';')) == NULL) {
+        entry_info->display = FALSE;
+        return;
+    }
+
+    /** Cut out file or directory name at VMS version number. **/
+    *cp++ ='\0';
+    StrAllocCopy(entry_info->filename,line);
+
+    /** Cast VMS non-README file and directory names to lowercase. **/
+    if (strstr(entry_info->filename, "READ") == NULL) {
+        for (i = 0; entry_info->filename[i]; i++)
+	    entry_info->filename[i] = TOLOWER(entry_info->filename[i]);
+    } else {
+        i = strlen(entry_info->filename);
+    }
+
+    /** Uppercase terminal .z's or _z's. **/
+    if ((--i > 2) &&
+        entry_info->filename[i] == 'z' &&
+        (entry_info->filename[i-1] == '.' ||
+         entry_info->filename[i-1] == '_'))
+        entry_info->filename[i] = 'Z';
+
+    /** Convert any tabs in rest of line to spaces. **/
+    cps = cp-1;
+    while ((cps=strchr(cps+1, '\t')) != NULL)
+        *cps = ' ';
+
+    /** Collapse serial spaces. **/
+    i = 0; j = 1;
+    cps = cp;
+    while (cps[j] != '\0') {
+        if (cps[i] == ' ' && cps[j] == ' ')
+            j++;
+        else
+            cps[++i] = cps[j++];
+    }
+    cps[++i] = '\0';
+
+    /* Set the years and date, if we don't have them yet. **/
+    if (!HaveYears) {
+        set_years_and_date();
+    }
+
+    /** Track down the date. **/
+    if ((cpd=strchr(cp, '-')) != NULL &&
+        strlen(cpd) > 9 && isdigit(*(cpd-1)) &&
+        isalpha(*(cpd+1)) && *(cpd+4) == '-') {
+
+        /** Month **/
+        *(cpd+4) = '\0';
+        *(cpd+2) = TOLOWER(*(cpd+2));
+        *(cpd+3) = TOLOWER(*(cpd+3));
+	sprintf(date, "%s ", cpd+1);
+	*(cpd+4) = '-';
+
+	/** Day **/
+	*cpd = '\0';
+	if (isdigit(*(cpd-2)))
+	    sprintf(date+4, "%s ", cpd-2);
+	else
+	    sprintf(date+4, "%c%s ", HT_NON_BREAK_SPACE, cpd-1);
+	*cpd = '-';
+
+	/** Time or Year **/
+	if (!strncmp(ThisYear, cpd+5, 4) &&
+	    strlen(cpd) > 15 && *(cpd+12) == ':') {
+	    *(cpd+15) = '\0';
+	    sprintf(date+7, "%s", cpd+10);
+	    *(cpd+15) = ' ';
+	} else {
+	    *(cpd+9) = '\0';
+	    sprintf(date+7, " %s", cpd+5);
+	    *(cpd+9) = ' ';
+	}
+
+        StrAllocCopy(entry_info->date, date);
+    }
+
+    /** Track down the size **/
+    if ((cpd=strchr(cp, '/')) != NULL) {
+        /* Appears be in used/allocated format */
+        cps = cpd;
+        while (isdigit(*(cps-1)))
+            cps--;
+        if (cps < cpd)
+            *cpd = '\0';
+        entry_info->size = atoi(cps);
+        cps = cpd+1;
+        while (isdigit(*cps))
+            cps++;
+        *cps = '\0';
+        ialloc = atoi(cpd+1);
+        /* Check if used is in blocks or bytes */
+        if (entry_info->size <= ialloc)
+            entry_info->size *= 512;
+
+    } else if ((cps=strtok(cp, sp)) != NULL) {
+        /* We just initialized on the version number */
+        /* Now let's hunt for a lone, size number    */
+        while ((cps=strtok(NULL, sp)) != NULL) {
+           cpd = cps;
+           while (isdigit(*cpd))
+               cpd++;
+           if (*cpd == '\0') {
+               /* Assume it's blocks */
+               entry_info->size = atoi(cps) * 512;
+               break;
+           }
+       }
+    }
+
+    /** Wrap it up **/
+    if (TRACE)
+	fprintf(stderr, "HTFTP: VMS filename: %s  date: %s  size: %d\n",
+			entry_info->filename,
+			entry_info->date ? entry_info->date : "",
+			entry_info->size);
+    return;
+} /* parse_vms_dir_entry() */
+
+/*
+ * parse_ms_windows_dir_entry() --
+ *      Format the name, date, and size from an MS_WINDOWS LIST line into
+ *      the EntryInfo structure (assumes Chameleon NEWT format). - FM
+ */
+PRIVATE void parse_ms_windows_dir_entry ARGS2(
+	char *,		line,
+	EntryInfo *,	entry_info)
+{
+    char *cp = line;
+    char *cps, *cpd, date[16];
+    char *end = line + strlen(line);
+
+    /**  Get rid of blank or junk lines.  **/
+    while (*cp && isspace(*cp))
+	cp++;
+    if (!(*cp)) {
+        entry_info->display = FALSE;
+        return;
+    }
+
+    /** Cut out file or directory name. **/
+    cps = cp;
+    while (*cps && !isspace(*cps)) 
+	cps++;
+    *cps++ ='\0';
+    cpd = cps;
+    StrAllocCopy(entry_info->filename, cp);
+
+    /** Track down the size **/
+    if (cps < end) {
+	while (*cps && isspace(*cps))
+	    cps++;
+	cpd = cps;
+	while (*cpd && !isspace(*cpd))
+	    cpd++;
+	*cpd++ = '\0';
+	if (isdigit(*cps)) {
+	    entry_info->size = atoi(cps);
+	} else {
+	    StrAllocCopy(entry_info->type, "Directory");
+	}
+    } else {
+	StrAllocCopy(entry_info->type, "");
+    }
+
+    /* Set the years and date, if we don't have them yet. **/
+    if (!HaveYears) {
+	set_years_and_date();
+    }
+
+    /** Track down the date. **/
+    if (cpd < end) {
+	while (*cpd && isspace(*cpd))
+            cpd++;
+	if (strlen(cpd) > 17) {
+	    *(cpd+6)  = '\0';  /* Month and Day */
+	    *(cpd+11) = '\0';  /* Year */
+	    *(cpd+17) = '\0';  /* Time */
+	    if (strcmp(ThisYear, cpd+7))
+	        /* Not this year, so show the year */
+	        sprintf(date, "%s  %s", cpd, (cpd+7));
+	    else
+	        /* Is this year, so show the time */
+	        sprintf(date, "%s %s", cpd, (cpd+12));
+            StrAllocCopy(entry_info->date, date);
+	    if (entry_info->date[4] == ' '|| entry_info->date[4] == '0') {
+		entry_info->date[4] = HT_NON_BREAK_SPACE;
+	    }
+	}
+    }
+
+    /** Wrap it up **/
+    if (TRACE)
+	fprintf(stderr, "HTFTP: MS Windows filename: %s  date: %s  size: %d\n",
+			entry_info->filename,
+			entry_info->date ? entry_info->date : "",
+			entry_info->size);
+    return;
+} /* parse_ms_windows_dir_entry */
+
+/*
+ * parse_windows_nt_dir_entry() --
+ *      Format the name, date, and size from a WINDOWS_NT LIST line into
+ *      the EntryInfo structure (assumes Chameleon NEWT format). - FM
+ */
+PRIVATE void parse_windows_nt_dir_entry ARGS2(
+	char *,		line,
+	EntryInfo *,	entry_info)
+{
+    char *cp = line;
+    char *cps, *cpd, date[16];
+    char *end = line + strlen(line);
+    int i;
+
+    /**  Get rid of blank or junk lines.  **/
+    while (*cp && isspace(*cp))
+	cp++;
+    if (!(*cp)) {
+        entry_info->display = FALSE;
+        return;
+    }
+
+    /** Cut out file or directory name. **/
+    cpd = cp;
+    cps = (end-1);
+    while (cps >= cpd && !isspace(*cps)) 
+	cps--;
+    cp = (cps+1);
+    if (!strcmp(cp, ".") || !strcmp(cp, "..")) {
+        entry_info->display = FALSE;
+        return;
+    }
+    StrAllocCopy(entry_info->filename, cp);
+    if (cps < cpd)
+	return;
+    *cp = '\0';
+    end = cp;
+
+    /* Set the years and date, if we don't have them yet. **/
+    if (!HaveYears) {
+	set_years_and_date();
+    }
+
+    /** Cut out the date. **/
+    cp = cps = cpd;
+    while (*cps && !isspace(*cps))
+	cps++;
+    *cps++ ='\0';
+    if (cps > end) {
+        entry_info->display = FALSE;
+        return;
+    }
+    while (*cps && isspace(*cps))
+	cps++;
+    cpd = cps;
+    while (*cps && !isspace(*cps))
+	cps++;
+    *cps++ ='\0';
+    if (cps > end || cpd == cps || strlen(cpd) < 7) {
+        entry_info->display = FALSE;
+        return;
+    }
+    if (strlen(cp) == 8 &&
+	isdigit(*cp) && isdigit(*(cp+1)) && *(cp+2) == '-' &&
+	isdigit(*(cp+3)) && isdigit(*(cp+1)) && *(cp+5) == '-') {
+	*(cp+2)  = '\0';	/* Month */
+	i = atoi(cp) - 1;
+	*(cp+5) = '\0';		/* Day */
+	sprintf(date, "%s %s", months[i], (cp+3));
+	if (date[4] == '0')
+	    date[4] = ' ';
+	cp += 6;			/* Year */
+	if (strcmp((ThisYear+2), cp)) {
+	    /* Not this year, so show the year */
+	    if (atoi(cp) < atoi((char *)&ThisYear[2])) {
+		sprintf((char *)&date[6], "  %c%c%s",
+				          ThisYear[0], ThisYear[1], cp);
+	    } else {
+		sprintf((char *)&date[6], "  %c%c%s",
+					  LastYear[0], LastYear[1], cp);
+	    }
+	} else {
+	    /* Is this year, so show the time */
+	    *(cpd+2) = '\0';	/* Hour */
+	    i = atoi(cpd);
+	    if (*(cpd+5) == 'P' || *(cpd+5) == 'p')
+		i += 12;
+	    *(cpd+5) = '\0';
+	    sprintf((char*)&date[6], " %s%d:%s",
+				     (i < 10 ? "0" : ""), i, (cpd+3));
+	}
+        StrAllocCopy(entry_info->date, date);
+	if (entry_info->date[4] == ' '|| entry_info->date[4] == '0') {
+	    entry_info->date[4] = HT_NON_BREAK_SPACE;
+	}
+    }
+
+    /** Track down the size **/
+    if (cps < end) {
+	while (*cps && isspace(*cps))
+	    cps++;
+	cpd = cps;
+	while (*cpd && !isspace(*cpd))
+	    cpd++;
+	*cpd = '\0';
+	if (isdigit(*cps)) {
+	    entry_info->size = atoi(cps);
+	} else {
+	    StrAllocCopy(entry_info->type, "Directory");
+	}
+    } else {
+	StrAllocCopy(entry_info->type, "");
+    }
+
+    /** Wrap it up **/
+    if (TRACE)
+	fprintf(stderr, "HTFTP: Windows NT filename: %s  date: %s  size: %d\n",
+			entry_info->filename,
+			entry_info->date ? entry_info->date : "",
+			entry_info->size);
+    return;
+} /* parse_windows_nt_dir_entry */
+
+/*
+ * parse_cms_dir_entry() --
+ *      Format the name, date, and size from a VM/CMS line into
+ *      the EntryInfo structure. - FM
+ */
+PRIVATE void parse_cms_dir_entry ARGS2(
+	char *,		line,
+	EntryInfo *,	entry_info)
+{
+    char *cp = line;
+    char *cps, *cpd, date[16];
+    char *end = line + strlen(line);
+    int RecordLength = 0;
+    int Records = 0;
+    int i;
+
+    /**  Get rid of blank or junk lines.  **/
+    while (*cp && isspace(*cp))
+	cp++;
+    if (!(*cp)) {
+        entry_info->display = FALSE;
+        return;
+    }
+
+    /** Cut out file or directory name. **/
+    cps = cp;
+    while (*cps && !isspace(*cps)) 
+	cps++;
+    *cps++ ='\0';
+    StrAllocCopy(entry_info->filename, cp);
+    if (strchr(entry_info->filename, '.') != NULL)
+	/** If we already have a dot, we did an NLST. **/
+        return;
+    cp = cps;
+    while (*cp && isspace(*cp))
+	cp++;
+    if (!(*cp)) {
+        /** If we don't have more, we've misparsed. **/
+        FREE(entry_info->filename);
+        FREE(entry_info->type);
+        entry_info->display = FALSE;
+        return;
+    }
+    cps = cp;
+    while (*cps && !isspace(*cps)) 
+	cps++;
+    *cps++ ='\0';
+    if ((0 == strcasecomp(cp, "DIR")) && (cp - line) > 17) {
+        /** It's an SFS directory. **/
+        StrAllocCopy(entry_info->type, "Directory");
+        entry_info->size = 0;
+    } else {
+        /** It's a file. **/
+        cp--;
+	*cp = '.';
+	StrAllocCat(entry_info->filename, cp);
+
+        /** Track down the VM/CMS RECFM or type. **/
+	cp = cps;
+	if (cp < end) {
+	    while (*cp && isspace(*cp))
+	        cp++;
+	    cps = cp;
+	    while (*cps && !isspace(*cps))
+	        cps++;
+	    *cps++ = '\0';
+	    /** Check cp here, if it's relevant someday. **/
+	}
+    }
+
+    /** Track down the record length or dash. **/
+    cp = cps;
+    if (cp < end) {
+	while (*cp && isspace(*cp))
+	    cp++;
+	cps = cp;
+	while (*cps && !isspace(*cps))
+	    cps++;
+	*cps++ = '\0';
+	if (isdigit(*cp)) {
+	    RecordLength = atoi(cp);
+	}
+    }
+
+    /** Track down the number of records or the dash. **/
+    cp = cps;
+    if (cps < end) {
+	while (*cp && isspace(*cp))
+	    cp++;
+	cps = cp;
+	while (*cps && !isspace(*cps))
+	    cps++;
+	*cps++ = '\0';
+	if (isdigit(*cp)) {
+	    Records = atoi(cp);
+	}
+	if (Records > 0 && RecordLength > 0) {
+	    /** Compute an approximate size. **/
+	    entry_info->size = (Records * RecordLength);
+	}
+    }
+
+    /** Set the years and date, if we don't have them yet. **/
+    if (!HaveYears) {
+	set_years_and_date();
+    }
+
+    /** Track down the date. **/
+    cpd = cps;
+    if (((cps < end) &&
+         (cps = strchr(cpd, ':')) != NULL) &&
+        (cps < (end - 3) &&
+	 isdigit(*(cps+1)) && isdigit(*(cps+2)) && *(cps+3) == ':')) {
+	cps += 3;
+	*cps = '\0';
+	if ((cps - cpd) >= 14) {
+	    cpd = (cps - 14);
+	    *(cpd+2) = '\0';	/* Month */
+	    *(cpd+5) = '\0';	/* Day */
+	    *(cpd+8) = '\0';	/* Year */
+	    cps -= 5;		/* Time */
+	    if (*cpd == ' ')
+	       *cpd = '0';
+	    i = atoi(cpd) - 1;
+	    sprintf(date, "%s %s", months[i], (cpd+3));
+	    if (date[4] == '0')
+		date[4] = ' ';
+	    cpd += 6;		/* Year */
+	    if (strcmp((ThisYear+2), cpd)) {
+	        /* Not this year, so show the year. */
+		if (atoi(cpd) < atoi((char *)&ThisYear[2])) {
+		    sprintf((char *)&date[6], "  %c%c%s",
+					      ThisYear[0], ThisYear[1], cpd);
+		} else {
+		    sprintf((char *)&date[6], "  %c%c%s",
+					      LastYear[0], LastYear[1], cpd);
+		}
+	    } else {
+	        /* Is this year, so show the time. */
+		*(cps+2) = '\0';	/* Hour */
+		i = atoi(cps);
+		sprintf((char*)&date[6], " %s%d:%s",
+				     (i < 10 ? "0" : ""), i, (cps+3));
+	    }
+	    StrAllocCopy(entry_info->date, date);
+	    if (entry_info->date[4] == ' '|| entry_info->date[4] == '0') {
+		entry_info->date[4] = HT_NON_BREAK_SPACE;
+	    }
+	}
+    }
+
+    /** Wrap it up. **/
+    if (TRACE)
+	fprintf(stderr, "HTFTP: VM/CMS filename: %s  date: %s  size: %d\n",
+			entry_info->filename,
+			entry_info->date ? entry_info->date : "",
+			entry_info->size);
+    return;
+} /* parse_cms_dir_entry */
+
+/*
+ *     parse_dir_entry() 
+ *      Given a line of LIST/NLST output in entry, return results 
+ *      and a file/dir name in entry_info struct
+ *
+ *      If first is true, this is the first name in a directory.
+ */
+
+PRIVATE EntryInfo * parse_dir_entry ARGS2(
+	char *,		entry,
+	BOOLEAN *,	first)
+{
+    EntryInfo *entry_info;
+    int  i;
+    int  len;
+    BOOLEAN remove_size=FALSE;
+    char *cp;
+
+    entry_info = (EntryInfo *)malloc(sizeof(EntryInfo));    
+    entry_info->filename = NULL;
+    entry_info->type = NULL;
+    entry_info->date = NULL;
+    entry_info->size = 0;
+    entry_info->display = TRUE;
+
+    switch (server_type) {
+        case UNIX_SERVER:
+        case PETER_LEWIS_SERVER:
+        case MACHTEN_SERVER:
+	case MSDOS_SERVER:
+	    /*
+	    **  Check for EPLF output (local times).
+	    */
+	    if (*entry == '+') {
+		parse_eplf_line(entry, entry_info);
+		break;
+	    }
+  
+            /*
+	    **  Interpret and edit LIST output from Unix server.
+	    */
+            len = strlen(entry);
+	    if (*first) {
+	        *first = FALSE;
+                if (!strncmp(entry, "total ", 6) ||
+                    strstr(entry, "not available") != NULL) {
+		    entry_info->display=FALSE;
+		    return(entry_info);
+		} else if (unsure_type) {
+                    /* this isn't really a unix server! */
+                    server_type = GENERIC_SERVER;
+		    entry_info->display=FALSE;
+		    return(entry_info);
+		}
+	    }
+
+            /*
+	    **  Check first character of ls -l output.
+	    */
+            if (TOUPPER(entry[0]) == 'D')  {
+                /*
+		**  It's a directory.
+		*/
+                StrAllocCopy(entry_info->type, "Directory"); 
+	        remove_size=TRUE; /* size is not useful */
+	    } else if (entry[0] == 'l') {
+                /*
+		**  It's a symbolic link, does the user care about
+		**  knowing if it is symbolic?  I think so since
+		**  it might be a directory.
+		*/
+                StrAllocCopy(entry_info->type, "Symbolic Link"); 
+		remove_size=TRUE; /* size is not useful */
+
+                /*
+		**  Strip off " -> pathname".
+		*/
+                for (i = len - 1; (i > 3) &&
+		                  (!isspace(entry[i]) ||
+				   (entry[i-1] != '>')  ||
+				   (entry[i-2] != '-') ||
+				   (entry[i-3] != ' ')); i--)
+                    ; /* null body */
+                if (i > 3) {
+                    entry[i-3] = '\0';
+                    len = i - 3;
+                }
+            } /* link */
+
+	    parse_ls_line(entry, entry_info); 
+
+	    if (!strcmp(entry_info->filename,"..") ||
+	        !strcmp(entry_info->filename,"."))
+		entry_info->display=FALSE;
+	    /*
+	    **  Goto the bottom and get real type.
+	    */
+            break;
+
+        case VMS_SERVER:
+            /*
+	    **  Interpret and edit LIST output from VMS server
+	    **  and convert information lines to zero length.
+	    */
+	    parse_vms_dir_entry(entry, entry_info);
+
+            /*
+	    **  Get rid of any junk lines.
+	    */
+	    if (!entry_info->display)
+		return(entry_info);
+
+	    /*
+	    **  Trim off VMS directory extensions.
+	    */
+	    len = strlen(entry_info->filename);
+            if ((len > 4) && !strcmp(&entry_info->filename[len-4], ".dir")) {
+		entry_info->filename[len-4] = '\0';
+                StrAllocCopy(entry_info->type, "Directory"); 
+		remove_size=TRUE; /* size is not useful */
+	    }
+	    /*
+	    **  Goto the bottom and get real type.
+	    */
+            break;
+
+        case MS_WINDOWS_SERVER:
+            /*
+	    **  Interpret and edit LIST output from MS_WINDOWS server
+	    **  and convert information lines to zero length.
+	    */
+	    parse_ms_windows_dir_entry(entry, entry_info);
+
+            /*
+	    **  Get rid of any junk lines.
+	    */
+	    if (!entry_info->display)
+		return(entry_info);
+	    if (entry_info->type && *entry_info->type == '\0') {
+		FREE(entry_info->type);
+		return(entry_info);
+	    }
+	    /*
+	    **  Goto the bottom and get real type.
+	    */
+	    break;
+
+        case WINDOWS_NT_SERVER:
+            /*
+	    **  Interpret and edit LIST output from MS_WINDOWS server
+	    **  and convert information lines to zero length.
+	    */
+	    parse_windows_nt_dir_entry(entry, entry_info);
+
+            /*
+	    **  Get rid of any junk lines.
+	    */
+	    if (!entry_info->display)
+		return(entry_info);
+	    if (entry_info->type && *entry_info->type == '\0') {
+		FREE(entry_info->type);
+		return(entry_info);
+	    }
+	    /*
+	    **  Goto the bottom and get real type.
+	    */
+	    break;
+
+        case CMS_SERVER:
+	  {
+            /*
+	    **  Interpret and edit LIST output from VM/CMS server
+	    **  and convert any information lines to zero length.
+	    */
+	    parse_cms_dir_entry(entry, entry_info);
+
+            /*
+	    **  Get rid of any junk lines.
+	    */
+	    if (!entry_info->display)
+		return(entry_info);
+	    if (entry_info->type && *entry_info->type == '\0') {
+		FREE(entry_info->type);
+		return(entry_info);
+	    }
+	    /*
+	    **  Goto the bottom and get real type.
+	    */
+	    break;
+	  }
+
+        case NCSA_SERVER:
+        case TCPC_SERVER:
+            /*
+	    **  Directories identified by trailing "/" characters.
+	    */
+	    StrAllocCopy(entry_info->filename, entry);
+            len = strlen(entry);
+            if (entry[len-1] == '/') {
+                /*
+		**  It's a dir, remove / and mark it as such.
+		*/
+                entry[len-1] = '\0';
+                StrAllocCopy(entry_info->type, "Directory");
+		remove_size=TRUE; /* size is not useful */
+            }
+	    /*
+	    **  Goto the bottom and get real type.
+	    */
+            break;
+	
+	default:
+	    /*
+	    **  We can't tell if it is a directory since we only
+	    **  did an NLST :(  List bad file types anyways? NOT!
+	    */
+	    StrAllocCopy(entry_info->filename, entry);
+	    return(entry_info); /* mostly empty info */
+	    break; /* not needed */
+
+    } /* switch (server_type) */
+
+    if (remove_size && entry_info->size) {
+	entry_info->size = 0;
+    }
+
+    if (entry_info->filename && strlen(entry_info->filename) > 3) {
+	if (((cp=strrchr(entry_info->filename, '.')) != NULL &&
+	     0 == strncasecomp(cp, ".me", 3)) &&
+	    (cp[3] == '\0' || cp[3] == ';')) {
+	    /*
+	    **  Don't treat this as application/x-Troff-me
+	    **  if it's a Unix server but has the string
+	    **  "read.me", or if it's not a Unix server. - FM
+	    */
+	    if ((server_type != UNIX_SERVER) ||
+		(cp > (entry_info->filename + 3) &&
+		 0 == strncasecomp((cp - 4), "read.me", 7))) {
+		StrAllocCopy(entry_info->type, "text/plain");
+	    }
+	}
+    }
+
+    /*
+    **  Get real types eventually.
+    */
+    if (!entry_info->type) {
+	char *cp;
+        HTFormat format;
+        HTAtom * encoding;  /* @@ not used at all */
+        format = HTFileFormat(entry_info->filename, &encoding);
+
+	if (!strncmp(HTAtom_name(format), "application",11)) {
+	    cp = HTAtom_name(format) + 12;
+	    if (!strncmp(cp,"x-",2))
+		cp += 2;
+	} else {
+	    cp = HTAtom_name(format);
+	}
+
+        StrAllocCopy(entry_info->type, cp);
+    }
+
+    return(entry_info);
+} /* parse_dir_entry */
+
+PUBLIC int compare_EntryInfo_structs ARGS2(
+	EntryInfo *,	entry1, 
+	EntryInfo *,	entry2)
+{
+    int i, status;
+    char date1[16], date2[16], time1[8], time2[8], month[4];
+
+    switch(HTfileSortMethod) {
+        case FILE_BY_SIZE:
+	    /* both equal or both 0 */
+            if (entry1->size == entry2->size)
+		return(strcmp(entry1->filename, entry2->filename));
+	    else
+		if (entry1->size > entry2->size)
+		    return(1);
+		else
+		    return(-1);
+            break;
+
+        case FILE_BY_TYPE:
+            if (entry1->type && entry2->type) {
+                status = strcasecomp(entry1->type, entry2->type);
+		if (status)
+		    return(status);
+		/* else fall to filename comparison */
+	    }
+            return (strcmp(entry1->filename, entry2->filename));
+            break;
+
+        case FILE_BY_DATE:
+            if (entry1->date && entry2->date) {
+		/*
+		** Make sure we have the correct length. - FM
+		*/
+		if (strlen(entry1->date) != 12 || strlen(entry2->date) != 12) {
+		    return(strcmp(entry1->filename, entry2->filename));
+		}
+		/*
+		** Set the years and date,
+		** if we don't have them yet.
+		*/
+		if (!HaveYears) {
+		    set_years_and_date();
+		}
+		/*
+		** Set up for sorting in reverse
+		** chronological order. - FM
+		*/
+		if (entry1->date[9] == ':') {
+		    strcpy(date1, "9999");
+		    strcpy(time1, (char *)&entry1->date[7]);
+		    if (time1[0] == ' ') {
+			 time1[0] = '0';
+		    }
+		} else {
+		    strcpy(date1, (char *)&entry1->date[8]);
+		    strcpy(time1, "00:00");
+		}
+		strncpy(month, entry1->date, 3);
+		month[3] = '\0';
+		for (i = 0; i < 12; i++) {
+		    if (!strcasecomp(month, months[i])) {
+		        break;
+		    }
+	        }
+		i++;
+		sprintf(month, "%s%d", (i < 10 ? "0" : ""), i);
+		strcat(date1, month);
+		strncat(date1, (char *)&entry1->date[4], 2);
+		date1[8] = '\0';
+		if (date1[6] == ' ' || date1[6] == HT_NON_BREAK_SPACE) {
+		    date1[6] = '0';
+		}
+		if (date1[0] == '9' && atoi(date1) > TheDate) {
+		    for (i = 0; i < 4; i++) {
+			date1[i] = LastYear[i];
+		    }
+		}
+		strcat(date1, time1);
+		    if (entry2->date[9] == ':') {
+		        strcpy(date2, "9999");
+			strcpy(time2, (char *)&entry2->date[7]);
+			if (time2[0] == ' ') {
+			    time2[0] = '0';
+			}
+		    } else {
+			strcpy(date2, (char *)&entry2->date[8]);
+		        strcpy(time2, "00:00");
+		    }
+		strncpy(month, entry2->date, 3);
+		month[3] = '\0';
+		for (i = 0; i < 12; i++) {
+		    if (!strcasecomp(month, months[i])) {
+		        break;
+		    }
+		}
+		i++;
+		sprintf(month, "%s%d", (i < 10 ? "0" : ""), i);
+		strcat(date2, month);
+		strncat(date2, (char *)&entry2->date[4], 2);
+		date2[8] = '\0';
+		if (date2[6] == ' ' || date2[6] == HT_NON_BREAK_SPACE) {
+		    date2[6] = '0';
+		}
+		if (date2[0] == '9' && atoi(date2) > TheDate) {
+		    for (i = 0; i < 4; i++) {
+			date2[i] = LastYear[i];
+		    }
+		}
+		strcat(date2, time2);
+		/*
+		** Do the comparison. - FM
+		*/
+                status = strcasecomp(date2, date1);
+		if (status)
+		    return(status);
+		/* else fall to filename comparison */
+	    }
+            return (strcmp(entry1->filename, entry2->filename));
+            break;
+
+        case FILE_BY_NAME:
+        default:
+            return (strcmp(entry1->filename, entry2->filename));
+    }
+}
+
+
+/*	Read a directory into an hypertext object from the data socket
+**	--------------------------------------------------------------
+**
+** On entry,
+**	anchor		Parent anchor to link the this node to
+**	address		Address of the directory
+** On exit,
+**	returns		HT_LOADED if OK
+**			<0 if error.
+*/
+PRIVATE int read_directory ARGS4(
+	HTParentAnchor *,	parent,
+	CONST char *,		address,
+	HTFormat,		format_out,
+	HTStream *,		sink)
+{
+    int status;
+    BOOLEAN WasInterrupted = FALSE;
+    HTStructured* target = HTML_new(parent, format_out, sink);
+    HTStructuredClass targetClass;
+    char *filename = HTParse(address, "", PARSE_PATH + PARSE_PUNCTUATION);
+    EntryInfo *entry_info;
+    BOOLEAN first=TRUE;
+    char string_buffer[64];
+    char *lastpath=NULL;/* prefix for link, either "" (for root) or xxx  */
+
+    targetClass = *(target->isa);
+
+    _HTProgress ("Receiving FTP directory.");
+    HTDirTitles(target, (HTAnchor*)parent);
+  
+    data_read_pointer = data_write_pointer = data_buffer;
+
+    if (*filename == '\0') {		  /* Empty filename: use root. */
+        StrAllocCopy (lastpath, "/");
+    } else if (!strcmp(filename,"/")) {	  /* Root path. */
+        StrAllocCopy (lastpath, "/foo/..");
+    } else {
+        char * p = strrchr(filename, '/');	     /* Find the lastslash. */
+	char *cp;
+	
+	if (server_type == CMS_SERVER) {
+	    StrAllocCopy(lastpath, filename); /* Use absolute path for CMS. */
+	} else {
+	    StrAllocCopy(lastpath, p+1);   /* Take slash off the beginning. */
+	}
+	if ((cp = strrchr(lastpath, ';')) != NULL) {   /* Trim type= param. */
+	    if (!strncasecomp((cp+1), "type=", 5)) {
+	        if (TOUPPER(*(cp+6)) == 'D' ||
+		    TOUPPER(*(cp+6)) == 'A' ||
+		    TOUPPER(*(cp+6)) == 'I')
+		    *cp = '\0';
+	    }
+	}
+    }
+    FREE (filename);
+
+   
+    {
+        HTBTree * bt = HTBTree_new((HTComparer)compare_EntryInfo_structs);
+        char c;
+	HTChunk * chunk = HTChunkCreate(128);
+	int BytesReceived = 0;
+	int BytesReported = 0;
+	char NumBytes[64];
+	PUTS("\n");  /* prettier LJM */
+	for (c = 0; c != (char)EOF;) {  /* For each entry in the directory */
+	    HTChunkClear(chunk);
+
+	    if (HTCheckForInterrupt()) {
+	    	WasInterrupted = TRUE;
+		if (BytesReceived) {
+		    goto unload_btree;  /* unload btree */
+		} else {
+		    ABORT_TARGET;
+		    HTBTreeAndObject_free(bt);
+		    return HT_INTERRUPTED;
+		}
+	    }
+
+	    /*   read directory entry
+	     */
+	    for (;;) {                 /* Read in one line as filename */
+		c = NEXT_DATA_CHAR;
+AgainForMultiNet:
+		if (interrupted_in_next_data_char) {
+	    	    WasInterrupted = TRUE;
+		    if (BytesReceived) {
+		        goto unload_btree;  /* unload btree */
+                    } else {
+                        ABORT_TARGET;
+                        HTBTreeAndObject_free(bt);
+                        return HT_INTERRUPTED;
+                    }
+		} else if (c == CR || c == LF) {    /* Terminator? */ 
+		    if (chunk->size != 0) {  /* got some text */
+		        /* Deal with MultiNet's wrapping of long lines */
+                        if (server_type == VMS_SERVER) {
+                        /* Deal with MultiNet's wrapping of long lines - F.M. */
+                            if (data_read_pointer < data_write_pointer &&
+                                *(data_read_pointer+1) == ' ')
+                                data_read_pointer++;
+                            else if (data_read_pointer >= data_write_pointer) {
+                                status = NETREAD(data_soc, data_buffer,
+                                                 DATA_BUFFER_SIZE);
+                                if (status == HT_INTERRUPTED) {
+                                    interrupted_in_next_data_char = 1;
+                                    goto AgainForMultiNet;
+                                }
+                                if (status <= 0) {
+                                    c = (char)EOF;
+                                    break;
+                                }
+                                data_write_pointer = data_buffer + status;
+                                data_read_pointer = data_buffer;
+                                if (*data_read_pointer == ' ')
+                                    data_read_pointer++;
+                                else
+                                    break;
+                            }
+                            else
+                                break;
+                        }
+			else
+		            break;            /* finish getting one entry */
+		    }
+		} else if (c == (char)EOF) {
+		    break;             /* End of file */
+		} else {
+		    HTChunkPutc(chunk, c);
+		}
+            }
+	    HTChunkTerminate(chunk);
+
+	    BytesReceived += chunk->size;
+	    if (BytesReceived > BytesReported + 1024) {
+	        sprintf(NumBytes,"Transferred %d bytes",BytesReceived);
+		HTProgress(NumBytes);
+		BytesReported = BytesReceived;
+	    }
+
+	    if (c == (char) EOF && chunk->size == 1)
+	    /* 1 means empty: includes terminating 0 */
+	        break;
+            if (TRACE)
+	        fprintf(stderr, "HTFTP: Line in %s is %s\n",
+	    			lastpath, chunk->data);
+
+	    entry_info = parse_dir_entry(chunk->data, &first);
+	    if (entry_info->display) {
+		 if (TRACE)
+		     fprintf(stderr, "Adding file to BTree: %s\n",
+		     		     entry_info->filename);
+	         HTBTree_add(bt, (EntryInfo *)entry_info); 
+	    }
+
+	}  /* next entry */
+
+unload_btree:
+
+        HTChunkFree(chunk);
+
+	/* print out the handy help message if it exits :) */
+	if (help_message_cache_non_empty()) {
+	    START(HTML_PRE);
+	    START(HTML_HR);
+	    PUTS(help_message_cache_contents());
+	    init_help_message_cache();  /* to free memory */
+	    START(HTML_HR);
+	} else {
+	    START(HTML_PRE);
+	    PUTS("\n");
+	}
+
+	/* Put up header 
+	 */
+	/* PUTS("    Date        Type             Size     Filename\n"); 
+	 */
+	   
+	/* Run through tree printing out in order 
+	 */
+	{
+	    HTBTElement * ele;
+	    int i;
+	    for (ele = HTBTree_next(bt, NULL);
+		 ele != NULL;
+		 ele = HTBTree_next(bt, ele)) {
+		entry_info = (EntryInfo *)HTBTree_object(ele);
+
+		if (entry_info->date) {
+		    PUTS(entry_info->date);
+		    PUTS("  ");
+		} else {
+		    PUTS("     * ");
+		}
+
+		if (entry_info->type) {
+		    for (i = 0; entry_info->type[i] != '\0' && i < 15; i++)
+		        PUTC(entry_info->type[i]);
+		    for (; i < 17; i++)
+		        PUTC(' ');
+		}
+
+		/* start the anchor */
+		HTDirEntry(target, lastpath, entry_info->filename);  
+		PUTS(entry_info->filename);
+		END(HTML_A);
+
+		if (entry_info->size) {
+		    if (entry_info->size < 1024)
+			sprintf(string_buffer, "  %d bytes",
+					       entry_info->size);
+		    else
+			sprintf(string_buffer, "  %dKb",
+					        entry_info->size/1024);
+			  PUTS(string_buffer);
+		}
+
+		PUTC('\n'); /* end of this entry */
+
+		free_entryinfo_struct_contents(entry_info);
+	    }
+	}
+	FREE_TARGET;
+	HTBTreeAndObject_free(bt);
+    }
+
+    FREE(lastpath);
+    if (WasInterrupted || HTCheckForInterrupt()) {
+        if (server_type != CMS_SERVER)
+            response(NIL);
+	_HTProgress("Data transfer interrupted.");
+	return HT_LOADED;
+    }
+    if (server_type != CMS_SERVER)
+        response(NIL);
+    return HT_LOADED;
+#ifdef NOTDEFINED
+    return response(NIL) == 2 ? HT_LOADED : -1;
+#endif /* NOTDEFINED */
+}
+
+/*	Retrieve File from Server
+**	-------------------------
+**
+** On entry,
+**	name		WWW address of a file: document, including hostname
+** On exit,
+**	returns		Socket number for file if good.
+**			<0 if bad.
+*/
+PUBLIC int HTFTPLoad ARGS4(
+	CONST char *,		name,
+	HTParentAnchor *,	anchor,
+	HTFormat,		format_out,
+	HTStream *,		sink)
+{
+    BOOL isDirectory = NO;
+    int status;
+    int retry;			/* How many times tried? */
+    HTFormat format;
+    char command[LINE_LENGTH+1];
+    
+
+    /* set use_list to NOT since we don't know what kind of server
+     * this is yet.  And set the type to GENERIC
+     */
+    use_list = FALSE;
+    server_type = GENERIC_SERVER;
+
+    for (retry = 0; retry < 2; retry++) { /* For timed out/broken connections */
+	status = get_connection(name);
+	if (status < 0)
+	    return status;
+
+#ifdef LISTEN
+	status = get_listen_socket();
+	if (status < 0) {
+	    NETCLOSE (control->socket);
+            control->socket = -1;
+            close_master_socket ();
+            /* HT_INTERRUPTED would fall through, if we could interrupt
+               somehow in the middle of it, which we currently can't. */
+	    return status;
+	}
+    
+#ifdef REPEAT_PORT
+/*	Inform the server of the port number we will listen on
+*/
+	{
+	    status = response(port_command);
+	    if (status == HT_INTERRUPTED) {
+              if (TRACE)
+                fprintf (stderr,
+			 "HTFTP: Interrupted in response (port_command)\n");
+              _HTProgress ("Connection interrupted.");
+              NETCLOSE (control->socket);
+              control->socket = -1;
+              close_master_socket ();
+              return HT_INTERRUPTED;
+            }
+	    if (status != 2) {		/* Could have timed out */
+		if (status < 0)
+		    continue;		/* try again - net error*/
+		return -status;		/* bad reply */
+	    }
+	    if (TRACE)
+	        fprintf(stderr, "HTFTP: Port defined.\n");
+	}
+#endif /* REPEAT_PORT */
+#else	/* Use PASV */
+/*	Tell the server to be passive
+*/
+	{
+	    char *p;
+	    int reply, h0, h1, h2, h3, p0, p1;	/* Parts of reply */
+	    int status;
+	    data_soc = status;
+
+	    sprintf(command, "PASV%c%c", CR, LF);
+	    status = response(command);
+	    if (status != 2) {
+		if (status < 0)
+		    continue;		/* retry or Bad return */
+		return -status;		/* bad reply */
+	    }
+	    for (p = response_text; *p && *p != ','; p++)
+		; /* null body */
+
+	    while (--p > response_text && '0' <= *p && *p <= '9')
+		; /* null body */
+	
+           status = sscanf(p+1, "%d,%d,%d,%d,%d,%d",
+                   &h0, &h1, &h2, &h3, &p0, &p1);
+           if (status < 4) {
+               fprintf(stderr, "HTFTP: PASV reply has no inet address!\n");
+               return -99;
+           }
+           passive_port = (p0<<8) + p1;
+	   if (TRACE)
+               fprintf(stderr, "HTFTP: Server is listening on port %d\n",
+				passive_port);
+
+
+/*	Open connection for data:
+*/
+	    sprintf(command,
+            "ftp://%d.%d.%d.%d:%d/",h0,h1,h2,h3,passive_port);
+            status = HTDoConnect(name, "FTP", passive_port, &data_soc);
+
+	    if (status < 0) {
+		(void) HTInetStatus("connect for data");
+		NETCLOSE(data_soc);
+		return status;			/* Bad return */
+	    }
+	    
+	    if (TRACE)
+	        fprintf(stderr, "FTP data connected, socket %d\n", data_soc);
+	}
+#endif /* use PASV */
+	status = 0;
+        break;	/* No more retries */
+
+    } /* for retries */
+    if (status < 0)
+        return status;		/* Failed with this code */
+    
+/*	Ask for the file:
+*/    
+    {
+        char *filename = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
+	char *fname = filename;	/** Save for subsequent free() **/
+	BOOL binary;
+	HTAtom * encoding;
+	char *type = NULL;
+	char *cp;
+
+	if (server_type == CMS_SERVER) {
+	    /** If the unescaped path has a %2f, reject it as illegal. - FM **/
+	    if (((cp = strstr(filename, "%2")) != NULL) &&
+	        TOUPPER(cp[2]) == 'F') {
+		FREE(fname);
+		init_help_message_cache();  /* to free memory */
+		NETCLOSE(control->socket);
+		control->socket = -1;
+		if (TRACE) {
+		    fprintf(stderr,
+		     "HTFTP: Rejecting path due to illegal escaped slash.\n");
+		}
+		return -1;
+	    }
+	}
+
+	if (!*filename) {
+	    StrAllocCopy(filename, "/");
+	    type = "D";
+	} else if ((type = strrchr(filename, ';')) != NULL) {
+	    /*
+	    **  Check and trim the type= parameter. - FM
+	    */
+	    if (!strncasecomp((type+1), "type=", 5)) {
+	        switch(TOUPPER(*(type+6))) {
+		case 'D':
+		    *type = '\0';
+		    type = "D";
+		    break;
+		case 'A':
+		    *type = '\0';
+		    type = "A";
+		    break;
+		case 'I':
+		    *type = '\0';
+		    type = "I";
+		    break;
+		default:
+		    type = "";
+		    break;
+		}
+		if (!*filename) {
+		    *filename = '/';
+		    *(filename+1) = '\0';
+		}
+	    }
+	    if (TRACE && *type != '\0') {
+	        fprintf(stderr, "HTFTP: type=%s\n", type);
+	    }
+	}
+	HTUnEscape(filename);
+	if (TRACE)
+	    fprintf(stderr, "HTFTP: UnEscaped %s\n", filename);
+	if (filename[1] == '~') {
+	    /*
+	    ** Check if translation of HOME as tilde is supported,
+	    ** and adjust filename if so. - FM
+	    */
+	    char *cp = NULL;
+	    char *fn = NULL;
+
+	    if ((cp = strchr((filename+1), '/')) != NULL) {
+		*cp = '\0';
+	    }
+	    sprintf(command, "PWD%c%c", CR, LF);
+	    status = response(command);
+	    if (status == 2 && response_text[5] == '/') {
+		sprintf(command, "CWD %s%c%c", (filename+1), CR, LF);
+		status = response(command);
+		if (status == 2) {
+		    StrAllocCopy(fn, (filename+1));
+		    if (cp) {
+			*cp = '/';
+			if (fn[strlen(fn)-1] != '/') {
+			    StrAllocCat(fn, cp);
+			} else {
+			    StrAllocCat(fn, (cp+1));
+			}
+		    }
+		    FREE(fname);
+		    fname = filename = fn;
+		}
+	    }
+	}
+	if (strlen(filename) > 3) {
+	    char *cp;
+	    if (((cp=strrchr(filename, '.')) != NULL &&
+	         0 == strncasecomp(cp, ".me", 3)) &&
+		(cp[3] == '\0' || cp[3] == ';')) {
+		/*
+		**  Don't treat this as application/x-Troff-me
+		**  if it's a Unix server but has the string
+		**  "read.me", or if it's not a Unix server. - FM
+		*/
+	        if ((server_type != UNIX_SERVER) ||
+		    (cp > (filename + 3) &&
+	             0 == strncasecomp((cp - 4), "read.me", 7))) {
+		    *cp = '\0';
+		    format = HTFileFormat(filename, &encoding);
+		    *cp = '.';
+		} else {
+		    format = HTFileFormat(filename, &encoding);
+		}
+	    } else {
+	        format = HTFileFormat(filename, &encoding);
+	    }
+	} else {
+	    format = HTFileFormat(filename, &encoding);
+	}
+	format = HTCharsetFormat(format, anchor);
+	binary = (encoding != HTAtom_for("8bit") &&
+		  encoding != HTAtom_for("7bit"));
+	if (!binary &&
+	    /*
+	    **  Force binary if we're in source, download or dump
+	    **  mode and this is not a VM/CMS server, so we don't
+	    **  get CRLF instead of LF (or CR) for newlines in text
+	    **  files.  Can't do this for VM/CMS or we'll get
+	    **  raw EBCDIC.  - FM
+	    */
+	    (format_out == WWW_SOURCE ||
+    	     format_out == HTAtom_for("www/download") ||
+	     format_out == HTAtom_for("www/dump")) &&
+	    (server_type != CMS_SERVER))
+	    binary = TRUE;
+	if (!binary && type && *type == 'I') {
+	    /*
+	    **  Force binary if we had ;type=I - FM
+	    */
+	    binary = TRUE;
+	} else if (binary && type && *type == 'A') {
+	    /*
+	    **  Force ASCII if we had ;type=A - FM
+	    */
+	    binary = FALSE;
+	}
+        if (binary != control->binary) {
+	    /*
+	    **  Act on our setting if not alread set. - FM
+	    */
+	    char * mode = binary ? "I" : "A";
+	    sprintf(command, "TYPE %s%c%c", mode, CR, LF);
+	    status = response(command);
+	    if (status != 2) {
+		init_help_message_cache();  /* to free memory */
+	        return ((status < 0) ? status : -status);
+	    }
+	    control->binary = binary;
+	}
+	switch (server_type) {
+	/*
+	**  Handle what for Lynx are special case servers, e.g.,
+	**  for which we respect RFC 1738, or which have known
+	**  conflicts in suffix mappings. - FM
+	*/
+	case VMS_SERVER:
+	  {
+	    char *cp1, *cp2;
+	    BOOL included_device = FALSE;
+	    /** Accept only Unix-style filename **/
+	    if (strchr(filename, ':') != NULL ||
+	        strchr(filename, '[') != NULL) {
+		FREE(fname);
+		init_help_message_cache();  /* to free memory */
+		NETCLOSE(control->socket);
+		control->socket = -1;
+		if (TRACE) {
+		    fprintf(stderr,
+		     "HTFTP: Rejecting path due to non-Unix-style syntax.\n");
+		}
+		return -1;
+	    }
+	    /** Handle any unescaped "/%2F" path **/
+	    if (!strncmp(filename, "//", 2)) {
+	        int i;
+		included_device = TRUE;
+		for (i = 0; filename[(i+1)]; i++)
+		    filename[i] = filename[(i+1)];
+		filename[i] = '\0';
+		if (TRACE) {
+		    fprintf(stderr, "HTFTP: Trimmed '%s'\n", filename);
+		}
+		cp = HTMake_VMS_name("", filename);
+		if (TRACE) {
+		    fprintf(stderr, "HTFTP: VMSized '%s'\n", cp);
+		}
+		if ((cp1=strrchr(cp, ']')) != NULL) {
+		    cp1++;
+		    for (i = 0; cp1[i]; i++)
+		        filename[i] = cp1[i];
+		    filename[i] = '\0';
+		    if (TRACE) {
+		        fprintf(stderr, "HTFTP: Filename '%s'\n", filename);
+		    }
+		    *cp1 = '\0';
+		    sprintf(command, "CWD %s%c%c", cp, CR, LF);
+		    status = response (command);
+		    if (status != 2) {
+		        if ((cp1=strchr(cp, '[')) != NULL) {
+			    *cp1++ = '\0';
+			    sprintf(command, "CWD %s%c%c", cp, CR, LF);
+			    status = response (command);
+			    if (status != 2) {
+			        FREE(fname);
+				init_help_message_cache(); /* to free memory */
+				NETCLOSE(control->socket);
+				control->socket = -1;
+				return ((status < 0) ? status : -status);
+			    }
+			    sprintf(command, "CWD [.%s%c%c", cp1, CR, LF);
+			    status = response (command);
+			    if (status != 2) {
+			        FREE(fname);
+				init_help_message_cache(); /* to free memory */
+				NETCLOSE(control->socket);
+				control->socket = -1;
+				return ((status < 0) ? status : -status);
+			    }
+			} else {
+		            FREE(fname);
+			    init_help_message_cache();  /* to free memory */
+			    NETCLOSE(control->socket);
+			    control->socket = -1;
+			    return ((status < 0) ? status : -status);
+			}
+		    }
+		} else if ((cp1=strchr(cp, ':')) != NULL &&
+			   strchr(cp, '[') == NULL &&
+			   strchr(cp, ']') == NULL) {
+		    cp1++;
+		    if (*cp1 != '\0') {
+			for (i = 0; cp1[i]; i++)
+			    filename[i] = cp1[i];
+			filename[i] = '\0';
+			if (TRACE) {
+			    fprintf(stderr, "HTFTP: Filename '%s'\n", filename);
+			}
+		        *cp1 = '\0';
+			strcat(cp, "[");
+			strcat(cp, filename);
+			strcat(cp, "]");
+			sprintf(command, "CWD %s%c%c", cp, CR, LF);
+			status = response (command);
+			if (status != 2) {
+			    *cp1 = '\0';
+			    strcat(cp, "[000000]");
+			    sprintf(command, "CWD %s%c%c", cp, CR, LF);
+			    status = response (command);
+			    if (status != 2) {
+			        *cp1 = '\0';
+				sprintf(command, "CWD %s%c%c", cp, CR, LF);
+				status = response (command);
+				if (status != 2) {
+			            FREE(fname);
+				    init_help_message_cache();
+				    NETCLOSE(control->socket);
+				    control->socket = -1;
+			            return ((status < 0) ? status : -status);
+				}
+			    }
+			} else {
+			    strcpy(cp, "000000");
+			    filename = cp;
+			}
+		    } 
+		} else if (0==strcmp(cp, (filename+1))) {
+		    sprintf(command, "CWD %s%c%c", cp, CR, LF);
+		    status = response (command);
+		    if (status != 2) {
+			strcat(cp, ":");
+			sprintf(command, "CWD %s%c%c", cp, CR, LF);
+			status = response (command);
+			if (status != 2) {
+			    FREE(fname);
+			    init_help_message_cache();  /* to free memory */
+			    NETCLOSE(control->socket);
+			    control->socket = -1;
+			    return ((status < 0) ? status : -status);
+			}
+		    }
+		    strcpy(cp, "000000");
+		    filename = cp;
+		}
+	    }
+	    /** Trim trailing slash if filename is not the top directory **/
+	    if (strlen(filename) > 1 && filename[strlen(filename)-1] == '/')
+	        filename[strlen(filename)-1] = '\0';
+
+#ifdef MAINTAIN_CONNECTION /* Don't need this if always new connection - F.M. */
+	    if (!included_device) {
+	        /** Get the current default VMS device:[directory] **/
+	        sprintf(command, "PWD%c%c", CR, LF);
+	        status = response (command);
+	        if (status != 2) {
+	            FREE(fname);
+		    init_help_message_cache();  /* to free memory */
+		    NETCLOSE(control->socket);
+		    control->socket = -1;
+		    return ((status < 0) ? status : -status);
+	        }
+	        /** Go to the VMS account's top directory **/
+	        if ((cp=strchr(response_text, '[')) != NULL &&
+	            (cp1=strrchr(response_text, ']')) != NULL) {
+		    sprintf(command, "CWD %s", cp);
+		    if ((cp2=strchr(cp, '.')) != NULL && cp2 < cp1)
+		        sprintf(command+(cp2-cp)+4, "]%c%c", CR, LF);
+		    else
+		        sprintf(command+(cp1-cp)+4, "]%c%c", CR, LF);
+		    status = response (command);
+		    if (status != 2) {
+		        FREE(fname);
+			init_help_message_cache();  /* to free memory */
+			NETCLOSE(control->socket);
+			control->socket = -1;
+		        return ((status < 0) ? status : -status);
+		    }
+		}
+	    }
+#endif /* MAINTAIN_CONNECTION */
+
+	    /** If we want the VMS account's top directory, list it now **/
+	    if ((included_device && 0==strcmp(filename, "000000")) ||
+	        (strlen(filename) == 1 && *filename == '/')) {
+		isDirectory = YES;
+		sprintf(command, "LIST%c%c", CR, LF);
+		status = response (command);
+		FREE(fname);
+		if (status != 1) {
+		    /* Action not started */
+		    init_help_message_cache();  /* to free memory */
+		    NETCLOSE(control->socket);
+		    control->socket = -1;
+		    return ((status < 0) ? status : -status);
+		}
+		/** Big goto! **/
+		goto listen;
+	    }
+	    /** Otherwise, go to appropriate directory and doctor filename **/
+	    if (!included_device &&
+	        (cp=strchr(filename, '/')) != NULL &&
+	        (cp1=strrchr(cp, '/')) != NULL && cp != cp1) {
+		sprintf(command, "CWD [.%s", cp+1);
+		sprintf(command+(cp1-cp)+5, "]%c%c", CR, LF);
+		while ((cp2=strrchr(command, '/')) != NULL)
+		    *cp2 = '.';
+		status = response(command);
+		if (status != 2) {
+		    FREE(fname);
+		    init_help_message_cache();  /* to free memory */
+		    NETCLOSE(control->socket);
+		    control->socket = -1;
+		    return ((status < 0) ? status : -status);
+		}
+		filename = cp1+1;
+	    } else {
+	        if (!included_device) {
+	            filename += 1;
+		}
+	    }
+	    break;
+	  }
+	case CMS_SERVER:
+	  {
+	    /*
+	    **  If we want the CMS account's top directory, or a base
+	    **  SFS or anonymous directory path (i.e., without a slash),
+	    **  list it now. FM
+	    */
+	    if ((strlen(filename) == 1 && *filename == '/') ||
+	        ((0 == strncasecomp((filename+1), "vmsysu:", 7)) &&
+		 (cp = strchr((filename+1), '.')) != NULL &&
+		 strchr(cp, '/') == NULL) ||
+		(0 == strncasecomp(filename+1, "anonymou.", 9) &&
+		 strchr(filename+1, '/') == NULL)) {
+		if (filename[1] != '\0') {
+		    sprintf(command, "CWD %s%c%c", (filename+1), CR, LF);
+		    status = response(command);
+		    if (status != 2) {
+		        /* Action not started */
+			init_help_message_cache();  /* to free memory */
+			NETCLOSE(control->socket);
+			control->socket = -1;
+			return ((status < 0) ? status : -status);
+		    }
+		}
+		isDirectory = YES;
+		if (use_list)
+		    sprintf(command, "LIST%c%c", CR, LF);
+		else
+		    sprintf(command, "NLST%c%c", CR, LF);
+		status = response (command);
+		FREE(fname);
+		if (status != 1) {
+		    /* Action not started */
+		    init_help_message_cache();  /* to free memory */
+		    NETCLOSE(control->socket);
+		    control->socket = -1;
+		    return ((status < 0) ? status : -status);
+		}
+		/** Big goto! **/
+		goto listen;
+	    }
+	    filename++;
+
+	    /** Otherwise, go to appropriate directory and adjust filename **/
+	    while ((cp = strchr(filename, '/')) != NULL) {
+	        *cp++ = '\0';
+		sprintf(command, "CWD %s%c%c", filename, CR, LF);
+		status = response(command);
+		if (status == 2) {
+		    if (*cp == '\0') {
+			isDirectory = YES;
+			if (use_list)
+			    sprintf(command, "LIST%c%c", CR, LF);
+			else
+			    sprintf(command, "NLST%c%c", CR, LF);
+			status = response (command);
+			FREE(fname);
+			if (status != 1) {
+			    /** Action not started **/
+			    init_help_message_cache();  /* to free memory */
+			    NETCLOSE(control->socket);
+			    control->socket = -1;
+			    return ((status < 0) ? status : -status);
+			}
+			/** Clear any messages from the login directory **/
+			init_help_message_cache();
+			/** Big goto! **/
+			goto listen;
+		    }
+		    filename = cp;
+		}
+	    }
+	    break;
+	  }
+	default:
+	    /** Shift for any unescaped "/%2F" path **/
+	    if (!strncmp(filename, "//", 2))
+	        filename++;
+	    break;
+	}
+	/*
+	**  Act on a file or listing request, or try to figure out
+	**  which we're dealing with if we don't know yet. - FM
+	*/
+	if (!(type) || (type && *type != 'D')) {
+	    sprintf(command, "RETR %s%c%c", filename, CR, LF);
+	    status = response(command);
+	} else {
+	    status = 5;		/* Failed status set as flag. - FM */
+	}
+	if (status != 1) {	/* Failed : try to CWD to it */
+	    /** Clear any login messages if this isn't the login directory **/
+	    if (strcmp(filename, "/"))
+	        init_help_message_cache();
+
+	    sprintf(command, "CWD %s%c%c", filename, CR, LF);
+	    status = response(command);
+
+	    if (status == 2) {  /* Successed : let's NAME LIST it */
+	        isDirectory = YES;
+	    if (use_list)
+	        sprintf(command, "LIST%c%c", CR, LF);
+	    else
+	        sprintf(command, "NLST%c%c", CR, LF);
+	    status = response (command);
+	    }
+	}
+	FREE(fname);
+	if (status != 1) {
+	    init_help_message_cache();  /* to free memory */
+	    NETCLOSE(control->socket);
+	    control->socket = -1;
+	    if (status < 0)
+	        return status;
+	    else
+	        return -status;
+	}
+    }
+
+listen:
+#ifdef LISTEN
+/*	Wait for the connection
+*/
+    {
+	struct sockaddr_in soc_address;
+        int	soc_addrlen=sizeof(soc_address);
+#ifdef SOCKS
+	if (socks_flag)
+	    status = Raccept(master_socket,
+			     (struct sockaddr *)&soc_address,
+			     (void *)&soc_addrlen);
+	else
+#endif /* SOCKS */
+	status = accept(master_socket,
+			(struct sockaddr *)&soc_address,
+			(void *)&soc_addrlen);
+	if (status < 0) {
+	    init_help_message_cache();  /* to free memory */
+	    return HTInetStatus("accept");
+	}
+	CTRACE(tfp, "TCP: Accepted new socket %d\n", status);
+	data_soc = status;
+    }
+#else
+/* @@ */
+#endif /* LISTEN */
+    if (isDirectory) {
+        status = read_directory (anchor, name, format_out, sink);
+        NETCLOSE(data_soc);
+	NETCLOSE(control->socket);
+	control->socket = -1;
+	init_help_message_cache();  /* to free memory */
+        return status;
+      /* returns HT_LOADED or error */
+    } else {
+        int rv;
+	int len;
+	HTAtom * encoding;
+	char *FileName = HTParse(name, "", PARSE_PATH + PARSE_PUNCTUATION);
+
+	/** Clear any login messages **/
+	init_help_message_cache();
+
+	/** Fake a Content-Encoding for compressed files. - FM **/
+	HTUnEscape(FileName);
+	if ((len = strlen(FileName)) > 2) {
+	    if ((FileName[len - 1] == 'Z') &&
+	        (FileName[len - 2] == '.' ||
+		 FileName[len - 2] == '-' ||
+		 FileName[len - 2] == '_')) {
+		
+		FileName[len - 2] = '\0';
+		format = HTFileFormat(FileName, &encoding);
+		format = HTCharsetFormat(format, anchor);
+		StrAllocCopy(anchor->content_type, format->name);
+		StrAllocCopy(anchor->content_encoding, "x-compress");
+		format = HTAtom_for("www/compressed");
+	    } else if ((len > 3) &&
+	    	       !strcasecomp((char *)&FileName[len - 2], "gz")) {
+		if (FileName[len - 3] == '.' ||
+		    FileName[len - 3] == '-' ||
+		    FileName[len - 3] == '_') {
+		    FileName[len - 3] = '\0';
+		    format = HTFileFormat(FileName, &encoding);
+		    format = HTCharsetFormat(format, anchor);
+		    StrAllocCopy(anchor->content_type, format->name);
+		    StrAllocCopy(anchor->content_encoding, "x-gzip");
+		    format = HTAtom_for("www/compressed");
+		}
+	    }
+	}
+	FREE(FileName);
+
+	_HTProgress ("Receiving FTP file.");
+	rv = HTParseSocket(format, format_out, anchor, data_soc, sink);
+
+	if (rv == HT_INTERRUPTED)
+	     _HTProgress("Data transfer interrupted.");
+
+	HTInitInput(control->socket);
+	/* Reset buffering to control connection DD 921208 */
+    
+	status = NETCLOSE(data_soc);
+	if (TRACE)
+	    fprintf(stderr, "HTFTP: Closing data socket %d\n", data_soc);
+	if (status < 0 && rv != HT_INTERRUPTED && rv != -1) {
+	    (void) HTInetStatus("close");	/* Comment only */
+	    data_soc = -1;			/* invalidate it */
+	} else {
+	    data_soc = -1;			/* invalidate it */
+	    status = response(NIL);		/* Pick up final reply */
+	    if (status != 2 && rv != HT_INTERRUPTED && rv != -1) {
+		init_help_message_cache();  /* to free memory */
+	        return HTLoadError(sink, 500, response_text);
+	    }
+	}
+
+	NETCLOSE(control->socket);
+	control->socket = -1;
+	init_help_message_cache();  /* to free memory */
+	return HT_LOADED;
+    }       
+} /* open_file_read */
diff --git a/WWW/Library/Implementation/HTFTP.h b/WWW/Library/Implementation/HTFTP.h
new file mode 100644
index 00000000..9fc4bf20
--- /dev/null
+++ b/WWW/Library/Implementation/HTFTP.h
@@ -0,0 +1,72 @@
+/*                                                               FTP access module for libwww
+                                   FTP ACCESS FUNCTIONS
+                                             
+   This isn't really  a valid protocol module -- it is lumped together with HTFile . That
+   could be changed easily.
+   
+   Author: Tim Berners-Lee. Public Domain. Please mail changes to timbl@info.cern.ch
+   
+ */
+#ifndef HTFTP_H
+#define HTFTP_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+#include "HTAnchor.h"
+#include "HTStream.h"
+#include "HTParse.h"
+
+#define FILE_BY_NAME 0 
+#define FILE_BY_TYPE 1
+#define FILE_BY_SIZE 2
+#define FILE_BY_DATE 3
+extern BOOLEAN HTfileSortMethod;  /* specifies the method of sorting */
+
+
+/* PUBLIC						 HTMake_VMS_name()
+**		CONVERTS WWW name into a VMS name
+** ON ENTRY:
+**	nn		Node Name (optional)
+**	fn		WWW file name
+**
+** ON EXIT:
+**	returns 	vms file specification
+**
+** Bug:	Returns pointer to static -- non-reentrant
+*/
+PUBLIC char * HTMake_VMS_name PARAMS((
+	CONST char * nn, 
+	CONST char * fn));
+
+
+/*
+
+Retrieve File from Server
+
+  ON EXIT,
+  
+  returns                 Socket number for file if good.<0 if bad.
+                         
+ */
+extern int HTFTPLoad PARAMS
+((
+  CONST char *          name,
+  HTParentAnchor *      anchor,
+  HTFormat              format_out,
+  HTStream*             sink
+));
+
+
+/*
+
+Return Host Name
+
+ */
+extern CONST char * HTHostName NOPARAMS;
+
+#endif
+
+/*
+
+   end  */
diff --git a/WWW/Library/Implementation/HTFWriter.c b/WWW/Library/Implementation/HTFWriter.c
new file mode 100644
index 00000000..8ef9ba1b
--- /dev/null
+++ b/WWW/Library/Implementation/HTFWriter.c
@@ -0,0 +1,358 @@
+/*		FILE WRITER				HTFWrite.h
+**		===========
+**
+**	This version of the stream object just writes to a C file.
+**	The file is assumed open and left open.
+**
+**	Bugs:
+**		strings written must be less than buffer size.
+*/
+
+#include "HTUtils.h"
+
+#include "HTFWriter.h"
+
+#include "HTFormat.h"
+#include "HTAlert.h"
+#include "HTFile.h"
+
+#include "LYLeaks.h"
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+/*		Stream Object
+**		------------
+*/
+
+struct _HTStream {
+	CONST HTStreamClass *	isa;
+	
+	FILE *			fp;
+	char * 			end_command;
+	char * 			remove_command;
+	BOOL			announce;
+};
+
+
+/*_________________________________________________________________________
+**
+**		B L A C K    H O L E    C L A S S
+**
+**	There is only one black hole instance shared by anyone
+**	who wanst a black hole.  These black holes don't radiate,
+**	they just absorb data.
+*/
+PRIVATE void HTBlackHole_put_character ARGS2(HTStream *, me, char, c)
+{}
+PRIVATE void HTBlackHole_put_string ARGS2(HTStream *, me, CONST char*, s)
+{}
+PRIVATE void HTBlackHole_write ARGS3(HTStream *, me, CONST char*, s, int, l)
+{}
+PRIVATE void HTBlackHole_free ARGS1(HTStream *, me)
+{}
+PRIVATE void HTBlackHole_abort ARGS2(HTStream *, me, HTError, e)
+{}
+
+
+/*	Black Hole stream
+**	-----------------
+*/
+PRIVATE CONST HTStreamClass HTBlackHoleClass =
+{		
+	"BlackHole",
+	HTBlackHole_free,
+	HTBlackHole_abort,
+	HTBlackHole_put_character, 	HTBlackHole_put_string,
+	HTBlackHole_write
+}; 
+
+PRIVATE HTStream HTBlackHoleInstance =
+{
+	&HTBlackHoleClass,
+	NULL,
+	NULL,
+	NULL,
+	NO
+};
+
+/*	Black hole craetion
+*/
+PUBLIC HTStream * HTBlackHole NOARGS
+{
+    return &HTBlackHoleInstance;
+}
+
+
+/*_________________________________________________________________________
+**
+**		F I L E     A C T I O N 	R O U T I N E S
+**  Bug:
+**	All errors are ignored.
+*/
+
+/*	Character handling
+**	------------------
+*/
+
+PRIVATE void HTFWriter_put_character ARGS2(HTStream *, me, char, c)
+{
+    putc(c, me->fp);
+}
+
+
+
+/*	String handling
+**	---------------
+**
+**	Strings must be smaller than this buffer size.
+*/
+PRIVATE void HTFWriter_put_string ARGS2(HTStream *, me, CONST char*, s)
+{
+    fputs(s, me->fp);
+}
+
+
+/*	Buffer write.  Buffers can (and should!) be big.
+**	------------
+*/
+PRIVATE void HTFWriter_write ARGS3(HTStream *, me, CONST char*, s, int, l)
+{
+    fwrite(s, 1, l, me->fp); 
+}
+
+
+
+
+/*	Free an HTML object
+**	-------------------
+**
+**	Note that the SGML parsing context is freed, but the created
+**	object is not,
+**	as it takes on an existence of its own unless explicitly freed.
+*/
+PRIVATE void HTFWriter_free ARGS1(HTStream *, me)
+{
+    fclose(me->fp);
+    if (me->end_command) {		/* Temp file */
+        _HTProgress(me->end_command);	/* Tell user what's happening */
+	system(me->end_command);
+	FREE(me->end_command);
+	if (me->remove_command) {
+	    system(me->remove_command);
+	    FREE(me->remove_command);
+	}
+    }
+
+    FREE(me);
+}
+
+/*	End writing
+*/
+
+PRIVATE void HTFWriter_abort ARGS2(HTStream *, me, HTError, e)
+{
+    fclose(me->fp);
+    if (me->end_command) {		/* Temp file */
+	if (TRACE) fprintf(stderr,
+		"HTFWriter: Aborting: file not executed.\n");
+	FREE(me->end_command);
+	if (me->remove_command) {
+	    system(me->remove_command);
+	    FREE(me->remove_command);
+	}
+    }
+
+    FREE(me);
+}
+
+
+
+/*	Structured Object Class
+**	-----------------------
+*/
+PRIVATE CONST HTStreamClass HTFWriter = /* As opposed to print etc */
+{		
+	"FileWriter",
+	HTFWriter_free,
+	HTFWriter_abort,
+	HTFWriter_put_character, 	HTFWriter_put_string,
+	HTFWriter_write
+}; 
+
+
+/*	Subclass-specific Methods
+**	-------------------------
+*/
+
+PUBLIC HTStream* HTFWriter_new ARGS1(FILE *, fp)
+{
+    HTStream* me;
+    
+    if (!fp) return NULL;
+
+    me = (HTStream*)malloc(sizeof(*me));
+    if (me == NULL) outofmem(__FILE__, "HTML_new");
+    me->isa = &HTFWriter;       
+
+    me->fp = fp;
+    me->end_command = NULL;
+    me->remove_command = NULL;
+    me->announce = NO;
+
+    return me;
+}
+
+/*	Make system command from template
+**	---------------------------------
+**
+**	See mailcap spec for description of template.
+*/
+/* @@ to be written.  sprintfs will do for now.  */
+
+
+
+/*	Take action using a system command
+**	----------------------------------
+**
+**	originally from Ghostview handling by Marc Andreseen.
+**	Creates temporary file, writes to it, executes system command
+**	on end-document.  The suffix of the temp file can be given
+**	in case the application is fussy, or so that a generic opener can
+**	be used.
+*/
+PUBLIC HTStream* HTSaveAndExecute ARGS3(
+	HTPresentation *,	pres,
+	HTParentAnchor *,	anchor,	/* Not used */
+	HTStream *,		sink)	/* Not used */
+
+#ifdef unix
+#define REMOVE_COMMAND "/bin/rm -f %s\n"
+#endif
+#ifdef VMS
+#define REMOVE_COMMAND "delete/noconfirm/nolog %s.."
+#endif
+
+#ifdef REMOVE_COMMAND
+{
+    char *fnam;
+    CONST char * suffix;
+    
+    HTStream* me;
+    
+    if (HTClientHost) {
+        HTAlert("Can't save data to file -- please run WWW locally");
+	return HTBlackHole();
+    }
+    
+    me = (HTStream*)malloc(sizeof(*me));
+    if (me == NULL) outofmem(__FILE__, "Save and execute");
+    me->isa = &HTFWriter;  
+    
+    /* Save the file under a suitably suffixed name */
+    
+    suffix = HTFileSuffix(pres->rep);
+
+    fnam = (char *)malloc (L_tmpnam + 16 + strlen(suffix));
+    tmpnam (fnam);
+    if (suffix) strcat(fnam, suffix);
+    
+    me->fp = fopen (fnam, "w");
+    if (!me->fp) {
+	HTAlert("Can't open temporary file!");
+        FREE(fnam);
+	FREE(me);
+	return NULL;
+    }
+
+/*	Make command to process file
+*/
+    me->end_command = (char *)malloc (
+    			(strlen (pres->command) + 10+ 3*strlen(fnam))
+    			 * sizeof (char));
+    if (me == NULL) outofmem(__FILE__, "SaveAndExecute");
+    
+    sprintf (me->end_command, pres->command, fnam, fnam, fnam);
+
+    me->remove_command = NULL;	/* If needed, put into end_command */
+#ifdef NOPE
+/*	Make command to delete file
+*/ 
+    me->remove_command = (char *)malloc (
+    			(strlen (REMOVE_COMMAND) + 10+ strlen(fnam))
+    			 * sizeof (char));
+    if (me == NULL) outofmem(__FILE__, "SaveAndExecute");
+    
+    sprintf (me->remove_command, REMOVE_COMMAND, fnam);
+#endif
+
+    me->announce = NO;
+    FREE(fnam);
+    return me;
+}
+
+#else	/* can do remove */
+{ return NULL; }
+#endif
+
+
+/*	Save Locally
+**	------------
+**
+**  Bugs:
+**	GUI Apps should open local Save panel here really.
+**
+*/
+PUBLIC HTStream* HTSaveLocally ARGS3(
+	HTPresentation *,	pres,
+	HTParentAnchor *,	anchor,	/* Not used */
+	HTStream *,		sink)	/* Not used */
+
+{
+    char *fnam;
+    char *answer;
+    CONST char * suffix;
+    
+    HTStream* me;
+    
+    if (HTClientHost) {
+        HTAlert("Can't save data to file -- please run WWW locally");
+	return HTBlackHole();
+    }
+    
+    me = (HTStream*)malloc(sizeof(*me));
+    if (me == NULL) outofmem(__FILE__, "SaveLocally");
+    me->isa = &HTFWriter;  
+    me->end_command = NULL;
+    me->remove_command = NULL;	/* If needed, put into end_command */
+    me->announce = YES;
+    
+    /* Save the file under a suitably suffixed name */
+    
+    suffix = HTFileSuffix(pres->rep);
+
+    fnam = (char *)malloc (L_tmpnam + 16 + strlen(suffix));
+    tmpnam (fnam);
+    if (suffix) strcat(fnam, suffix);
+    
+    /*	Save Panel */
+    answer = HTPrompt("Give name of file to save in", fnam);
+    
+    FREE(fnam);
+    
+    me->fp = fopen (answer, "w");
+    if (!me->fp) {
+	HTAlert("Can't open local file to write into.");
+        FREE(answer);
+	FREE(me);
+	return NULL;
+    }
+
+    FREE(answer);
+    return me;
+}
+
+
+
+/*	Format Converter using system command
+**	-------------------------------------
+*/
diff --git a/WWW/Library/Implementation/HTFWriter.h b/WWW/Library/Implementation/HTFWriter.h
new file mode 100644
index 00000000..052bdd7e
--- /dev/null
+++ b/WWW/Library/Implementation/HTFWriter.h
@@ -0,0 +1,37 @@
+/*                                                                     File Writer for libwww
+                                      C FILE WRITER
+                                             
+   It is useful to have both FWriter and Writer for environments in which fdopen() doesn't
+   exist for example.
+   
+ */
+#ifndef HTFWRITE_H
+#define HTFWRITE_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+#include "HTStream.h"
+/*#include <stdio.h> included by HTUtils.h -- FM */
+#include "HTFormat.h"
+
+#ifdef SHORT_NAMES
+#define HTFWriter_new   HTFWnew
+#endif
+
+extern HTStream * HTFWriter_new PARAMS((FILE * fp));
+
+extern HTStream * HTSaveAndExecute PARAMS((
+        HTPresentation *        pres,
+        HTParentAnchor *        anchor, /* Not used */
+        HTStream *              sink));
+
+extern HTStream * HTSaveLocally PARAMS((
+        HTPresentation *        pres,
+        HTParentAnchor *        anchor, /* Not used */
+        HTStream *              sink));
+
+#endif
+/*
+
+   end */
diff --git a/WWW/Library/Implementation/HTFile.c b/WWW/Library/Implementation/HTFile.c
new file mode 100644
index 00000000..9148f5d1
--- /dev/null
+++ b/WWW/Library/Implementation/HTFile.c
@@ -0,0 +1,1871 @@
+/*			File Access				HTFile.c
+**			===========
+**
+**	This is unix-specific code in general, with some VMS bits.
+**	These are routines for file access used by browsers.
+**	Development of this module for Unix DIRED_SUPPORT in Lynx
+**	 regrettably has has been conducted in a manner with now
+**	 creates a major impediment for hopes of adapting Lynx to
+**	 a newer version of the library.
+**
+** History:
+**	   Feb 91	Written Tim Berners-Lee CERN/CN
+**	   Apr 91	vms-vms access included using DECnet syntax
+**	26 Jun 92 (JFG) When running over DECnet, suppressed FTP.
+**			Fixed access bug for relative names on VMS.
+**	   Sep 93 (MD)  Access to VMS files allows sharing.
+**	15 Nov 93 (MD)	Moved HTVMSname to HTVMSUTILS.C
+**      27 Dec 93 (FM)  FTP now works with VMS hosts.
+**			FTP path must be Unix-style and cannot include
+**			 the device or top directory.
+*/
+
+#ifndef VMS
+/* #define LONG_LIST */ /* Define this for long style unix listings (ls -l) */
+/* #define NO_PARENT_DIR_REFERENCE */ /* Define this for no parent links */
+#endif /* !VMS */
+
+#include "HTUtils.h"
+#include "tcp.h"
+#include "HTFile.h"		/* Implemented here */
+#ifdef VMS
+#include <stat.h>
+#endif /* VMS */
+
+#ifndef VMS
+#ifdef LONG_LIST
+#include <pwd.h>
+#include <grp.h>
+#endif /* LONG_LIST */
+#endif /* !VMS */
+  
+#define INFINITY 512		/* file name length @@ FIXME */
+#define MULTI_SUFFIX ".multi"   /* Extension for scanning formats */
+
+#define HT_EM_SPACE ((char)2)
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+#ifdef VMS
+#include "HTVMSUtils.h"
+#endif /* VMS */
+
+#include "HTParse.h"
+#include "HTTCP.h"
+#ifndef DECNET
+#include "HTFTP.h"
+#endif /* !DECNET */
+#include "HTAnchor.h"
+#include "HTAtom.h"
+#include "HTWriter.h"
+#include "HTFWriter.h"
+#include "HTInit.h"
+#include "HTBTree.h"
+#include "HTAlert.h"
+#include "HTCJK.h"
+
+#include "LYexit.h"
+#include "LYLeaks.h"
+
+typedef struct _HTSuffix {
+	char *		suffix;
+	HTAtom *	rep;
+	HTAtom *	encoding;
+	float		quality;
+} HTSuffix;
+
+#ifndef NGROUPS
+#ifdef NGROUPS_MAX
+#define NGROUPS NGROUPS_MAX
+#else
+#define NGROUPS 32
+#endif /* NGROUPS_MAX */
+#endif /* NGROUPS */
+
+
+#ifdef USE_DIRENT		/* Set this for Sys V systems */
+#define STRUCT_DIRENT struct dirent
+#else
+#define STRUCT_DIRENT struct direct
+#endif /* USE_DIRENT */
+
+#include "HTML.h"		/* For directory object building */
+
+#define PUTC(c) (*target->isa->put_character)(target, c)
+#define PUTS(s) (*target->isa->put_string)(target, s)
+#define START(e) (*target->isa->start_element)(target, e, 0, 0, 0)
+#define END(e) (*target->isa->end_element)(target, e, 0)
+#define FREE_TARGET (*target->isa->_free)(target)
+struct _HTStructured {
+	CONST HTStructuredClass *	isa;
+	/* ... */
+};
+
+
+/*                   Controlling globals
+**
+*/
+
+PUBLIC int HTDirAccess = HT_DIR_OK;
+
+#ifdef DIRED_SUPPORT
+PUBLIC int HTDirReadme = HT_DIR_README_NONE;
+#define FILES_FIRST 1
+#define MIXED_STYLE 2
+extern BOOLEAN lynx_edit_mode;
+extern BOOLEAN dir_list_style;
+#else
+PUBLIC int HTDirReadme = HT_DIR_README_TOP;
+#endif /* DIRED_SUPPORT */
+
+extern int current_char_set;
+extern char *LYchar_set_names[];
+extern BOOL HTPassEightBitRaw;
+extern HTCJKlang HTCJK;
+
+PRIVATE char *HTMountRoot = "/Net/";		/* Where to find mounts */
+#ifdef VMS
+PRIVATE char *HTCacheRoot = "/WWW$SCRATCH";   /* Where to cache things */
+#else
+PRIVATE char *HTCacheRoot = "/tmp/W3_Cache_";   /* Where to cache things */
+#endif /* VMS */
+
+/* PRIVATE char *HTSaveRoot  = "$(HOME)/WWW/";*/    /* Where to save things */
+
+
+/*	Suffix registration
+*/
+
+PRIVATE HTList * HTSuffixes = 0;
+PRIVATE HTSuffix no_suffix = { "*", NULL, NULL, 1.0 };
+PRIVATE HTSuffix unknown_suffix = { "*.*", NULL, NULL, 1.0};
+
+/*
+ *	To free up the suffixes at program exit.
+ */
+PRIVATE void free_suffixes NOPARAMS;
+
+#ifdef LONG_LIST
+PRIVATE void LYListFmtParse ARGS5(
+	char *,		fmtstr,
+	char *,		file,
+	HTStructured *,	target,
+	char *,		entry,
+	char *,		tail)
+{
+	char c;
+	char *s;
+	char *end;
+	char *start;
+	char *str = NULL;
+	struct stat st;
+	char buf[512];
+	char fmt[512];
+	char type;
+	struct passwd *p;
+	struct group *g;
+	time_t now;
+	char *datestr;
+	int len;
+#define SEC_PER_YEAR	(60 * 60 * 24 * 365)
+	static char *pbits[] = { "---", "--x", "-w-", "-wx",
+		"r--", "r-x", "rw-", "rwx", 0 };
+	static char *psbits[] = { "--S", "--s", "-wS", "-ws",
+		"r-S", "r-s", "rwS", "rws", 0 };
+#define PBIT(a, n, s)  (s) ? psbits[((a) >> (n)) & 0x7] : \
+	pbits[((a) >> (n)) & 0x7]
+
+	if (lstat(file, &st) < 0)
+		fmtstr = "%a";	/* can't stat so just do anchor */
+	
+	StrAllocCopy(str, fmtstr);
+	s = str;
+	end = str + strlen(str);
+	START(HTML_PRE);
+	while (*s) {
+		start = s;
+		while (*s) {
+			if (*s == '%') {
+				if (*(s+1) == '%') /* literal % */
+					s++;
+				else
+					break;
+			}
+			s++;
+		}
+		/* s is positioned either at a % or at \0 */
+		*s = '\0';
+		if (s > start) {	/* some literal chars. */
+			PUTS(start);
+		}
+		if (s == end)
+			break;
+		start = ++s;
+		while (isdigit(*s) || *s == '.' || *s == '-')
+			s++;
+		c = *s;		/* the format char. or \0 */
+		*s = '\0';
+
+		switch (c) {
+		case '\0':
+			break;
+		
+		case 'A':
+		case 'a':	/* anchor */
+			HTDirEntry(target, tail, entry);
+			sprintf(fmt, "%%%ss", start);
+			sprintf(buf, fmt, entry);
+			PUTS(buf);
+			END(HTML_A);
+			if (c != 'A' && (st.st_mode & S_IFMT) == S_IFLNK &&
+			    (len = readlink(file, buf, sizeof(buf))) >= 0) {
+				PUTS(" -> ");
+				buf[len] = '\0';
+				PUTS(buf);
+			}
+			*buf = '\0';
+			break;
+
+		case 'd':	/* date */
+			now = time(0);
+			datestr = ctime(&st.st_mtime);
+			if ((now - st.st_mtime) < SEC_PER_YEAR/2)
+				/* MMM DD HH:MM */
+				sprintf(buf, "%.12s", datestr + 4);
+			else
+				/* MMM DD  YYYY */
+				sprintf(buf, "%.7s %.4s ", datestr + 4,
+				  datestr + 20);
+			sprintf(fmt, "%%%ss", start);
+			sprintf(buf, fmt, buf);
+			break;
+
+		case 's':	/* size in bytes */
+			sprintf(fmt, "%%%sd", start);
+			sprintf(buf, fmt, st.st_size);
+			break;
+
+		case 'K':	/* size in Kilobytes but not for directories */
+			if ((st.st_mode & S_IFMT) == S_IFDIR) {
+				sprintf(fmt, "%%%ss ", start);
+				sprintf(buf, fmt, "");
+				break;
+			}
+			/* FALL THROUGH */
+		case 'k':	/* size in Kilobytes */
+			sprintf(fmt, "%%%sdK", start);
+			sprintf(buf, fmt, (st.st_size+1023)/1024);
+			break;
+
+		case 'p':	/* unix-style permission bits */
+			switch(st.st_mode & S_IFMT) {
+			case S_IFIFO: type = 'p'; break;
+			case S_IFCHR: type = 'c'; break;
+			case S_IFDIR: type = 'd'; break;
+			case S_IFBLK: type = 'b'; break;
+			case S_IFREG: type = '-'; break;
+			case S_IFLNK: type = 'l'; break;
+#ifdef S_IFSOCK
+			case S_IFSOCK: type = 's'; break;
+#endif /* S_IFSOCK */
+			default: type = '?'; break;
+			}
+			sprintf(buf, "%c%s%s%s", type,
+			  PBIT(st.st_mode, 6, st.st_mode & S_ISUID),
+			  PBIT(st.st_mode, 3, st.st_mode & S_ISGID),
+			  PBIT(st.st_mode, 0, 0));
+			sprintf(fmt, "%%%ss", start);
+			sprintf(buf, fmt, buf);
+			break;
+
+		case 'o':	/* owner */
+			sprintf(fmt, "%%%ss", start);
+			p = getpwuid(st.st_uid);
+			if (p) {
+				sprintf(fmt, "%%%ss", start);
+				sprintf(buf, fmt, p->pw_name);
+			} else {
+				
+				sprintf(fmt, "%%%sd", start);
+				sprintf(buf, fmt, st.st_uid);
+			}
+			break;
+
+		case 'g':	/* group */
+			g = getgrgid(st.st_gid);
+			if (g) {
+				sprintf(fmt, "%%%ss", start);
+				sprintf(buf, fmt, g->gr_name);
+			} else {
+				sprintf(fmt, "%%%sd", start);
+				sprintf(buf, fmt, st.st_gid);
+			}
+			break;
+
+		case 'l':	/* link count */
+			sprintf(fmt, "%%%sd", start);
+			sprintf(buf, fmt, st.st_nlink);
+			break;
+
+		default:
+			fprintf(stderr,
+			"Unknown format character `%c' in list format\n", c);
+			break;
+		}
+		PUTS(buf);
+
+		s++;
+	}
+	END(HTML_PRE);
+	PUTS("\n");
+	FREE(str);
+}
+#endif /* LONG_LIST */
+
+/*	Define the representation associated with a file suffix
+**	-------------------------------------------------------
+**
+**	Calling this with suffix set to "*" will set the default
+**	representation.
+**	Calling this with suffix set to "*.*" will set the default
+**	representation for unknown suffix files which contain a ".".
+**
+**	If filename suffix is already defined its previous
+**	definition is overridden.
+*/
+PUBLIC void HTSetSuffix ARGS4(
+	CONST char *,	suffix,
+	CONST char *,	representation,
+	CONST char *,	encoding,
+	float,		value)
+{
+    HTSuffix * suff;
+
+    if (strcmp(suffix, "*") == 0)
+        suff = &no_suffix;
+    else if (strcmp(suffix, "*.*") == 0)
+        suff = &unknown_suffix;
+    else {
+	HTList *cur = HTSuffixes;
+
+	while (NULL != (suff = (HTSuffix*)HTList_nextObject(cur))) {
+	    if (suff->suffix && 0 == strcmp(suff->suffix, suffix))
+		break;
+	}
+	if (!suff) { /* Not found -- create a new node */
+	    suff = (HTSuffix *) calloc(1, sizeof(HTSuffix));
+	    if (suff == NULL)
+	        outofmem(__FILE__, "HTSetSuffix");
+
+	    /*
+             *	Memory leak fixed.
+	     *	05-28-94 Lynx 2-3-1 Garrett Arch Blythe
+	     */	
+	    if (!HTSuffixes)	{
+		HTSuffixes = HTList_new();
+		atexit(free_suffixes);
+	    }
+
+	    HTList_addObject(HTSuffixes, suff);
+	
+	    StrAllocCopy(suff->suffix, suffix);
+	}
+    }
+
+    suff->rep = HTAtom_for(representation);
+   
+    /*
+     *	Memory leak fixed.
+     *	05-28-94 Lynx 2-3-1 Garrett Arch Blythe
+     *	Invariant code removed.
+     */
+    suff->encoding = HTAtom_for(encoding);
+    
+    suff->quality = value;
+}
+
+/*
+**	Purpose:	Free all added suffixes.
+**	Arguments:	void
+**	Return Value:	void
+**	Remarks/Portability/Dependencies/Restrictions:
+**		To be used at program exit.
+**	Revision History:
+**		05-28-94	created Lynx 2-3-1 Garrett Arch Blythe
+*/
+PRIVATE void free_suffixes NOARGS
+{
+    HTSuffix * suff = NULL;
+
+    /*
+     *  Loop through all suffixes.
+     */
+    while (!HTList_isEmpty(HTSuffixes)) {
+	/*
+	 *  Free off each item and its members if need be.
+	 */
+	suff = (HTSuffix *)HTList_removeLastObject(HTSuffixes);
+	FREE(suff->suffix);
+	FREE(suff);
+    }
+    /*
+     *  Free off the list itself.
+     */
+    HTList_delete(HTSuffixes);
+    HTSuffixes = NULL;
+}
+
+
+/*	Send README file
+**
+**  If a README file exists, then it is inserted into the document here.
+*/
+#ifdef GOT_READ_DIR
+PRIVATE void do_readme ARGS2(HTStructured *, target, CONST char *, localname)
+{ 
+    FILE * fp;
+    char * readme_file_name = 
+	malloc(strlen(localname)+ 1 + strlen(HT_DIR_README_FILE) + 1);
+    strcpy(readme_file_name, localname);
+    strcat(readme_file_name, "/");
+    strcat(readme_file_name, HT_DIR_README_FILE);
+    
+    fp = fopen(readme_file_name,  "r");
+    
+    if (fp) {
+	HTStructuredClass targetClass;
+	
+	targetClass =  *target->isa;	/* (Can't init agregate in K&R) */
+	START(HTML_PRE);
+	for (;;){
+	    char c = fgetc(fp);
+	    if (c == (char)EOF) break;
+#ifdef NOTDEFINED
+	    switch (c) {
+	    	case '&':
+		case '<':
+		case '>':
+			PUTC('&');
+			PUTC('#');
+			PUTC((char)(c / 10));
+			PUTC((char) (c % 10));
+			PUTC(';');
+			break;
+/*	    	case '\n':
+			PUTC('\r');    
+Bug removed thanks to joe@athena.mit.edu */			
+		default:
+			PUTC(c);
+	    }
+#else
+	    PUTC(c);
+#endif /* NOTDEFINED */
+	}
+	END(HTML_PRE);
+	fclose(fp);
+    } 
+}
+#endif /* GOT_READ_DIR */
+
+
+/*	Make the cache file name for a W3 document
+**	------------------------------------------
+**	Make up a suitable name for saving the node in
+**
+**	E.g.	/tmp/WWW_Cache_news/1234@cernvax.cern.ch
+**		/tmp/WWW_Cache_http/crnvmc/FIND/xx.xxx.xx
+**
+** On exit,
+**	returns	a malloc'ed string which must be freed by the caller.
+*/
+PUBLIC char * HTCacheFileName ARGS1(
+	CONST char *,	name)
+{
+    char * access = HTParse(name, "", PARSE_ACCESS);
+    char * host = HTParse(name, "", PARSE_HOST);
+    char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION);
+    
+    char * result;
+    result = (char *)malloc(
+	    strlen(HTCacheRoot)+strlen(access)
+	    +strlen(host)+strlen(path)+6+1);
+    if (result == NULL)
+        outofmem(__FILE__, "HTCacheFileName");
+    sprintf(result, "%s/WWW/%s/%s%s", HTCacheRoot, access, host, path);
+    FREE(path);
+    FREE(access);
+    FREE(host);
+    return result;
+}
+
+
+/*	Open a file for write, creating the path
+**	----------------------------------------
+*/
+#ifdef NOT_IMPLEMENTED
+PRIVATE int HTCreatePath ARGS1(CONST char *,path)
+{
+    return -1;
+}
+#endif /* NOT_IMPLEMENTED */
+
+/*	Convert filenames between local and WWW formats
+**	-----------------------------------------------
+**	Make up a suitable name for saving the node in
+**
+**	E.g.	$(HOME)/WWW/news/1234@cernvax.cern.ch
+**		$(HOME)/WWW/http/crnvmc/FIND/xx.xxx.xx
+**
+** On exit,
+**	returns	a malloc'ed string which must be freed by the caller.
+*/
+PUBLIC char * HTLocalName ARGS1(
+	CONST char *,	name)
+{
+    char * access = HTParse(name, "", PARSE_ACCESS);
+    char * host = HTParse(name, "", PARSE_HOST);
+    char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION);
+    
+    HTUnEscape(path);	/* Interpret % signs */
+    
+    if (0 == strcmp(access, "file")) { /* local file */
+        FREE(access);	
+	if ((0 == strcasecomp(host, HTHostName())) ||
+	    (0 == strcasecomp(host, "localhost")) || !*host) {
+	    FREE(host);
+	    if (TRACE)
+	        fprintf(stderr, "Node `%s' means path `%s'\n", name, path);
+	    return(path);
+	} else {
+	    char * result = (char *)malloc(
+	    			strlen("/Net/")+strlen(host)+strlen(path)+1);
+              if (result == NULL)
+	          outofmem(__FILE__, "HTLocalName");
+	    sprintf(result, "%s%s%s", "/Net/", host, path);
+	    FREE(host);
+	    FREE(path);
+	    if (TRACE)
+	        fprintf(stderr, "Node `%s' means file `%s'\n", name, result);
+	    return result;
+	}
+    } else {  /* other access */
+	char * result;
+#ifdef VMS
+        char * home =  getenv("HOME");
+	if (!home) 
+	    home = HTCacheRoot; 
+	else
+   	    home = HTVMS_wwwName(home);
+#else
+        CONST char * home =  (CONST char*)getenv("HOME");
+	if (!home)
+	    home = "/tmp"; 
+#endif /* VMS */
+	result = (char *)malloc(
+		strlen(home)+strlen(access)+strlen(host)+strlen(path)+6+1);
+        if (result == NULL)
+            outofmem(__FILE__, "HTLocalName");
+	sprintf(result, "%s/WWW/%s/%s%s", home, access, host, path);
+	FREE(path);
+	FREE(access);
+	FREE(host);
+	return result;
+    }
+}
+
+
+/*	Make a WWW name from a full local path name
+**
+** Bugs:
+**	At present, only the names of two network root nodes are hand-coded
+**	in and valid for the NeXT only. This should be configurable in
+**	the general case.
+*/
+
+PUBLIC char * WWW_nameOfFile ARGS1(
+	CONST char *,	name)
+{
+    char * result;
+#ifdef NeXT
+    if (0 == strncmp("/private/Net/", name, 13)) {
+	result = (char *)malloc(7+strlen(name+13)+1);
+	if (result == NULL)
+	    outofmem(__FILE__, "WWW_nameOfFile");
+	sprintf(result, "file://%s", name+13);
+    } else
+#endif /* NeXT */
+    if (0 == strncmp(HTMountRoot, name, 5)) {
+	result = (char *)malloc(7+strlen(name+5)+1);
+	if (result == NULL)
+	    outofmem(__FILE__, "WWW_nameOfFile");
+	sprintf(result, "file://%s", name+5);
+    } else {
+        result = (char *)malloc(7+strlen(HTHostName())+strlen(name)+1);
+	if (result == NULL)
+	    outofmem(__FILE__, "WWW_nameOfFile");
+	sprintf(result, "file://%s%s", HTHostName(), name);
+    }
+    if (TRACE)
+        fprintf(stderr, "File `%s'\n\tmeans node `%s'\n", name, result);
+    return result;
+}
+
+
+/*	Determine a suitable suffix, given the representation
+**	-----------------------------------------------------
+**
+** On entry,
+**	rep	is the atomized MIME style representation
+**
+** On exit,
+**	returns	a pointer to a suitable suffix string if one has been
+**		found, else "".
+*/
+PUBLIC CONST char * HTFileSuffix ARGS1(
+	HTAtom*,	rep)
+{
+    HTSuffix * suff;
+    int n;
+    int i;
+
+#define NO_INIT  /* dont init anymore since I do it in Lynx at startup */
+#ifndef NO_INIT    
+    if (!HTSuffixes)
+        HTFileInit();
+#endif /* !NO_INIT */
+    n = HTList_count(HTSuffixes);
+    for (i = 0; i < n; i++) {
+	suff = (HTSuffix *)HTList_objectAt(HTSuffixes, i);
+	if (suff->rep == rep) {
+	    return suff->suffix;		/* OK -- found */
+	}
+    }
+    return "";		/* Dunno */
+}
+
+
+/*	Determine file format from file name
+**	------------------------------------
+**
+**	This version will return the representation and also set
+**	a variable for the encoding.
+**
+**	It will handle for example  x.txt, x.txt,Z, x.Z
+*/
+
+PUBLIC HTFormat HTFileFormat ARGS2(
+	CONST char *,	filename,
+	HTAtom **,	pencoding)
+{
+    HTSuffix * suff;
+    int n;
+    int i;
+    int lf;
+#ifdef VMS
+    char *semicolon = NULL;
+#endif /* VMS */
+    extern char LYforce_HTML_mode;
+
+    if (LYforce_HTML_mode) {
+        LYforce_HTML_mode = FALSE;
+        return WWW_HTML;
+    }
+
+#ifdef VMS
+    /*
+     * Trim at semicolon if a version number was
+     * included, so it doesn't interfere with the
+     * code for getting the MIME type. - FM
+     */
+    if ((semicolon = strchr(filename, ';')) != NULL)
+        *semicolon = '\0';
+#endif /* VMS */
+
+#ifndef NO_INIT    
+    if (!HTSuffixes)
+        HTFileInit();
+#endif /* !NO_INIT */
+    *pencoding = NULL;
+    lf  = strlen(filename);
+    n = HTList_count(HTSuffixes);
+    for (i = 0; i < n; i++) {
+        int ls;
+	suff = (HTSuffix *)HTList_objectAt(HTSuffixes, i);
+	ls = strlen(suff->suffix);
+	if ((ls <= lf) && 0 == strcasecomp(suff->suffix, filename + lf - ls)) {
+	    int j;
+	    *pencoding = suff->encoding;
+	    if (suff->rep) {
+#ifdef VMS
+		if (semicolon != NULL)
+		    *semicolon = ';';
+#endif /* VMS */
+	        return suff->rep;		/* OK -- found */
+	    }
+	    for (j = 0; j < n; j++) {  /* Got encoding, need representation */
+		int ls2;
+		suff = (HTSuffix *)HTList_objectAt(HTSuffixes, j);
+		ls2 = strlen(suff->suffix);
+		if ((ls <= lf) && 0 == strncasecomp(
+			suff->suffix, filename + lf - ls -ls2, ls2)) {
+		    if (suff->rep) {
+#ifdef VMS
+			if (semicolon != NULL)
+			    *semicolon = ';';
+#endif /* VMS */
+		        return suff->rep;
+		    }
+		}
+	    }
+	    
+	}
+    }
+    
+    /* defaults tree */
+    
+    suff = strchr(filename, '.') ? 	/* Unknown suffix */
+    	 ( unknown_suffix.rep ? &unknown_suffix : &no_suffix)
+	 : &no_suffix;
+	 
+    /* set default encoding unless found with suffix already */
+    if (!*pencoding)
+        *pencoding = suff->encoding ? suff->encoding
+				    : HTAtom_for("binary");
+#ifdef VMS
+    if (semicolon != NULL)
+        *semicolon = ';';
+#endif /* VMS */
+    return suff->rep ? suff->rep : WWW_BINARY;
+}
+
+
+/*	Revise the file format in relation to the Lynx charset. - FM
+**	-------------------------------------------------------
+**
+**	This checks the format associated with an anchor for
+**	an extended MIME Content-Type, and if a charset is
+**	indicated, sets Lynx up for proper handling in relation
+**	to the currently selected character set. - FM
+*/
+PUBLIC HTFormat HTCharsetFormat ARGS2(
+	HTFormat,		format,
+	HTParentAnchor *,	anchor)
+
+{
+    char *cp = NULL, *cp1, *cp2;
+    int i;
+
+    FREE(anchor->charset);
+    StrAllocCopy(cp, format->name);
+    for (i = 0; cp[i]; i++)
+	cp[i] = TOLOWER(cp[i]);
+    if (((cp1 = strchr(cp, ';')) != NULL) &&
+	(cp2 = strstr(cp1, "charset")) != NULL) {
+	if (TRACE)
+	    fprintf(stderr,
+	    	    "HTCharsetFormat: Extended MIME Content-Type is %s\n",
+		    format->name);
+	cp2 += 7;
+	while (*cp2 == ' ' || *cp2 == '=')
+	    cp2++;
+	if (!strncmp(cp2, "us-ascii", 8) ||
+	    !strncmp(cp2, "iso-8859-1", 10)) {
+	    *cp1 = '\0';
+	    format = HTAtom_for(cp);
+	    StrAllocCopy(anchor->charset, "iso-8859-1");
+	    HTCJK = NOCJK;
+	} else if (!strncmp(cp2, "iso-8859-2", 10) &&
+		   !strncmp(LYchar_set_names[current_char_set],
+			    "ISO Latin 2", 11)) {
+	    *cp1 = '\0';
+	    format = HTAtom_for(cp);
+	    StrAllocCopy(anchor->charset, "iso-8859-2");
+	    HTPassEightBitRaw = TRUE;
+	} else if (!strncmp(cp2, "iso-8859-", 9) &&
+		   !strncmp(LYchar_set_names[current_char_set],
+			    "Other ISO Latin", 15)) {
+	    /*
+	    **  Hope it's a match, for now. - FM
+	    */
+	    *cp1 = '\0';
+	    format = HTAtom_for(cp);
+	    StrAllocCopy(anchor->charset, "iso-8859- ");
+	    anchor->charset[9] = cp2[9];
+	    HTPassEightBitRaw = TRUE;
+	    HTAlert(anchor->charset);
+	} else if (!strncmp(cp2, "koi8-r", 6) &&
+		   !strncmp(LYchar_set_names[current_char_set],
+			    "KOI8-R character set", 20)) {
+	    *cp1 = '\0';
+	    format = HTAtom_for(cp);
+	    StrAllocCopy(anchor->charset, "koi8-r");
+	    HTPassEightBitRaw = TRUE;
+	} else if (!strncmp(cp2, "euc-jp", 6) &&
+		   HTCJK == JAPANESE) {
+	    *cp1 = '\0';
+	    format = HTAtom_for(cp);
+	    StrAllocCopy(anchor->charset, "euc-jp");
+	} else if (!strncmp(cp2, "shift_jis", 9) &&
+		   HTCJK == JAPANESE) {
+	    *cp1 = '\0';
+	    format = HTAtom_for(cp);
+	    StrAllocCopy(anchor->charset, "shift_jis");
+	} else if (!strncmp(cp2, "iso-2022-jp", 11) &&
+		   HTCJK == JAPANESE) {
+	    *cp1 = '\0';
+	    format = HTAtom_for(cp);
+	    StrAllocCopy(anchor->charset, "iso-2022-jp");
+	} else if (!strncmp(cp2, "iso-2022-jp-2", 13) &&
+		   HTCJK == JAPANESE) {
+	    *cp1 = '\0';
+	    format = HTAtom_for(cp);
+	    StrAllocCopy(anchor->charset, "iso-2022-jp-2");
+	} else if (!strncmp(cp2, "euc-kr", 6) &&
+		   HTCJK == KOREAN) {
+	    *cp1 = '\0';
+	    format = HTAtom_for(cp);
+	    StrAllocCopy(anchor->charset, "euc-kr");
+	} else if (!strncmp(cp2, "iso-2022-kr", 11) &&
+		   HTCJK == KOREAN) {
+	    *cp1 = '\0';
+	    format = HTAtom_for(cp);
+	    StrAllocCopy(anchor->charset, "iso-2022-kr");
+	} else if ((!strncmp(cp2, "big5", 4) ||
+		    !strncmp(cp2, "cn-big5", 7)) &&
+		   HTCJK == TAIPEI) {
+	    *cp1 = '\0';
+	    format = HTAtom_for(cp);
+	    StrAllocCopy(anchor->charset, "big5");
+	} else if (!strncmp(cp2, "euc-cn", 6) &&
+		   HTCJK == CHINESE) {
+	    *cp1 = '\0';
+	    format = HTAtom_for(cp);
+	    StrAllocCopy(anchor->charset, "euc-cn");
+	} else if ((!strncmp(cp2, "gb2312", 6) ||
+		    !strncmp(cp2, "cn-gb", 5)) &&
+		   HTCJK == CHINESE) {
+	    *cp1 = '\0';
+	    format = HTAtom_for(cp);
+	    StrAllocCopy(anchor->charset, "gb2312");
+	} else if (!strncmp(cp2, "iso-2022-cn", 11) &&
+		   HTCJK == CHINESE) {
+	    *cp1 = '\0';
+	    format = HTAtom_for(cp);
+	    StrAllocCopy(anchor->charset, "iso-2022-cn");
+	}
+    }
+    FREE(cp);
+
+    return format;
+}
+
+
+/*	Determine value from file name
+**	------------------------------
+**
+*/
+
+PUBLIC float HTFileValue ARGS1(
+	CONST char *,	filename)
+{
+    HTSuffix * suff;
+    int n;
+    int i;
+    int lf = strlen(filename);
+
+#ifndef NO_INIT    
+    if (!HTSuffixes)
+        HTFileInit();
+#endif /* !NO_INIT */
+    n = HTList_count(HTSuffixes);
+    for (i = 0; i < n; i++) {
+        int ls;
+	suff = (HTSuffix *)HTList_objectAt(HTSuffixes, i);
+	ls = strlen(suff->suffix);
+	if ((ls <= lf) && 0==strcmp(suff->suffix, filename + lf - ls)) {
+	    if (TRACE)
+	        fprintf(stderr, "File: Value of %s is %.3f\n",
+			        filename, suff->quality);
+	    return suff->quality;		/* OK -- found */
+	}
+    }
+    return 0.3;		/* Dunno! */
+}
+
+
+/*	Determine write access to a file
+**	--------------------------------
+**
+** On exit,
+**	return value	YES if file can be accessed and can be written to.
+**
+** Bugs:
+**	1.	No code for non-unix systems.
+**	2.	Isn't there a quicker way?
+*/
+
+#ifdef VMS
+#define NO_GROUPS
+#endif /* VMS */
+#ifdef NO_UNIX_IO
+#define NO_GROUPS
+#endif /* NO_UNIX_IO */
+#ifdef PCNFS
+#define NO_GROUPS
+#endif /* PCNFS */
+
+PUBLIC BOOL HTEditable ARGS1(
+	CONST char *,	filename)
+{
+#ifdef NO_GROUPS
+    return NO;		/* Safe answer till we find the correct algorithm */
+#else
+#ifdef NeXT
+    int		groups[NGROUPS];
+#else
+    gid_t	groups[NGROUPS];
+#endif /* NeXT */
+    uid_t	myUid;
+    int		ngroups;			/* The number of groups  */
+    struct stat	fileStatus;
+    int		i;
+        
+    if (stat(filename, &fileStatus))		/* Get details of filename */
+    	return NO;				/* Can't even access file! */
+
+    ngroups = getgroups(NGROUPS, groups);	/* Groups to which I belong  */
+    myUid = geteuid();				/* Get my user identifier */
+
+    if (TRACE) {
+        int i;
+	fprintf(stderr, 
+	    "File mode is 0%o, uid=%d, gid=%d. My uid=%d, %d groups (",
+    	    (unsigned int) fileStatus.st_mode, fileStatus.st_uid,
+	    fileStatus.st_gid,
+	    myUid, ngroups);
+	for (i=0; i<ngroups; i++) fprintf(stderr, " %d", groups[i]);
+	fprintf(stderr, ")\n");
+    }
+    
+    if (fileStatus.st_mode & 0002)		/* I can write anyway? */
+    	return YES;
+	
+    if ((fileStatus.st_mode & 0200)		/* I can write my own file? */
+     && (fileStatus.st_uid == myUid))
+    	return YES;
+
+    if (fileStatus.st_mode & 0020)		/* Group I am in can write? */
+    {
+   	for (i=0; i<ngroups; i++) {
+            if (groups[i] == fileStatus.st_gid)
+	        return YES;
+	}
+    }
+    if (TRACE)
+        fprintf(stderr, "\tFile is not editable.\n");
+    return NO;					/* If no excuse, can't do */
+#endif /* NO_GROUPS */
+}
+
+
+/*	Make a save stream
+**	------------------
+**
+**	The stream must be used for writing back the file.
+**	@@@ no backup done
+*/
+PUBLIC HTStream * HTFileSaveStream ARGS1(
+	HTParentAnchor *,	anchor)
+{
+
+    CONST char * addr = HTAnchor_address((HTAnchor*)anchor);
+    char *  localname = HTLocalName(addr);
+    
+    FILE* fp = fopen(localname, "w");
+    if (!fp)
+        return NULL;
+    
+    return HTFWriter_new(fp);
+    
+}
+
+/*      Output one directory entry
+**
+*/
+PUBLIC void HTDirEntry ARGS3(
+	HTStructured *,	target,
+	CONST char *,	tail,
+	CONST char *,	entry)
+{
+    char * relative;
+    char * escaped = HTEscape(entry, URL_XPALPHAS);
+
+
+    if (tail == NULL || *tail == '\0') {
+        /* handle extra slash at end of path */
+        HTStartAnchor(target, NULL, escaped);
+    } else {
+        /* If empty tail, gives absolute ref below */
+        relative = (char*) malloc(strlen(tail) + strlen(escaped)+2);
+        if (relative == NULL)
+	    outofmem(__FILE__, "DirRead");
+        sprintf(relative, "%s/%s", tail, escaped);
+        HTStartAnchor(target, NULL, relative);
+        FREE(relative);
+    }
+    FREE(escaped);
+}
+ 
+/*      Output parent directory entry
+**
+**    This gives the TITLE and H1 header, and also a link
+**    to the parent directory if appropriate.
+*/
+PUBLIC void HTDirTitles ARGS2(
+	HTStructured *,	target,
+	HTAnchor * ,	anchor)
+{
+    char * logical = HTAnchor_address(anchor);
+    char * path = HTParse(logical, "", PARSE_PATH + PARSE_PUNCTUATION);
+    char * current;
+    char * cp = NULL;
+
+    /* Trim out the ;type= parameter, if present. - FM */
+    if ((cp = strrchr(path, ';')) != NULL) {
+	if (!strncasecomp((cp+1), "type=", 5)) {
+	    if (TOUPPER(*(cp+6)) == 'D' ||
+		TOUPPER(*(cp+6)) == 'A' ||
+		TOUPPER(*(cp+6)) == 'I')
+		*cp = '\0';
+	}
+    }
+    current = strrchr(path, '/');	/* last part or "" */
+
+    {
+      char * printable = NULL;
+
+#ifdef DIRED_SUPPORT
+      if (0 == strncasecomp(path, "/%2F", 4))
+          StrAllocCopy(printable, (path+1));
+      else
+          StrAllocCopy(printable, path);
+      if (0 == strncasecomp(printable, "/vmsysu%2b", 10) ||
+          0 == strncasecomp(printable, "/anonymou.", 10)) {
+          StrAllocCopy(cp, (printable+1));
+	  StrAllocCopy(printable, cp);
+	  FREE(cp);
+      }
+#else 
+      StrAllocCopy(printable, (current + 1));
+#endif /* DIRED_SUPPORT */
+
+      START(HTML_HEAD);
+      PUTS("\n");
+      HTUnEscape(printable);
+      START(HTML_TITLE);
+      PUTS(*printable ? printable : "Welcome");
+      PUTS(" directory");
+      END(HTML_TITLE);
+      PUTS("\n");
+      END(HTML_HEAD);
+      PUTS("\n");
+
+#ifdef DIRED_SUPPORT
+      START(HTML_H2);
+      PUTS(*printable ? "Current directory is " : "");
+      PUTS(*printable ? printable : "Welcome");
+      END(HTML_H2);
+      PUTS("\n");
+#else
+      START(HTML_H1);
+      PUTS(*printable ? printable : "Welcome");
+      END(HTML_H1);
+      PUTS("\n");
+#endif /* DIRED_SUPPORT */
+      if (((0 == strncasecomp(printable, "vmsysu:", 7)) &&
+           (cp = strchr(printable, '.')) != NULL &&
+	   strchr(cp, '/') == NULL) ||
+	  (0 == strncasecomp(printable, "anonymou.", 9) &&
+	   strchr(printable, '/') == NULL)) {
+	  FREE(printable);
+	  FREE(logical);
+	  FREE(path);
+	  return;
+      }
+      FREE(printable);
+    }
+
+#ifndef NO_PARENT_DIR_REFERENCE
+    /*  Make link back to parent directory
+     */
+
+    if (current && current[1]) {   /* was a slash AND something else too */
+        char * parent;
+	char * relative;
+	*current++ = 0;
+        parent = strrchr(path, '/');  /* penultimate slash */
+
+	if ((parent && 0 == strncasecomp(parent, "/%2F", 4)) ||
+	    0 == strncasecomp(current, "%2F", 3)) {
+	    FREE(logical);
+	    FREE(path);
+	    return;
+	} 
+
+	relative = (char*) malloc(strlen(current) + 4);
+	if (relative == NULL)
+	    outofmem(__FILE__, "DirRead");
+	sprintf(relative, "%s/..", current);
+#ifndef VMS
+	{
+	    /*
+	     *  On Unix, if it's not ftp and the directory cannot
+	     *  be read, don't put out a link.
+	     *
+	     *  On VMS, this problem is dealt with internally by
+	     *  HTVMSBrowseDir().
+	     */
+	    extern BOOLEAN LYisLocalFile PARAMS((char *logical));
+	    DIR  * dp=NULL;
+
+	    if (LYisLocalFile(logical)) {
+	        if ((dp = opendir(relative)) == NULL) {
+	            FREE(logical);
+	            FREE(relative);
+	            FREE(path);
+	            return;
+		}
+		if (dp)
+	    	    closedir(dp);
+	    }
+	}
+#endif /* !VMS */
+	HTStartAnchor(target, "", relative);
+	FREE(relative);
+
+#ifdef DIRED_SUPPORT
+	if (dir_list_style != MIXED_STYLE)
+#endif /* DIRED_SUPPORT */
+	    PUTS("Up to ");
+	if (parent) {
+#ifdef DIRED_SUPPORT
+	   if (dir_list_style == MIXED_STYLE) {
+	      PUTS("../");
+	   } else {
+#else
+	   {
+#endif /* DIRED_SUPPORT */
+	      char * printable = NULL;
+	      StrAllocCopy(printable, parent + 1);
+	      HTUnEscape(printable);
+	      PUTS(printable);
+	      FREE(printable);
+	   }
+	} else {
+	  PUTS("/");
+	}
+
+	END(HTML_A);
+    }
+#endif /* NO_PARENT_DIR_REFERENCE */
+
+    FREE(logical);
+    FREE(path);
+    return;
+}
+		
+
+
+/*	Load a document
+**	---------------
+**
+** On entry,
+**	addr		must point to the fully qualified hypertext reference.
+**			This is the physical address of the file
+**
+** On exit,
+**	returns		<0		Error has occured.
+**			HTLOADED	OK 
+**
+*/
+PUBLIC int HTLoadFile ARGS4(
+	CONST char *,		addr,
+	HTParentAnchor *,	anchor,
+	HTFormat,		format_out,
+	HTStream *,		sink)
+{
+    char * filename = NULL;
+    char * access = NULL;
+    HTFormat format;
+    char * nodename = NULL;
+    char * newname = NULL;	/* Simplified name of file */
+    HTAtom * encoding;		/* @@ not used yet */
+#ifdef VMS
+    struct stat stat_info;
+#else
+    extern char *list_format;
+#endif /* VMS */
+
+    /*
+    **  Reduce the filename to a basic form (hopefully unique!)
+    */
+    StrAllocCopy(newname, addr);
+    filename=HTParse(newname, "", PARSE_PATH|PARSE_PUNCTUATION);
+    nodename=HTParse(newname, "", PARSE_HOST);
+    
+    /*
+    **  If access is ftp, or file is on another host, invoke ftp now.
+    */
+    access = HTParse(newname, "", PARSE_ACCESS);
+    if (strcmp("ftp", access) == 0 ||
+       (strcmp("localhost", nodename) != 0 &&
+#ifdef VMS
+        strcasecomp(nodename, HTHostName()) != 0))
+#else
+        strcmp(nodename, HTHostName()) != 0))
+#endif /* VMS */
+    {
+        FREE(newname);
+	FREE(filename);
+	FREE(nodename);
+        FREE(access);
+        return HTFTPLoad(addr, anchor, format_out, sink);
+    } else {
+        FREE(newname);
+        FREE(access);
+    }
+#ifdef VMS
+    HTUnEscape(filename);
+#endif /* VMS */
+
+    /*
+    ** Determine the format and encoding mapped to any suffix.
+    */
+    format = HTFileFormat(filename, &encoding);
+
+    /*
+    **  Check the format for an extended MIME charset value, and
+    **  act on it if present.  Otherwise, assume the ISO-8859-1
+    **  character set for local files.  If it's actually another
+    **  charset (e.g., ISO-8859-2 or KOI8-R) and the terminal is
+    **  using that, Lynx users should make the current character
+    **  set "ISO Latin 1" so that 8-bit characters are passed raw.
+    */
+    format = HTCharsetFormat(format, anchor);
+
+#ifdef VMS
+    /*
+    **  Check to see if the 'filename' is in fact a directory.  If it is
+    **  create a new hypertext object containing a list of files and 
+    **  subdirectories contained in the directory.  All of these are links
+    **  to the directories or files listed.
+    */
+    if (HTStat(filename, &stat_info) == -1) {
+	if (TRACE)
+	    fprintf(stderr, "HTLoadFile: Can't stat %s\n", filename);
+    } else {
+	if (((stat_info.st_mode) & S_IFMT) == S_IFDIR) {
+	    if (HTDirAccess == HT_DIR_FORBID) {
+		FREE(filename);
+		FREE(nodename);
+		return HTLoadError(sink, 403,
+		"Directory browsing is not allowed.");
+	    }
+
+	    if (HTDirAccess == HT_DIR_SELECTIVE) {
+		char * enable_file_name = 
+		    malloc(strlen(filename)+ 1 +
+		    strlen(HT_DIR_ENABLE_FILE) + 1);
+		strcpy(enable_file_name, filename);
+		strcat(enable_file_name, "/");
+		strcat(enable_file_name, HT_DIR_ENABLE_FILE);
+		if (HTStat(enable_file_name, &stat_info) == -1) {
+		    FREE(filename);
+		    FREE(nodename);
+		    return HTLoadError(sink, 403,
+		    "Selective access is not enabled for this directory");
+		}
+	    }
+
+	    FREE(filename);
+	    FREE(nodename);
+	    return HTVMSBrowseDir(addr, anchor, format_out, sink);
+	}
+    }
+
+    /*
+    **  Assume that the file is in Unix-style syntax if it contains a '/'
+    **  after the leading one @@
+    */
+    {
+        FILE * fp;
+	char * vmsname = strchr(filename + 1, '/') ?
+		    HTVMS_name(nodename, filename) : filename + 1;
+	fp = fopen(vmsname, "r", "shr=put", "shr=upd");
+	
+        /*
+	**  If the file wasn't VMS syntax, then perhaps it is ultrix
+	*/
+	if (!fp) {
+	    char ultrixname[INFINITY];
+	    if (TRACE)
+	        fprintf(stderr, "HTLoadFile: Can't open as %s\n", vmsname);
+	    sprintf(ultrixname, "%s::\"%s\"", nodename, filename);
+  	    fp = fopen(ultrixname, "r", "shr=put", "shr=upd");
+	    if (!fp) {
+		if (TRACE)
+		    fprintf(stderr, "HTLoadFile: Can't open as %s\n",
+		    		    ultrixname);
+	    }
+	}
+        if (fp) {
+	    int len;
+	    char *cp = NULL;
+	    char *semicolon = NULL;
+
+	    if (HTEditable(vmsname)) {
+		HTAtom * put = HTAtom_for("PUT");
+		HTList * methods = HTAnchor_methods(anchor);
+		if (HTList_indexOf(methods, put) == (-1)) {
+	   	    HTList_addObject(methods, put);
+	        }
+	    }
+	    /*
+	     * Trim vmsname at semicolon if a version number was
+	     * included, so it doesn't interfere with the check
+	     * for a compressed file. - FM
+	     */
+	    if ((semicolon = strchr(vmsname, ';')) != NULL)
+	        *semicolon = '\0';
+	    /*
+	    **  Fake a Content-Encoding for compressed files. - FM
+	    */
+	    if ((len = strlen(vmsname)) > 2) {
+	        if ((vmsname[len - 1] == 'Z') &&
+		    (vmsname[len - 2] == '.' ||
+		     vmsname[len - 2] == '-' ||
+		     vmsname[len - 2] == '_') &&
+		    vmsname[len - 3] != ']' &&
+		    vmsname[len - 3] != ':') {
+		    StrAllocCopy(cp, vmsname);
+		    cp[len - 2] = '\0';
+		    format = HTFileFormat(cp, &encoding);
+		    FREE(cp);
+		    format = HTCharsetFormat(format, anchor);
+		    StrAllocCopy(anchor->content_type, format->name);
+		    StrAllocCopy(anchor->content_encoding, "x-compress");
+		    format = HTAtom_for("www/compressed");
+		} else if ((len > 3) &&
+			   !strcasecomp((char *)&vmsname[len - 2], "gz")) {
+		    if (vmsname[len - 3] == '.' ||
+		        vmsname[len - 3] == '-' ||
+			vmsname[len - 3] == '_') {
+			StrAllocCopy(cp, vmsname);
+			cp[len - 3] = '\0';
+			format = HTFileFormat(cp, &encoding);
+			FREE(cp);
+			format = HTCharsetFormat(format, anchor);
+			StrAllocCopy(anchor->content_type, format->name);
+			StrAllocCopy(anchor->content_encoding, "x-gzip");
+			format = HTAtom_for("www/compressed");
+		    }
+		}
+	    }
+	    if (semicolon != NULL)
+	        *semicolon = ';';
+	    FREE(filename);
+	    FREE(nodename);
+	    HTParseFile(format, format_out, anchor, fp, sink);
+	    fclose(fp);
+            return HT_LOADED;
+        }  /* If successfull open */
+	FREE(filename);
+    }
+
+#else /* Unix: */
+
+    FREE(filename);
+    
+    /*
+    **  For unix, we try to translate the name into the name of a
+    **  transparently mounted file.
+    **
+    **  Not allowed in secure (HTClienntHost) situations TBL 921019
+    */
+#ifndef NO_UNIX_IO
+    /*  Need protection here for telnet server but not httpd server */
+	 
+    if (!HTSecure) {		/* try local file system */
+	char * localname = HTLocalName(addr);
+	struct stat dir_info;
+	
+#ifdef GOT_READ_DIR
+        /*		  Multiformat handling
+	**
+	**  If needed, scan directory to find a good file.
+	**  Bug:  we don't stat the file to find the length
+	*/
+	if ((strlen(localname) > strlen(MULTI_SUFFIX)) &&
+	    (0 == strcmp(localname + strlen(localname) - strlen(MULTI_SUFFIX),
+	                 MULTI_SUFFIX))) {
+	    DIR *dp;
+
+	    STRUCT_DIRENT * dirbuf;
+	    float best = NO_VALUE_FOUND;	/* So far best is bad */
+	    HTFormat best_rep = NULL;	/* Set when rep found */
+	    STRUCT_DIRENT best_dirbuf;	/* Best dir entry so far */
+
+	    char * base = strrchr(localname, '/');
+	    int baselen;
+
+	    if (!base || base == localname)
+	        goto forget_multi;
+	    *base++ = 0;		/* Just got directory name */
+	    baselen = strlen(base)- strlen(MULTI_SUFFIX);
+	    base[baselen] = 0;	/* Chop off suffix */
+
+	    dp = opendir(localname);
+	    if (!dp) {
+forget_multi:
+		FREE(localname);
+		FREE(nodename);
+		return HTLoadError(sink, 500,
+			"Multiformat: directory scan failed.");
+	    }
+	    
+	    while ((dirbuf = readdir(dp))!=0) {
+		/* while there are directory entries to be read */
+		if (dirbuf->d_ino == 0)
+		    continue;	/* if the entry is not being used, skip it */
+
+		if ((int)strlen(dirbuf->d_name) > baselen &&     /* Match? */
+		    !strncmp(dirbuf->d_name, base, baselen)) {	
+		    HTFormat rep = HTFileFormat(dirbuf->d_name, &encoding);
+		    float value = HTStackValue(rep, format_out,
+		    				HTFileValue(dirbuf->d_name),
+						0.0  /* @@@@@@ */);
+		    if (value != NO_VALUE_FOUND) {
+		        if (TRACE)
+			    fprintf(stderr,
+				 "HTLoadFile: value of presenting %s is %f\n",
+				    HTAtom_name(rep), value);
+			if  (value > best) {
+			    best_rep = rep;
+			    best = value;
+			    best_dirbuf = *dirbuf;
+		       }
+		    }	/* if best so far */ 		    
+		 } /* if match */  
+		    
+	    } /* end while directory entries left to read */
+	    closedir(dp);
+	    
+	    if (best_rep) {
+		format = best_rep;
+		base[-1] = '/';		/* Restore directory name */
+		base[0] = 0;
+		StrAllocCat(localname, best_dirbuf.d_name);
+		goto open_file;
+		
+	    } else { 			/* If not found suitable file */
+		FREE(localname);
+		FREE(nodename);
+		return HTLoadError(sink, 403,	/* List formats? */
+		   "Could not find suitable representation for transmission.");
+	    }
+	    /*NOTREACHED*/
+	} /* if multi suffix */
+
+	/*
+	**  Check to see if the 'localname' is in fact a directory.  If it
+	**  is create a new hypertext object containing a list of files and 
+	**  subdirectories contained in the directory.  All of these are
+	**  links to the directories or files listed.
+	**  NB This assumes the existance of a type 'STRUCT_DIRENT', which
+	**  will hold the directory entry, and a type 'DIR' which is used
+	**  to point to the current directory being read.
+	*/
+	if (stat(localname,&dir_info) == -1) {     /* get file information */
+	                               /* if can't read file information */
+	    if (TRACE)
+	        fprintf(stderr, "HTLoadFile: can't stat %s\n", localname);
+
+	}  else {		/* Stat was OK */
+		
+	    if (((dir_info.st_mode) & S_IFMT) == S_IFDIR) {
+		/* if localname is a directory */	
+
+		HTStructured* target;		/* HTML object */
+		HTStructuredClass targetClass;
+
+		DIR *dp;
+		STRUCT_DIRENT * dirbuf;
+		
+		char * logical=0;
+		char * pathname=0;
+		char * tail=0;
+		
+		BOOL present[HTML_A_ATTRIBUTES];
+		
+		char * tmpfilename = NULL;
+		struct stat file_info;
+		
+		if (TRACE)
+		    fprintf(stderr,"%s is a directory\n",localname);
+			
+		/*
+		**  Check directory access.
+		**  Selective access means only those directories containing
+		**  a marker file can be browsed
+		*/
+		if (HTDirAccess == HT_DIR_FORBID) {
+		    FREE(localname);
+		    FREE(nodename);
+		    return HTLoadError(sink, 403,
+		    "Directory browsing is not allowed.");
+		}
+
+
+		if (HTDirAccess == HT_DIR_SELECTIVE) {
+		    char * enable_file_name = 
+			malloc(strlen(localname)+ 1 +
+			 strlen(HT_DIR_ENABLE_FILE) + 1);
+		    strcpy(enable_file_name, localname);
+		    strcat(enable_file_name, "/");
+		    strcat(enable_file_name, HT_DIR_ENABLE_FILE);
+		    if (stat(enable_file_name, &file_info) != 0) {
+			FREE(localname);
+			FREE(nodename);
+			return HTLoadError(sink, 403,
+			"Selective access is not enabled for this directory");
+		    }
+		}
+
+ 
+		dp = opendir(localname);
+		if (!dp) {
+		    FREE(localname);
+		    FREE(nodename);
+		    return HTLoadError(sink, 403, "This directory is not readable.");
+		}
+
+
+		/*
+		**  Directory access is allowed and possible.
+		*/
+		logical = HTAnchor_address((HTAnchor*)anchor);
+        	pathname = HTParse(logical, "", 
+					PARSE_PATH + PARSE_PUNCTUATION);
+
+    		if (!strcmp(pathname,"/"))  /* root path */
+        	    StrAllocCopy (tail, "/foo/..");
+    	    	else
+    		  {
+        	    char * p = strrchr(pathname, '/');  /* find lastslash */
+        	    StrAllocCopy(tail, p+1); /* take slash off the beginning */
+    		  }
+    		FREE(pathname);
+		
+		target = HTML_new(anchor, format_out, sink);
+		targetClass = *target->isa;	/* Copy routine entry points */
+		    
+  		{ int i;
+			for (i = 0; i < HTML_A_ATTRIBUTES; i++)
+				present[i] = (i==HTML_A_HREF);
+		}
+		
+                HTDirTitles(target, (HTAnchor *)anchor);
+
+#ifdef DIRED_SUPPORT
+		HTAnchor_setFormat((HTParentAnchor *) anchor, WWW_DIRED);
+		lynx_edit_mode = TRUE;
+#endif /* DIRED_SUPPORT */
+                if (HTDirReadme == HT_DIR_README_TOP)
+		    do_readme(target, localname);
+		{
+		    HTBTree * bt = HTBTree_new((HTComparer)strcmp);
+
+		    while ((dirbuf = readdir(dp))!=0)
+		    {
+			/* while there are directory entries to be read */
+		        HTBTElement * dirname = NULL;
+			extern BOOLEAN no_dotfiles, show_dotfiles;
+
+		        if (dirbuf->d_ino == 0)
+			    /* if the entry is not being used, skip it */
+			    continue;
+
+			if (strcmp(dirbuf->d_name, ".") == 0  /* skip self */
+#ifdef NO_PARENT_DIR_REFERENCE
+			    || strcmp(dirbuf->d_name, "..") == 0 ||
+#else
+			    || strcmp(dirbuf->d_name, "..") != 0 &&
+#endif
+			    ((no_dotfiles || !show_dotfiles) &&
+			     dirbuf->d_name[0] == '.'))
+			    /* skip those files whose name
+			     * begins with '.' */
+			    continue;
+
+			dirname = (HTBTElement *)malloc(
+					strlen(dirbuf->d_name) + 4);
+			if (dirname == NULL)
+			    outofmem(__FILE__,"DirRead");
+			StrAllocCopy(tmpfilename,localname);
+			if (strcmp(localname,"/")) 
+			    /* if filename is not root directory */
+			    StrAllocCat(tmpfilename,"/"); 
+
+			StrAllocCat(tmpfilename,dirbuf->d_name);
+			stat(tmpfilename, &file_info);
+			if (((file_info.st_mode) & S_IFMT) == S_IFDIR)
+#ifndef DIRED_SUPPORT
+		            sprintf((char *)dirname,"D%s",dirbuf->d_name);
+			else
+			    sprintf((char *)dirname,"F%s",dirbuf->d_name);
+			    /* D & F to have first directories, then files */
+#else
+			    if (dir_list_style == MIXED_STYLE)
+			        sprintf((char *)dirname," %s/",dirbuf->d_name);
+			    else
+			        sprintf((char *)dirname,"D%s",dirbuf->d_name);
+			else if (dir_list_style == MIXED_STYLE)
+			    sprintf((char *)dirname," %s",dirbuf->d_name);
+			else if (dir_list_style == FILES_FIRST)
+			    sprintf((char *)dirname,"C%s",dirbuf->d_name);
+			    /* C & D to have first files, then directories */
+			else
+			    sprintf((char *)dirname,"F%s",dirbuf->d_name);
+#endif /* !DIRED_SUPPORT */
+			HTBTree_add(bt,dirname); /* Sort dirname in the tree bt */
+		    }
+
+		    /*
+		    **  Run through tree printing out in order
+		    */
+		    {
+		        HTBTElement * next_element = HTBTree_next(bt,NULL);
+			    /* pick up the first element of the list */
+			char state;
+			    /* I for initial (.. file),
+			       D for directory file,
+			       F for file */
+			
+#ifdef DIRED_SUPPORT
+			char test;
+#endif /* DIRED_SUPPORT */
+			state = 'I';
+
+			while (next_element != NULL)
+		        {
+			    char *entry, *file_extra;
+
+			    StrAllocCopy(tmpfilename,localname);
+			    if (strcmp(localname,"/")) 
+
+					/* if filename is not root directory */
+			        StrAllocCat(tmpfilename,"/"); 
+
+			    StrAllocCat(tmpfilename,
+					(char *)HTBTree_object(next_element)+1);
+			    /* append the current entry's filename to the path */
+			    HTSimplify(tmpfilename);
+			    /* Output the directory entry */
+			    if (strcmp((char *)
+				       (HTBTree_object(next_element)),"D.."))
+			    {			    
+#ifdef DIRED_SUPPORT
+			        test = *(char *)(HTBTree_object(next_element))=='D'?'D':'F';
+				if (state != test)
+				{
+#ifndef LONG_LIST
+				    if (dir_list_style == FILES_FIRST) {
+				       if (state == 'F')
+  					  END(HTML_DIR); 
+				    } else if (dir_list_style != MIXED_STYLE)
+				       if (state == 'D')
+  					  END(HTML_DIR); 
+#endif /* !LONG_LIST */
+				    state = *(char *)
+				        (HTBTree_object(next_element))=='D'?'D':'F';
+				    START(HTML_H2);
+				    if (dir_list_style != MIXED_STYLE)
+				       PUTS(state == 'D'?"Directories:":"Files");
+#else
+				if (state != *(char *)(HTBTree_object(next_element))) 
+				{
+#ifndef LONG_LIST
+				    if (state == 'D')
+				        END(HTML_DIR);
+#endif /* !LONG_LIST */
+				    state = *(char *)
+				        (HTBTree_object(next_element))=='D'?'D':'F';
+				    START(HTML_H2);
+				    PUTS(state == 'D'?"Subdirectories:":"Files");
+#endif /* DIRED_SUPPORT */
+				    END(HTML_H2);
+#ifndef LONG_LIST
+				    START(HTML_DIR);
+#endif /* !LONG_LIST */
+				}
+#ifndef LONG_LIST
+			        START(HTML_LI);
+#endif /* !LONG_LIST */
+			    }
+			    entry = (char*)HTBTree_object(next_element)+1;
+			    file_extra = NULL;
+
+#ifdef LONG_LIST
+			    LYListFmtParse(list_format, tmpfilename, target,
+				entry, tail);
+#else
+			    HTDirEntry(target, tail, entry);
+			    PUTS(entry);
+			    END(HTML_A);
+			    if (file_extra) {
+				PUTS(file_extra);
+				FREE(file_extra);
+			    }
+#endif /* LONG_LIST */
+
+			    next_element = HTBTree_next(bt,next_element);
+			        /* pick up the next element of the list; 
+				 if none, return NULL*/
+			}
+			if (state == 'I')
+			{
+			    START(HTML_P);
+			    PUTS("Empty Directory");
+			}
+#ifndef LONG_LIST
+			else
+			    END(HTML_DIR);
+#endif /* !LONG_LIST */
+		    }
+
+		        /* end while directory entries left to read */
+		    closedir(dp);
+		    FREE(logical);
+		    FREE(tmpfilename);
+		    FREE(tail);
+		    HTBTreeAndObject_free(bt);
+
+		    if (HTDirReadme == HT_DIR_README_BOTTOM)
+			  do_readme(target, localname);
+		    FREE_TARGET;
+		    FREE(localname);
+		    FREE(nodename);
+		    return HT_LOADED;	/* document loaded */
+		}
+
+	    } /* end if localname is directory */
+	
+	} /* end if file stat worked */
+	
+/* End of directory reading section
+*/
+#endif /* GOT_READ_DIR */
+open_file:
+	{
+	    FILE * fp = fopen(localname,"r");
+
+	    if (TRACE)
+	        fprintf (stderr, "HTLoadFile: Opening `%s' gives %p\n",
+				 localname, (void*)fp);
+	    if (fp) {		/* Good! */
+	        int len;
+		char *cp = NULL;
+
+		if (HTEditable(localname)) {
+		    HTAtom * put = HTAtom_for("PUT");
+		    HTList * methods = HTAnchor_methods(anchor);
+		    if (HTList_indexOf(methods, put) == (-1)) {
+			HTList_addObject(methods, put);
+		    }
+		}
+		/*
+		**  Fake a Content-Encoding for compressed files. - FM
+		*/
+		if ((len = strlen(localname)) > 2) {
+		    if (localname[len - 1] == 'Z' &&
+		        localname[len - 2] == '.') {
+			StrAllocCopy(cp, localname);
+			cp[len - 2] = '\0';
+			format = HTFileFormat(cp, &encoding);
+			FREE(cp);
+			format = HTCharsetFormat(format, anchor);
+			StrAllocCopy(anchor->content_type, format->name);
+			StrAllocCopy(anchor->content_encoding, "x-compress");
+			format = HTAtom_for("www/compressed");
+		    } else if ((len > 3) &&
+			       !strcasecomp((char *)&localname[len - 2],
+			       		    "gz") &&
+			       localname[len - 3] == '.') {
+			StrAllocCopy(cp, localname);
+			cp[len - 3] = '\0';
+			format = HTFileFormat(cp, &encoding);
+			FREE(cp);
+			format = HTCharsetFormat(format, anchor);
+			StrAllocCopy(anchor->content_type, format->name);
+			StrAllocCopy(anchor->content_encoding, "x-gzip");
+			format = HTAtom_for("www/compressed");
+		    }
+		}
+		FREE(localname);
+		FREE(nodename);
+		HTParseFile(format, format_out, anchor, fp, sink);
+		fclose(fp);
+		return HT_LOADED;
+	    }  /* If succesfull open */
+	}    /* scope of fp */
+    }  /* local unix file system */    
+#endif /* !NO_UNIX_IO */
+#endif /* VMS */
+
+#ifndef DECNET
+/*	Now, as transparently mounted access has failed, we try FTP.
+*/
+    {
+	/** Deal with case-sensitivity differences on VMS verus Unix **/
+#ifdef VMS
+        if (strcasecomp(nodename, HTHostName()) != 0)
+#else
+	if (strcmp(nodename, HTHostName()) != 0)
+#endif /* VMS */
+	{
+	    FREE(nodename);
+	    if (!strncmp(addr, "file://localhost", 16)) {
+	        return -1;  /* never go to ftp site when URL
+			     * is file://localhost
+			     */
+	    } else {
+	        return HTFTPLoad(addr, anchor, format_out, sink);
+	    }
+	}
+	FREE(nodename);
+    }
+#endif /* !DECNET */
+
+/*	All attempts have failed.
+*/
+    {
+    	if (TRACE)
+	    fprintf(stderr, "Can't open `%s', errno=%d\n", addr, SOCKET_ERRNO);
+
+	return HTLoadError(sink, 403, "Can't access requested file.");
+    }
+    
+ 
+}
+
+/*		Protocol descriptors
+*/
+#ifdef GLOBALDEF_IS_MACRO
+#define _HTFILE_C_1_INIT { "ftp", HTLoadFile, 0 }
+GLOBALDEF (HTProtocol,HTFTP,_HTFILE_C_1_INIT);
+#define _HTFILE_C_2_INIT { "file", HTLoadFile, HTFileSaveStream }
+GLOBALDEF (HTProtocol,HTFile,_HTFILE_C_2_INIT);
+#else
+GLOBALDEF PUBLIC HTProtocol HTFTP  = { "ftp", HTLoadFile, 0 };
+GLOBALDEF PUBLIC HTProtocol HTFile = { "file", HTLoadFile, HTFileSaveStream };
+#endif /* GLOBALDEF_IS_MACRO */
diff --git a/WWW/Library/Implementation/HTFile.h b/WWW/Library/Implementation/HTFile.h
new file mode 100644
index 00000000..466ea8c3
--- /dev/null
+++ b/WWW/Library/Implementation/HTFile.h
@@ -0,0 +1,173 @@
+/*							File access in libwww
+**				FILE ACCESS
+**
+**  These are routines for local file access used by WWW browsers and servers.
+**  Implemented by HTFile.c.
+**
+**  If the file is not a local file, then we pass it on to HTFTP in case it
+**  can be reached by FTP.
+*/
+#ifndef HTFILE_H
+#define HTFILE_H
+
+#include "HTFormat.h"
+#include "HTAccess.h"
+#include "HTML.h"               /* SCW */
+
+/*
+**  Controlling globals
+**
+**  These flags control how directories and files are represented as
+**  hypertext, and are typically set by the application from command
+**  line options, etc.
+*/
+extern int HTDirAccess;         /* Directory access level */
+
+#define HT_DIR_FORBID           0       /* Altogether forbidden */
+#define HT_DIR_SELECTIVE        1       /* If HT_DIR_ENABLE_FILE exists */
+#define HT_DIR_OK               2       /* Any accesible directory */
+
+#define HT_DIR_ENABLE_FILE      ".www_browsable" /* If exists, can browse */
+
+extern int HTDirReadme;         /* Include readme files in listing? */
+                                        /* Values: */
+#define HT_DIR_README_NONE      0       /* No */
+#define HT_DIR_README_TOP       1       /* Yes, first */
+#define HT_DIR_README_BOTTOM    2       /* Yes, at the end */
+
+#define HT_DIR_README_FILE              "README"
+
+/*
+**  Convert filenames between local and WWW formats
+*/
+extern char * HTLocalName PARAMS((CONST char * name));
+
+/*
+**  Make a WWW name from a full local path name
+*/
+extern char * WWW_nameOfFile PARAMS((const char * name));
+
+/*
+**  Generate the name of a cache file
+*/
+extern char * HTCacheFileName PARAMS((CONST char * name));
+
+/*
+**  Output directory titles
+**
+** This is (like the next one) used by HTFTP. It is common code to generate
+** the title and heading 1 and the parent directory link for any anchor.
+*/
+extern void HTDirTitles PARAMS((
+        HTStructured *  target,
+        HTAnchor *      anchor));
+
+/*
+**  Output a directory entry
+**
+** This is used by HTFTP.c for example -- it is a common routine for
+**  generating a linked directory entry.
+*/
+extern void HTDirEntry PARAMS((
+        HTStructured *  target,         /* in which to put the linked text */
+        CONST char *    tail,           /* last part of directory name */
+        CONST char *    entry));        /* name of this entry */
+
+/*
+**  HTSetSuffix: Define the representation for a file suffix
+**
+**  This defines a mapping between local file suffixes and file content
+**  types and encodings.
+**
+**  ON ENTRY,
+**
+**  suffix		includes the "." if that is important (normally, yes!)
+**
+**  representation	is MIME-style content-type
+**
+**  encoding		is MIME-style content-transfer-encoding
+**			(8bit, 7bit, etc)
+**
+**  quality		an a priori judgement of the quality of such files
+**			(0.0..1.0)
+**
+**  Example:   HTSetSuffix(".ps", "application/postscript", "8bit", 1.0);
+*/
+extern void HTSetSuffix PARAMS((
+        CONST char *    suffix,
+        CONST char *    representation,
+        CONST char *    encoding,
+        float           quality));
+        
+
+/*
+**  HTFileFormat: Get Representation and Encoding from file name.
+**
+**  ON EXIT,
+**
+**  return		The represntation it imagines the file is in.
+**
+**  *pEncoding		The encoding (binary, 7bit, etc). See HTSetSuffix.
+*/
+extern HTFormat HTFileFormat PARAMS((
+	CONST char *		filename,
+	HTAtom **		pEncoding));
+
+/*
+**  HTCharsetFormat: Revise the file format in relation to the Lynx charset.
+**
+**  This checks the format associated with an anchor for
+**  for an extended MIME Content-Type, and if a charset is
+**  indicated, sets Lynx up for proper handling in relation
+**  to the currently selected character set. - FM
+*/
+extern HTFormat HTCharsetFormat PARAMS((
+	HTFormat		format,
+	HTParentAnchor *	anchor));
+
+/*
+**  Determine file value from file name.
+*/
+extern float HTFileValue PARAMS((
+	CONST char *	filename));
+
+/*
+**  Determine write access to a file.
+**
+**  ON EXIT,
+**
+**  return value	YES if file can be accessed and can be written to.
+**
+**  BUGS
+**
+**   Isn't there a quicker way?
+*/
+extern BOOL HTEditable PARAMS((CONST char * filename));
+
+/*
+** Determine a suitable suffix, given the representation.
+**
+**  ON ENTRY,
+**
+**  rep			is the atomized MIME style representation
+**
+**  ON EXIT,
+**
+**  returns		a pointer to a suitable suffix string if one has
+**			been found, else NULL.
+*/
+extern CONST char * HTFileSuffix PARAMS((
+                HTAtom* rep));
+
+/*
+**  The Protocols
+*/
+#ifdef GLOBALREF_IS_MACRO
+extern GLOBALREF (HTProtocol,HTFTP);
+extern GLOBALREF (HTProtocol,HTFile);
+#else
+GLOBALREF HTProtocol HTFTP, HTFile;
+#endif /* GLOBALREF_IS_MACRO */
+#endif /* HTFILE_H */
+
+/* end of HTFile */
diff --git a/WWW/Library/Implementation/HTFinger.c b/WWW/Library/Implementation/HTFinger.c
new file mode 100644
index 00000000..3e37049c
--- /dev/null
+++ b/WWW/Library/Implementation/HTFinger.c
@@ -0,0 +1,438 @@
+/*			FINGER ACCESS				HTFinger.c
+**			=============
+** Authors:
+**  ARB  Andrew Brooks
+**
+** History:
+**	21 Apr 94   First version (ARB, from HTNews.c by TBL)
+**	12 Mar 96   Made the URL and command buffering secure from
+**		     stack modifications, beautified the HTLoadFinger()
+**		     and response() functions, and added support for the
+**		     following URL formats for sending a "", "/w",
+**		     "username[@host]", or "/w username[@host]" command
+**		     to the server:
+**			finger://host
+**			finger://host/
+**			finger://host/%2fw
+**			finger://host/%2fw%20username[@host]
+**			finger://host/w/username[@host]
+**			finger://host/username[@host]
+**			finger://host/username[@host]/w
+**			finger://username@host
+**			finger://username@host/
+**			finger://username@host/w
+**	15 Mar 96   Added support for port 79 gtype 0 gopher URLs
+**		     relayed from HTLoadGopher. - FM
+*/
+
+#include "HTUtils.h"
+#include "tcp.h"
+#include "HTAlert.h"
+#include "HTML.h"
+#include "HTParse.h"
+#include "HTFormat.h"
+#include "HTTCP.h"
+#include "HTString.h"
+#include "HTFinger.h"
+
+#include "LYLeaks.h"
+
+/* #define TRACE 1 */
+
+#define FINGER_PORT 79		/* See rfc742 */
+#define BIG 1024		/* Bug */
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+#define PUTC(c) (*targetClass.put_character)(target, c)
+#define PUTS(s) (*targetClass.put_string)(target, s)
+#define START(e) (*targetClass.start_element)(target, e, 0, 0, 0)
+#define END(e) (*targetClass.end_element)(target, e, 0)
+#define FREE_TARGET (*targetClass._free)(target)
+#define NEXT_CHAR HTGetCharacter() 
+
+
+/*	Module-wide variables
+*/
+PRIVATE int s;					/* Socket for FingerHost */
+
+struct _HTStructured {
+	CONST HTStructuredClass * isa;		/* For gopher streams */
+	/* ... */
+};
+
+PRIVATE HTStructured * target;			/* The output sink */
+PRIVATE HTStructuredClass targetClass;		/* Copy of fn addresses */
+
+/*	Initialisation for this module
+**	------------------------------
+*/
+PRIVATE BOOL initialized = NO;
+PRIVATE BOOL initialize NOARGS
+{
+  s = -1;		/* Disconnected */
+  return YES;
+}
+
+
+
+/*	Start anchor element
+**	--------------------
+*/
+PRIVATE void start_anchor ARGS1(CONST char *,  href)
+{
+    BOOL		present[HTML_A_ATTRIBUTES];
+    CONST char*		value[HTML_A_ATTRIBUTES];
+    
+    {
+    	int i;
+    	for(i=0; i<HTML_A_ATTRIBUTES; i++)
+	    present[i] = (i==HTML_A_HREF);
+    }
+    ((CONST char **)value)[HTML_A_HREF] = href;
+    (*targetClass.start_element)(target, HTML_A, present,
+    				 (CONST char **)value, 0);
+
+}
+
+/*	Send Finger Command line to remote host & Check Response
+**	--------------------------------------------------------
+**
+** On entry,
+**	command	points to the command to be sent, including CRLF, or is null
+**		pointer if no command to be sent.
+** On exit,
+**	Negative status indicates transmission error, socket closed.
+**	Positive status is a Finger status.
+*/
+
+
+PRIVATE int response ARGS5(
+	CONST char *,		command,
+	char *,			sitename,
+	HTParentAnchor *,	anAnchor,
+	HTFormat,		format_out,
+	HTStream*,		sink)
+{
+    int status;
+    int length = strlen(command);
+    int ch, i;
+    char line[BIG], *l, *cmd=NULL;
+    char *p = line, *href=NULL;
+    extern int interrupted_in_htgetcharacter;
+
+    if (length == 0)
+        return(-1);
+
+    /* Set up buffering.
+    */
+    HTInitInput(s);
+
+    /* Send the command.
+    */
+    if (TRACE) 
+        fprintf(stderr, "HTFinger command to be sent: %s", command);
+    status = NETWRITE(s, (char *)command, length);
+    if (status < 0) {
+        if (TRACE)
+	    fprintf(stderr,
+                    "HTFinger: Unable to send command. Disconnecting.\n");
+        NETCLOSE(s);
+        s = -1;
+        return status;
+    } /* if bad status */
+  
+    /* Make a hypertext object with an anchor list.
+    */
+    target = HTML_new(anAnchor, format_out, sink);
+    targetClass = *target->isa;	/* Copy routine entry points */
+
+    /* Create the results report.
+    */
+    if (TRACE)
+	fprintf(stderr,"HTFinger: Reading finger information\n");
+    START(HTML_HTML);
+    PUTS("\n");
+    START(HTML_HEAD);
+    PUTS("\n");
+    START(HTML_TITLE);
+    PUTS("Finger server on ");
+    PUTS(sitename);
+    END(HTML_TITLE);
+    PUTS("\n");
+    END(HTML_HEAD);
+    PUTS("\n");
+    START(HTML_BODY);
+    PUTS("\n");
+    START(HTML_H1);
+    PUTS("Finger server on ");
+    START(HTML_EM);
+    PUTS(sitename);
+    END(HTML_EM);
+    PUTS(": ");
+    if (command) {
+        StrAllocCopy(cmd, command);
+    } else {
+        StrAllocCopy(cmd, "");
+    }
+    for (i = (strlen(cmd) - 1); i >= 0; i--) {
+        if (cmd[i] == LF || cmd[i] == CR) {
+	    cmd[i] = '\0';
+	} else {
+	    break;
+	}
+    }
+    PUTS(cmd);
+    FREE(cmd);
+    END(HTML_H1);
+    PUTS("\n");
+    START(HTML_PRE);
+
+    while ((ch=NEXT_CHAR) != (char)EOF) {
+
+	if (interrupted_in_htgetcharacter) {
+	    if (TRACE) {
+	        fprintf(stderr,
+		  "HTFinger: Interrupted in HTGetCharacter, apparently.\n");
+	    }
+	    _HTProgress ("Connection interrupted.");
+	    goto end_html;
+        }
+
+	if (ch != LF) {
+	    *p = ch;		/* Put character in line */
+	    if (p < &line[BIG-1]) {
+	        p++;
+	    }
+	} else {
+	    *p = '\0';		/* Terminate line */
+	    /*
+	     * OK we now have a line.
+	     * Load it as 'l' and parse it.
+	     */
+	    p = l = line;
+	    while (*l) {
+		if (strncmp (l, "news:", 5) &&
+		    strncmp (l, "snews://", 8) &&
+		    strncmp (l, "nntp://", 7) &&
+		    strncmp (l, "ftp://", 6) &&
+		    strncmp (l, "file:/", 6) &&
+		    strncmp (l, "finger://", 9) &&
+		    strncmp (l, "http://", 7) &&
+		    strncmp (l, "https://", 8) &&
+		    strncmp (l, "wais://", 7) &&
+		    strncmp (l, "mailto:", 7) &&
+		    strncmp (l, "cso://", 6) &&
+		    strncmp (l, "gopher://", 9)) 
+		    PUTC(*l++);
+		else {
+		    StrAllocCopy(href, l);
+		    start_anchor(strtok(href, " \r\n\t,>)\""));
+		    while (*l && !strchr(" \r\n\t,>)\"", *l))
+		        PUTC(*l++);
+		    END(HTML_A);
+		    FREE(href);
+		}
+	    }
+	    PUTC('\n');
+	}
+    }
+    NETCLOSE(s);
+    s = -1;
+
+end_html:
+    END(HTML_PRE);
+    PUTS("\n");
+    END(HTML_BODY);
+    PUTS("\n");
+    END(HTML_HTML);
+    PUTS("\n");
+    FREE_TARGET;
+    return(0);
+}
+
+
+/*		Load by name					HTLoadFinger
+**		============
+*/
+PUBLIC int HTLoadFinger ARGS4(
+	CONST char *,		arg,
+	HTParentAnchor *,	anAnchor,
+	HTFormat,		format_out,
+	HTStream*,		stream)
+{
+    char *username, *sitename, *colon;	/* Fields extracted from URL */
+    char *slash, *at_sign;		/* Fields extracted from URL */
+    char *command, *str;		/* Buffers */
+    int port;				/* Port number from URL */
+    int status;				/* tcp return */
+  
+    if (TRACE) {
+        fprintf(stderr, "HTFinger: Looking for %s\n", (arg ? arg : "NULL"));
+    }
+  
+    if (!(arg && *arg)) {
+        HTAlert("Could not load data.");
+	return HT_NOT_LOADED;			/* Ignore if no name */
+    }
+  
+    if (!initialized) 
+        initialized = initialize();
+    if (!initialized) {
+        HTAlert ("Could not set up finger connection.");
+	return HT_NOT_LOADED;	/* FAIL */
+    }
+    
+  {
+    CONST char * p1=arg;
+    BOOL IsGopherURL = FALSE;
+    
+    /*  Set up the host and command fields.
+    */        
+    if (!strncasecomp(arg, "finger://", 9)) {
+        p1 = arg + 9;  /* Skip "finger://" prefix */
+    } else if (!strncasecomp(arg, "gopher://", 9)) {
+        p1 = arg + 9;  /* Skip "gopher://" prefix */
+	IsGopherURL = TRUE;
+    }
+    sitename = (char *)p1;
+
+    if ((slash = strchr(sitename, '/')) != NULL) {
+        *slash++ = '\0';
+	HTUnEscape(slash);
+	if (IsGopherURL) {
+	    if (*slash != '0') {
+	        HTAlert("Could not load data.");
+		return HT_NOT_LOADED;	/* FAIL */
+	    }
+	    *slash++ = '\0';
+	}
+    }
+    if ((at_sign = strchr(sitename, '@')) != NULL) {
+        if (IsGopherURL) {
+            HTAlert("Could not load data.");
+	    return HT_NOT_LOADED;	/* FAIL */
+	}
+        *at_sign++ = '\0';
+        username = sitename;
+	sitename = at_sign;
+	HTUnEscape(username);
+    } else if (slash) {
+        username = slash;
+    } else {
+        username = "";
+    }
+    
+    if (*sitename == '\0') {
+        HTAlert("Could not load data (no sitename in finger URL)");
+	return HT_NOT_LOADED;		/* Ignore if no name */
+    }
+
+    if ((colon = strchr(sitename, ':')) != NULL) {
+        *colon++ = '\0';
+	port = atoi(colon);
+	if (port != 79) {
+	    HTAlert("Invalid port number - will only use port 79!");
+	    return HT_NOT_LOADED;	/* Ignore if wrong port */
+	}
+    }
+
+    /* Load the string for making a connection/
+    */
+    str = (char *)calloc(1, (strlen(sitename) + 10));
+    if (str == NULL)
+        outofmem(__FILE__, "HTLoadFinger");
+    sprintf(str, "lose://%s/", sitename);
+    
+    /* Load the command for the finger server.
+    */
+    command = (char *)calloc(1, (strlen(username) + 10));
+    if (command == NULL)
+        outofmem(__FILE__, "HTLoadFinger");
+    if (at_sign && slash) {
+        if (*slash == 'w' || *slash == 'W') {
+	    sprintf(command, "/w %s%c%c", username, CR, LF);
+	} else {
+	    sprintf(command, "%s%c%c", username, CR, LF);
+	}
+    } else if (at_sign) {
+	sprintf(command, "%s%c%c", username, CR, LF);
+    } else if (*username == '/') {
+        if ((slash = strchr((username+1), '/')) != NULL) {
+	    *slash = ' ';
+	}
+	sprintf(command, "%s%c%c", username, CR, LF);
+    } else if ((*username == 'w' || *username == 'W') &&
+    	       *(username+1) == '/') {
+	if (*username+2 != '\0') {
+	    *(username+1) = ' ';
+	} else {
+	    *(username+1) = '\0';
+	}
+	sprintf(command, "/%s%c%c", username, CR, LF);
+    } else if ((*username == 'w' || *username == 'W') &&
+    	       *(username+1) == '\0') {
+	sprintf(command, "/%s%c%c", username, CR, LF);
+    } else if ((slash = strchr(username, '/')) != NULL) {
+	*slash++ = '\0';
+	if (*slash == 'w' || *slash == 'W') {
+	    sprintf(command, "/w %s%c%c", username, CR, LF);
+	} else {
+	    sprintf(command, "%s%c%c", username, CR, LF);
+	}
+    } else {
+	sprintf(command, "%s%c%c", username, CR, LF);
+    }
+  } /* scope of p1 */
+  
+    /* Now, let's get a stream setup up from the FingerHost:
+    ** CONNECTING to finger host
+    */
+    if (TRACE)
+        fprintf(stderr, "HTFinger: doing HTDoConnect on '%s'\n", str);
+    status = HTDoConnect(str, "finger", FINGER_PORT, &s);
+    if (TRACE)
+        fprintf(stderr, "HTFinger: Done DoConnect; status %d\n", status);
+
+    if (status == HT_INTERRUPTED) {
+        /* Interrupt cleanly */
+	if (TRACE)
+	    fprintf(stderr,
+	    	  "HTFinger: Interrupted on connect; recovering cleanly.\n");
+	HTProgress ("Connection interrupted.");
+	FREE(str);
+	FREE(command);
+	return HT_INTERRUPTED;
+    }
+    if (status < 0) {
+        NETCLOSE(s);
+	s = -1;
+	if (TRACE) 
+	    fprintf(stderr, "HTFinger: Unable to connect to finger host.\n");
+        HTAlert("Could not access finger host.");
+	FREE(str);
+	FREE(command);
+	return HT_NOT_LOADED;	/* FAIL */
+    }
+    if (TRACE)
+        fprintf(stderr, "HTFinger: Connected to finger host '%s'.\n", str);
+    FREE(str);
+
+    /* Send the command, and process response if successful.
+    */
+    if (response(command, sitename, anAnchor, format_out, stream) != 0) {
+        HTAlert("No response from finger server.");
+	FREE(command);
+	return HT_NOT_LOADED;
+    }
+
+    FREE(command);
+    return HT_LOADED;
+}
+
+#ifdef GLOBALDEF_IS_MACRO
+#define _HTFINGER_C_1_INIT { "finger", HTLoadFinger, NULL }
+GLOBALDEF (HTProtocol, HTFinger, _HTFINGER_C_1_INIT);
+#else
+GLOBALDEF PUBLIC HTProtocol HTFinger = { "finger", HTLoadFinger, NULL };
+#endif /* GLOBALDEF_IS_MACRO */
diff --git a/WWW/Library/Implementation/HTFinger.h b/WWW/Library/Implementation/HTFinger.h
new file mode 100644
index 00000000..98d98070
--- /dev/null
+++ b/WWW/Library/Implementation/HTFinger.h
@@ -0,0 +1,24 @@
+/* Finger protocol module for the WWW library */
+/* History:
+**      21 Apr 94       Andrew Brooks
+*/
+
+#ifndef HTFINGER_H
+#define HTFINGER_H
+
+#include "HTAccess.h"
+#include "HTAnchor.h"
+
+#ifdef GLOBALREF_IS_MACRO
+extern GLOBALREF (HTProtocol, HTFinger);
+#else
+GLOBALREF HTProtocol HTFinger;
+#endif /* GLOBALREF_IS_MACRO */
+
+extern int HTLoadFinger PARAMS((
+	CONST char *		arg,
+	HTParentAnchor *	anAnchor,
+	HTFormat		format_out,
+	HTStream *		stream));
+
+#endif /* HTFINGER_H */
diff --git a/WWW/Library/Implementation/HTFormat.c b/WWW/Library/Implementation/HTFormat.c
new file mode 100644
index 00000000..f25d36b3
--- /dev/null
+++ b/WWW/Library/Implementation/HTFormat.c
@@ -0,0 +1,836 @@
+
+/*		Manage different file formats			HTFormat.c
+**		=============================
+**
+** Bugs:
+**	Not reentrant.
+**
+**	Assumes the incoming stream is ASCII, rather than a local file
+**	format, and so ALWAYS converts from ASCII on non-ASCII machines.
+**	Therefore, non-ASCII machines can't read local files.
+**
+*/
+
+
+#include "HTUtils.h"
+#include "tcp.h"
+
+/* Implements:
+*/
+#include "HTFormat.h"
+
+PUBLIC float HTMaxSecs = 1e10;		/* No effective limit */
+PUBLIC float HTMaxLength = 1e10;	/* No effective limit */
+PUBLIC long int HTMaxBytes  = 0;	/* No effective limit */
+
+PUBLIC int loading_length= -1;
+
+#ifdef unix
+#ifdef NeXT
+#define PRESENT_POSTSCRIPT "open %s; /bin/rm -f %s\n"
+#else
+#define PRESENT_POSTSCRIPT "(ghostview %s ; /bin/rm -f %s)&\n"	
+	/* Full pathname would be better! */
+#endif
+#endif
+
+
+#include "HTML.h"
+#include "HTMLDTD.h"
+#include "HText.h"
+#include "HTAlert.h"
+#include "HTList.h"
+#include "HTInit.h"
+#include "HTTCP.h"
+/*	Streams and structured streams which we use:
+*/
+#include "HTFWriter.h"
+#include "HTPlain.h"
+#include "SGML.h"
+#include "HTML.h"
+#include "HTMLGen.h"
+
+#include "LYexit.h"
+#include "LYLeaks.h"
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+extern int HTCheckForInterrupt NOPARAMS;
+
+PUBLIC	BOOL HTOutputSource = NO;	/* Flag: shortcut parser to stdout */
+/* extern  BOOL interactive; LJM */
+
+#ifdef ORIGINAL
+struct _HTStream {
+      CONST HTStreamClass*	isa;
+      /* ... */
+};
+#endif
+
+/* this version used by the NetToText stream */
+struct _HTStream {
+	CONST HTStreamClass *	isa;
+	BOOL			had_cr;
+	HTStream * 		sink;
+};
+
+
+/*	Presentation methods
+**	--------------------
+*/
+
+PUBLIC  HTList * HTPresentations = NULL;
+PUBLIC  HTPresentation * default_presentation = NULL;
+
+/*
+ *	To free off the presentation list.
+ */
+PRIVATE void HTFreePresentations NOPARAMS;
+
+/*	Define a presentation system command for a content-type
+**	-------------------------------------------------------
+*/
+PUBLIC void HTSetPresentation ARGS6(
+	CONST char *,	representation,
+	CONST char *,	command,
+	float,		quality,
+	float,		secs, 
+	float,		secs_per_byte,
+	long int,	maxbytes)
+{
+    HTPresentation * pres = (HTPresentation *)malloc(sizeof(HTPresentation));
+    if (pres == NULL)
+        outofmem(__FILE__, "HTSetPresentation");
+    
+    pres->rep = HTAtom_for(representation);
+    pres->rep_out = WWW_PRESENT;		/* Fixed for now ... :-) */
+    pres->converter = HTSaveAndExecute;		/* Fixed for now ...     */
+    pres->quality = quality;
+    pres->secs = secs;
+    pres->secs_per_byte = secs_per_byte;
+    pres->maxbytes = maxbytes;
+    pres->command = NULL;
+    StrAllocCopy(pres->command, command);
+   
+    /*
+     *	Memory leak fixed.
+     *  05-28-94 Lynx 2-3-1 Garrett Arch Blythe
+     */ 
+    if (!HTPresentations)	{
+	HTPresentations = HTList_new();
+	atexit(HTFreePresentations);
+    }
+    
+    if (strcmp(representation, "*")==0) {
+	FREE(default_presentation);
+	default_presentation = pres;
+    } else {
+        HTList_addObject(HTPresentations, pres);
+    }
+}
+
+
+/*	Define a built-in function for a content-type
+**	---------------------------------------------
+*/
+PUBLIC void HTSetConversion ARGS7(
+	CONST char *,	representation_in,
+	CONST char *,	representation_out,
+	HTConverter*,	converter,
+	float,		quality,
+	float,		secs, 
+	float,		secs_per_byte,
+	long int,	maxbytes)
+{
+    HTPresentation * pres = (HTPresentation *)malloc(sizeof(HTPresentation));
+    if (pres == NULL)
+        outofmem(__FILE__, "HTSetConversion");
+    
+    pres->rep = HTAtom_for(representation_in);
+    pres->rep_out = HTAtom_for(representation_out);
+    pres->converter = converter;
+    pres->command = NULL;		/* Fixed */
+    pres->quality = quality;
+    pres->secs = secs;
+    pres->secs_per_byte = secs_per_byte;
+    pres->maxbytes = maxbytes;
+    pres->command = NULL;
+   
+    /*
+     *	Memory Leak fixed.
+     *	05-28-94 Lynx 2-3-1 Garrett Arch Blythe
+     */ 
+    if (!HTPresentations)	{
+	HTPresentations = HTList_new();
+	atexit(HTFreePresentations);
+    }
+    
+    HTList_addObject(HTPresentations, pres);
+}
+
+/*
+**	Purpose:	Free the presentation list.
+**	Arguments:	void
+**	Return Value:	void
+**	Remarks/Portability/Dependencies/Restrictions:
+**		Made to clean up Lynx's bad leakage.
+**	Revision History:
+**		05-28-94	created Lynx 2-3-1 Garrett Arch Blythe
+*/
+PRIVATE void HTFreePresentations NOARGS
+{
+    HTPresentation * pres = NULL;
+
+    /*
+     *  Loop through the list.
+     */
+    while (!HTList_isEmpty(HTPresentations)) {
+	/*
+	 *  Free off each item.
+	 *  May also need to free off it's items, but not sure
+	 *  as of yet.
+	 */
+	pres = (HTPresentation *)HTList_removeLastObject(HTPresentations);
+	FREE(pres->command);
+	FREE(pres);
+    }
+    /*
+     *	Free the list itself.
+     */
+    HTList_delete(HTPresentations);
+    HTPresentations = NULL;
+}
+
+
+/*	File buffering
+**	--------------
+**
+**	The input file is read using the macro which can read from
+**	a socket or a file.
+**	The input buffer size, if large will give greater efficiency and
+**	release the server faster, and if small will save space on PCs etc.
+*/
+#define INPUT_BUFFER_SIZE 4096		/* Tradeoff */
+PRIVATE char input_buffer[INPUT_BUFFER_SIZE];
+PRIVATE char * input_pointer;
+PRIVATE char * input_limit;
+PRIVATE int input_file_number;
+
+
+/*	Set up the buffering
+**
+**	These routines are public because they are in fact needed by
+**	many parsers, and on PCs and Macs we should not duplicate
+**	the static buffer area.
+*/
+PUBLIC void HTInitInput ARGS1 (int,file_number)
+{
+    input_file_number = file_number;
+    input_pointer = input_limit = input_buffer;
+}
+
+PUBLIC int interrupted_in_htgetcharacter = 0;
+PUBLIC char HTGetCharacter NOARGS
+{
+    char ch;
+    interrupted_in_htgetcharacter = 0;
+    do {
+	if (input_pointer >= input_limit) {
+	    int status = NETREAD(
+		    input_file_number, input_buffer, INPUT_BUFFER_SIZE);
+	    if (status <= 0) {
+		if (status == 0) return (char)EOF;
+		if (status == HT_INTERRUPTED)
+                {
+                  if (TRACE)
+                    fprintf (stderr,
+			     "HTFormat: Interrupted in HTGetCharacter\n");
+                  interrupted_in_htgetcharacter = 1;
+                  return (char)EOF;
+                }
+		if (TRACE) fprintf(stderr,
+		    "HTFormat: File read error %d\n", status);
+		return (char)EOF; /* -1 is returned by UCX at end of HTTP link */
+	    }
+	    input_pointer = input_buffer;
+	    input_limit = input_buffer + status;
+	}
+	ch = *input_pointer++;
+    } while (ch == (char) 13); /* Ignore ASCII carriage return */
+    
+    return FROMASCII(ch);
+}
+
+/*	Stream the data to an ouput file as binary
+*/
+PUBLIC int HTOutputBinary ARGS2( int, 		input,
+				  FILE *, 	output)
+{
+    do {
+	    int status = NETREAD(
+		    input, input_buffer, INPUT_BUFFER_SIZE);
+	    if (status <= 0) {
+		if (status == 0) return 0;
+		if (TRACE) fprintf(stderr,
+		    "HTFormat: File read error %d\n", status);
+		return 2;			/* Error */
+	    }
+	    fwrite(input_buffer, sizeof(char), status, output);
+    } while (YES);
+}
+
+/*  Match maintype to any MIME type starting with maintype,
+ *  for example:  image/gif should match image
+ */
+PRIVATE int half_match ARGS2(char *,trial_type, char *,target)
+{
+    char *cp=strchr(trial_type,'/');
+
+    /* if no '/' or no '*' */
+    if(!cp || *(cp+1) != '*')
+	return 0;
+
+    if(TRACE)
+	fprintf(stderr,"HTFormat: comparing %s and %s for half match\n",
+						      trial_type, target);
+
+	/* main type matches */
+    if(!strncmp(trial_type, target, (cp-trial_type)-1)) 
+	return 1;
+
+    return 0;
+}
+
+
+/*		Create a filter stack
+**		---------------------
+**
+**	If a wildcard match is made, a temporary HTPresentation
+**	structure is made to hold the destination format while the
+**	new stack is generated. This is just to pass the out format to
+**	MIME so far.  Storing the format of a stream in the stream might
+**	be a lot neater.
+**
+*/
+PUBLIC HTStream * HTStreamStack ARGS4(
+	HTFormat,		rep_in,
+	HTFormat,		rep_out,
+	HTStream*,		sink,
+	HTParentAnchor*,	anchor)
+{
+    HTAtom * wildcard = HTAtom_for("*");
+
+    if (TRACE) fprintf(stderr,
+    	"HTFormat: Constructing stream stack for %s to %s\n",
+	HTAtom_name(rep_in),	
+	HTAtom_name(rep_out));
+		
+       /* don't return on WWW_SOURCE some people might like
+        * to make use of the source!!!!  LJM
+        */
+     /* if (rep_out == WWW_SOURCE ||
+       		rep_out == rep_in) return sink;  LJM */
+
+     if(rep_out == rep_in) return sink;
+
+	/* don't do anymore do it in the Lynx code at startup LJM */
+    /* if (!HTPresentations) HTFormatInit(); */	/* set up the list */
+    
+    {
+	int n = HTList_count(HTPresentations);
+	int i;
+	HTPresentation * pres, *match,
+			*strong_wildcard_match=0,
+			*weak_wildcard_match=0,
+			*last_default_match=0,
+			*strong_subtype_wildcard_match=0;
+
+	for(i=0; i<n; i++) {
+	    pres = (HTPresentation *)HTList_objectAt(HTPresentations, i);
+	    if (pres->rep == rep_in) {
+	        if (pres->rep_out == rep_out) {
+		    if(TRACE)
+			fprintf(stderr,"StreamStack: found exact match: %s\n",HTAtom_name(pres->rep));
+	    	    return (*pres->converter)(pres, anchor, sink);
+
+		} else if (pres->rep_out == wildcard) {
+		    if(!strong_wildcard_match)
+		        strong_wildcard_match = pres;
+		    /* otherwise use the first one */
+		    if(TRACE)
+			fprintf(stderr,"StreamStack: found strong wildcard match: %s\n",HTAtom_name(pres->rep));
+		}
+
+	    } else if(half_match(HTAtom_name(pres->rep),
+						HTAtom_name(rep_in))) {
+		
+	        if (pres->rep_out == rep_out) {
+		    if(!strong_subtype_wildcard_match)
+		       strong_subtype_wildcard_match = pres;
+		    /* otherwise use the first one */
+		    if(TRACE)
+			fprintf(stderr,"StreamStack: found strong subtype wildcard match: %s\n",HTAtom_name(pres->rep));
+		}
+	    }
+
+	    if (pres->rep == WWW_SOURCE) {
+		if(pres->rep_out == rep_out) {
+		    if(!weak_wildcard_match)
+		        weak_wildcard_match = pres;
+		    /* otherwise use the first one */
+		    if(TRACE)
+			fprintf(stderr,"StreamStack: found weak wildcard match: %s\n",HTAtom_name(pres->rep_out));
+
+		}
+		if(pres->rep_out == wildcard) {
+		    if(!last_default_match)
+		        last_default_match = pres;
+		    /* otherwise use the first one */
+		}
+	    }
+	}
+	
+	match = strong_subtype_wildcard_match ? strong_subtype_wildcard_match :
+		strong_wildcard_match ?	strong_wildcard_match : 
+		weak_wildcard_match ? weak_wildcard_match : 
+		last_default_match;
+	
+	if (match) {
+		HTPresentation temp;
+		temp = *match;			/* Specific instance */
+		temp.rep = rep_in;		/* yuk */
+		temp.rep_out = rep_out;		/* yuk */
+		if(TRACE)
+		    fprintf(stderr,"StreamStack: Using %s\n",HTAtom_name(temp.rep_out));
+		return (*match->converter)(&temp, anchor, sink);
+        }
+    }
+
+    return NULL;
+}
+	
+
+/*		Find the cost of a filter stack
+**		-------------------------------
+**
+**	Must return the cost of the same stack which StreamStack would set up.
+**
+** On entry,
+**	length	The size of the data to be converted
+*/
+PUBLIC float HTStackValue ARGS4(
+	HTFormat,		rep_in,
+	HTFormat,		rep_out,
+	float,			initial_value,
+	long int,		length)
+{
+    HTAtom * wildcard = HTAtom_for("*");
+
+    if (TRACE) fprintf(stderr,
+    	"HTFormat: Evaluating stream stack for %s worth %.3f to %s\n",
+	HTAtom_name(rep_in),	initial_value,
+	HTAtom_name(rep_out));
+		
+    if (rep_out == WWW_SOURCE ||
+    	rep_out == rep_in) return 0.0;
+
+	/* don't do anymore do it in the Lynx code at startup LJM */
+    /* if (!HTPresentations) HTFormatInit(); */	/* set up the list */
+    
+    {
+	int n = HTList_count(HTPresentations);
+	int i;
+	HTPresentation * pres;
+	for(i=0; i<n; i++) {
+	    pres = (HTPresentation *)HTList_objectAt(HTPresentations, i);
+	    if (pres->rep == rep_in && (
+	    		pres->rep_out == rep_out ||
+			pres->rep_out == wildcard)) {
+	        float value = initial_value * pres->quality;
+		if (HTMaxSecs != 0.0)
+		    value = value - (length*pres->secs_per_byte + pres->secs)
+			                 /HTMaxSecs;
+		return value;
+	    }
+	}
+    }
+    
+    return -1e30;		/* Really bad */
+
+}
+	
+
+/*	Push data from a socket down a stream
+**	-------------------------------------
+**
+**   This routine is responsible for creating and PRESENTING any
+**   graphic (or other) objects described by the file.
+**
+**   The file number given is assumed to be a TELNET stream ie containing
+**   CRLF at the end of lines which need to be stripped to LF for unix
+**   when the format is textual.
+**
+*/
+
+PUBLIC int HTCopy ARGS3(
+	int,			file_number,
+	void*,			handle,
+	HTStream*,		sink)
+{
+    HTStreamClass targetClass;    
+    char line[256];
+    int bytes=0;
+    int rv = 0;
+    char * msg;
+
+    if (loading_length == -1)
+        msg = "Read %d bytes of data.";
+    else
+        /* We have a loading_length. */
+        msg = "Read %d of %d bytes of data.";
+
+    
+/*	Push the data down the stream
+**
+*/
+    targetClass = *(sink->isa);	/* Copy pointers to procedures */
+    
+    /*	Push binary from socket down sink
+    **
+    **		This operation could be put into a main event loop
+    */
+    for(;;) {
+        int status;
+	extern char LYCancelDownload;
+
+	if (LYCancelDownload) {
+	    LYCancelDownload = FALSE;
+	    (*targetClass._abort)(sink, NULL);
+	    rv = -1;
+	    goto finished;
+	}
+
+        if (HTCheckForInterrupt())
+          {
+            _HTProgress ("Data transfer interrupted.");
+            (*targetClass._abort)(sink, NULL);
+	    if(bytes)
+                rv = HT_INTERRUPTED;
+	    else
+		rv = -1;
+	    goto finished;
+          }
+
+
+        status = NETREAD(file_number, input_buffer, INPUT_BUFFER_SIZE);
+
+	if (status <= 0) {
+	    if (status == 0) 
+	      break;
+	    else if (status == HT_INTERRUPTED)
+              {
+                _HTProgress ("Data transfer interrupted.");
+                (*targetClass._abort)(sink, NULL);
+	        if(bytes)
+                    rv = HT_INTERRUPTED;
+	        else
+		    rv = -1;
+	        goto finished;
+              }
+	    else if (SOCKET_ERRNO == ENOTCONN || SOCKET_ERRNO == ECONNRESET 
+						   || SOCKET_ERRNO == EPIPE)
+              {
+                /* Arrrrgh, HTTP 0/1 compability problem, maybe. */
+		rv = -2;
+	        goto finished;
+              }
+            break;
+	}
+
+#ifdef NOT_ASCII
+	{
+	    char * p;
+	    for(p = input_buffer; p < input_buffer+status; p++) {
+		*p = FROMASCII(*p);
+	    }
+	}
+#endif
+
+	(*targetClass.put_block)(sink, input_buffer, status);
+
+	bytes += status;
+        sprintf(line, msg, bytes, loading_length);
+        HTProgress(line);
+
+    } /* next bufferload */
+
+    _HTProgress("Data transfer complete");
+    NETCLOSE(file_number);
+    rv = HT_LOADED;
+
+finished:
+    loading_length = -1;
+    return(rv);
+	
+}
+
+
+
+/*	Push data from a file pointer down a stream
+**	-------------------------------------
+**
+**   This routine is responsible for creating and PRESENTING any
+**   graphic (or other) objects described by the file.
+**
+**
+*/
+PUBLIC void HTFileCopy ARGS2(
+	FILE *,			fp,
+	HTStream*,		sink)
+{
+    HTStreamClass targetClass;    
+    
+/*	Push the data down the stream
+**
+*/
+    targetClass = *(sink->isa);	/* Copy pointers to procedures */
+    
+    /*	Push binary from socket down sink
+    */
+    for(;;) {
+	int status = fread(
+	       input_buffer, 1, INPUT_BUFFER_SIZE, fp);
+	if (status == 0) { /* EOF or error */
+	    if (ferror(fp) == 0) break;
+	    if (TRACE) fprintf(stderr,
+		"HTFormat: Read error, read returns %d\n", ferror(fp));
+	    break;
+	}
+	(*targetClass.put_block)(sink, input_buffer, status);
+    } /* next bufferload */
+	
+}
+
+
+
+
+/*	Push data from a socket down a stream STRIPPING CR
+**	--------------------------------------------------
+**
+**   This routine is responsible for creating and PRESENTING any
+**   graphic (or other) objects described by the socket.
+**
+**   The file number given is assumed to be a TELNET stream ie containing
+**   CRLF at the end of lines which need to be stripped to LF for unix
+**   when the format is textual.
+**
+*/
+PUBLIC void HTCopyNoCR ARGS2(
+	int,			file_number,
+	HTStream*,		sink)
+{
+    HTStreamClass targetClass;    
+    
+/*	Push the data, ignoring CRLF, down the stream
+**
+*/
+    targetClass = *(sink->isa);	/* Copy pointers to procedures */
+
+/*	Push text from telnet socket down sink
+**
+**	@@@@@ To push strings could be faster? (especially is we
+**	cheat and don't ignore CR! :-}
+*/  
+    HTInitInput(file_number);
+    for(;;) {
+	char character;
+	character = HTGetCharacter();
+	if (character == (char)EOF) break;
+	(*targetClass.put_character)(sink, character);           
+    }
+}
+
+
+
+/*	Parse a socket given format and file number
+**
+**   This routine is responsible for creating and PRESENTING any
+**   graphic (or other) objects described by the file.
+**
+**   The file number given is assumed to be a TELNET stream ie containing
+**   CRLF at the end of lines which need to be stripped to LF for unix
+**   when the format is textual.
+**
+*/
+PUBLIC int HTParseSocket ARGS5(
+	HTFormat,		rep_in,
+	HTFormat,		format_out,
+	HTParentAnchor *,	anchor,
+	int,			file_number,
+	HTStream*,		sink)
+{
+    HTStream * stream;
+    HTStreamClass targetClass;
+    int rv;
+    extern char LYCancelDownload;
+
+    stream = HTStreamStack(rep_in,
+			format_out,
+	 		sink , anchor);
+    
+    if (!stream) {
+        char buffer[1024];	/* @@@@@@@@ */
+        if (LYCancelDownload) {
+	    LYCancelDownload = FALSE;
+	    return -1;
+	}
+	sprintf(buffer, "Sorry, can't convert from %s to %s.",
+	        HTAtom_name(rep_in), HTAtom_name(format_out));
+	if (TRACE) fprintf(stderr, "HTFormat: %s\n", buffer);
+        return HTLoadError(sink, 501, buffer); /* returns -501 */
+    }
+    
+/*
+**	Push the data, don't worry about CRLF we can strip them later.
+*/
+    targetClass = *(stream->isa);	/* Copy pointers to procedures */
+    rv = HTCopy(file_number, NULL, stream);
+    if (rv != -1 && rv != HT_INTERRUPTED)
+        (*targetClass._free)(stream);
+    
+    return rv; /* full: HT_LOADED;  partial: HT_INTERRUPTED;  no bytes: -1 */
+}
+
+
+
+/*	Parse a file given format and file pointer
+**
+**   This routine is responsible for creating and PRESENTING any
+**   graphic (or other) objects described by the file.
+**
+**   The file number given is assumed to be a TELNET stream ie containing
+**   CRLF at the end of lines which need to be stripped to \n for unix
+**   when the format is textual.
+**
+*/
+PUBLIC int HTParseFile ARGS5(
+	HTFormat,		rep_in,
+	HTFormat,		format_out,
+	HTParentAnchor *,	anchor,
+	FILE *,			fp,
+	HTStream*,		sink)
+{
+    HTStream * stream;
+    HTStreamClass targetClass;    
+
+    stream = HTStreamStack(rep_in,
+			format_out,
+	 		sink , anchor);
+    
+    if (!stream) {
+        char buffer[1024];	/* @@@@@@@@ */
+	extern char LYCancelDownload;
+        if (LYCancelDownload) {
+	    LYCancelDownload = FALSE;
+	    return -1;
+	}
+	sprintf(buffer, "Sorry, can't convert from %s to %s.",
+		HTAtom_name(rep_in), HTAtom_name(format_out));
+	if (TRACE) fprintf(stderr, "HTFormat(in HTParseFile): %s\n", buffer);
+        return HTLoadError(sink, 501, buffer);
+    }
+    
+/*	Push the data down the stream
+**
+**
+**   @@  Bug:  This decision ought to be made based on "encoding"
+**   rather than on content-type.  @@@  When we handle encoding.
+**   The current method smells anyway.
+*/
+    targetClass = *(stream->isa);	/* Copy pointers to procedures */
+    HTFileCopy(fp, stream);
+    (*targetClass._free)(stream);
+    
+    return HT_LOADED;
+}
+
+
+/*	Converter stream: Network Telnet to internal character text
+**	-----------------------------------------------------------
+**
+**	The input is assumed to be in ASCII, with lines delimited
+**	by (13,10) pairs, These pairs are converted into (CR,LF)
+**	pairs in the local representation.  The (CR,LF) sequence
+**	when found is changed to a '\n' character, the internal
+**	C representation of a new line.
+*/
+
+
+PRIVATE void NetToText_put_character ARGS2(HTStream *, me, char, net_char)
+{
+    char c = FROMASCII(net_char);
+    if (me->had_cr) {
+        if (c==LF) {
+	    me->sink->isa->put_character(me->sink, '\n');	/* Newline */
+	    me->had_cr = NO;
+	    return;
+        } else {
+	    me->sink->isa->put_character(me->sink, CR);	/* leftover */
+	}
+    }
+    me->had_cr = (c==CR);
+    if (!me->had_cr)
+	me->sink->isa->put_character(me->sink, c);		/* normal */
+}
+
+PRIVATE void NetToText_put_string ARGS2(HTStream *, me, CONST char *, s)
+{
+    CONST char * p;
+
+    for (p=s; *p; p++)
+        NetToText_put_character(me, *p);
+}
+
+PRIVATE void NetToText_put_block ARGS3(HTStream *, me, CONST char*, s, int, l)
+{
+    CONST char * p;
+    for(p=s; p<(s+l); p++) NetToText_put_character(me, *p);
+}
+
+PRIVATE void NetToText_free ARGS1(HTStream *, me)
+{
+    (me->sink->isa->_free)(me->sink);		/* Close rest of pipe */
+    FREE(me);
+}
+
+PRIVATE void NetToText_abort ARGS2(HTStream *, me, HTError, e)
+{
+    me->sink->isa->_abort(me->sink,e);		/* Abort rest of pipe */
+    FREE(me);
+}
+
+/*	The class structure
+*/
+PRIVATE HTStreamClass NetToTextClass = {
+    "NetToText",
+    NetToText_free,
+    NetToText_abort,
+    NetToText_put_character,
+    NetToText_put_string,
+    NetToText_put_block
+};
+
+/*	The creation method
+*/
+PUBLIC HTStream * HTNetToText ARGS1(HTStream *, sink)
+{
+    HTStream* me = (HTStream*)malloc(sizeof(*me));
+    if (me == NULL)
+        outofmem(__FILE__, "NetToText");
+    me->isa = &NetToTextClass;
+    
+    me->had_cr = NO;
+    me->sink = sink;
+    return me;
+}
+
diff --git a/WWW/Library/Implementation/HTFormat.h b/WWW/Library/Implementation/HTFormat.h
new file mode 100644
index 00000000..5b513fbb
--- /dev/null
+++ b/WWW/Library/Implementation/HTFormat.h
@@ -0,0 +1,394 @@
+/*                                            HTFormat: The format manager in the WWW Library
+                            MANAGE DIFFERENT DOCUMENT FORMATS
+                                             
+   Here we describe the functions of the HTFormat module which handles conversion between
+   different data representations.  (In MIME parlance, a representation is known as a
+   content-type. In WWW  the term "format" is often used as it is shorter).
+   
+   This module is implemented by HTFormat.c . This hypertext document is used to generate
+   the HTFormat.h include file.  Part of the WWW library .
+   
+Preamble
+
+ */
+#ifndef HTFORMAT_H
+#define HTFORMAT_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+#include "HTStream.h"
+#include "HTAtom.h"
+#include "HTList.h"
+
+#ifdef SHORT_NAMES
+#define HTOutputSource HTOuSour
+#define HTOutputBinary HTOuBina
+#endif
+
+/*
+
+The HTFormat type
+
+   We use the HTAtom object for holding representations. This allows faster manipulation
+   (comparison and copying) that if we stayed with strings.
+   
+ */
+typedef HTAtom * HTFormat;
+                        
+/*
+
+   These macros (which used to be constants) define some basic internally referenced
+   representations.  The www/xxx ones are of course not MIME standard.
+   
+   www/source  is an output format which leaves the input untouched. It is useful for
+   diagnostics, and for users who want to see the original, whatever it is.
+   
+ */
+                        /* Internal ones */
+#define WWW_SOURCE HTAtom_for("www/source")     /* Whatever it was originally*/
+
+/*
+
+   www/present represents the user's perception of the document.  If you convert to
+   www/present, you present the material to the user.
+   
+ */
+#define WWW_PRESENT HTAtom_for("www/present")   /* The user's perception */
+
+/*
+
+   The message/rfc822 format means a MIME message or a plain text message with no MIME
+   header. This is what is returned by an HTTP server.
+   
+ */
+#define WWW_MIME HTAtom_for("www/mime")         /* A MIME message */
+
+/*
+
+   www/print is like www/present except it represents a printed copy.
+   
+ */
+#define WWW_PRINT HTAtom_for("www/print")       /* A printed copy */
+
+/*
+
+   www/unknown is a really unknown type. Some default action is appropriate.
+   
+ */
+#define WWW_UNKNOWN     HTAtom_for("www/unknown")
+
+#ifdef DIRED_SUPPORT
+/*
+   www/dired signals directory edit mode.
+*/
+#define WWW_DIRED      HTAtom_for("www/dired")
+#endif
+
+/*
+
+   These are regular MIME types.  HTML is assumed to be added by the W3 code.
+   application/octet-stream was mistakenly application/binary in earlier libwww versions
+   (pre 2.11).
+   
+ */
+#define WWW_PLAINTEXT   HTAtom_for("text/plain")
+#define WWW_POSTSCRIPT  HTAtom_for("application/postscript")
+#define WWW_RICHTEXT    HTAtom_for("application/rtf")
+#define WWW_AUDIO       HTAtom_for("audio/basic")
+#define WWW_HTML        HTAtom_for("text/html")
+#define WWW_BINARY      HTAtom_for("application/octet-stream")
+
+/*
+
+   We must include the following file after defining HTFormat, to which it makes
+   reference.
+   
+The HTEncoding type
+
+ */
+typedef HTAtom* HTEncoding;
+
+/*
+
+   The following are values for the MIME types:
+   
+ */
+#define WWW_ENC_7BIT            HTAtom_for("7bit")
+#define WWW_ENC_8BIT            HTAtom_for("8bit")
+#define WWW_ENC_BINARY          HTAtom_for("binary")
+
+/*
+
+   We also add
+   
+ */
+#define WWW_ENC_COMPRESS        HTAtom_for("compress")
+
+#include "HTAnchor.h"
+
+/*
+
+The HTPresentation and HTConverter types
+
+   This HTPresentation structure represents a possible conversion algorithm from one
+   format to annother.  It includes a pointer to a conversion routine. The conversion
+   routine returns a stream to which data should be fed. See also HTStreamStack which
+   scans the list of registered converters and calls one. See the initialisation module
+   for a list of conversion routines.
+   
+ */
+typedef struct _HTPresentation HTPresentation;
+
+typedef HTStream * HTConverter PARAMS((
+        HTPresentation *        pres,
+        HTParentAnchor *        anchor,
+        HTStream *              sink));
+        
+struct _HTPresentation {
+        HTAtom	*	rep;            /* representation name atmoized */
+        HTAtom	*	rep_out;        /* resulting representation */
+        HTConverter *	converter;  /* The routine to gen the stream stack */
+        char *		command;        /* MIME-format string */
+        float		quality;        /* Between 0 (bad) and 1 (good) */
+        float		secs;
+        float		secs_per_byte;
+	long int	maxbytes;
+};
+
+/*
+
+   The list of presentations is kept by this module.  It is also scanned by modules which
+   want to know the set of formats supported. for example.
+   
+ */
+extern HTList * HTPresentations;
+
+/*
+
+   The default presentation is used when no other is appriporate
+   
+ */
+extern  HTPresentation* default_presentation;
+
+/*
+
+HTSetPresentation: Register a system command to present a format
+
+  ON ENTRY,
+  
+  rep                     is the MIME - style format name
+                         
+  command                 is the MAILCAP - style command template
+                         
+  quality                 A degradation faction 0..1.0
+                         
+  secs                    A limit on the time user will wait (0.0 for infinity)
+  secs_per_byte
+                         
+  maxbytes                A limit on the length acceptable as input (0 infinite)
+                         
+ */
+extern void HTSetPresentation PARAMS((
+        CONST char *	representation,
+        CONST char *	command,
+        float		quality,
+        float		secs,
+        float		secs_per_byte,
+	long int	maxbytes
+));
+
+
+/*
+
+HTSetConversion:   Register a converstion routine
+
+  ON ENTRY,
+  
+  rep_in                  is the content-type input
+                         
+  rep_out                 is the resulting content-type
+                         
+  converter               is the routine to make the stream to do it
+                         
+ */
+
+extern void HTSetConversion PARAMS((
+        CONST char *    rep_in,
+        CONST char *    rep_out,
+        HTConverter *   converter,
+        float           quality,
+        float           secs,
+        float           secs_per_byte,
+	long int	maxbytes
+));
+
+
+/*
+
+HTStreamStack:   Create a stack of streams
+
+   This is the routine which actually sets up the conversion. It currently checks only for
+   direct conversions, but multi-stage conversions are forseen. It takes a stream into
+   which the output should be sent in the final format, builds the conversion stack, and
+   returns a stream into which the data in the input format should be fed.  The anchor is
+   passed because hypertxet objects load information into the anchor object which
+   represents them.
+   
+ */
+extern HTStream * HTStreamStack PARAMS((
+        HTFormat                format_in,
+        HTFormat                format_out,
+        HTStream*               stream_out,
+        HTParentAnchor*         anchor));
+
+/*
+
+HTStackValue: Find the cost of a filter stack
+
+   Must return the cost of the same stack which HTStreamStack would set up.
+   
+  ON ENTRY,
+  
+  format_in               The fomat of the data to be converted
+                         
+  format_out              The format required
+                         
+  initial_value           The intrinsic "value" of the data before conversion on a scale
+                         from 0 to 1
+                         
+  length                  The number of bytes expected in the input format
+                         
+ */
+extern float HTStackValue PARAMS((
+        HTFormat                format_in,
+        HTFormat                rep_out,
+        float                   initial_value,
+        long int                length));
+
+#define NO_VALUE_FOUND  -1e20           /* returned if none found */
+
+/*
+
+HTCopy:  Copy a socket to a stream
+
+   This is used by the protocol engines to send data down a stream, typically one which
+   has been generated by HTStreamStack.
+   
+ */
+extern int HTCopy PARAMS((
+        int                     file_number,
+	void*			handle,
+        HTStream*               sink));
+
+        
+/*
+
+HTFileCopy:  Copy a file to a stream
+
+   This is used by the protocol engines to send data down a stream, typically one which
+   has been generated by HTStreamStack. It is currently called by HTParseFile
+   
+ */
+extern void HTFileCopy PARAMS((
+        FILE*                   fp,
+        HTStream*               sink));
+
+        
+/*
+
+HTCopyNoCR: Copy a socket to a stream, stripping CR characters.
+
+   It is slower than HTCopy .
+   
+ */
+
+extern void HTCopyNoCR PARAMS((
+        int                     file_number,
+        HTStream*               sink));
+
+
+/*
+
+Clear input buffer and set file number
+
+   This routine and the one below provide simple character input from sockets. (They are
+   left over from the older architecure and may not be used very much.)  The existence of
+   a common routine and buffer saves memory space in small implementations.
+   
+ */
+extern void HTInitInput PARAMS((int file_number));
+
+/*
+
+Get next character from buffer
+
+ */
+extern char HTGetCharacter NOPARAMS;
+
+
+/*
+
+HTParseSocket: Parse a socket given its format
+
+   This routine is called by protocol modules to load an object.  uses HTStreamStack and
+   the copy routines above.  Returns HT_LOADED if succesful, <0 if not.
+   
+ */
+extern int HTParseSocket PARAMS((
+        HTFormat        format_in,
+        HTFormat        format_out,
+        HTParentAnchor  *anchor,
+        int             file_number,
+        HTStream*       sink));
+
+/*
+
+HTParseFile: Parse a File through a file pointer
+
+   This routine is called by protocols modules to load an object. uses HTStreamStack and
+   HTFileCopy .  Returns HT_LOADED if succesful, <0 if not.
+   
+ */
+extern int HTParseFile PARAMS((
+        HTFormat        format_in,
+        HTFormat        format_out,
+        HTParentAnchor  *anchor,
+        FILE            *fp,
+        HTStream*       sink));
+
+/*
+
+HTNetToText: Convert Net ASCII to local representation
+
+   This is a filter stream suitable for taking text from a socket and passing it into a
+   stream which expects text in the local C representation. It does ASCII and newline
+   conversion. As usual, pass its output stream to it when creating it.
+   
+ */
+extern HTStream *  HTNetToText PARAMS ((HTStream * sink));
+
+/*
+
+HTFormatInit: Set up default presentations and conversions
+
+   These are defined in HTInit.c or HTSInit.c if these have been replaced. If you don't
+   call this routine, and you don't define any presentations, then this routine will
+   automatically be called the first time a conversion is needed. However, if you
+   explicitly add some conversions (eg using HTLoadRules) then you may want also to
+   explicitly call this to get the defaults as well.
+   
+ */
+extern void HTFormatInit NOPARAMS;
+
+/*
+
+Epilogue
+
+ */
+extern BOOL HTOutputSource;     /* Flag: shortcut parser */
+#endif
+
+/*
+
+   end */
diff --git a/WWW/Library/Implementation/HTGopher.c b/WWW/Library/Implementation/HTGopher.c
new file mode 100644
index 00000000..f7ce00c4
--- /dev/null
+++ b/WWW/Library/Implementation/HTGopher.c
@@ -0,0 +1,2008 @@
+/*			GOPHER ACCESS				HTGopher.c
+**			=============
+**
+** History:
+**	26 Sep 90	Adapted from other accesses (News, HTTP) TBL
+**	29 Nov 91	Downgraded to C, for portable implementation.
+**	10 Mar 96	Foteos Macrides (macrides@sci.wfbr.edu).  Added a
+**			  form-based CSO/PH gateway.  Can be invoked via a
+**			  "cso://host[:port]/" or "gopher://host:105/2"
+**			  URL.  If a gopher URL is used with a query token
+**			  ('?'), the old ISINDEX procedure will be used
+**			  instead of the form-based gateway.
+**	15 Mar 96	Foteos Macrides (macrides@sci.wfbr.edu).  Pass
+**			  port 79, gtype 0 gopher URLs to the finger
+**			  gateway.
+*/
+
+#include "HTUtils.h"		/* Coding convention macros */
+#include "tcp.h"
+#include "HTAlert.h"
+#include "HTParse.h"
+#include "HTTCP.h"
+#include "HTFinger.h"
+
+/* Implements:
+*/
+#include "HTGopher.h"
+
+#define HT_EM_SPACE ((char)2)           /* For now */
+
+#define GOPHER_PORT 70		/* See protocol spec */
+#define CSO_PORT 105		/* See protocol spec */
+#define BIG 1024		/* Bug */
+#define LINE_LENGTH 256		/* Bug */
+
+/*	Gopher entity types:
+*/
+#define GOPHER_TEXT		'0'
+#define GOPHER_MENU		'1'
+#define GOPHER_CSO		'2'
+#define GOPHER_ERROR		'3'
+#define GOPHER_MACBINHEX	'4'
+#define GOPHER_PCBINARY		'5'
+#define GOPHER_UUENCODED	'6'
+#define GOPHER_INDEX		'7'
+#define GOPHER_TELNET		'8'
+#define GOPHER_BINARY           '9'
+#define GOPHER_GIF              'g'
+#define GOPHER_HTML		'h'	        /* HTML */
+#define GOPHER_CHTML		'H'	        /* HTML */
+#define GOPHER_SOUND            's'
+#define GOPHER_WWW		'w'		/* W3 address */
+#define GOPHER_IMAGE            'I'
+#define GOPHER_TN3270           'T'
+#define GOPHER_INFO             'i'
+#define GOPHER_DUPLICATE	'+'
+#define GOPHER_PLUS_IMAGE	':'		/* Addition from Gopher Plus */
+#define GOPHER_PLUS_MOVIE	';'
+#define GOPHER_PLUS_SOUND	'<'
+#define GOPHER_PLUS_PDF		'P'
+
+#include <ctype.h>
+
+#include "HTParse.h"
+#include "HTFormat.h"
+#include "HTTCP.h"
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+/*		Hypertext object building machinery
+*/
+#include "HTML.h"
+
+#include "LYLeaks.h"
+
+#define PUTC(c) (*targetClass.put_character)(target, c)
+#define PUTS(s) (*targetClass.put_string)(target, s)
+#define START(e) (*targetClass.start_element)(target, e, 0, 0, 0)
+#define END(e) (*targetClass.end_element)(target, e, 0)
+#define FREE_TARGET (*targetClass._free)(target)
+
+#define GOPHER_PROGRESS(foo) HTAlert(foo)
+
+#define NEXT_CHAR HTGetCharacter() 
+
+
+/*	Module-wide variables
+*/
+PRIVATE int s;				/* Socket for gopher or CSO host */
+
+struct _HTStructured {
+	CONST HTStructuredClass * isa;	/* For gopher streams */
+	/* ... */
+};
+
+PRIVATE HTStructured *target;		/* the new gopher hypertext */
+PRIVATE HTStructuredClass targetClass;	/* Its action routines */
+
+struct _HTStream 
+{
+  HTStreamClass * isa;			/* For form-based CSO  gateway - FM */
+};
+
+typedef struct _CSOfield_info {		/* For form-based CSO gateway - FM */
+    struct _CSOfield_info *	next;
+    char *			name;
+    char *			attributes;
+    char *			description;
+    int 			id;
+    int				lookup;
+    int				indexed;
+    int				url;
+    int				max_size;
+    int				defreturn;
+    int				explicit_return;
+    int				reserved;
+    int				public;
+    char			name_buf[16];	/* Avoid malloc if we can */
+    char			desc_buf[32];	/* Avoid malloc if we can */
+    char			attr_buf[80];	/* Avoid malloc if we can */
+} CSOfield_info;
+
+PRIVATE CSOfield_info *CSOfields = NULL; /* For form-based CSO gateway - FM */
+
+typedef struct _CSOformgen_context {	 /* For form-based CSO gateway - FM */
+    char *		host;
+    char *		seek;
+    CSOfield_info *	fld;
+    int			port;
+    int			cur_line;
+    int			cur_off;
+    int			rep_line;
+    int			rep_off;
+    int			public_override;
+    int			field_select;
+} CSOformgen_context;
+
+
+/*	Matrix of allowed characters in filenames
+**	=========================================
+*/
+PRIVATE BOOL acceptable[256];
+PRIVATE BOOL acceptable_inited = NO;
+
+PRIVATE void init_acceptable NOARGS
+{
+    unsigned int i;
+    char * good = 
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./-_$";
+    for(i = 0; i < 256; i++)
+        acceptable[i] = NO;
+    for(; *good; good++)
+        acceptable[(unsigned int)*good] = YES;
+    acceptable_inited = YES;
+}
+
+
+/*	Decode one hex character
+**	========================
+*/
+PRIVATE CONST char hex[17] = "0123456789abcdef";
+
+PRIVATE char from_hex ARGS1(char, c)
+{
+    return 		  (c>='0')&&(c<='9') ? c-'0'
+			: (c>='A')&&(c<='F') ? c-'A'+10
+			: (c>='a')&&(c<='f') ? c-'a'+10
+			:		       0;
+}
+
+
+/*	Paste in an Anchor
+**	==================
+**
+**	The title of the destination is set, as there is no way
+**	of knowing what the title is when we arrive.
+**
+** On entry,
+**	HT 	is in append mode.
+**	text 	points to the text to be put into the file, 0 terminated.
+**	addr	points to the hypertext refernce address 0 terminated.
+*/
+PUBLIC BOOLEAN HT_Is_Gopher_URL=FALSE;
+
+PRIVATE void write_anchor ARGS2(CONST char *,text, CONST char *,addr)
+{
+    BOOL present[HTML_A_ATTRIBUTES];
+    CONST char * value[HTML_A_ATTRIBUTES];
+    
+    int i;
+    
+    for (i = 0; i < HTML_A_ATTRIBUTES; i++)
+        present[i] = 0;
+    present[HTML_A_HREF] = YES;
+    ((CONST char **)value)[HTML_A_HREF] = addr;
+    present[HTML_A_TITLE] = YES;
+    ((CONST char **)value)[HTML_A_TITLE] = text;
+
+    if(TRACE)
+	fprintf(stderr,"HTGopher: adding URL: %s\n",addr);
+    
+    HT_Is_Gopher_URL = TRUE;  /* tell HTML.c that this is a Gopher URL */
+    (*targetClass.start_element)(target, HTML_A, present,
+    				 (CONST char **)value, 0);
+	    
+    PUTS(text);
+    END(HTML_A);
+}
+
+
+/*	Parse a Gopher Menu document
+**	============================
+*/
+PRIVATE void parse_menu ARGS2(
+	CONST char *,		arg,
+	HTParentAnchor *,	anAnchor)
+{
+    char gtype;
+    char ch;
+    char line[BIG];
+    char address[BIG];
+    char *name = NULL, *selector = NULL;	/* Gopher menu fields */
+    char *host = NULL;
+    char *port;
+    char *p = line;
+    CONST char *title;
+    int bytes = 0;
+    int BytesReported = 0;
+    char buffer[128];
+    extern int interrupted_in_htgetcharacter;
+
+#define TAB 		'\t'
+#define HEX_ESCAPE 	'%'
+
+    
+    START(HTML_HTML);
+    PUTS("\n");
+    START(HTML_HEAD);
+    PUTS("\n");
+    START(HTML_TITLE);
+    if ((title = HTAnchor_title(anAnchor)))
+	PUTS(title);
+    else
+        PUTS("Gopher Menu");
+    END(HTML_TITLE);
+    PUTS("\n");
+    END(HTML_HEAD);
+    PUTS("\n");
+
+    START(HTML_BODY);
+    PUTS("\n");
+    START(HTML_H1);
+    if ((title = HTAnchor_title(anAnchor)))
+	PUTS(title);
+    else
+        PUTS("Gopher Menu");
+    END(HTML_H1);
+    PUTS("\n");
+    START(HTML_PRE);
+    while ((ch=NEXT_CHAR) != (char)EOF) {
+
+	if (interrupted_in_htgetcharacter) {
+	    if (TRACE)
+	        fprintf(stderr,
+		    "HTGopher: Interrupted in HTGetCharacter, apparently.\n");
+	    goto end_html;
+        }
+
+        if (ch != LF) {
+	    *p = ch;		/* Put character in line */
+	    if (p< &line[BIG-1]) p++;
+	    
+	} else {
+	    *p++ = '\0';	/* Terminate line */
+	    bytes += p-line;    /* add size */
+	    p = line;		/* Scan it to parse it */
+	    port = 0;		/* Flag "not parsed" */
+	    if (TRACE)
+	        fprintf(stderr, "HTGopher: Menu item: %s\n", line);
+	    gtype = *p++;
+
+	    if (bytes > BytesReported + 1024) {
+	        sprintf(buffer, "Transferred %d bytes", bytes);
+                HTProgress(buffer);
+		BytesReported = bytes;
+	    }
+	    
+	    /* Break on line with a dot by itself */
+	    if ((gtype=='.') && ((*p=='\r') || (*p==0)))
+	        break;
+
+	    if (gtype && *p) {
+	        name = p;
+		selector = strchr(name, TAB);
+		if (selector) {
+		    *selector++ = '\0';	/* Terminate name */
+		    /*
+		     * Gopher+ Type=0+ objects can be binary, and will
+		     * have 9 or 5 beginning their selector.  Make sure
+		     * we don't trash the terminal by treating them as
+		     * text. - FM
+		     */
+		    if (gtype == GOPHER_TEXT && (*selector == GOPHER_BINARY ||
+		    				 *selector == GOPHER_PCBINARY))
+		        gtype = *selector;
+		    host = strchr(selector, TAB);
+		    if (host) {
+			*host++ = '\0';	/* Terminate selector */
+			port = strchr(host, TAB);
+			if (port) {
+			    char *junk;
+			    port[0] = ':';	/* delimit host a la W3 */
+			    junk = strchr(port, TAB);
+			    if (junk) *junk++ = '\0';	/* Chop port */
+			    if ((port[1]=='0') && (!port[2]))
+			        port[0] = '\0';	/* 0 means none */
+			} /* no port */
+		    } /* host ok */
+		} /* selector ok */
+	    } /* gtype and name ok */
+	    
+	    /* Nameless files are a separator line */
+	    if (gtype == GOPHER_TEXT) {
+	    	int i = strlen(name)-1;
+		while (name[i] == ' ' && i >= 0)
+		    name[i--] = '\0';
+		if (i < 0)
+		    gtype = GOPHER_INFO;
+	    }
+
+	    if (gtype == GOPHER_WWW) {	/* Gopher pointer to W3 */
+		PUTS("(HTML) ");
+		write_anchor(name, selector);
+
+	    } else if (gtype == GOPHER_INFO) {
+	    /* Information or separator line */
+		PUTS("       ");
+		PUTS(name);
+
+	    } else if (port) {		/* Other types need port */
+		if (gtype == GOPHER_TELNET) {
+		    PUTS(" (TEL) ");
+		    if (*selector) sprintf(address, "telnet://%s@%s/",
+					   selector, host);
+		    else sprintf(address, "telnet://%s/", host);
+		}
+		else if (gtype == GOPHER_TN3270) 
+		{
+		    PUTS("(3270) ");
+		    if (*selector) 
+			sprintf(address, "tn3270://%s@%s/",
+				selector, host);
+		    else 
+			sprintf(address, "tn3270://%s/", host);
+		}
+		else {			/* If parsed ok */
+		    char *q;
+		    char *p;
+
+		    switch(gtype) {
+               		case GOPHER_TEXT:
+                   	    PUTS("(FILE) ");
+                   	    break;
+                	case GOPHER_MENU:
+                    	    PUTS(" (DIR) ");
+                    	    break;
+                	case GOPHER_CSO:
+                    	    PUTS(" (CSO) ");
+                    	    break;
+                	case GOPHER_PCBINARY:
+                    	    PUTS(" (BIN) ");
+                    	    break;
+                	case GOPHER_UUENCODED:
+                    	    PUTS(" (UUE) ");
+                    	    break;
+                	case GOPHER_INDEX:
+                    	    PUTS("  (?)  ");
+                    	    break;
+                	case GOPHER_BINARY:
+                    	    PUTS(" (BIN) ");
+                    	    break;
+                	case GOPHER_GIF:
+                	case GOPHER_IMAGE:
+                	case GOPHER_PLUS_IMAGE:
+                    	    PUTS(" (IMG) ");
+                    	    break;
+                	case GOPHER_SOUND:
+                	case GOPHER_PLUS_SOUND:
+                    	    PUTS(" (SND) ");
+                    	    break;
+                	case GOPHER_MACBINHEX:
+                    	    PUTS(" (HQX) ");
+                    	    break;
+			case GOPHER_HTML:
+			case GOPHER_CHTML:
+                    	    PUTS("(HTML) ");
+                    	    break;
+                	case 'm':
+                    	    PUTS("(MIME) ");
+                    	    break;
+                	case GOPHER_PLUS_MOVIE:
+                    	    PUTS(" (MOV) ");
+                    	    break;
+                	case GOPHER_PLUS_PDF:
+                    	    PUTS(" (PDF) ");
+                    	    break;
+                	default:
+                    	    PUTS("(UNKN) ");
+                    	    break;
+		    }
+
+		    sprintf(address, "//%s/%c", host, gtype);
+
+		    q = address+ strlen(address);
+		    for(p=selector; *p; p++) {	/* Encode selector string */
+			if (acceptable[(unsigned char)*p]) *q++ = *p;
+			else {
+			    *q++ = HEX_ESCAPE;	/* Means hex coming */
+			    *q++ = hex[(TOASCII(*p)) >> 4];
+			    *q++ = hex[(TOASCII(*p)) & 15];
+			}
+		    }
+
+		    *q++ = '\0';	/* terminate address */
+		}
+		/* Error response from Gopher doesn't deserve to
+		   be a hyperlink. */
+		if (strcmp (address, "gopher://error.host:1/0"))
+		    write_anchor(name, address);
+		else
+		    PUTS(name);
+	    } else { /* parse error */
+	        if (TRACE)
+		    fprintf(stderr, "HTGopher: Bad menu item.\n");
+		PUTS(line);
+
+	    } /* parse error */
+	    
+	    PUTS("\n");
+	    p = line;	/* Start again at beginning of line */
+	    
+	} /* if end of line */
+	
+    } /* Loop over characters */
+	
+end_html:
+    END(HTML_PRE);
+    PUTS("\n");
+    END(HTML_BODY);
+    PUTS("\n");
+    END(HTML_HTML);
+    PUTS("\n");
+    FREE_TARGET;
+    
+    return;
+}
+
+
+/*	Parse a Gopher CSO document from and ISINDEX query.
+**	===================================================
+**
+**   Accepts an open socket to a CSO server waiting to send us
+**   data and puts it on the screen in a reasonable manner.
+**
+**   Perhaps this data can be automatically linked to some
+**   other source as well???
+**
+**  Taken from hacking by Lou Montulli@ukanaix.cc.ukans.edu
+**  on XMosaic-1.1, and put on libwww 2.11 by Arthur Secret, 
+**  secret@dxcern.cern.ch .
+*/
+PRIVATE void parse_cso ARGS2(
+	CONST char *,		arg,
+	HTParentAnchor *,	anAnchor)
+{
+    char ch;
+    char line[BIG];
+    char *p = line;
+    char *second_colon, last_char='\0';
+    CONST char *title;
+    
+    START(HTML_HEAD);
+    PUTS("\n");
+    START(HTML_TITLE);
+    if ((title = HTAnchor_title(anAnchor)))
+        PUTS(title);
+    else
+        PUTS("CSO Search Results");
+    END(HTML_TITLE);
+    PUTS("\n");
+    END(HTML_HEAD);
+    PUTS("\n");
+    START(HTML_H1);
+    if ((title = HTAnchor_title(anAnchor)))
+        PUTS(title);
+    else {
+        PUTS(arg);
+        PUTS(" Search Results");
+    }
+    END(HTML_H1);
+    PUTS("\n");
+    START(HTML_PRE);
+
+    /* start grabbing chars from the network */
+    while ((ch=NEXT_CHAR) != (char)EOF) 
+	{
+	    if (ch != LF) 
+		{
+		    *p = ch;		/* Put character in line */
+		    if (p< &line[BIG-1]) p++;
+		} 
+	    else 
+		{
+		    *p = '\0';		/* Terminate line */
+		    p = line;		/* Scan it to parse it */
+		    
+		    /* OK we now have a line in 'p' lets parse it and 
+		       print it */
+		    
+		    /* Break on line that begins with a 2. It's the end of
+		     * data.
+		     */
+		    if (*p == '2')
+			break;
+		    
+		    /*  lines beginning with 5 are errors, 
+		     *  print them and quit
+		     */
+		    if (*p == '5') {
+			START(HTML_H2);
+			PUTS(p+4);
+			END(HTML_H2);
+			break;
+		    }
+		    
+		    if(*p == '-') {
+			/*  data lines look like  -200:#:
+			 *  where # is the search result number and can be  
+			 *  multiple digits (infinate?)
+			 *  find the second colon and check the digit to the
+			 *  left of it to see if they are diferent
+			 *  if they are then a different person is starting. 
+			 *  make this line an <h2>
+			 */
+			
+			/* find the second_colon */
+			second_colon = strchr( strchr(p,':')+1, ':');
+			
+			if(second_colon != NULL) {  /* error check */
+			    
+			    if (*(second_colon-1) != last_char)   
+				/* print seperator */
+			    {
+				END(HTML_PRE);
+				START(HTML_H2);
+			    }
+				
+			    
+			    /* right now the record appears with the alias 
+			     * (first line)
+			     * as the header and the rest as <pre> text
+			     * It might look better with the name as the
+			     * header and the rest as a <ul> with <li> tags
+			     * I'm not sure whether the name field comes in any
+			     * special order or if its even required in a 
+			     * record,
+			     * so for now the first line is the header no 
+			     * matter
+			     * what it is (it's almost always the alias)
+			     * A <dl> with the first line as the <DT> and
+			     * the rest as some form of <DD> might good also?
+			     */
+			    
+			    /* print data */
+			    PUTS(second_colon+1);
+			    PUTS("\n");
+			    
+			    if (*(second_colon-1) != last_char)   
+				/* end seperator */
+			    {
+				END(HTML_H2);
+				START(HTML_PRE);
+			    }
+							    
+			    /* save the char before the second colon
+			     * for comparison on the next pass
+			     */
+			    last_char =  *(second_colon-1) ;
+			    
+			} /* end if second_colon */
+		    } /* end if *p == '-' */
+		} /* if end of line */
+	    
+	} /* Loop over characters */
+    
+    /* end the text block */
+    PUTS("\n");
+    END(HTML_PRE);
+    PUTS("\n");
+    FREE_TARGET;
+
+    return;  /* all done */
+} /* end of procedure */
+
+
+/*      Display a Gopher CSO ISINDEX cover page
+**	=======================================
+*/
+PRIVATE void display_cso ARGS2(
+        CONST char *,   	arg,
+        HTParentAnchor *,	anAnchor)
+{
+    CONST char * title;
+
+    START(HTML_HEAD);
+    PUTS("\n");
+    START(HTML_TITLE);
+    if ((title = HTAnchor_title(anAnchor)))
+	PUTS(title);
+    else
+        PUTS("CSO index");
+    END(HTML_TITLE);
+    PUTS("\n");
+    START(HTML_ISINDEX);
+    PUTS("\n");
+    END(HTML_HEAD);
+    PUTS("\n");
+    START(HTML_H1);
+    if ((title = HTAnchor_title(anAnchor)))
+	PUTS(title);
+    else {
+       PUTS(arg);
+       PUTS(" index");
+    }
+    END(HTML_H1);
+    PUTS("\nThis is a searchable index of a CSO database.\n");
+    START(HTML_P);
+    PUTS("\nPress the 's' key and enter search keywords.\n"); 
+    START(HTML_P);
+    PUTS("\nThe keywords that you enter will allow you to search on a");
+    PUTS(" person's name in the database.\n");
+
+    if (!HTAnchor_title(anAnchor))
+    	HTAnchor_setTitle(anAnchor, arg);
+    
+    FREE_TARGET;
+    return;
+}
+
+
+/*	Display a Gopher Index document
+**	===============================
+*/
+PRIVATE void display_index ARGS2(
+				  CONST char *,	arg,
+				  HTParentAnchor *,anAnchor)
+{
+    CONST char * title;
+    
+    START(HTML_HEAD);
+    PUTS("\n");
+    PUTS("\n");
+    START(HTML_TITLE);
+    if ((title = HTAnchor_title(anAnchor)))
+	PUTS(title);
+    else
+        PUTS("Gopher index");
+    END(HTML_TITLE);
+    PUTS("\n");
+    START(HTML_ISINDEX);
+    PUTS("\n");
+    END(HTML_HEAD);
+    PUTS("\n");
+    START(HTML_H1);
+    if ((title = HTAnchor_title(anAnchor)))
+	PUTS(title);
+    else {
+       PUTS(arg);
+       PUTS(" index");
+    }
+    END(HTML_H1);
+    PUTS("\nThis is a searchable Gopher index.\n");
+    START(HTML_P);
+    PUTS("\nPlease enter search keywords.\n");
+    
+    if (!HTAnchor_title(anAnchor))
+    	HTAnchor_setTitle(anAnchor, arg);
+    
+    FREE_TARGET;
+    return;
+}
+
+
+/*	De-escape a selector into a command
+**	===================================
+**
+**	The % hex escapes are converted. Otheriwse, the string is copied.
+*/
+PRIVATE void de_escape ARGS2(char *, command, CONST char *, selector)
+{
+    CONST char * p = selector;
+    char * q = command;
+	if (command == NULL)
+	    outofmem(__FILE__, "HTLoadGopher");
+    while (*p) {		/* Decode hex */
+	if (*p == HEX_ESCAPE) {
+	    char c;
+	    unsigned int b;
+	    p++;
+	    c = *p++;
+	    b =   from_hex(c);
+	    c = *p++;
+	    if (!c) break;	/* Odd number of chars! */
+	    *q++ = FROMASCII((b<<4) + from_hex(c));
+	} else {
+	    *q++ = *p++;	/* Record */
+	}
+    }
+    *q++ = '\0';	/* Terminate command */
+}
+
+
+/*	Free the CSOfields structures. - FM
+**	===================================
+*/
+PRIVATE void free_CSOfields NOPARAMS
+{
+    CSOfield_info *cur = CSOfields;
+    CSOfield_info *prev;
+
+    while (cur) {
+        if (cur->name != cur->name_buf)
+	    FREE(cur->name);
+	if (cur->attributes != cur->attr_buf)
+	    FREE(cur->attributes);
+	if (cur->description != cur->desc_buf)
+	    FREE(cur->description);
+	prev = cur;
+	cur = cur->next;
+	FREE(prev);
+    }
+
+    return;
+}
+
+/*	Interpret CSO/PH form template keys. - FM
+**	=========================================
+*/
+PRIVATE int interpret_cso_key ARGS5(
+	char *,			key,
+	char *,			buf,
+	int *,			length,
+	CSOformgen_context *,	ctx,
+	HTStream *, 		Target)
+{
+    CSOfield_info *fld;
+
+    if (fld = ctx->fld) {
+	/*
+	 * Most substitutions only recognized inside of loops.
+	 */
+	int error = 0;
+	if (0 == strncmp(key, "$(FID)", 6)) {
+	    sprintf(buf, "%d", fld->id); 
+        } else if (0 == strncmp(key, "$(FDESC)", 8)) {
+	    sprintf(buf, "%s%s%s", fld->description,
+		    ctx->public_override ? /***" "***/"" : "",
+		    ctx->public_override ? /***fld->attributes***/"" : ""); 
+        } else if (0 == strncmp(key, "$(FDEF)", 7)) {
+            strcpy(buf, fld->defreturn ? " checked" : "");
+        } else if (0 == strncmp(key, "$(FNDX)", 7)) {
+	    strcpy(buf, fld->indexed ? "*" : "");
+        } else if (0 == strncmp(key, "$(FSIZE)", 8)) {
+	    sprintf(buf, " size=%d maxlength=%d",
+		    fld->max_size > 55 ? 55 : fld->max_size,
+		    fld->max_size);
+        } else if (0 == strncmp(key, "$(FSIZE2)", 9)) {
+	    sprintf(buf, " maxlength=%d", fld->max_size);
+        } else {
+	    error = 1;
+	}
+	if (!error) {
+	    *length = strlen(buf);
+	    return -1;
+	}
+    }
+    /*
+     */
+    buf[0] = '\0';
+    if (0 == strncmp(key, "$(NEXTFLD)", 10)) {
+	if (!ctx->fld)
+	    fld = CSOfields;
+	else
+	    fld = ctx->fld->next;
+	switch (ctx->field_select) {
+	  case 0:
+	    /* 'Query' fields, public and lookup attributes */
+	    for (; fld; fld = fld->next)
+		 if (fld->public && (fld->lookup==1))
+		     break;
+	    break;
+	  case 1:
+	    /* 'Query' fields, accept lookup attribute */
+	    for (; fld; fld = fld->next)
+	        if (fld->lookup == 1)
+		    break;
+	    break;
+	  case 2:
+	    /* 'Return' fields, public only */
+	    for (; fld; fld = fld->next)
+	        if (fld->public)
+		    break;
+	    break;
+	  case 3:
+	    /* all fields */
+	    break;
+	}
+	if (fld) { 
+	    ctx->cur_line = ctx->rep_line;
+	    ctx->cur_off = ctx->rep_off;
+	}
+	ctx->fld = fld;
+
+    } else if ((0 == strncmp(key, "$(QFIELDS)", 10)) ||
+	       (0 == strncmp(key, "$(RFIELDS)", 10))) {
+	/*
+	 * Begin interation sequence.
+	 */
+	ctx->rep_line = ctx->cur_line;
+	ctx->rep_off = ctx->cur_off;
+	ctx->fld = (CSOfield_info *) 0;
+	ctx->seek = "$(NEXTFLD)";
+	ctx->field_select = (key[2] == 'Q') ? 0 : 2;
+	if (ctx->public_override)
+	    ctx->field_select++;
+
+    } else if (0 == strncmp(key, "$(NAMEFLD)", 10)) {
+	/*
+	 * Special, locate name field.  Flag lookup so QFIELDS will skip it.
+	 */
+	for (fld = CSOfields; fld; fld = fld->next)
+	    if (strcmp(fld->name, "name") == 0 ||
+	    	strcmp(fld->name, "Name") == 0) { 
+		if (fld->lookup)
+		    fld->lookup = 2; 
+		break;
+	    }
+	ctx->fld = fld;
+    } else if (0 == strncmp (key, "$(HOST)", 7)) {
+	strcpy (buf, ctx->host); 
+    } else if (0 == strncmp (key, "$(PORT)", 7)) {
+	sprintf(buf, "%d", ctx->port); 
+    } else {
+	/*
+	 * No match, dump key to buffer so client sees it for debugging.
+	 */
+	int out = 0;
+	while (*key && (*key != ')')) {
+	    buf[out++] = (*key++);
+	    if (out > sizeof(buf)-2) {
+	        buf[out] = '\0';
+		(*Target->isa->put_block)(Target, buf, strlen(buf));
+		out = 0;
+	    }
+	}
+	buf[out++] = ')';
+	buf[out] = '\0';
+	*length = strlen(buf);
+	return -1;
+    }
+    *length = strlen(buf);
+    return 0;
+}
+
+
+/*	Parse the elements in a CSO/PH fields structure. - FM
+**	=====================================================
+*/
+PRIVATE int parse_cso_field_info ARGS1(
+	CSOfield_info *,	blk)
+{
+    int i;
+    char *info, *max_spec;
+
+    /*
+     * initialize all fields to default values.  
+     */
+    blk->indexed = blk->lookup = blk->reserved = blk->max_size = blk->url = 0;
+    blk->defreturn = blk->explicit_return = blk->public = 0;
+
+    /*
+     * Search for keywords in info string and set values.  Attributes
+     * are converted to all lower-case for comparison.
+     */
+    info = blk->attributes;
+    for (i = 0; info[i]; i++)
+        info[i] = TOLOWER(info[i]);
+    if (strstr(info, "indexed "))
+        blk->indexed = 1;
+    if (strstr(info, "default "))
+        blk->defreturn = 1;
+    if (strstr(info, "public "))
+        blk->public = 1;
+    if (strstr(info, "lookup "))
+        blk->lookup = 1;
+    if (strstr(info, "url ")) {
+        blk->url = 1;
+	blk->defreturn = 1;
+    }
+    max_spec = strstr(info, "max ");
+    if (max_spec) {
+	sscanf(&max_spec[4], "%d", &blk->max_size);
+    } else {
+        blk->max_size = 32;
+    }
+
+    return 0;
+}
+
+
+/*	Parse a reply from a CSO/PH fields request. - FM
+**	================================================
+*/
+PRIVATE int parse_cso_fields ARGS2(
+	char *,		buf,
+	int,		size)
+{
+    char ch;
+    char *p = buf;
+    int i, code, prev_code, alen;
+    char *index, *name;
+    CSOfield_info *last, *new;
+    extern int interrupted_in_htgetcharacter;
+    
+    last = CSOfields = (CSOfield_info *) 0;
+    prev_code = -2555;
+    buf[0] = '\0';
+
+    /* start grabbing chars from the network */
+    while ((ch=NEXT_CHAR) != (char)EOF) {
+
+	if (interrupted_in_htgetcharacter) {
+	    if (TRACE) {
+	        fprintf(stderr,
+		  "HTLoadCSO: Interrupted in HTGetCharacter, apparently.\n");
+	    }
+	    free_CSOfields();
+	    buf[0] = '\0';
+	    return HT_INTERRUPTED;
+        }
+
+	if (ch != LF) {
+	    *p = ch;		/* Put character in buffer */
+	    if (p < &buf[size-1]) {
+	        p++;
+	    }
+	} else {
+	    *p = '\0';		/* Terminate line */
+	    p = buf;		/* Scan it to parse it */
+
+	    /* OK we now have a line in 'p' lets parse it.
+	     */
+	    
+	    /* Break on line that begins with a 2.
+	     * It's the end of data.
+	     */
+	    if (*p == '2')
+		break;
+
+	    /*  lines beginning with 5 are errors, 
+	     *  print them and quit
+	     */
+	    if (*p == '5') {
+	        strcpy (buf, p);
+		return 5;
+	    }
+
+	    if (*p == '-') {
+		/*  data lines look like  -200:#:
+		 *  where # is the search result number and can be  
+		 *  multiple digits (infinite?).
+		 */
+
+	    /*
+             * Check status, ignore any non-success.
+             */
+	    if (p[1] != '2' )
+	        continue;
+
+	    /*
+	     * Parse fields within returned line into status, ndx, name, data.
+	     */
+	    index = NULL;
+	    name = NULL;
+	    for (i = 0; p[i]; i++)
+	        if (p[i] == ':' ) {
+		    p[i] = '\0';
+		    if (!index) {
+		        index = (char *)&p[i+1];
+			code = atoi (index);
+		    } else if (!name) {
+		        name = (char *)&p[i+1];
+		    } else {
+		       i++;
+		       break;
+		    }
+                }
+	        /*
+	         * Add data to field structure.
+	         */
+	        if (name) {
+	            if (code == prev_code) {
+		        /*
+		         * Remaining data is description,
+			 * save in current info block.
+		         */
+		        alen = strlen((char *)&p[i]) + 1;
+		        if (alen > sizeof(last->desc_buf)) {
+			    if (last->description != last->desc_buf)
+			        FREE(last->description);
+		            if (!(last->description = (char *)malloc(alen))) {
+			        outofmem(__FILE__, "HTLoadCSO");
+		            }
+		        }
+		        strcpy(last->description, (char *)&p[i]);
+	            } else {
+		        /*
+		         * Initialize new block, append to end of list
+		         * to preserve order.
+		         */
+		        new = (CSOfield_info *)calloc(1, sizeof(CSOfield_info));
+		        if (!new) {
+			    outofmem(__FILE__, "HTLoadCSO");
+		        }
+		        if (last)
+		            last->next = new;
+		        else
+		            CSOfields = new;
+		        last = new;
+
+		        new->next = (CSOfield_info *) 0;
+		        new->name = new->name_buf;
+		        alen = strlen(name) + 1;
+		        if (alen > sizeof(new->name_buf)) {
+		            if (!(new->name = (char *)malloc(alen))) {
+			        outofmem(__FILE__, "HTLoadCSO");
+		            }
+		        }
+		        strcpy (new->name, name);
+
+		        new->attributes = new->attr_buf;
+		        alen = strlen((char *)&p[i]) + 2;
+		        if (alen > sizeof(new->attr_buf)) {
+		            if (!(new->attributes = (char *)malloc(alen))) {
+			        outofmem(__FILE__, "HTLoadCSO");
+		            }
+		        }
+		        strcpy(new->attributes, (char *)&p[i]);
+		        strcpy((char *)&new->attributes[alen-2], " ");
+		        new->description = new->desc_buf; 
+		        new->desc_buf[0] = '\0';
+		        new->id = atoi(index);
+		        /*
+		         * Scan for keywords.
+		         */
+		        parse_cso_field_info(new);
+	            }
+	            prev_code = code;
+	        } else
+	            break;
+	    } /* end if *p == '-' */
+	} /* if end of line */
+	    
+    } /* Loop over characters */
+    
+    /* end the text block */
+
+    if (buf[0] == '\0') {
+        return -1; /* no response */
+    }
+    buf[0] = '\0';
+    return 0;  /* all done */
+} /* end of procedure */
+
+
+/*	Generate a form for submitting CSO/PH searches. - FM
+**	====================================================
+*/
+PRIVATE int generate_cso_form ARGS4(
+	char *,		host,
+	int,		port,
+	char *,		buf,
+	HTStream *, 	Target)
+{
+    int i, j, length, out;
+    int full_flag = 1;
+    char *key, *line;
+    CSOformgen_context ctx;
+    static char *template[] = {
+   "<HEAD>\n<TITLE>CSO/PH Query Form for $(HOST)</TITLE>\n</HEAD>\n<BODY>",
+   "<H2><I>CSO/PH Query Form</I> for <EM>$(HOST)</EM></H2>",
+   "To search the database for a name, fill in one or more of the fields",
+   "in the form below and activate the 'Submit query' button.  At least",
+   "one of the entered fields must be flagged as indexed.",
+   "<HR><FORM method=\"POST\" action=\"cso://$(HOST)/\">",
+   "[ <input type=\"submit\" value=\"Submit query\"> | ",
+   "<input type=\"reset\" value=\"Clear fields\"> ]",
+   "<P><DL>",
+   "   <DT>Search parameters (* indicates indexed field):",
+   "   <DD>", "$(NAMEFLD)    <DL COMPACT>\n    <DT><I>$(FDESC)</I>$(FNDX)",
+   "    <DD>Last: <input name=\"q_$(FID)\" type=\"text\" size=49$(FSIZE2)>",
+   "    <DD>First: <input name=\"q_$(FID)\" type=\"text\" size=48$(FSIZE2)>",
+   "$(QFIELDS)    <DT><I>$(FDESC)</I>$(FNDX)",
+   "    <DD><input name=\"q_$(FID)\" type=\"text\" $(FSIZE)>\n$(NEXTFLD)",
+   "    </DL>",
+   "   </DL>\n<P><DL>",
+   "   <DT>Output format:",
+   "   <DD>Returned data option: <select name=\"return\">",
+   "    <option>default<option selected>all<option>selected</select><BR>",
+   "$(RFIELDS)    <input type=\"checkbox\" name=\"r_$(FID)\"$(FDEF)> $(FDESC)<BR>",
+   "$(NEXTFLD)    ",
+   "   </DL></FORM><HR>\n</BODY>\n</HTML>",
+   (char *) 0
+    };
+
+    out = 0;
+    ctx.host = host;
+    ctx.seek = (char *) 0;
+    ctx.port = port;
+    ctx.fld = (CSOfield_info *) 0;
+    ctx.public_override = full_flag;
+    /*
+     * Parse the strings in the template array to produce HTML document
+     * to send to client.  First line is skipped for 'full' lists.
+     */
+    out = 0;
+    buf[out] = '\0';
+    for (i = full_flag ? /***1***/ 0 : 0; template[i]; i++) {
+	/*
+	 * Search the current string for substitution, flagged by $(
+	 */
+	for (line=template[i], j = 0; line[j]; j++) {
+	    if ((line[j] == '$') && (line[j+1] == '(')) {
+		/*
+		 * Command detected, flush output buffer and find closing ')'
+		 * that delimits the command.
+		 */
+		buf[out] = '\0'; 
+		if (out > 0)
+		    (*Target->isa->put_block)(Target, buf, strlen(buf));
+	        out = 0;
+		for (key = &line[j]; line[j+1] && (line[j] != ')'); j++)
+		    ;
+		/*
+		 * Save context, interpet command and restore updated context.
+		 */
+		ctx.cur_line = i;
+		ctx.cur_off = j;
+		interpret_cso_key(key, buf, &length, &ctx, Target);
+		i = ctx.cur_line;
+		j = ctx.cur_off;
+		line = template[i];
+		out = length;
+
+		if (ctx.seek) {
+		    /*
+		     * command wants us to skip (forward) to indicated token.
+		     * Start at current position.
+		     */
+		    int slen = strlen(ctx.seek);
+		    for (; template[i]; i++) {
+			for (line = template[i]; line[j]; j++) {
+			    if (line[j] == '$') 
+				if (0 == strncmp(ctx.seek, &line[j], slen)) {
+				    if (j == 0)
+				        j = strlen(template[--i])-1;
+				    else
+				        --j;
+				    line = template[i];
+				    ctx.seek = (char *) 0;
+				    break;
+			        }
+			}
+			if (!ctx.seek)
+			    break;
+			j = 0;
+		    }
+		    if (ctx.seek) {
+		        char *temp = (char *)malloc(strlen(ctx.seek) + 20);
+			if (temp) {
+			    outofmem(__FILE__, "HTLoadCSO");
+			}
+			sprintf(temp, "Seek fail on %s\n", ctx.seek);
+		        (*Target->isa->put_block)(Target, temp, strlen(temp));
+		        FREE(temp);
+		    }
+		}
+	    } else {
+		/*
+		 * Non-command text, add to output buffer.
+		 */
+		buf[out++] = line[j];
+		if (out > (sizeof(buf)-3)) {
+		    buf[out] = '\0';
+		        (*Target->isa->put_block)(Target, buf, strlen(buf));
+		    out = 0;
+		}
+	    }
+	}
+	buf[out++] = '\n';
+	buf[out] = '\0';
+    }
+    if (out > 0)
+        (*Target->isa->put_block)(Target, buf, strlen(buf));
+
+    return 0;
+}
+
+
+/*	Generate a results report for CSO/PH form-based searches. - FM
+**	==============================================================
+*/
+PRIVATE int generate_cso_report ARGS2(
+	char *,		buf,
+	HTStream *, 	Target)
+{
+    char ch;
+    char line[BIG];
+    char *p = line, *href = NULL;
+    int len, i, prev_ndx, ndx;
+    char *rcode, *ndx_str, *fname, *fvalue, *l;
+    CSOfield_info *fld;
+    BOOL stop = FALSE; 
+    extern int interrupted_in_htgetcharacter;
+
+    /*
+     * Read lines until non-negative status.
+     */
+    prev_ndx = -100;
+    /* start grabbing chars from the network */
+    while (!stop && (ch=NEXT_CHAR) != (char)EOF) {
+
+	if (interrupted_in_htgetcharacter) {
+	    buf[0] = '\0';
+	    if (TRACE) {
+	        fprintf(stderr,
+		  "HTLoadCSO: Interrupted in HTGetCharacter, apparently.\n");
+	    }
+	    _HTProgress ("Connection interrupted.");
+	    goto end_CSOreport;
+        }
+
+	if (ch != LF) {
+	    *p = ch;		/* Put character in line */
+	    if (p < &line[BIG-1]) {
+	        p++;
+	    }
+	} else {
+	    *p = '\0';		/* Terminate line */
+	    /*
+	     * OK we now have a line.
+	     * Load it as 'p' and parse it.
+	     */
+	    p = line;
+	    if (p[0] != '-' && p[0] != '1') {
+	        stop = TRUE;
+	    }
+	    rcode = (p[0] == '-') ? &p[1] : p;
+	    ndx_str = fname = NULL;
+	    len = strlen(p);
+	    for (i = 0; i < len; i++) {
+	        if (p[i] == ':') {
+	            p[i] = '\0';
+	            if (!ndx_str) {
+		        fname = ndx_str = &p[i+1];
+		    } else {
+		        fname = &p[i+1];
+			break;
+		    }
+	        }
+	    }
+	    if (ndx_str) {
+	        ndx = atoi(ndx_str);
+	        if (prev_ndx != ndx) {
+		    if (prev_ndx != -100) {
+		        strcpy(buf, "</DL></DL>\n");
+			(*Target->isa->put_block)(Target, buf, strlen(buf));
+		    }
+		    if (ndx == 0) {
+		        strcpy(buf,
+	          "<HR><DL><DT>Information/status<DD><DL><DT>\n");
+			(*Target->isa->put_block)(Target, buf, strlen(buf));
+		    } else {
+		        sprintf(buf,
+	      "<HR><DL><DT>Entry %d:<DD><DL COMPACT><DT>\n", ndx);
+			(*Target->isa->put_block)(Target, buf, strlen(buf));
+		    }
+		    prev_ndx = ndx;
+	        }
+	    } else {
+	        sprintf(buf, "<DD>%s\n", rcode);
+		(*Target->isa->put_block)(Target, buf, strlen(buf));
+		continue;
+	    }
+	    if ((*rcode >= '2') && (*rcode <= '5') && (fname != ndx_str)) {
+	        while (*fname == ' ') {
+		    fname++;	/* trim leading spaces */
+		}
+	        for (fvalue = fname; *fvalue; fvalue++) {
+		    if (*fvalue == ':') {
+		        *fvalue++ = '\0';
+			i = strlen(fname) - 1;
+			while (i >= 0 && fname[i] == ' ') {
+			    fname[i--] = '\0'; /* trim trailing */
+			}
+			break;
+		    }
+		}
+		if (fvalue) {
+		    while (*fvalue == ' ') {
+		        fvalue++;	/* trim leading spaces */
+		    }
+		}
+	        if (*fname) {
+		    for (fld = CSOfields; fld; fld = fld->next) {
+		        if (!strcmp(fld->name, fname)) {
+			    if (fld->description) {
+			        fname = fld->description;
+			    }
+			    break;
+		        }
+		    }
+		    if (fld && fld->url) {
+		        sprintf(buf,
+				"<DT><I>%s</I><DD><A HREF=\"%s\">%s</A>\n",
+				fname, fvalue, fvalue);
+			(*Target->isa->put_block)(Target, buf, strlen(buf));
+		    } else {
+		        sprintf(buf, "<DT><I>%s</I><DD>", fname);
+			(*Target->isa->put_block)(Target, buf, strlen(buf));
+			i = 0;
+			buf[i] = '\0';
+			l = fvalue;
+			while (*l) {
+			    if (*l == '<') {
+			        strcat(buf, "&lt;");
+				l++;
+				i += 4;
+				buf[i] = '\0';
+			    } else if (*l == '>') {
+			        strcat(buf, "&gt;");
+				l++;
+				i += 4;
+				buf[i] = '\0';
+			    } else if (strncmp (l, "news:", 5) &&
+				       strncmp (l, "snews://", 8) &&
+				       strncmp (l, "nntp://", 7) &&
+				       strncmp (l, "ftp://", 6) &&
+				       strncmp (l, "file:/", 6) &&
+				       strncmp (l, "finger://", 9) &&
+				       strncmp (l, "http://", 7) &&
+				       strncmp (l, "https://", 8) &&
+				       strncmp (l, "wais://", 7) &&
+				       strncmp (l, "mailto:", 7) &&
+				       strncmp (l, "cso://", 6) &&
+				       strncmp (l, "gopher://", 9)) {
+			        buf[i++] = *l++;
+				buf[i] = '\0';
+			    } else {
+			        strcat(buf, "<a href=\"");
+				i += 9;
+				buf[i] = '\0';
+			        StrAllocCopy(href, l);
+				strcat(buf, strtok(href, " \r\n\t,>)\""));
+				strcat(buf, "\">");
+				i = strlen(buf);
+				while (*l && !strchr(" \r\n\t,>)\"", *l)) {
+				    buf[i++] = *l++;
+				}
+				buf[i] = '\0';
+				strcat(buf, "</a>");
+				i += 4;
+				FREE(href);
+			    }
+			}
+			strcat(buf, "\n");
+			(*Target->isa->put_block)(Target, buf, strlen(buf));
+		    }
+	        } else {
+		    sprintf(buf, "<DD>");
+		    (*Target->isa->put_block)(Target, buf, strlen(buf));
+		    i = 0;
+		    buf[i] = '\0';
+		    l = fvalue;
+		    while (*l) {
+			if (*l == '<') {
+			    strcat(buf, "&lt;");
+			    l++;
+			    i += 4;
+			    buf[i] = '\0';
+			} else if (*l == '>') {
+			    strcat(buf, "&gt;");
+			    l++;
+			    i += 4;
+			    buf[i] = '\0';
+			} else if (strncmp (l, "news:", 5) &&
+				   strncmp (l, "snews://", 8) &&
+				   strncmp (l, "nntp://", 7) &&
+				   strncmp (l, "ftp://", 6) &&
+				   strncmp (l, "file:/", 6) &&
+				   strncmp (l, "finger://", 9) &&
+				   strncmp (l, "http://", 7) &&
+				   strncmp (l, "https://", 8) &&
+				   strncmp (l, "wais://", 7) &&
+				   strncmp (l, "mailto:", 7) &&
+				   strncmp (l, "cso://", 6) &&
+				   strncmp (l, "gopher://", 9)) {
+			    buf[i++] = *l++;
+			    buf[i] = '\0';
+			} else {
+			    strcat(buf, "<a href=\"");
+			    i += 9;
+			    buf[i] = '\0';
+			    StrAllocCopy(href, l);
+			    strcat(buf, strtok(href, " \r\n\t,>)\""));
+			    strcat(buf, "\">");
+			    i = strlen(buf);
+			    while (*l && !strchr(" \r\n\t,>)\"", *l)) {
+				buf[i++] = *l++;
+			    }
+			    buf[i] = '\0';
+			    strcat(buf, "</a>");
+			    i += 4;
+			    FREE(href);
+			}
+		    }
+		    strcat(buf, "\n");
+		    (*Target->isa->put_block)(Target, buf, strlen(buf));
+		}
+	    } else {
+	        sprintf(buf, "<DD>%s\n", fname ? fname : rcode );
+		(*Target->isa->put_block)(Target, buf, strlen(buf));
+	    }
+        }
+    }
+end_CSOreport:
+    if (prev_ndx != -100) {
+	sprintf(buf, "</DL></DL>\n");
+	(*Target->isa->put_block)(Target, buf, strlen(buf));
+    }
+    return 0;
+}
+
+
+/*      CSO/PH form-based search gateway - FM			HTLoadCSO
+**	=====================================
+*/
+PUBLIC int HTLoadCSO ARGS4(
+        CONST char *,		arg,
+        HTParentAnchor *,	anAnchor,
+	HTFormat,		format_out,
+	HTStream*,		sink)
+{
+    char *host, *cp;
+    int port = CSO_PORT;
+    int status;				/* tcp return */
+    char *command = NULL;
+    char *content = NULL;
+    int len, i, j, start, finish, flen, ndx, clen;
+    int return_type, has_indexed;
+    CSOfield_info *fld;
+    char buf[2048];
+    HTFormat format_in = WWW_HTML;
+    HTStream *Target = NULL;
+ 
+    struct sockaddr_in soc_address;	/* Binary network address */
+    struct sockaddr_in* sin = &soc_address;
+
+    if (!acceptable_inited)
+         init_acceptable();
+
+    if (!arg)
+        return -3;		/* Bad if no name sepcified	*/
+    if (!*arg)
+        return -2;		/* Bad if name had zero length	*/
+    if (TRACE)
+        fprintf(stderr, "HTLoadCSO: Looking for %s\n", arg);
+
+    /*  Set up defaults:
+    */
+    sin->sin_family = AF_INET;	    		/* Family, host order  */
+    sin->sin_port = htons(CSO_PORT);	    	/* Default: new port,  */
+
+    /* Get node name and optional port number:
+    */
+    {
+	char *p1 = HTParse(arg, "", PARSE_HOST);
+	int status = HTParseInet(sin, p1);
+        FREE(p1);
+        if (status) {
+	    return status;   /* Bad */
+	}
+    }
+
+    /*	Set up a socket to the server for the data:
+    */      
+    status = HTDoConnect (arg, "cso", CSO_PORT, &s);
+    if (status == HT_INTERRUPTED) {
+        /* Interrupt cleanly. */
+	if (TRACE)
+	    fprintf(stderr,
+                 "HTLoadCSO: Interrupted on connect; recovering cleanly.\n");
+	_HTProgress ("Connection interrupted.");
+	return HT_INTERRUPTED;
+    }
+    if (status < 0) {
+	if (TRACE)
+	    fprintf(stderr,
+	    	    "HTLoadCSO: Unable to connect to remote host for `%s'.\n",
+	    	    arg);
+	return HTInetStatus("connect");
+    }
+    
+    HTInitInput(s);		/* Set up input buffering */
+    
+    if ((command = (char *)malloc(12)) == NULL)
+        outofmem(__FILE__, "HTLoadCSO");
+    sprintf(command, "fields%c%c", CR, LF);
+    if (TRACE)
+        fprintf(stderr,
+		"HTLoadCSO: Connected, writing command `%s' to socket %d\n",
+		command, s);
+    _HTProgress ("Sending CSO/PH request.");
+    status = NETWRITE(s, command, (int)strlen(command));
+    FREE(command);
+    if (status < 0) {
+	if (TRACE)
+	    fprintf(stderr, "HTLoadCSO: Unable to send command.\n");
+	return HTInetStatus("send");
+    }
+    _HTProgress ("CSO/PH request sent; waiting for response.");
+
+    /*	Now read the data from the socket:
+    */
+    status = parse_cso_fields(buf, sizeof(buf));
+    if (status) {
+        NETCLOSE(s);
+	if (status = HT_INTERRUPTED) {
+	    _HTProgress ("Connection interrupted.");
+	} else if (buf[0] != '\0') {
+            HTAlert(buf);
+	} else {
+	    HTAlert("No response from server!");
+	}
+	return HT_NOT_LOADED;
+    }
+    Target = HTStreamStack(format_in, 
+			   format_out,
+			   sink, anAnchor);
+    if (!Target || Target == NULL) {
+        char *temp = (char *)malloc(256);
+	if (!temp) {
+	    outofmem(__FILE__, "HTLoadCSO");
+	}
+	sprintf(temp, "Sorry, no known way of converting %s to %s.",
+		HTAtom_name(format_in), HTAtom_name(format_out));
+	HTAlert(temp);
+	FREE(temp);
+	NETCLOSE(s);
+	return HT_NOT_LOADED;
+    }
+    host = HTParse(arg, "", PARSE_HOST);
+    if ((cp=strchr(host, ':')) != NULL) {
+	if (cp[1] >= '0' && cp[1] <= '9') {
+	    port = atoi((cp+1));
+	    if (port == CSO_PORT) {
+	        *cp = '\0';
+	    }
+	}
+    }
+    if (!(anAnchor->post_data && *anAnchor->post_data)) {
+        generate_cso_form(host, port, buf, Target);
+	(*Target->isa->_free)(Target);
+	FREE(host);
+	NETCLOSE(s);
+	free_CSOfields();
+	return HT_LOADED;
+    }
+    sprintf(buf,
+     "<HTML>\n<HEAD>\n<TITLE>CSO/PH Results on %s</TITLE>\n</HEAD>\n<BODY>\n",
+    	    host);
+    (*Target->isa->put_block)(Target, buf, strlen(buf));
+    FREE(host);
+    StrAllocCopy(content, anAnchor->post_data);
+    if (content[strlen(content)-1] != '&')
+        StrAllocCat(content, "&");
+    len = strlen(content);
+    for (i = 0; i < len; i++) {
+        if (content[i] == '+') {
+	    content[i] = ' ';
+	}
+    }
+    HTUnEscape(content);
+    len = strlen(content);
+    return_type = 0;
+    has_indexed = 0;
+    start = finish = clen = 0;
+    for (i = 0; i < len; i++) {
+        if (!content[i] || content[i] == '&') {
+	    /*
+	     * Value parsed.  Unescape characters and look for first '='
+	     * to delimit field name from value.
+	     */
+	    flen = i - start;
+	    finish = start + flen;
+	    content[finish] = '\0';
+	    for (j = start; j < finish; j++) {
+	        if (content[j] == '=') {
+	            /*
+	             * content[start..j-1] is field name,
+		     * [j+1..finish-1] is value.
+	             */
+	            if ((content[start+1] == '_') &&
+		        ((content[start] == 'r') || (content[start] == 'q'))) {
+		        /*
+		         * Decode fields number and lookup field info.
+		         */
+		        sscanf (&content[start+2], "%d=", &ndx);
+		        for (fld = CSOfields; fld; fld = fld->next) {
+		            if (ndx==fld->id) {
+		                if ((j+1) >= finish)
+				    break;	/* ignore nulls */
+		                if (content[start] == 'q') {
+			            /*
+			             * Append field to query line.
+			             */
+			            if (fld->lookup) {
+			                if (fld->indexed)
+					    has_indexed = 1;
+			                if (clen == 0) {
+				            StrAllocCopy(command, "query ");
+					    clen = 6;
+			                } else {
+					    StrAllocCat(command, " ");
+					    clen++;
+					}
+					sprintf(buf, "%s=\"%s\"",
+				                fld->name, &content[j+1]);
+					StrAllocCat(command, buf);
+			                clen += strlen(buf);
+			            } else {
+			                strcpy(buf,
+				"Warning: non-lookup field ignored<BR>\n");
+				        (*Target->isa->put_block)(Target,
+					                          buf,
+								  strlen(buf));
+			            }
+		                } else if (content[start] == 'r') {
+			            fld->explicit_return = 1;
+		                }
+		                break;
+		            }
+			}
+	            } else if (!strncmp(&content[start],"return=",7)) {
+		        if (!strcmp(&content[start+7],"all")) {
+			    return_type = 1;
+		        } else if (!strcmp(&content[start+7],"selected")) {
+		            return_type = 2;
+		        }
+	            }
+	        }
+            }
+	    start = i + 1;
+	}
+    }
+    FREE(content);
+    if ((clen == 0) || !has_indexed) {
+	NETCLOSE(s);
+	strcpy(buf,
+  "<EM>Error:</EM> At least one indexed field value must be specified!\n");
+	(*Target->isa->put_block)(Target, buf, strlen(buf));
+	strcpy(buf, "</BODY>\n</HTML>\n");
+	(*Target->isa->put_block)(Target, buf, strlen(buf));
+	(*Target->isa->_free)(Target);
+	free_CSOfields();
+	return HT_LOADED;
+    }
+    /*
+     * Append return fields.
+     */
+    if (return_type == 1) {
+	StrAllocCat(command, " return all");
+	clen += 11;
+    } else if (return_type == 2) {
+	StrAllocCat(command, " return");
+	clen += 7;
+	for (fld = CSOfields; fld; fld = fld->next) {
+	    if (fld->explicit_return) {
+	        sprintf(buf, " %s", fld->name);
+	        StrAllocCat(command, buf);
+	        clen += strlen(buf);
+	    }
+	}
+    }
+    sprintf(buf, "%c%c", CR, LF);
+    StrAllocCat(command, buf);
+    clen += strlen(buf);
+    strcpy(buf, "<H2>\n<EM>CSO/PH command:</EM> ");
+    (*Target->isa->put_block)(Target, buf, strlen(buf));
+    (*Target->isa->put_block)(Target, command, clen);
+    strcpy(buf, "</H2>\n");
+    (*Target->isa->put_block)(Target, buf, strlen(buf));
+    if (TRACE)
+        fprintf(stderr,
+		"HTLoadCSO: Writing command `%s' to socket %d\n",
+		command, s);
+    status = NETWRITE(s, command, clen);
+    FREE(command);
+    if (status < 0) {
+	if (TRACE)
+	    fprintf(stderr, "HTLoadCSO: Unable to send command.\n");
+	free_CSOfields();
+	return HTInetStatus("send");
+    }
+    generate_cso_report(buf, Target);
+    NETCLOSE(s);
+    strcpy(buf, "</BODY>\n</HTML>\n");
+    (*Target->isa->put_block)(Target, buf, strlen(buf));
+    (*Target->isa->_free)(Target);
+    FREE(host);
+    free_CSOfields();
+    return HT_LOADED;
+}
+
+
+/*		Load by name					HTLoadGopher
+**		============
+**
+**	 Bug:	No decoding of strange data types as yet.
+**
+*/
+PUBLIC int HTLoadGopher ARGS4(
+	CONST char *,		arg,
+	HTParentAnchor *,	anAnchor,
+	HTFormat,		format_out,
+	HTStream*,		sink)
+{
+    char *command;			/* The whole command */
+    int status;				/* tcp return */
+    char gtype;				/* Gopher Node type */
+    char * selector;			/* Selector string */
+
+    struct sockaddr_in soc_address;	/* Binary network address */
+    struct sockaddr_in* sin = &soc_address;
+
+    if (!acceptable_inited)
+         init_acceptable();
+    
+    if (!arg)
+        return -3;		/* Bad if no name sepcified	*/
+    if (!*arg)
+        return -2;		/* Bad if name had zero length	*/
+    if (TRACE)
+        fprintf(stderr, "HTGopher: Looking for %s\n", arg);
+
+    /*  If it's a port 105 GOPHER_CSO gtype with no ISINDEX token ('?'),
+    **  use the form-based CSO gateway (otherwise, return an ISINDEX
+    **  cover page or do the ISINDEX search). - FM
+    */
+    {
+        int len;
+
+	if ((len = strlen(arg)) > 5) {
+	    if (0 == strcmp((char *)&arg[len-6], ":105/2")) {
+	        /* Use CSO gateway. */
+		if (TRACE)
+		    fprintf(stderr, "HTGopher: Passing to CSO/PH gateway.\n");
+                return HTLoadCSO(arg, anAnchor, format_out, sink);
+            }
+	}
+    }
+
+    /* If it's a port 79/0[/...] URL, use the finger gateway. - FM
+    */
+    if (strstr(arg, ":79/0") != NULL) {
+        if (TRACE)
+	    fprintf(stderr, "HTGopher: Passing to finger gateway.\n");
+	return HTLoadFinger(arg, anAnchor, format_out, sink);
+    }
+    
+/*  Set up defaults:
+*/
+    sin->sin_family = AF_INET;	    		/* Family, host order  */
+    sin->sin_port = htons(GOPHER_PORT);	    	/* Default: new port,  */
+
+/* Get node name and optional port number:
+*/
+    {
+	char *p1 = HTParse(arg, "", PARSE_HOST);
+	int status = HTParseInet(sin, p1);
+        FREE(p1);
+        if (status)
+	    return status;   /* Bad */
+    }
+
+/* Get entity type, and selector string.
+*/        
+    {
+	char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION);
+        gtype = '1';		/* Default = menu */
+	selector = p1;
+	if ((*selector++=='/') && (*selector)) {	/* Skip first slash */
+	    gtype = *selector++;			/* Pick up gtype */
+	}
+	if (gtype == GOPHER_INDEX) {
+	    char * query;
+	    /* Search is allowed */
+            HTAnchor_setIndex(anAnchor, anAnchor->address);	
+	    query = strchr(selector, '?');	/* Look for search string */
+	    if (!query || !query[1]) {		/* No search required */
+		target = HTML_new(anAnchor, format_out, sink);
+		targetClass = *target->isa;
+		display_index(arg, anAnchor);	/* Display "cover page" */
+		return HT_LOADED;		/* Local function only */
+	    }
+	    *query++ = '\0';			/* Skip '?' 	*/
+	    command =
+	    	    (char *)malloc(strlen(selector)+ 1 + strlen(query)+ 2 + 1);
+              if (command == NULL)
+	          outofmem(__FILE__, "HTLoadGopher");
+	      
+	    de_escape(command, selector);	/* Bug fix TBL 921208 */
+
+	    strcat(command, "\t");
+	  
+	    {					/* Remove plus signs 921006 */
+	    	char *p;
+		for (p=query; *p; p++) {
+		    if (*p == '+') *p = ' ';
+		}
+	    }
+
+	    de_escape(&command[strlen(command)], query);/* bug fix LJM 940415 */
+        } else if (gtype == GOPHER_CSO) {
+            char * query;
+	    /* Search is allowed */
+            query = strchr(selector, '?');	/* Look for search string */
+            if (!query || !query[1]) {		/* No search required */
+		target = HTML_new(anAnchor, format_out, sink);
+		targetClass = *target->isa;
+                display_cso(arg, anAnchor);	/* Display "cover page" */
+                return HT_LOADED;		/* Local function only */
+            }
+            HTAnchor_setIndex(anAnchor, anAnchor->address);	
+            *query++ = '\0';			/* Skip '?'     */
+            command = (char *)malloc(strlen("query")+1 + strlen(query)+2+1);
+              if (command == NULL)
+	          outofmem(__FILE__, "HTLoadGopher");
+
+            de_escape(command, selector);       /* Bug fix TBL 921208 */
+
+            strcpy(command, "query ");
+
+            {                                   /* Remove plus signs 921006 */
+                char *p;
+                for (p=query; *p; p++) {
+                    if (*p == '+') *p = ' ';
+                }
+            }
+	    de_escape(&command[strlen(command)], query);/* bug fix LJM 940415 */
+
+	    
+	} else {				/* Not index */
+	    command = (char *)malloc(strlen(selector)+2+1);
+	    de_escape(command, selector);
+	}
+	FREE(p1);
+    }
+    
+    {
+	char * p = command + strlen(command);
+	*p++ = CR;		/* Macros to be correct on Mac */
+	*p++ = LF;
+	*p++ = '\0';
+	/* strcat(command, "\r\n");	*/	/* CR LF, as in rfc 977 */
+    }
+
+/*	Set up a socket to the server for the data:
+*/      
+
+  status = HTDoConnect (arg, "gopher", GOPHER_PORT, &s);
+  if (status == HT_INTERRUPTED)
+    {
+      /* Interrupt cleanly. */
+      if (TRACE)
+        fprintf(stderr,
+        	"HTGopher: Interrupted on connect; recovering cleanly.\n");
+      _HTProgress ("Connection interrupted.");
+      return HT_INTERRUPTED;
+    }
+  if (status < 0) {
+	if (TRACE)
+	    fprintf(stderr,
+	    	    "HTGopher: Unable to connect to remote host for `%s'.\n",
+	    	    arg);
+	FREE(command);
+	return HTInetStatus("connect");
+  }
+    
+    HTInitInput(s);		/* Set up input buffering */
+    
+    if (TRACE)
+        fprintf(stderr,
+		"HTGopher: Connected, writing command `%s' to socket %d\n",
+	        command, s);
+    
+#ifdef NOT_ASCII
+    {
+    	char * p;
+	for(p = command; *p; p++) {
+	    *p = TOASCII(*p);
+	}
+    }
+#endif
+
+    _HTProgress ("Sending Gopher request.");
+
+    status = NETWRITE(s, command, (int)strlen(command));
+    FREE(command);
+    if (status < 0) {
+	if (TRACE)
+	    fprintf(stderr, "HTGopher: Unable to send command.\n");
+	return HTInetStatus("send");
+    }
+
+    _HTProgress ("Gopher request sent; waiting for response.");
+
+/*	Now read the data from the socket:
+*/    
+    switch (gtype) {
+    
+    case GOPHER_TEXT :
+     	HTParseSocket(WWW_PLAINTEXT, format_out, anAnchor, s, sink);
+	break;
+
+    case GOPHER_HTML :
+    case GOPHER_CHTML :
+    	HTParseSocket(WWW_HTML, format_out, anAnchor, s, sink);
+	break;
+
+    case GOPHER_GIF:
+    case GOPHER_IMAGE:
+    case GOPHER_PLUS_IMAGE:
+    	HTParseSocket(HTAtom_for("image/gif"), 
+			   format_out, anAnchor, s, sink);
+  	break;
+
+    case GOPHER_MENU :
+    case GOPHER_INDEX :
+	target = HTML_new(anAnchor, format_out, sink);
+	targetClass = *target->isa;
+        parse_menu(arg, anAnchor);
+	break;
+	 
+    case GOPHER_CSO:
+	target = HTML_new(anAnchor, format_out, sink);
+	targetClass = *target->isa;
+      	parse_cso(arg, anAnchor);
+	break;
+   	
+    case GOPHER_SOUND :
+    case GOPHER_PLUS_SOUND :
+    	HTParseSocket(WWW_AUDIO, format_out, anAnchor, s, sink);
+	break;
+	
+    case GOPHER_PLUS_MOVIE:
+    	HTParseSocket(HTAtom_for("video/mpeg"), format_out, anAnchor, s, sink);
+	break;
+
+    case GOPHER_PLUS_PDF:
+    	HTParseSocket(HTAtom_for("application/pdf"), format_out, anAnchor,
+				  s, sink);
+	break;
+
+    case GOPHER_MACBINHEX:
+    case GOPHER_PCBINARY:
+    case GOPHER_UUENCODED:
+    case GOPHER_BINARY:
+    default:
+        /* Specifying WWW_UNKNOWN forces dump to local disk. */
+        HTParseSocket (WWW_UNKNOWN, format_out, anAnchor, s, sink);
+	break;
+
+    } /* switch(gtype) */
+
+    NETCLOSE(s);
+    return HT_LOADED;
+}
+
+#ifdef GLOBALDEF_IS_MACRO
+#define _HTGOPHER_C_1_INIT { "gopher", HTLoadGopher, NULL }
+GLOBALDEF (HTProtocol, HTGopher, _HTGOPHER_C_1_INIT);
+#define _HTCSO_C_1_INIT { "cso", HTLoadCSO, NULL }
+GLOBALDEF (HTProtocol, HTCSO, _HTCSO_C_1_INIT);
+#else
+GLOBALDEF PUBLIC HTProtocol HTGopher = { "gopher", HTLoadGopher, NULL };
+GLOBALDEF PUBLIC HTProtocol HTCSO = { "cso", HTLoadCSO, NULL };
+#endif /* GLOBALDEF_IS_MACRO */
diff --git a/WWW/Library/Implementation/HTGopher.h b/WWW/Library/Implementation/HTGopher.h
new file mode 100644
index 00000000..947bd3e9
--- /dev/null
+++ b/WWW/Library/Implementation/HTGopher.h
@@ -0,0 +1,27 @@
+/*                                                          Gopher protocol module for libwww
+                                      GOPHER ACCESS
+                                             
+  HISTORY:
+  
+  8 Jan 92               Adapted from HTTP TBL
+                         
+ */
+
+
+#ifndef HTGOPHER_H
+#define HTGOPHER_H
+
+#include "HTAccess.h"
+#include "HTAnchor.h"
+
+#ifdef GLOBALREF_IS_MACRO
+extern GLOBALREF (HTProtocol, HTGopher);
+#else
+GLOBALREF HTProtocol HTGopher;
+#endif /* GLOBALREF_IS_MACRO */
+
+#endif /* HTGOPHER_H */
+
+/*
+
+   end of gopher module */
diff --git a/WWW/Library/Implementation/HTGroup.c b/WWW/Library/Implementation/HTGroup.c
new file mode 100644
index 00000000..e281a578
--- /dev/null
+++ b/WWW/Library/Implementation/HTGroup.c
@@ -0,0 +1,772 @@
+
+/* MODULE							HTGroup.c
+**		GROUP FILE ROUTINES
+**
+**	Contains group file parser and routines to match IP
+**	address templates and to find out group membership.
+**
+**
+** AUTHORS:
+**	AL	Ari Luotonen	luotonen@dxcern.cern.ch
+**
+** HISTORY:
+**
+**
+** BUGS:
+**
+**
+**
+** GROUP DEFINITION GRAMMAR:
+**
+**	string = "sequence of alphanumeric characters"
+**	user_name ::= string
+**	group_name ::= string
+**	group_ref ::= group_name
+**	user_def ::= user_name | group_ref
+**	user_def_list ::= user_def { ',' user_def }
+**	user_part = user_def | '(' user_def_list ')'
+**
+**	templ = "sequence of alphanumeric characters and '*'s"
+**	ip_number_mask ::= templ '.' templ '.' templ '.' templ
+**	domain_name_mask ::= templ { '.' templ }
+**	address ::= ip_number_mask | domain_name_mask
+**	address_def ::= address
+**	address_def_list ::= address_def { ',' address_def }
+**	address_part = address_def | '(' address_def_list ')'
+**
+**	item ::= [user_part] ['@' address_part]
+**	item_list ::= item { ',' item }
+**	group_def ::= item_list
+**	group_decl ::= group_name ':' group_def
+**
+*/
+
+
+
+#include "HTUtils.h"
+#include <string.h>
+#include "HTAAUtil.h"
+#include "HTLex.h"	/* Lexical analysor	*/
+#include "HTGroup.h"	/* Implemented here	*/
+
+#include "LYLeaks.h"
+
+/*
+** Group file parser
+*/
+
+typedef HTList UserDefList;
+typedef HTList AddressDefList;
+
+typedef struct {
+    UserDefList *	user_def_list;
+    AddressDefList *	address_def_list;
+} Item;
+
+typedef struct {
+    char *	name;
+    GroupDef *	translation;
+} Ref;
+
+	
+
+PRIVATE void syntax_error ARGS3(FILE *,	 fp,
+				char *,	 msg,
+				LexItem, lex_item)
+{
+    char buffer[41];
+    int cnt = 0;
+    char ch;
+
+    while ((ch = getc(fp)) != EOF  &&  ch != '\n')
+	if (cnt < 40) buffer[cnt++] = ch;
+    buffer[cnt] = (char)0;
+
+    if (TRACE)
+	fprintf(stderr, "%s %d before: '%s'\nHTGroup.c: %s (got %s)\n",
+		"HTGroup.c: Syntax error in rule file at line",
+		HTlex_line, buffer, msg, lex_verbose(lex_item));
+    HTlex_line++;
+}
+
+
+PRIVATE AddressDefList *parse_address_part ARGS1(FILE *, fp)
+{
+    AddressDefList *address_def_list = NULL;
+    LexItem lex_item;
+    BOOL only_one = NO;
+
+    lex_item = lex(fp);
+    if (lex_item == LEX_ALPH_STR || lex_item == LEX_TMPL_STR)
+	only_one = YES;
+    else if (lex_item != LEX_OPEN_PAREN  ||
+	     ((lex_item = lex(fp)) != LEX_ALPH_STR &&
+	      lex_item != LEX_TMPL_STR)) {
+	syntax_error(fp, "Expecting a single address or '(' beginning list",
+		     lex_item);
+	return NULL;
+    }
+    address_def_list = HTList_new();
+
+    for(;;) {
+	Ref *ref = (Ref*)calloc(1, sizeof(Ref));
+	ref->name = NULL;
+	ref->translation = NULL;
+	StrAllocCopy(ref->name, HTlex_buffer);
+	
+	HTList_addObject(address_def_list, (void*)ref);
+
+	if (only_one || (lex_item = lex(fp)) != LEX_ITEM_SEP)
+	    break;
+	/*
+	** Here lex_item == LEX_ITEM_SEP; after item separator it
+	** is ok to have one or more newlines (LEX_REC_SEP) and
+	** they are ignored (continuation line).
+	*/
+	do {
+	    lex_item = lex(fp);
+	} while (lex_item == LEX_REC_SEP);
+
+	if (lex_item != LEX_ALPH_STR && lex_item != LEX_TMPL_STR) {
+	    syntax_error(fp, "Expecting an address template", lex_item);
+	    HTList_delete(address_def_list);
+	    address_def_list = NULL;
+	    return NULL;
+	}
+    }
+
+    if (!only_one && lex_item != LEX_CLOSE_PAREN) {
+	HTList_delete(address_def_list);
+	address_def_list = NULL;
+	syntax_error(fp, "Expecting ')' closing address list", lex_item);
+	return NULL;
+    }
+    return address_def_list;
+}
+
+
+PRIVATE UserDefList *parse_user_part ARGS1(FILE *, fp)
+{
+    UserDefList *user_def_list = NULL;
+    LexItem lex_item;
+    BOOL only_one = NO;
+
+    lex_item = lex(fp);
+    if (lex_item == LEX_ALPH_STR)
+	only_one = YES;
+    else if (lex_item != LEX_OPEN_PAREN  ||
+	     (lex_item = lex(fp)) != LEX_ALPH_STR) {
+	syntax_error(fp, "Expecting a single name or '(' beginning list",
+		     lex_item);
+	return NULL;
+    }
+    user_def_list = HTList_new();
+
+    for (;;) {
+	Ref *ref = (Ref*)calloc(1, sizeof(Ref));
+	ref->name = NULL;
+	ref->translation = NULL;
+	StrAllocCopy(ref->name, HTlex_buffer);
+
+	HTList_addObject(user_def_list, (void*)ref);
+	
+	if (only_one || (lex_item = lex(fp)) != LEX_ITEM_SEP)
+	    break;
+	/*
+	** Here lex_item == LEX_ITEM_SEP; after item separator it
+	** is ok to have one or more newlines (LEX_REC_SEP) and
+	** they are ignored (continuation line).
+	*/
+	do {
+	    lex_item = lex(fp);
+	} while (lex_item == LEX_REC_SEP);
+
+	if (lex_item != LEX_ALPH_STR) {
+	    syntax_error(fp, "Expecting user or group name", lex_item);
+	    HTList_delete(user_def_list);
+	    user_def_list = NULL;
+	    return NULL;
+	}
+    }
+
+    if (!only_one && lex_item != LEX_CLOSE_PAREN) {
+	HTList_delete(user_def_list);
+	user_def_list = NULL;
+	syntax_error(fp, "Expecting ')' closing user/group list", lex_item);
+	return NULL;
+    }
+    return user_def_list;
+}
+
+
+PRIVATE Item *parse_item ARGS1(FILE *, fp)
+{
+    Item *item = NULL;
+    UserDefList *user_def_list = NULL;
+    AddressDefList *address_def_list = NULL;
+    LexItem lex_item;
+
+    lex_item = lex(fp);
+    if (lex_item == LEX_ALPH_STR || lex_item == LEX_OPEN_PAREN) {
+	unlex(lex_item);
+	user_def_list = parse_user_part(fp);
+	lex_item = lex(fp);
+    }
+
+    if (lex_item == LEX_AT_SIGN) {
+	lex_item = lex(fp);
+	if (lex_item == LEX_ALPH_STR || lex_item == LEX_TMPL_STR ||
+	    lex_item == LEX_OPEN_PAREN) {
+	    unlex(lex_item);
+	    address_def_list = parse_address_part(fp);
+	}
+	else {
+	    if (user_def_list) {
+	        HTList_delete(user_def_list);	/* @@@@ */
+		user_def_list = NULL;
+	    }
+	    syntax_error(fp, "Expected address part (single address or list)",
+			 lex_item);
+	    return NULL;
+	}
+    }
+    else unlex(lex_item);
+
+    if (!user_def_list && !address_def_list) {
+	syntax_error(fp, "Empty item not allowed", lex_item);
+	return NULL;
+    }
+    item = (Item*)calloc(1, sizeof(Item));
+    item->user_def_list = user_def_list;
+    item->address_def_list = address_def_list;
+    return item;
+}
+
+
+PRIVATE ItemList *parse_item_list ARGS1(FILE *, fp)
+{
+    ItemList *item_list = HTList_new();
+    Item *item;
+    LexItem lex_item;
+
+    for(;;) {
+	if (!(item = parse_item(fp))) {
+	    HTList_delete(item_list);	/* @@@@ */
+	    item_list = NULL;
+	    return NULL;
+	}
+	HTList_addObject(item_list, (void*)item);
+	lex_item = lex(fp);
+	if (lex_item != LEX_ITEM_SEP) {
+	    unlex(lex_item);
+	    return item_list;
+	}
+	/*
+	** Here lex_item == LEX_ITEM_SEP; after item separator it
+	** is ok to have one or more newlines (LEX_REC_SEP) and
+	** they are ignored (continuation line).
+	*/
+	do {
+	    lex_item = lex(fp);
+	} while (lex_item == LEX_REC_SEP);
+	unlex(lex_item);
+    }
+}
+
+
+PUBLIC GroupDef *HTAA_parseGroupDef ARGS1(FILE *, fp)
+{
+    ItemList *item_list = NULL;
+    GroupDef *group_def = NULL;
+    LexItem lex_item;
+
+    if (!(item_list = parse_item_list(fp))) {
+	return NULL;
+    }
+    group_def = (GroupDef*)calloc(1, sizeof(GroupDef));
+    group_def->group_name = NULL;
+    group_def->item_list = item_list;
+
+    if ((lex_item = lex(fp)) != LEX_REC_SEP) {
+	syntax_error(fp, "Garbage after group definition", lex_item);
+    }
+    
+    return group_def;
+}    
+
+
+PRIVATE GroupDef *parse_group_decl ARGS1(FILE *, fp)
+{
+    char *group_name = NULL;
+    GroupDef *group_def = NULL;
+    LexItem lex_item;
+
+    do {
+	lex_item = lex(fp);
+    } while (lex_item == LEX_REC_SEP);	/* Ignore empty lines */
+
+    if (lex_item != LEX_ALPH_STR) {
+	if (lex_item != LEX_EOF)
+	    syntax_error(fp, "Expecting group name", lex_item);
+	return NULL;
+    }
+    StrAllocCopy(group_name, HTlex_buffer);
+
+    if (LEX_FIELD_SEP != (lex_item = lex(fp))) {
+	syntax_error(fp, "Expecting field separator", lex_item);
+	FREE(group_name);
+	return NULL;
+    }
+
+    if (!(group_def = HTAA_parseGroupDef(fp))) {
+	FREE(group_name);
+	return NULL;
+    }
+    group_def->group_name = group_name;
+
+    return group_def;
+}
+	
+
+
+/*
+** Group manipulation routines
+*/
+
+PRIVATE GroupDef *find_group_def ARGS2(GroupDefList *,	group_list,
+				       CONST char *,	group_name)
+{
+    if (group_list && group_name) {
+	GroupDefList *cur = group_list;
+	GroupDef *group_def;
+
+	while (NULL != (group_def = (GroupDef*)HTList_nextObject(cur))) {
+	    if (!strcmp(group_name, group_def->group_name)) {
+		return group_def;
+            }
+        }
+    }
+    return NULL;
+}
+
+
+PUBLIC void HTAA_resolveGroupReferences ARGS2(GroupDef *,	group_def,
+					      GroupDefList *,	group_def_list)
+{
+    if (group_def && group_def->item_list && group_def_list) {
+	ItemList *cur1 = group_def->item_list;
+	Item *item;
+
+	while (NULL != (item = (Item*)HTList_nextObject(cur1))) {
+	    UserDefList *cur2 = item->user_def_list;
+	    Ref *ref;
+
+	    while (NULL != (ref = (Ref*)HTList_nextObject(cur2)))
+		ref->translation = find_group_def(group_def_list, ref->name);
+
+	    /* Does NOT translate address_def_list */
+	}
+    }
+}
+
+
+PRIVATE void add_group_def ARGS2(GroupDefList *, group_def_list,
+				 GroupDef *,	 group_def)
+{
+    HTAA_resolveGroupReferences(group_def, group_def_list);
+    HTList_addObject(group_def_list, (void*)group_def);
+}
+
+
+PRIVATE GroupDefList *parse_group_file ARGS1(FILE *, fp)
+{
+    GroupDefList *group_def_list = HTList_new();
+    GroupDef *group_def;
+    
+    while (NULL != (group_def = parse_group_decl(fp)))
+	add_group_def(group_def_list, group_def);
+    
+    return group_def_list;
+}
+
+
+/*
+** Trace functions
+*/
+
+PRIVATE void print_item ARGS1(Item *, item)
+{
+    if (!item)
+	fprintf(stderr, "\tNULL-ITEM\n");
+    else {
+	UserDefList *cur1 = item->user_def_list;
+	AddressDefList *cur2 = item->address_def_list;
+	Ref *user_ref = (Ref*)HTList_nextObject(cur1);
+	Ref *addr_ref = (Ref*)HTList_nextObject(cur2);
+
+	if (user_ref) {
+	    fprintf(stderr, "\t[%s%s", user_ref->name,
+		    (user_ref->translation ? "*REF*" : ""));
+	    while (NULL != (user_ref = (Ref*)HTList_nextObject(cur1)))
+		fprintf(stderr, "; %s%s", user_ref->name,
+			(user_ref->translation ? "*REF*" : ""));
+	    fprintf(stderr, "] ");
+	} else fprintf(stderr, "\tANYBODY ");
+
+	if (addr_ref) {
+	    fprintf(stderr, "@ [%s", addr_ref->name);
+	    while (NULL != (addr_ref = (Ref*)HTList_nextObject(cur2)))
+		fprintf(stderr, "; %s", addr_ref->name);
+	    fprintf(stderr, "]\n");
+	} else fprintf(stderr, "@ ANYADDRESS\n");
+    }
+}
+
+
+PRIVATE void print_item_list ARGS1(ItemList *, item_list)
+{
+    ItemList *cur = item_list;
+    Item *item;
+
+    if (!item_list)
+	fprintf(stderr, "EMPTY");
+    else while (NULL != (item = (Item*)HTList_nextObject(cur)))
+	print_item(item);
+}
+
+
+PUBLIC void HTAA_printGroupDef ARGS1(GroupDef *, group_def)
+{
+    if (!group_def) {
+	fprintf(stderr, "\nNULL RECORD\n");
+	return;
+    }
+
+    fprintf(stderr, "\nGroup %s:\n",
+	    (group_def->group_name ? group_def->group_name : "NULL"));
+
+    print_item_list(group_def->item_list);
+    fprintf(stderr, "\n");
+}
+
+
+PRIVATE void print_group_def_list ARGS1(GroupDefList *, group_list)
+{
+    GroupDefList *cur = group_list;
+    GroupDef *group_def;
+    
+    while (NULL != (group_def = (GroupDef*)HTList_nextObject(cur)))
+	HTAA_printGroupDef(group_def);
+}
+
+
+
+/*
+** IP address template matching
+*/
+
+/* PRIVATE						part_match()
+**		MATCH ONE PART OF INET ADDRESS AGAIST
+**		A PART OF MASK (inet address has 4 parts)
+** ON ENTRY:
+**	tcur	pointer to the beginning of template part.
+**	icur	pointer to the beginning of actual inet
+**		number part.
+**
+** ON EXIT:
+**	returns	YES, if match.
+*/
+PRIVATE BOOL part_match ARGS2(CONST char *, tcur,
+			      CONST char *, icur)
+{
+    char required[4];
+    char actual[4];
+    CONST char *cur;
+    int cnt;
+
+    if (!tcur || !icur) return NO;
+
+    cur=tcur;
+    cnt=0;
+    while (cnt < 3  &&  *cur && *cur != '.')
+	required[cnt++] = *(cur++);
+    required[cnt] = (char)0;
+
+    cur=icur;
+    cnt=0;
+    while (cnt < 3  &&  *cur && *cur != '.')
+	actual[cnt++] = *(cur++);
+    actual[cnt] = (char)0;
+
+    if (TRACE) {
+	BOOL status = HTAA_templateMatch(required, actual);
+	fprintf(stderr, "part_match: req: '%s' act: '%s' match: %s\n",
+		required, actual, (status ? "yes" : "no"));
+	return status;
+    }
+
+    return HTAA_templateMatch(required, actual);
+}
+
+
+
+/* PRIVATE						ip_number_match()
+**		MATCH INET NUMBER AGAINST AN INET NUMBER MASK
+** ON ENTRY:
+**	template	mask to match agaist, e.g. 128.141.*.*
+**	inet_addr	actual inet address, e.g. 128.141.201.74
+**
+** ON EXIT:
+**	returns		YES, if match;  NO, if not.
+*/
+PRIVATE BOOL ip_number_match ARGS2(CONST char *,	template,
+				   CONST char *,	inet_addr)
+{
+    CONST char *tcur = template;
+    CONST char *icur = inet_addr;
+    int cnt;
+    
+    for (cnt=0; cnt<4; cnt++) {
+	if (!tcur || !icur || !part_match(tcur, icur))
+	    return NO;
+	if (NULL != (tcur = strchr(tcur, '.'))) tcur++;
+	if (NULL != (icur = strchr(icur, '.'))) icur++;
+    }
+    return YES;
+}
+
+
+
+/* PRIVATE						is_domain_mask()
+**		DETERMINE IF A GIVEN MASK IS A
+**		DOMAIN NAME MASK OR AN INET NUMBER MASK
+** ON ENTRY:
+**	mask	either a domain name mask,
+**		e.g.
+**			*.cern.ch
+**
+**		or an inet number mask,
+**		e.g.
+**			128.141.*.*
+**
+** ON EXIT:
+**	returns	YES, if mask is a domain name mask.
+**		NO, if it is an inet number mask.
+*/
+PRIVATE BOOL is_domain_mask ARGS1(CONST char *,	mask)
+{
+    CONST char *cur = mask;
+
+    if (!mask) return NO;
+
+    while (*cur) {
+	if (*cur != '.'  &&  *cur != '*'  &&  (*cur < '0' || *cur > '9'))
+	    return YES;	/* Even one non-digit makes it a domain name mask */
+	cur++;
+    }
+    return NO;	/* All digits and dots, so it is an inet number mask */
+}
+
+
+
+/* PRIVATE							ip_mask_match()
+**		MATCH AN IP NUMBER MASK OR IP NAME MASK
+**		AGAINST ACTUAL IP NUMBER OR IP NAME
+**		
+** ON ENTRY:
+**	mask		mask. Mask may be either an inet number
+**			mask or a domain name mask,
+**			e.g.
+**				128.141.*.*
+**			or
+**				*.cern.ch
+**
+**	ip_number	IP number of connecting host.
+**	ip_name		IP name of the connecting host.
+**
+** ON EXIT:
+**	returns		YES, if hostname/internet number
+**			matches the mask.
+**			NO, if no match (no fire).
+*/
+PRIVATE BOOL ip_mask_match ARGS3(CONST char *,	mask,
+				 CONST char *,	ip_number,
+				 CONST char *,	ip_name)
+{
+    if (mask && (ip_number || ip_name)) {
+	if (is_domain_mask(mask)) {
+	    if (HTAA_templateMatch(mask, ip_name))
+		return YES;
+	}
+	else {
+	    if (ip_number_match(mask, ip_number))
+		return YES;
+	}
+    }
+    return NO;
+}
+
+
+
+
+PRIVATE BOOL ip_in_def_list ARGS3(AddressDefList *,	address_def_list,
+				  char *,		ip_number,
+				  char *,		ip_name)
+{
+    if (address_def_list && (ip_number || ip_name)) {
+	AddressDefList *cur = address_def_list;
+	Ref *ref;
+
+	while (NULL != (ref = (Ref*)HTList_nextObject(cur))) {
+	    /* Value of ref->translation is ignored, i.e. */
+	    /* no recursion for ip address tamplates.	  */
+	    if (ip_mask_match(ref->name, ip_number, ip_name))
+		return YES;
+	}
+    }
+    return NO;
+}
+
+
+/*
+** Group file cached reading
+*/
+
+typedef struct {
+    char *	   group_filename;
+    GroupDefList * group_list;
+} GroupCache;
+
+typedef HTList GroupCacheList;
+
+PRIVATE GroupCacheList *group_cache_list = NULL;
+
+
+PUBLIC GroupDefList *HTAA_readGroupFile ARGS1(CONST char *, filename)
+{
+    FILE *fp;
+    GroupCache *group_cache;
+
+    if (!filename || !*filename) return NULL;
+
+    if (!group_cache_list)
+	group_cache_list = HTList_new();
+    else {
+	GroupCacheList *cur = group_cache_list;
+
+	while (NULL != (group_cache = (GroupCache*)HTList_nextObject(cur))) {
+	    if (!strcmp(filename, group_cache->group_filename)) {
+		if (TRACE) fprintf(stderr, "%s '%s' %s\n",
+				   "HTAA_readGroupFile: group file",
+				   filename, "already found in cache");
+		return group_cache->group_list;
+	    } /* if cache match */
+	} /* while cached files remain */
+    } /* cache exists */
+
+    if (TRACE) fprintf(stderr, "HTAA_readGroupFile: reading group file `%s'\n",
+		       filename);
+
+    if (!(fp = fopen(filename, "r"))) {
+	if (TRACE) fprintf(stderr, "%s '%s'\n",
+			   "HTAA_readGroupFile: unable to open group file",
+			   filename);
+	return NULL;
+    }
+
+    if (!(group_cache = (GroupCache*)calloc(1, sizeof(GroupCache))))
+	outofmem(__FILE__, "HTAA_readGroupFile");
+
+    group_cache->group_filename = NULL;
+    StrAllocCopy(group_cache->group_filename, filename);
+    group_cache->group_list = parse_group_file(fp);
+    HTList_addObject(group_cache_list, (void*)group_cache);
+    fclose(fp);
+
+    if (TRACE) {
+	fprintf(stderr, "Read group file '%s', results follow:\n", filename);
+	print_group_def_list(group_cache->group_list);
+    }
+
+    return group_cache->group_list;
+}
+
+
+/* PUBLIC					HTAA_userAndInetInGroup()
+**		CHECK IF USER BELONGS TO TO A GIVEN GROUP
+**		AND THAT THE CONNECTION COMES FROM AN
+**		ADDRESS THAT IS ALLOWED BY THAT GROUP
+** ON ENTRY:
+**	group		the group definition structure.
+**	username	connecting user.
+**	ip_number	browser host IP number, optional.
+**	ip_name		browser host IP name, optional.
+**			However, one of ip_number or ip_name
+**			must be given.
+** ON EXIT:
+**	returns		HTAA_IP_MASK, if IP address mask was
+**			reason for failing.
+**			HTAA_NOT_MEMBER, if user does not belong
+**			to the group.
+**			HTAA_OK if both IP address and user are ok.
+*/
+PUBLIC HTAAFailReasonType HTAA_userAndInetInGroup ARGS4(GroupDef *, group,
+							char *,	    username,
+							char *,	    ip_number,
+							char *,	    ip_name)
+{
+    HTAAFailReasonType reason = HTAA_NOT_MEMBER;
+
+    if (group && username) {
+	ItemList *cur1 = group->item_list;
+	Item *item;
+
+	while (NULL != (item = (Item*)HTList_nextObject(cur1))) {
+	    if (!item->address_def_list ||	/* Any address allowed */
+		ip_in_def_list(item->address_def_list, ip_number, ip_name)) {
+		    
+		if (!item->user_def_list)	/* Any user allowed */
+		    return HTAA_OK;
+		else {
+		    UserDefList *cur2 = item->user_def_list;
+		    Ref *ref;
+
+		    while (NULL != (ref = (Ref*)HTList_nextObject(cur2))) {
+			
+			if (ref->translation) {	/* Group, check recursively */
+			    reason = HTAA_userAndInetInGroup(ref->translation,
+							     username,
+							     ip_number,ip_name);
+			    if (reason == HTAA_OK)
+				return HTAA_OK;
+			}
+			else {	/* Username, check directly */
+			    if (username && *username &&
+				0==strcmp(ref->name, username))
+				return HTAA_OK;
+			}
+		    } /* Every user/group name in this group */
+		} /* search for username */
+	    } /* IP address ok */
+	    else {
+		reason = HTAA_IP_MASK;
+	    }
+	} /* while items in group */
+    } /* valid parameters */
+    
+    return reason;		/* No match, or invalid parameters */
+}
+
+
+PUBLIC void GroupDef_delete ARGS1(GroupDef *, group_def)
+{
+    if (group_def) {
+	FREE(group_def->group_name);
+	if (group_def->item_list) {
+	    HTList_delete(group_def->item_list);	/* @@@@ */
+	    group_def->item_list = NULL;
+	}
+	FREE(group_def);
+    }
+}
+
diff --git a/WWW/Library/Implementation/HTGroup.h b/WWW/Library/Implementation/HTGroup.h
new file mode 100644
index 00000000..496a5077
--- /dev/null
+++ b/WWW/Library/Implementation/HTGroup.h
@@ -0,0 +1,189 @@
+/*                                    GROUP FILE ROUTINES
+                                             
+ */
+
+#ifndef HTGROUP_H
+#define HTGROUP_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+#include "HTList.h"
+
+#ifdef SHORT_NAMES
+#define HTAApGrD        HTAA_parseGroupDef
+#define HTAArGrR        HTAA_resolveGroupReferences
+#define HTAApGrD        HTAA_printGroupDef
+#define HTAAGD_d        GroupDef_delete
+#define HTAAuIIG        HTAA_userAndInetInGroup
+#endif /* SHORT_NAMES */
+
+typedef HTList GroupDefList;
+typedef HTList ItemList;
+
+typedef struct {
+    char *      group_name;
+    ItemList *  item_list;
+} GroupDef;
+
+
+/*
+** Access Authorization failure reasons
+*/
+typedef enum {
+    HTAA_OK,            /* 200 OK                               */
+    HTAA_OK_GATEWAY,    /* 200 OK, acting as a gateway          */
+    HTAA_NO_AUTH,       /* 401 Unauthorized, not authenticated  */
+    HTAA_NOT_MEMBER,    /* 401 Unauthorized, not authorized     */
+    HTAA_IP_MASK,       /* 403 Forbidden by IP mask             */
+    HTAA_BY_RULE,       /* 403 Forbidden by rule                */
+    HTAA_NO_ACL,        /* 403 Forbidden, ACL non-existent      */
+    HTAA_NO_ENTRY,      /* 403 Forbidden, no ACL entry          */
+    HTAA_SETUP_ERROR,   /* 403 Forbidden, server setup error    */
+    HTAA_DOTDOT,        /* 403 Forbidden, URL with /../ illegal */
+    HTAA_HTBIN,         /* 403 Forbidden, /htbin not enabled    */
+    HTAA_NOT_FOUND      /* 404 Not found, or read protected     */
+} HTAAFailReasonType;
+
+/*
+
+Group definition grammar
+
+  string
+                         "sequence of alphanumeric characters"
+                         
+  user_name
+                         string
+                         
+  group_name
+                         string
+                         
+  group_ref
+                         group_name
+                         
+  user_def
+                         user_name | group_ref
+                         
+  user_def_list
+                           user_def { ',' user_def }
+                         
+  user_part
+                         user_def | '(' user_def_list ')'
+                         
+  templ
+                         
+                         "sequence of alphanumeric characters and '*'s"
+                         
+  ip_number_mask
+                         templ '.' templ '.' templ '.' templ
+                         
+  domain_name_mask
+                         templ { '.' templ }
+                         
+  address
+                         
+                         ip_number_mask | domain_name_mask
+                         
+  address_def
+                         
+                         address
+                         
+  address_def_list
+                         address_def { ',' address_def }
+                         
+  address_part
+                         address_def | '(' address_def_list ')'
+                         
+  item
+                         [user_part] ['@' address_part]
+                         
+  item_list
+                         item { ',' item }
+                         
+  group_def
+                         item_list
+                         
+  group_decl
+                         group_name ':' group_def
+                         
+  PARSE GROUP DEFINITION
+  
+ */
+
+PUBLIC GroupDef *HTAA_parseGroupDef PARAMS((FILE * fp));
+/*
+
+Fill in Pointers to referenced Group Definitions in a Group Definition
+
+   References to groups (by their name) are resolved from group_def_list and pointers to
+   those structures are added to group_def.
+   
+ */
+
+PUBLIC void HTAA_resolveGroupReferences PARAMS((GroupDef *     group_def,
+                                                GroupDefList * group_def_list));
+/*
+
+Read Group File (and do caching)
+
+   If group file is already in cache returns a pointer to previously read group definition
+   list.
+   
+ */
+
+PUBLIC GroupDefList *HTAA_readGroupFile PARAMS((CONST char * filename));
+/*
+
+Delete Group Definition
+
+   Groups in cache should never be freed by this function. This should only be used to
+   free group definitions read by HTAA_parseGroupDef.
+   
+ */
+
+PUBLIC void GroupDef_delete PARAMS((GroupDef * group_def));
+/*
+
+Print Out Group Definition (for trace purposes)
+
+ */
+
+PUBLIC void HTAA_printGroupDef PARAMS((GroupDef * group_def));
+/*
+
+Does a User Belong to a Given Set of Groups
+
+   This function checks both the username and the internet address.
+   
+ */
+
+/* PUBLIC                                       HTAA_userAndInetInGroup()
+**              CHECK IF USER BELONGS TO TO A GIVEN GROUP
+**              AND THAT THE CONNECTION COMES FROM AN
+**              ADDRESS THAT IS ALLOWED BY THAT GROUP
+** ON ENTRY:
+**      group           the group definition structure.
+**      username        connecting user.
+**      ip_number       browser host IP number, optional.
+**      ip_name         browser host IP name, optional.
+**                      However, one of ip_number or ip_name
+**                      must be given.
+** ON EXIT:
+**      returns         HTAA_IP_MASK, if IP address mask was
+**                      reason for failing.
+**                      HTAA_NOT_MEMBER, if user does not belong
+**                      to the group.
+**                      HTAA_OK if both IP address and user are ok.
+*/
+PUBLIC HTAAFailReasonType HTAA_userAndInetInGroup PARAMS((GroupDef * group,
+                                                          char *     username,
+                                                          char *     ip_number,
+                                                          char *     ip_name));
+/*
+
+ */
+
+#endif /* not HTGROUP_H */
+/*
+
+   End of file HTGroup.h.  */
diff --git a/WWW/Library/Implementation/HTHistory.c b/WWW/Library/Implementation/HTHistory.c
new file mode 100644
index 00000000..726380a6
--- /dev/null
+++ b/WWW/Library/Implementation/HTHistory.c
@@ -0,0 +1,157 @@
+#include "HTUtils.h"
+#include "tcp.h"		/* for standard io */
+
+#include "HTHistory.h"
+
+#include "LYLeaks.h"
+
+static HTList * history;	/* List of visited anchors */
+
+
+/*				Navigation
+**				==========
+*/
+
+/*		Record the jump to an anchor
+**		----------------------------
+*/
+
+void HTHistory_record
+  ARGS1 (HTAnchor *,destination)
+{
+  if (destination) {
+    if (! history)
+      history = HTList_new();
+    HTList_addObject (history, destination);
+  }
+}
+
+/*		Go back in history (find the last visited node)
+**		------------------
+*/
+
+HTAnchor * HTHistory_backtrack
+  NOARGS  /* FIXME: Should we add a `sticky' option ? */
+{
+  if (HTHistory_canBacktrack())
+    HTList_removeLastObject(history);
+  return(HTAnchor *)HTList_lastObject(history); /* is Home if can't backtrack */
+}
+
+BOOL HTHistory_canBacktrack
+  NOARGS
+{
+  return (HTList_objectAt (history, 1) != NULL);
+}
+
+/*		Browse through references in the same parent node
+**		-------------------------------------------------
+**
+**	Take the n-th child's link after or before the one we took to get here.
+**	Positive offset means go towards most recently added children.
+*/
+
+HTAnchor * HTHistory_moveBy
+ ARGS1 (int,offset)
+{
+  HTAnchor * last = (HTAnchor *)HTList_objectAt (history, 1);
+  if (! last)
+    return NULL;  /* No last visited node */
+  if (last != (HTAnchor *) last->parent) {  /* Was a child */
+    HTList * kids = last->parent->children;
+    int i = HTList_indexOf (kids, last); 
+    HTAnchor * nextOne = (HTAnchor *)HTList_objectAt (kids, i - offset);
+    if (nextOne) {
+      HTAnchor * destination = HTAnchor_followMainLink (nextOne);
+      if (destination) {
+	HTList_removeLastObject (history);
+	HTList_removeLastObject (history);
+	HTList_addObject (history, nextOne);
+	HTList_addObject (history, destination);
+      }
+      return destination;
+    } else {
+      if (TRACE) fprintf(stderr, 
+      		"HTHistory_moveBy: offset by %+d goes out of list %p.\n",
+		offset, (void*)kids);
+      return NULL;
+    }
+  } else {  /* Was a parent */
+    return NULL;  /* FIXME we could possibly follow the next link... */
+  }
+}
+
+BOOL HTHistory_canMoveBy
+ ARGS1 (int,offset)
+{
+  HTAnchor * last = (HTAnchor *)HTList_objectAt (history, 1);
+  if (! last)
+    return NO;  /* No last visited node */
+  if (last != (HTAnchor *) last->parent) {  /* Was a child */
+    HTList * kids = last->parent->children;
+    int i = HTList_indexOf (kids, last); 
+    return (HTList_objectAt (kids, i - offset) != NULL);
+  } else {  /* Was a parent */
+    return NO;  /* FIXME we could possibly follow the next link... */
+  }
+}
+
+
+/*				Retrieval
+**				=========
+*/
+
+/*		Read numbered visited anchor (1 is the oldest)
+**		----------------------------
+*/
+
+HTAnchor * HTHistory_read
+  ARGS1 (int,number)
+{
+  return (HTAnchor *)HTList_objectAt(history, HTList_count (history) - number);
+}
+
+
+/*		Recall numbered visited anchor (1 is the oldest)
+**		------------------------------
+**	This reads the anchor and stores it again in the list, except if last.
+*/
+
+HTAnchor * HTHistory_recall
+  ARGS1 (int,number)
+{
+  HTAnchor * destination =
+    (HTAnchor *)HTList_objectAt (history, HTList_count (history) - number);
+  if (destination && destination != (HTAnchor *)HTList_lastObject (history))
+    HTList_addObject (history, destination);
+  return destination;
+}
+
+/*		Number of Anchors stored
+**		------------------------
+**
+**	This is needed in order to check the validity of certain commands
+**	for menus, etc.
+(not needed for now. Use canBacktrack, etc.)
+int HTHistory_count
+  NOARGS
+{
+  return HTList_count (history);
+}
+*/
+
+/*		Change last history entry
+**		-------------------------
+**
+**	Sometimes we load a node by one anchor but leave by a different
+**	one, and it is the one we left from which we want to remember.
+*/
+
+void HTHistory_leavingFrom
+  ARGS1 (HTAnchor *,anchor)
+{
+  if (HTList_removeLastObject (history))
+    HTList_addObject (history, anchor);
+  else
+    if (TRACE) fprintf(stderr, "HTHistory_leavingFrom: empty history !\n");
+}
diff --git a/WWW/Library/Implementation/HTHistory.h b/WWW/Library/Implementation/HTHistory.h
new file mode 100644
index 00000000..a93781e9
--- /dev/null
+++ b/WWW/Library/Implementation/HTHistory.h
@@ -0,0 +1,112 @@
+/*  */
+
+#ifndef HTHISTORY_H
+#define HTHISTORY_H
+
+#include "HTAnchor.h"
+
+#ifdef SHORT_NAMES
+#define HTHistory_record                HTHiReco
+#define HTHistory_backtrack             HTHiBack
+#define HTHistory_canBacktrack          HTHiCaBa
+#define HTHistory_moveBy                HTHiMoBy
+#define HTHistory_canMoveBy             HTHiCaMo
+#define HTHistory_read                  HTHiRead
+#define HTHistory_recall                HTHiReca
+#define HTHistory_count                 HTHiCoun
+#define HTHistory_leavingFrom           HTHiLeFr
+#endif
+
+/*                              Navigation
+**                              ==========
+*/
+
+/*              Record the jump to an anchor
+**              ----------------------------
+*/
+
+extern void HTHistory_record
+  PARAMS(
+    (HTAnchor * destination)
+  );
+
+/*              Go back in history (find the last visited node)
+**              ------------------
+*/
+
+extern HTAnchor * HTHistory_backtrack
+  NOPARAMS;  /* FIXME: Should we add a `sticky' option ? */
+
+extern BOOL HTHistory_canBacktrack
+  NOPARAMS;
+
+/*              Browse through references in the same parent node
+**              -------------------------------------------------
+**
+**      Take the n-th child's link after or before the one we took to get here.
+**      Positive offset means go towards most recently added children.
+*/
+
+extern HTAnchor * HTHistory_moveBy
+  PARAMS(
+     (int offset)
+     );
+
+extern BOOL HTHistory_canMoveBy
+  PARAMS(
+     (int offset)
+     );
+
+#define HTHistory_next (HTHistory_moveBy (+1))
+#define HTHistory_canNext (HTHistory_canMoveBy (+1))
+#define HTHistory_previous (HTHistory_moveBy (-1))
+#define HTHistory_canPrevious (HTHistory_canMoveBy (-1))
+
+
+/*                              Retrieval
+**                              =========
+*/
+
+/*              Read numbered visited anchor (1 is the oldest)
+**              ----------------------------
+*/
+
+extern HTAnchor * HTHistory_read
+  PARAMS(
+    (int number)
+  );
+
+/*              Recall numbered visited anchor (1 is the oldest)
+**              ------------------------------
+**      This reads the anchor and stores it again in the list, except if last.
+*/
+
+extern HTAnchor * HTHistory_recall
+  PARAMS(
+    (int number)
+  );
+
+/*              Number of Anchors stored
+**              ------------------------
+**
+**      This is needed in order to check the validity of certain commands
+**      for menus, etc.
+(not needed for now. Use canBacktrack, etc.)
+extern int HTHistory_count NOPARAMS;
+*/
+
+/*              Change last history entry
+**              -------------------------
+**
+**      Sometimes we load a node by one anchor but leave by a different
+**      one, and it is the one we left from which we want to remember.
+*/
+extern void HTHistory_leavingFrom
+  PARAMS(
+    (HTAnchor * anchor)
+  );
+
+#endif /* HTHISTORY_H */
+/*
+
+    */
diff --git a/WWW/Library/Implementation/HTInit.c b/WWW/Library/Implementation/HTInit.c
new file mode 100644
index 00000000..764da7b5
--- /dev/null
+++ b/WWW/Library/Implementation/HTInit.c
@@ -0,0 +1,176 @@
+/*		Configuration-specific Initialialization	HTInit.c
+**		----------------------------------------
+*/
+
+/*	Define a basic set of suffixes and presentations
+**	------------------------------------------------
+**
+*/
+
+#include "HTUtils.h"
+
+/* Implements:
+*/
+#include "HTInit.h"
+
+#include "HTML.h"
+#include "HTPlain.h"
+#include "HTMLGen.h"
+#include "HTFile.h"
+#include "HTFormat.h"
+#include "HTMIME.h"
+#include "HTWSRC.h"
+#include "HTFWriter.h"
+
+#include "LYLeaks.h"
+
+PUBLIC void HTFormatInit NOARGS
+{
+#ifdef NeXT
+    HTSetPresentation("application/postscript", "open %s", 1.0, 2.0, 0.0, 0);
+    /* The following needs the GIF previewer -- you might not have it. */
+    HTSetPresentation("image/gif", 		"open %s", 0.3, 2.0, 0.0, 0);
+    HTSetPresentation("image/x-tiff", 		"open %s", 1.0, 2.0, 0.0, 0);
+    HTSetPresentation("audio/basic", 		"open %s", 1.0, 2.0, 0.0, 0);
+    HTSetPresentation("*", 			"open %s", 1.0, 0.0, 0.0, 0);
+#else
+    if (getenv("DISPLAY")) {	/* Must have X11 */
+	HTSetPresentation("application/postscript", "ghostview %s",
+							 1.0, 3.0, 0.0, 0);
+	HTSetPresentation("image/gif", 		"xv %s", 1.0, 3.0, 0.0, 0);
+	HTSetPresentation("image/x-tiff", 	"xv %s", 1.0, 3.0, 0.0, 0);
+	HTSetPresentation("image/jpeg", 	"xv %s", 1.0, 3.0, 0.0, 0);
+    }
+#endif
+    HTSetConversion("www/mime",			"*",		HTMIMEConvert,
+    							1.0, 0.0, 0.0, 0);
+    HTSetConversion("application/x-wais-source","*",		HTWSRCConvert,
+    							1.0, 0.0, 0.0, 0);
+    HTSetConversion("text/html",		"text/x-c",	HTMLToC,
+    							0.5, 0.0, 0.0, 0);
+    HTSetConversion("text/html",		"text/plain",	HTMLToPlain,
+    							0.5, 0.0, 0.0, 0);
+    HTSetConversion("text/html",		"www/present",	HTMLPresent,
+    							1.0, 0.0, 0.0, 0);
+    HTSetConversion("text/plain",		"text/html",	HTPlainToHTML,
+    							1.0, 0.0, 0.0, 0);
+    HTSetConversion("text/plain",		"www/present",	HTPlainPresent,
+    							1.0, 0.0, 0.0, 0);
+    HTSetConversion("application/octet-stream",	"www/present",	HTSaveLocally,
+    							0.1, 0.0, 0.0, 0);
+    HTSetConversion("www/unknown",		"www/present",	HTSaveLocally,
+    							0.3, 0.0, 0.0, 0);
+    HTSetConversion("www/source",		"www/present",	HTSaveLocally,
+    							0.3, 0.0, 0.0, 0);
+}
+
+
+
+/*	Define a basic set of suffixes
+**	------------------------------
+**
+**	The LAST suffix for a type is that used for temporary files
+**	of that type.
+**	The quality is an apriori bias as to whether the file should be
+**	used.  Not that different suffixes can be used to represent files
+**	which are of the same format but are originals or regenerated,
+**	with different values.
+*/
+
+#ifndef NO_INIT
+PUBLIC void HTFileInit NOARGS
+{
+    /*		Suffix     Contenet-Type	Content-Encoding  Quality			*/
+
+    HTSetSuffix(".mime",   "www/mime",			"8bit",   1.0);	/* Internal -- MIME is	*/
+                                                                        /* not recursive	*/
+    HTSetSuffix(".bin",    "application/octet-stream",	"binary", 1.0); /* Uninterpreted binary	*/
+    HTSetSuffix(".oda",    "application/oda",		"binary", 1.0);
+    HTSetSuffix(".pdf",    "application/pdf",		"binary", 1.0);
+    HTSetSuffix(".ai",     "application/postscript",	"8bit",   0.5);	/* Adobe Illustrator	*/
+    HTSetSuffix(".PS",     "application/postscript",	"8bit",	  0.8);	/* PostScript		*/
+    HTSetSuffix(".eps",    "application/postscript",	"8bit",   0.8);
+    HTSetSuffix(".ps",     "application/postscript",	"8bit",   0.8);
+    HTSetSuffix(".rtf",    "application/x-rtf",		"7bit",   1.0);	/* RTF			*/
+    HTSetSuffix(".Z",      "application/x-compressed",	"binary", 1.0);	/* Compressed data	*/
+    HTSetSuffix(".csh",    "application/x-csh",		"7bit",   0.5);	/* C-shell script	*/
+    HTSetSuffix(".dvi",    "application/x-dvi",		"binary", 1.0);	/* TeX DVI		*/
+    HTSetSuffix(".hdf",    "application/x-hdf",		"binary", 1.0);	/* NCSA HDF data file	*/
+    HTSetSuffix(".latex",  "application/x-latex",	"8bit",   1.0);	/* LaTeX source		*/
+    HTSetSuffix(".nc",     "application/x-netcdf",	"binary", 1.0);	/* Unidata netCDF data	*/
+    HTSetSuffix(".cdf",    "application/x-netcdf",	"binary", 1.0);
+    HTSetSuffix(".sh",     "application/x-sh",		"7bit",   0.5);	/* Shell-script		*/
+    HTSetSuffix(".tcl",    "application/x-tcl",		"7bit",   0.5);	/* TCL-script		*/
+    HTSetSuffix(".tex",    "application/x-tex",		"8bit",   1.0);	/* TeX source		*/
+    HTSetSuffix(".texi",   "application/x-texinfo",	"7bit",   1.0);	/* Texinfo		*/
+    HTSetSuffix(".texinfo","application/x-texinfo",	"7bit",   1.0);
+    HTSetSuffix(".t",      "application/x-troff",	"7bit",   0.5);	/* Troff		*/
+    HTSetSuffix(".roff",   "application/x-troff",	"7bit",   0.5);
+    HTSetSuffix(".tr",     "application/x-troff",	"7bit",   0.5);
+    HTSetSuffix(".man",    "application/x-troff-man",	"7bit",   0.5);	/* Troff with man macros*/
+    HTSetSuffix(".me",     "application/x-troff-me",	"7bit",   0.5);	/* Troff with me macros	*/
+    HTSetSuffix(".ms",     "application/x-troff-ms",	"7bit",   0.5);	/* Troff with ms macros	*/
+    HTSetSuffix(".src",    "application/x-wais-source",	"7bit",   1.0);	/* WAIS source		*/
+    HTSetSuffix(".zip",    "application/zip",		"binary", 1.0);	/* PKZIP		*/
+    HTSetSuffix(".bcpio",  "application/x-bcpio",	"binary", 1.0);	/* Old binary CPIO	*/
+    HTSetSuffix(".cpio",   "application/x-cpio",	"binary", 1.0);	/* POSIX CPIO		*/
+    HTSetSuffix(".gtar",   "application/x-gtar",	"binary", 1.0);	/* Gnu tar		*/
+    HTSetSuffix(".shar",   "application/x-shar",	"8bit",   1.0);	/* Shell archive	*/
+    HTSetSuffix(".sv4cpio","application/x-sv4cpio",	"binary", 1.0);	/* SVR4 CPIO		*/
+    HTSetSuffix(".sv4crc", "application/x-sv4crc",	"binary", 1.0);	/* SVR4 CPIO with CRC	*/
+    HTSetSuffix(".tar",    "application/x-tar",		"binary", 1.0);	/* 4.3BSD tar		*/
+    HTSetSuffix(".ustar",  "application/x-ustar",	"binary", 1.0);	/* POSIX tar		*/
+    HTSetSuffix(".snd",    "audio/basic",		"binary", 1.0);	/* Audio		*/
+    HTSetSuffix(".au",     "audio/basic",		"binary", 1.0);
+    HTSetSuffix(".aiff",   "audio/x-aiff",		"binary", 1.0);
+    HTSetSuffix(".aifc",   "audio/x-aiff",		"binary", 1.0);
+    HTSetSuffix(".aif",    "audio/x-aiff",		"binary", 1.0);
+    HTSetSuffix(".wav",    "audio/x-wav",		"binary", 1.0);	/* Windows+ WAVE format	*/
+    HTSetSuffix(".gif",    "image/gif",			"binary", 1.0);	/* GIF			*/
+    HTSetSuffix(".ief",    "image/ief",			"binary", 1.0);	/* Image Exchange fmt	*/
+    HTSetSuffix(".jpg",    "image/jpeg",		"binary", 1.0);	/* JPEG			*/
+    HTSetSuffix(".JPG",    "image/jpeg",		"binary", 1.0);
+    HTSetSuffix(".JPE",    "image/jpeg",		"binary", 1.0);
+    HTSetSuffix(".jpe",    "image/jpeg",		"binary", 1.0);
+    HTSetSuffix(".JPEG",   "image/jpeg",		"binary", 1.0);
+    HTSetSuffix(".jpeg",   "image/jpeg",		"binary", 1.0);
+    HTSetSuffix(".tif",    "image/tiff",		"binary", 1.0);	/* TIFF			*/
+    HTSetSuffix(".tiff",   "image/tiff",		"binary", 1.0);
+    HTSetSuffix(".ras",    "image/cmu-raster",		"binary", 1.0);
+    HTSetSuffix(".pnm",    "image/x-portable-anymap",	"binary", 1.0);	/* PBM Anymap format	*/
+    HTSetSuffix(".pbm",    "image/x-portable-bitmap",	"binary", 1.0);	/* PBM Bitmap format	*/
+    HTSetSuffix(".pgm",    "image/x-portable-graymap",	"binary", 1.0);	/* PBM Graymap format	*/
+    HTSetSuffix(".ppm",    "image/x-portable-pixmap",	"binary", 1.0);	/* PBM Pixmap format	*/
+    HTSetSuffix(".rgb",    "image/x-rgb",		"binary", 1.0);
+    HTSetSuffix(".xbm",    "image/x-xbitmap",		"binary", 1.0);	/* X bitmap		*/
+    HTSetSuffix(".xpm",    "image/x-xpixmap",		"binary", 1.0);	/* X pixmap format	*/
+    HTSetSuffix(".xwd",    "image/x-xwindowdump",	"binary", 1.0);	/* X window dump (xwd)	*/
+    HTSetSuffix(".html",   "text/html",			"8bit",   1.0);	/* HTML			*/
+    HTSetSuffix(".c",      "text/plain",		"7bit",   0.5);	/* C source		*/
+    HTSetSuffix(".h",      "text/plain",		"7bit",   0.5);	/* C headers		*/
+    HTSetSuffix(".C",      "text/plain",		"7bit",   0.5);	/* C++ source		*/
+    HTSetSuffix(".cc",     "text/plain",		"7bit",   0.5);	/* C++ source		*/
+    HTSetSuffix(".hh",     "text/plain",		"7bit",   0.5);	/* C++ headers		*/
+    HTSetSuffix(".m",      "text/plain",		"7bit",   0.5);	/* Objective-C source	*/
+    HTSetSuffix(".f90",    "text/plain",		"7bit",   0.5);	/* Fortran 90 source	*/
+    HTSetSuffix(".txt",    "text/plain",		"7bit",   0.5);	/* Plain text		*/
+    HTSetSuffix(".rtx",    "text/richtext",		"7bit",   1.0);	/* MIME Richtext format	*/
+    HTSetSuffix(".tsv",    "text/tab-separated-values",	"7bit",   1.0);	/* Tab-separated values	*/
+    HTSetSuffix(".etx",    "text/x-setext",		"7bit",   0.9);	/* Struct Enchanced Txt	*/
+    HTSetSuffix(".MPG",    "video/mpeg",		"binary", 1.0);	/* MPEG			*/
+    HTSetSuffix(".mpg",    "video/mpeg",		"binary", 1.0);
+    HTSetSuffix(".MPE",    "video/mpeg",		"binary", 1.0);
+    HTSetSuffix(".mpe",    "video/mpeg",		"binary", 1.0);
+    HTSetSuffix(".MPEG",   "video/mpeg",		"binary", 1.0);
+    HTSetSuffix(".mpeg",   "video/mpeg",		"binary", 1.0);
+    HTSetSuffix(".qt",     "video/quicktime",		"binary", 1.0);	/* QuickTime		*/
+    HTSetSuffix(".mov",    "video/quicktime",		"binary", 1.0);
+    HTSetSuffix(".avi",    "video/x-msvideo",		"binary", 1.0);	/* MS Video for Windows	*/
+    HTSetSuffix(".movie",  "video/x-sgi-movie",		"binary", 1.0);	/* SGI "moviepalyer"	*/
+    
+    HTSetSuffix("*.*",     "application/octet-stream",	"binary", 0.1);
+    HTSetSuffix("*",       "text/plain",		"7bit",   0.5);
+
+}
+#endif /* NO_INIT */
+
diff --git a/WWW/Library/Implementation/HTInit.h b/WWW/Library/Implementation/HTInit.h
new file mode 100644
index 00000000..b11e7238
--- /dev/null
+++ b/WWW/Library/Implementation/HTInit.h
@@ -0,0 +1,22 @@
+/*                   /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTInit.html
+                                  INITIALISATION MODULE
+                                             
+   This module resisters all the plug & play software modules which will be used in the
+   program.  This is for a browser.
+   
+   To override this, just copy it and link in your version befoe you link with the
+   library.
+   
+   Implemented by HTInit.c by default.
+   
+ */
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+
+extern void HTFormatInit NOPARAMS;
+extern void HTFileInit NOPARAMS;
+
+/*
+
+    */
diff --git a/WWW/Library/Implementation/HTLex.c b/WWW/Library/Implementation/HTLex.c
new file mode 100644
index 00000000..26870170
--- /dev/null
+++ b/WWW/Library/Implementation/HTLex.c
@@ -0,0 +1,142 @@
+
+/* MODULE							HTLex.c
+**		LEXICAL ANALYSOR
+**
+** AUTHORS:
+**	AL	Ari Luotonen	luotonen@dxcern.cern.ch
+**
+** HISTORY:
+**
+**
+** BUGS:
+**
+**
+*/
+
+#include "HTUtils.h"
+#include "HTAAUtil.h"
+#include "HTLex.h"	/* Implemented here */
+
+#include "LYLeaks.h"
+
+/*
+** Global variables
+*/
+PUBLIC char HTlex_buffer[40];	/* Read lexical string		*/
+PUBLIC int HTlex_line = 1;	/* Line number in source file	*/
+
+
+/*
+** Module-wide variables
+*/
+PRIVATE int lex_cnt;
+PRIVATE BOOL lex_template;
+PRIVATE LexItem lex_pushed_back = LEX_NONE;
+PRIVATE FILE *cache = NULL;
+
+
+PUBLIC void unlex ARGS1(LexItem, lex_item)
+{
+    lex_pushed_back = lex_item;
+}
+
+
+PUBLIC LexItem lex ARGS1(FILE *, fp)
+{
+    int ch;
+
+    if (fp != cache) {	/* This cache doesn't work ok because the system  */
+	cache = fp;	/* often assign same FILE structure the next open */
+	HTlex_line = 1;	/* file. So, if there are syntax errors in setup  */
+    }			/* files it may confuse things later on.	  */
+
+    if (lex_pushed_back != LEX_NONE) {
+	LexItem ret = lex_pushed_back;
+	lex_pushed_back = LEX_NONE;
+	return ret;
+    }
+
+    lex_cnt = 0;
+    lex_template = NO;
+
+    for(;;) {
+	switch (ch = getc(fp)) {
+	  case EOF:
+	  case ' ':
+	  case '\t':
+	  case '\r':
+	  case '\n':
+	  case ':':
+	  case ',':
+	  case '(':
+	  case ')':
+	  case '@':
+	    if (lex_cnt > 0) {
+		if (ch != EOF) ungetc(ch,fp);
+		if (lex_template) return LEX_TMPL_STR;
+		else		  return LEX_ALPH_STR;
+	    }
+	    else switch(ch) {
+	      case EOF:		return LEX_EOF;		break;
+	      case '\n':
+		HTlex_line++;	return LEX_REC_SEP;	break;
+	      case ':':		return LEX_FIELD_SEP;	break;
+	      case ',':		return LEX_ITEM_SEP;	break;
+	      case '(':		return LEX_OPEN_PAREN;	break;
+	      case ')':		return LEX_CLOSE_PAREN;	break;
+	      case '@':		return LEX_AT_SIGN;	break;
+	      default:	;	/* Leading white space ignored (SP,TAB,CR) */
+	    }
+	    break;
+	  default:
+	    HTlex_buffer[lex_cnt++] = ch;
+	    HTlex_buffer[lex_cnt] = '\0';
+	    if ('*' == ch) lex_template = YES;
+	} /* switch ch */
+    } /* forever */
+}
+
+
+PUBLIC char *lex_verbose ARGS1(LexItem, lex_item)
+{
+    static char msg[100];
+
+    switch (lex_item) {
+      case LEX_NONE:		/* Internally used	*/
+	return "NO-LEX-ITEM";
+	break;
+      case LEX_EOF:		/* End of file		*/
+	return "end-of-file";
+	break;
+      case LEX_REC_SEP:		/* Record separator	*/
+	return "record separator (newline)";
+	break;
+      case LEX_FIELD_SEP:	/* Field separator	*/
+	return "field separator ':'";
+	break;
+      case LEX_ITEM_SEP:	/* List item separator	*/
+	return "item separator ','";
+	break;
+      case LEX_OPEN_PAREN:	/* Group start tag	*/
+	return "'('";
+	break;
+      case LEX_CLOSE_PAREN:	/* Group end tag	*/
+	return "')'";
+	break;
+      case LEX_AT_SIGN:		/* Address qualifier	*/
+	return "address qualifier '@'";
+	break;
+      case LEX_ALPH_STR:	/* Alphanumeric string	*/
+	sprintf(msg, "alphanumeric string '%s'", HTlex_buffer);
+	return msg;
+	break;
+      case LEX_TMPL_STR:	/* Template string	*/
+	sprintf(msg, "template string '%s'", HTlex_buffer);
+	return msg;
+	break;
+      default:
+	return "UNKNOWN-LEX-ITEM";
+	break;
+    }
+}
+
diff --git a/WWW/Library/Implementation/HTLex.h b/WWW/Library/Implementation/HTLex.h
new file mode 100644
index 00000000..5895579b
--- /dev/null
+++ b/WWW/Library/Implementation/HTLex.h
@@ -0,0 +1,64 @@
+/*                         LEXICAL ANALYSOR (MAINLY FOR CONFIG FILES)
+                                             
+ */
+
+#ifndef HTLEX_H
+#define HTLEX_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+
+
+#ifdef SHORT_NAMES
+#define lex_verb        lex_verbose
+#endif /*SHORT_NAMES*/
+
+
+typedef enum {
+    LEX_NONE,		/* Internally used      */
+    LEX_EOF,		/* End of file          */
+    LEX_REC_SEP,	/* Record separator     */
+    LEX_FIELD_SEP,	/* Field separator      */
+    LEX_ITEM_SEP,	/* List item separator  */
+    LEX_OPEN_PAREN,	/* Group start tag      */
+    LEX_CLOSE_PAREN,	/* Group end tag        */
+    LEX_AT_SIGN,	/* Address qualifier    */
+    LEX_ALPH_STR,	/* Alphanumeric string  */
+    LEX_TMPL_STR	/* Template string      */
+} LexItem;
+
+extern char HTlex_buffer[];	/* Read lexical string          */
+extern int HTlex_line;		/* Line number in source file   */
+
+/*
+
+Get Next Lexical Item
+
+   If returns LEX_ALPH_STR or LEX_TMPL_STR the string is in global buffer lex_buffer.
+   
+ */
+
+PUBLIC LexItem lex PARAMS((FILE * fp));
+/*
+
+Push Back Latest Item
+
+ */
+
+PUBLIC void unlex PARAMS((LexItem lex_item));
+/*
+
+Get the Name for Lexical Item
+
+ */
+
+PUBLIC char *lex_verbose PARAMS((LexItem lex_item));
+/*
+
+ */
+
+#endif /* not HTLEX_H */
+/*
+
+   End of file HTLex.h.  */
diff --git a/WWW/Library/Implementation/HTList.c b/WWW/Library/Implementation/HTList.c
new file mode 100644
index 00000000..7a41e83e
--- /dev/null
+++ b/WWW/Library/Implementation/HTList.c
@@ -0,0 +1,262 @@
+/*	A small List class					      HTList.c
+**	==================
+**
+**	A list is represented as a sequence of linked nodes of type HTList.
+**	The first node is a header which contains no object.
+**	New nodes are inserted between the header and the rest of the list.
+*/
+
+#include "HTUtils.h"
+#include "HTList.h"
+
+/*#include <stdio.h> included by HTUtils.h -- FM *//* joe@athena, TBL 921019 */
+
+#include "LYLeaks.h"
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+
+/*	Create list.
+*/
+PUBLIC HTList * HTList_new NOARGS
+{
+    HTList *newList;
+
+    if ((newList = (HTList *)calloc(1, sizeof(HTList))) == NULL)
+        outofmem(__FILE__, "HTList_new");
+
+    newList->object = NULL;
+    newList->next = NULL;
+
+    return newList;
+}
+
+
+/*	Delete list.
+*/
+PUBLIC void HTList_delete ARGS1(
+	HTList *,	me)
+{
+    HTList *current;
+
+    while ((current = me)) {
+      me = me->next;
+      FREE (current);
+    }
+
+    return;
+}
+
+
+/*      Add object to START of list (so it is pointed to by the head).
+*/
+PUBLIC void HTList_addObject ARGS2(
+	HTList *,	me,
+	void *,		newObject)
+{
+    HTList *newNode;
+
+    if (me) {
+        if ((newNode = (HTList *)calloc(1, sizeof(HTList))) == NULL)
+	    outofmem(__FILE__, "HTList_addObject");
+	newNode->object = newObject;
+	newNode->next = me->next;
+	me->next = newNode;
+
+    } else if (TRACE) {
+        fprintf(stderr,
+		"HTList: Trying to add object %p to a nonexisting list\n",
+		newObject);
+    }
+
+    return;
+}
+
+
+/*      Append object to END of list (furthest from the head).
+*/
+PUBLIC void HTList_appendObject ARGS2(
+	HTList *,	me,
+	void *,		newObject)
+{
+    HTList *temp = me;
+
+    if (temp && newObject) {
+	while (temp->next)
+	    temp = temp->next;
+	HTList_addObject(temp, newObject);
+    }
+
+    return;
+}
+
+
+/*	Remove specified object from list.
+*/
+PUBLIC BOOL HTList_removeObject ARGS2(
+	HTList *,	me,
+	void *,		oldObject)
+{
+    HTList *temp = me;
+    HTList *prevNode;
+
+    if (temp && oldObject) {
+	while (temp->next) {
+	    prevNode = temp;
+	    temp = temp->next;
+	    if (temp->object == oldObject) {
+	        prevNode->next = temp->next;
+		FREE (temp);
+		return YES;  /* Success */
+	    }
+	}
+    }
+    return NO;  /* object not found or NULL list */
+}
+
+
+/*	Remove object at a given position in the list, where 0 is the
+**	object pointed to by the head (returns a pointer to the element
+**	(->object) for the object, and NULL if the list is empty, or
+**	if it doesn't exist - Yuk!).
+*/
+PUBLIC void * HTList_removeObjectAt  ARGS2(
+	HTList *,	me,
+	int,		position)
+{
+    HTList * temp = me;
+    HTList * prevNode;
+    int pos = position;
+
+    if (!temp || pos < 0)
+	return NULL;
+
+    prevNode = temp;
+    while ((temp = temp->next)) {
+	if (pos == 0) {
+	    prevNode->next = temp->next;
+	    prevNode = temp;
+	    FREE(temp);
+	    return prevNode->object;
+	}
+	prevNode = temp;
+	pos--;
+    }
+
+    return NULL;  /* Reached the end of the list */
+}
+
+
+/*	Remove object from START of list (the Last one inserted
+**	via HTList_addObject(), and pointed to by the head).
+*/
+PUBLIC void * HTList_removeLastObject ARGS1(
+	HTList *,	me)
+{
+    HTList * lastNode;
+    void * lastObject;
+
+    if (me && me->next) {
+        lastNode = me->next;
+	lastObject = lastNode->object;
+	me->next = lastNode->next;
+	FREE (lastNode);
+	return lastObject;
+
+    } else {  /* Empty list */
+        return NULL;
+    }
+}
+
+
+/*	Remove object from END of list (the First one inserted
+**	via HTList_addObject(), and furthest from the head).
+*/
+PUBLIC void * HTList_removeFirstObject ARGS1(
+	HTList *,	me)
+{
+    HTList * temp = me;
+    HTList * prevNode;
+    void *firstObject;
+
+    if (!temp)
+        return NULL;
+
+    prevNode = temp;
+    if (temp->next) {
+	while (temp->next) {
+	    prevNode = temp;
+	    temp = temp->next;
+	}
+	firstObject = temp->object;
+	prevNode->next = NULL;
+	FREE (temp);
+	return firstObject;
+
+    } else {  /* Empty list */
+        return NULL;
+    }
+}
+
+
+/*	Determine total number of objects in the list,
+**	not counting the head.
+*/
+PUBLIC int HTList_count ARGS1(
+	HTList *,	me)
+{
+    HTList * temp = me;
+    int count = 0;
+
+    if (temp)
+        while ((temp = temp->next))
+	    count++;
+
+    return count;
+}
+
+
+/*	Determine position of an object in the list (a value of 0
+**	means it is pointed to by the head; returns -1 if not found).
+*/
+PUBLIC int HTList_indexOf ARGS2(
+	HTList *,	me,
+	void *,		object)
+{
+    HTList * temp = me;
+    int position = 0;
+
+    if (temp) {
+	while ((temp = temp->next)) {
+	    if (temp->object == object)
+	        return position;
+	    position++;
+	}
+    }
+
+    return -1;	/* Object not in the list */
+}
+
+
+/*	Return pointer to the object at a specified position in the list,
+**	where 0 is the object pointed to by the head (returns NULL if
+**	the list is empty, or if it doesn't exist - Yuk!).
+*/
+PUBLIC void * HTList_objectAt ARGS2(
+	HTList *,	me,
+	int,		position)
+{
+    HTList * temp = me;
+    int pos = position;
+
+    if (!temp || pos < 0)
+	return NULL;
+
+    while ((temp = temp->next)) {
+	if (pos == 0)
+	    return temp->object;
+	pos--;
+    }
+
+    return NULL;	/* Reached the end of the list */
+}
diff --git a/WWW/Library/Implementation/HTList.h b/WWW/Library/Implementation/HTList.h
new file mode 100644
index 00000000..ddab85ca
--- /dev/null
+++ b/WWW/Library/Implementation/HTList.h
@@ -0,0 +1,136 @@
+
+/*              List object
+**
+**      The list object is a generic container for storing collections
+**      of things in order.
+*/
+#ifndef HTLIST_H
+#define HTLIST_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"  /* for BOOL type and PARAMS and ARGS*/
+#endif /* HTUTILS_H */
+
+typedef struct _HTList HTList;
+
+struct _HTList {
+	void *		object;
+	HTList *	next;
+};
+
+#ifdef SHORT_NAMES
+#define HTList_new                      HTLiNew
+#define HTList_delete                   HTLiDele
+#define HTList_addObject                HTLiAdOb
+#define HTList_removeObject             HTLiReOb
+#define HTList_removeObjectAt           HTLiReAt
+#define HTList_removeLastObject         HTLiReLa
+#define HTList_removeFirstObject        HTLiReFi
+#define HTList_count                    HTLiCoun
+#define HTList_indexOf                  HTLiInOf
+#define HTList_objectAt                 HTLiObAt
+#endif /* SHORT_NAMES */
+
+
+/*	Fast macro to traverse a list.  Call it first with copy of the list
+**	header.  It returns the first object and increments the passed list
+**	pointer.  Call it with the same variable until it returns NULL.
+*/
+#define HTList_nextObject(me) \
+	((me) && ((me) = (me)->next) ? (me)->object : NULL)
+
+
+/*	Macro to find object pointed to by the head (returns NULL
+**	if list is empty, OR if it doesn't exist - Yuk!)
+*/
+#define HTList_lastObject(me) \
+	((me) && (me)->next ? (me)->next->object : NULL)
+
+
+/*	Macro to check if a list is empty (or doesn't exist - Yuk!)
+*/
+#define HTList_isEmpty(me) ((me) ? ((me)->next == NULL) : YES)
+
+
+/*	Create list.
+*/
+extern HTList * HTList_new NOPARAMS;
+
+
+/*	Delete list.
+*/
+extern void HTList_delete PARAMS((
+	HTList *	me));
+
+
+/*      Add object to START of list (so it is pointed to by the head).
+*/
+extern void HTList_addObject PARAMS((
+	HTList *	me,
+	void *		newObject));
+
+
+/*      Append object to END of list (furthest from the head).
+*/
+extern void HTList_appendObject PARAMS((
+	HTList *	me,
+	void *		newObject));
+
+
+/*	Remove specified object from list.
+*/
+extern BOOL HTList_removeObject PARAMS((
+	HTList *	me,
+	void *		oldObject));
+
+
+/*	Remove object at a given position in the list, where 0 is the
+**	object pointed to by the head (returns a pointer to the element
+**	(->object) for the object, and NULL if the list is empty, or
+**	if it doesn't exist - Yuk!).
+*/
+extern void * HTList_removeObjectAt PARAMS((
+	HTList *	me,
+	int		position));
+
+
+/*	Remove object from START of list (the Last one inserted
+**	via HTList_addObject(), and pointed to by the head).
+*/
+extern void * HTList_removeLastObject PARAMS((
+	HTList *	me));
+
+
+/*	Remove object from END of list (the First one inserted
+**	via HTList_addObject(), and furthest from the head).
+*/
+extern void * HTList_removeFirstObject PARAMS((
+	HTList *	me));
+
+
+/*	Determine total number of objects in the list,
+**	not counting the head.
+*/
+extern int HTList_count PARAMS((
+	HTList *	me));
+
+
+/*	Determine position of an object in the list (a value of 0
+**	means it is pointed to by the head; returns -1 if not found).
+*/
+extern int HTList_indexOf PARAMS((
+	HTList *	me,
+	void *		object));
+
+
+/*	Return pointer to the object at a specified position in the list,
+**	where 0 is the object pointed to by the head (returns NULL if
+**	the list is empty, or if it doesn't exist - Yuk!).
+*/
+extern void * HTList_objectAt PARAMS((
+	HTList *	me,
+	int		position));
+
+
+#endif /* HTLIST_H */
+
diff --git a/WWW/Library/Implementation/HTMIME.c b/WWW/Library/Implementation/HTMIME.c
new file mode 100644
index 00000000..313c34b9
--- /dev/null
+++ b/WWW/Library/Implementation/HTMIME.c
@@ -0,0 +1,2009 @@
+/*			MIME Message Parse			HTMIME.c
+**			==================
+**
+**	This is RFC 1341-specific code.
+**	The input stream pushed into this parser is assumed to be
+**	stripped on CRs, ie lines end with LF, not CR LF.
+**	(It is easy to change this except for the body part where
+**	conversion can be slow.)
+**
+** History:
+**	   Feb 92	Written Tim Berners-Lee, CERN
+**
+*/
+#include "HTUtils.h"
+#include "HTMIME.h"		/* Implemented here */
+#include "HTAlert.h"
+#include "HTCJK.h"
+
+#include "LYLeaks.h"
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+extern int current_char_set;
+extern char *LYchar_set_names[];
+extern BOOL HTPassEightBitRaw;
+extern HTCJKlang HTCJK;
+
+
+/*		MIME Object
+**		-----------
+*/
+
+extern int loading_length;	/* from HTFormat.c (for HTCopy) */
+
+typedef enum _MIME_state {
+	MIME_TRANSPARENT,	/* put straight through to target ASAP! */
+	miBEGINNING_OF_LINE,	/* first character and not a continuation */
+	miA,
+	miACCEPT_RANGES,
+	miAGE,
+	miAL,
+	miALLOW,
+	miALTERNATES,
+	miC,
+	miCACHE_CONTROL,
+	miCO,
+	miCOOKIE,
+	miCON,
+	miCONNECTION,
+	miCONTENT_,
+	miCONTENT_BASE,
+	miCONTENT_ENCODING,
+	miCONTENT_FEATURES,
+	miCONTENT_L,
+	miCONTENT_LANGUAGE,
+	miCONTENT_LENGTH,
+	miCONTENT_LOCATION,
+	miCONTENT_MD5,
+	miCONTENT_RANGE,
+	miCONTENT_T,
+	miCONTENT_TRANSFER_ENCODING,
+	miCONTENT_TYPE,
+	miDATE,
+	miE,
+	miETAG,
+	miEXPIRES,
+	miKEEP_ALIVE,
+	miL,
+	miLAST_MODIFIED,
+	miLINK,
+	miLOCATION,
+	miP,
+	miPR,
+	miPRAGMA,
+	miPROXY_AUTHENTICATE,
+	miPUBLIC,
+	miRETRY_AFTER,
+	miSERVER,
+	miT,
+	miTITLE,
+	miTRANSFER_ENCODING,
+	miU,
+	miUPGRADE,
+	miURI,
+	miV,
+	miVARY,
+	miVIA,
+	miW,
+	miWARNING,
+	miWWW_AUTHENTICATE,
+	miSKIP_GET_VALUE,	/* Skip space then get value */
+	miGET_VALUE,		/* Get value till white space */
+	miJUNK_LINE,		/* Ignore the rest of this folded line */
+	miNEWLINE,		/* Just found a LF .. maybe continuation */
+	miCHECK,		/* check against check_pointer */
+	MIME_NET_ASCII,		/* Translate from net ascii */
+	MIME_IGNORE		/* Ignore entire file */
+	/* TRANSPARENT and IGNORE are defined as stg else in _WINDOWS */
+} MIME_state;
+
+#define VALUE_SIZE 1024		/* @@@@@@@ Arbitrary? */
+struct _HTStream {
+	CONST HTStreamClass *	isa;
+	
+	BOOL			net_ascii;	/* Is input net ascii? */
+	MIME_state		state;		/* current state */
+	MIME_state		if_ok;		/* got this state if match */
+	MIME_state		field;		/* remember which field */
+	MIME_state		fold_state;	/* state on a fold */
+	CONST char *		check_pointer;	/* checking input */
+	
+	char *			value_pointer;	/* storing values */
+	char 			value[VALUE_SIZE];
+	
+	HTParentAnchor *	anchor;		/* Given on creation */
+	HTStream *		sink;		/* Given on creation */
+	
+	char *			boundary;	/* For multipart */
+	
+	HTFormat		encoding;	/* Content-Transfer-Encoding */
+	char *			compression_encoding;
+	int			content_length;
+	HTFormat		format;		/* Content-Type */
+	HTStream *		target;		/* While writing out */
+	HTStreamClass		targetClass;
+	
+	HTAtom *		targetRep;	/* Converting into? */
+};
+
+
+/*_________________________________________________________________________
+**
+**			A C T I O N 	R O U T I N E S
+*/
+
+/*	Character handling
+**	------------------
+**
+**	This is a FSM parser which is tolerant as it can be of all
+**	syntax errors.  It ignores field names it does not understand,
+**	and resynchronises on line beginnings.
+*/
+
+PRIVATE void HTMIME_put_character ARGS2(HTStream *, me, char, c)
+{
+    int i, j;
+
+    if (me->state == MIME_TRANSPARENT) {
+    	(*me->targetClass.put_character)(me->target, c);/* MUST BE FAST */
+	return;
+    }
+
+    /* This slightly simple conversion just strips CR and turns LF to
+    ** newline. On unix LF is \n but on Mac \n is CR for example.
+    ** See NetToText for an implementation which preserves single CR or LF.
+    */
+    if (me->net_ascii) {
+        c = FROMASCII(c);
+	if (c == CR)
+	    return;
+	else if (c == LF)
+	    c = '\n';
+    }
+
+    switch(me->state) {
+
+    case MIME_IGNORE:
+    	return;
+
+    case MIME_TRANSPARENT:		/* Not reached see above */
+    	(*me->targetClass.put_character)(me->target, c);
+	return;
+
+    case MIME_NET_ASCII:
+    	(*me->targetClass.put_character)(me->target, c); /* MUST BE FAST */
+	return;
+
+    case miNEWLINE:
+	if (c != '\n' && WHITE(c)) {		/* Folded line */
+	    me->state = me->fold_state;	/* pop state before newline */
+	    break;
+	}
+
+	/*	else Falls through */
+	
+    case miBEGINNING_OF_LINE:
+        me->net_ascii = YES;
+	switch (c) {
+	case 'a':
+	case 'A':
+	    me->state = miA;
+	    if (TRACE)
+	        fprintf(stderr,
+		       "HTMIME: Got 'A' at beginning of line, state now A\n");
+	    break;
+
+	case 'c':
+	case 'C':
+	    me->state = miC;
+	    if (TRACE)
+	        fprintf (stderr,
+		       "HTMIME: Got 'C' at beginning of line, state now C\n");
+	    break;
+
+	case 'd':
+	case 'D':
+	    me->check_pointer = "ate:";
+	    me->if_ok = miDATE;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf (stderr,
+	      "HTMIME: Got 'D' at beginning of line, checking for 'ate:'\n");
+	    break;
+
+	case 'e':
+	case 'E':
+	    me->state = miE;
+	    if (TRACE)
+	        fprintf (stderr,
+		       "HTMIME: Got 'E' at beginning of line, state now E\n");
+	    break;
+
+	case 'k':
+	case 'K':
+	    me->check_pointer = "eep-alive:";
+	    me->if_ok = miKEEP_ALIVE;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+	 "HTMIME: Got 'S' at beginning of line, checking for 'eep-alive:'\n");
+	    break;
+
+	case 'l':
+	case 'L':
+	    me->state = miL;
+	    if (TRACE)
+	        fprintf (stderr,
+		       "HTMIME: Got 'L' at beginning of line, state now L\n");
+	    break;
+
+	case 'p':
+	case 'P':
+	    me->state = miP;
+	    if (TRACE)
+	        fprintf (stderr,
+		       "HTMIME: Got 'P' at beginning of line, state now P\n");
+	    break;
+
+	case 'r':
+	case 'R':
+	    me->check_pointer = "etry-after:";
+	    me->if_ok = miRETRY_AFTER;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+	 "HTMIME: Got 'R' at beginning of line, checking for 'etry-after'\n");
+	    break;
+
+	case 's':
+	case 'S':
+	    me->check_pointer = "erver:";
+	    me->if_ok = miSERVER;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+	      "HTMIME: Got 'S' at beginning of line, checking for 'erver'\n");
+	    break;
+
+	case 't':
+	case 'T':
+	    me->state = miT;
+	    if (TRACE)
+	        fprintf (stderr,
+		       "HTMIME: Got 'T' at beginning of line, state now T\n");
+	    break;
+
+	case 'u':
+	case 'U':
+	    me->state = miU;
+	    if (TRACE)
+	        fprintf (stderr,
+		       "HTMIME: Got 'U' at beginning of line, state now U\n");
+	    break;
+
+	case 'v':
+	case 'V':
+	    me->state = miV;
+	    if (TRACE)
+	        fprintf (stderr,
+		       "HTMIME: Got 'V' at beginning of line, state now V\n");
+	    break;
+
+	case 'w':
+	case 'W':
+	    me->state = miW;
+	    if (TRACE)
+	        fprintf (stderr,
+		       "HTMIME: Got 'W' at beginning of line, state now W\n");
+	    break;
+
+	case '\n':			/* Blank line: End of Header! */
+	    {
+	        me->net_ascii = NO;
+		if (strchr(HTAtom_name(me->format), ';') != NULL) {
+		    char *cp = NULL, *cp1, *cp2;
+
+		    if (TRACE)
+		        fprintf(stderr,
+				"HTMIME: Extended MIME Content-Type is %s\n",
+				HTAtom_name(me->format));
+		    StrAllocCopy(cp, HTAtom_name(me->format));
+		    /*
+		    **  Note that the Content-Type value was converted
+		    **  to lower case when we loaded into me->format,
+		    **  but there may have been a mixed or upper-case
+		    **  atom, so we'll force lower-casing again. - FM
+		    */
+		    for (i = 0; cp[i]; i++)
+		        cp[i] = TOLOWER(cp[i]);
+		    if ((cp1=strchr(cp, ';')) != NULL) {
+		        if ((cp2=strstr(cp1, "charset")) != NULL) {
+			    cp2 += 7;
+			    while (*cp2 == ' ' || *cp2 == '=')
+			        cp2++;
+			    if (!strncmp(cp2, "us-ascii", 8) ||
+			        !strncmp(cp2, "iso-8859-1", 10)) {
+				*cp1 = '\0';
+				me->format = HTAtom_for(cp);
+				StrAllocCopy(me->anchor->charset,
+					         "iso-8859-1");
+				HTCJK = NOCJK;
+			    } else if
+			       (!strncmp(cp2, "iso-8859-2", 10) &&
+				!strncmp(LYchar_set_names[current_char_set],
+				 	 "ISO Latin 2", 11)) {
+				*cp1 = '\0';
+				me->format = HTAtom_for(cp);
+				StrAllocCopy(me->anchor->charset,
+					     "iso-8859-2");
+				HTPassEightBitRaw = TRUE;
+			    } else if
+			       (!strncmp(cp2, "iso-8859-", 9) &&
+				!strncmp(LYchar_set_names[current_char_set],
+				 	 "Other ISO Latin", 15)) {
+			        /*
+			        **  Hope it's a match, for now. - FM
+			        */
+				*cp1 = '\0';
+				me->format = HTAtom_for(cp);
+				StrAllocCopy(me->anchor->charset,
+					     "iso-8859- ");
+				me->anchor->charset[9] = cp2[9];
+				HTPassEightBitRaw = TRUE;
+				HTAlert(me->anchor->charset);
+			    } else if
+			       (!strncmp(cp2, "koi8-r", 6) &&
+				!strncmp(LYchar_set_names[current_char_set],
+					 "KOI8-R character set", 20)) {
+				*cp1 = '\0';
+				me->format = HTAtom_for(cp);
+				StrAllocCopy(me->anchor->charset,
+					     "koi8-r");
+				HTPassEightBitRaw = TRUE;
+			    } else if
+			       (!strncmp(cp2, "euc-jp", 6) &&
+			        HTCJK == JAPANESE) {
+				*cp1 = '\0';
+				me->format = HTAtom_for(cp);
+				StrAllocCopy(me->anchor->charset,
+					     "euc-jp");
+			    } else if
+			       (!strncmp(cp2, "shift_jis", 9) &&
+			        HTCJK == JAPANESE) {
+				*cp1 = '\0';
+				me->format = HTAtom_for(cp);
+				StrAllocCopy(me->anchor->charset,
+					     "shift_jis");
+			    } else if
+			       (!strncmp(cp2, "iso-2022-jp", 11) &&
+			        HTCJK == JAPANESE) {
+				*cp1 = '\0';
+				me->format = HTAtom_for(cp);
+				StrAllocCopy(me->anchor->charset,
+					     "iso-2022-jp");
+			    } else if
+			       (!strncmp(cp2, "iso-2022-jp-2", 13) &&
+			        HTCJK == JAPANESE) {
+				*cp1 = '\0';
+				me->format = HTAtom_for(cp);
+				StrAllocCopy(me->anchor->charset,
+					     "iso-2022-jp-2");
+			    } else if
+			       (!strncmp(cp2, "euc-kr", 6) &&
+			        HTCJK == KOREAN) {
+				*cp1 = '\0';
+				me->format = HTAtom_for(cp);
+				StrAllocCopy(me->anchor->charset,
+					     "euc-kr");
+			    } else if
+			       (!strncmp(cp2, "iso-2022-kr", 11) &&
+			        HTCJK == KOREAN) {
+				*cp1 = '\0';
+				me->format = HTAtom_for(cp);
+				StrAllocCopy(me->anchor->charset,
+					     "iso-2022-kr");
+			    } else if
+			       ((!strncmp(cp2, "big5", 4) ||
+			         !strncmp(cp2, "cn-big5", 7)) &&
+			        HTCJK == TAIPEI) {
+				*cp1 = '\0';
+				me->format = HTAtom_for(cp);
+				StrAllocCopy(me->anchor->charset,
+					     "big5");
+			    } else if
+			       (!strncmp(cp2, "euc-cn", 6) &&
+			        HTCJK == CHINESE) {
+				*cp1 = '\0';
+				me->format = HTAtom_for(cp);
+				StrAllocCopy(me->anchor->charset,
+					     "euc-cn");
+			    } else if
+			       ((!strncmp(cp2, "gb2312", 6) ||
+			         !strncmp(cp2, "cn-gb", 5)) &&
+			        HTCJK == CHINESE) {
+				*cp1 = '\0';
+				me->format = HTAtom_for(cp);
+				StrAllocCopy(me->anchor->charset,
+					     "gb2312");
+			    } else if
+			       (!strncmp(cp2, "iso-2022-cn", 11) &&
+			        HTCJK == CHINESE) {
+				*cp1 = '\0';
+				me->format = HTAtom_for(cp);
+				StrAllocCopy(me->anchor->charset,
+					     "iso-2022-cn");
+			    }
+			}
+		    }
+		    FREE(cp);
+		}
+		StrAllocCopy(me->anchor->content_type,
+			     HTAtom_name(me->format));
+		if (!me->compression_encoding) {
+	            if (TRACE) {
+		        fprintf(stderr,
+		    "HTMIME: MIME Content-Type is '%s', converting to '%s'\n",
+			 HTAtom_name(me->format), HTAtom_name(me->targetRep));
+		    }
+		} else {
+		    /*
+		    **  Change the format to that for "www/compressed"
+		    **  and set up a stream to deal with it. - FM
+		    */
+	            if (TRACE) {
+		        fprintf(stderr,
+	     "HTMIME: MIME Content-Type is '%s',\n", HTAtom_name(me->format));
+		    }
+		    me->format = HTAtom_for("www/compressed");
+	            if (TRACE) {
+		        fprintf(stderr,
+			 "        Treating as '%s'.  Converting to '%s'\n",
+			 HTAtom_name(me->format), HTAtom_name(me->targetRep));
+		    }
+		}
+		me->target = HTStreamStack(me->format, me->targetRep,
+	 				   me->sink , me->anchor);
+		if (!me->target) {
+		    if (TRACE)
+		        fprintf(stderr, "HTMIME: Can't translate! ** \n");
+		    me->target = me->sink;	/* Cheat */
+		}
+		if (me->target) {
+		    me->targetClass = *me->target->isa;
+		    /*
+		    **  Check for encoding and select state from there,
+		    **  someday, but until we have the relevant code,
+		    **  from now push straight through. - FM
+		    */
+		    me->state = MIME_TRANSPARENT;
+		} else {
+		    me->state = MIME_IGNORE;	/* What else to do? */
+		}
+		FREE(me->compression_encoding);
+	    }
+	    break;
+
+	default:
+	   goto bad_field_name;
+	   break;
+
+	} /* switch on character */
+        break;
+
+    case miA:				/* Check for 'c','g' or 'l' */
+        switch (c) {
+	case 'c':
+	case 'C':
+	    me->check_pointer = "cept-ranges:";
+	    me->if_ok = miACCEPT_RANGES;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+		    "HTMIME: Was A, found C, checking for 'cept-ranges:'\n");
+	    break;
+
+	case 'g':
+	case 'G':
+	    me->check_pointer = "e:";
+	    me->if_ok = miAGE;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+			"HTMIME: Was A, found G, checking for 'e:'\n");
+	    break;
+
+	case 'l':
+	case 'L':
+	    me->state = miAL;
+	    if (TRACE)
+	        fprintf(stderr, "HTMIME: Was A, found L, state now AL'\n");
+	    break;
+
+	default:
+	   if (TRACE)
+	       fprintf(stderr,
+		   "HTMIME: Bad character `%c' found where `%s' expected\n",
+		       c, "'g' or 'l'");
+	    goto bad_field_name;
+	    break;
+
+	} /* switch on character */
+	break;
+
+    case miAL:				/* Check for 'l' or 't' */
+        switch (c) {
+        case 'l':
+        case 'L':
+	    me->check_pointer = "low:";
+	    me->if_ok = miALLOW;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+		      "HTMIME: Was AL, found L, checking for 'low:'\n");
+	    break;
+
+	case 't':
+	case 'T':
+	    me->check_pointer = "ernates:";
+	    me->if_ok = miALTERNATES;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+			"HTMIME: Was AL, found T, checking for 'ernates:'\n");
+	    break;
+
+	default:
+	    if (TRACE)
+		fprintf(stderr,
+		   "HTMIME: Bad character `%c' found where `%s' expected\n",
+			c, "'l' or 't'");
+	    goto bad_field_name;
+	    break;
+
+	} /* switch on character */
+	break;
+
+    case miC:				/* Check for 'a' or 'o' */
+        switch (c) {
+ 	case 'a':
+	case 'A':
+	    me->check_pointer = "che-control:";
+	    me->if_ok = miCACHE_CONTROL;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+		     "HTMIME: Was C, found A, checking for 'che-control:'\n");
+	    break;
+
+	case 'o':
+	case 'O':
+	    me->state = miCO;
+	    if (TRACE)
+	        fprintf(stderr, "HTMIME: Was C, found O, state now CO'\n");
+	    break;
+
+	default:
+	   if (TRACE)
+	       fprintf(stderr,
+		    "HTMIME: Bad character `%c' found where `%s' expected\n",
+		       c, "'a' or 'o'");
+	    goto bad_field_name;
+	    break;
+
+	} /* switch on character */
+	break;
+
+    case miCO:				/* Check for 'n' or 'o' */
+        switch (c) {
+        case 'n':
+        case 'N':
+	    me->state = miCON;
+	    if (TRACE)
+	        fprintf(stderr,
+			"HTMIME: Was CO, found N, state now CON\n");
+ 	    break;
+
+        case 'o':
+        case 'O':
+	    me->check_pointer = "kie:";
+	    me->if_ok = miCOOKIE;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+			"HTMIME: Was CO, found O, checking for 'kie:'\n");
+ 	    break;
+
+	default:
+	    if (TRACE)
+	        fprintf(stderr,
+		   "HTMIME: Bad character `%c' found where `%s' expected\n",
+			c, "'n' or 'o'");
+	    goto bad_field_name;
+	    break;
+
+	} /* switch on character */
+	break;
+
+    case miCON:				/* Check for 'n' or 't' */
+        switch (c) {
+        case 'n':
+        case 'N':
+	    me->check_pointer = "nection:";
+	    me->if_ok = miCONNECTION;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+		      "HTMIME: Was CON, found N, checking for 'nection:'\n");
+	    break;
+
+	case 't':
+	case 'T':
+	    me->check_pointer = "ent-";
+	    me->if_ok = miCONTENT_;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+			"HTMIME: Was CON, found T, checking for 'ent-'\n");
+	    break;
+
+	default:
+	    if (TRACE)
+		fprintf(stderr,
+		   "HTMIME: Bad character `%c' found where `%s' expected\n",
+			c, "'n' or 't'");
+	    goto bad_field_name;
+	    break;
+
+	} /* switch on character */
+	break;
+
+    case miE:				/* Check for 't' or 'x' */
+        switch (c) {
+ 	case 't':
+	case 'T':
+	    me->check_pointer = "ag:";
+	    me->if_ok = miETAG;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+			"HTMIME: Was E, found T, checking for 'ag:'\n");
+	    break;
+
+	case 'x':
+	case 'X':
+	    me->check_pointer = "pires:";
+	    me->if_ok = miEXPIRES;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+			"HTMIME: Was E, found X, checking for 'pires:'\n");
+	    break;
+
+	default:
+	    if (TRACE)
+	        fprintf(stderr,
+		   "HTMIME: Bad character `%c' found where `%s' expected\n",
+		        c, "'t' or 'x'");
+	    goto bad_field_name;
+	    break;
+
+	} /* switch on character */
+	break;
+
+    case miL:				/* Check for 'a', 'i' or 'o' */
+        switch (c) {
+	case 'a':
+	case 'A':
+	    me->check_pointer = "st-modified:";
+	    me->if_ok = miLAST_MODIFIED;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+		     "HTMIME: Was L, found A, checking for 'st-modified:'\n");
+	    break;
+
+	case 'i':
+	case 'I':
+	    me->check_pointer = "nk:";
+	    me->if_ok = miLINK;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+		     "HTMIME: Was L, found I, checking for 'nk:'\n");
+	    break;
+
+	case 'o':
+	case 'O':
+	    me->check_pointer = "cation:";
+	    me->if_ok = miLOCATION;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+			"HTMIME: Was L, found O, checking for 'cation:'\n");
+	    break;
+
+	default:
+	    if (TRACE)
+	        fprintf(stderr,
+		   "HTMIME: Bad character `%c' found where `%s' expected\n",
+		        c, "'a', 'i' or 'o'");
+	    goto bad_field_name;
+	    break;
+
+	} /* switch on character */
+	break;
+
+    case miP:				/* Check for 'r' or 'u' */
+        switch (c) {
+	case 'r':
+	case 'R':
+	    me->state = miPR;
+	    if (TRACE)
+	        fprintf(stderr, "HTMIME: Was P, found O, state now PR'\n");
+	    break;
+
+ 	case 'u':
+	case 'U':
+	    me->check_pointer = "lic:";
+	    me->if_ok = miPUBLIC;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+			"HTMIME: Was P, found U, checking for 'blic:'\n");
+	    break;
+
+	default:
+	    if (TRACE)
+	        fprintf(stderr,
+		   "HTMIME: Bad character `%c' found where `%s' expected\n",
+		        c, "'r' or 'u'");
+	    goto bad_field_name;
+	    break;
+
+	} /* switch on character */
+	break;
+
+    case miPR:				/* Check for 'a' or 'o' */
+        switch (c) {
+	case 'a':
+	case 'A':
+	    me->check_pointer = "agma:";
+	    me->if_ok = miPRAGMA;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+			"HTMIME: Was PR, found A, checking for 'agma'\n");
+	    break;
+
+	case 'o':
+	case 'O':
+	    me->check_pointer = "xy-authenticate:";
+	    me->if_ok = miPROXY_AUTHENTICATE;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+		 "HTMIME: Was PR, found O, checking for 'xy-authenticate'\n");
+	    break;
+
+	default:
+	    if (TRACE)
+	        fprintf(stderr,
+		   "HTMIME: Bad character `%c' found where `%s' expected\n",
+		        c, "'a' or 'o'");
+	    goto bad_field_name;
+	    break;
+
+	} /* switch on character */
+	break;
+
+    case miT:				/* Check for 'i' or 'r' */
+        switch (c) {
+ 	case 'i':
+	case 'I':
+	    me->check_pointer = "tle:";
+	    me->if_ok = miTITLE;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+			"HTMIME: Was T, found I, checking for 'tle:'\n");
+  	    break;
+
+	case 'r':
+	case 'R':
+	    me->check_pointer = "ansfer-encoding:";
+	    me->if_ok = miTRANSFER_ENCODING;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+		 "HTMIME: Was T, found R, checking for 'ansfer-encoding'\n");
+	    break;
+
+	default:
+	    if (TRACE)
+	        fprintf(stderr,
+		   "HTMIME: Bad character `%c' found where `%s' expected\n",
+		        c, "'i' or 'r'");
+	    goto bad_field_name;
+	    break;
+
+	} /* switch on character */
+	break;
+
+    case miU:				/* Check for 'p' or 'r' */
+        switch (c) {
+ 	case 'p':
+	case 'P':
+	    me->check_pointer = "grade:";
+	    me->if_ok = miUPGRADE;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+			"HTMIME: Was U, found P, checking for 'grade:'\n");
+	    break;
+
+	case 'r':
+	case 'R':
+	    me->check_pointer = "i:";
+	    me->if_ok = miURI;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+			"HTMIME: Was U, found R, checking for 'i:'\n");
+  	    break;
+
+	default:
+	    if (TRACE)
+	        fprintf(stderr,
+		   "HTMIME: Bad character `%c' found where `%s' expected\n",
+		        c, "'p' or 'r'");
+	    goto bad_field_name;
+	    break;
+
+	} /* switch on character */
+	break;
+
+    case miV:				/* Check for 'a' or 'i' */
+        switch (c) {
+ 	case 'a':
+	case 'A':
+	    me->check_pointer = "ry:";
+	    me->if_ok = miVARY;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+			"HTMIME: Was V, found A, checking for 'ry:'\n");
+	    break;
+
+	case 'i':
+	case 'I':
+	    me->check_pointer = "a:";
+	    me->if_ok = miVIA;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+			"HTMIME: Was V, found I, checking for 'a:'\n");
+  	    break;
+
+	default:
+	    if (TRACE)
+	        fprintf(stderr,
+		   "HTMIME: Bad character `%c' found where `%s' expected\n",
+		        c, "'a' or 'i'");
+	    goto bad_field_name;
+	    break;
+
+	} /* switch on character */
+	break;
+
+    case miW:				/* Check for 'a' or 'w' */
+        switch (c) {
+ 	case 'a':
+	case 'A':
+	    me->check_pointer = "rning:";
+	    me->if_ok = miWARNING;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+			"HTMIME: Was W, found A, checking for 'rning:'\n");
+	    break;
+
+	case 'w':
+	case 'W':
+	    me->check_pointer = "w-authenticate:";
+	    me->if_ok = miWWW_AUTHENTICATE;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+		  "HTMIME: Was W, found W, checking for 'w-authenticate:'\n");
+  	    break;
+
+	default:
+	    if (TRACE)
+	        fprintf(stderr,
+		   "HTMIME: Bad character `%c' found where `%s' expected\n",
+		        c, "'a' or 'w'");
+	    goto bad_field_name;
+	    break;
+
+	} /* switch on character */
+	break;
+
+    case miCHECK:			/* Check against string */
+        if (TOLOWER(c) == *(me->check_pointer)++) {
+	    if (!*me->check_pointer)
+	        me->state = me->if_ok;
+	} else {		/* Error */
+	    if (TRACE)
+	        fprintf(stderr,
+	    	    "HTMIME: Bad character `%c' found where `%s' expected\n",
+		        c, me->check_pointer - 1);
+	    goto bad_field_name;
+	}
+	break;
+
+    case miCONTENT_:
+	if (TRACE)
+           fprintf (stderr,
+                 "HTMIME: in case CONTENT_\n");
+        switch(c) {
+        case 'b':
+        case 'B':
+	    me->check_pointer = "ase:";
+	    me->if_ok = miCONTENT_BASE;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+		      "HTMIME: Was CONTENT_, found B, checking for 'ase:'\n");
+	    break;
+
+        case 'e':
+        case 'E':
+	    me->check_pointer = "ncoding:";
+	    me->if_ok = miCONTENT_ENCODING;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+		  "HTMIME: Was CONTENT_, found E, checking for 'ncoding:'\n");
+	    break;
+
+        case 'f':
+        case 'F':
+	    me->check_pointer = "eatures:";
+	    me->if_ok = miCONTENT_FEATURES;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+		  "HTMIME: Was CONTENT_, found F, checking for 'eatures:'\n");
+	    break;
+
+        case 'l':
+        case 'L':
+	    me->state = miCONTENT_L;
+	    if (TRACE)
+	        fprintf (stderr,
+		     "HTMIME: Was CONTENT_, found L, state now CONTENT_L\n");
+	    break;
+
+        case 'm':
+        case 'M':
+	    me->check_pointer = "d5:";
+	    me->if_ok = miCONTENT_MD5;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+		      "HTMIME: Was CONTENT_, found M, checking for 'd5:'\n");
+	    break;
+
+        case 'r':
+        case 'R':
+	    me->check_pointer = "ange:";
+	    me->if_ok = miCONTENT_RANGE;
+	    me->state = miCHECK;
+	    if (TRACE)
+	        fprintf(stderr,
+		    "HTMIME: Was CONTENT_, found R, checking for 'ange:'\n");
+	    break;
+
+        case 't':
+        case 'T':
+	    me->state = miCONTENT_T;
+	    if (TRACE)
+	        fprintf(stderr,
+		    "HTMIME: Was CONTENT_, found T, state now CONTENT_T\n");
+	    break;
+
+        default:
+	    if (TRACE)
+	        fprintf(stderr,
+			"HTMIME: Was CONTENT_, found nothing; bleah\n");
+	    goto bad_field_name;
+	    break;
+
+        } /* switch on character */
+	break;
+
+    case miCONTENT_L:
+      if (TRACE)
+        fprintf (stderr,
+                 "HTMIME: in case CONTENT_L\n");
+      switch(c) {
+	case 'a':
+	case 'A':
+	    me->check_pointer = "nguage:";
+	    me->if_ok = miCONTENT_LANGUAGE;
+	    me->state = miCHECK;
+            if (TRACE)
+                fprintf(stderr,
+                   "HTMIME: Was CONTENT_L, found A, checking for 'nguage:'\n");
+	    break;
+	    
+	case 'e':
+	case 'E':
+	    me->check_pointer = "ngth:";
+	    me->if_ok = miCONTENT_LENGTH;
+	    me->state = miCHECK;
+            if (TRACE)
+                fprintf(stderr,
+                   "HTMIME: Was CONTENT_L, found E, checking for 'ngth:'\n");
+	    break;
+	    
+	default:
+          if (TRACE)
+            fprintf (stderr,
+                     "HTMIME: Was CONTENT_L, found nothing; bleah\n");
+	    goto bad_field_name;
+	    break;
+	    
+	} /* switch on character */
+	break;
+
+    case miCONTENT_T:
+      if (TRACE)
+        fprintf (stderr,
+                 "HTMIME: in case CONTENT_T\n");
+      switch(c) {
+	case 'r':
+	case 'R':
+	    me->check_pointer = "ansfer-encoding:";
+	    me->if_ok = miCONTENT_TRANSFER_ENCODING;
+	    me->state = miCHECK;
+            if (TRACE)
+                fprintf(stderr,
+         "HTMIME: Was CONTENT_T, found R, checking for 'ansfer-encoding:'\n");
+	    break;
+	    
+	case 'y':
+	case 'Y':
+	    me->check_pointer = "pe:";
+	    me->if_ok = miCONTENT_TYPE;
+	    me->state = miCHECK;
+            if (TRACE)
+                fprintf(stderr,
+                   "HTMIME: Was CONTENT_T, found Y, checking for 'pe:'\n");
+	    break;
+	    
+	default:
+          if (TRACE)
+            fprintf (stderr,
+                     "HTMIME: Was CONTENT_T, found nothing; bleah\n");
+	    goto bad_field_name;
+	    break;
+	    
+	} /* switch on character */
+	break;
+
+    case miACCEPT_RANGES:
+    case miAGE:
+    case miALLOW:
+    case miALTERNATES:
+    case miCACHE_CONTROL:
+    case miCOOKIE:
+    case miCONNECTION:
+    case miCONTENT_BASE:
+    case miCONTENT_ENCODING:
+    case miCONTENT_FEATURES:
+    case miCONTENT_LANGUAGE:
+    case miCONTENT_LENGTH:
+    case miCONTENT_LOCATION:
+    case miCONTENT_MD5:
+    case miCONTENT_RANGE:
+    case miCONTENT_TRANSFER_ENCODING:
+    case miCONTENT_TYPE:
+    case miDATE:
+    case miETAG:
+    case miEXPIRES:
+    case miKEEP_ALIVE:
+    case miLAST_MODIFIED:
+    case miLINK:
+    case miLOCATION:
+    case miPRAGMA:
+    case miPROXY_AUTHENTICATE:
+    case miPUBLIC:
+    case miRETRY_AFTER:
+    case miSERVER:
+    case miTITLE:
+    case miTRANSFER_ENCODING:
+    case miUPGRADE:
+    case miURI:
+    case miVARY:
+    case miVIA:
+    case miWARNING:
+    case miWWW_AUTHENTICATE:
+        me->field = me->state;		/* remember it */
+	me->state = miSKIP_GET_VALUE;
+				/* Fall through! */
+    case miSKIP_GET_VALUE:
+    	if (c == '\n') {
+	   me->fold_state = me->state;
+	   me->state = miNEWLINE;
+	   break;
+	}
+	if (WHITE(c))
+	    break;	/* Skip white space */
+	
+	me->value_pointer = me->value;
+	me->state = miGET_VALUE;   
+	/* Fall through to store first character */
+	
+    case miGET_VALUE:
+    	if (WHITE(c) && c != 32) {			/* End of field */
+	    char *cp;
+	    *me->value_pointer = '\0';
+	    cp = (me->value_pointer - 1);
+	    while ((cp >= me->value) && *cp == 32)
+	        *cp = '\0';	/* Trim trailing spaces. - FM */
+	    switch (me->field) {
+	    case miACCEPT_RANGES:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Accept-Ranges: '%s'\n",
+			    me->value);
+                break;
+	    case miAGE:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Age: '%s'\n",
+			    me->value);
+                break;
+	    case miALLOW:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Allow: '%s'\n",
+			    me->value);
+                break;
+	    case miALTERNATES:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Alternates: '%s'\n",
+			    me->value);
+                break;
+	    case miCACHE_CONTROL:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Cache-Control: '%s'\n",
+			    me->value);
+		/*
+		**  Convert to lowercase and indicate in anchor. - FM
+		*/
+		for (i = 0; me->value[i]; i++)
+		    me->value[i] = TOLOWER(me->value[i]);
+		StrAllocCopy(me->anchor->cache_control, me->value);
+		/*
+		 *  Check whether to set no_cache for the anchor. - FM
+		 */
+		if (!strcmp(me->value, "no-cache"))
+		    me->anchor->no_cache = TRUE;
+                break;
+	    case miCOOKIE:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Cookie: '%s'\n",
+			    me->value);
+                break;
+	    case miCONNECTION:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Connection: '%s'\n",
+			    me->value);
+                break;
+	    case miCONTENT_BASE:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Content-Base: '%s'\n",
+			    me->value);
+                break;
+            case miCONTENT_ENCODING:
+                if (TRACE)
+                    fprintf(stderr,
+                       "HTMIME: PICKED UP Content-Encoding: '%s'\n",
+                            me->value);
+		if (!(me->value && *me->value))
+		    break;
+		/*
+		**  Convert to lowercase and indicate in anchor. - FM
+		*/
+		for (i = 0; me->value[i]; i++)
+		    me->value[i] = TOLOWER(me->value[i]);
+		StrAllocCopy(me->anchor->content_encoding, me->value);
+		FREE(me->compression_encoding);
+		if (!strcmp(me->value, "8bit") ||
+		    !strcmp(me->value, "7bit") ||
+		    !strcmp(me->value, "binary")) {
+		    /*
+		    **  Some server indicated "8bit", "7bit" or "binary"
+		    **  inappropriately.  We'll ignore it. - FM
+		    */
+		    if (TRACE)
+			fprintf(stderr,
+				"                Ignoring it!\n");
+		} else {
+		    /*
+		    **  Save it to use as a flag for setting
+		    **  up a "www/compressed" target. - FM
+		    */
+		    StrAllocCopy(me->compression_encoding, me->value);
+		}
+		break;
+	    case miCONTENT_FEATURES:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Content-Base: '%s'\n",
+			    me->value);
+                break;
+	    case miCONTENT_LANGUAGE:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Content-Language: '%s'\n",
+			    me->value);
+		/*
+		**  Convert to lowercase and indicate in anchor. - FM
+		*/
+		for (i = 0; me->value[i]; i++)
+		    me->value[i] = TOLOWER(me->value[i]);
+		StrAllocCopy(me->anchor->content_language, me->value);
+                break;
+	    case miCONTENT_LENGTH:
+                me->content_length = atoi(me->value);
+                /* This is TEMPORARY. */
+                loading_length = me->content_length;
+                if (TRACE)
+                    fprintf(stderr,
+                            "HTMIME: PICKED UP Content-Length: '%d'\n",
+                            me->content_length);
+                break;
+	    case miCONTENT_LOCATION:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Content-Location: '%s'\n",
+			    me->value);
+                break;
+	    case miCONTENT_MD5:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Content-MD5: '%s'\n",
+			    me->value);
+                break;
+	    case miCONTENT_RANGE:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Content-Range: '%s'\n",
+			    me->value);
+                break;
+	    case miCONTENT_TRANSFER_ENCODING:
+                if (TRACE)
+                    fprintf(stderr,
+			"HTMIME: PICKED UP Content-Transfer-Encoding: '%s'\n",
+                            me->value);
+		/*
+		**  Force the Content-Transfer-Encoding value
+		**  to all lower case. - FM
+		*/
+		for (i = 0; me->value[i]; i++)
+		    me->value[i] = TOLOWER(me->value[i]);
+	        me->encoding = HTAtom_for(me->value);
+		break;
+	    case miCONTENT_TYPE:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Content-Type: '%s'\n",
+			    me->value);
+		/*
+		**  Force the Content-Type value to all
+		**  lower case and strip spaces. - FM
+		*/
+		for (i = 0, j = 0; me->value[i]; i++) {
+		    if (me->value[i] != ' ') {
+		        me->value[j++] = TOLOWER(me->value[i]);
+		    }
+		}
+		me->value[j] = '\0';
+	        me->format = HTAtom_for(me->value);
+		break;
+	    case miDATE:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Date: '%s'\n",
+			    me->value);
+                break;
+	    case miETAG:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP ETag: '%s'\n",
+			    me->value);
+                break;
+	    case miEXPIRES:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Expires: '%s'\n",
+			    me->value);
+                break;
+	    case miKEEP_ALIVE:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Keep-Alive: '%s'\n",
+			    me->value);
+                break;
+	    case miLAST_MODIFIED:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Last-Modified: '%s'\n",
+			    me->value);
+                break;
+	    case miLINK:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Link: '%s'\n",
+			    me->value);
+                break;
+	    case miLOCATION:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Location: '%s'\n",
+			    me->value);
+                break;
+	    case miPRAGMA:
+                if (TRACE)
+                    fprintf(stderr,
+                            "HTMIME: PICKED UP Pragma: '%s'\n",
+			    me->value);
+		/*
+		 *  Check whether to set no_cache for the anchor. - FM
+		 */
+		if (!strcmp(me->value, "no-cache"))
+		    me->anchor->no_cache = TRUE;
+                break;
+	    case miPROXY_AUTHENTICATE:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Proxy-Authenticate: '%s'\n",
+			    me->value);
+                break;
+	    case miPUBLIC:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Public: '%s'\n",
+			    me->value);
+                break;
+	    case miRETRY_AFTER:
+                if (TRACE)
+                    fprintf(stderr,
+		    	    "HTMIME: PICKED UP Retry-After: '%s'\n",
+			    me->value);
+                break;
+	    case miSERVER:
+                if (TRACE)
+                    fprintf(stderr,
+                            "HTMIME: PICKED UP Server: '%s'\n",
+			    me->value);
+                break;
+	    case miTITLE:
+                if (TRACE)
+                    fprintf(stderr,
+                            "HTMIME: PICKED UP Title: '%s'\n",
+			    me->value);
+                break;
+	    case miTRANSFER_ENCODING:
+                if (TRACE)
+                    fprintf(stderr,
+                            "HTMIME: PICKED UP Transfer-Encoding: '%s'\n",
+			    me->value);
+                break;
+	    case miUPGRADE:
+                if (TRACE)
+                    fprintf(stderr,
+                            "HTMIME: PICKED UP Upgrade: '%s'\n",
+			    me->value);
+                break;
+	    case miURI:
+                if (TRACE)
+                    fprintf(stderr,
+                            "HTMIME: PICKED UP URI: '%s'\n",
+			    me->value);
+                break;
+	    case miVARY:
+                if (TRACE)
+                    fprintf(stderr,
+                            "HTMIME: PICKED UP Vary: '%s'\n",
+			    me->value);
+                break;
+	    case miVIA:
+                if (TRACE)
+                    fprintf(stderr,
+                            "HTMIME: PICKED UP Via: '%s'\n",
+			    me->value);
+                break;
+	    case miWARNING:
+                if (TRACE)
+                    fprintf(stderr,
+                            "HTMIME: PICKED UP Warning: '%s'\n",
+			    me->value);
+                break;
+	    case miWWW_AUTHENTICATE:
+                if (TRACE)
+                    fprintf(stderr,
+                            "HTMIME: PICKED UP WWW-Authenticate: '%s'\n",
+			    me->value);
+                break;
+	    default:		/* Should never get here */
+	    	break;
+	    }
+	} else {
+	    if (me->value_pointer < me->value + VALUE_SIZE - 1) {
+	        *me->value_pointer++ = c;
+		break;
+	    } else {
+	        goto value_too_long;
+	    }
+	}
+	/* Fall through */
+	
+    case miJUNK_LINE:
+        if (c == '\n') {
+	    me->state = miNEWLINE;
+	    me->fold_state = me->state;
+	}
+	break;
+	
+	
+    } /* switch on state*/
+    
+    return;
+    
+value_too_long:
+    if (TRACE)
+        fprintf(stderr, "HTMIME: *** Syntax error. (string too long)\n");
+    
+bad_field_name:				/* Ignore it */
+    me->state = miJUNK_LINE;
+    return;
+    
+}
+
+
+
+/*	String handling
+**	---------------
+**
+**	Strings must be smaller than this buffer size.
+*/
+PRIVATE void HTMIME_put_string ARGS2(HTStream *, me, CONST char*, s)
+{
+    CONST char * p;
+
+    if (me->state == MIME_TRANSPARENT) {	/* Optimisation */
+        (*me->targetClass.put_string)(me->target,s);
+
+    } else if (me->state != MIME_IGNORE) {
+        if (TRACE)
+	    fprintf(stderr, "HTMIME:  %s\n", s);
+
+        for (p=s; *p; p++)
+	    HTMIME_put_character(me, *p);
+    }
+}
+
+
+/*	Buffer write.  Buffers can (and should!) be big.
+**	------------
+*/
+PRIVATE void HTMIME_write ARGS3(HTStream *, me, CONST char*, s, int, l)
+{
+    CONST char * p;
+
+    if (me->state == MIME_TRANSPARENT) {	/* Optimisation */
+        (*me->targetClass.put_block)(me->target, s, l);
+
+    } else {
+        if (TRACE)
+	    fprintf(stderr, "HTMIME:  %s\n", s);
+
+        for (p = s; p < s+l; p++)
+	    HTMIME_put_character(me, *p);
+    }
+}
+
+
+/*	Free an HTML object
+**	-------------------
+**
+*/
+PRIVATE void HTMIME_free ARGS1(HTStream *, me)
+{
+    if (me->target)
+        (*me->targetClass._free)(me->target);
+    FREE(me);
+}
+
+/*	End writing
+*/
+
+PRIVATE void HTMIME_abort ARGS2(HTStream *, me, HTError, e)
+{
+    if (me->target)
+        (*me->targetClass._abort)(me->target, e);
+    FREE(me);
+}
+
+
+/*	Structured Object Class
+**	-----------------------
+*/
+PRIVATE CONST HTStreamClass HTMIME =
+{		
+	"MIMEParser",
+	HTMIME_free,
+	HTMIME_abort,
+	HTMIME_put_character,
+	HTMIME_put_string,
+	HTMIME_write
+}; 
+
+
+/*	Subclass-specific Methods
+**	-------------------------
+*/
+
+PUBLIC HTStream* HTMIMEConvert ARGS3(
+	HTPresentation *,	pres,
+	HTParentAnchor *,	anchor,
+	HTStream *,		sink)
+{
+    HTStream* me;
+    
+    me = (HTStream *)calloc(1, sizeof(*me));
+    if (me == NULL)
+        outofmem(__FILE__, "HTML_new");
+    me->isa	=	&HTMIME;       
+    me->sink	=	sink;
+    me->anchor	=	anchor;
+    me->anchor->no_cache = FALSE;
+    FREE(me->anchor->cache_control);
+    FREE(me->anchor->charset);
+    FREE(me->anchor->content_language);
+    FREE(me->anchor->content_encoding);
+    me->target	= 	NULL;
+    me->state	=	miBEGINNING_OF_LINE;
+    /*
+     *	Sadly enough, change this to always default to WWW_HTML
+     *	to parse all text as HTML for the users.
+     *  GAB 06-30-94
+     *	Thanks to Robert Rowland robert@cyclops.pei.edu
+     *
+     *	After discussion of the correct handline, should be application/octet-
+     *		stream or unknown; causing servers to send a correct content
+     *		type.
+     *
+     *  The consequence of using WWW_UNKNOWN is that you end up downloading
+     *  as a binary file what 99.9% of the time is an HTML file, which should
+     *  have been rendered or displayed.  So sadly enough, I'm changing it
+     *  back to WWW_HTML, and it will handle the situation like Mosaic does,
+     *  and as Robert Rowland suggested, because being functionally correct
+     *  99.9% of the time is better than being technically correct but
+     *  functionally nonsensical. - FM
+     *//***
+    me->format	  =	WWW_UNKNOWN;
+        ***/
+    me->format	  =	WWW_HTML;
+    me->targetRep =	pres->rep_out;
+    me->boundary  =	NULL;		/* Not set yet */
+    me->encoding  =	0;		/* Not set yet */
+    me->compression_encoding = NULL;	/* Not set yet */
+    me->net_ascii =	NO;		/* Local character set */
+    return me;
+}
+
+PUBLIC HTStream* HTNetMIME ARGS3(
+	HTPresentation *,	pres,
+	HTParentAnchor *,	anchor,
+	HTStream *,		sink)
+{
+    HTStream* me = HTMIMEConvert(pres,anchor, sink);
+    if (!me)
+        return NULL;
+    
+    me->net_ascii = YES;
+    return me;
+}
+
+/*		Japanese header handling functions
+**		==================================
+**
+**	K&Rized and added 07-Jun-96 by FM, based on:
+**
+////////////////////////////////////////////////////////////////////////
+**
+**	ISO-2022-JP handling routines
+**			&
+**	MIME decode routines (quick hack just for ISO-2022-JP)
+**
+**		Thu Jan 25 10:11:42 JST 1996
+**
+**  Copyright (C) 1994, 1995, 1996
+**  Shuichi Ichikawa (ichikawa@nuee.nagoya-u.ac.jp)
+**
+**  This program is free software; you can redistribute it and/or modify
+**  it under the terms of the GNU General Public License as published by
+**  the Free Software Foundation; either versions 2, or (at your option)
+**  any later version.
+**
+**  This program is distributed in the hope that it will be useful
+**  but WITHOUT ANY WARRANTY; without even the implied warranty of
+**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+**  GNU General Public License for more details.
+**
+**  You should have received a copy of the GNU General Public License
+**  along with SKK, see the file COPYING.  If not, write to the Free
+**  Software Foundation Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* #include <stdio.h> */	/* Included via previous headers. - FM */
+/* #include <string.h> */	/* Included via previous headers. - FM */
+
+/*
+ *  MIME decoding routines
+ *
+ *	Written by S. Ichikawa,
+ *	partially inspired by encdec.c of <jh@efd.lth.se>.
+ */
+#define	BUFLEN	1024
+#ifdef ESC
+#undef ESC
+#endif /* ESC */
+#define ESC	'\033'
+
+PRIVATE char HTmm64[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" ;
+PRIVATE char HTmmquote[] = "0123456789ABCDEF";
+PRIVATE int HTmmcont = 0;
+
+PUBLIC void HTmmdec_base64 ARGS2(
+	char *,		t,
+	char *,		s)
+{
+    int   d, count, j, val;
+    char  buf[BUFLEN], *bp, nw[4], *p;
+
+    for (bp = buf; *s; s += 4) {
+	val = 0;
+	if (s[2] == '=')
+	    count = 1;
+	else if (s[3] == '=')
+	    count = 2;
+	else
+	    count = 3;
+
+	for (j = 0; j <= count; j++) {
+		if (!(p = strchr(HTmm64, s[j]))) {
+			return;
+		}
+		d = p - HTmm64;
+		d <<= (3-j)*6;
+		val += d;
+	}
+	for (j = 2; j >= 0; j--) {
+		nw[j] = val & 255;
+		val >>= 8;
+	}
+	if (count--)
+	    *bp++ = nw[0];
+	if (count--)
+	    *bp++ = nw[1];
+	if (count)
+	    *bp++ = nw[2];
+    }
+    *bp = '\0';
+    strcpy(t, buf);
+}
+
+PUBLIC void HTmmdec_quote ARGS2(
+	char *,		t,
+	char *,		s)
+{
+    char  buf[BUFLEN], cval, *bp, *p;
+
+    for (bp = buf; *s; ) {
+	if (*s == '=') {
+	    cval = 0;
+	    if (s[1] && (p = strchr(HTmmquote, s[1]))) {
+		cval += (p - HTmmquote);
+	    } else {
+		*bp++ = *s++;
+		continue;
+	    }
+	    if (s[2] && (p = strchr(HTmmquote, s[2]))) {
+		cval <<= 4;
+		cval += (p - HTmmquote);
+		*bp++ = cval;
+		s += 3;
+	    } else {
+		*bp++ = *s++;
+	    }
+	} else if (*s == '_') {
+	    *bp++ = 0x20;
+	    s++;
+	} else {
+	    *bp++ = *s++;
+	}
+    }
+    *bp = '\0';
+    strcpy(t, buf);
+}
+
+PUBLIC void HTmmdecode ARGS2(
+	char *,		trg,
+	char *,		str)
+{
+    char buf[BUFLEN], mmbuf[BUFLEN];
+    char *s, *t, *u;
+    int  base64, quote;
+
+    buf[0] = '\0';
+
+    for (s = str, u = buf; *s; ) {
+	if (!strncasecomp(s, "=?ISO-2022-JP?B?", 16)) {
+	    base64 = 1;
+	} else {
+	    base64 = 0;
+	}
+	if (!strncasecomp(s, "=?ISO-2022-JP?Q?", 16)) {
+	    quote = 1;
+	} else {
+	    quote = 0;
+	}
+	if (base64 || quote) {
+	    if (HTmmcont) {
+		for (t = s - 1;
+		    t >= str && (*t == ' ' || *t == '\t'); t--) {
+			u--;
+		}
+	    }
+	    for (s += 16, t = mmbuf; *s; ) {
+		if (s[0] == '?' && s[1] == '=') { 
+		    break;
+		} else {
+		    *t++ = *s++;
+		}
+	    }
+	    if (s[0] != '?' || s[1] != '=') {
+		goto end;
+	    } else {
+		s += 2;
+		*t = '\0';
+	    }
+	    if (base64)
+	        HTmmdec_base64(mmbuf, mmbuf);
+	    if (quote)
+	        HTmmdec_quote(mmbuf, mmbuf);
+	    for (t = mmbuf; *t; )
+	        *u++ = *t++;
+	    HTmmcont = 1;
+	    /* if (*s == ' ' || *s == '\t') *u++ = *s; */
+	    /* for ( ; *s == ' ' || *s == '\t'; s++) ; */
+	} else {
+	    if (*s != ' ' && *s != '\t')
+	        HTmmcont = 0;
+	    *u++ = *s++;
+	}
+    }
+    *u = '\0';
+end:
+    strcpy(trg, buf);
+}
+
+/* 
+**  Modified for Lynx-jp by Takuya ASADA (and K&Rized by FM).
+*/
+#if NOTDEFINED
+PUBLIC int main ARGS2(
+	int, 		ac,
+	char **,	av)
+{
+    FILE *fp;
+    char buf[BUFLEN];
+    char header = 1, body = 0, r_jis = 0;
+    int  i, c;
+
+    for (i = 1; i < ac; i++) {
+	if (strcmp(av[i], "-B") == NULL)
+	    body = 1;
+	else if (strcmp(av[i], "-r") == NULL)
+	    r_jis = 1;
+	else
+	    break;
+    }
+
+    if (i >= ac) {
+	fp = stdin;
+    } else {
+	if ((fp = fopen(av[i], "r")) == NULL) {
+	    fprintf(stderr, "%s: cannot open %s\n", av[0], av[i]);
+	    exit(1);
+	}
+    }
+
+    while (fgets(buf, BUFLEN, fp)) {
+	if (buf[0] == '\n' && buf[1] == '\0')
+	    header = 0;
+	if (header) {
+	    c = fgetc(fp);
+	    if (c == ' ' || c == '\t') {
+		buf[strlen(buf)-1] = '\0';
+		ungetc(c, fp);
+	    } else {
+		ungetc(c, fp);
+	    }
+	}
+	if (header || body)
+	    HTmmdecode(buf, buf);
+	if (r_jis)
+	    HTrjis(buf, buf);
+	fprintf(stdout, "%s", buf);
+    }
+
+    close(fp);
+    exit(0);
+}
+#endif /* NOTDEFINED */
+
+/*
+**  Insert ESC where it seems lost.
+**  (The author of this function "rjis" is S. Ichikawa.)
+*/
+PUBLIC int HTrjis ARGS2(
+	char *,		t,
+	char *,		s)
+{
+    char *p, buf[BUFLEN];
+    int kanji = 0;
+
+    if (strchr(s, ESC) || !strchr(s, '$')) {
+	if (s != t)
+	    strcpy(t, s);
+	return 1;
+    }
+    for (p = buf; *s; ) {
+	if (!kanji && s[0] == '$' && (s[1] == '@' || s[1] == 'B')) {
+	    if (HTmaybekanji((int)s[2], (int)s[3])) {
+		kanji = 1;
+		*p++ = ESC;
+		*p++ = *s++;
+		*p++ = *s++;
+		*p++ = *s++;
+		*p++ = *s++;
+		continue;
+	    }
+	    *p++ = *s++;
+	    continue;
+	}
+	if (kanji && s[0] == '(' && (s[1] == 'J' || s[1] == 'B')) {
+	    kanji = 0;
+	    *p++ = ESC;
+	    *p++ = *s++;
+	    *p++ = *s++;
+	    continue;
+	}
+	*p++ = *s++;
+    }
+    *p = *s;    /* terminate string */
+
+    strcpy(t, buf);
+    return 0;
+}
+
+/*
+**  The following function "maybekanji" is derived from
+**  RJIS-1.0 by Mr. Hironobu Takahashi.
+**  Maybekanji() is included here under the courtesy of the author.
+**  The original comment of rjis.c is also included here.
+*/
+/*
+ * RJIS ( Recover JIS code from broken file )
+ * $Header: /usr/build/VCS/lynx/WWW/Library/Implementation/RCS/HTMIME.c,v 1.1 1996/09/02 00:21:10 tom Exp $
+ * Copyright (C) 1992 1994
+ * Hironobu Takahashi (takahasi@tiny.or.jp)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either versions 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with SKK, see the file COPYING.  If not, write to the Free
+ * Software Foundation Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+PUBLIC int HTmaybekanji ARGS2(
+	int,		c1,
+	int,		c2)
+{
+
+    if ((c2 < 33) || (c2 > 126))
+        return 0;
+    if ((c1 < 33) || ((40 < c1) && (c1 < 48)) || (116 < c1))
+        return 0;
+    c2 -= 32;
+    switch(c1-32) {
+      case 2:
+	if ((14 < c2) && ( c2 < 26))
+	    return 0;
+	if ((33 < c2) && ( c2 < 42))
+	    return 0;
+	if ((48 < c2) && ( c2 < 60))
+	    return 0;
+	if ((74 < c2) && ( c2 < 82))
+	    return 0;
+	if ((89 < c2) && ( c2 < 94))
+	    return 0;
+	break;
+      case 3:
+	if (c2 < 16)
+	    return 0;
+	if ((25 < c2) && ( c2 < 33))
+	    return 0;
+	if ((58 < c2) && ( c2 < 65))
+	    return 0;
+	if (90 < c2)
+	    return 0;
+	break;
+      case 4:
+	if (83 < c2)
+	    return 0;
+	break;
+      case 5:
+	if (86 < c2)
+	    return 0;
+	break;
+      case 6:
+	if ((24 < c2) && ( c2 < 33))
+	    return 0;
+	if (56 < c2)
+	    return 0;
+	break;
+      case 7:
+	if ((33 < c2) && ( c2 < 49))
+	    return 0;
+	if (81 < c2)
+	    return 0;
+	break;
+      case 8:
+	if (32 < c2)
+	    return 0;
+	break;
+      case 47:
+	if (51 < c2)
+	    return 0;
+	break;
+      case 84:
+	if (6 < c2)
+	    return 0;
+	break;
+    }
+    return 1;
+}
+
diff --git a/WWW/Library/Implementation/HTMIME.h b/WWW/Library/Implementation/HTMIME.h
new file mode 100644
index 00000000..94b9665c
--- /dev/null
+++ b/WWW/Library/Implementation/HTMIME.h
@@ -0,0 +1,77 @@
+/*                   /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTMIME.html
+                                       MIME PARSER
+                                             
+   The MIME parser stream presents a MIME document. It recursively invokes the format
+   manager to handle embedded formats.
+   
+   As well as stripping off and parsing the headers, the MIME parser has to parse any
+   weirld MIME encodings it may meet within the body parts of messages, and must deal with
+   multipart messages.
+   
+   This module is implemented to the level necessary for operation with WWW, but is not
+   currently complete for any arbitrary MIME message.
+   
+   Check the source for latest additions to functionality.
+   
+   The MIME parser is complicated by the fact that WWW allows real binary to be sent, not
+   ASCII encoded.  Therefore the netascii decoding is included in this module. One cannot
+   layer it by converting first from Net to local text, then decoding it. Of course, for
+   local files, the net ascii decoding is not needed.  There are therefore two creation
+   routines.
+   
+ */
+#ifndef HTMIME_H
+#define HTMIME_H
+
+#include "HTStream.h"
+#include "HTAnchor.h"
+
+/*
+
+  INPUT: LOCAL TEXT
+  
+ */
+extern HTStream * HTMIMEConvert PARAMS((HTPresentation * pres,
+                                        HTParentAnchor * anchor,
+                                        HTStream * sink));
+/*
+
+  INPUT: NET ASCII
+  
+ */
+extern HTStream * HTNetMIME PARAMS((HTPresentation * pres,
+                                        HTParentAnchor * anchor,
+                                        HTStream * sink));
+
+
+/*
+
+  For handling Japanese headers.
+  
+*/
+extern void HTmmdec_base64 PARAMS((
+	char *	t,
+	char *	s));
+
+extern void HTmmdec_quote PARAMS((
+	char *	t,
+	char *	s));
+
+extern void HTmmdecode PARAMS((
+	char *	trg,
+	char *	str));
+
+extern int HTrjis PARAMS((
+	char *	t,
+	char *	s));
+
+PUBLIC int HTmaybekanji PARAMS((
+	int	c1,
+	int	c2));
+
+
+#endif /* !HTMIME_H */
+
+/*
+
+   end of HTMIME */
diff --git a/WWW/Library/Implementation/HTML.h b/WWW/Library/Implementation/HTML.h
new file mode 100644
index 00000000..aada006f
--- /dev/null
+++ b/WWW/Library/Implementation/HTML.h
@@ -0,0 +1,79 @@
+/*                                                     HTML to rich text converter for libwww
+                             THE HTML TO RTF OBJECT CONVERTER
+                                             
+   This interprets the HTML semantics.
+   
+ */
+#ifndef HTML_H
+#define HTML_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+#include "HTAnchor.h"
+#include "HTMLDTD.h"
+
+#ifdef SHORT_NAMES
+#define HTMLPresentation        HTMLPren
+#define HTMLPresent             HTMLPres
+#endif /* SHORT_NAMES */
+
+extern CONST HTStructuredClass HTMLPresentation;
+
+/*
+
+HTConverter to present HTML
+
+ */
+PUBLIC HTStream* HTMLToPlain PARAMS((
+        HTPresentation *        pres,
+        HTParentAnchor *        anchor,
+        HTStream *              sink));
+
+PUBLIC HTStream* HTMLToC PARAMS((
+        HTPresentation *        pres,
+        HTParentAnchor *        anchor,
+        HTStream *              sink));
+
+PUBLIC HTStream* HTMLPresent PARAMS((
+        HTPresentation *        pres,
+        HTParentAnchor *        anchor,
+        HTStream *              sink));
+
+extern HTStructured* HTML_new PARAMS((
+        HTParentAnchor * anchor,
+        HTFormat        format_out,
+        HTStream *      target));
+
+/*      Names for selected internal representations:
+*/
+typedef enum _HTMLCharacterSet {
+        HTML_ISO_LATIN1,
+        HTML_NEXT_CHARS,
+        HTML_PC_CP950
+} HTMLCharacterSet;
+
+
+/*
+
+Record error message as a hypertext object
+
+   The error message should be marked as an error so that it can be reloaded later. This
+   implementation just throws up an error message and leaves the document unloaded.
+   
+ */
+/* On entry,
+**      sink    is a stream to the output device if any
+**      number  is the HTTP error number
+**      message is the human readable message.
+** On exit,
+**      a retrun code like HT_LOADED if object exists else 60; 0
+*/
+
+PUBLIC int HTLoadError PARAMS((
+        HTStream *      sink,
+        int             number,
+        CONST char *    message));
+
+#endif /* HTML_H */
+
diff --git a/WWW/Library/Implementation/HTMLDTD.c b/WWW/Library/Implementation/HTMLDTD.c
new file mode 100644
index 00000000..3a982b2f
--- /dev/null
+++ b/WWW/Library/Implementation/HTMLDTD.c
@@ -0,0 +1,1122 @@
+/*		Our Static DTD for HTML
+**		-----------------------
+*/
+
+/* Implements:
+*/
+
+#include "HTUtils.h"
+#include "HTMLDTD.h"
+#include "LYLeaks.h"
+
+/* 	Entity Names
+**	------------
+**
+**	This table must be matched exactly with ALL the translation tables
+*/
+static CONST char* entities[] = {
+  "AElig",	/* capital AE diphthong (ligature) */ 
+  "Aacute",	/* capital A, acute accent */ 
+  "Acirc",	/* capital A, circumflex accent */ 
+  "Agrave",	/* capital A, grave accent */ 
+  "Aring",	/* capital A, ring */ 
+  "Atilde",	/* capital A, tilde */ 
+  "Auml",	/* capital A, dieresis or umlaut mark */ 
+  "Ccedil",	/* capital C, cedilla */ 
+  "Dstrok",	/* capital Eth, Icelandic */ 
+  "ETH",	/* capital Eth, Icelandic */ 
+  "Eacute",	/* capital E, acute accent */ 
+  "Ecirc",	/* capital E, circumflex accent */ 
+  "Egrave",	/* capital E, grave accent */ 
+  "Euml",	/* capital E, dieresis or umlaut mark */ 
+  "Iacute",	/* capital I, acute accent */ 
+  "Icirc",	/* capital I, circumflex accent */ 
+  "Igrave",	/* capital I, grave accent */ 
+  "Iuml",	/* capital I, dieresis or umlaut mark */ 
+  "Ntilde",	/* capital N, tilde */ 
+  "Oacute",	/* capital O, acute accent */ 
+  "Ocirc",	/* capital O, circumflex accent */ 
+  "Ograve",	/* capital O, grave accent */ 
+  "Oslash",	/* capital O, slash */ 
+  "Otilde",	/* capital O, tilde */ 
+  "Ouml",	/* capital O, dieresis or umlaut mark */ 
+  "THORN",	/* capital THORN, Icelandic */ 
+  "Uacute",	/* capital U, acute accent */ 
+  "Ucirc",	/* capital U, circumflex accent */ 
+  "Ugrave",	/* capital U, grave accent */ 
+  "Uuml",	/* capital U, dieresis or umlaut mark */ 
+  "Yacute",	/* capital Y, acute accent */ 
+  "aacute",	/* small a, acute accent */ 
+  "acirc",	/* small a, circumflex accent */ 
+  "acute",	/* spacing acute */
+  "aelig",	/* small ae diphthong (ligature) */ 
+  "agrave",	/* small a, grave accent */ 
+  "amp",	/* ampersand */ 
+  "aring",	/* small a, ring */ 
+  "atilde",	/* small a, tilde */ 
+  "auml",	/* small a, dieresis or umlaut mark */ 
+  "brkbar",	/* broken vertical bar */
+  "brvbar",	/* broken vertical bar */
+  "ccedil",	/* small c, cedilla */ 
+  "cedil",	/* spacing cedilla */
+  "cent",	/* cent sign */
+  "copy",	/* copyright sign */
+  "curren",	/* currency sign */
+  "deg",	/* degree sign */
+  "die",	/* spacing diaresis */
+  "divide",	/* division sign */
+  "eacute",	/* small e, acute accent */ 
+  "ecirc",	/* small e, circumflex accent */ 
+  "egrave",	/* small e, grave accent */ 
+  "emdash",	/* dash the width of emsp */
+  "emsp",	/* em space - not collapsed */
+  "endash",	/* dash the width of ensp */
+  "ensp",	/* en space - not collapsed */
+  "eth",	/* small eth, Icelandic */ 
+  "euml",	/* small e, dieresis or umlaut mark */ 
+  "frac12",	/* fraction 1/2 */
+  "frac14",	/* fraction 1/4 */
+  "frac34",	/* fraction 3/4 */
+  "gt",		/* greater than */ 
+  "hibar",	/* spacing macron */
+  "iacute",	/* small i, acute accent */ 
+  "icirc",	/* small i, circumflex accent */ 
+  "iexcl",	/* inverted exclamation mark */
+  "igrave",	/* small i, grave accent */ 
+  "iquest",	/* inverted question mark */
+  "iuml",	/* small i, dieresis or umlaut mark */ 
+  "laquo",	/* angle quotation mark, left */
+  "lt",		/* less than */ 
+  "macr",	/* spacing macron */
+  "mdash",	/* dash the width of emsp */
+  "micro",	/* micro sign */
+  "middot",	/* middle dot */
+  "nbsp",       /* non breaking space */
+  "ndash",	/* dash the width of ensp */
+  "not",	/* negation sign */
+  "ntilde",	/* small n, tilde */ 
+  "oacute",	/* small o, acute accent */ 
+  "ocirc",	/* small o, circumflex accent */ 
+  "ograve",	/* small o, grave accent */ 
+  "ordf",	/* feminine ordinal indicator */
+  "ordm",	/* masculine ordinal indicator */
+  "oslash",	/* small o, slash */ 
+  "otilde",	/* small o, tilde */ 
+  "ouml",	/* small o, dieresis or umlaut mark */ 
+  "para",	/* paragraph sign */
+  "plusmn",	/* plus-or-minus sign */
+  "pound",	/* pound sign */
+  "quot",	/* quot '"' */
+  "raquo",	/* angle quotation mark, right */
+  "reg",	/* circled R registered sign */
+  "sect",	/* section sign */
+  "shy",	/* soft hyphen */
+  "sup1",	/* superscript 1 */
+  "sup2",	/* superscript 2 */
+  "sup3",	/* superscript 3 */
+  "szlig",	/* small sharp s, German (sz ligature) */ 
+  "thinsp",	/* thin space (not collapsed) */ 
+  "thorn",	/* small thorn, Icelandic */ 
+  "times",	/* multiplication sign */ 
+  "trade",	/* registerd trademark */ 
+  "uacute",	/* small u, acute accent */ 
+  "ucirc",	/* small u, circumflex accent */ 
+  "ugrave",	/* small u, grave accent */ 
+  "uml",	/* spacing diaresis */
+  "uuml",	/* small u, dieresis or umlaut mark */ 
+  "yacute",	/* small y, acute accent */ 
+  "yen",	/* yen sign */
+  "yuml",	/* small y, dieresis or umlaut mark */ 
+};
+
+#define HTML_ENTITIES 112
+
+
+/*		Attribute Lists
+**		---------------
+**
+**	Lists must be in alphatbetical order by attribute name
+**	The tag elements contain the number of attributes
+*/
+
+static attr a_attr[] = {			/* Anchor attributes */
+	{ "ACCESSKEY" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "COORDS" },
+	{ "DIR" },
+	{ "HREF" },
+	{ "ID" },
+	{ "ISMAP" },
+	{ "LANG" },
+	{ "MD" },
+	{ "NAME" },
+	{ "NOTAB" },
+	{ "ONCLICK" },
+	{ "ONMOUSEOUT" },
+	{ "ONMOUSEOVER" },
+	{ "REL" },
+	{ "REV" },
+	{ "SHAPE" },
+	{ "STYLE" },
+	{ "TABINDEX" },
+	{ "TARGET" },
+	{ "TITLE" },
+	{ "TYPE" },
+	{ "URN" },
+	{ 0 }	/* Terminate list */
+};	
+
+static attr address_attr[] = {			/* ADDRESS attributes */
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "NOWRAP" },
+	{ "STYLE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr applet_attr[] = {			/* APPLET attributes */
+	{ "ALIGN" },
+	{ "ALT" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "CODE" },
+	{ "CODEBASE" },
+	{ "DIR" },
+	{ "DOWNLOAD" },
+	{ "HEIGHT" },
+	{ "HSPACE" },
+	{ "ID" },
+	{ "LANG" },
+	{ "NAME" },
+	{ "STYLE" },
+	{ "TITLE" },
+	{ "VSPACE" },
+	{ "WIDTH" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr area_attr[] = {			/* AREA attributes */
+	{ "ALT" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "COORDS" },
+	{ "DIR" },
+	{ "HREF" },
+	{ "ID" },
+	{ "LANG" },
+	{ "NOHREF" },
+	{ "NOTAB" },
+	{ "ONCLICK" },
+	{ "ONMOUSEOUT" },
+	{ "ONMOUSEOVER" },
+	{ "SHAPE" },
+	{ "STYLE" },
+	{ "TABINDEX" },
+	{ "TARGET" },
+	{ "TITLE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr base_attr[] = {			/* BASE attributes */
+	{ "HREF" },
+	{ "TARGET" },
+	{ 0 }	/* Terminate list */
+};	
+
+static attr bgsound_attr[] = {			/* BGSOUND attributes */
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "LOOP" },
+	{ "SRC" },
+	{ "STYLE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr body_attr[] = {			/* BODY attributes */
+	{ "ALINK" },
+	{ "BACKGROUND" },
+	{ "BGCOLOR" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "LINK" },
+	{ "STYLE" },
+	{ "ONLOAD" },
+	{ "ONUNLOAD" },
+	{ "TEXT" },
+	{ "VLINK" },
+	{ 0 } /* Terminate list */
+};
+
+static attr bodytext_attr[] = {			/* BODYTEXT attributes */
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DATA" },
+	{ "DIR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "NAME" },
+	{ "OBJECT" },
+	{ "REF" },
+	{ "STYLE" },
+	{ "TYPE" },
+	{ "VALUE" },
+	{ "VALUETYPE" },
+	{ 0 } /* Terminate list */
+};
+
+static attr bq_attr[] = {			/* BQ (BLOCKQUOTE) attributes */
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "NOWRAP" },
+	{ "STYLE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr caption_attr[] = {			/* CAPTION attributes */
+	{ "ACCESSKEY" },
+	{ "ALIGN" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "STYLE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr col_attr[] = {		/* COL and COLGROUP attributes */
+	{ "ALIGN" },
+	{ "CHAR" },
+	{ "CHAROFF" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "SPAN" },
+	{ "STYLE" },
+	{ "VALIGN" },
+	{ "WIDTH" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr credit_attr[] = {			/* CREDIT attributes */
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "STYLE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr div_attr[] = {			/* DIV attribures */
+	{ "ALIGN" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "STYLE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr embed_attr[] = {			/* EMBED attributes */
+	{ "ALIGN" },	/* (including, for now, those from FIG and IMG) */
+	{ "ALT" },
+	{ "BORDER" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "HEIGHT" },
+	{ "ID" },
+	{ "IMAGEMAP" },
+	{ "ISMAP" },
+	{ "LANG" },
+	{ "MD" },
+	{ "NAME" },
+	{ "NOFLOW" },
+	{ "PARAMS" },
+	{ "SRC" },
+	{ "STYLE" },
+	{ "UNITS" },
+	{ "USEMAP" },
+	{ "WIDTH" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr fig_attr[] = {			/* FIG attributes */
+	{ "ALIGN" },
+	{ "BORDER" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "HEIGHT" },
+	{ "ID" },
+	{ "IMAGEMAP" },
+	{ "ISOBJECT" },
+	{ "LANG" },
+	{ "MD" },
+	{ "NOFLOW" },
+	{ "SRC" },
+	{ "STYLE" },
+	{ "UNITS" },
+	{ "WIDTH" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr fieldset_attr[] = {			/* FIELDSET attributes */
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "STYLE" },
+	{ "TITLE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr fn_attr[] = {			/* FN attributes */
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "STYLE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr font_attr[] = {			/* FONT attributes */
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "COLOR" },
+	{ "DIR" },
+	{ "END" },
+	{ "FACE" },
+	{ "ID" },
+	{ "LANG" },
+	{ "SIZE" },
+	{ "STYLE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr form_attr[] = {			/* FORM attributes */
+	{ "ACTION"},
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "ENCTYPE" },
+	{ "ID" },
+	{ "LANG" },
+	{ "METHOD" },
+	{ "ONSUBMIT" },
+	{ "SCRIPT" },
+	{ "STYLE" },
+	{ "SUBJECT" },
+	{ "TARGET" },
+	{ "TITLE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr frame_attr[] = {			/* FRAME attributes */
+	{ "MARGINHEIGHT"},
+	{ "MARGINWIDTH" },
+	{ "NAME" },
+	{ "NORESIZE" },
+	{ "SCROLLING" },
+	{ "SRC" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr frameset_attr[] = {			/* FRAMESET attributes */
+	{ "COLS"},
+	{ "ROWS" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr gen_attr[] = {			/* Minimum HTML 3.0 */
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "STYLE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr glossary_attr[] = {			/* DL (and DLC) attributes */
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "COMPACT" },
+	{ "DIR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "STYLE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr h_attr[] = {			/* H1 - H6 attributes */
+	{ "ALIGN" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DINGBAT" },
+	{ "DIR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "MD" },
+	{ "NOWRAP" },
+	{ "SEQNUM" },
+	{ "SKIP" },
+	{ "SRC" },
+	{ "STYLE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr hr_attr[] = {			/* HR attributes */
+	{ "ALIGN" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "ID" },
+	{ "MD" },
+	{ "NOSHADE" },
+	{ "SIZE" },
+	{ "SRC" },
+	{ "STYLE" },
+	{ "WIDTH" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr img_attr[] = {			/* IMG attributes */
+	{ "ALIGN" },
+	{ "ALT" },
+	{ "BORDER" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "HEIGHT" },
+	{ "ID" },
+	{ "ISMAP" },
+	{ "ISOBJECT" },
+	{ "LANG" },
+	{ "MD" },
+	{ "SRC" },
+	{ "STYLE" },
+	{ "TITLE" },
+	{ "UNITS" },
+	{ "USEMAP" },
+	{ "WIDTH" },
+	{ 0 }	/* Terminate list */
+};	
+
+static attr input_attr[] = {			/* INPUT attributes */
+	{ "ACCEPT" },
+	{ "ALIGN" },
+	{ "CHECKED" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "DISABLED" },
+	{ "ERROR" },
+	{ "HEIGHT" },
+	{ "ID" },
+	{ "LANG" },
+	{ "MAX" },
+	{ "MAXLENGTH" },
+	{ "MD" },
+	{ "MIN" },
+	{ "NAME" },
+	{ "NOTAB" },
+	{ "ONBLUR" },
+	{ "ONCHANGE" },
+	{ "ONCLICK" },
+	{ "ONFOCUS" },
+	{ "ONSELECT" },
+	{ "SIZE" },
+	{ "SRC" },
+	{ "STYLE" },
+	{ "TABINDEX" },
+	{ "TITLE" },
+	{ "TYPE" },
+	{ "VALUE" },
+	{ "WIDTH" },
+	{ 0 } /* Terminate list */
+};
+
+static attr isindex_attr[] = {			/* ISINDEX attributes */
+	{ "ACTION" },	/* Not in spec.  Lynx treats it as HREF. - FM */
+	{ "DIR" },
+	{ "HREF" },	/* HTML 3.0 attritute for search action. - FM */ 
+	{ "LANG" },
+	{ "PROMPT" },	/* HTML 3.0 attribute for prompt string. - FM */
+	{ 0 }	/* Terminate list */
+};	
+
+static attr label_attr[] = {			/* LABEL attributes */
+	{ "ACCESSKEY" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "FOR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "ONCLICK" },
+	{ "STYLE" },
+	{ "TITLE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr link_attr[] = {			/* LINK attributes */
+	{ "CLASS" },
+	{ "HREF" },
+	{ "ID" },
+	{ "REL" },
+	{ "REV" },
+	{ "STYLE" },
+	{ "TARGET" },
+	{ "TITLE" },
+	{ "TYPE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr list_attr[] = {			/* LI attributes */
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DINGBAT" },
+	{ "DIR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "MD" },
+	{ "SRC" },
+	{ "SKIP" },
+	{ "STYLE" },
+	{ "TYPE" },
+	{ "VALUE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr map_attr[] = {			/* MAP attributes */
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "NAME" },
+	{ "STYLE" },
+	{ "TITLE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr math_attr[] = {			/* MATH attributes */
+	{ "BOX" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "STYLE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr meta_attr[] = {			/* META attributes */
+	{ "CONTENT" },
+	{ "HTTP-EQUIV" },
+	{ "NAME" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr nextid_attr[] = {			/* NEXTID attribures */
+	{ "N" }
+};
+
+static attr note_attr[]	= {			/* NOTE attributes */
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "MD" },
+	{ "ROLE" },
+	{ "SRC" },
+	{ "STYLE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr object_attr[] = {			/* OBJECT attributes */
+	{ "ALIGN" },
+	{ "BORDER" },
+	{ "CLASS" },
+	{ "CLASSID" },
+	{ "CODEBASE" },
+	{ "CODETYPE" },
+	{ "DATA" },
+	{ "DECLARE" },
+	{ "DIR" },
+	{ "HEIGHT" },
+	{ "HSPACE" },
+	{ "ID" },
+	{ "ISMAP" },
+	{ "LANG" },
+	{ "NAME" },
+	{ "NOTAB" },
+	{ "SHAPES" },
+	{ "STANDBY" },
+	{ "STYLE" },
+	{ "TABINDEX" },
+	{ "TITLE" },
+	{ "TYPE" },
+	{ "USEMAP" },
+	{ "VSPACE" },
+	{ "WIDTH" },
+	{ 0 } /* Terminate list */
+};
+
+static attr olist_attr[] = {			/* OL attributes */
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "COMPACT" },
+	{ "CONTINUE" },
+	{ "DIR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "SEQNUM" },
+	{ "START" },
+	{ "STYLE" },
+	{ "TYPE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr option_attr[] = {			/* OPTION attributes */
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "DISABLED" },
+	{ "ERROR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "SELECTED" },
+	{ "SHAPE" },
+	{ "STYLE" },
+	{ "VALUE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr overlay_attr[] = {			/* OVERLAY attributes */
+	{ "CLASS" },
+	{ "HEIGHT" },
+	{ "ID" },
+	{ "IMAGEMAP" },
+	{ "MD" },
+	{ "SRC" },
+	{ "STYLE" },
+	{ "UNITS" },
+	{ "WIDTH" },
+	{ "X" },
+	{ "Y" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr p_attr[] = {			/* P attributes */
+	{ "ALIGN" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "NOWRAP" },
+	{ "STYLE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr param_attr[] = {			/* PARAM attribures */
+	{ "ACCEPT" },
+	{ "ACCEPT-CHARSET" },
+	{ "ACCEPT-ENCODING" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "DATA" },
+	{ "NAME" },
+	{ "OBJECT" },
+	{ "REF" },
+	{ "STYLE" },
+	{ "TYPE" },
+	{ "VALUE" },
+	{ "VALUEREF" },
+	{ "VALUETYPE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr script_attr[] = {			/* SCRIPT attribures */
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "EVENT" },
+	{ "FOR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "LANGUAGE" },
+	{ "NAME" },
+	{ "SCRIPTENGINE" },
+	{ "SRC" },
+	{ "TYPE" },
+	{ "STYLE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr select_attr[] = {			/* SELECT attributes */
+	{ "ALIGN" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "DISABLED" },
+	{ "ERROR" },
+	{ "HEIGHT" },
+	{ "ID" },
+	{ "LANG" },
+	{ "MD" },
+	{ "MULTIPLE" },
+	{ "NAME" },
+	{ "NOTAB" },
+	{ "ONBLUR" },
+	{ "ONCHANGE" },
+	{ "ONFOCUS" },
+	{ "SIZE" },
+	{ "STYLE" },
+	{ "TABINDEX" },
+	{ "TITLE" },
+	{ "UNITS" },
+	{ "WIDTH" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr style_attr[] = {			/* STYLE attributes */
+	{ "DIR" },
+	{ "LANG" },
+	{ "NOTATION" },
+	{ "TITLE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr tab_attr[] = {			/* TAB attributes */
+	{ "ALIGN" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "DP" },
+	{ "ID" },
+	{ "INDENT" },
+	{ "LANG" },
+	{ "STYLE" },
+	{ "TO" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr table_attr[] = {			/* TABLE attributes */
+	{ "ALIGN" },
+	{ "BORDER" },
+	{ "CELLPADDING" },
+	{ "CELLSPACING" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "COLS" },
+	{ "COLSPEC" },
+	{ "DIR" },
+	{ "DP" },
+	{ "FRAME" },
+	{ "ID" },
+	{ "LANG" },
+	{ "NOFLOW" },
+	{ "NOWRAP" },
+	{ "RULES" },
+	{ "STYLE" },
+	{ "UNITS" },
+	{ "WIDTH" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr td_attr[] = {			/* TD and TH attributes */
+	{ "ALIGN" },
+	{ "AXES" },
+	{ "AXIS" },
+	{ "CHAR" },
+	{ "CHAROFF" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "COLSPAN" },
+	{ "DIR" },
+	{ "DP" },
+	{ "ID" },
+	{ "LANG" },
+	{ "NOWRAP" },
+	{ "ROWSPAN" },
+	{ "STYLE" },
+	{ "VALIGN" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr textarea_attr[] = {			/* TEXTAREA attributes */
+	{ "ALIGN" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "COLS" },
+	{ "DIR" },
+	{ "DISABLED" },
+	{ "ERROR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "NAME" },
+	{ "NOTAB" },
+	{ "ONBLUR" },
+	{ "ONCHANGE" },
+	{ "ONFOCUS" },
+	{ "ONSELECT" },
+	{ "ROWS" },
+	{ "STYLE" },
+	{ "TABINDEX" },
+	{ "TITLE" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr tr_attr[] = {	/* TR, THEAD, TFOOT, and TBODY attributes */
+	{ "ALIGN" },
+	{ "CHAR" },
+	{ "CHAROFF" },
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "DIR" },
+	{ "DP" },
+	{ "ID" },
+	{ "LANG" },
+	{ "NOWRAP" },
+	{ "STYLE" },
+	{ "VALIGN" },
+	{ 0 }	/* Terminate list */
+};
+
+static attr ulist_attr[] = {			/* UL attributes */
+	{ "CLASS" },
+	{ "CLEAR" },
+	{ "COMPACT" },
+	{ "DINGBAT" },
+	{ "DIR" },
+	{ "ID" },
+	{ "LANG" },
+	{ "MD" },
+	{ "PLAIN" },
+	{ "SRC" },
+	{ "STYLE" },
+	{ "TYPE" },
+	{ "WRAP" },
+	{ 0 }	/* Terminate list */
+};
+
+/*	Elements
+**	--------
+**
+**	Must match definitions in HTMLDTD.html!
+**	Must be in alphabetical order.
+**
+**    Name, 	Attributes, 		content
+*/
+static HTTag tags[HTML_ELEMENTS] = {
+    { "A"	, a_attr,	HTML_A_ATTRIBUTES,	SGML_MIXED },
+    { "ABBREV"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "ACRONYM"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "ADDRESS"	, address_attr,	HTML_ADDRESS_ATTRIBUTES, SGML_MIXED },
+    { "APPLET"	, applet_attr,	HTML_APPLET_ATTRIBUTES, SGML_MIXED },
+    { "AREA"	, area_attr,	HTML_AREA_ATTRIBUTES,   SGML_EMPTY },
+    { "AU"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "AUTHOR"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "B"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "BANNER"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "BASE"	, base_attr,	HTML_BASE_ATTRIBUTES,	SGML_EMPTY },
+    { "BASEFONT", font_attr,	HTML_FONT_ATTRIBUTES,	SGML_EMPTY },
+    { "BDO"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "BGSOUND"	, bgsound_attr,	HTML_BGSOUND_ATTRIBUTES, SGML_EMPTY },
+    { "BIG"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "BLINK"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "BLOCKQUOTE", bq_attr,	HTML_BQ_ATTRIBUTES,	SGML_MIXED },
+    { "BODY"	, body_attr,	HTML_BODY_ATTRIBUTES,	SGML_MIXED },
+    { "BODYTEXT", bodytext_attr,HTML_BODYTEXT_ATTRIBUTES, SGML_MIXED },
+    { "BQ"	, bq_attr,	HTML_BQ_ATTRIBUTES,	SGML_MIXED },
+    { "BR"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_EMPTY },
+    { "CAPTION"	, caption_attr,	HTML_CAPTION_ATTRIBUTES, SGML_MIXED },
+    { "CENTER"	, div_attr,	HTML_DIV_ATTRIBUTES,	SGML_MIXED },
+    { "CITE"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "CODE"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "COL"	, col_attr,	HTML_COL_ATTRIBUTES,	SGML_EMPTY },
+    { "COLGROUP", col_attr,	HTML_COL_ATTRIBUTES,	SGML_EMPTY },
+    { "COMMENT"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "CREDIT"	, credit_attr,	HTML_CREDIT_ATTRIBUTES,	SGML_MIXED },
+    { "DD"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_EMPTY },
+    { "DEL"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "DFN"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "DIR"	, ulist_attr,	HTML_UL_ATTRIBUTES,	SGML_MIXED },
+    { "DIV"	, div_attr,	HTML_DIV_ATTRIBUTES,	SGML_MIXED },
+    { "DL"	, glossary_attr, HTML_DL_ATTRIBUTES,	SGML_MIXED },
+    { "DLC"	, glossary_attr, HTML_DL_ATTRIBUTES,	SGML_MIXED },
+    { "DT"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_EMPTY },
+    { "EM"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "EMBED"	, embed_attr,	HTML_EMBED_ATTRIBUTES,	SGML_EMPTY },
+    { "FIELDSET", fieldset_attr,HTML_FIELDSET_ATTRIBUTES, SGML_MIXED },
+    { "FIG"	, fig_attr,	HTML_FIG_ATTRIBUTES,	SGML_MIXED },
+    { "FN"	, fn_attr,	HTML_FN_ATTRIBUTES,	SGML_MIXED },
+    { "FONT"	, font_attr,	HTML_FONT_ATTRIBUTES,	SGML_EMPTY },
+    { "FORM"	, form_attr,	HTML_FORM_ATTRIBUTES,	SGML_MIXED },
+    { "FRAME"	, frame_attr,	HTML_FRAME_ATTRIBUTES,	SGML_EMPTY },
+    { "FRAMESET", frameset_attr,HTML_FRAMESET_ATTRIBUTES, SGML_MIXED },
+    { "H1"	, h_attr,	HTML_H_ATTRIBUTES,	SGML_MIXED },
+    { "H2"	, h_attr,	HTML_H_ATTRIBUTES,	SGML_MIXED },
+    { "H3"	, h_attr,	HTML_H_ATTRIBUTES,	SGML_MIXED },
+    { "H4"	, h_attr,	HTML_H_ATTRIBUTES,	SGML_MIXED },
+    { "H5"	, h_attr,	HTML_H_ATTRIBUTES,	SGML_MIXED },
+    { "H6"	, h_attr,	HTML_H_ATTRIBUTES,	SGML_MIXED },
+    { "HEAD"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "HR"	, hr_attr,	HTML_HR_ATTRIBUTES,	SGML_EMPTY },
+    { "HTML"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "I"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "IMG"     , img_attr,	HTML_IMG_ATTRIBUTES,	SGML_EMPTY },
+    { "INPUT"   , input_attr,	HTML_INPUT_ATTRIBUTES,	SGML_EMPTY },
+    { "INS"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "ISINDEX" , isindex_attr,	HTML_ISINDEX_ATTRIBUTES,SGML_EMPTY },
+    { "KBD"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "LABEL"	, label_attr,	HTML_LABEL_ATTRIBUTES,	SGML_MIXED },
+    { "LH"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_EMPTY },
+    { "LI"	, list_attr,	HTML_LI_ATTRIBUTES,	SGML_EMPTY },
+    { "LINK"	, link_attr,	HTML_LINK_ATTRIBUTES,	SGML_EMPTY },
+    { "LISTING"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_LITTERAL },
+    { "MAP"	, map_attr,	HTML_MAP_ATTRIBUTES,	SGML_MIXED },
+    { "MARQUEE"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "MATH"	, math_attr,	HTML_MATH_ATTRIBUTES,	SGML_LITTERAL },
+    { "MENU"	, ulist_attr,	HTML_UL_ATTRIBUTES,	SGML_MIXED },
+    { "META"	, meta_attr,	HTML_META_ATTRIBUTES,	SGML_EMPTY },
+    { "NEXTID"  , nextid_attr,	1,			SGML_EMPTY },
+    { "NOFRAMES", gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "NOTE"	, note_attr,	HTML_NOTE_ATTRIBUTES,	SGML_MIXED },
+    { "OBJECT"	, object_attr,	HTML_OBJECT_ATTRIBUTES,	SGML_LITTERAL },
+    { "OL"	, olist_attr,	HTML_OL_ATTRIBUTES,	SGML_MIXED },
+    { "OPTION"	, option_attr,	HTML_OPTION_ATTRIBUTES,	SGML_EMPTY },
+    { "OVERLAY"	, overlay_attr,	HTML_OVERLAY_ATTRIBUTES, SGML_EMPTY },
+    { "P"	, p_attr,	HTML_P_ATTRIBUTES,	SGML_EMPTY },
+    { "PARAM"	, param_attr,	HTML_PARAM_ATTRIBUTES,	SGML_EMPTY },
+    { "PLAINTEXT", gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_LITTERAL },
+    { "PRE"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "Q"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "S"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "SAMP"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "SCRIPT"	, script_attr,	HTML_SCRIPT_ATTRIBUTES,	SGML_LITTERAL },
+    { "SELECT"	, select_attr,	HTML_SELECT_ATTRIBUTES,	SGML_MIXED },
+    { "SMALL"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "SPAN"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "SPOT"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_EMPTY },
+    { "STRIKE"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "STRONG"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "STYLE"	, style_attr,	HTML_STYLE_ATTRIBUTES,	SGML_LITTERAL },
+    { "SUB"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "SUP"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "TAB"	, tab_attr,	HTML_TAB_ATTRIBUTES,	SGML_EMPTY },
+    { "TABLE"	, table_attr,	HTML_TABLE_ATTRIBUTES,	SGML_MIXED },
+    { "TBODY"	, tr_attr,	HTML_TR_ATTRIBUTES,	SGML_EMPTY },
+    { "TD"	, td_attr,	HTML_TD_ATTRIBUTES,	SGML_EMPTY },
+    { "TEXTAREA", textarea_attr,HTML_TEXTAREA_ATTRIBUTES, SGML_LITTERAL },
+    { "TEXTFLOW", bodytext_attr,HTML_BODYTEXT_ATTRIBUTES, SGML_MIXED },
+    { "TFOOT"	, tr_attr,	HTML_TR_ATTRIBUTES,	SGML_EMPTY },
+    { "TH"	, td_attr,	HTML_TD_ATTRIBUTES,	SGML_EMPTY },
+    { "THEAD"	, tr_attr,	HTML_TR_ATTRIBUTES,	SGML_EMPTY },
+    { "TITLE", 	  gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_RCDATA },
+    { "TR"	, tr_attr,	HTML_TR_ATTRIBUTES,	SGML_EMPTY },
+    { "TT"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "U"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "UL"	, ulist_attr,	HTML_UL_ATTRIBUTES,	SGML_MIXED },
+    { "VAR"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_MIXED },
+    { "XMP"	, gen_attr,	HTML_GEN_ATTRIBUTES,	SGML_LITTERAL },
+};
+
+
+PUBLIC CONST SGML_dtd HTML_dtd = {
+	tags,
+	HTML_ELEMENTS,
+	entities,
+	sizeof(entities)/sizeof(char**)
+};
+
+/*	Utility Routine: useful for people building HTML objects */
+
+/*	Start anchor element
+**	--------------------
+**
+**	It is kinda convenient to have a particulr routine for
+**	starting an anchor element, as everything else for HTML is
+**	simple anyway.
+*/
+struct _HTStructured {
+    HTStructuredClass * isa;
+	/* ... */
+};
+
+PUBLIC void HTStartAnchor ARGS3(HTStructured *, obj,
+		CONST char *,  name,
+		CONST char *,  href)
+{
+    BOOL		present[HTML_A_ATTRIBUTES];
+    CONST char * 	value[HTML_A_ATTRIBUTES];
+    
+    {
+    	int i;
+    	for(i=0; i<HTML_A_ATTRIBUTES; i++)
+	    present[i] = NO;
+    }
+    if (name) {
+    	present[HTML_A_NAME] = YES;
+	value[HTML_A_NAME] = (CONST char *)name;
+    }
+    if (href) {
+        present[HTML_A_HREF] = YES;
+        value[HTML_A_HREF] = (CONST char *)href;
+    }
+    
+    (*obj->isa->start_element)(obj, HTML_A , present, value, 0);
+
+}
+
+PUBLIC void HTStartIsIndex ARGS3(HTStructured *, obj,
+		CONST char *,  prompt,
+		CONST char *,  href)
+{
+    BOOL		present[HTML_ISINDEX_ATTRIBUTES];
+    CONST char * 	value[HTML_ISINDEX_ATTRIBUTES];
+    
+    {
+    	int i;
+    	for(i=0; i<HTML_ISINDEX_ATTRIBUTES; i++)
+	    present[i] = NO;
+    }
+    if (prompt) {
+    	present[HTML_ISINDEX_PROMPT] = YES;
+	value[HTML_ISINDEX_PROMPT] = (CONST char *)prompt;
+    }
+    if (href) {
+        present[HTML_ISINDEX_HREF] = YES;
+        value[HTML_ISINDEX_HREF] = (CONST char *)href;
+    }
+    
+    (*obj->isa->start_element)(obj, HTML_ISINDEX , present, value, 0);
+
+}
+
diff --git a/WWW/Library/Implementation/HTMLDTD.h b/WWW/Library/Implementation/HTMLDTD.h
new file mode 100644
index 00000000..a176e59b
--- /dev/null
+++ b/WWW/Library/Implementation/HTMLDTD.h
@@ -0,0 +1,890 @@
+/*                                               The HTML DTD -- software interface in libwww
+                              HTML DTD - SOFTWARE INTERFACE
+                                             
+   SGML purists should excuse the use of the term "DTD" in this file to represent
+   DTD-related information which is not exactly a DTD itself.
+   
+   The C modular structure doesn't work very well here, as the dtd is partly in the .h and
+   partly in the .c which are not very independent.  Tant pis.
+   
+ */
+#ifndef HTMLDTD_H
+#define HTMLDTD_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+#include "SGML.h"
+
+/*
+
+Element Numbers
+
+ */
+
+/*
+
+   Must Match all tables by element!
+   These include tables in HTMLDTD.c and code in HTML.c.
+   
+ */
+typedef enum _HTMLElement {
+        HTML_A,
+        HTML_ABBREV,
+        HTML_ACRONYM,
+        HTML_ADDRESS,
+	HTML_APPLET,
+	HTML_AREA,
+        HTML_AU,
+        HTML_AUTHOR,
+        HTML_B,
+	HTML_BANNER,
+	HTML_BASE,
+	HTML_BASEFONT,
+        HTML_BDO,
+	HTML_BGSOUND,
+        HTML_BIG,
+	HTML_BLINK,
+	HTML_BLOCKQUOTE,
+	HTML_BODY,
+	HTML_BODYTEXT,
+	HTML_BQ,
+	HTML_BR,
+        HTML_CAPTION,
+	HTML_CENTER,
+	HTML_CITE,
+	HTML_CODE,
+	HTML_COL,
+	HTML_COLGROUP,
+	HTML_COMMENT,
+	HTML_CREDIT,
+        HTML_DD,
+        HTML_DEL,
+	HTML_DFN,
+	HTML_DIR,
+	HTML_DIV,
+        HTML_DL,
+	HTML_DLC,
+	HTML_DT,
+        HTML_EM,
+	HTML_EMBED,
+	HTML_FIELDSET,
+	HTML_FIG,
+	HTML_FN,
+	HTML_FONT,
+	HTML_FORM,
+	HTML_FRAME,
+	HTML_FRAMESET,
+        HTML_H1,
+	HTML_H2,
+	HTML_H3,
+	HTML_H4,
+	HTML_H5,
+	HTML_H6,
+	HTML_HEAD,
+	HTML_HR,
+	HTML_HTML,
+        HTML_I,
+	HTML_IMG,
+	HTML_INPUT,
+        HTML_INS,
+	HTML_ISINDEX,
+        HTML_KBD,
+	HTML_LABEL,
+	HTML_LH,
+	HTML_LI,
+	HTML_LINK,
+	HTML_LISTING,
+        HTML_MAP,
+	HTML_MARQUEE,
+	HTML_MATH,
+	HTML_MENU,
+	HTML_META,
+	HTML_NEXTID,
+	HTML_NOFRAMES,
+	HTML_NOTE,
+        HTML_OBJECT,
+        HTML_OL,
+	HTML_OPTION,
+	HTML_OVERLAY,
+	HTML_P,
+	HTML_PARAM,
+	HTML_PLAINTEXT,
+	HTML_PRE,
+	HTML_Q,
+        HTML_S,
+        HTML_SAMP,
+	HTML_SCRIPT,
+	HTML_SELECT,
+        HTML_SMALL,
+	HTML_SPAN,
+	HTML_SPOT,
+	HTML_STRIKE,
+	HTML_STRONG,
+	HTML_STYLE,
+        HTML_SUB,
+        HTML_SUP,
+	HTML_TAB,
+	HTML_TABLE,
+	HTML_TBODY,
+	HTML_TD,
+	HTML_TEXTAREA,
+	HTML_TEXTFLOW,
+	HTML_TFOOT,
+	HTML_TH,
+	HTML_THEAD,
+	HTML_TITLE,
+	HTML_TR,
+	HTML_TT,
+        HTML_U,
+	HTML_UL,
+        HTML_VAR,
+	HTML_XMP } HTMLElement;
+
+#define HTML_ELEMENTS 111
+
+/*
+
+Attribute numbers
+
+ */
+
+/*
+
+   Identifier is HTML_<element>_<attribute>.
+   These must match the tables in HTML.c!
+   
+ */
+#define HTML_A_ACCESSKEY        0
+#define HTML_A_CLASS            1
+#define HTML_A_CLEAR            2
+#define HTML_A_COORDS           3
+#define HTML_A_DIR              4
+#define HTML_A_HREF             5
+#define HTML_A_ID               6
+#define HTML_A_ISMAP            7
+#define HTML_A_LANG             8
+#define HTML_A_MD               9
+#define HTML_A_NAME            10
+#define HTML_A_NOTAB           11
+#define HTML_A_ONCLICK         12
+#define HTML_A_ONMOUSEOUT      13
+#define HTML_A_ONMOUSEOVER     14
+#define HTML_A_REL             15
+#define HTML_A_REV             16
+#define HTML_A_SHAPE           17
+#define HTML_A_STYLE           18
+#define HTML_A_TABINDEX        19
+#define HTML_A_TARGET          20
+#define HTML_A_TITLE           21
+#define HTML_A_TYPE            22
+#define HTML_A_URN             23
+#define HTML_A_ATTRIBUTES      24
+
+#define HTML_ADDRESS_CLASS      0
+#define HTML_ADDRESS_CLEAR      1
+#define HTML_ADDRESS_DIR        2
+#define HTML_ADDRESS_ID         3
+#define HTML_ADDRESS_LANG       4
+#define HTML_ADDRESS_NOWRAP     5
+#define HTML_ADDRESS_STYLE      6
+#define HTML_ADDRESS_ATTRIBUTES 7
+
+#define	HTML_APPLET_ALIGN       0
+#define	HTML_APPLET_ALT         1
+#define	HTML_APPLET_CLASS       2
+#define	HTML_APPLET_CLEAR       3
+#define	HTML_APPLET_CODE        4
+#define	HTML_APPLET_CODEBASE    5
+#define	HTML_APPLET_DIR         6
+#define	HTML_APPLET_DOWNLOAD    7
+#define	HTML_APPLET_HEIGHT      8
+#define	HTML_APPLET_HSPACE      9
+#define	HTML_APPLET_ID         10
+#define	HTML_APPLET_LANG       11
+#define	HTML_APPLET_NAME       12
+#define	HTML_APPLET_STYLE      13
+#define	HTML_APPLET_TITLE      14
+#define	HTML_APPLET_VSPACE     15
+#define	HTML_APPLET_WIDTH      16
+#define	HTML_APPLET_ATTRIBUTES 17
+
+#define	HTML_AREA_ALT           0
+#define	HTML_AREA_CLASS         1
+#define	HTML_AREA_CLEAR         2
+#define	HTML_AREA_COORDS        3
+#define	HTML_AREA_DIR           4
+#define	HTML_AREA_HREF          5
+#define	HTML_AREA_ID            6
+#define	HTML_AREA_LANG          7
+#define	HTML_AREA_NOHREF        8
+#define	HTML_AREA_NONOTAB       9
+#define	HTML_AREA_ONCLICK      10
+#define	HTML_AREA_ONMOUSEOUT   11
+#define	HTML_AREA_ONMOUSEOVER  12
+#define	HTML_AREA_SHAPE        13
+#define	HTML_AREA_STYLE        14
+#define	HTML_AREA_TABINDEX     15
+#define	HTML_AREA_TARGET       16
+#define	HTML_AREA_TITLE        17
+#define	HTML_AREA_ATTRIBUTES   18
+
+#define HTML_BASE_HREF          0
+#define	HTML_BASE_TARGET        1
+#define HTML_BASE_ATTRIBUTES    2
+
+#define HTML_BGSOUND_CLASS      0
+#define HTML_BGSOUND_CLEAR      1
+#define HTML_BGSOUND_DIR        2
+#define HTML_BGSOUND_ID         3
+#define HTML_BGSOUND_LANG       4
+#define HTML_BGSOUND_LOOP       5
+#define HTML_BGSOUND_SRC        6
+#define HTML_BGSOUND_STYLE      7
+#define HTML_BGSOUND_ATTRIBUTES 8
+
+#define HTML_BQ_CLASS           0
+#define HTML_BQ_CLEAR           1
+#define HTML_BQ_DIR             2
+#define HTML_BQ_ID              3
+#define HTML_BQ_LANG            4
+#define HTML_BQ_NOWRAP          5
+#define HTML_BQ_STYLE           6
+#define HTML_BQ_ATTRIBUTES      7
+
+#define HTML_BODYTEXT_CLASS     1
+#define HTML_BODYTEXT_CLEAR     2
+#define HTML_BODYTEXT_DATA      3
+#define HTML_BODYTEXT_DIR       4
+#define HTML_BODYTEXT_ID        5
+#define HTML_BODYTEXT_LANG      6
+#define HTML_BODYTEXT_NAME      7
+#define HTML_BODYTEXT_OBJECT    8
+#define HTML_BODYTEXT_REF       9
+#define HTML_BODYTEXT_STYLE    10
+#define HTML_BODYTEXT_TYPE     11
+#define HTML_BODYTEXT_VALUE    12
+#define HTML_BODYTEXT_VALUETYPE  13
+#define HTML_BODYTEXT_ATTRIBUTES 14
+
+#define HTML_BODY_ALINK         0
+#define HTML_BODY_BACKGROUND    1
+#define HTML_BODY_BGCOLOR       2
+#define HTML_BODY_CLASS         3
+#define HTML_BODY_CLEAR         4
+#define HTML_BODY_DIR           5
+#define HTML_BODY_ID            6
+#define HTML_BODY_LANG          7
+#define HTML_BODY_LINK          8
+#define HTML_BODY_ONLOAD        9
+#define HTML_BODY_ONUNLOAD     10
+#define HTML_BODY_STYLE        11
+#define HTML_BODY_TEXT         12
+#define HTML_BODY_VLINK        13
+#define HTML_BODY_ATTRIBUTES   14
+
+#define HTML_CAPTION_ACCESSKEY  0
+#define HTML_CAPTION_ALIGN      1
+#define HTML_CAPTION_CLASS      2
+#define HTML_CAPTION_CLEAR      3
+#define HTML_CAPTION_DIR        4
+#define HTML_CAPTION_ID         5
+#define HTML_CAPTION_LANG       6
+#define HTML_CAPTION_STYLE      7
+#define HTML_CAPTION_ATTRIBUTES 8
+
+#define HTML_COL_ALIGN          0
+#define HTML_COL_CHAR           1
+#define HTML_COL_CHAROFF        2
+#define HTML_COL_CLASS          3
+#define HTML_COL_CLEAR          4
+#define HTML_COL_DIR            5
+#define HTML_COL_ID             6
+#define HTML_COL_LANG           7
+#define HTML_COL_SPAN           8
+#define HTML_COL_STYLE          9
+#define HTML_COL_VALIGN        10
+#define HTML_COL_WIDTH         11
+#define HTML_COL_ATTRIBUTES    12
+
+#define HTML_CREDIT_CLASS       0
+#define HTML_CREDIT_CLEAR       1
+#define HTML_CREDIT_DIR         2
+#define HTML_CREDIT_ID          3
+#define HTML_CREDIT_LANG        4
+#define HTML_CREDIT_STYLE       5
+#define HTML_CREDIT_ATTRIBUTES  6
+
+#define HTML_DIV_ALIGN          0
+#define HTML_DIV_CLASS          1
+#define HTML_DIV_CLEAR          2
+#define HTML_DIV_DIR            3
+#define HTML_DIV_ID             4
+#define HTML_DIV_LANG           5
+#define HTML_DIV_STYLE          6
+#define HTML_DIV_ATTRIBUTES     7
+
+#define HTML_DL_CLASS           0
+#define HTML_DL_CLEAR           1
+#define HTML_DL_COMPACT         2
+#define HTML_DL_DIR             3
+#define HTML_DL_ID              4
+#define HTML_DL_LANG            5
+#define HTML_DL_STYLE           6
+#define HTML_DL_ATTRIBUTES      7
+
+#define HTML_EMBED_ALIGN        0
+#define HTML_EMBED_ALT          1
+#define HTML_EMBED_BORDER       2
+#define HTML_EMBED_CLASS        3
+#define HTML_EMBED_CLEAR        4
+#define HTML_EMBED_DIR          5
+#define HTML_EMBED_HEIGHT       6
+#define HTML_EMBED_ID           7
+#define HTML_EMBED_IMAGEMAP     8
+#define HTML_EMBED_ISMAP        9
+#define HTML_EMBED_LANG        10
+#define HTML_EMBED_MD          11
+#define HTML_EMBED_NAME        12
+#define HTML_EMBED_NOFLOW      13
+#define HTML_EMBED_PARAMS      14
+#define HTML_EMBED_SRC         15
+#define HTML_EMBED_STYLE       16
+#define HTML_EMBED_UNITS       17
+#define HTML_EMBED_USEMAP      18
+#define HTML_EMBED_WIDTH       19
+#define HTML_EMBED_ATTRIBUTES  20
+
+#define HTML_FIELDSET_CLASS     0
+#define HTML_FIELDSET_CLEAR     1
+#define HTML_FIELDSET_DIR       2
+#define HTML_FIELDSET_ID        3
+#define HTML_FIELDSET_LANG      4
+#define HTML_FIELDSET_STYLE     5
+#define HTML_FIELDSET_TITLE     6
+#define HTML_FIELDSET_ATTRIBUTES 7
+
+#define HTML_FIG_ALIGN          0
+#define HTML_FIG_BORDER         1
+#define HTML_FIG_CLASS          2
+#define HTML_FIG_CLEAR          3
+#define HTML_FIG_DIR            4
+#define HTML_FIG_HEIGHT         5
+#define HTML_FIG_ID             6
+#define HTML_FIG_IMAGEMAP       7
+#define HTML_FIG_ISOBJECT       8
+#define HTML_FIG_LANG           9
+#define HTML_FIG_MD            10
+#define HTML_FIG_NOFLOW        11
+#define HTML_FIG_SRC           12
+#define HTML_FIG_STYLE         13
+#define HTML_FIG_UNITS         14
+#define HTML_FIG_WIDTH         15
+#define HTML_FIG_ATTRIBUTES    16
+
+#define HTML_FN_CLASS           0
+#define HTML_FN_CLEAR           1
+#define HTML_FN_DIR             2
+#define HTML_FN_ID              3
+#define HTML_FN_LANG            4
+#define HTML_FN_STYLE           5
+#define HTML_FN_ATTRIBUTES      6
+
+#define HTML_FONT_CLASS         0
+#define HTML_FONT_CLEAR         1
+#define HTML_FONT_COLOR         2
+#define HTML_FONT_DIR           3
+#define HTML_FONT_END           4
+#define HTML_FONT_FACE          5
+#define HTML_FONT_ID            6
+#define HTML_FONT_LANG          7
+#define HTML_FONT_SIZE          8
+#define HTML_FONT_STYLE         9
+#define HTML_FONT_ATTRIBUTES   10
+
+#define HTML_FORM_ACTION        0
+#define HTML_FORM_CLASS         1
+#define HTML_FORM_CLEAR         2
+#define HTML_FORM_DIR           3
+#define HTML_FORM_ENCTYPE       4
+#define HTML_FORM_ID            5
+#define HTML_FORM_LANG          6
+#define HTML_FORM_METHOD        7
+#define HTML_FORM_ONSUBMIT      8
+#define HTML_FORM_SCRIPT        9
+#define HTML_FORM_STYLE        10
+#define HTML_FORM_SUBJECT      11
+#define	HTML_FORM_TARGET       12
+#define HTML_FORM_TITLE        13
+#define HTML_FORM_ATTRIBUTES   14
+
+#define HTML_FRAME_MARGINHEIGHT 0
+#define HTML_FRAME_MARGINWIDTH  1
+#define HTML_FRAME_NAME         2
+#define HTML_FRAME_NORESIZE     3
+#define HTML_FRAME_SCROLLING    4
+#define HTML_FRAME_SRC          5
+#define HTML_FRAME_ATTRIBUTES   6
+
+#define HTML_FRAMESET_COLS      0
+#define HTML_FRAMESET_ROWS      1
+#define HTML_FRAMESET_ATTRIBUTES 2
+
+#define HTML_GEN_CLASS          0
+#define HTML_GEN_CLEAR          1
+#define HTML_GEN_DIR            2
+#define HTML_GEN_ID             3
+#define HTML_GEN_LANG           4
+#define HTML_GEN_STYLE          5
+#define HTML_GEN_ATTRIBUTES     6
+
+#define HTML_H_ALIGN            0
+#define HTML_H_CLASS            1
+#define HTML_H_CLEAR            2
+#define HTML_H_DINGBAT          3
+#define HTML_H_DIR              4
+#define HTML_H_ID               5
+#define HTML_H_LANG             6
+#define HTML_H_MD               7
+#define HTML_H_NOWRAP           8
+#define HTML_H_SEQNUM           9
+#define HTML_H_SKIP            10
+#define HTML_H_SRC             11
+#define HTML_H_STYLE           12
+#define HTML_H_ATTRIBUTES      13
+
+#define HTML_HR_ALIGN           0
+#define HTML_HR_CLASS           1
+#define HTML_HR_CLEAR           2
+#define HTML_HR_DIR             3
+#define HTML_HR_ID              4
+#define HTML_HR_MD              5
+#define HTML_HR_NOSHADE         6
+#define HTML_HR_SIZE            7
+#define HTML_HR_SRC             8
+#define HTML_HR_STYLE           9
+#define HTML_HR_WIDTH          10
+#define HTML_HR_ATTRIBUTES     11
+
+#define HTML_IMG_ALIGN          0
+#define HTML_IMG_ALT            1
+#define HTML_IMG_BORDER         2
+#define HTML_IMG_CLASS          3
+#define HTML_IMG_CLEAR          4
+#define HTML_IMG_DIR            5
+#define HTML_IMG_HEIGHT         6
+#define HTML_IMG_ID             7
+#define HTML_IMG_ISMAP          8
+#define HTML_IMG_ISOBJECT       9
+#define HTML_IMG_LANG          10
+#define HTML_IMG_MD            11
+#define HTML_IMG_SRC           12
+#define HTML_IMG_STYLE         13
+#define HTML_IMG_TITLE         14
+#define HTML_IMG_UNITS         15
+#define HTML_IMG_USEMAP        16
+#define HTML_IMG_WIDTH         17
+#define HTML_IMG_ATTRIBUTES    18
+
+#define HTML_INPUT_ACCEPT       0
+#define HTML_INPUT_ALIGN        1
+#define HTML_INPUT_CHECKED      2
+#define HTML_INPUT_CLASS        3
+#define HTML_INPUT_CLEAR        4
+#define HTML_INPUT_DIR          5
+#define HTML_INPUT_DISABLED     6
+#define HTML_INPUT_ERROR        7
+#define HTML_INPUT_HEIGHT       8
+#define HTML_INPUT_ID           9
+#define HTML_INPUT_LANG        10
+#define HTML_INPUT_MAX         11
+#define HTML_INPUT_MAXLENGTH   12
+#define HTML_INPUT_MD          13
+#define HTML_INPUT_MIN         14
+#define HTML_INPUT_NAME        15
+#define HTML_INPUT_NOTAB       16
+#define HTML_INPUT_ONBLUR      17
+#define HTML_INPUT_ONCHANGE    18
+#define HTML_INPUT_ONCLICK     19
+#define HTML_INPUT_ONFOCUS     20
+#define HTML_INPUT_ONSELECT    21
+#define HTML_INPUT_SIZE        22
+#define HTML_INPUT_SRC         23
+#define HTML_INPUT_STYLE       24
+#define HTML_INPUT_TABINDEX    25
+#define HTML_INPUT_TITLE       26
+#define HTML_INPUT_TYPE        27
+#define HTML_INPUT_VALUE       28
+#define HTML_INPUT_WIDTH       29
+#define HTML_INPUT_ATTRIBUTES  30
+
+#define HTML_ISINDEX_ACTION     0  /* Treat as synonym for HREF. - FM */
+#define HTML_ISINDEX_DIR        1
+#define HTML_ISINDEX_HREF       2  /* HTML 3.0 "action". - FM */
+#define HTML_ISINDEX_LANG       3
+#define HTML_ISINDEX_PROMPT     4  /* HTML 3.0 "prompt". - FM */
+#define HTML_ISINDEX_ATTRIBUTES 5
+
+#define HTML_LINK_CLASS         0
+#define HTML_LINK_HREF          1
+#define HTML_LINK_ID            2
+#define HTML_LINK_REL           3
+#define HTML_LINK_REV           4
+#define HTML_LINK_STYLE         5
+#define	HTML_LINK_TARGET        6
+#define HTML_LINK_TITLE         7
+#define HTML_LINK_TYPE          8
+#define HTML_LINK_ATTRIBUTES    9
+
+#define HTML_LABEL_ACCESSKEY    0
+#define HTML_LABEL_CLASS        1
+#define HTML_LABEL_CLEAR        2
+#define HTML_LABEL_DIR          3
+#define HTML_LABEL_FOR          4
+#define HTML_LABEL_ID           5
+#define HTML_LABEL_LANG         6
+#define HTML_LABEL_ONCLICK      7
+#define HTML_LABEL_STYLE        8
+#define HTML_LABEL_TITLE        9
+#define HTML_LABEL_ATTRIBUTES  10
+
+#define HTML_LI_CLASS           0
+#define HTML_LI_CLEAR           1
+#define HTML_LI_DINGBAT         2
+#define HTML_LI_DIR             3
+#define HTML_LI_ID              4
+#define HTML_LI_LANG            5
+#define HTML_LI_MD              6
+#define HTML_LI_SRC             7
+#define HTML_LI_SKIP            8
+#define HTML_LI_STYLE           9
+#define HTML_LI_TYPE           10
+#define HTML_LI_VALUE          11
+#define HTML_LI_ATTRIBUTES     12
+
+#define HTML_MAP_CLASS          0
+#define HTML_MAP_CLEAR          1
+#define HTML_MAP_DIR            2
+#define HTML_MAP_ID             3
+#define HTML_MAP_LANG           4
+#define HTML_MAP_NAME           5
+#define HTML_MAP_STYLE          6
+#define HTML_MAP_TITLE          7
+#define HTML_MAP_ATTRIBUTES     8
+
+#define HTML_MATH_BOX           0
+#define HTML_MATH_CLASS         1
+#define HTML_MATH_CLEAR         2
+#define HTML_MATH_DIR           3
+#define HTML_MATH_ID            4
+#define HTML_MATH_LANG          5
+#define HTML_MATH_STYLE         6
+#define HTML_MATH_ATTRIBUTES    7
+
+#define HTML_META_CONTENT       0
+#define HTML_META_HTTP_EQUIV    1  /* For parsing in HTML.c - FM */
+#define HTML_META_NAME          2
+#define HTML_META_ATTRIBUTES    3
+
+#define NEXTID_N 		0
+
+#define HTML_NOTE_CLASS         0
+#define HTML_NOTE_CLEAR         1
+#define HTML_NOTE_DIR           2
+#define HTML_NOTE_ID            3
+#define HTML_NOTE_LANG          4
+#define HTML_NOTE_MD            5
+#define HTML_NOTE_ROLE          6 /* Old name for CLASS - FM */
+#define HTML_NOTE_SRC           7
+#define HTML_NOTE_STYLE         8
+#define HTML_NOTE_ATTRIBUTES    9
+
+#define HTML_OBJECT_ALIGN       0
+#define HTML_OBJECT_BORDER      1
+#define HTML_OBJECT_CLASS       2
+#define HTML_OBJECT_CLASSID     3
+#define HTML_OBJECT_CODEBASE    4
+#define HTML_OBJECT_CODETYPE    5
+#define HTML_OBJECT_DATA        6
+#define HTML_OBJECT_DECLARE     7
+#define HTML_OBJECT_DIR         8
+#define HTML_OBJECT_HEIGHT      9
+#define HTML_OBJECT_HSPACE     10
+#define HTML_OBJECT_ID         11
+#define HTML_OBJECT_ISMAP      12
+#define HTML_OBJECT_LANG       13
+#define HTML_OBJECT_NAME       14
+#define HTML_OBJECT_NOTAB      15
+#define HTML_OBJECT_SHAPES     16
+#define HTML_OBJECT_STANDBY    17
+#define HTML_OBJECT_STYLE      18
+#define HTML_OBJECT_TABINDEX   19
+#define HTML_OBJECT_TITLE      20
+#define HTML_OBJECT_TYPE       21
+#define HTML_OBJECT_USEMAP     22
+#define HTML_OBJECT_VSPACE     23
+#define HTML_OBJECT_WIDTH      24
+#define HTML_OBJECT_ATTRIBUTES 25
+
+#define HTML_OL_CLASS           0
+#define HTML_OL_CLEAR           1
+#define HTML_OL_COMPACT         2
+#define HTML_OL_CONTINUE        3
+#define HTML_OL_DIR             4
+#define HTML_OL_ID              5
+#define HTML_OL_LANG            6
+#define HTML_OL_SEQNUM          7
+#define HTML_OL_START           8
+#define HTML_OL_STYLE           9
+#define HTML_OL_TYPE           10
+#define HTML_OL_ATTRIBUTES     11
+
+#define HTML_OPTION_CLASS       0
+#define HTML_OPTION_CLEAR       1
+#define HTML_OPTION_DIR         2
+#define HTML_OPTION_DISABLED    3
+#define HTML_OPTION_ERROR       4
+#define HTML_OPTION_ID          5
+#define HTML_OPTION_LANG        6
+#define HTML_OPTION_SELECTED    7
+#define HTML_OPTION_SHAPE       8
+#define HTML_OPTION_STYLE       9
+#define HTML_OPTION_VALUE      10
+#define HTML_OPTION_ATTRIBUTES 11
+
+#define HTML_OVERLAY_CLASS      0
+#define HTML_OVERLAY_HEIGHT     1
+#define HTML_OVERLAY_ID         2
+#define HTML_OVERLAY_IMAGEMAP   3
+#define HTML_OVERLAY_MD         4
+#define HTML_OVERLAY_SRC        5
+#define HTML_OVERLAY_STYLE      6
+#define HTML_OVERLAY_UNITS      7
+#define HTML_OVERLAY_WIDTH      8
+#define HTML_OVERLAY_X          9
+#define HTML_OVERLAY_Y         10
+#define HTML_OVERLAY_ATTRIBUTES 11
+
+#define HTML_P_ALIGN            0
+#define HTML_P_CLASS            1
+#define HTML_P_CLEAR            2
+#define HTML_P_DIR              3
+#define HTML_P_ID               4
+#define HTML_P_LANG             5
+#define HTML_P_STYLE            6
+#define HTML_P_NOWRAP           7
+#define HTML_P_ATTRIBUTES       8
+
+#define HTML_PARAM_ACCEPT       0
+#define HTML_PARAM_ACCEPT_CHARSET  1
+#define HTML_PARAM_ACCEPT_ENCODING 2
+#define HTML_PARAM_CLASS        3
+#define HTML_PARAM_CLEAR        4
+#define HTML_PARAM_DATA         5
+#define HTML_PARAM_DIR          6
+#define HTML_PARAM_ID           7
+#define HTML_PARAM_LANG         8
+#define HTML_PARAM_NAME         9
+#define HTML_PARAM_OBJECT      10
+#define HTML_PARAM_REF         11
+#define HTML_PARAM_STYLE       12
+#define HTML_PARAM_TYPE        13
+#define HTML_PARAM_VALUE       14
+#define HTML_PARAM_VALUEREF    15  /* Use VALUETYPE (DATA|REF|OBJECT). - FM */
+#define HTML_PARAM_VALUETYPE   16
+#define HTML_PARAM_ATTRIBUTES  17
+
+#define HTML_SCRIPT_CLASS       0
+#define HTML_SCRIPT_CLEAR       1
+#define HTML_SCRIPT_DIR         2
+#define HTML_SCRIPT_EVENT       3
+#define HTML_SCRIPT_FOR         4
+#define HTML_SCRIPT_ID          5
+#define HTML_SCRIPT_LANG        6
+#define HTML_SCRIPT_LANGUAGE    7
+#define HTML_SCRIPT_NAME        8
+#define HTML_SCRIPT_SCRIPTENGINE 9
+#define HTML_SCRIPT_SRC        10
+#define HTML_SCRIPT_STYLE      11
+#define HTML_SCRIPT_TYPE       12
+#define HTML_SCRIPT_ATTRIBUTES 13
+
+#define HTML_SELECT_ALIGN       0
+#define HTML_SELECT_CLASS       1
+#define HTML_SELECT_CLEAR       2
+#define HTML_SELECT_DIR         3
+#define HTML_SELECT_DISABLED    4
+#define HTML_SELECT_ERROR       5
+#define HTML_SELECT_HEIGHT      6
+#define HTML_SELECT_ID          7
+#define HTML_SELECT_LANG        8
+#define HTML_SELECT_MD          9
+#define HTML_SELECT_MULTIPLE   10
+#define HTML_SELECT_NAME       11
+#define HTML_SELECT_NOTAB      12
+#define HTML_SELECT_ONBLUR     13
+#define HTML_SELECT_ONCHANGE   14
+#define HTML_SELECT_ONFOCUS    15
+#define HTML_SELECT_SIZE       16
+#define HTML_SELECT_STYLE      17
+#define HTML_SELECT_TABINDEX   18
+#define HTML_SELECT_TITLE      19
+#define HTML_SELECT_UNITS      20
+#define HTML_SELECT_WIDTH      21
+#define HTML_SELECT_ATTRIBUTES 22
+
+#define HTML_STYLE_DIR          0
+#define HTML_STYLE_LANG         1
+#define HTML_STYLE_NOTATION     2
+#define HTML_STYLE_TITLE        3
+#define HTML_STYLE_ATTRIBUTES   4
+
+#define HTML_TAB_ALIGN          0
+#define HTML_TAB_CLASS          1
+#define HTML_TAB_CLEAR          2
+#define HTML_TAB_DIR            3
+#define HTML_TAB_DP             4
+#define HTML_TAB_ID             5
+#define HTML_TAB_INDENT         6
+#define HTML_TAB_LANG           7
+#define HTML_TAB_STYLE          8
+#define HTML_TAB_TO             9
+#define HTML_TAB_ATTRIBUTES    10
+
+#define HTML_TABLE_ALIGN        0
+#define HTML_TABLE_BORDER       1
+#define HTML_TABLE_CELLPADDING  2
+#define HTML_TABLE_CELLSPACING  3
+#define HTML_TABLE_CLASS        4
+#define HTML_TABLE_CLEAR        5
+#define HTML_TABLE_COLS         6
+#define HTML_TABLE_COLSPEC      7
+#define HTML_TABLE_DIR          8
+#define HTML_TABLE_DP           9
+#define HTML_TABLE_FRAME       10
+#define HTML_TABLE_ID          11
+#define HTML_TABLE_LANG        12
+#define HTML_TABLE_NOFLOW      13
+#define HTML_TABLE_NOWRAP      14
+#define HTML_TABLE_RULES       15
+#define HTML_TABLE_STYLE       16
+#define HTML_TABLE_UNITS       17
+#define HTML_TABLE_WIDTH       18
+#define HTML_TABLE_ATTRIBUTES  19
+
+#define HTML_TD_ALIGN           0
+#define HTML_TD_AXES            1
+#define HTML_TD_AXIS            2
+#define HTML_TD_CHAR            3
+#define HTML_TD_CHAROFF         4
+#define HTML_TD_CLASS           5
+#define HTML_TD_CLEAR           6
+#define HTML_TD_COLSPAN         7
+#define HTML_TD_DIR             8
+#define HTML_TD_DP              9
+#define HTML_TD_ID             10
+#define HTML_TD_LANG           11
+#define HTML_TD_NOWRAP         12
+#define HTML_TD_ROWSPAN        13
+#define HTML_TD_STYLE          14
+#define HTML_TD_VALIGN         15
+#define HTML_TD_ATTRIBUTES     16
+
+#define HTML_TEXTAREA_ALIGN     0
+#define HTML_TEXTAREA_CLASS     1
+#define HTML_TEXTAREA_CLEAR     2
+#define HTML_TEXTAREA_COLS      3
+#define HTML_TEXTAREA_DIR       4
+#define HTML_TEXTAREA_DISABLED  5
+#define HTML_TEXTAREA_ERROR     6
+#define HTML_TEXTAREA_ID        7
+#define HTML_TEXTAREA_LANG      8
+#define HTML_TEXTAREA_NAME      9
+#define HTML_TEXTAREA_NOTAB    10
+#define HTML_TEXTAREA_ONBLUR   11
+#define HTML_TEXTAREA_ONCHANGE 12
+#define HTML_TEXTAREA_ONFOCUS  13
+#define HTML_TEXTAREA_ONSELECT 14
+#define HTML_TEXTAREA_ROWS     15
+#define HTML_TEXTAREA_STYLE    16
+#define HTML_TEXTAREA_TABINDEX 17
+#define HTML_TEXTAREA_TITLE    18
+#define HTML_TEXTAREA_ATTRIBUTES 19
+
+#define HTML_TR_ALIGN           0
+#define HTML_TR_CHAR            1
+#define HTML_TR_CHAROFF         2
+#define HTML_TR_CLASS           3
+#define HTML_TR_CLEAR           4
+#define HTML_TR_DIR             5
+#define HTML_TR_DP              6
+#define HTML_TR_ID              7
+#define HTML_TR_LANG            8
+#define HTML_TR_NOWRAP          9
+#define HTML_TR_STYLE          10
+#define HTML_TR_VALIGN         11
+#define HTML_TR_ATTRIBUTES     12
+
+#define HTML_UL_CLASS           0
+#define HTML_UL_CLEAR           1
+#define HTML_UL_COMPACT         2
+#define HTML_UL_DINGBAT         3
+#define HTML_UL_DIR             4
+#define HTML_UL_ID              5
+#define HTML_UL_LANG            6
+#define HTML_UL_MD              7
+#define HTML_UL_PLAIN           8
+#define HTML_UL_SRC             9
+#define HTML_UL_STYLE          10
+#define HTML_UL_TYPE           11
+#define HTML_UL_WRAP           12
+#define HTML_UL_ATTRIBUTES     13
+
+extern CONST SGML_dtd HTML_dtd;
+
+/*
+
+Start anchor element
+
+   It is kinda convenient to have a particular routine for starting an anchor
+   element, as everything else for HTML is simple anyway.
+   
+  ON ENTRY
+  
+   targetstream points to a structured stream object.
+   
+   name and href point to attribute strings or are NULL if the attribute is
+   to be omitted.
+   
+ */
+extern void HTStartAnchor PARAMS((
+                HTStructured * targetstream,
+                CONST char *    name,
+                CONST char *    href));
+
+/*
+
+Start IsIndex element - FM
+
+   It is kinda convenient to have a particular routine for starting an IsIndex
+   element with the prompt and/or href (action) attributes specified.
+   
+  ON ENTRY
+  
+   targetstream points to a structured stream object.
+   
+   prompt and href point to attribute strings or are NULL if the attribute is
+   to be omitted.
+   
+ */
+extern void HTStartIsIndex PARAMS((
+                HTStructured * targetstream,
+                CONST char *    prompt,
+                CONST char *    href));
+
+
+#endif /* HTMLDTD_H */
+
+/*
+
+   End of module definition  */
diff --git a/WWW/Library/Implementation/HTMLGen.c b/WWW/Library/Implementation/HTMLGen.c
new file mode 100644
index 00000000..bea63bac
--- /dev/null
+++ b/WWW/Library/Implementation/HTMLGen.c
@@ -0,0 +1,367 @@
+/*		HTML Generator
+**		==============
+**
+**	This version of the HTML object sends HTML markup to the output stream.
+**
+** Bugs:	Line wrapping is not done at all.
+**		All data handled as PCDATA.
+**		Should convert old XMP, LISTING and PLAINTEXT to PRE.
+**
+**	It is not obvious to me right now whether the HEAD should be generated
+**	from the incomming data or the anchor.  Currently it is from the former
+**	which is cleanest.
+*/
+
+#include "HTUtils.h"
+#include "tcp.h"
+
+#define BUFFER_SIZE	80	/* Line buffer attempts to make neat breaks */
+
+/* Implements:
+*/
+#include "HTMLGen.h"
+
+#include <stdio.h>
+#include "HTMLDTD.h"
+#include "HTStream.h"
+#include "SGML.h"
+#include "HTFormat.h"
+
+#include "LYLeaks.h"
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+#define PUTC(c) (*me->targetClass.put_character)(me->target, c)
+/* #define PUTS(s) (*me->targetClass.put_string)(me->target, s) */
+#define PUTB(s,l) (*me->targetClass.put_block)(me->target, s, l)
+
+/*		HTML Object
+**		-----------
+*/
+
+struct _HTStream {
+	CONST HTStreamClass *		isa;	
+	HTStream * 			target;
+	HTStreamClass			targetClass;	/* COPY for speed */
+};
+
+struct _HTStructured {
+	CONST HTStructuredClass *	isa;
+	HTStream * 			target;
+	HTStreamClass			targetClass;	/* COPY for speed */
+	
+	char				buffer[BUFFER_SIZE+1]; /* 1for NL */
+	char *				write_pointer;
+	char *				line_break;
+	int				cleanness;
+	BOOL				delete_line_break_char;
+	BOOL				preformatted;
+};
+
+
+/*	Flush Buffer
+**	------------
+*/
+PRIVATE void HTMLGen_flush ARGS1(HTStructured *, me)
+{
+    (*me->targetClass.put_block)(me->target, 
+    				me->buffer,
+				me->write_pointer - me->buffer);
+    me->write_pointer = me->buffer;
+    me->line_break = me->buffer;
+    me->cleanness = 0;
+    me->delete_line_break_char = NO;
+}
+
+
+/*	Character handling
+**	------------------
+**
+**	The tricky bits are the line break handling.  This attempts
+**	to synchrononise line breaks on sentence or phrase ends. This
+**	is important if one stores SGML files in a line-oriented code
+**	repository, so that if a small change is made, line ends don't
+**	shift in a ripple-through to apparently change a large part of the
+**	file. We give extra "cleanness" to spaces appearing directly
+**	after periods (full stops), [semi]colons and commas.
+**	   This should make the source files easier to read and modify
+**	by hand, too, though this is not a primary design consideration.
+*/
+PRIVATE void HTMLGen_put_character ARGS2(HTStructured *, me, char, c)
+{
+
+    *me->write_pointer++ = c;
+    
+    if (c=='\n') {
+        HTMLGen_flush(me);
+	return;
+    }
+    
+    if ((!me->preformatted  && c==' ')) {
+        int new_cleanness = 1;
+	if (me->write_pointer > (me->buffer + 1)) {
+	    char delims[5];
+	    char * p;
+	    strcpy(delims, ",;:.");		/* @@ english bias */
+	    p = strchr(delims, me->write_pointer[-2]);
+	    if (p) new_cleanness = p - delims + 2;
+	}
+	if (new_cleanness >= me->cleanness) {
+	    me->line_break = me->write_pointer - 1;  /* Point to space */
+	    me->cleanness = new_cleanness;
+	    me->delete_line_break_char = YES;
+	}
+    }
+    
+    /* Flush buffer out when full */
+    if (me->write_pointer == me->buffer + BUFFER_SIZE) {
+    	if (me->cleanness) {
+	    char line_break_char = me->line_break[0];
+	    char * saved = me->line_break;
+	    
+	    if (me->delete_line_break_char) saved++; 
+	    me->line_break[0] = '\n'; 
+	    (*me->targetClass.put_block)(me->target,
+	    				me->buffer,
+					me->line_break - me->buffer + 1);
+	    me->line_break[0] = line_break_char;
+	    {  /* move next line in */
+	    	char * p=saved;
+		char *q;
+		for(q=me->buffer; p < me->write_pointer; )
+			*q++ = *p++;
+	    }
+	    me->cleanness = 0;
+	    me->delete_line_break_char = 0;
+	    me->write_pointer = me->write_pointer - (saved-me->buffer);
+
+	} else {
+	    (*me->targetClass.put_block)(me->target,
+	    	me->buffer,
+		BUFFER_SIZE);
+	    me->write_pointer = me->buffer;
+	}
+	me->line_break = me->buffer;
+    }
+}
+
+
+
+/*	String handling
+**	---------------
+*/
+PRIVATE void HTMLGen_put_string ARGS2(HTStructured *, me, CONST char*, s)
+{
+    CONST char * p;
+    for(p=s; *p; p++) HTMLGen_put_character(me, *p);
+}
+
+PRIVATE void HTMLGen_write ARGS3(HTStructured *, me, CONST char*, s, int, l)
+{
+    CONST char * p;
+    for(p=s; p<s+l; p++) HTMLGen_put_character(me, *p);
+}
+
+
+/*	Start Element
+**	-------------
+**
+**	Within the opening tag, there may be spaces
+**	and the line may be broken at these spaces.
+*/
+PRIVATE void HTMLGen_start_element ARGS5(
+	HTStructured *, 	me,
+	int,			element_number,
+	CONST BOOL*,	 	present,
+	CONST char **,		value,
+	char **,		insert)
+{
+    int i;
+    
+    BOOL was_preformatted = me->preformatted;
+    HTTag * tag = &HTML_dtd.tags[element_number];
+
+    me->preformatted = NO;	/* free text within tags */
+    HTMLGen_put_character(me, '<');
+    HTMLGen_put_string(me, tag->name);
+    if (present) for (i=0; i< tag->number_of_attributes; i++) {
+        if (present[i]) {
+	    HTMLGen_put_character(me, ' ');
+	    HTMLGen_put_string(me, tag->attributes[i].name);
+	    if (value[i]) {
+	 	HTMLGen_put_string(me, "=\"");
+		HTMLGen_put_string(me, value[i]);
+		HTMLGen_put_character(me, '"');
+	    }
+	}
+    }
+    HTMLGen_put_string(me, ">"); /* got rid of \n LJM */
+    
+    /* Make very specific HTML assumption that PRE can't be
+       nested! */
+    me->preformatted = (element_number == HTML_PRE)  ? YES : was_preformatted;
+
+	/* can break after element start */ 
+    if (!me->preformatted && tag->contents != SGML_EMPTY) {  
+    	me->line_break = me->write_pointer;	/* Don't you hate SGML?  */
+	me->cleanness = 1;
+	me->delete_line_break_char = NO;
+    }
+}
+
+
+/*		End Element
+**		-----------
+**
+*/
+/*	When we end an element, the style must be returned to that
+**	in effect before that element.  Note that anchors (etc?)
+**	don't have an associated style, so that we must scan down the
+**	stack for an element with a defined style. (In fact, the styles
+**	should be linked to the whole stack not just the top one.)
+**	TBL 921119
+*/
+PRIVATE void HTMLGen_end_element ARGS3(HTStructured *, me,
+			int , element_number, char **, insert)
+{
+    if (!me->preformatted && 
+		HTML_dtd.tags[element_number].contents != SGML_EMPTY) {
+    				/* can break before element end */ 
+    	me->line_break = me->write_pointer;	/* Don't you hate SGML?  */
+	me->cleanness = 1;
+	me->delete_line_break_char = NO;
+    }
+    HTMLGen_put_string(me, "</");
+    HTMLGen_put_string(me, HTML_dtd.tags[element_number].name);
+    HTMLGen_put_character(me, '>');
+    if (element_number == HTML_PRE) me->preformatted = NO;
+}
+
+
+/*		Expanding entities
+**		------------------
+**
+*/
+
+PRIVATE void HTMLGen_put_entity ARGS2(HTStructured *, me, int, entity_number)
+{
+    HTMLGen_put_character(me, '&');
+    HTMLGen_put_string(me, HTML_dtd.entity_names[entity_number]);
+    HTMLGen_put_character(me, ';');
+}
+
+
+
+/*	Free an HTML object
+**	-------------------
+**
+*/
+PRIVATE void HTMLGen_free ARGS1(HTStructured *, me)
+{
+    (*me->targetClass.put_character)(me->target, '\n');
+    HTMLGen_flush(me);
+    (*me->targetClass._free)(me->target);	/* ripple through */
+    FREE(me);
+}
+
+
+PRIVATE void PlainToHTML_free ARGS1(HTStructured *, me)
+{
+    HTMLGen_end_element(me, HTML_PRE, 0);
+    HTMLGen_free(me);
+}
+
+
+
+PRIVATE void HTMLGen_abort ARGS2(HTStructured *, me, HTError, e)
+{
+    HTMLGen_free(me);
+}
+
+
+PRIVATE void PlainToHTML_abort ARGS2(HTStructured *, me, HTError, e)
+{
+    PlainToHTML_free(me);
+}
+
+
+
+/*	Structured Object Class
+**	-----------------------
+*/
+PRIVATE CONST HTStructuredClass HTMLGeneration = /* As opposed to print etc */
+{		
+	"text/html",
+	HTMLGen_free,
+	HTMLGen_abort,
+	HTMLGen_put_character, 	HTMLGen_put_string, HTMLGen_write,
+	HTMLGen_start_element, 	HTMLGen_end_element,
+	HTMLGen_put_entity
+}; 
+
+
+/*	Subclass-specific Methods
+**	-------------------------
+*/
+
+PUBLIC HTStructured * HTMLGenerator ARGS1(HTStream *, output)
+{
+    HTStructured* me = (HTStructured*)malloc(sizeof(*me));
+    if (me == NULL)
+        outofmem(__FILE__, "HTMLGenerator");
+    me->isa = &HTMLGeneration;       
+
+    me->target = output;
+    me->targetClass = *me->target->isa; /* Copy pointers to routines for speed*/
+    
+    me->write_pointer = me->buffer;
+    me->line_break = 	me->buffer;
+    me->cleanness = 	0;
+    me->delete_line_break_char = NO;
+    me->preformatted = 	NO;
+    return me;
+}
+
+/*	Stream Object Class
+**	-------------------
+**
+**	This object just converts a plain text stream into HTML
+**	It is officially a structured strem but only the stream bits exist.
+**	This is just the easiest way of typecasting all the routines.
+*/
+PRIVATE CONST HTStructuredClass PlainToHTMLConversion =
+{		
+	"plaintexttoHTML",
+	HTMLGen_free,	
+	PlainToHTML_abort,	
+	HTMLGen_put_character,
+	HTMLGen_put_string,
+	HTMLGen_write,
+	NULL,		/* Structured stuff */
+	NULL,
+	NULL
+}; 
+
+
+/*	HTConverter from plain text to HTML Stream
+**	------------------------------------------
+*/
+
+PUBLIC HTStream* HTPlainToHTML ARGS3(
+	HTPresentation *,	pres,
+	HTParentAnchor *,	anchor,	
+	HTStream *,		sink)
+{
+    HTStructured* me = (HTStructured*)malloc(sizeof(*me));
+    if (me == NULL)
+        outofmem(__FILE__, "PlainToHTML");
+    me->isa = (HTStructuredClass*) &PlainToHTMLConversion;       
+
+    me->target = sink;
+    me->targetClass = *me->target->isa;
+    	/* Copy pointers to routines for speed*/
+	
+    HTMLGen_put_string(me, "<HTML>\n<BODY>\n<PRE>\n");
+    me->preformatted = YES;
+    return (HTStream*) me;
+}
diff --git a/WWW/Library/Implementation/HTMLGen.h b/WWW/Library/Implementation/HTMLGen.h
new file mode 100644
index 00000000..ac814ad8
--- /dev/null
+++ b/WWW/Library/Implementation/HTMLGen.h
@@ -0,0 +1,32 @@
+/*                  /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTMLGen.html
+                                      HTML GENERATOR
+                                             
+   This module converts structed stream into stream.  That is, given a stream to write to,
+   it will give you a structured stream to
+   
+ */
+#ifndef HTMLGEN_H
+#define HTMLGEN_H
+
+#include "HTML.h"
+#include "HTStream.h"
+
+/* Subclass:
+*/
+/* extern CONST HTStructuredClass HTMLGeneration; */
+
+/* Special Creation:
+*/
+extern HTStructured * HTMLGenerator PARAMS((HTStream * output));
+
+extern HTStream * HTPlainToHTML PARAMS((
+        HTPresentation *        pres,
+        HTParentAnchor *        anchor,
+        HTStream *              sink));
+
+
+#endif
+
+/*
+
+    */
diff --git a/WWW/Library/Implementation/HTNews.c b/WWW/Library/Implementation/HTNews.c
new file mode 100644
index 00000000..7eeb7c2a
--- /dev/null
+++ b/WWW/Library/Implementation/HTNews.c
@@ -0,0 +1,1646 @@
+/*			NEWS ACCESS				HTNews.c
+**			===========
+**
+** History:
+**	26 Sep 90	Written TBL
+**	29 Nov 91	Downgraded to C, for portable implementation.
+*/
+
+#include "HTUtils.h"		/* Coding convention macros */
+#include "tcp.h"
+
+/* Implements:
+*/
+#include "HTNews.h"
+
+#include "HTCJK.h"
+#include "HTMIME.h"
+#include "HTTCP.h"
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+/* this define should be in HTFont.h :( */
+#define HT_NON_BREAK_SPACE ((char)1)   /* For now */
+
+#define CR   FROMASCII('\015')	/* Must be converted to ^M for transmission */
+#define LF   FROMASCII('\012')	/* Must be converted to ^J for transmission */
+
+#define NEWS_PORT 119		/* See rfc977 */
+#define SNEWS_PORT 563		/* See Lou Montulli */
+#define APPEND			/* Use append methods */
+PUBLIC int HTNewsChunkSize = 30;/* Number of articles for quick display */
+PUBLIC int HTNewsMaxChunk = 40;	/* Largest number of articles in one window */
+
+#ifndef DEFAULT_NEWS_HOST
+#define DEFAULT_NEWS_HOST "news"
+#endif
+#ifndef SERVER_FILE
+#define SERVER_FILE "/usr/local/lib/rn/server"
+#endif
+
+#define NEWS_NETREAD   NETREAD
+#define NEWS_NETWRITE  NETWRITE
+			
+#include <ctype.h>
+
+#include "HTML.h"
+#include "HTParse.h"
+#include "HTFormat.h"
+#include "HTAlert.h"
+
+#include "LYLeaks.h"
+
+#define BIG 1024 /* @@@ */
+
+struct _HTStructured {
+	CONST HTStructuredClass *	isa;
+	/* ... */
+};
+
+#define NEXT_CHAR HTGetCharacter()
+#define LINE_LENGTH 512			/* Maximum length of line of ARTICLE etc */
+#define GROUP_NAME_LENGTH	256	/* Maximum length of group name */
+extern BOOLEAN scan_for_buried_news_references;
+extern BOOLEAN LYListNewsNumbers;
+extern BOOLEAN LYListNewsDates;
+extern HTCJKlang HTCJK;
+
+/*
+**  Module-wide variables.
+*/
+PUBLIC  char * HTNewsHost=NULL;
+PRIVATE char * NewsHost=NULL;
+PRIVATE char * NewsHREF=NULL;
+PRIVATE int s;					/* Socket for NewsHost */
+PRIVATE char response_text[LINE_LENGTH+1];	/* Last response */
+/* PRIVATE HText *	HT;	*/		/* the new hypertext */
+PRIVATE HTStructured * target;			/* The output sink */
+PRIVATE HTStructuredClass targetClass;		/* Copy of fn addresses */
+PRIVATE HTParentAnchor *node_anchor;		/* Its anchor */
+PRIVATE int	diagnostic;			/* level: 0=none 2=source */
+
+#define PUTC(c) (*targetClass.put_character)(target, c)
+#define PUTS(s) (*targetClass.put_string)(target, s)
+#define START(e) (*targetClass.start_element)(target, e, 0, 0, 0)
+#define END(e) (*targetClass.end_element)(target, e, 0)
+
+PUBLIC CONST char * HTGetNewsHost NOARGS
+{
+	return HTNewsHost;
+}
+
+PUBLIC void HTSetNewsHost ARGS1(CONST char *, value)
+{
+	StrAllocCopy(HTNewsHost, value);
+}
+
+/*	Initialisation for this module
+**	------------------------------
+**
+**	Except on the NeXT, we pick up the NewsHost name from
+**
+**	1.	Environment variable NNTPSERVER
+**	2.	File SERVER_FILE
+**	3.	Compilation time macro DEFAULT_NEWS_HOST
+**	4.	Default to "news"
+**
+**	On the NeXT, we pick up the NewsHost name from, in order:
+**
+**	1.	WorldWideWeb default "NewsHost"
+**	2.	Global default "NewsHost"
+**	3.	News default "NewsHost"
+**	4.	Compilation time macro DEFAULT_NEWS_HOST
+**	5.	Default to "news"
+*/
+PRIVATE BOOL initialized = NO;
+PRIVATE BOOL initialize NOARGS
+{
+
+/*   Get name of Host
+*/
+#ifdef NeXTStep
+    if ((HTNewsHost = NXGetDefaultValue("WorldWideWeb","NewsHost"))==0)
+        if ((HTNewsHost = NXGetDefaultValue("News","NewsHost")) == 0)
+	    HTNewsHost = DEFAULT_NEWS_HOST;
+#else
+    if (getenv("NNTPSERVER")) {
+        StrAllocCopy(HTNewsHost, (char *)getenv("NNTPSERVER"));
+	if (TRACE) fprintf(stderr, "HTNews: NNTPSERVER defined as `%s'\n",
+		HTNewsHost);
+    } else {
+        char server_name[256];
+        FILE* fp = fopen(SERVER_FILE, "r");
+        if (fp) {
+	    if (fscanf(fp, "%s", server_name)==1) {
+	        StrAllocCopy(HTNewsHost, server_name);
+		if (TRACE) fprintf(stderr,
+		"HTNews: File %s defines news host as `%s'\n",
+		        SERVER_FILE, HTNewsHost);
+	    }
+	    fclose(fp);
+	}
+    }
+    if (!HTNewsHost)
+        HTNewsHost = DEFAULT_NEWS_HOST;
+#endif /* NeXTStep */
+
+    s = -1;		/* Disconnected */
+    
+    return YES;
+}
+
+/*	Send NNTP Command line to remote host & Check Response
+**	------------------------------------------------------
+**
+** On entry,
+**	command	points to the command to be sent, including CRLF, or is null
+**		pointer if no command to be sent.
+** On exit,
+**	Negative status indicates transmission error, socket closed.
+**	Positive status is an NNTP status.
+*/
+PRIVATE int response ARGS1(CONST char *,command)
+{
+    int result;    
+    char * p = response_text;
+    char ch;
+    if (command) {
+        int status;
+	int length = strlen(command);
+	if (TRACE) fprintf(stderr, "NNTP command to be sent: %s", command);
+#ifdef NOT_ASCII
+	{
+	    CONST char  * p;
+	    char 	* q;
+	    char ascii[LINE_LENGTH+1];
+	    for(p = command, q=ascii; *p; p++, q++) {
+		*q = TOASCII(*p);
+	    }
+            status = NEWS_NETWRITE(s, ascii, length);
+	}
+#else
+        status = NEWS_NETWRITE(s, (char *)command, length);
+#endif
+	if (status < 0){
+	    if (TRACE) fprintf(stderr,
+	        "HTNews: Unable to send command. Disconnecting.\n");
+	    NETCLOSE(s);
+	    s = -1;
+	    return status;
+	} /* if bad status */
+    } /* if command to be sent */
+    
+    for(;;) {  
+	if (((*p++ = NEXT_CHAR) == LF) ||
+	    (p == &response_text[LINE_LENGTH])) {
+	    *p++ = '\0';			/* Terminate the string */
+	    if (TRACE) fprintf(stderr, "NNTP Response: %s\n", response_text);
+	    sscanf(response_text, "%d", &result);
+	    return result;	    
+	} /* if end of line */
+	
+	if ((ch = *(p-1)) == (char)EOF) {
+	    if (TRACE) fprintf(stderr,
+	    	"HTNews: EOF on read, closing socket %d\n", s);
+	    NETCLOSE(s);	/* End of file, close socket */
+	    return s = -1;	/* End of file on response */
+	}
+    } /* Loop over characters */
+}
+
+/*	Case insensitive string comparisons
+**	-----------------------------------
+**
+** On entry,
+**	template must be already un upper case.
+**	unknown may be in upper or lower or mixed case to match.
+*/
+PRIVATE BOOL match ARGS2 (CONST char *,unknown, CONST char *,template)
+{
+    CONST char * u = unknown;
+    CONST char * t = template;
+    for (; *u && *t && (TOUPPER(*u) == *t); u++, t++)
+        ; /* Find mismatch or end */
+    return (BOOL)(*t == 0);		/* OK if end of template */
+}
+
+/*	Find Author's name in mail address
+**	----------------------------------
+**
+** On exit,
+**	Returns allocated string which cannot be freed by the
+**	calling function, and is reallocated on subsequent calls
+**	to this function.
+**
+** For example, returns "Tim Berners-Lee" if given any of
+**	" Tim Berners-Lee <tim@online.cern.ch> "
+**  or	" tim@online.cern.ch ( Tim Berners-Lee ) "
+*/
+PRIVATE char * author_name ARGS1 (char *,email)
+{
+    static char *name = NULL;
+    char *s, *e;
+    
+    StrAllocCopy(name, email);
+    if (TRACE)
+        fprintf(stderr,"Trying to find name in: %s\n",name);
+
+    if ((s = strchr(name, '(')) && (e = strchr(name, ')'))) {
+        if (e > s) {
+	    *e = '\0';			/* Chop off everything after the ')'  */
+	    return HTStrip(s+1);	/* Remove leading and trailing spaces */
+	}
+    }
+	
+    if ((s = strchr(name, '<')) && (e = strchr(name, '>'))) {
+        if (e > s) {
+	    strcpy(s, e+1);		/* Remove <...> */
+	    return HTStrip(name);	/* Remove leading and trailing spaces */
+	}
+    }
+	
+    return HTStrip(name);		/* Default to the whole thing */
+}
+
+/*      Find Author's mail address
+**      --------------------------
+**
+** On exit,
+**	Returns allocated string which cannot be freed by the
+**	calling function, and is reallocated on subsequent calls
+**	to this function.
+**
+** For example, returns "montulli@spaced.out.galaxy.net" if given any of
+**      " Lou Montulli <montulli@spaced.out.galaxy.net> "
+**  or  " montulli@spacedout.galaxy.net ( Lou "The Stud" Montulli ) "
+*/
+PRIVATE char * author_address ARGS1(char *,email)
+{
+    static char *address = NULL;
+    char *s, *at, *e;
+
+    StrAllocCopy(address, email);
+    if (TRACE)
+        fprintf(stderr,"Trying to find address in: %s\n",address);
+
+    if ((s = strchr(address, '<'))) {
+        if ((e = strchr(s, '>')) && (at = strchr(s, '@'))) {
+	    if (at < e) {
+                *e = '\0';               /* Remove > */
+                return HTStrip(s+1);  /* Remove leading and trailing spaces */
+	    }
+	}
+    }
+
+    if ((s = strchr(address, '(')) &&
+        (e = strchr(address, ')')) && (at = strchr(address, '@'))) {
+        if (e > s && at < e) {
+            *s = '\0';                  /* Chop off everything after the ')'  */
+            return HTStrip(address);    /* Remove leading and trailing spaces */
+        }
+    }
+
+    if ((at = strchr(address, '@')) && at > address) {
+        s = (at - 1);
+	e = (at + 1);
+        while (s > address && !isspace((unsigned char)*s))
+	    s--;
+	while (*e && !isspace((unsigned char)*e))
+	    e++;
+	*e = 0;
+	return HTStrip(s);
+    }
+
+    /* Default to the first word */
+    s = address;
+    while (isspace((unsigned char)*s))
+        s++; /* find first non-space */
+    e = s;
+    while (!isspace((unsigned char)*e) && *e != '\0')
+        e++; /* find next space or end */
+    *e = '\0'; /* terminate space */
+
+    return(s);
+}
+
+/*	Start anchor element
+**	--------------------
+*/
+PRIVATE void start_anchor ARGS1(CONST char *,  href)
+{
+    BOOL		present[HTML_A_ATTRIBUTES];
+    CONST char*		value[HTML_A_ATTRIBUTES];
+    
+    {
+    	int i;
+    	for(i=0; i < HTML_A_ATTRIBUTES; i++)
+	    present[i] = (i == HTML_A_HREF);
+    }
+    ((CONST char **)value)[HTML_A_HREF] = href;
+    (*targetClass.start_element)(target, HTML_A , present,
+    				 (CONST char **)value, 0);
+}
+
+/*      Start link element
+**      ------------------
+*/
+PRIVATE void start_link ARGS2(CONST char *,  href, CONST char *, rev)
+{
+    BOOL                present[HTML_LINK_ATTRIBUTES];
+    CONST char*         value[HTML_LINK_ATTRIBUTES];
+   
+    {
+        int i;
+        for(i=0; i < HTML_LINK_ATTRIBUTES; i++)
+            present[i] = (i == HTML_LINK_HREF || i == HTML_LINK_REV);
+    }
+    ((CONST char **)value)[HTML_LINK_HREF] = href;
+    ((CONST char **)value)[HTML_LINK_REV]  = rev;
+    (*targetClass.start_element)(target, HTML_LINK, present,
+				 (CONST char **)value, 0);
+}
+
+/*      Start list element
+**      ------------------
+*/
+PRIVATE void start_list ARGS1(int, seqnum)
+{
+    BOOL                present[HTML_OL_ATTRIBUTES];
+    CONST char*         value[HTML_OL_ATTRIBUTES];
+    char SeqNum[20];
+    int i;
+   
+    for (i=0; i < HTML_OL_ATTRIBUTES; i++)
+        present[i] = (i == HTML_OL_SEQNUM || i == HTML_OL_START);
+    sprintf(SeqNum, "%d", seqnum);
+    ((CONST char **)value)[HTML_OL_SEQNUM] = SeqNum;
+    ((CONST char **)value)[HTML_OL_START]  = SeqNum;
+    (*targetClass.start_element)(target, HTML_OL , present,
+				 (CONST char **)value, 0);
+}
+
+/*	Paste in an Anchor
+**	------------------
+**
+**
+** On entry,
+**	HT 	has a selection of zero length at the end.
+**	text 	points to the text to be put into the file, 0 terminated.
+**	addr	points to the hypertext refernce address,
+**		terminated by white space, comma, NULL or '>' 
+*/
+PRIVATE void write_anchor ARGS2(CONST char *,text, CONST char *,addr)
+{
+    char href[LINE_LENGTH+1];
+		
+    {
+    	CONST char * p;
+	strcpy(href, NewsHREF);
+	for (p = addr; *p && (*p != '>') && !WHITE(*p) && (*p!=','); p++)
+	    ;
+        strncat(href, addr, p-addr);	/* Make complete hypertext reference */
+    }
+    
+    start_anchor(href);
+    PUTS(text);
+    END(HTML_A);
+}
+
+/*	Write list of anchors
+**	---------------------
+**
+**	We take a pointer to a list of objects, and write out each,
+**	generating an anchor for each.
+**
+** On entry,
+**	HT 	has a selection of zero length at the end.
+**	text 	points to a comma or space separated list of addresses.
+** On exit,
+**	*text	is NOT any more chopped up into substrings.
+*/
+PRIVATE void write_anchors ARGS1 (char *,text)
+{
+    char * start = text;
+    char * end;
+    char c;
+    for (;;) {
+        for (; *start && (WHITE(*start)); start++)
+	    ;  /* Find start */
+	if (!*start)
+	    return;			/* (Done) */
+        for (end = start;
+	     *end && (*end != ' ') && (*end != ','); end++)
+	    ;/* Find end */
+	if (*end)
+	    end++;	/* Include comma or space but not NULL */
+	c = *end;
+	*end = '\0';
+	if (*start == '<')
+	    write_anchor(start, start+1);
+	else
+	    write_anchor(start, start);
+	START(HTML_BR);
+	*end = c;
+	start = end;			/* Point to next one */
+    }
+}
+
+/*	Abort the connection					abort_socket
+**	--------------------
+*/
+PRIVATE void abort_socket NOARGS
+{
+    if (TRACE)
+        fprintf(stderr,
+	        "HTNews: EOF on read, closing socket %d\n", s);
+    NETCLOSE(s);	/* End of file, close socket */
+    PUTS("Network Error: connection lost");
+    PUTC('\n');
+    s = -1;		/* End of file on response */
+    return;
+}
+
+/*	Read in an Article					read_article
+**	------------------
+**
+**
+**	Note the termination condition of a single dot on a line by itself.
+**	RFC 977 specifies that the line "folding" of RFC850 is not used, so we
+**	do not handle it here.
+**
+** On entry,
+**	s	Global socket number is OK
+**	HT	Global hypertext object is ready for appending text
+*/       
+PRIVATE void read_article NOARGS
+{
+
+    char line[LINE_LENGTH+1];
+    char *full_line = NULL;
+    char *subject=NULL;				/* Subject string	    */
+    char *from=NULL;				/* From string		    */
+    char *replyto=NULL;				/* Reply-to string	    */
+    char *date=NULL;				/* Date string		    */
+    char *organization=NULL;			/* Organization string	    */
+    char *references=NULL;			/* Hrefs for other articles */
+    char *newsgroups=NULL;			/* Newsgroups list	    */
+    char *followupto=NULL;			/* Followup list	    */
+    char *href=NULL;
+    char *p = line;
+    BOOL done = NO;
+
+    /*
+    **  Read in the HEADer of the article.
+    **
+    **  The header fields are either ignored, or formatted and put into the
+    **  Text.
+    */
+    if (!diagnostic) {
+	while (!done) {
+	    char ch = *p++ = NEXT_CHAR;
+	    if (ch == (char)EOF) {
+		abort_socket();	/* End of file, close socket */
+	    	return;		/* End of file on response */
+	    }
+	    if ((ch == LF) || (p == &line[LINE_LENGTH])) {
+		*--p = '\0';			/* Terminate the string */
+		if (TRACE)
+		    fprintf(stderr, "H %s\n", line);
+
+		if (line[0] == '\t' || line[0] == ' ') {
+		    int i = 0;
+		    while (line[i]) {
+		        if (line[i] == '\t')
+			    line[i] = ' ';
+			i++;
+		    }
+		    if (full_line == NULL) {
+		        StrAllocCopy(full_line, line);
+		    } else {
+		        StrAllocCat(full_line, line);
+		    }
+		} else {
+		    StrAllocCopy(full_line, line);
+		}
+
+		if (full_line[0] == '.') {	
+		    if (full_line[1] < ' ') {		/* End of article? */
+			done = YES;
+			break;
+		    }
+		} else if (full_line[0] < ' ') {
+		    break;		/* End of Header? */
+
+		} else if (match(full_line, "SUBJECT:")) {
+		    StrAllocCopy(subject, HTStrip(strchr(full_line,':')+1));
+		    if (HTCJK == JAPANESE) {
+		        HTmmdecode(subject, subject);
+			HTrjis(subject, subject);
+		    }
+
+		} else if (match(full_line, "DATE:")) {
+		    StrAllocCopy(date, HTStrip(strchr(full_line,':')+1));
+
+		} else if (match(full_line, "ORGANIZATION:")) {
+		    StrAllocCopy(organization, HTStrip(strchr(full_line,':')+1));
+		    if (HTCJK == JAPANESE) {
+		        HTmmdecode(organization, organization);
+			HTrjis(organization, organization);
+		    }
+
+		} else if (match(full_line, "FROM:")) {
+		    StrAllocCopy(from, HTStrip(strchr(full_line,':')+1));
+		    if (HTCJK == JAPANESE) {
+		        HTmmdecode(from, from);
+			HTrjis(from, from);
+		    }
+
+		} else if (match(full_line, "REPLY-TO:")) {
+		    StrAllocCopy(replyto, HTStrip(strchr(full_line,':')+1));
+		    if (HTCJK == JAPANESE) {
+		        HTmmdecode(replyto, replyto);
+			HTrjis(replyto, replyto);
+		    }
+
+		} else if (match(full_line, "NEWSGROUPS:")) {
+		    StrAllocCopy(newsgroups, HTStrip(strchr(full_line,':')+1));
+
+		} else if (match(full_line, "REFERENCES:")) {
+		    StrAllocCopy(references, HTStrip(strchr(full_line,':')+1));
+
+		} else if (match(full_line, "FOLLOWUP-TO:")) {
+		    StrAllocCopy(followupto, HTStrip(strchr(full_line,':')+1));
+
+		} /* end if match */
+		p = line;			/* Restart at beginning */
+	    } /* if end of line */
+	} /* Loop over characters */
+	FREE(full_line);
+
+	START(HTML_HEAD);
+	PUTC('\n');
+	START(HTML_TITLE);
+	if (subject && *subject != '\0')
+	    PUTS(subject);
+	else
+	    PUTS("No Subject");
+	END(HTML_TITLE);
+	PUTC('\n');
+	/* put in the owner as a link rel. */
+	if (from || replyto) {
+	    char *temp=NULL;
+	    StrAllocCopy(temp, replyto ? replyto : from);
+	    StrAllocCopy(href,"mailto:");
+	    StrAllocCat(href, author_address(temp));
+	    start_link(href, "made");
+	    PUTC('\n');
+	    FREE(temp);
+	}
+	END(HTML_HEAD);
+	PUTC('\n');
+
+	START(HTML_H1);
+	if (subject && *subject != '\0')
+	    PUTS(subject);
+	else
+	    PUTS("No Subject");
+	END(HTML_H1);
+	PUTC('\n');
+
+	if (subject)
+	    FREE(subject);
+
+	START(HTML_DLC);
+	PUTC('\n');
+
+	if (from || replyto) {
+	    START(HTML_DT);
+	    START(HTML_B);
+	    PUTS("From:");
+	    END(HTML_B);
+	    PUTC(' ');
+	    if (from)
+		PUTS(from);
+	    else
+		PUTS(from);
+	    PUTC('\n');
+
+	    if (!replyto)
+		StrAllocCopy(replyto, from);
+	    START(HTML_DT);
+	    START(HTML_B);
+	    PUTS("Reply to:");
+	    END(HTML_B);
+            PUTC(' ');
+	    start_anchor(href);
+	    if (*replyto != '<')
+    	        PUTS(author_name(replyto));
+	    else
+    	        PUTS(author_address(replyto));
+     	    END(HTML_A);
+	    START(HTML_BR);
+	    PUTC('\n');
+
+	    FREE(from);
+  	    FREE(replyto);
+	}
+
+	if (date) {
+	    START(HTML_DT);
+	    START(HTML_B);
+	    PUTS("Date:");
+	    END(HTML_B);
+            PUTC(' ');
+	    PUTS(date);
+	    PUTC('\n');
+	    FREE(date);
+	}
+
+	if (organization) {
+	    START(HTML_DT);
+	    START(HTML_B);
+	    PUTS("Organization:");
+	    END(HTML_B);
+            PUTC(' ');
+	    PUTS(organization);
+	    PUTC('\n');
+	    FREE(organization);
+	}
+
+	if (newsgroups && !strncmp(NewsHREF, "news:", 5)) {
+	    /* make posting possible */
+	    StrAllocCopy(href,"newsreply:");
+	    StrAllocCat(href, (followupto ? followupto : newsgroups));
+
+	    START(HTML_DT);
+	    START(HTML_B);
+	    PUTS("Newsgroups:");
+	    END(HTML_B);
+	    PUTC('\n');
+	    START(HTML_DD);
+	    write_anchors(newsgroups);
+	    PUTC('\n');
+
+	    START(HTML_DT);
+	    START(HTML_B);
+            PUTS("Followup to:");
+	    END(HTML_B);
+            PUTC(' ');
+            start_anchor(href);
+            PUTS("newsgroup(s)");
+            END(HTML_A);
+	    PUTC('\n');
+	}
+	FREE(newsgroups);
+	FREE(followupto);
+	    
+	if (references) {
+	    START(HTML_DT);
+	    START(HTML_B);
+	    PUTS("References:");
+	    END(HTML_B);
+	    PUTC('\n');
+	    START(HTML_DD);
+	    write_anchors(references);
+	    PUTC('\n');
+	    FREE(references);
+	}
+
+	END(HTML_DLC);
+	PUTC('\n');
+	FREE(href);
+    }
+
+    /*
+    **  Read in the BODY of the Article.
+    */
+    START(HTML_PRE);
+    PUTC('\n');
+
+    p = line;
+    while (!done) {
+	char ch = *p++ = NEXT_CHAR;
+	if (ch == (char)EOF) {
+	    abort_socket();	/* End of file, close socket */
+	    return;		/* End of file on response */
+	}
+	if ((ch == LF) || (p == &line[LINE_LENGTH])) {
+	    *p++ = '\0';			/* Terminate the string */
+	    if (TRACE)
+	        fprintf(stderr, "B %s", line);
+	    if (line[0] == '.') {
+		if (line[1] < ' ') {		/* End of article? */
+		    done = YES;
+		    break;
+		} else {			/* Line starts with dot */
+		    PUTS(&line[1]);	/* Ignore first dot */
+		}
+	    } else {
+
+	        if (diagnostic || !scan_for_buried_news_references) {
+
+/*	All lines are passed as unmodified source. - FM
+*/
+	            PUTS(line);
+
+	        } else {
+
+/*	Normal lines are scanned for buried references to other articles.
+**	Unfortunately, it could pick up mail addresses as well!  It also
+**	can corrupt uuencoded messages!  So we don't do this when fetching
+**	articles as WWW_SOURCE or when downloading (diagnostic is TRUE) or
+**	if the client has set scan_for_buried_news_references to FALSE.
+**	Otherwise, we convert all "<...@...>" strings preceded by "rticle "
+**	to "news:...@..." links, and any strings that look like URLs to
+**	links. - FM
+*/
+		    char *l = line;
+		    char *p;
+
+		    while (p=strstr(l, "rticle <")) {
+		        char *q  = strchr(p,'>');
+		        char *at = strchr(p, '@');
+		        if (q && at && at<q) {
+		            char c = q[1];
+			    q[1] = 0;		/* chop up */
+			    p += 7;
+			    *p = 0;
+			    while (*l) {
+			        if (strncmp (l, "news:", 5) &&
+				    strncmp (l, "snews://", 8) &&
+				    strncmp (l, "nntp://", 7) &&
+				    strncmp (l, "ftp://", 6) &&
+				    strncmp (l, "file:/", 6) &&
+				    strncmp (l, "finger://", 9) &&
+				    strncmp (l, "http://", 7) &&
+				    strncmp (l, "https://", 8) &&
+				    strncmp (l, "wais://", 7) &&
+				    strncmp (l, "mailto:", 7) &&
+				    strncmp (l, "cso://", 6) &&
+				    strncmp (l, "gopher://", 9)) 
+				    PUTC (*l++);
+				else {
+				    StrAllocCopy(href, l);
+				    start_anchor(strtok(href, " \r\n\t,>)\""));
+				    while (*l && !strchr(" \r\n\t,>)\"", *l))
+				        PUTC(*l++);
+				    END(HTML_A);
+				    FREE(href);
+				}
+			    }
+			    *p = '<'; 		/* again */
+			    *q = 0;
+			    start_anchor(p+1);
+			    *q = '>'; 		/* again */
+			    PUTS(p);
+			    END(HTML_A);
+			    q[1] = c;		/* again */
+			    l=q+1;
+		        } else {
+			    break;		/* line has unmatched <> */
+			}
+		    }
+		    while (*l) {		/* Last bit of the line */
+			if (strncmp (l, "news:", 5) &&
+			    strncmp (l, "snews://", 8) &&
+			    strncmp (l, "nntp://", 7) &&
+			    strncmp (l, "ftp://", 6) &&
+			    strncmp (l, "file:/", 6) &&
+			    strncmp (l, "finger://", 9) &&
+			    strncmp (l, "http://", 7) &&
+			    strncmp (l, "https://", 8) &&
+			    strncmp (l, "wais://", 7) &&
+			    strncmp (l, "mailto:", 7) &&
+			    strncmp (l, "cso://", 6) &&
+			    strncmp (l, "gopher://", 9)) 
+			    PUTC (*l++);
+			else {
+			    StrAllocCopy(href, l);
+			    start_anchor(strtok(href, " \r\n\t,>)\""));
+			    while (*l && !strchr(" \r\n\t,>)\"", *l))
+			        PUTC(*l++);
+			    END(HTML_A);
+			    FREE(href);
+			}
+		    }
+	        } /* if diagnostic or not scan_for_buried_news_references */
+	    } /* if not dot */
+	    p = line;				/* Restart at beginning */
+	} /* if end of line */
+    } /* Loop over characters */
+    
+    END(HTML_PRE);
+    PUTC('\n');
+}
+
+/*	Read in a List of Newsgroups
+**	----------------------------
+**
+**	Note the termination condition of a single dot on a line by itself.
+**	RFC 977 specifies that the line "folding" of RFC850 is not used, so we
+**	do not handle it here.
+*/        
+PRIVATE void read_list ARGS1(char *, arg)
+{
+
+    char line[LINE_LENGTH+1];
+    char *p;
+    BOOL done = NO;
+    BOOL head = NO;
+    BOOL tail = NO;
+    int listing = 0;
+    char *pattern = NULL;
+    int len = 0;
+    
+    /*
+     * Support head or tail matches for groups to list. - FM
+     */
+    if (arg && strlen(arg) > 1) {
+        if (*arg == '*') {
+            tail = YES;
+	    StrAllocCopy(pattern, (arg+1));
+        } else if (arg[strlen(arg)-1] == '*') {
+            head = YES;
+	    StrAllocCopy(pattern, arg);
+	    pattern[strlen(pattern)-1] = '\0';
+        }
+        if (tail || head) {
+           len = strlen(pattern);
+	}
+
+    }
+
+    /*
+    **  Read in the HEADer of the article.
+    **
+    **  The header fields are either ignored, or formatted and put into the
+    **  Text.
+    */
+    START(HTML_HEAD);
+    PUTC('\n');
+    START(HTML_TITLE);
+    PUTS("Newsgroups");
+    END(HTML_TITLE);
+    PUTC('\n');
+    END(HTML_HEAD);
+    PUTC('\n');
+    START(HTML_H1);
+    PUTS( "Newsgroups");
+    END(HTML_H1);
+    PUTC('\n');
+    p = line;
+    START(HTML_DLC);
+    PUTC('\n');
+    while (!done) {
+	char ch = *p++ = NEXT_CHAR;
+	if (ch == (char)EOF) {
+	    abort_socket();	/* End of file, close socket */
+	    FREE(pattern);
+	    return;		/* End of file on response */
+	}
+	if ((ch == LF) || (p == &line[LINE_LENGTH])) {
+	    *p++ = '\0';			/* Terminate the string */
+	    if (TRACE)
+	        fprintf(stderr, "B %s", line);
+	    if (line[0] == '.') {
+		if (line[1] < ' ') {		/* End of article? */
+		    done = YES;
+		    break;
+		} else {			/* Line starts with dot */
+		    START(HTML_DT);
+		    PUTS(&line[1]);
+		}
+	    } else {
+		/*
+		**  Normal lines are scanned for references to newsgroups.
+		*/
+		int i = 0;
+
+		/* find whitespace if it exits */
+		for (; line[i] != '\0' && !WHITE(line[i]); i++)
+		    ;  /* null body */
+	
+		if (line[i] != '\0') {
+		    line[i] = '\0';
+		    if ((head && strncasecomp(line, pattern, len)) ||
+		        (tail && (i < len ||
+				  strcasecomp((line + (i - len)), pattern)))) {
+		        p = line;	/* Restart at beginning */
+			continue;
+		    }
+		    START(HTML_DT);
+		    write_anchor(line, line);
+		    listing++;
+		    PUTC('\n');
+    	            START(HTML_DD);
+		    PUTS(&line[i+1]); /* put description */
+		} else {
+		    if ((head && strncasecomp(line, pattern, len)) ||
+		        (tail && (i < len ||
+				  strcasecomp((line + (i - len)), pattern)))) {
+		        p = line;	/* Restart at beginning */
+			continue;
+		    }
+		    START(HTML_DT);
+		    write_anchor(line, line);
+		    listing++;
+		}
+	    } /* if not dot */
+	    p = line;			/* Restart at beginning */
+	} /* if end of line */
+    } /* Loop over characters */
+    if (!listing) {
+        START(HTML_DT);
+	sprintf(line, "No matches for: %s", arg);
+	PUTS(line);
+    }
+    END(HTML_DLC);
+    PUTC('\n');
+    FREE(pattern);
+    return;
+}
+
+/*	Read in a Newsgroup
+**	-------------------
+**	Unfortunately, we have to ask for each article one by one if we
+**	want more than one field.
+**
+*/
+PRIVATE void read_group ARGS3(
+  CONST char *,groupName,
+  int,first_required,
+  int,last_required)
+{
+    char line[LINE_LENGTH+1];
+    char author[LINE_LENGTH+1];
+    char subject[LINE_LENGTH+1];
+    char *date = NULL;
+    int i;
+    char *p;
+    BOOL done;
+
+    char buffer[LINE_LENGTH];
+    char *reference = NULL;		/* Href for article */
+    int art;				/* Article number WITHIN GROUP */
+    int status, count, first, last;	/* Response fields */
+					/* count is only an upper limit */
+
+    author[0] = '\0';
+    START(HTML_HEAD);
+    PUTC('\n');
+    START(HTML_TITLE);
+    PUTS("Newsgroup ");
+    PUTS(groupName);
+    END(HTML_TITLE);
+    PUTC('\n');
+    END(HTML_HEAD);
+    PUTC('\n');
+
+    sscanf(response_text, " %d %d %d %d", &status, &count, &first, &last);
+    if (TRACE)
+        fprintf(stderr,
+    		"Newsgroup status=%d, count=%d, (%d-%d) required:(%d-%d)\n",
+		status, count, first, last, first_required, last_required);
+    if (last ==0 ) {
+        PUTS("\nNo articles in this group.\n");
+	goto add_post;
+    }
+    
+#define FAST_THRESHOLD 100	/* Above this, read IDs fast */
+#define CHOP_THRESHOLD 50	/* Above this, chop off the rest */
+
+    if (first_required < first)
+        first_required = first;		/* clip */
+    if ((last_required == 0) || (last_required > last))
+        last_required = last;
+    
+    if (last_required < first_required) {
+        PUTS("\nNo articles in this range.\n");
+	goto add_post;
+    }
+
+    if (last_required-first_required+1 > HTNewsMaxChunk) { /* Trim this block */
+        first_required = last_required-HTNewsChunkSize+1;
+    }
+    if (TRACE)
+        fprintf(stderr, "    Chunk will be (%d-%d)\n",
+    		        first_required, last_required);
+
+    /*
+    **  Set window title.
+    */
+    sprintf(buffer, "%s,  Articles %d-%d",
+    		    groupName, first_required, last_required);
+    START(HTML_H1);
+    PUTS(buffer);
+    END(HTML_H1);
+    PUTC('\n');
+
+    /*
+    **  Link to earlier articles.
+    */
+    if (first_required > first) {
+    	int before;			/* Start of one before */
+	if (first_required-HTNewsMaxChunk <= first)
+	    before = first;
+	else
+	    before = first_required-HTNewsChunkSize;
+    	sprintf(buffer, "%s/%d-%d", groupName, before, first_required-1);
+	if (TRACE)
+	    fprintf(stderr, "    Block before is %s\n", buffer);
+	PUTC('(');
+	start_anchor(buffer);
+	PUTS("Earlier articles");
+	END(HTML_A);
+	PUTS("...)\n");
+	START(HTML_P);
+	PUTC('\n');
+    }
+    
+    done = NO;
+
+/*#define USE_XHDR*/
+#ifdef USE_XHDR
+    if (count > FAST_THRESHOLD)  {
+        sprintf(buffer,
+ "\nThere are about %d articles currently available in %s, IDs as follows:\n\n",
+		count, groupName); 
+        PUTS(buffer);
+        sprintf(buffer, "XHDR Message-ID %d-%d%c%c", first, last, CR, LF);
+	status = response(buffer);
+	if (status == 221) {
+
+	    p = line;
+	    while (!done) {
+		char ch = *p++ = NEXT_CHAR;
+		if (ch == (char)EOF) {
+		    abort_socket();	/* End of file, close socket */
+		    return;		/* End of file on response */
+		}
+		if ((ch == '\n') || (p == &line[LINE_LENGTH])) {
+		    *p++ = '\0';		/* Terminate the string */
+		    if (TRACE)
+		        fprintf(stderr, "X %s", line);
+		    if (line[0] == '.') {
+			if (line[1] < ' ') {	/* End of article? */
+			    done = YES;
+			    break;
+			} else {		/* Line starts with dot */
+			    	/* Ignore strange line */
+			}
+		    } else {
+	
+	/*	Normal lines are scanned for references to articles.
+	*/
+			char * space = strchr(line, ' ');
+			if (space++)
+			    write_anchor(space, space);
+		    } /* if not dot */
+		    p = line;			/* Restart at beginning */
+		} /* if end of line */
+	    } /* Loop over characters */
+
+	    /* leaving loop with "done" set */
+	} /* Good status */
+    }
+#endif /* USE_XHDR */
+
+    /*
+    **  Read newsgroup using individual fields.
+    */
+    if (!done) {
+        START(HTML_B);
+        if (first == first_required && last == last_required)
+	    PUTS("All available articles in ");
+        else
+	    PUTS("Articles in ");
+	PUTS(groupName);
+	END(HTML_B);
+	PUTC('\n');
+	if (LYListNewsNumbers)
+	    start_list(first_required);
+	else
+	    START(HTML_UL);
+	for (art = first_required; art <= last_required; art++) {
+    
+/*#define OVERLAP*/
+#ifdef OVERLAP
+/* With this code we try to keep the server running flat out by queuing just
+** one extra command ahead of time. We assume (1) that the server won't abort
+** if it gets input during output, and (2) that TCP buffering is enough for the
+** two commands. Both these assumptions seem very reasonable. However, we HAVE
+** had a hangup with a loaded server.
+*/
+	    if (art == first_required) {
+		if (art == last_required) {		/* Only one */
+			sprintf(buffer,
+				"HEAD %d%c%c", art, CR, LF);
+			status = response(buffer);
+		    } else {				/* First of many */
+			sprintf(buffer, "HEAD %d%c%cHEAD %d%c%c",
+				art, CR, LF, art+1, CR, LF);
+			status = response(buffer);
+		    }
+	    } else if (art == last_required) {		/* Last of many */
+		    status = response(NULL);
+	    } else {					/* Middle of many */
+		    sprintf(buffer, "HEAD %d%c%c", art+1, CR, LF);
+		    status = response(buffer);
+	    }
+	    
+#else	/* NOT OVERLAP: */
+	    sprintf(buffer, "HEAD %d%c%c", art, CR, LF);
+	    status = response(buffer);
+#endif	/* OVERLAP */
+
+	    if (status == 221) {	/* Head follows - parse it:*/
+    
+		p = line;				/* Write pointer */
+		done = NO;
+		while( !done ) {
+		    char ch = *p++ = NEXT_CHAR;
+		    if (ch == (char)EOF) {
+			abort_socket();	/* End of file, close socket */
+			return;		/* End of file on response */
+		    }
+		    if ((ch == LF) ||
+		        (p == &line[LINE_LENGTH])) {
+		    
+			*--p = '\0';		/* Terminate  & chop LF*/
+			p = line;		/* Restart at beginning */
+			if (TRACE)
+			    fprintf(stderr, "G %s\n", line);
+			switch(line[0]) {
+    
+			case '.':
+			    done = (line[1] < ' ');	/* End of article? */
+			    break;
+    
+			case 'S':
+			case 's':
+			    if (match(line, "SUBJECT:"))
+				strcpy(subject, line+9);/* Save subject */
+			    break;
+    
+			case 'M':
+			case 'm':
+			    if (match(line, "MESSAGE-ID:")) {
+				char * addr = HTStrip(line+11) +1; /* Chop < */
+				addr[strlen(addr)-1] = '\0';	   /* Chop > */
+				StrAllocCopy(reference, addr);
+			    }
+			    break;
+    
+			case 'f':
+			case 'F':
+			    if (match(line, "FROM:")) {
+				char * p;
+				strcpy(author,
+					author_name(strchr(line,':')+1));
+				p = author + strlen(author) - 1;
+				if (*p==LF)
+				    *p = '\0';	/* Chop off newline */
+			    }
+			    break;
+				    
+			case 'd':
+			case 'D':
+			    if (LYListNewsDates && match(line, "DATE:")) {
+			        StrAllocCopy(date,
+					     HTStrip(strchr(line,':')+1));
+			    }
+			    break;
+				    
+			} /* end switch on first character */
+		    } /* if end of line */
+		} /* Loop over characters */
+
+		PUTC('\n');
+		START(HTML_LI);
+		sprintf(buffer, "\"%s\"", subject);
+		if (reference) {
+		    write_anchor(buffer, reference);
+		    FREE(reference);
+		} else {
+		    PUTS(buffer);
+		}
+		if (author[0] != '\0') {
+		     PUTS(" - ");
+		     if (LYListNewsDates)
+		         START(HTML_I);
+		     PUTS(author);
+		     if (LYListNewsDates)
+		         END(HTML_I);
+		     author[0] = '\0';
+		}
+		if (date) {
+		    if (!diagnostic) {
+		        for (i = 0; date[i]; i++) {
+			    if (date[i] == ' ') {
+			        date[i] = HT_NON_BREAK_SPACE;
+			    }
+			}
+		    }
+		    sprintf(buffer, " [%s]", date);
+		    PUTS(buffer);
+		    FREE(date);
+		}
+		
+    
+/*	 indicate progress!   @@@@@@
+*/
+    
+	    } /* If good response */
+	} /* Loop over article */	    
+    } /* If read headers */
+    PUTC('\n');
+    if (LYListNewsNumbers)
+        END(HTML_OL);
+    else
+        END(HTML_UL);
+    PUTC('\n');
+    
+    /*
+    **  Link to later articles.
+    */
+    if (last_required < last) {
+    	int after;			/* End of article after */
+	after = last_required+HTNewsChunkSize;
+    	if (after == last)
+	    sprintf(buffer, "%s%s", NewsHREF, groupName); /* original group */
+    	else
+	    sprintf(buffer, "%s%s/%d-%d", NewsHREF, groupName,
+	    				  last_required+1, after);
+	if (TRACE)
+	    fprintf(stderr, "    Block after is %s\n", buffer);
+	PUTC('(');
+	start_anchor(buffer);
+	PUTS("Later articles");
+	END(HTML_A);
+	PUTS("...)\n");
+    }
+
+add_post:
+    if (!strncmp(NewsHREF, "news:", 5)) {
+	char *href = NULL;
+	START(HTML_HR);
+	PUTC('\n');
+	StrAllocCopy(href,"newspost:");
+	StrAllocCat(href,groupName);
+	start_anchor(href);
+	PUTS("Post to ");
+	PUTS(groupName);
+	END(HTML_A);
+	FREE(href);
+    } else {
+	START(HTML_HR);
+    }
+    PUTC('\n');
+}
+
+/*		Load by name					HTLoadNews
+**		============
+*/
+PUBLIC int HTLoadNews ARGS4(
+	CONST char *,		arg,
+	HTParentAnchor *,	anAnchor,
+	HTFormat,		format_out,
+	HTStream*,		stream)
+{
+    char command[260];			/* The whole command */
+    char groupName[GROUP_NAME_LENGTH];	/* Just the group name */
+    int status;				/* tcp return */
+    int retries;			/* A count of how hard we have tried */ 
+    BOOL group_wanted;			/* Flag: group was asked for, not article */
+    BOOL list_wanted;			/* Flag: group was asked for, not article */
+    int first, last;			/* First and last articles asked for */
+    char *cp;
+    char *ListArg = NULL;
+
+    diagnostic = (format_out == WWW_SOURCE ||	/* set global flag */
+    		  format_out == HTAtom_for("www/download") ||
+		  format_out == HTAtom_for("www/dump"));
+    
+    if (TRACE) fprintf(stderr, "HTNews: Looking for %s\n", arg);
+    
+    if (!initialized)
+	initialized = initialize();
+    if (!initialized)
+	return -1;	/* FAIL */
+    
+    FREE(NewsHREF);
+
+    {
+        CONST char * p1 = arg;
+
+    /*
+    **  We will ask for the document, omitting the host name & anchor.
+    **
+    **  Syntax of address is
+    **  	xxx@yyy			Article
+    **  	<xxx@yyy>		Same article
+    **  	xxxxx			News group (no "@")
+    **  	group/n1-n2		Articles n1 to n2 in group
+    */
+	group_wanted = (strchr(arg, '@')==0) && (strchr(arg, '*')==0);
+	list_wanted  = (strchr(arg, '@')==0) && (strchr(arg, '*')!=0);
+
+	/* p1 = HTParse(arg, "", PARSE_PATH | PARSE_PUNCTUATION); */
+	/*
+	 *  Don't use HTParse because news: access doesn't follow traditional
+	 *  rules. For instance, if the article reference contains a '#',
+	 *  the rest of it is lost -- JFG 10/7/92, from a bug report
+	 */
+ 	if (!strncasecomp (arg, "nntp://", 7)) {
+	  if (!(cp = strchr(arg+7, '/')) || *(cp+1) == '\0') {
+	      p1 = "*";
+	      group_wanted = FALSE;
+	      list_wanted = TRUE;
+	  } else {
+	      p1 = (cp+1);
+	  }
+	  if (!(cp = HTParse(arg, "", PARSE_HOST)) || *cp == '\0') {
+	      if (s >= 0 && NewsHost && strcasecomp(NewsHost, HTNewsHost)) {
+	          NETCLOSE(s);
+		  s = -1;
+	      }
+	      StrAllocCopy(NewsHost, HTNewsHost);
+	  } else {
+	      if (s >= 0 && NewsHost && strcasecomp(NewsHost, cp)) {
+	          NETCLOSE(s);
+		  s = -1;
+	      }
+	      StrAllocCopy(NewsHost, cp);
+	  }
+	  FREE(cp);
+	  sprintf(command, "nntp://%s/", NewsHost);
+	  StrAllocCopy(NewsHREF, command);
+	}
+	else if (!strncasecomp(arg, "snews:", 6)) {
+	  HTAlert("This client does not contain support for SNEWS URLs.");
+	  return HT_NO_DATA;
+	}
+ 	else if (!strncasecomp (arg, "news://", 7)) {
+	  if (!(cp = strchr(arg+7, '/')) || *(cp+1) == '\0') {
+	      p1 = "*";
+	      group_wanted = FALSE;
+	      list_wanted = TRUE;
+	  } else {
+	      p1 = (cp+1);
+	  }
+	  if (!(cp = HTParse(arg, "", PARSE_HOST)) || *cp == '\0') {
+	      if (s >= 0 && NewsHost && strcasecomp(NewsHost, HTNewsHost)) {
+	          NETCLOSE(s);
+		  s = -1;
+	      }
+	      StrAllocCopy(NewsHost, HTNewsHost);
+	  } else {
+	      if (s >= 0 && NewsHost && strcasecomp(NewsHost, cp)) {
+	          NETCLOSE(s);
+		  s = -1;
+	      }
+	      StrAllocCopy(NewsHost, cp);
+	  }
+	  FREE(cp);
+	  sprintf(command, "news://%s/", NewsHost);
+	  StrAllocCopy(NewsHREF, command);
+	}
+	else {
+	  p1 = arg + 5;  /* Skip "news:" prefix */
+	  if (s >= 0 && NewsHost && strcasecomp(NewsHost, HTNewsHost)) {
+	      NETCLOSE(s);
+	      s = -1;
+	  }
+	  StrAllocCopy(NewsHost, HTNewsHost);
+	  StrAllocCopy(NewsHREF, "news:");
+	}
+	if (list_wanted) {
+	    strcpy(command, "LIST NEWSGROUPS");
+	} else if (group_wanted) {
+	    char * slash = strchr(p1, '/');
+	    strcpy(command, "GROUP ");
+	    first = 0;
+	    last = 0;
+	    if (slash) {
+		*slash = '\0';
+		strcpy(groupName, p1);
+		*slash = '/';
+		(void)sscanf(slash+1, "%d-%d", &first, &last);
+		if ((first > 0) && (isdigit(*(slash+1))) &&
+		    (strchr(slash+1, '-') == NULL || first == last)) {
+		    /*
+		     *  We got a number greater than 0, which will be
+		     *  loaded as first, and either no range or the
+		     *  range computes to zero, so make last negative,
+		     *  as a flag to select the group and then fetch
+		     *  an article by number (first) instead of by
+		     *  messageID. - FM
+		     */
+		     last = -1;
+		}
+	    } else {
+		strcpy(groupName, p1);
+	    }
+	    strcat(command, groupName);
+	} else {
+	    strcpy(command, "ARTICLE ");
+	    if (strchr(p1, '<') == 0)
+	        strcat(command,"<");
+	    strcat(command, p1);
+	    if (strchr(p1, '>') == 0)
+	        strcat(command,">");
+	}
+
+        {
+	    char * p = command + strlen(command);
+	    *p++ = CR;		/* Macros to be correct on Mac */
+	    *p++ = LF;
+	    *p++ = 0;
+	    /* strcat(command, "\r\n");	*/	/* CR LF, as in rfc 977 */
+	}
+	StrAllocCopy(ListArg, p1);
+    } /* scope of p1 */
+    
+    if (!*arg) {
+        FREE(NewsHREF);
+	FREE(ListArg);
+        return NO;			/* Ignore if no name */
+    }
+
+    /*
+     *  Make a hypertext object with an anchor list.
+     */
+    node_anchor = anAnchor;
+    target = HTML_new(anAnchor, format_out, stream);
+    targetClass = *target->isa;	/* Copy routine entry points */
+
+    /*
+     *  Now, let's get a stream setup up from the NewsHost.
+     */       
+    for (retries = 0; retries < 2; retries++) {
+        if (s < 0) {
+	    /* CONNECTING to news host */
+            char url[1024];
+	    if (!strncmp(arg, "news:", 5))
+                sprintf (url, "lose://%s/", NewsHost);
+	    else
+                strcpy (url, arg);
+            if (TRACE)
+                fprintf (stderr, "News: doing HTDoConnect on '%s'\n", url);
+
+            _HTProgress("Connecting to NewsHost ...");
+
+	    status = HTDoConnect (url, "NNTP", NEWS_PORT, &s);
+            if (status == HT_INTERRUPTED) {
+                /*
+		 *  Interrupt cleanly.
+		 */
+		if (TRACE)
+                    fprintf(stderr,
+                         "News: Interrupted on connect; recovering cleanly.\n");
+                _HTProgress("Connection interrupted.");
+
+		(*targetClass._abort)(target, NULL);
+  
+		FREE(NewsHost);
+		FREE(NewsHREF);
+		FREE(ListArg);
+                return HT_INTERRUPTED;
+            }
+	    if (status < 0) {
+		char message[256];
+	        NETCLOSE(s);
+		s = -1;
+		if (TRACE)
+		    fprintf(stderr,
+		    	    "HTNews: Unable to connect to news host.\n");
+		if (retries < 1)
+		    continue;
+		sprintf(message, "Could not access %s.", NewsHost);
+		FREE(NewsHost);
+		FREE(NewsHREF);
+		FREE(ListArg);
+		return HTLoadError(stream, 500, message);
+	    } else {
+		if (TRACE)
+		    fprintf(stderr, "HTNews: Connected to news host %s.\n",
+				    NewsHost);
+		HTInitInput(s);		/* set up buffering */
+		if ((response(NULL) / 100) != 2) {
+			char message[BIG];
+			NETCLOSE(s);
+			s = -1;
+			if (retries < 1)
+			    continue;
+			sprintf(message, 
+		  "Can't read news info. News host %.20s responded: %.200s",
+		  		NewsHost, response_text);
+			FREE(NewsHost);
+			FREE(NewsHREF);
+			FREE(ListArg);
+		        return HTLoadError(stream, 500, message);
+		}
+	    }
+	} /* If needed opening */
+	
+        /*
+	 *  Ensure reader mode, but don't bother checking the
+	 *  status for anything but HT_INERRUPTED, because if
+	 *  if the reader mode command is not needed, the server
+	 *  probably return a 500, which is irrelevant at this
+	 *  point. - FM
+	 */
+	{
+	    char buffer[20];
+	    sprintf(buffer, "mode reader%c%c", CR, LF);
+	    if ((status = response(buffer)) == HT_INTERRUPTED) {
+                _HTProgress("Connection interrupted.");
+		break;
+	    }
+	}
+
+Send_NNTP_command:
+	if ((status = response(command)) == HT_INTERRUPTED) {
+	    _HTProgress("Connection interrupted.");
+	    break;
+	}
+	if (status < 0) {
+	    if (retries < 1) {
+	        continue;
+	    } else {
+	        break;
+	    }
+	}
+	if ((status/100) != 2) {
+	    if (retries)
+	        HTAlert(response_text);
+	    else
+	        _HTProgress(response_text);
+	    NETCLOSE(s);
+	    s = -1;
+	    /*
+	     *  Message might be a leftover "Timeout-disconnected",
+	     *  so try again if retries is not exhausted.
+	     */
+	    continue;
+	}
+  
+	/*
+	 *  Load a group, article, etc
+	 */
+	if (list_wanted) {
+	    _HTProgress("Reading list of available newsgroups.");
+	    read_list(ListArg);
+	} else if (group_wanted) {
+	    if (last < 0) {
+	        /*
+		 *  We got one article number rather than a range
+		 *  following the slash which followed the group
+		 *  name, or the range was zero, so now that we
+		 *  have selected that group, load ARTICLE and the
+		 *  the number (first) as the command and go back
+		 *  to send it and check the response. - FM
+		 */
+		sprintf(command, "ARTICLE %d%c%c", first, CR, LF);
+		group_wanted = FALSE;
+		retries = 2;
+		goto Send_NNTP_command;
+	    }
+	    _HTProgress("Reading list of articles in newsgroup.");
+	    read_group(groupName, first, last);
+        } else {
+	    _HTProgress("Reading news article.");
+	    read_article();
+	}
+
+	(*targetClass._free)(target);
+	FREE(NewsHREF);
+	FREE(ListArg);
+	return HT_LOADED;
+	
+    } /* Retry loop */
+    
+    
+    /* HTAlert("Sorry, could not load requested news."); */
+        
+/*    NXRunAlertPanel(NULL, "Sorry, could not load `%s'.",
+	    NULL,NULL,NULL, arg);No -- message earlier wil have covered it */
+
+    (*targetClass._abort)(target, NULL);
+    FREE(NewsHREF);
+    FREE(ListArg);
+    return HT_NO_DATA;
+}
+
+#ifdef GLOBALDEF_IS_MACRO
+#define _HTNEWS_C_1_INIT { "news", HTLoadNews, NULL }
+GLOBALDEF (HTProtocol,HTNews,_HTNEWS_C_1_INIT);
+#define _HTNEWS_C_2_INIT { "nntp", HTLoadNews, NULL }
+GLOBALDEF (HTProtocol,HTNNTP,_HTNEWS_C_2_INIT);
+#define _HTNEWS_C_3_INIT { "snews", HTLoadNews, NULL }
+GLOBALDEF (HTProtocol,HTSNews,_HTNEWS_C_1_INIT);
+#else
+GLOBALDEF PUBLIC HTProtocol HTNews = { "news", HTLoadNews, NULL };
+GLOBALDEF PUBLIC HTProtocol HTNNTP = { "nntp", HTLoadNews, NULL };
+GLOBALDEF PUBLIC HTProtocol HTSNews = { "snews", HTLoadNews, NULL };
+#endif /* GLOBALDEF_IS_MACRO */
diff --git a/WWW/Library/Implementation/HTNews.h b/WWW/Library/Implementation/HTNews.h
new file mode 100644
index 00000000..4bc998af
--- /dev/null
+++ b/WWW/Library/Implementation/HTNews.h
@@ -0,0 +1,35 @@
+/*                                  Network News Transfer protocol module for the WWW library
+                                          HTNEWS
+                                             
+ */
+/* History:
+**      26 Sep 90       Written TBL in Objective-C
+**      29 Nov 91       Downgraded to C, for portable implementation.
+*/
+
+#ifndef HTNEWS_H
+#define HTNEWS_H
+
+#include "HTAccess.h"
+#include "HTAnchor.h"
+
+#ifdef GLOBALREF_IS_MACRO
+extern GLOBALREF(HTProtocol, HTNews);
+extern GLOBALREF(HTProtocol, HTNNTP);
+extern GLOBALREF(HTProtocol, HTSNews);
+#else
+GLOBALREF HTProtocol HTNews;
+GLOBALREF HTProtocol HTNNTP;
+GLOBALREF HTProtocol HTSNews;
+#endif /* GLOBALREF_IS_MACRO */
+
+extern void HTSetNewsHost PARAMS((CONST char *value));
+extern CONST char * HTGetNewsHost NOPARAMS;
+extern char * HTNewsHost;
+
+#endif /* HTNEWS_H */
+
+
+/*
+
+   tbl */
diff --git a/WWW/Library/Implementation/HTParse.c b/WWW/Library/Implementation/HTParse.c
new file mode 100644
index 00000000..463488f7
--- /dev/null
+++ b/WWW/Library/Implementation/HTParse.c
@@ -0,0 +1,606 @@
+/*		Parse HyperText Document Address		HTParse.c
+**		================================
+*/
+
+#include "HTUtils.h"
+#include "tcp.h"
+#include "HTParse.h"
+
+#include "LYLeaks.h"
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+#define HEX_ESCAPE '%'
+
+struct struct_parts {
+	char * access;
+	char * host;
+	char * absolute;
+	char * relative;
+/*	char * search;		no - treated as part of path */
+	char * anchor;
+};
+
+
+/*	Strip white space off a string
+**	------------------------------
+**
+** On exit,
+**	Return value points to first non-white character, or to 0 if none.
+**	All trailing white space is OVERWRITTEN with zero.
+*/
+
+PUBLIC char * HTStrip ARGS1(
+	char *,		s)
+{
+#define SPACE(c) ((c==' ')||(c=='\t')||(c=='\n')) 
+    char * p = s;
+    for (p = s; *p; p++)
+        ;		        /* Find end of string */
+    for (p--; p >= s; p--) {
+    	if (SPACE(*p))
+	    *p = '\0';		/* Zap trailing blanks */
+	else
+	    break;
+    }
+    while (SPACE(*s))
+        s++;			/* Strip leading blanks */
+    return s;
+}
+
+
+/*	Scan a filename for its consituents
+**	-----------------------------------
+**
+** On entry,
+**	name	points to a document name which may be incomplete.
+** On exit,
+**      absolute or relative may be nonzero (but not both).
+**	host, anchor and access may be nonzero if they were specified.
+**	Any which are nonzero point to zero terminated strings.
+*/
+PRIVATE void scan ARGS2(
+	char *,			name,
+	struct struct_parts *,	parts)
+{
+    char * after_access;
+    char * p;
+    int length = strlen(name);
+    
+    parts->access = NULL;
+    parts->host = NULL;
+    parts->absolute = NULL;
+    parts->relative = NULL;
+    parts->anchor = NULL;
+    
+    after_access = name;
+    for (p = name; *p; p++) {
+	if (*p==':') {
+	    *p = '\0';
+	    parts->access = name;	/* Access name has been specified */
+	    after_access = p+1;
+	    break;
+	}
+	if (*p=='/')
+	    break;
+	if (*p=='#')
+	    break;
+    }
+    
+    for (p = (name+length-1); p >= name; p--) {
+	if (*p =='#') {
+	    parts->anchor = p + 1;
+	    *p = '\0';			/* terminate the rest */
+	}
+    }
+    p = after_access;
+    if (*p == '/') {
+	if (p[1] == '/') {
+	    parts->host = p+2;		 /* host has been specified 	*/
+	    *p = '\0';			 /* Terminate access 		*/
+	    p = strchr(parts->host,'/'); /* look for end of host name if any */
+	    if (p) {
+	        *p = '\0';			/* Terminate host */
+	        parts->absolute = p+1;		/* Root has been found */
+	    }
+	} else {
+	    parts->absolute = p+1;		/* Root found but no host */
+	}	    
+    } else {
+        parts->relative = (*after_access) ? after_access : 0;	/* zero for "" */
+    }
+
+    if (parts->access && parts->anchor) {
+        if ((!parts->host && strcasecomp(parts->access, "lynxcgi")) ||
+	    !strcasecomp(parts->access, "nntp") ||
+	    !strcasecomp(parts->access, "snews") ||
+	    !strcasecomp(parts->access, "news") ||
+	    !strcasecomp(parts->access, "data")) {
+	    /* 
+	     *  Access specified but no host and not a lynxcgi URL, so the
+	     *  anchor may not really be one, e.g., news:j462#36487@foo.bar,
+	     *  or it's an nntp or snews URL, or news URL with a host.
+	     *  Restore the '#' in the address.
+	     */
+	    *(parts->anchor - 1) = '#';
+	    parts->anchor = NULL;
+	}
+    }
+
+#ifdef NOT_DEFINED	/* search is just treated as part of path */
+    {
+        char *p = relative ? relative : absolute;
+	if (p) {
+	    char * q = strchr(p, '?');	/* Any search string? */
+	    if (q) {
+	    	*q = '\0';			/* If so, chop that off. */
+		parts->search = q+1;
+	    }
+	}
+    }
+#endif /* NOT_DEFINED */
+} /*scan */    
+
+
+/*	Parse a Name relative to another name
+**	-------------------------------------
+**
+**	This returns those parts of a name which are given (and requested)
+**	substituting bits from the related name where necessary.
+**
+** On entry,
+**	aName		A filename given
+**      relatedName     A name relative to which aName is to be parsed
+**      wanted          A mask for the bits which are wanted.
+**
+** On exit,
+**	returns		A pointer to a malloc'd string which MUST BE FREED
+*/
+PUBLIC char * HTParse ARGS3(
+	CONST char *,	aName,
+	CONST char *,	relatedName,
+	int,		wanted)
+{
+    char * result = NULL;
+    char * return_value = NULL;
+    int len;
+    char * name = NULL;
+    char * rel = NULL;
+    char * p;
+    char * access;
+    struct struct_parts given, related;
+    
+    /* Make working copies of input strings to cut up:
+    */
+    len = strlen(aName)+strlen(relatedName)+10;
+    result = (char *)malloc(len);	/* Lots of space: more than enough */
+    if (result == NULL)
+        outofmem(__FILE__, "HTParse");
+
+    if (TRACE)
+	fprintf(stderr,
+		"HTParse: aName:%s   relatedName:%s\n",aName,relatedName);
+    
+    StrAllocCopy(name, aName);
+    StrAllocCopy(rel, relatedName);
+
+    scan(name, &given);
+    scan(rel,  &related); 
+    result[0] = '\0';		/* Clear string  */
+    if (given.access && given.host && !given.relative && !given.absolute) {
+        if (!strcmp(given.access, "http") ||
+	    !strcmp(given.access, "https") ||
+	    !strcmp(given.access, "ftp"))
+	    given.absolute = "";	/* Assume root */
+    }
+    access = given.access ? given.access : related.access;
+    if (wanted & PARSE_ACCESS)
+        if (access) {
+	    strcat(result, access);
+	    if (wanted & PARSE_PUNCTUATION)
+	        strcat(result, ":");
+	}
+
+    /* If different, inherit nothing. */
+    if (given.access && related.access && strcmp(given.access,related.access)) {
+	related.host = NULL;
+	related.absolute = NULL;
+	related.relative = NULL;
+	related.anchor = NULL;
+    }
+	
+    if (wanted & PARSE_HOST)
+        if (given.host || related.host) {
+	    char * tail = result + strlen(result);
+	    if (wanted & PARSE_PUNCTUATION)
+	        strcat(result, "//");
+	    strcat(result, given.host ? given.host : related.host);
+#define CLEAN_URLS
+#ifdef CLEAN_URLS
+	    /* Ignore default port numbers, and trailing dots on FQDNs
+	       which will only cause identical addresses to look different */
+	    {
+	    	char * p, * h;
+		p = strchr(tail, ':');
+		if (p && access) {		/* Port specified */
+		    if ( (    strcmp(access, "http") == 0
+		    	   && strcmp(p, ":80") == 0 )
+			||
+		          (   strcmp(access, "gopher") == 0
+		    	   && strcmp(p, ":70") == 0 )
+			||
+		          (   strcmp(access, "ftp") == 0
+		    	   && strcmp(p, ":21") == 0 )
+			||
+		          (   strcmp(access, "wais") == 0
+		    	   && strcmp(p, ":210") == 0 )
+		    	||
+		          (   (strcmp(access, "nntp") == 0 ||
+			       strcmp(access, "news") == 0)
+		    	   && strcmp(p, ":119") == 0 )
+		    	||
+		          (   strcmp(access, "snews") == 0
+		    	   && strcmp(p, ":563") == 0 )
+		    	||
+		          (   strcmp(access, "finger") == 0
+		    	   && strcmp(p, ":79") == 0 )
+		    	||
+		          (   strcmp(access, "cso") == 0
+		    	   && strcmp(p, ":105") == 0 )
+		    	)
+		    *p = '\0';	/* It is the default: ignore it */
+		}
+		if (!p) { 
+		    int len = strlen(tail);
+	
+		    if (len > 0) {
+		        h = tail + len - 1;	/* last char of hostname */
+		        if (*h == '.') 
+		            *h = '\0';		/* chop final . */
+		    }
+		} else { 
+		    h = p;
+		    h--;		/* End of hostname */
+		    if (*h == '.') 
+			strcpy(h, p);  /* slide p over h */
+		}
+	    }
+#endif /* CLEAN_URLS */
+	}
+	
+    if (given.host && related.host)  /* If different hosts, inherit no path. */
+        if (strcmp(given.host, related.host) != 0) {
+	    related.absolute = NULL;
+	    related.relative = NULL;
+	    related.anchor = NULL;
+	}
+	
+    if (wanted & PARSE_PATH) {
+        if (access && !given.absolute && given.relative) {
+	    if (!strcasecomp(access, "nntp") ||
+	        !strcasecomp(access, "snews") ||
+		(!strcasecomp(access, "news") &&
+		 !strncasecomp(result, "news://", 7))) {
+		/*
+		 * Treat all given nntp or snews paths,
+		 * or given paths for news URLs with a host,
+		 * as absolute.
+		 */
+		given.absolute = given.relative;
+		given.relative = NULL;
+	    }
+	}
+        if (given.absolute) {				/* All is given */
+	    if (wanted & PARSE_PUNCTUATION)
+	        strcat(result, "/");
+	    strcat(result, given.absolute);
+	    if (TRACE)
+	        fprintf(stderr,"1\n");
+	} else if (related.absolute) {	/* Adopt path not name */
+	    strcat(result, "/");
+	    strcat(result, related.absolute);
+	    if (given.relative) {
+		p = strchr(result, '?');	/* Search part? */
+		if (!p)
+		    p = result+strlen(result)-1;
+		for (; *p!='/'; p--)
+		    ;				/* last / */
+		p[1] = '\0';			/* Remove filename */
+		strcat(result, given.relative);	/* Add given one */
+		HTSimplify (result);
+	    }
+	    if (TRACE)
+	        fprintf(stderr,"2\n");
+	} else if (given.relative) {
+	    strcat(result, given.relative);		/* what we've got */
+	    if (TRACE)
+	        fprintf(stderr,"3\n");
+	} else if (related.relative) {
+	    strcat(result, related.relative);
+	    if (TRACE)
+	        fprintf(stderr,"4\n");
+	} else {  /* No inheritance */
+	    strcat(result, "/");
+	    if (TRACE)
+	        fprintf(stderr,"5\n");
+	}
+    }
+		
+    if (TRACE)
+	fprintf(stderr,"HTParse: result:%s\n",result);
+
+    if (wanted & PARSE_ANCHOR)
+        if (given.anchor || related.anchor) {
+	    if (wanted & PARSE_PUNCTUATION)
+	        strcat(result, "#");
+	    strcat(result, given.anchor ? given.anchor : related.anchor);
+	}
+    FREE(rel);
+    FREE(name);
+    
+    StrAllocCopy(return_value, result);
+    FREE(result);
+
+    return return_value;		/* exactly the right length */
+}
+
+
+/*	        Simplify a filename
+**		-------------------
+**
+** A unix-style file is allowed to contain the seqeunce xxx/../ which may be
+** replaced by "" , and the seqeunce "/./" which may be replaced by "/".
+** Simplification helps us recognize duplicate filenames.
+**
+**	Thus, 	/etc/junk/../fred 	becomes	/etc/fred
+**		/etc/junk/./fred	becomes	/etc/junk/fred
+**
+**      but we should NOT change
+**		http://fred.xxx.edu/../..
+**
+**	or	../../albert.html
+*/
+PUBLIC void HTSimplify ARGS1(
+	char *,		filename)
+{
+    char * p;
+    char * q;
+
+    if (filename == NULL)
+	return;
+
+    if (filename[0] && filename[1])	/* Bug fix 12 Mar 93 TBL */
+     for (p = filename+2; *p; p++) {
+        if (*p == '/') {
+	    if ((p[1] == '.') && (p[2]=='.') && (p[3] == '/' || !p[3])) {
+		for (q = (p-1); (q >= filename) && (*q != '/'); q--)
+		    ;			/* prev slash */
+		if (q[0] == '/' && 0 != strncmp(q, "/../", 4) &&
+		    !(q-1 > filename && q[-1] == '/')) {
+	            strcpy(q, p+3);	/* Remove  /xxx/.. */
+		    if (!(*filename))
+		        strcpy(filename, "/");
+		    p = q-1;		/* Start again with prev slash 	*/
+		} else {		/*   xxx/.. leave it! */
+#ifdef BUG_CODE
+		    strcpy(filename, p[3] ? p+4 : p+3); /* rm  xxx/../	*/
+		    p = filename;	/* Start again */
+#endif /* BUG_CODE */
+		}
+	    } else if ((p[1] == '.') && (p[2] == '/' || !p[2])) {
+	        strcpy(p, p+2);		/* Remove a slash and a dot */
+	    }
+	}
+    }
+}
+
+
+/*		Make Relative Name
+**		------------------
+**
+** This function creates and returns a string which gives an expression of
+** one address as related to another. Where there is no relation, an absolute
+** address is retured.
+**
+**  On entry,
+**	Both names must be absolute, fully qualified names of nodes
+**	(no anchor bits)
+**
+**  On exit,
+**	The return result points to a newly allocated name which, if
+**	parsed by HTParse relative to relatedName, will yield aName.
+**	The caller is responsible for freeing the resulting name later.
+**
+*/
+PUBLIC char * HTRelative ARGS2(
+	CONST char *,	aName,
+	CONST char *,	relatedName)
+{
+    char * result = NULL;
+    CONST char *p = aName;
+    CONST char *q = relatedName;
+    CONST char * after_access = NULL;
+    CONST char * path = NULL;
+    CONST char * last_slash = NULL;
+    int slashes = 0;
+    
+    for (; *p; p++, q++) {	/* Find extent of match */
+    	if (*p != *q)
+	    break;
+	if (*p == ':')
+	    after_access = p+1;
+	if (*p == '/') {
+	    last_slash = p;
+	    slashes++;
+	    if (slashes == 3)
+	        path=p;
+	}
+    }
+    
+    /* q, p point to the first non-matching character or zero */
+    
+    if (!after_access) {			/* Different access */
+        StrAllocCopy(result, aName);
+    } else if (slashes < 3){			/* Different nodes */
+    	StrAllocCopy(result, after_access);
+    } else if (slashes == 3){			/* Same node, different path */
+        StrAllocCopy(result, path);
+    } else {					/* Some path in common */
+        int levels = 0;
+        for (; *q && (*q!='#'); q++)
+	    if (*q=='/')
+	        levels++;
+	result = (char *)malloc(3*levels + strlen(last_slash) + 1);
+        if (result == NULL)
+	    outofmem(__FILE__, "HTRelative");
+	result[0] = '\0';
+	for (; levels; levels--)
+	    strcat(result, "../");
+	strcat(result, last_slash+1);
+    }
+    if (TRACE)
+        fprintf(stderr, "HT: `%s' expressed relative to\n    `%s' is\n   `%s'.",
+    		aName, relatedName, result);
+    return result;
+}
+
+
+/*		Escape undesirable characters using %		HTEscape()
+**		-------------------------------------
+**
+**	This function takes a pointer to a string in which
+**	some characters may be unacceptable unescaped.
+**	It returns a string which has these characters
+**	represented by a '%' character followed by two hex digits.
+**
+**	Unlike HTUnEscape(), this routine returns a malloced string.
+*/
+
+PRIVATE CONST unsigned char isAcceptable[96] =
+
+/*	Bit 0		xalpha		-- see HTFile.h
+**	Bit 1		xpalpha		-- as xalpha but with plus.
+**	Bit 3 ...	path		-- as xpalphas but with /
+*/
+    /*   0 1 2 3 4 5 6 7 8 9 A B C D E F */
+    {    0,0,0,0,0,0,0,0,0,0,7,6,0,7,7,4,	/* 2x   !"#$%&'()*+,-./	 */
+         7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0,	/* 3x  0123456789:;<=>?	 */
+	 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,	/* 4x  @ABCDEFGHIJKLMNO  */
+	 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7,	/* 5X  PQRSTUVWXYZ[\]^_	 */
+	 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,	/* 6x  `abcdefghijklmno	 */
+	 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0 };	/* 7X  pqrstuvwxyz{\}~	DEL */
+
+PRIVATE char *hex = "0123456789ABCDEF";
+#define ACCEPTABLE(a)	( a>=32 && a<128 && ((isAcceptable[a-32]) & mask))
+
+PUBLIC char * HTEscape ARGS2(
+	CONST char *,	str,
+	unsigned char,	mask)
+{
+    CONST char * p;
+    char * q;
+    char * result;
+    int unacceptable = 0;
+    for (p = str; *p; p++)
+        if (!ACCEPTABLE((unsigned char)TOASCII(*p)))
+	    unacceptable++;
+    result = (char *) malloc(p-str + unacceptable+ unacceptable + 1);
+    if (result == NULL)
+        outofmem(__FILE__, "HTEscape");
+    for (q = result, p = str; *p; p++) {
+    	unsigned char a = TOASCII(*p);
+	if (!ACCEPTABLE(a)) {
+	    *q++ = HEX_ESCAPE;	/* Means hex commming */
+	    *q++ = hex[a >> 4];
+	    *q++ = hex[a & 15];
+	}
+	else *q++ = *p;
+    }
+    *q++ = '\0';			/* Terminate */
+    return result;
+}
+
+
+/*		Escape undesirable characters using %		HTEscapeSP()
+**		-------------------------------------
+**
+**	This function takes a pointer to a string in which
+**	some characters may be unacceptable unescaped.
+**	It returns a string which has these characters
+**	represented by a '%' character followed by two hex digits,
+**	except that spaces are converted to '+'.
+**
+**	Unlike HTUnEscape(), this routine returns a malloced string.
+*/
+PUBLIC char * HTEscapeSP ARGS2(
+	CONST char *,	str,
+	unsigned char,	mask)
+{
+    CONST char * p;
+    char * q;
+    char * result;
+    int unacceptable = 0;
+    for (p = str; *p; p++)
+        if (!(*p == ' ' || ACCEPTABLE((unsigned char)TOASCII(*p))))
+	    unacceptable++;
+    result = (char *) malloc(p-str + unacceptable+ unacceptable + 1);
+    if (result == NULL)
+        outofmem(__FILE__, "HTEscape");
+    for (q = result, p = str; *p; p++) {
+    	unsigned char a = TOASCII(*p);
+	if (a == 32) {
+	    *q++ = '+';
+	} else if (!ACCEPTABLE(a)) {
+	    *q++ = HEX_ESCAPE;	/* Means hex commming */
+	    *q++ = hex[a >> 4];
+	    *q++ = hex[a & 15];
+	} else {
+	    *q++ = *p;
+	}
+    }
+    *q++ = '\0';			/* Terminate */
+    return result;
+}
+
+
+/*		Decode %xx escaped characters			HTUnEscape()
+**		-----------------------------
+**
+**	This function takes a pointer to a string in which some
+**	characters may have been encoded in %xy form, where xy is
+**	the acsii hex code for character 16x+y.
+**	The string is converted in place, as it will never grow.
+*/
+
+PRIVATE char from_hex ARGS1(
+	char,		c)
+{
+    return  c >= '0' && c <= '9' ?  c - '0' 
+    	    : c >= 'A' && c <= 'F'? c - 'A' + 10
+    	    : c - 'a' + 10;	/* accept small letters just in case */
+}
+
+PUBLIC char * HTUnEscape ARGS1(
+	char *,		str)
+{
+    char * p = str;
+    char * q = str;
+    while (*p) {
+        if (*p == HEX_ESCAPE) {
+	    p++;
+	    if (*p)
+	        *q = from_hex(*p++) * 16;
+	    if (*p)
+	        *q = FROMASCII(*q + from_hex(*p++));
+	    q++;
+	} else {
+	    *q++ = *p++; 
+	}
+    }
+    
+    *q++ = '\0';
+    return str;
+    
+} /* HTUnEscape */
+
+
diff --git a/WWW/Library/Implementation/HTParse.h b/WWW/Library/Implementation/HTParse.h
new file mode 100644
index 00000000..669837c2
--- /dev/null
+++ b/WWW/Library/Implementation/HTParse.h
@@ -0,0 +1,159 @@
+/*                                                   HTParse:  URL parsing in the WWW Library
+                                         HTPARSE
+                                             
+   This module of the WWW library contains code to parse URLs and various related things.
+   Implemented by HTParse.c .
+   
+ */
+#ifndef HTPARSE_H
+#define HTPARSE_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+
+/*
+
+   The following are flag bits which may be ORed together to form a number to give the
+   'wanted' argument to HTParse.
+   
+ */
+#define PARSE_ACCESS            16
+#define PARSE_HOST               8
+#define PARSE_PATH               4
+#define PARSE_ANCHOR             2
+#define PARSE_PUNCTUATION        1
+#define PARSE_ALL               31
+
+
+/*
+
+HTParse:  Parse a URL relative to another URL
+
+   This returns those parts of a name which are given (and requested) substituting bits
+   from the related name where necessary.
+   
+  ON ENTRY
+  
+  aName                   A filename given
+                         
+  relatedName             A name relative to which aName is to be parsed
+                         
+  wanted                  A mask for the bits which are wanted.
+                         
+  ON EXIT,
+  
+  returns                 A pointer to a malloc'd string which MUST BE FREED
+                         
+ */
+
+extern char * HTParse PARAMS((
+	CONST char *	aName,
+	CONST char *	relatedName,
+	int		wanted));
+
+
+/*
+
+HTStrip: Strip white space off a string
+
+  ON EXIT
+  
+   Return value points to first non-white character, or to 0 if none.
+   
+   All trailing white space is OVERWRITTEN with zero.
+   
+ */
+extern char * HTStrip PARAMS((
+	char *		s));
+
+/*
+
+HTSimplify: Simplify a UTL
+
+   A URL is allowed to contain the seqeunce xxx/../ which may be replaced by "" , and the
+   seqeunce "/./" which may be replaced by "/". Simplification helps us recognize
+   duplicate filenames. It doesn't deal with soft links, though. The new (shorter)
+   filename overwrites the old.
+   
+ */
+/*
+**      Thus,   /etc/junk/../fred       becomes /etc/fred
+**              /etc/junk/./fred        becomes /etc/junk/fred
+*/
+extern void HTSimplify PARAMS((
+	char *		filename));
+
+
+/*
+
+HTRelative:  Make Relative (Partial) URL
+
+   This function creates and returns a string which gives an expression of one address as
+   related to another. Where there is no relation, an absolute address is retured.
+   
+  ON ENTRY,
+  
+   Both names must be absolute, fully qualified names of nodes (no anchor bits)
+   
+  ON EXIT,
+  
+   The return result points to a newly allocated name which, if parsed by HTParse relative
+   to relatedName, will yield aName. The caller is responsible for freeing the resulting
+   name later.
+   
+ */
+extern char * HTRelative PARAMS((
+	CONST char *	aName,
+	CONST char *	relatedName));
+
+
+/*
+
+HTEscape:  Encode unacceptable characters in string
+
+   This funtion takes a string containing any sequence of ASCII characters, and returns a
+   malloced string containing the same infromation but with all "unacceptable" characters
+   represented in the form %xy where X and Y are two hex digits.
+   
+ */
+extern char * HTEscape PARAMS((
+	CONST char *	str,
+	unsigned char	mask));
+
+/* Convert space to plus instead of %2b - FM */
+extern char * HTEscapeSP PARAMS((
+	CONST char *	str,
+	unsigned char	mask));
+
+/*
+
+   The following are valid mask values. The terms are the BNF names in the URL document.
+   
+ */
+#define URL_XALPHAS     (unsigned char) 1
+#define URL_XPALPHAS    (unsigned char) 2
+#define URL_PATH        (unsigned char) 4
+
+
+/*
+
+HTUnEscape: Decode %xx escaped characters
+
+   This function takes a pointer to a string in which character smay have been encoded in
+   %xy form, where xy is the acsii hex code for character 16x+y. The string is converted
+   in place, as it will never grow.
+   
+ */
+extern char * HTUnEscape PARAMS((
+	char *		str));
+
+
+#endif  /* HTPARSE_H */
+
+
+/*
+
+   end of HTParse
+   
+    */
diff --git a/WWW/Library/Implementation/HTPasswd.c b/WWW/Library/Implementation/HTPasswd.c
new file mode 100644
index 00000000..58ae62c8
--- /dev/null
+++ b/WWW/Library/Implementation/HTPasswd.c
@@ -0,0 +1,301 @@
+
+/* MODULE							HTPasswd.c
+**		PASSWORD FILE ROUTINES
+**
+** AUTHORS:
+**	AL	Ari Luotonen	luotonen@dxcern.cern.ch
+**	MD	Mark Donszelmann    duns@vxdeop.cern.ch
+**
+** HISTORY:
+**	 7 Nov 93 	MD 	free for crypt taken out (static data returned) 
+**
+**
+** BUGS:
+**
+**
+*/
+
+
+#include "HTUtils.h"
+#include "tcp.h"	/* FROMASCII()		*/
+#include <string.h>
+#include "HTAAUtil.h"	/* Common parts of AA	*/
+#include "HTAAFile.h"	/* File routines	*/
+#include "HTPasswd.h"	/* Implemented here	*/
+
+#include "LYLeaks.h"
+
+extern char *crypt();
+
+
+PRIVATE char salt_chars [65] =
+    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
+
+
+/* PRIVATE						next_rec()
+**		GO TO THE BEGINNING OF THE NEXT RECORD
+**		Otherwise like HTAAFile_nextRec() but
+**		does not handle continuation lines
+**		(because password file has none).
+** ON ENTRY:
+**	fp	is the password file from which records are read from.
+**
+** ON EXIT:
+**	returns	nothing. File read pointer is located at the beginning
+**		of the next record.
+*/
+PRIVATE void next_rec ARGS1(FILE *, fp)
+{
+    int ch = getc(fp);
+
+    while (ch != EOF  &&  ch != CR  &&  ch != LF)
+	ch = getc(fp);		/* Skip until end-of-line */
+
+    while (ch != EOF &&
+	   (ch == CR  ||  ch == LF))	/*Skip carriage returns and linefeeds*/
+	ch = getc(fp);
+
+    if (ch != EOF)
+	ungetc(ch, fp);
+}
+
+
+/* PUBLIC						HTAA_encryptPasswd()
+**		ENCRYPT PASSWORD TO THE FORM THAT IT IS SAVED
+**		IN THE PASSWORD FILE.
+** ON ENTRY:
+**	password	is a string of arbitrary lenght.
+**
+** ON EXIT:
+**	returns		password in one-way encrypted form.
+**
+** NOTE:
+**	Uses currently the C library function crypt(), which
+**	only accepts at most 8 characters long strings and produces
+**	always 13 characters long strings. This function is
+**	called repeatedly so that longer strings can be encrypted.
+**	This is of course not as safe as encrypting the entire
+**	string at once, but then again, we are not that paranoid
+**	about the security inside the machine.
+**
+*/
+PUBLIC char *HTAA_encryptPasswd ARGS1(CONST char *, password)
+{
+    char salt[3];
+    char chunk[9];
+    char *result;
+    char *tmp;
+    CONST char *cur = password;
+    int len = strlen(password);
+    extern time_t theTime;
+    int random = (int)theTime;	/* This is random enough */
+
+    if (!(result = (char*)malloc(13*((strlen(password)+7)/8) + 1)))
+	outofmem(__FILE__, "HTAA_encryptPasswd");
+
+    *result = (char)0;
+    while (len > 0) {
+	salt[0] = salt_chars[random%64];
+	salt[1] = salt_chars[(random/64)%64];
+	salt[2] = (char)0;
+
+	strncpy(chunk, cur, 8);
+	chunk[8] = (char)0;
+
+	tmp = crypt((char*)password, salt);  /*crypt() doesn't change its args*/
+	strcat(result, tmp);
+
+	cur += 8;
+	len -= 8;
+    } /* while */
+
+    return result;
+}
+
+
+
+/* PUBLIC						HTAA_passwdMatch()
+**		VERIFY THE CORRECTNESS OF A GIVEN PASSWORD
+**		AGAINST A ONE-WAY ENCRYPTED FORM OF PASSWORD.
+** ON ENTRY:
+**	password	is cleartext password.
+**	encrypted	is one-way encrypted password, as returned
+**			by function HTAA_encryptPasswd().
+**			This is typically read from the password
+**			file.
+**
+** ON EXIT:
+**	returns		YES, if password matches the encrypted one.
+**			NO, if not, or if either parameter is NULL.
+** FIX:
+**	Only the length of original encrypted password is
+**	checked -- longer given passwords are accepted if
+**	common length is correct (but not shorter).
+**	This is to allow interoperation of servers and clients
+**	who have a hard-coded limit of 8 to password.
+*/
+PUBLIC BOOL HTAA_passwdMatch ARGS2(CONST char *, password,
+				   CONST char *, encrypted)
+{
+    char *result;
+    int len;
+    int status;
+
+    if (!password || !encrypted)
+	return NO;
+
+    len = 13*((strlen(password)+7)/8);
+    if (len < strlen(encrypted))
+	return NO;
+
+    if (!(result = (char*)malloc(len + 1)))
+	outofmem(__FILE__, "HTAA_encryptPasswd");
+
+    *result = (char)0;
+    while (len > 0) {
+	char salt[3];
+	char chunk[9];
+	CONST char *cur1 = password;
+	CONST char *cur2 = encrypted;
+	char *tmp;
+
+	salt[0] = *cur2;
+	salt[1] = *(cur2+1);
+	salt[2] = (char)0;
+
+	strncpy(chunk, cur1, 8);
+	chunk[8] = (char)0;
+
+	tmp = crypt((char*)password, salt);
+	strcat(result, tmp);
+
+	cur1 += 8;
+	cur2 += 13;
+	len -= 13;
+    } /* while */
+
+    status = strncmp(result, encrypted, strlen(encrypted));
+
+    if (TRACE)
+	fprintf(stderr,
+		"%s `%s' (encrypted: `%s') with: `%s' => %s\n",
+		"HTAA_passwdMatch: Matching password:",
+		password, result, encrypted,
+		(status==0 ? "OK" : "INCORRECT"));
+
+    FREE(result);
+
+    if (status==0)
+	return YES;
+    else
+	return NO;
+}
+
+
+/* PUBLIC					HTAAFile_readPasswdRec()
+**			READ A RECORD FROM THE PASSWORD FILE
+** ON ENTRY:
+**	fp		open password file
+**	out_username	buffer to put the read username, must be at
+**			least MAX_USERNAME_LEN+1 characters long.
+**	out_passwd	buffer to put the read password, must be at
+**			least MAX_PASSWORD_LEN+1 characters long.
+** ON EXIT:
+**	returns		EOF on end of file,
+**			otherwise the number of read fields
+**			(i.e. in a correct case returns 2).
+**	out_username	contains the null-terminated read username.
+**	out_password	contains the null-terminated read password.
+**
+** FORMAT OF PASSWORD FILE:
+**	username:password:maybe real name or other stuff
+**				(may include even colons)
+**
+**	There may be whitespace (blanks or tabs) in the beginning and
+**	the end of each field. They are ignored.
+*/
+PUBLIC int HTAAFile_readPasswdRec ARGS3(FILE *, fp,
+					char *, out_username,
+					char *, out_password)
+{
+    char terminator;
+    
+    terminator = HTAAFile_readField(fp, out_username, MAX_USERNAME_LEN);
+
+    if (terminator == EOF) {				/* End of file */
+	return EOF;
+    }
+    else if (terminator == CR  ||  terminator == LF) {	/* End of line */
+	next_rec(fp);
+	return 1;
+    }
+    else {
+	HTAAFile_readField(fp, out_password, MAX_PASSWORD_LEN);
+	next_rec(fp);
+	return 2;
+    }
+}
+
+
+
+/* PUBLIC						HTAA_checkPassword()
+**		CHECK A USERNAME-PASSWORD PAIR
+** ON ENTRY:
+**	username	is a null-terminated string containing
+**			the client's username.
+**	password	is a null-terminated string containing
+**			the client's corresponding password.
+**	filename	is a null-terminated absolute filename
+**			for password file.
+**			If NULL or empty, the value of
+**			PASSWD_FILE is used.
+** ON EXIT:
+**	returns		YES, if the username-password pair was correct.
+**			NO, otherwise; also, if open fails.
+*/
+PUBLIC BOOL HTAA_checkPassword ARGS3(CONST char *, username,
+				     CONST char *, password,
+				     CONST char *, filename)
+{
+    FILE *fp = NULL;
+    char user[MAX_USERNAME_LEN+1];
+    char pw[MAX_PASSWORD_LEN+1];
+    int status;
+    
+    if (filename && *filename)  fp = fopen(filename,"r");
+    else			fp = fopen(PASSWD_FILE,"r");
+
+    if (!fp) {
+	if (TRACE) fprintf(stderr, "%s `%s'\n",
+			   "HTAA_checkPassword: Unable to open password file",
+			   (filename && *filename ? filename : PASSWD_FILE));
+	return NO;
+    }
+    do {
+	if (2 == (status = HTAAFile_readPasswdRec(fp,user,pw))) {
+	    if (TRACE)
+		fprintf(stderr,
+			"HTAAFile_validateUser: %s \"%s\" %s \"%s:%s\"\n",
+			"Matching username:", username,
+			"against passwd record:", user, pw);
+	    if (username  &&  user  &&  !strcmp(username,user)) {
+		/* User's record found */
+		if (*pw != '\0') { /* So password is required for this user */
+		    if (!password ||
+			!HTAA_passwdMatch(password,pw)) /* Check the password */
+			status = EOF;	/* If wrong, indicate it with EOF */
+		}
+		break;  /* exit loop */
+	    }  /* if username found */
+	}  /* if record is ok */
+    } while (status != EOF);
+
+    fclose(fp);
+    
+    if (TRACE) fprintf(stderr, "HTAAFile_checkPassword: (%s,%s) %scorrect\n",
+		       username, password, ((status != EOF) ? "" : "in"));
+
+    if (status == EOF)  return NO;  /* We traversed to the end without luck */
+    else                return YES; /* The user was found */
+}
+
diff --git a/WWW/Library/Implementation/HTPasswd.h b/WWW/Library/Implementation/HTPasswd.h
new file mode 100644
index 00000000..0c3b3eb6
--- /dev/null
+++ b/WWW/Library/Implementation/HTPasswd.h
@@ -0,0 +1,129 @@
+/*                                   PASSWORD FILE ROUTINES
+                                             
+ */
+
+#ifndef HTPASSWD_H
+#define HTPASSWD_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+#include "HTList.h"
+
+#ifdef SHORT_NAMES
+#define HTAAenPw        HTAA_encryptPasswd
+#define HTAApwMa        HTAA_passwdMatch
+#define HTAAFrPR        HTAAFile_readPasswdRec
+#define HTAAchPw        HTAA_checkPasswd
+#endif /* SHORT_NAMES */
+
+/*
+
+User Authentication
+
+   HTAA_checkPassword(username,password,passwdfile)opens the password file, and checks if
+   the username-password pair is correct. Return value is YES, if and only if they are
+   correct. Otherwise, and also if the open fails, returns NO.
+   
+   If the given password file name is NULL or an empty string, the default password file
+   name is used (macro PASSWD_FILE).
+   
+ */
+
+/* PUBLIC                                               HTAA_checkPassword()
+**                      VALIDATE A USERNAME-PASSWORD PAIR
+** ON ENTRY:
+**      username        is a null-terminated string containing
+**                      the client's username.
+**      password        is a null-terminated string containing
+**                      the client's corresponding password.
+**      filename        is a null-terminated absolute filename
+**                      for password file.
+**                      If NULL or empty, the value of
+**                      PASSWD_FILE is used.
+** ON EXIT:
+**      returns         YES, if the username-password pair was correct.
+**                      NO, otherwise; also, if open fails.
+*/
+PUBLIC BOOL HTAA_checkPassword PARAMS((CONST char * username,
+                                       CONST char * password,
+                                       CONST char * filename));
+/*
+
+Password File Maintenance Routines
+
+ */
+
+/* PUBLIC                                               HTAA_encryptPasswd()
+**              ENCRYPT PASSWORD TO THE FORM THAT IT IS SAVED
+**              IN THE PASSWORD FILE.
+** ON ENTRY:
+**      password        is a string of arbitrary lenght.
+**
+** ON EXIT:
+**      returns         password in one-way encrypted form.
+**
+** NOTE:
+**      Uses currently the C library function crypt(), which
+**      only accepts at most 8 characters long strings and produces
+**      always 13 characters long strings. This function is
+**      called repeatedly so that longer strings can be encrypted.
+**      This is of course not as safe as encrypting the entire
+**      string at once, but then again, we are not that paranoid
+**      about the security inside the machine.
+**
+*/
+PUBLIC char *HTAA_encryptPasswd PARAMS((CONST char * password));
+
+
+/* PUBLIC                                               HTAA_passwdMatch()
+**              VERIFY THE CORRECTNESS OF A GIVEN PASSWORD
+**              AGAINST A ONE-WAY ENCRYPTED FORM OF PASSWORD.
+** ON ENTRY:
+**      password        is cleartext password.
+**      encrypted       is one-way encrypted password, as returned
+**                      by function HTAA_encryptPasswd().
+**                      This is typically read from the password
+**                      file.
+**
+** ON EXIT:
+**      returns         YES, if password matches the encrypted one.
+**                      NO, if not, or if either parameter is NULL.
+*/
+PUBLIC BOOL HTAA_passwdMatch PARAMS((CONST char * password,
+                                     CONST char * encrypted));
+
+
+/* PUBLIC                                               HTAAFile_readPasswdRec()
+**                      READ A RECORD FROM THE PASSWORD FILE
+** ON ENTRY:
+**      fp              open password file
+**      out_username    buffer to put the read username, must be at
+**                      least MAX_USERNAME_LEN+1 characters long.
+**      out_passwd      buffer to put the read password, must be at
+**                      least MAX_PASSWORD_LEN+1 characters long.
+** ON EXIT:
+**      returns         EOF on end of file,
+**                      otherwise the number of read fields
+**                      (i.e. in a correct case returns 2).
+**      out_username    contains the null-terminated read username.
+**      out_password    contains the null-terminated read password.
+**
+** FORMAT OF PASSWORD FILE:
+**      username:password:maybe real name or other stuff
+**                              (may include even colons)
+**
+**      There may be whitespace (blanks or tabs) in the beginning and
+**      the end of each field. They are ignored.
+*/
+PUBLIC int HTAAFile_readPasswdRec PARAMS((FILE * fp,
+                                          char * out_username,
+                                          char * out_password));
+/*
+
+ */
+
+#endif /* not HTPASSWD_H */
+/*
+
+   End of file HTPasswd.h.  */
diff --git a/WWW/Library/Implementation/HTPlain.c b/WWW/Library/Implementation/HTPlain.c
new file mode 100644
index 00000000..009f24e7
--- /dev/null
+++ b/WWW/Library/Implementation/HTPlain.c
@@ -0,0 +1,311 @@
+/*		Plain text object		HTWrite.c
+**		=================
+**
+**	This version of the stream object just writes to a socket.
+**	The socket is assumed open and left open.
+**
+**	Bugs:
+**		strings written must be less than buffer size.
+*/
+#include "HTUtils.h"
+
+#include "HTPlain.h"
+
+#define BUFFER_SIZE 4096;	/* Tradeoff */
+
+#include "HText.h"
+#include "HTStyle.h"
+#include "HTMLDTD.h"
+#include "HTCJK.h"
+
+#include "LYLeaks.h"
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+extern HTStyleSheet * styleSheet;
+
+extern int current_char_set;
+extern char * LYchar_set_names[];
+extern CONST char **LYCharSets[];
+extern CONST char * HTMLGetEntityName PARAMS((int i));
+extern BOOL HTPassEightBitRaw;
+extern BOOL HTPassHighCtrlRaw;
+extern HTCJKlang HTCJK;
+
+PUBLIC int HTPlain_lastraw = -1;
+
+/*		HTML Object
+**		-----------
+*/
+struct _HTStream {
+	CONST HTStreamClass *	isa;
+
+	HText * 		text;
+};
+
+/*	Write the buffer out to the socket
+**	----------------------------------
+*/
+
+
+/*_________________________________________________________________________
+**
+**			A C T I O N 	R O U T I N E S
+*/
+
+/*	Character handling
+**	------------------
+*/
+PRIVATE void HTPlain_put_character ARGS2(HTStream *, me, char, c)
+{
+#ifdef REMOVE_CR_ONLY
+    /* throw away \r's */
+    if (c != '\r') {
+       HText_appendCharacter(me->text, c);
+    }
+#else
+    /*
+    **  See HTPlain_write() for explanations of the following code
+    **  (we've been called via HTPlain_put_string() to do for each
+    **  character of a terminated string what HTPlain_write() does
+    **  via a while loop for each character in a stream of given
+    **  length). - FM
+    */
+    if ((HTPlain_lastraw == '\r') && c == '\n') {
+	HTPlain_lastraw = -1;
+	return;
+    }
+    HTPlain_lastraw = c;
+    if (c == '\r') {
+	HText_appendCharacter(me->text, '\n');
+    } else if (HTCJK != NOCJK) {
+	HText_appendCharacter(me->text, c);
+    } else if ((unsigned char)c >= 127 && (unsigned char)c < 161 &&
+    	       HTPassHighCtrlRaw) {
+	HText_appendCharacter(me->text, c);
+    } else if ((unsigned char)c == 160) {
+	HText_appendCharacter(me->text, ' ');
+    } else if ((unsigned char)c == 173) {
+        return;
+    } else if (((unsigned char)c >= 32 && (unsigned char)c < 127) ||
+	       c == '\n' || c == '\t') {
+	HText_appendCharacter(me->text, c);
+    } else if ((unsigned char)c > 160) {
+	if (!HTPassEightBitRaw &&
+	    strncmp(LYchar_set_names[current_char_set], "ISO Latin 1", 11)) {
+	    int len, high, low, i, diff;
+	    CONST char * name;
+	    int value = (int)((unsigned char)c - 160);
+	    name = HTMLGetEntityName(value);
+	    len =  strlen(name);
+	    for(low=0, high = HTML_dtd.number_of_entities;
+		high > low;
+		diff < 0 ? (low = i+1) : (high = i)) {
+		/* Binary search */
+		i = (low + (high-low)/2);
+		diff = strncmp(HTML_dtd.entity_names[i], name, len);
+		if (diff==0) {
+		    HText_appendText(me->text,
+		    		     LYCharSets[current_char_set][i]);
+		    break;
+		}
+	    }
+	    if (diff) {
+		HText_appendCharacter(me->text, c);
+	    }
+	} else {
+	    HText_appendCharacter(me->text, c);
+	}
+    }
+#endif /* REMOVE_CR_ONLY */
+}
+
+
+/*	String handling
+**	---------------
+**
+*/
+PRIVATE void HTPlain_put_string ARGS2(HTStream *, me, CONST char*, s)
+{
+#ifdef REMOVE_CR_ONLY
+    HText_appendText(me->text, s);
+#else
+    CONST char * p;
+
+    if (s == NULL)
+	return;
+    for (p = s; *p; p++) {
+        HTPlain_put_character(me, *p);
+    }
+#endif /* REMOVE_CR_ONLY */
+}
+
+
+/*
+**	Entry function for displayed text/plain and WWW_SOURCE strings. - FM
+**	---------------------------------------------------------------
+*/
+PRIVATE void HTPlain_write ARGS3(HTStream *, me, CONST char*, s, int, l)
+{
+    CONST char * p;
+    CONST char * e = s+l;
+
+    for (p = s; p < e; p++) {
+#ifdef REMOVE_CR_ONLY
+	/* 
+	**  Append the whole string, but remove any \r's. - FM
+	*/
+	if (*p != '\r') {
+	    HText_appendCharacter(me->text, *p);
+	}
+#else
+	/*
+	**  Try to handle lone LFs, CRLFs and lone CRs
+	**  as newline, and to deal with control, ASCII,
+	**  and 8-bit characters based on best guesses
+	**  of what's appropriate. - FM
+	*/
+	if ((HTPlain_lastraw == '\r') && *p == '\n') {
+	    HTPlain_lastraw = -1;
+	    continue;
+	}
+	HTPlain_lastraw = *p;
+	if (*p == '\r') {
+	    HText_appendCharacter(me->text, '\n');
+	/*
+	**  If CJK mode is on, we'll assume the document matches
+	**  the user's selected character set, and if not, the
+	**  user should toggle off raw/CJK mode to reload. - FM
+	*/
+	} else if (HTCJK != NOCJK) {
+	    HText_appendCharacter(me->text, *p);
+	/*
+	**  If HTPassHighCtrlRaw is set (e.g., for KOI8-R) assume the
+	**  document matches and pass 127-160 8-bit characters.  If it
+	**  doesn't match, the user should toggle raw/CJK mode off. - FM
+	*/
+	} else if ((unsigned char)*p >= 127 && (unsigned char)*p < 161 &&
+		    HTPassHighCtrlRaw) {
+	    HText_appendCharacter(me->text, *p);
+	/*
+	**  If neither HTPassHighCtrlRaw nor CJK is set, play it safe
+	**  and treat 160 (nbsp) as an ASCII space (32). - FM
+	*/
+	} else if ((unsigned char)*p == 160) {
+	    HText_appendCharacter(me->text, ' ');
+	/*
+	**  If neither HTPassHighCtrlRaw nor CJK is set, play it safe
+	**  and ignore 173 (shy). - FM
+	*/
+	} else if ((unsigned char)*p == 173) {
+	    continue;
+	/*
+	**  If we get to here, pass the displayable ASCII characters. - FM
+	*/
+	} else if (((unsigned char)*p >= 32 && (unsigned char)*p < 127) ||
+		   *p == '\n' || *p == '\t') {
+	    HText_appendCharacter(me->text, *p);
+	/*
+	**  If we get to here and HTPassEightBitRaw or the
+	**  selected character set is not "ISO Latin 1",
+	**  use the translation tables for 161-255 8-bit
+	**  characters (173 was handled above). - FM
+	*/
+	} else if ((unsigned char)*p > 160) {
+	    if (!HTPassEightBitRaw &&
+		strncmp(LYchar_set_names[current_char_set],
+		   	"ISO Latin 1", 11)) {
+		/*
+		**  Attempt to translate. - FM
+		*/
+		int len, high, low, i, diff;
+		CONST char * name;
+		int value = (int)((unsigned char)*p - 160);
+		name = HTMLGetEntityName(value);
+		len =  strlen(name);
+		for(low=0, high = HTML_dtd.number_of_entities;
+		    high > low;
+		    diff < 0 ? (low = i+1) : (high = i)) {
+		    /* Binary search */
+		    i = (low + (high-low)/2);
+		    diff = strncmp(HTML_dtd.entity_names[i], name, len);
+		    if (diff==0) {
+			HText_appendText(me->text,
+					 LYCharSets[current_char_set][i]);
+			break;
+		    }
+		}
+		if (diff) {
+		    /*
+		    **  Something went wrong in the translation, so
+		    **  pass the raw character and hope it's OK. - FM
+		    */
+		    HText_appendCharacter(me->text, *p);
+		}
+	    } else {
+	        /*
+		**  Didn't attempt a translation. - FM
+		*/
+	        HText_appendCharacter(me->text, *p);
+	    }
+	}
+#endif /* REMOVE_CR_ONLY */
+    }
+}
+
+
+/*	Free an HTML object
+**	-------------------
+**
+**	Note that the SGML parsing context is freed, but the created object is
+**	not, as it takes on an existence of its own unless explicitly freed.
+*/
+PRIVATE void HTPlain_free ARGS1(HTStream *, me)
+{
+    FREE(me);
+}
+
+
+/*	End writing
+*/
+PRIVATE void HTPlain_abort ARGS2(HTStream *, me, HTError, e)
+{
+    HTPlain_free(me);
+}
+
+
+/*		Structured Object Class
+**		-----------------------
+*/
+PUBLIC CONST HTStreamClass HTPlain =
+{		
+	"SocketWriter",
+	HTPlain_free,
+	HTPlain_abort,
+	HTPlain_put_character, 	HTPlain_put_string, HTPlain_write,
+}; 
+
+
+/*		New object
+**		----------
+*/
+PUBLIC HTStream* HTPlainPresent ARGS3(
+	HTPresentation *,	pres,
+	HTParentAnchor *,	anchor,	
+	HTStream *,		sink)
+{
+
+    HTStream* me = (HTStream*)malloc(sizeof(*me));
+    if (me == NULL)
+        outofmem(__FILE__, "HTPlain_new");
+    me->isa = &HTPlain;
+
+    HTPlain_lastraw = -1;
+
+    me->text = HText_new(anchor);
+    HText_setStyle(me->text, HTStyleNamed(styleSheet, "Example"));
+    HText_beginAppend(me->text);
+
+    return (HTStream*) me;
+}
+
diff --git a/WWW/Library/Implementation/HTPlain.h b/WWW/Library/Implementation/HTPlain.h
new file mode 100644
index 00000000..f4ce3e14
--- /dev/null
+++ b/WWW/Library/Implementation/HTPlain.h
@@ -0,0 +1,21 @@
+/*                  /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTPlain.html
+                                    PLAIN TEXT OBJECT
+                                             
+ */
+#ifndef HTPLAIN_H
+#define HTPLAIN_H
+
+#include "HTStream.h"
+#include "HTAnchor.h"
+
+extern HTStream* HTPlainPresent PARAMS((
+        HTPresentation *        pres,
+        HTParentAnchor *        anchor,
+        HTStream *              sink));
+
+
+#endif
+
+/*
+
+    */
diff --git a/WWW/Library/Implementation/HTRules.c b/WWW/Library/Implementation/HTRules.c
new file mode 100644
index 00000000..92de0fc7
--- /dev/null
+++ b/WWW/Library/Implementation/HTRules.c
@@ -0,0 +1,418 @@
+/*	Configuration manager for Hypertext Daemon		HTRules.c
+**	==========================================
+**
+**
+** History:
+**	 3 Jun 91	Written TBL
+**	10 Aug 91	Authorisation added after Daniel Martin (pass, fail)
+**			Rule order in file changed
+**			Comments allowed with # on 1st char of rule line
+**      17 Jun 92       Bug fix: pass and fail failed if didn't contain '*' TBL
+**       1 Sep 93       Bug fix: no memory check - Nathan Torkington
+**                      BYTE_ADDRESSING removed - Arthur Secret
+**	11 Sep 93  MD	Changed %i into %d in debug printf. 
+**			VMS does not recognize %i.
+**			Bug Fix: in case of PASS, only one parameter to printf.
+**	19 Sep 93  AL	Added Access Authorization stuff.
+**	 1 Nov 93  AL	Added htbin.
+**
+*/
+
+/* (c) CERN WorldWideWeb project 1990,91. See Copyright.html for details */
+#include "HTRules.h"
+
+#include "HTUtils.h"
+#include "tcp.h"
+/*#include <stdio.h> included by HTUtils.h -- FM */
+#include "HTFile.h"
+#include "HTAAServ.h"	/* Access Authorization */
+
+#include "LYLeaks.h"
+
+#define LINE_LENGTH 256
+
+
+typedef struct _rule {
+	struct _rule *	next;
+	HTRuleOp	op;
+	char *		pattern;
+	char *		equiv;
+} rule;
+
+/*	Global variables
+**	----------------
+*/
+PUBLIC char *HTBinDir = NULL;	/* Physical /htbin directory path.	*/
+                                /* In future this should not be global.	*/
+PUBLIC char *HTSearchScript = NULL;	/* Search script name.		*/
+
+
+/*	Module-wide variables
+**	---------------------
+*/
+
+PRIVATE rule * rules = 0;	/* Pointer to first on list */
+#ifndef PUT_ON_HEAD
+PRIVATE rule * rule_tail = 0;	/* Pointer to last on list */
+#endif
+
+
+/*	Add rule to the list					HTAddRule()
+**	--------------------
+**
+**  On entry,
+**	pattern		points to 0-terminated string containing a single "*"
+**	equiv		points to the equivalent string with * for the
+**			place where the text matched by * goes.
+**  On exit,
+**	returns		0 if success, -1 if error.
+*/
+
+#ifdef __STDC__
+PUBLIC int HTAddRule (HTRuleOp op, const char * pattern, const char * equiv)
+#else
+int HTAddRule(op, pattern, equiv)
+    HTRuleOp	op;
+    char *	pattern;
+    char *	equiv;
+#endif
+{ /* BYTE_ADDRESSING removed and memory check - AS - 1 Sep 93 */
+    rule *      temp;
+    char *      pPattern;
+
+    temp = (rule *)malloc(sizeof(*temp));
+    if (temp==NULL) 
+	outofmem(__FILE__, "HTAddRule"); 
+    pPattern = (char *)malloc(strlen(pattern)+1);
+    if (pPattern==NULL) 
+	outofmem(__FILE__, "HTAddRule"); 
+    if (equiv) {		/* Two operands */
+	char *	pEquiv = (char *)malloc(strlen(equiv)+1);
+	if (pEquiv==NULL) 
+	    outofmem(__FILE__, "HTAddRule"); 
+        temp->equiv = pEquiv;
+        strcpy(pEquiv, equiv);
+    } else {
+        temp->equiv = 0;
+    }
+    temp->pattern = pPattern;
+    temp->op = op;
+
+    strcpy(pPattern, pattern);
+    if (TRACE) {
+       if (equiv)
+          fprintf(stderr, "Rule: For `%s' op %d `%s'\n", pattern, op, equiv);
+       else
+          fprintf(stderr, "Rule: For `%s' op %d\n", pattern, op);
+    }
+
+#ifdef PUT_ON_HEAD
+    temp->next = rules;
+    rules = temp;
+#else
+    temp->next = 0;
+    if (rule_tail) rule_tail->next = temp;
+    else rules = temp;
+    rule_tail = temp;
+#endif
+
+        
+    return 0;
+}
+
+
+/*	Clear all rules						HTClearRules()
+**	---------------
+**
+** On exit,
+**	There are no rules
+**	returns		0 if success, -1 if error.
+**
+** See also
+**	HTAddRule()
+*/
+#ifdef __STDC__
+int HTClearRules(void)
+#else
+int HTClearRules()
+#endif
+{
+    while (rules) {
+    	rule * temp = rules;
+	rules = temp->next;
+	FREE(temp->pattern);
+	FREE(temp->equiv);
+	FREE(temp);
+    }
+#ifndef PUT_ON_HEAD
+    rule_tail = 0;
+#endif
+
+    return 0;
+}
+
+
+/*	Translate by rules					HTTranslate()
+**	------------------
+**
+**	The most recently defined rules are applied first.
+**
+** On entry,
+**	required	points to a string whose equivalent value is neeed
+** On exit,
+**	returns		the address of the equivalent string allocated from
+**			the heap which the CALLER MUST FREE. If no translation
+**			occured, then it is a copy of te original.
+** NEW FEATURES:
+**			When a "protect" or "defprot" rule is mathed,
+**			a call to HTAA_setCurrentProtection() or
+**			HTAA_setDefaultProtection() is made to notify
+**			the Access Authorization module that the file is
+**			protected, and so it knows how to handle it.
+**								-- AL
+*/
+#ifdef __STDC__
+char * HTTranslate(const char * required)
+#else
+char * HTTranslate(required)
+	char * required;
+#endif
+{
+    rule * r;
+    char *current = NULL;
+    StrAllocCopy(current, required);
+
+    HTAA_clearProtections();	/* Reset from previous call -- AL */
+
+    for(r = rules; r; r = r->next) {
+        char * p = r->pattern;
+	int m=0;   /* Number of characters matched against wildcard */
+	CONST char * q = current;
+	for(;*p && *q; p++, q++) {   /* Find first mismatch */
+	    if (*p!=*q) break;
+	}
+
+	if (*p == '*') {		/* Match up to wildcard */
+	    m = strlen(q) - strlen(p+1); /* Amount to match to wildcard */
+	    if(m<0) continue;           /* tail is too short to match */
+	    if (0!=strcmp(q+m, p+1)) continue;	/* Tail mismatch */
+	} else 				/* Not wildcard */
+	    if (*p != *q) continue;	/* plain mismatch: go to next rule */
+
+	switch (r->op) {		/* Perform operation */
+
+#ifdef ACCESS_AUTH
+	case HT_DefProt:
+	case HT_Protect:
+	    {
+		char *local_copy = NULL;
+		char *p;
+		char *eff_ids = NULL;
+		char *prot_file = NULL;
+
+		if (TRACE) fprintf(stderr,
+				   "HTRule: `%s' matched %s %s: `%s'\n",
+				   current,
+				   (r->op==HT_Protect ? "Protect" : "DefProt"),
+				   "rule, setup",
+				   (r->equiv ? r->equiv :
+				    (r->op==HT_Protect ?"DEFAULT" :"NULL!!")));
+
+		if (r->equiv) {
+		    StrAllocCopy(local_copy, r->equiv);
+		    p = local_copy;
+		    prot_file = HTNextField(&p);
+		    eff_ids = HTNextField(&p);
+		}
+
+		if (r->op == HT_Protect)
+		    HTAA_setCurrentProtection(current, prot_file, eff_ids);
+		else
+		    HTAA_setDefaultProtection(current, prot_file, eff_ids);
+
+		FREE(local_copy);
+
+		/* continue translating rules */
+	    }
+	    break;
+#endif /* ACCESS_AUTH */
+
+	case HT_Pass:				/* Authorised */
+    		if (!r->equiv) {
+		    if (TRACE) fprintf(stderr, "HTRule: Pass `%s'\n", current);
+		    return current;
+	        }
+		/* Else fall through ...to map and pass */
+		
+	case HT_Map:
+	    if (*p == *q) { /* End of both strings, no wildcard */
+    	          if (TRACE) fprintf(stderr,
+			       "For `%s' using `%s'\n", current, r->equiv);  
+	          StrAllocCopy(current, r->equiv); /* use entire translation */
+	    } else {
+		  char * ins = strchr(r->equiv, '*');	/* Insertion point */
+	          if (ins) {	/* Consistent rule!!! */
+			char * temp = (char *)malloc(
+				strlen(r->equiv)-1 + m + 1);
+			if (temp==NULL) 
+			    outofmem(__FILE__, "HTTranslate"); /* NT & AS */
+			strncpy(temp, 	r->equiv, ins-r->equiv);
+			/* Note: temp may be unterminated now! */
+			strncpy(temp+(ins-r->equiv), q, m);  /* Matched bit */
+			strcpy (temp+(ins-r->equiv)+m, ins+1);	/* Last bit */
+    			if (TRACE) fprintf(stderr, "For `%s' using `%s'\n",
+						current, temp);
+			FREE(current);
+			current = temp;			/* Use this */
+
+		    } else {	/* No insertion point */
+			char * temp = (char *)malloc(strlen(r->equiv)+1);
+			if (temp==NULL) 
+			    outofmem(__FILE__, "HTTranslate"); /* NT & AS */
+			strcpy(temp, r->equiv);
+    			if (TRACE) fprintf(stderr, "For `%s' using `%s'\n",
+						current, temp);
+			FREE(current);
+			current = temp;			/* Use this */
+		    } /* If no insertion point exists */
+		}
+		if (r->op == HT_Pass) {
+		    if (TRACE) fprintf(stderr, "HTRule: ...and pass `%s'\n",
+		    		       current);
+		    return current;
+		}
+		break;
+
+	case HT_Invalid:
+	case HT_Fail:				/* Unauthorised */
+    		    if (TRACE) fprintf(stderr, "HTRule: *** FAIL `%s'\n",
+		    		       current);
+		    return (char *)0;
+		    		    
+	} /* if tail matches ... switch operation */
+
+    } /* loop over rules */
+
+
+    return current;
+}
+
+/*	Load one line of configuration
+**	------------------------------
+**
+**	Call this, for example, to load a X resource with config info.
+**
+** returns	0 OK, < 0 syntax error.
+*/
+PUBLIC int  HTSetConfiguration ARGS1(CONST char *, config)
+{
+    HTRuleOp op;
+    char * line = NULL;
+    char * pointer = line;
+    char *word1, *word2, *word3;
+    float quality, secs, secs_per_byte;
+    int maxbytes;
+    int status;
+    
+    StrAllocCopy(line, config);
+    {
+	char * p = strchr(line, '#');	/* Chop off comments */
+	if (p) *p = 0;
+    }
+    pointer = line;
+    word1 = HTNextField(&pointer);
+    if (!word1) {
+    	FREE(line);
+	return 0;
+    } ;	/* Comment only or blank */
+
+    word2 = HTNextField(&pointer);
+
+    if (0==strcasecomp(word1, "defprot") ||
+	0==strcasecomp(word1, "protect"))
+	word3 = pointer;  /* The rest of the line to be parsed by AA module */
+    else
+	word3 = HTNextField(&pointer);	/* Just the next word */
+
+    if (!word2) {
+	fprintf(stderr, "HTRule: Insufficient operands: %s\n", line);
+	FREE(line);
+	return -2;	/*syntax error */
+    }
+
+    if (0==strcasecomp(word1, "suffix")) {
+        char * encoding = HTNextField(&pointer);
+	if (pointer) status = sscanf(pointer, "%f", &quality);
+	else status = 0;
+	HTSetSuffix(word2,	word3,
+				encoding ? encoding : "binary",
+				status >= 1? quality : 1.0);
+
+    } else if (0==strcasecomp(word1, "presentation")) {
+        if (pointer) status = sscanf(pointer, "%f%f%f%d",
+			    &quality, &secs, &secs_per_byte, &maxbytes);
+        else status = 0;
+	HTSetPresentation(word2, word3,
+		    status >= 1? quality 		: 1.0,
+		    status >= 2 ? secs 			: 0.0,
+		    status >= 3 ? secs_per_byte 	: 0.0,
+		    status >= 4 ? maxbytes		: 0 );
+
+    } else if (0==strncasecomp(word1, "htbin", 5) ||
+	       0==strncasecomp(word1, "bindir", 6)) {
+	StrAllocCopy(HTBinDir, word2);	/* Physical /htbin location */
+
+    } else if (0==strncasecomp(word1, "search", 6)) {
+	StrAllocCopy(HTSearchScript, word2);	/* Search script name */
+
+    } else {
+	op =	0==strcasecomp(word1, "map")  ?	HT_Map
+	    :	0==strcasecomp(word1, "pass") ?	HT_Pass
+	    :	0==strcasecomp(word1, "fail") ?	HT_Fail
+	    :   0==strcasecomp(word1, "defprot") ? HT_DefProt
+	    :	0==strcasecomp(word1, "protect") ? HT_Protect
+	    :						HT_Invalid;
+	if (op==HT_Invalid) {
+	    fprintf(stderr, "HTRule: Bad rule `%s'\n", config);
+	} else {  
+	    HTAddRule(op, word2, word3);
+	} 
+    }
+    FREE(line);
+    return 0;
+}
+
+
+/*	Load the rules from a file				HTLoadRules()
+**	--------------------------
+**
+** On entry,
+**	Rules can be in any state
+** On exit,
+**	Any existing rules will have been kept.
+**	Any new rules will have been loaded.
+**	Returns		0 if no error, 0 if error!
+**
+** Bugs:
+**	The strings may not contain spaces.
+*/
+
+int HTLoadRules ARGS1(CONST char *, filename)
+{
+    FILE * fp = fopen(filename, "r");
+    char line[LINE_LENGTH+1];
+    
+    if (!fp) {
+        if (TRACE) fprintf(stderr,
+			   "HTRules: Can't open rules file %s\n", filename);
+	return -1; /* File open error */
+    }
+    for(;;) {
+	if (!fgets(line, LINE_LENGTH+1, fp)) break;	/* EOF or error */
+	(void) HTSetConfiguration(line);
+    }
+    fclose(fp);
+    return 0;		/* No error or syntax errors ignored */
+}
+
+
diff --git a/WWW/Library/Implementation/HTRules.h b/WWW/Library/Implementation/HTRules.h
new file mode 100644
index 00000000..84075244
--- /dev/null
+++ b/WWW/Library/Implementation/HTRules.h
@@ -0,0 +1,165 @@
+/*                                                           Configuration Manager for libwww
+                                  CONFIGURATION MANAGER
+                                             
+   Author Tim Berners-Lee/CERN. Public domain. Please mail changes to timbl@info.cern.ch.
+   
+   The configuration information loaded includes tables (file suffixes, presentation
+   methods) in other modules.  The most likely routines needed by developers will be:
+   
+  HTSetConfiguration      to load configuration information.
+                         
+  HTLoadRules             to load a whole file of configuration information
+                         
+  HTTranslate             to translate a URL using the rule table.
+                         
+ */
+#ifndef HTRULE_H
+#define HTRULE_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+
+typedef enum _HTRuleOp {
+        HT_Invalid,
+        HT_Map,
+        HT_Pass,
+        HT_Fail,
+        HT_DefProt,
+        HT_Protect
+} HTRuleOp;
+
+#ifdef SHORT_NAMES
+#define HTSearSc HTSearchScript
+#endif /*SHORT_NAMES*/
+
+/*
+
+Server Side Script Execution
+
+   If a URL starts with /htbin/ it is understood to mean a script execution request on
+   server. This feature needs to be turned on by setting HTBinDir by the htbin rule.
+   Index searching is enabled by setting HTSearchScript into the name of script in BinDir
+   doing the actual search by search rule (BinDir must also be set in this case, of
+   course).
+   
+ */
+
+extern char * HTBinDir;         /* Physical /htbin location */
+extern char * HTSearchScript;   /* Search script name */
+
+/*
+
+HTAddRule:  Add rule to the list
+
+  ON ENTRY,
+  
+  pattern                points to 0-terminated string containing a single "*"
+                         
+  equiv                  points to the equivalent string with * for the place where the
+                         text matched by * goes.
+                         
+  ON EXIT,
+  
+  returns                0 if success, -1 if error.
+                         
+   Note that if BYTE_ADDRESSING is set, the three blocks required are allocated and
+   deallocated as one. This will save time and storage, when malloc's allocation units are
+   large.
+   
+ */
+extern int HTAddRule PARAMS((HTRuleOp op, const char * pattern, const char * equiv));
+
+
+/*
+
+HTClearRules: Clear all rules
+
+  ON EXIT,
+  
+  Rule file               There are no rules
+                         
+  returns
+                          0 if success, -1 if error.
+                         
+ */
+
+#ifdef __STDC__
+extern int HTClearRules(void);
+#else
+extern int HTClearRules();
+#endif
+
+
+/*
+
+HTTranslate: Translate by rules
+
+ */
+        
+/*
+
+  ON ENTRY,
+  
+  required                points to a string whose equivalent value is neeed
+                         
+  ON EXIT,
+  
+  returns                 the address of the equivalent string allocated from the heap
+                         which the CALLER MUST FREE. If no translation occured, then it is
+                         a copy of the original.
+                         
+ */
+#ifdef __STDC__
+extern char * HTTranslate(const char * required);
+#else
+extern char * HTTranslate();
+#endif
+
+
+/*
+
+HTSetConfiguration:  Load one line of configuration information
+
+  ON ENTRY,
+  
+  config                  is a string in the syntax of a rule file line.
+                         
+   This routine may be used for loading configuration information from sources other than
+   the  rule file, for example INI files for X resources.
+   
+ */
+extern int HTSetConfiguration PARAMS((CONST char * config));
+
+
+/*
+
+HtLoadRules:  Load the rules from a file
+
+  ON ENTRY,
+  
+  Rule table              Rules can be in any state
+                         
+  ON EXIT,
+  
+  Rule table              Any existing rules will have been kept. Any new rules will have
+                         been loaded on top, so as to be tried first.
+                         
+  Returns                 0 if no error.
+                         
+ */
+
+#ifdef __STDC__
+extern int HTLoadRules(const char * filename);
+#else
+extern int HTLoadRules();
+#endif
+/*
+
+ */
+
+
+#endif /* HTUtils.h */
+/*
+
+   end */
diff --git a/WWW/Library/Implementation/HTStream.h b/WWW/Library/Implementation/HTStream.h
new file mode 100644
index 00000000..72344cd3
--- /dev/null
+++ b/WWW/Library/Implementation/HTStream.h
@@ -0,0 +1,61 @@
+/*                                                      The Stream class definition -- libwww
+                                 STREAM OBJECT DEFINITION
+                                             
+   A Stream object is something which accepts a stream of text.
+   
+   The creation methods will vary on the type of Stream Object.   All creation methods
+   return a pointer to the stream type below.
+   
+   As you can see, but the methods used to write to the stream and close it are pointed to
+   be the object itself.
+   
+ */
+#ifndef HTSTREAM_H
+#define HTSTREAM_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+
+typedef struct _HTStream HTStream;
+
+/*
+
+   These are the common methods of all streams.  They should be self-explanatory, except
+   for end_document which must be called before free.  It should be merged with free in
+   fact:  it should be dummy for new streams.
+   
+   The put_block method was write, but this upset systems whiuch had macros for write().
+   
+ */
+typedef struct _HTStreamClass {
+
+        char*  name;                            /* Just for diagnostics */
+                
+        void (*_free) PARAMS((
+                HTStream*       me));
+
+        void (*_abort) PARAMS((
+                HTStream*       me,
+                HTError         e));
+                
+        void (*put_character) PARAMS((
+                HTStream*       me,
+                char            ch));
+                                
+        void (*put_string) PARAMS((
+                HTStream*       me,
+                CONST char *    str));
+                
+        void (*put_block) PARAMS((
+                HTStream*       me,
+                CONST char *    str,
+                int             len));
+
+}HTStreamClass;
+
+#endif /* HTSTREAM_H */
+
+/*
+
+   end of HTStream.h */
diff --git a/WWW/Library/Implementation/HTString.c b/WWW/Library/Implementation/HTString.c
new file mode 100644
index 00000000..9f693924
--- /dev/null
+++ b/WWW/Library/Implementation/HTString.c
@@ -0,0 +1,156 @@
+/*		Case-independent string comparison		HTString.c
+**
+**	Original version came with listserv implementation.
+**	Version TBL Oct 91 replaces one which modified the strings.
+**	02-Dec-91 (JFG) Added stralloccopy and stralloccat
+**	23 Jan 92 (TBL) Changed strallocc* to 8 char HTSAC* for VM and suchlike
+**	 6 Oct 92 (TBL) Moved WWW_TraceFlag in here to be in library
+*/
+#include <ctype.h>
+#include "HTUtils.h"
+#include "tcp.h"
+
+#include "LYLeaks.h"
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+PUBLIC int WWW_TraceFlag = 0;	/* Global trace flag for ALL W3 code */
+
+#ifndef VC
+#define VC "unknown"
+#endif /* !VC */
+
+PUBLIC CONST char * HTLibraryVersion = VC; /* String for help screen etc */
+
+#ifndef VM		/* VM has these already it seems */
+	
+/*	Strings of any length
+**	---------------------
+*/
+PUBLIC int strcasecomp ARGS2(
+	CONST char*,	a,
+	CONST char *,	b)
+{
+    CONST char *p = a;
+    CONST char *q = b;
+
+    for (p = a, q = b; *p && *q; p++, q++) {
+        int diff = TOLOWER(*p) - TOLOWER(*q);
+	if (diff) return diff;
+    }
+    if (*p)
+        return 1;	/* p was longer than q */
+    if (*q)
+        return -1;	/* p was shorter than q */
+    return 0;		/* Exact match */
+}
+
+
+/*	With count limit
+**	----------------
+*/
+PUBLIC int strncasecomp ARGS3(
+	CONST char*,	a,
+	CONST char *,	b,
+	int,		n)
+{
+    CONST char *p = a;
+    CONST char *q = b;
+
+    for (p = a, q = b; ; p++, q++) {
+        int diff;
+	if (p == (a+n))
+	    return 0;	/*   Match up to n characters */
+	if (!(*p && *q))
+	    return (*p - *q);
+	diff = TOLOWER(*p) - TOLOWER(*q);
+	if (diff)
+	    return diff;
+    }
+    /*NOTREACHED*/
+}
+#endif /* VM */
+
+/*	Allocate a new copy of a string, and returns it
+*/
+PUBLIC char * HTSACopy ARGS2(
+	char **,	dest,
+	CONST char *,	src)
+{
+    FREE(*dest);
+    if (src) {
+        *dest = (char *) malloc (strlen(src) + 1);
+	if (*dest == NULL)
+	    outofmem(__FILE__, "HTSACopy");
+	strcpy (*dest, src);
+    }
+    return *dest;
+}
+
+/*	String Allocate and Concatenate
+*/
+PUBLIC char * HTSACat ARGS2(
+	char **,	dest,
+	CONST char *,	src)
+{
+    if (src && *src) {
+        if (*dest) {
+	    int length = strlen(*dest);
+	    *dest = (char *)realloc(*dest, length + strlen(src) + 1);
+	    if (*dest == NULL)
+	        outofmem(__FILE__, "HTSACat");
+	    strcpy (*dest + length, src);
+	} else {
+	    *dest = (char *)malloc(strlen(src) + 1);
+	    if (*dest == NULL)
+	        outofmem(__FILE__, "HTSACat");
+	    strcpy (*dest, src);
+	}
+    }
+    return *dest;
+}
+
+
+/*	Find next Field
+**	---------------
+**
+** On entry,
+**	*pstr	points to a string containig white space separated
+**		field, optionlly quoted.
+**
+** On exit,
+**	*pstr	has been moved to the first delimiter past the
+**		field
+**		THE STRING HAS BEEN MUTILATED by a 0 terminator
+**
+**	returns	a pointer to the first field
+*/
+PUBLIC char * HTNextField ARGS1(
+	char **,	pstr)
+{
+    char * p = *pstr;
+    char * start;			/* start of field */
+    
+    while (*p && WHITE(*p))
+        p++;				/* Strip white space */
+    if (!*p) {
+	*pstr = p;
+        return NULL;		/* No first field */
+    }
+    if (*p == '"') {			/* quoted field */
+        p++;
+	start = p;
+	for (; *p && *p!='"'; p++) {
+	    if (*p == '\\' && p[1])
+	        p++;			/* Skip escaped chars */
+	}
+    } else {
+	start = p;
+	while (*p && !WHITE(*p))
+	    p++;			/* Skip first field */
+    }
+    if (*p)
+        *p++ = '\0';
+    *pstr = p;
+    return start;
+}
diff --git a/WWW/Library/Implementation/HTString.h b/WWW/Library/Implementation/HTString.h
new file mode 100644
index 00000000..7fad84bb
--- /dev/null
+++ b/WWW/Library/Implementation/HTString.h
@@ -0,0 +1,51 @@
+/*                                                                 String handling for libwww
+                                         STRINGS
+                                             
+   Case-independent string comparison and allocations with copies etc
+   
+ */
+#ifndef HTSTRING_H
+#define HTSTRING_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+
+extern int WWW_TraceFlag;       /* Global flag for all W3 trace */
+
+extern CONST char * HTLibraryVersion;   /* String for help screen etc */
+
+/*
+
+Case-insensitive string comparison
+
+   The usual routines (comp instead of cmp) had some problem.
+   
+ */
+extern int strcasecomp  PARAMS((CONST char *a, CONST char *b));
+extern int strncasecomp PARAMS((CONST char *a, CONST char *b, int n));
+
+/*
+
+Malloced string manipulation
+
+ */
+#define StrAllocCopy(dest, src) HTSACopy (&(dest), src)
+#define StrAllocCat(dest, src)  HTSACat  (&(dest), src)
+extern char * HTSACopy PARAMS ((char **dest, CONST char *src));
+extern char * HTSACat  PARAMS ((char **dest, CONST char *src));
+
+/*
+
+Next word or quoted string
+
+ */
+extern char * HTNextField PARAMS ((char** pstr));
+
+
+#endif
+/*
+
+   end
+   
+    */
diff --git a/WWW/Library/Implementation/HTStyle.c b/WWW/Library/Implementation/HTStyle.c
new file mode 100644
index 00000000..5b31e252
--- /dev/null
+++ b/WWW/Library/Implementation/HTStyle.c
@@ -0,0 +1,363 @@
+/*	Style Implementation for Hypertext			HTStyle.c
+**	==================================
+**
+**	Styles allow the translation between a logical property
+**	of a piece of text and its physical representation.
+**
+**	A StyleSheet is a collection of styles, defining the
+**	translation necessary to
+**	represent a document. It is a linked list of styles.
+*/
+#include "HTUtils.h"
+#include "HTStyle.h"
+
+#include "LYLeaks.h"
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+/*	Create a new style
+*/
+PUBLIC HTStyle* HTStyleNew NOARGS
+{
+    HTStyle * self = (HTStyle *)malloc(sizeof(*self));
+    memset((void *)self, 0, sizeof(*self));
+    self->font = (HTFont) 0;
+    self->color = 0;
+    return self;
+}
+
+/*	Create a new style with a name
+*/
+PUBLIC HTStyle* HTStyleNewNamed ARGS1 (CONST char *,name)
+{
+    HTStyle * self = HTStyleNew();
+    StrAllocCopy(self->name, name);
+    return self;
+}
+
+
+/*	Free a style
+*/
+PUBLIC HTStyle * HTStyleFree ARGS1 (HTStyle *,self)
+{
+    FREE(self->name);
+    FREE(self->SGMLTag);
+    FREE(self);
+    return NULL;
+}
+
+
+#ifdef SUPPRESS  /* Only on the NeXT */
+/*	Read a style from a stream	(without its name)
+**	--------------------------
+**
+**	Reads a style with paragraph information from a stream.
+**	The style name is not read or written by these routines.
+*/
+#define NONE_STRING "(None)"
+#define HTStream NXStream
+
+HTStyle * HTStyleRead (HTStyle * style, HTStream * stream)
+{
+    char myTag[STYLE_NAME_LENGTH];
+    char fontName[STYLE_NAME_LENGTH];
+    NXTextStyle *p;
+    int	tab;
+    int gotpara;		/* flag: have we got a paragraph definition? */
+	
+    NXScanf(stream, "%s%s%f%d",
+	myTag,
+	fontName,
+	&style->fontSize,
+	&gotpara);
+    if (gotpara) {
+	if (!style->paragraph) {
+	    style->paragraph = malloc(sizeof(*(style->paragraph)));
+	    style->paragraph->tabs = 0;
+	}
+	p = style->paragraph;
+	NXScanf(stream, "%f%f%f%f%hd%f%f%hd",
+	    &p->indent1st,
+	    &p->indent2nd,
+	    &p->lineHt,
+	    &p->descentLine,
+	    &p->alignment,
+	    &style->spaceBefore,
+	    &style->spaceAfter,
+	    &p->numTabs);
+	FREE(p->tabs);
+	p->tabs = malloc(p->numTabs * sizeof(p->tabs[0]));
+	for (tab=0; tab < p->numTabs; tab++) {
+	    NXScanf(stream, "%hd%f",
+		    &p->tabs[tab].kind,
+		    &p->tabs[tab].x);
+	}
+    } else { /* No paragraph */
+    	FREE(style->paragraph);
+    } /* if no paragraph */
+    StrAllocCopy(style->SGMLTag, myTag);
+    if (strcmp(fontName, NONE_STRING)==0)
+        style->font = 0;
+    else
+        style->font = [Font newFont:fontName size:style->fontSize];
+    return NULL;
+}
+
+
+/*	Write a style to a stream in a compatible way
+*/
+HTStyle * HTStyleWrite (HTStyle * style, NXStream * stream)
+{
+    int tab;
+    NXTextStyle *p = style->paragraph;
+    NXPrintf(stream, "%s %s %f %d\n",
+	style->SGMLTag,
+	style->font ? [style->font name] : NONE_STRING,
+	style->fontSize,
+	p!=0);
+
+    if (p) {
+	NXPrintf(stream, "\t%f %f %f %f %d %f %f\t%d\n",
+	    p->indent1st,
+	    p->indent2nd,
+	    p->lineHt,
+	    p->descentLine,
+	    p->alignment,
+	    style->spaceBefore,
+	    style->spaceAfter,
+	    p->numTabs);
+	    
+	for (tab=0; tab < p->numTabs; tab++)
+	    NXPrintf(stream, "\t%d %f\n",
+		    p->tabs[tab].kind,
+		    p->tabs[tab].x);
+	}
+    return style;
+}
+
+
+/*	Write a style to stdout for diagnostics
+*/
+HTStyle * HTStyleDump (HTStyle * style)
+{
+    int tab;
+    NXTextStyle *p = style->paragraph;
+    printf("Style %d `%s' SGML:%s. Font %s %.1f point.\n",
+    	style,
+	style->name,
+	style->SGMLTag,
+	[style->font name],
+	style->fontSize);
+    if (p) {
+        printf(
+    	"\tIndents: first=%.0f others=%.0f, Height=%.1f Desc=%.1f\n"
+	"\tAlign=%d, %d tabs. (%.0f before, %.0f after)\n",
+	    p->indent1st,
+	    p->indent2nd,
+	    p->lineHt,
+	    p->descentLine,
+	    p->alignment,
+	    p->numTabs,
+	    style->spaceBefore,
+	    style->spaceAfter);
+	    
+	for (tab=0; tab < p->numTabs; tab++) {
+	    printf("\t\tTab kind=%d at %.0f\n",
+		    p->tabs[tab].kind,
+		    p->tabs[tab].x);
+    	}
+	printf("\n");
+    } /* if paragraph */
+    return style;
+}
+#endif /* SUPPRESS */
+
+
+/*			StyleSheet Functions
+**			====================
+*/
+
+/*	Searching for styles:
+*/
+HTStyle * HTStyleNamed ARGS2 (HTStyleSheet *,self, CONST char *,name)
+{
+    HTStyle * scan;
+    for (scan=self->styles; scan; scan=scan->next)
+        if (0==strcmp(scan->name, name)) return scan;
+    if (TRACE) fprintf(stderr, "StyleSheet: No style named `%s'\n", name);
+    return NULL;
+}
+
+#ifdef NEXT_SUPRESS		/* Not in general common code */
+
+HTStyle * HTStyleMatching (HTStyleSheet * self, HTStyle *style)
+{
+    HTStyle * scan;
+    for (scan=self->styles; scan; scan=scan->next)
+        if (scan->paragraph == para) return scan;
+    return NULL;
+}
+
+/*	Find the style which best fits a given run
+**	------------------------------------------
+**
+**	This heuristic is used for guessing the style for a run of
+**	text which has been pasted in. In order, we try:
+**
+**	A style whose paragraph structure is actually used by the run.
+**	A style matching in font
+**	A style matching in paragraph style exactly
+**	A style matching in paragraph to a degree
+*/
+
+HTStyle * HTStyleForRun (HTStyleSheet *self, NXRun *run)
+{
+    HTStyle * scan;
+    HTStyle * best = 0;
+    int	bestMatch = 0;
+    NXTextStyle * rp = run->paraStyle;
+    for (scan=self->styles; scan; scan=scan->next)
+        if (scan->paragraph == run->paraStyle) return scan;	/* Exact */
+
+    for (scan=self->styles; scan; scan=scan->next){
+    	NXTextStyle * sp = scan->paragraph;
+    	if (sp) {
+	    int match = 0;
+	    if (sp->indent1st ==	rp->indent1st)	match = match+1;
+	    if (sp->indent2nd ==	rp->indent2nd)	match = match+2;
+	    if (sp->lineHt ==		rp->lineHt)	match = match+1;
+	    if (sp->numTabs ==		rp->numTabs)	match = match+1;
+	    if (sp->alignment ==	rp->alignment)	match = match+3;
+	    if (scan->font ==		run->font)	match = match+10;
+	    if (match>bestMatch) {
+		    best=scan;
+		    bestMatch=match;
+	    }
+	}
+    }
+    if (TRACE) fprintf(stderr, "HTStyleForRun: Best match for style is %d out of 18\n",
+    			 bestMatch);
+    return best;
+}
+#endif /* NEXT_SUPRESS */
+
+
+/*	Add a style to a sheet
+**	----------------------
+*/
+HTStyleSheet * HTStyleSheetAddStyle ARGS2
+  (HTStyleSheet *,self, HTStyle *,style)
+{
+    style->next = 0;		/* The style will go on the end */
+    if (!self->styles) {
+    	self->styles = style;
+    } else {
+    	HTStyle * scan;
+        for(scan=self->styles; scan->next; scan=scan->next); /* Find end */
+	scan->next=style;
+    }
+    return self;
+}
+
+
+/*	Remove the given object from a style sheet if it exists
+*/
+HTStyleSheet * HTStyleSheetRemoveStyle ARGS2
+  (HTStyleSheet *,self, HTStyle *,style)
+{
+    if (self->styles = style) {
+    	self->styles = style->next;
+	return self;
+    } else {
+    	HTStyle * scan;
+	for(scan = self->styles; scan; scan = scan->next) {
+	    if (scan->next = style) {
+	        scan->next = style->next;
+		return self;
+	    }
+	}
+    }
+    return NULL;
+}
+
+/*	Create new style sheet
+*/
+
+HTStyleSheet * HTStyleSheetNew NOARGS
+{
+    HTStyleSheet * self = (HTStyleSheet *)malloc(sizeof(*self));
+
+    memset((void*)self, 0, sizeof(*self));	/* ANSI */
+/* Harbison c ref man says (char*)self
+   but k&r ansii and abc books and Think_C say (void*) */
+    
+/*    bzero(self, sizeof(*self)); */		/* BSD */
+    return self;
+}
+
+
+/*	Free off a style sheet pointer
+*/
+HTStyleSheet * HTStyleSheetFree ARGS1 (HTStyleSheet *,self)
+{
+    HTStyle * style;
+    while((style=self->styles)!=0) {
+        self->styles = style->next;
+	HTStyleFree(style);
+    }
+    FREE(self);
+    return NULL;
+}
+
+
+/*	Read a stylesheet from a typed stream
+**	-------------------------------------
+**
+**	Reads a style sheet from a stream.  If new styles have the same names
+**	as existing styles, they replace the old ones without changing the ids.
+*/
+
+#ifdef NEXT_SUPRESS  /* Only on the NeXT */
+HTStyleSheet * HTStyleSheetRead(HTStyleSheet * self, NXStream * stream)
+{
+    int numStyles;
+    int i;
+    HTStyle * style;
+    char styleName[80];
+    NXScanf(stream, " %d ", &numStyles);
+    if (TRACE) fprintf(stderr, "Stylesheet: Reading %d styles\n", numStyles);
+    for (i=0; i<numStyles; i++) {
+        NXScanf(stream, "%s", styleName);
+        style = HTStyleNamed(self, styleName);
+	if (!style) {
+	    style = HTStyleNewNamed(styleName);
+	    (void) HTStyleSheetAddStyle(self, style);
+	}
+	(void) HTStyleRead(style, stream);
+	if (TRACE) HTStyleDump(style);
+    }
+    return self;
+}
+
+/*	Write a stylesheet to a typed stream
+**	------------------------------------
+**
+**	Writes a style sheet to a stream.
+*/
+
+HTStyleSheet * HTStyleSheetWrite(HTStyleSheet * self, NXStream * stream)
+{
+    int numStyles = 0;
+    HTStyle * style;
+    
+    for(style=self->styles; style; style=style->next) numStyles++;
+    NXPrintf(stream, "%d\n", numStyles);
+    
+    if (TRACE) fprintf(stderr, "StyleSheet: Writing %d styles\n", numStyles);
+    for (style=self->styles; style; style=style->next) {
+        NXPrintf(stream, "%s ", style->name);
+	(void) HTStyleWrite(style, stream);
+    }
+    return self;
+}
+#endif /* NEXT_SUPRESS */
diff --git a/WWW/Library/Implementation/HTStyle.h b/WWW/Library/Implementation/HTStyle.h
new file mode 100644
index 00000000..100162d2
--- /dev/null
+++ b/WWW/Library/Implementation/HTStyle.h
@@ -0,0 +1,183 @@
+/*                                                       HTStyle: Style management for libwww
+                              STYLE DEFINITION FOR HYPERTEXT
+                                             
+   Styles allow the translation between a logical property of a piece of text and its
+   physical representation.
+   
+   A StyleSheet is a collection of styles, defining the translation necessary to represent
+   a document. It is a linked list of styles.
+   
+Overriding this module
+
+   Why is the style structure declared in the HTStyle.h module, instead of having the user
+   browser define the structure, and the HTStyle routines just use sizeof() for copying?
+   
+   It's not obvious whether HTStyle.c should be common code.  It's useful to have common
+   code for loading style sheets, especially if the movement toward standard style sheets
+   gets going.
+   
+   If it IS common code, then both the hypertext object and HTStyle.c must know the
+   structure of a style, so HTStyle.h is a suitable place to put that.  HTStyle.c has to
+   be compiled with a knowledge of the
+   
+   It we take it out of the library, then of course HTStyle could be declared as an
+   undefined structure. The only references to it are in the structure-flattening code
+   HTML.c and HTPlain.c, which only use HTStypeNamed().
+   
+   You can in any case override this function in your own code, which will prevent the
+   HTStyle from being loaded.  You will be able to redefine your style structure in this
+   case without problems, as no other moule needs to know it.
+   
+ */
+#ifndef HTStyle_H
+#define HTStyle_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+#include "HTAnchor.h"
+
+typedef long int HTFont;        /* Dummy definition instead */
+
+#ifdef SHORT_NAMES
+#define HTStyleNew                      HTStNew
+#define HTStyleFree                     HTStFree
+#define HTStyleRead                     HTStRead
+#define HTStyleWrite                    HTStWrite
+#define HTStyleSheetNew                 HTStShNe
+#define HTStyleSheetFree                HTStShFr
+#define HTStyleNamed                    HTStName
+#define HTStyleForParagraph             HTStFoPa
+#define HTStyleMatching                 HTStMatc
+#define HTStyleForRun                   HTStFoRu
+#define HTStyleSheetAddStyle            HTStShAd
+#define HTStyleSheetRemoveStyle         HTStShRm
+#define HTStyleSheetRead                HTStShRe
+#define HTStyleSheetWrite               HTStShWr
+#endif
+
+#ifdef NeXT_suppressed
+#include <appkit/appkit.h>
+typedef NXCoord HTCoord;
+#define HTParagraphStyle NXTextStyle
+#define HTCoord NXCoord
+typedef struct _color {
+        float   grey;
+        int     RGBColor;
+} HTColor;
+#else
+
+typedef float HTCoord;
+
+typedef struct _HTParagraphStyle {
+    HTCoord     left_indent;            /* @@@@ junk! etc etc*/
+} HTParagraphStyle;
+
+typedef int HTColor;            /* Sorry about the US spelling! */
+
+#endif
+
+
+
+#define STYLE_NAME_LENGTH       80      /* @@@@@@@@@@@ */
+        
+typedef struct {
+    short               kind;           /* only NX_LEFTTAB implemented*/
+    HTCoord             position;       /* x coordinate for stop */
+} HTTabStop;
+
+
+/*      The Style Structure
+**      -------------------
+*/
+
+typedef struct _HTStyle {
+
+/*      Style management information
+*/
+    struct _HTStyle     *next;          /* Link for putting into stylesheet */
+    char *              name;           /* Style name */
+    char *              SGMLTag;        /* Tag name to start */
+
+
+/*      Character attributes    (a la NXRun)
+*/
+    HTFont              font;           /* Font id */
+    HTCoord             fontSize;       /* The size of font, not independent */
+    HTColor             color;  /* text gray of current run */
+    int                 superscript;    /* superscript (-sub) in points */
+
+    HTAnchor            *anchor;        /* Anchor id if any, else zero */
+
+/*      Paragraph Attribtes     (a la NXTextStyle)
+*/
+    HTCoord             indent1st;      /* how far first line in paragraph is
+                                 * indented */
+    HTCoord             leftIndent;     /* how far second line is indented */
+    HTCoord             rightIndent;    /* (Missing from NeXT version */
+    short               alignment;      /* quad justification */
+    HTCoord             lineHt;         /* line height */
+    HTCoord             descentLine;    /* descender bottom from baseline */
+    HTTabStop           *tabs;          /* array of tab stops, 0 terminated */
+
+    BOOL                wordWrap;       /* Yes means wrap at space not char */
+    BOOL                freeFormat;     /* Yes means \n is just white space */
+    HTCoord             spaceBefore;    /* Omissions from NXTextStyle */
+    HTCoord             spaceAfter;
+    int                 paraFlags;      /* Paragraph flags, bits as follows: */
+
+#define PARA_KEEP       1       /* Do not break page within this paragraph */
+#define PARA_WITH_NEXT  2       /* Do not break page after this paragraph */
+
+#define HT_JUSTIFY 0            /* For alignment */
+#define HT_LEFT 1
+#define HT_RIGHT 2
+#define HT_CENTER 3
+
+} HTStyle;
+
+
+/*      Style functions:
+*/
+extern HTStyle * HTStyleNew NOPARAMS;
+extern HTStyle* HTStyleNewNamed PARAMS ((CONST char * name));
+extern HTStyle * HTStyleFree PARAMS((HTStyle * self));
+#ifdef SUPRESS
+extern HTStyle * HTStyleRead PARAMS((HTStyle * self, HTStream * stream));
+extern HTStyle * HTStyleWrite PARAMS((HTStyle * self, HTStream * stream));
+#endif
+/*              Style Sheet
+**              -----------
+*/
+typedef struct _HTStyleSheet {
+        char *          name;
+        HTStyle *       styles;
+} HTStyleSheet;
+
+
+/*      Stylesheet functions:
+*/
+extern HTStyleSheet * HTStyleSheetNew NOPARAMS;
+extern HTStyleSheet * HTStyleSheetFree PARAMS((HTStyleSheet * self));
+extern HTStyle * HTStyleNamed PARAMS((HTStyleSheet * self, CONST char * name));
+extern HTStyle * HTStyleForParagraph PARAMS((HTStyleSheet * self,
+        HTParagraphStyle * paraStyle));
+extern HTStyle * HTStyleMatching PARAMS((HTStyleSheet *self, HTStyle * style));
+/* extern HTStyle * HTStyleForRun PARAMS((HTStyleSheet *self, NXRun * run)); */
+extern HTStyleSheet * HTStyleSheetAddStyle PARAMS((HTStyleSheet * self,
+        HTStyle * style));
+extern HTStyleSheet * HTStyleSheetRemoveStyle PARAMS((HTStyleSheet * self,
+        HTStyle * style));
+#ifdef SUPPRESS
+extern HTStyleSheet * HTStyleSheetRead PARAMS((HTStyleSheet * self,
+                                                HTStream * stream));
+extern HTStyleSheet * HTStyleSheetWrite PARAMS((HTStyleSheet * self,
+                                                HTStream * stream));
+#endif
+#define CLEAR_POINTER ((void *)-1)      /* Pointer value means "clear me" */
+#endif /* HTStyle_H */
+
+
+/*
+
+    */
diff --git a/WWW/Library/Implementation/HTTCP.c b/WWW/Library/Implementation/HTTCP.c
new file mode 100644
index 00000000..45d8a979
--- /dev/null
+++ b/WWW/Library/Implementation/HTTCP.c
@@ -0,0 +1,955 @@
+/*			Generic Communication Code		HTTCP.c
+**			==========================
+**
+**	This code is in common between client and server sides.
+**
+**	16 Jan 92  TBL	Fix strtol() undefined on CMU Mach.
+**	25 Jun 92  JFG  Added DECNET option through TCP socket emulation.
+**	13 Sep 93  MD   Added correct return of vmserrorno for HTInetStatus.
+**			Added decoding of vms error message for MULTINET.
+**      7-DEC-1993 Bjorn S. Nilsson, ALEPH, CERN, VMS UCX ioctl() changes
+**			(done of Mosaic)
+**      19 Feb 94  Danny Mayer	Added Bjorn Fixes to Lynx version
+**	 7 Mar 94  Danny Mayer  Added Fix UCX version for full domain name
+**	20 May 94  Andy Harper  Added support for CMU TCP/IP transport
+**	17 Nov 94  Andy Harper  Added support for SOCKETSHR transport
+**      16 Jul 95  S. Bjorndahl added kluge to deal with LIBCMU bug
+*/
+
+
+#include "HTUtils.h"
+#include "tcp.h"		/* Defines SHORT_NAMES if necessary */
+#include "HTAccess.h"
+#include "HTParse.h"
+#include "HTAlert.h"
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+extern int HTCheckForInterrupt NOPARAMS;
+
+#ifdef SVR4_BSDSELECT
+PUBLIC int BSDselect PARAMS((int nfds, fd_set * readfds, fd_set * writefds,
+	 		     fd_set * exceptfds, struct timeval * timeout));
+#ifdef select
+#undef select
+#endif /* select */
+#define select BSDselect
+#ifdef SOCKS
+#ifdef Rselect
+#undef Rselect
+#endif /* Rselect */
+#define Rselect BSDselect
+#endif /* SOCKS */
+#endif /* SVR4_BSDSELECT */
+
+#include "LYLeaks.h"
+
+#ifdef SHORT_NAMES
+#define HTInetStatus		HTInStat
+#define HTInetString 		HTInStri
+#define HTParseInet		HTPaInet
+#endif
+
+#ifndef FD_SETSIZE
+#if defined(UCX) || defined(SOCKETSHR_TCP) || defined(CMU_TCP)
+#define FD_SETSIZE 32
+#else
+#define FD_SETSIZE 256
+#endif /* Limit # sockets to 32 for UCX, BSN - also SOCKETSHR and CMU, AH */
+#endif
+
+/*	Module-Wide variables
+*/
+
+PRIVATE char *hostname = NULL;		/* The name of this host */
+
+
+/*	PUBLIC VARIABLES
+*/
+
+#ifdef SOCKS
+extern BOOLEAN socks_flag;
+PUBLIC unsigned long socks_bind_remoteAddr; /* for long Rbind */
+#endif /* SOCKS */
+
+/* PUBLIC SockA HTHostAddress; */	/* The internet address of the host */
+					/* Valid after call to HTHostName() */
+
+/*	Encode INET status (as in sys/errno.h)			  inet_status()
+**	------------------
+**
+** On entry,
+**	where		gives a description of what caused the error
+**	global errno	gives the error number in the unix way.
+**
+** On return,
+**	returns		a negative status in the unix way.
+*/
+#ifndef PCNFS
+
+#ifdef VMS
+#include <perror.h>
+#ifndef errno
+extern int errno;
+#endif /* !errno */
+#endif /* VMS */
+
+#ifndef VM
+#ifndef VMS
+#ifndef NeXT
+#ifndef THINK_C
+#ifndef __NetBSD__
+#ifndef __FreeBSD__
+#ifndef BSDI
+extern char *sys_errlist[];		/* see man perror on cernvax */
+extern int sys_nerr;
+#endif /* BSDI */
+#endif /* !__FreeBSD__ */
+#endif /* !__NetBSD__ */
+#endif /* !THINK_C */
+#endif /* !NeXT */
+#endif /* !VMS */
+#endif /* !VM */
+
+#endif	/* !PCNFS */
+
+#if defined(VMS) && defined(UCX)
+/*
+ * A routine to mimick the ioctl function for UCX.
+ * Bjorn S. Nilsson, 25-Nov-1993. Based on an example in the UCX manual.
+ */
+#include <iodef.h>
+#define IOC_OUT (int)0x40000000
+extern int vaxc$get_sdc(), sys$qiow();
+
+PUBLIC int HTioctl ARGS3
+	(int,		d, 
+	int,		request,
+	int *,		argp)
+{
+  int sdc, status;
+  unsigned short fun, iosb[4];
+  char *p5, *p6;
+  struct comm
+    {
+      int command;
+      char *addr;
+    } ioctl_comm;
+  struct it2
+    {
+      unsigned short len;
+      unsigned short opt;
+      struct comm *addr;
+    } ioctl_desc;
+  if ((sdc = vaxc$get_sdc (d)) == 0)
+    {
+      errno = EBADF;
+      return -1;
+    }
+  ioctl_desc.opt  = UCX$C_IOCTL;
+  ioctl_desc.len  = sizeof(struct comm);
+  ioctl_desc.addr = &ioctl_comm;
+  if (request & IOC_OUT)
+    {
+      fun = IO$_SENSEMODE;
+      p5 = 0;
+      p6 = (char *)&ioctl_desc;
+    }
+  else
+    {
+      fun = IO$_SETMODE;
+      p5 = (char *)&ioctl_desc;
+      p6 = 0;
+    }
+  ioctl_comm.command = request;
+  ioctl_comm.addr = (char *)argp;
+  status = sys$qiow (0, sdc, fun, iosb, 0, 0,
+    0, 0, 0, 0, p5, p6);
+  if (!(status & 01))
+    {
+      errno = status;
+      return -1;
+    }
+  if (!(iosb[0] & 01))
+    {
+      errno = iosb[0];
+      return -1;
+    }
+  return 0;
+}
+#endif /* VMS && UCX */
+
+
+/*	Report Internet Error
+**	---------------------
+*/
+PUBLIC int HTInetStatus ARGS1(
+	char *,		where)
+{
+#ifdef VMS
+#ifdef MULTINET
+            SOCKET_ERRNO = vmserrno;
+#endif /* MULTINET */
+#endif /* VMS */
+
+    CTRACE(tfp,
+    	"TCP: Error %d in `SOCKET_ERRNO' after call to %s() failed.\n\t%s\n",
+	SOCKET_ERRNO,  where, /* third arg is transport/platform specific */
+
+#ifdef VM
+	    "(Error number not translated)");	/* What Is the VM equiv? */
+#define ER_NO_TRANS_DONE
+#endif /* VM */
+
+#ifdef VMS
+#ifdef MULTINET
+            vms_errno_string());
+#else
+	    ((SOCKET_ERRNO > 0 && SOCKET_ERRNO <= 65) ?
+	     strerror(SOCKET_ERRNO) : "(Error number not translated)"));
+#endif /* MULTINET */
+#define ER_NO_TRANS_DONE
+#endif /* VMS */
+
+#if defined(NeXT) || defined(THINK_C)
+	    strerror(SOCKET_ERRNO));
+#define ER_NO_TRANS_DONE
+#endif /* NeXT || THINK_C */
+
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(BSDI)
+	    strerror(SOCKET_ERRNO));
+#define ER_NO_TRANS_DONE
+#endif /* __NetBSD__ || __FreeBSD__ || BSDI */
+
+#ifndef ER_NO_TRANS_DONE
+	    (SOCKET_ERRNO < sys_nerr ?
+	     sys_errlist[SOCKET_ERRNO] : "Unknown error" ));
+#endif /* !ER_NO_TRANS_DONE */
+
+#ifdef VMS
+#ifndef MULTINET
+    CTRACE(tfp,
+    	"         Unix error number (SOCKET_ERRNO) = %ld dec\n", SOCKET_ERRNO);
+    CTRACE(tfp,
+    	"         VMS error (vaxc$errno)    = %lx hex\n", vaxc$errno);
+#endif /* MULTINET */
+#endif /* VMS */
+
+#ifdef VMS
+    /* uerrno and errno happen to be zero if vmserrno <> 0 */
+#ifdef MULTINET
+    return -vmserrno;
+#else
+    return -vaxc$errno;
+#endif /* MULTINET */
+#else
+    return -SOCKET_ERRNO;
+#endif /* VMS */
+}
+
+
+/*	Parse a cardinal value				       parse_cardinal()
+**	----------------------
+**
+** On entry,
+**	*pp	    points to first character to be interpreted, terminated by
+**		    non 0:9 character.
+**	*pstatus    points to status already valid
+**	maxvalue    gives the largest allowable value.
+**
+** On exit,
+**	*pp	    points to first unread character
+**	*pstatus    points to status updated iff bad
+*/
+
+PUBLIC unsigned int HTCardinal ARGS3
+	(int *,		pstatus,
+	char **,	pp,
+	unsigned int,	max_value)
+{
+    int   n;
+    if ( (**pp<'0') || (**pp>'9')) {	    /* Null string is error */
+	*pstatus = -3;  /* No number where one expeceted */
+	return 0;
+    }
+
+    n=0;
+    while ((**pp>='0') && (**pp<='9')) n = n*10 + *((*pp)++) - '0';
+
+    if (n>max_value) {
+	*pstatus = -4;  /* Cardinal outside range */
+	return 0;
+    }
+
+    return n;
+}
+
+
+#ifndef DECNET  /* Function only used below for a trace message */
+
+/*	Produce a string for an Internet address
+**	----------------------------------------
+**
+** On exit,
+**	returns	a pointer to a static string which must be copied if
+**		it is to be kept.
+*/
+
+PUBLIC CONST char * HTInetString ARGS1(SockA*,sin)
+{
+    static char string[16];
+    sprintf(string, "%d.%d.%d.%d",
+	    (int)*((unsigned char *)(&sin->sin_addr)+0),
+	    (int)*((unsigned char *)(&sin->sin_addr)+1),
+	    (int)*((unsigned char *)(&sin->sin_addr)+2),
+	    (int)*((unsigned char *)(&sin->sin_addr)+3));
+    return string;
+}
+#endif /* Decnet */
+
+
+/*	Parse a network node address and port
+**	-------------------------------------
+**
+** On entry,
+**	str	points to a string with a node name or number,
+**		with optional trailing colon and port number.
+**	sin	points to the binary internet or decnet address field.
+**
+** On exit,
+**	*sin	is filled in. If no port is specified in str, that
+**		field is left unchanged in *sin.
+*/
+PUBLIC int HTParseInet ARGS2(
+	SockA *,	sin,
+	CONST char *,	str)
+{
+    char *port;
+    char *host = NULL;
+    struct hostent  *phost;	/* Pointer to host - See netdb.h */
+
+    if (!str) {
+    	if (TRACE)
+	    fprintf(stderr, 
+		    "HTParseInet: Can't parse `NULL'.\n");
+	return -1;
+    }
+    StrAllocCopy(host, str);	/* Make a copy we can mutilate */
+
+
+/*	Parse port number if present
+*/    
+    if ((port = strchr(host, ':')) != NULL) {
+    	*port++ = 0;		/* Chop off port */
+        if (port[0] >= '0' && port[0] <= '9') {
+
+#ifdef unix
+	    sin->sin_port = htons(atol(port));
+#else /* VMS */
+#ifdef DECNET
+	    sin->sdn_objnum = (unsigned char) (strtol(port, (char**)0 , 10));
+#else
+	    sin->sin_port = htons((unsigned short) strtol(port,(char**)0,10));
+#endif /* Decnet */
+#endif /* Unix vs. VMS */
+
+	} else {
+
+#ifdef SUPPRESS		/* 1. crashes!?!.  2. Not recommended */
+	    struct servent * serv = getservbyname(port, (char*)0);
+	    if (serv) sin->sin_port = serv->s_port;
+	    else if (TRACE) fprintf(stderr, "TCP: Unknown service %s\n", port);
+#endif
+	}
+      }
+
+#ifdef DECNET
+    /* read Decnet node name. @@ Should know about DECnet addresses, but it's
+       probably worth waiting until the Phase transition from IV to V. */
+
+    sin->sdn_nam.n_len = min(DN_MAXNAML, strlen(host));  /* <=6 in phase 4 */
+    strncpy (sin->sdn_nam.n_name, host, sin->sdn_nam.n_len + 1);
+
+    if (TRACE)
+        fprintf(stderr,  
+		"DECnet: Parsed address as object number %d on host %.6s...\n",
+		sin->sdn_objnum, host);
+
+#else  /* parse Internet host */
+
+/*	Parse host number if present.
+*/  
+    if (*host >= '0' && *host <= '9') {   /* Numeric node address: */
+#ifdef DGUX_OLD
+	sin->sin_addr.s_addr = inet_addr(host).s_addr;	/* See arpa/inet.h */
+#else
+	sin->sin_addr.s_addr = inet_addr(host);		/* See arpa/inet.h */
+#endif /* DGUX_OLD */
+	FREE(host);
+
+    } else {		    /* Alphanumeric node name: */
+#ifdef MVS	/* Oustanding problem with crash in MVS gethostbyname */
+	if(TRACE)
+	    fprintf(stderr, "HTTCP: Calling gethostbyname(%s)\n", host);
+#endif
+	phost=gethostbyname(host);	/* See netdb.h */
+#ifdef MVS
+	if(TRACE)
+	    fprintf(stderr, "HTTCP: gethostbyname() returned %d\n", phost);
+#endif
+	if (!phost) {
+	    if (TRACE)
+	        fprintf(stderr, 
+		    "HTTPAccess: Can't find internet node name `%s'.\n",host);
+	    FREE(host);
+	    return -1;  /* Fail? */
+	}
+	FREE(host);
+#if defined(VMS) && defined(CMU_TCP)
+ /*  In LIBCMU, phost->h_length contains not the length of one address
+  *  (four bytes) but the number of bytes in *h_addr, i.e. some multiple
+  *  of four. Thus we need to hard code the value here, and remember to
+  *  change it if/when IP addresses change in size. :-(  LIBCMU is no
+  *  longer supported, and CMU users are encouraged to obtain and use
+  *  SOCKETSHR/NETLIB instead.
+  *  S. Bjorndahl 
+  */
+	memcpy((void *)&sin->sin_addr, phost->h_addr, 4);
+#else
+	memcpy((void *)&sin->sin_addr, phost->h_addr, phost->h_length);
+#endif /* VMS && CMU_TCP */
+    }
+
+    if (TRACE)
+        fprintf(stderr,  
+		"TCP: Parsed address as port %d, IP address %d.%d.%d.%d\n",
+		(int)ntohs(sin->sin_port),
+		(int)*((unsigned char *)(&sin->sin_addr)+0),
+		(int)*((unsigned char *)(&sin->sin_addr)+1),
+		(int)*((unsigned char *)(&sin->sin_addr)+2),
+		(int)*((unsigned char *)(&sin->sin_addr)+3));
+
+#endif  /* Internet vs. Decnet */
+
+    return 0;	/* OK */
+}
+
+
+/*	Free our name for the host on which we are - FM
+**	-------------------------------------------
+**
+*/
+PRIVATE void free_HTTCP_hostname NOARGS
+{
+    FREE(hostname);
+}
+
+/*	Derive the name of the host on which we are
+**	-------------------------------------------
+**
+*/
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64		/* Arbitrary limit */
+#endif /* MAXHOSTNAMELEN */
+
+PRIVATE void get_host_details NOARGS
+{
+    char name[MAXHOSTNAMELEN+1];	/* The name of this host */
+#ifdef UCX
+    char *domain_name;			/* The name of this host domain */
+#endif
+#ifdef NEED_HOST_ADDRESS		/* no -- needs name server! */
+    struct hostent * phost;		/* Pointer to host -- See netdb.h */
+#endif
+    int namelength = sizeof(name);
+    
+    if (hostname)
+    	return;				/* Already done */
+    gethostname(name, namelength);	/* Without domain */
+    StrAllocCopy(hostname, name);
+    atexit(free_HTTCP_hostname);
+#ifdef UCX
+    /*  UCX doesn't give the complete domain name. get rest from UCX$BIND_DOM
+    **  Logical
+    */
+    if(strchr(hostname,'.') == NULL) {           /* Not full address */
+        domain_name = getenv("UCX$BIND_DOMAIN");
+        if(domain_name != NULL) {
+            StrAllocCat(hostname, ".");
+            StrAllocCat(hostname, domain_name);
+        }
+     }
+#endif /* UCX */
+    CTRACE(tfp, "TCP: Local host name is %s\n", hostname);
+
+#ifndef DECNET  /* Decnet ain't got no damn name server 8#OO */
+#ifdef NEED_HOST_ADDRESS		 /* no -- needs name server! */
+    phost=gethostbyname(name);		 /* See netdb.h */
+    if (!phost) {
+	if (TRACE) fprintf(stderr, 
+		"TCP: Can't find my own internet node address for `%s'!!\n",
+		name);
+	return;  /* Fail! */
+    }
+    StrAllocCopy(hostname, phost->h_name);
+    memcpy(&HTHostAddress, &phost->h_addr, phost->h_length);
+    if (TRACE) fprintf(stderr, "     Name server says that I am `%s' = %s\n",
+	    hostname, HTInetString(&HTHostAddress));
+#endif /* NEED_HOST_ADDRESS */
+
+#endif /* !DECNET */
+}
+
+PUBLIC CONST char * HTHostName NOARGS
+{
+    get_host_details();
+    return hostname;
+}
+
+/*
+ * interruptable connect as implemented by Marc Andreesen and
+ * hacked in by Lou Montulli
+ */ 
+
+PUBLIC int HTDoConnect ARGS4(
+	CONST char *,	url,
+	char *,		protocol,
+	int,		default_port, 
+	int *,		s)
+{
+  struct sockaddr_in soc_address;
+  struct sockaddr_in *sin = &soc_address;
+  int status;
+  char *line = NULL;
+
+  /* Set up defaults: */
+  sin->sin_family = AF_INET;
+  sin->sin_port = htons(default_port);
+
+  /* Get node name and optional port number: */
+  {
+    char *p1 = HTParse(url, "", PARSE_HOST);
+    char *at_sign;
+    char *host = NULL;
+    int status;
+
+    /* if there's an @ then use the stuff after it as a hostname */
+    if((at_sign = strchr(p1,'@')) != NULL)
+	StrAllocCopy(host, at_sign+1);
+    else
+	StrAllocCopy(host, p1);
+
+    line = (char *)malloc(strlen(host) + strlen(protocol) + 128);
+    if (line == NULL)
+        outofmem(__FILE__, "HTDoConnect");
+    sprintf (line, "Looking up %s.", host);
+    _HTProgress (line);
+
+    status = HTParseInet(sin, host);
+    if (status)
+      {
+        sprintf (line, "Unable to locate remote host %s.", host);
+        _HTProgress(line);
+        FREE(p1);
+	FREE(host);
+	FREE(line);
+        return HT_NO_DATA;
+      }
+
+    sprintf (line, "Making %s connection to %s.", protocol, host);
+    _HTProgress (line);
+    FREE(p1);
+    FREE(host);
+  }
+
+  /* Now, let's get a socket set up from the server for the data: */
+  *s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+   
+   if (*s == -1)
+     {
+	HTAlert("socket failed.");
+	FREE(line);
+	return HT_NO_DATA;
+     }
+   
+  /*
+   * Make the socket non-blocking, so the connect can be canceled.
+   * This means that when we issue the connect we should NOT
+   * have to wait for the accept on the other end.
+   */
+#if !defined(NO_IOCTL)
+  {
+    int ret;
+    int val = 1;
+ 
+    ret = IOCTL(*s, FIONBIO, &val);
+    if (ret == -1)
+      {
+        sprintf (line, "Could not make connection non-blocking.");
+        _HTProgress(line);
+      }
+  }
+#endif /* not NO_IOCTL */
+#if defined(USE_FCNTL)
+  {
+    int ret;
+
+    ret = fcntl(*s, F_SETFL, O_NONBLOCK);
+    if (ret == -1)
+      {
+        sprintf (line, "Could not make connection non-blocking.");
+        _HTProgress(line);
+      }
+  }
+#endif /* USE_FCNTL */
+
+  /*
+   * Issue the connect.  Since the server can't do an instantaneous accept
+   * and we are non-blocking, this will almost certainly return a negative
+   * status.
+   */
+#ifdef SOCKS
+  if (socks_flag) {
+      status = Rconnect(*s, (struct sockaddr*)&soc_address,
+      			sizeof(soc_address));
+      socks_bind_remoteAddr = soc_address.sin_addr.s_addr; /* for long Rbind */
+  }
+  else
+#endif /* SOCKS */
+  status = connect(*s, (struct sockaddr*)&soc_address, sizeof(soc_address));
+
+  /*
+   * According to the Sun man page for connect:
+   *     EINPROGRESS         The socket is non-blocking and the  con-
+   *                         nection cannot be completed immediately.
+   *                         It is possible to select(2) for  comple-
+   *                         tion  by  selecting the socket for writ-
+   *                         ing.
+   * According to the Motorola SVR4 man page for connect:
+   *     EAGAIN              The socket is non-blocking and the  con-
+   *                         nection cannot be completed immediately.
+   *                         It is possible to select for  completion
+   *                         by  selecting  the  socket  for writing.
+   *                         However, this is only  possible  if  the
+   *                         socket  STREAMS  module  is  the topmost
+   *                         module on  the  protocol  stack  with  a
+   *                         write  service  procedure.  This will be
+   *                         the normal case.
+   */
+  if ((status < 0) &&
+      (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EAGAIN))
+    {
+      struct timeval timeout;
+      int ret;
+      int tries=0;
+
+      ret = 0;
+      while (ret <= 0)
+        {
+          fd_set writefds;
+
+	  /*
+	   *  Protect against an infinite loop.
+	   */
+	  if (tries++ >= 180000) {
+	      HTAlert("Connection failed for 180,000 tries.");
+	      FREE(line);
+ 	      return HT_NO_DATA;
+	  }
+
+	  timeout.tv_sec = 0;
+	  timeout.tv_usec = 100000;
+          FD_ZERO(&writefds);
+          FD_SET(*s, &writefds);
+#ifdef SOCKS
+	  if (socks_flag)
+              ret = Rselect(FD_SETSIZE, NULL,
+	      		    (void *)&writefds, NULL, &timeout);
+	  else
+#endif /* SOCKS */
+          ret = select(FD_SETSIZE, NULL, (void *)&writefds, NULL, &timeout);
+          /*
+           * Again according to the Sun and Motorola man pagse for connect:
+           *     EALREADY            The socket is non-blocking and a  previ-
+           *                         ous  connection attempt has not yet been
+           *                         completed.
+           * Thus if the SOCKET_ERRNO is NOT EALREADY we have a real error, and
+           * should break out here and return that error.
+           * Otherwise if it is EALREADY keep on trying to complete the
+           * connection.
+           */
+          if ((ret < 0) && (SOCKET_ERRNO != EALREADY))
+            {
+              status = ret;
+              break;
+            }
+          else if (ret > 0)
+            {
+              /*
+               * Extra check here for connection success, if we try to connect
+               * again, and get EISCONN, it means we have a successful
+               * connection.
+               */
+#ifdef SOCKS
+	      if (socks_flag)
+                  status = Rconnect(*s, (struct sockaddr*)&soc_address,
+                                    sizeof(soc_address));
+	      else
+#endif /* SOCKS */
+              status = connect(*s, (struct sockaddr*)&soc_address,
+                               sizeof(soc_address));
+#ifndef UCX
+              if ((status < 0)&&(SOCKET_ERRNO == EISCONN))
+#else
+/*
+ * A UCX feature: Instead of returning EISCONN UCX returns EADDRINUSE.
+ * Test for this status also.
+ */
+              if ((status < 0)&&((SOCKET_ERRNO == EISCONN) ||
+				 (SOCKET_ERRNO == EADDRINUSE)))
+#endif /* VMS, UCX, BSN */
+                {
+                  status = 0;
+                }
+
+	      if (status && (SOCKET_ERRNO == EALREADY))  /* new stuff LJM */
+		  ret=0; /* keep going */
+	      else
+                  break;
+            }
+          /*
+           * The select says we aren't ready yet.  Try to connect again to
+	   * make sure.  If we don't get EALREADY or EISCONN, something has
+	   * gone wrong.  Break out and report it.
+	   *
+           * For some reason, SVR4 returns EAGAIN here instead of EALREADY,
+           * even though the man page says it should be EALREADY.
+	   *
+	   * For some reason, UCX pre 3 apparently returns errno = 18242
+	   * instead the EALREADY or EISCONN values.
+           */
+          else
+            {
+#ifdef SOCKS
+	      if (socks_flag)
+                  status = Rconnect(*s, (struct sockaddr*)&soc_address,
+                                    sizeof(soc_address));
+	      else
+#endif /* SOCKS */
+              status = connect(*s, (struct sockaddr*)&soc_address,
+                                 sizeof(soc_address));
+              if ((status < 0) &&
+	          (SOCKET_ERRNO != EALREADY && SOCKET_ERRNO != EAGAIN) &&
+#ifdef UCX
+		  (SOCKET_ERRNO != 18242) &&
+#endif /* UCX */
+		  (SOCKET_ERRNO != EISCONN))
+                {
+                  break;
+                }
+            }
+          if(HTCheckForInterrupt())
+            {
+              if (TRACE)
+                fprintf (stderr, "*** INTERRUPTED in middle of connect.\n");
+              status = HT_INTERRUPTED;
+              SOCKET_ERRNO = EINTR;
+              break;
+            }
+        }
+    }
+
+  /*
+   * Make the socket blocking again on good connect
+   */
+  if (status >= 0)
+    {
+#if !defined(NO_IOCTL)
+      int ret;
+      int val = 0;
+
+      ret = IOCTL(*s, FIONBIO, &val);
+      if (ret == -1)
+        {
+          sprintf (line, "Could not restore socket to blocking.");
+          _HTProgress(line);
+        }
+#endif /* not NO_IOCTL */
+#if defined(USE_FCNTL)
+  {
+    int ret;
+
+    ret = fcntl(*s, F_SETFL, 0);
+    if (ret == -1)
+      {
+        sprintf (line, "Could not restore socket to blocking.");
+        _HTProgress(line);
+      }
+  }
+#endif /* USE_FCNTL */
+    }
+  /*
+   * Else the connect attempt failed or was interrupted.
+   * so close up the socket.
+   */
+  else
+    {
+        NETCLOSE(*s);
+    }
+
+  FREE(line);
+  return status;
+}
+
+/* This is so interruptible reads can be implemented cleanly. */
+PUBLIC int HTDoRead ARGS3(
+	int,		fildes,
+	void *,		buf,
+	unsigned,	nbyte)
+{
+  int ready, ret;
+  fd_set readfds;
+  struct timeval timeout;
+  int tries=0;
+#ifdef UCX
+  int nb;
+#endif /* UCX, BSN */
+
+  if (fildes <= 0)
+      return -1;
+
+  if (HTCheckForInterrupt())
+    {
+        SOCKET_ERRNO = EINTR;
+        return (HT_INTERRUPTED);
+    }
+
+#if !defined(NO_IOCTL)
+  ready = 0;
+#else
+  ready = 1;
+#endif /* bypass for NO_IOCTL */
+  while (!ready)
+    {
+	/*
+	 *  Protect against an infinite loop.
+	 */
+	if (tries++ >= 180000) {
+	    HTAlert("Socket read failed for 180,000 tries.");
+	    SOCKET_ERRNO = EINTR;
+	    return HT_INTERRUPTED;
+	}
+
+        timeout.tv_sec = 0;
+	timeout.tv_usec = 100000;
+        FD_ZERO(&readfds);
+        FD_SET(fildes, &readfds);
+#ifdef SOCKS
+	if (socks_flag)
+            ret = Rselect(FD_SETSIZE, (void *)&readfds, NULL, NULL, &timeout);
+	else
+#endif /* SOCKS */
+        ret = select(FD_SETSIZE, (void *)&readfds, NULL, NULL, &timeout);
+        if (ret < 0)
+          {
+                return -1;
+          }
+        else if (ret > 0)
+          {
+                ready = 1;
+          }
+        else if(HTCheckForInterrupt())
+          {
+       	        SOCKET_ERRNO = EINTR;
+                return HT_INTERRUPTED;
+          }
+    }
+
+#if !defined(UCX) || !defined(VAXC)
+  return SOCKET_READ (fildes, buf, nbyte);
+#else                           /* VAXC and UCX problem only */
+  errno = vaxc$errno = 0;
+  nb = SOCKET_READ (fildes, buf, nbyte);
+  CTRACE(tfp, "Read - nb,errno,vaxc$errno: %d %d %d\n", nb,errno,vaxc$errno);
+  if ((nb <= 0) && TRACE)
+     perror ("HTTCP.C:HTDoRead:read");          /* RJF */
+  /*
+   * An errno value of EPIPE and nb < 0 indicates end-of-file on VAXC
+   */
+  if ((nb <= 0) && (errno == EPIPE)) {
+       nb = 0;
+       errno = 0;
+  }
+  return nb;
+#endif /* UCX, BSN */
+}
+
+#ifdef SVR4_BSDSELECT
+/*
+ *  This is a fix for the difference between BSD's select() and
+ *  SVR4's select().  SVR4's select() can never return a value larger
+ *  than the total number of file descriptors being checked.  So, if
+ *  you select for read and write on one file descriptor, and both
+ *  are true, SVR4 select() will only return 1.  BSD select in the
+ *  same situation will return 2.
+ *
+ *	Additionally, BSD select() on timing out, will zero the masks,
+ *	while SVR4 does not.  This is fixed here as well.
+ *
+ *	Set your tabstops to 4 characters to have this code nicely formatted.
+ *
+ *	Jerry Whelan, guru@bradley.edu, June 12th, 1993
+ */
+#ifdef select
+#undef select
+#endif /* select */
+
+#ifdef SOCKS
+#ifdef Rselect
+#undef Rselect
+#endif /* Rselect */
+#endif /* SOCKS */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/select.h>
+
+
+PUBLIC int BSDselect ARGS5 (int,nfds, fd_set *,readfds,fd_set *,writefds,
+	 		    fd_set *,exceptfds, struct timeval *,timeout)
+{
+	int		rval,
+			i;
+
+#ifdef SOCKS
+	if (socks_flag)
+	    rval = Rselect(nfds, readfds, writefds, exceptfds, timeout);
+	else
+#endif /* SOCKS */
+	rval = select(nfds, readfds, writefds, exceptfds, timeout);
+
+	switch(rval) {
+		case -1:	return(rval);
+					break;
+
+		case 0:		if(readfds != NULL)
+						FD_ZERO(readfds);
+					if(writefds != NULL)
+						FD_ZERO(writefds);
+					if(exceptfds != NULL)
+						FD_ZERO(exceptfds);
+
+					return(rval);
+					break;
+
+		default:	for(i=0, rval=0; i < nfds; i++) {
+		if((readfds != NULL) && FD_ISSET(i, readfds)) rval++;
+		if((writefds != NULL) && FD_ISSET(i, writefds)) rval++;
+		if((exceptfds != NULL) && FD_ISSET(i, exceptfds)) rval++;
+
+					}
+					return(rval);
+		}
+/* Should never get here */
+}
+#endif /* SVR4_BSDSELECT */
diff --git a/WWW/Library/Implementation/HTTCP.h b/WWW/Library/Implementation/HTTCP.h
new file mode 100644
index 00000000..1187c459
--- /dev/null
+++ b/WWW/Library/Implementation/HTTCP.h
@@ -0,0 +1,118 @@
+/*                               /Net/dxcern/userd/timbl/hypertext/WWW/Library/src/HTTCP.html
+                               GENERIC TCP/IP COMMUNICATION
+                                             
+   This module has the common code for handling TCP/IP connections etc.
+   
+ */
+#ifndef HTTCP_H
+#define HTTCP_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+#include "tcp.h"
+
+#ifdef SHORT_NAMES
+#define HTInetStatus            HTInStat
+#define HTInetString            HTInStri
+#define HTParseInet             HTPaInet
+#endif
+
+
+/*      Produce a string for an internet address
+**      ---------------------------------------
+**
+** On exit:
+**           returns a pointer to a static string which must be copied if
+**                it is to be kept.
+*/
+#ifdef __STDC__
+        extern const char * HTInetString(struct sockaddr_in* sin);
+#else
+        extern char * HTInetString();
+#endif
+
+
+/*      Encode INET status (as in sys/errno.h)                    inet_status()
+**      ------------------
+**
+** On entry:
+**              where gives a description of what caused the error
+**      global errno gives the error number in the unix way.
+**
+** On return:
+**      returns a negative status in the unix way.
+*/
+#ifdef __STDC__
+        extern int HTInetStatus(char *where);
+#else
+        extern int HTInetStatus();
+#endif
+
+/*      Publicly accessible variables
+*/
+/* extern struct sockaddr_in HTHostAddress; */
+                        /* The internet address of the host */
+                        /* Valid after call to HTHostName() */
+
+
+/*      Parse a cardinal value                                 parse_cardinal()
+**      ----------------------
+**
+** On entry:
+**      *pp points to first character to be interpreted, terminated by
+**      non 0..9 character.
+**      *pstatus points to status already valid,
+**      maxvalue gives the largest allowable value.
+**
+** On exit:
+**      *pp points to first unread character,
+**      *pstatus points to status updated iff bad
+*/
+
+extern unsigned int HTCardinal PARAMS((int *pstatus,
+                char            **pp,
+                unsigned int    max_value));
+
+
+/*      Parse an internet node address and port
+**      ---------------------------------------
+**
+** On entry:
+**               str points to a string with a node name or number,
+**               with optional trailing colon and port number.
+**               sin points to the binary internet or decnet address field.
+**
+** On exit:
+**               *sin is filled in. If no port is specified in str, that
+**               field is left unchanged in *sin.
+*/
+#ifdef __STDC__
+        extern int HTParseInet(struct sockaddr_in * sin, CONST char * str);
+        /*!! had to change this to get it to compile. CTB */
+#else
+        extern int HTParseInet();
+#endif
+
+/*      Get Name of This Machine
+**      ------------------------
+**
+*/
+
+extern CONST char * HTHostName NOPARAMS;
+
+extern int HTDoConnect PARAMS((
+	CONST char *	url,
+	char *		protocol,
+	int		default_port,
+	int *		s));
+
+extern int HTDoRead PARAMS((
+	int 		fildes,
+	void *		buf,
+	unsigned 	nbyte));
+
+#endif   /* HTTCP_H */
+/*
+
+   End.  */
diff --git a/WWW/Library/Implementation/HTTP.c b/WWW/Library/Implementation/HTTP.c
new file mode 100644
index 00000000..10a484f6
--- /dev/null
+++ b/WWW/Library/Implementation/HTTP.c
@@ -0,0 +1,1309 @@
+/*	HyperText Tranfer Protocol	- Client implementation		HTTP.c
+**	==========================
+** Modified:
+** 27 Jan 1994  PDM  Added Ari Luotonen's Fix for Reload when using proxy
+**                   servers.
+*/
+
+#include "HTUtils.h"
+#include "tcp.h"
+
+#include "HTTP.h"
+
+#define HTTP_VERSION	"HTTP/1.0"
+
+#define HTTP_PORT   80
+#define HTTPS_PORT  443
+#define SNEWS_PORT  563
+
+#define HTTP_NETREAD   NETREAD
+#define HTTP_NETWRITE  NETWRITE
+
+#define INIT_LINE_SIZE		1024	/* Start with line buffer this big */
+#define LINE_EXTEND_THRESH	256	/* Minimum read size */
+#define VERSION_LENGTH 		20	/* for returned protocol version */
+
+#include "HTParse.h"
+#include "HTTCP.h"
+#include "HTFormat.h"
+#include "HTFile.h"
+#include <ctype.h>
+#include "HTAlert.h"
+#include "HTMIME.h"
+#include "HTML.h"
+#include "HTInit.h"
+#include "HTAABrow.h"
+
+#include "LYLeaks.h"
+
+/* #define TRACE 1 */
+
+struct _HTStream 
+{
+  HTStreamClass * isa;
+};
+
+extern char * HTAppName;	/* Application name: please supply */
+extern char * HTAppVersion;	/* Application version: please supply */
+extern char * personal_mail_address;	/* User's name/email address */
+extern char * LYUserAgent;	/* Lynx User-Agent string */
+extern BOOL LYNoRefererHeader;	/* Never send Referer header? */
+extern BOOL LYNoRefererForThis;	/* No Referer header for this URL? */
+extern BOOL LYNoFromHeader;	/* Never send From header? */
+
+extern BOOL using_proxy;	/* Are we using an HTTP gateway? */
+PUBLIC BOOL reloading = FALSE;	/* Reloading => send no-cache pragma to proxy */
+PUBLIC char * redirecting_url = NULL;	    /* Location: value. */
+PUBLIC BOOL permanent_redirection = FALSE;  /* Got 301 status? */
+PUBLIC BOOL redirect_post_content = FALSE;  /* Don't convert to GET? */
+
+extern char LYUserSpecifiedURL; /* Is the URL a goto? */
+
+extern BOOL keep_mime_headers;   /* Include mime headers and force source dump */
+extern BOOL no_url_redirection;  /* Don't follow Location: URL for */
+extern char *http_error_file;    /* Store HTTP status code in this file */
+extern BOOL traversal;		 /* TRUE if we are doing a traversal */
+extern BOOL dump_output_immediately;  /* TRUE if no interactive user */
+
+extern char * HTLoadedDocumentURL NOPARAMS;
+extern int HTCheckForInterrupt NOPARAMS;
+
+
+/*		Load Document from HTTP Server			HTLoadHTTP()
+**		==============================
+**
+**	Given a hypertext address, this routine loads a document.
+**
+**
+** On entry,
+**	arg	is the hypertext reference of the article to be loaded.
+**
+** On exit,
+**	returns	>=0	If no error, a good socket number
+**		<0	Error.
+**
+**	The socket must be closed by the caller after the document has been
+**	read.
+**
+*/
+PUBLIC int HTLoadHTTP ARGS4 (
+	CONST char *, 		arg,
+	HTParentAnchor *,	anAnchor,
+	HTFormat,		format_out,
+	HTStream*,		sink)
+{
+  int s;			/* Socket number for returned data */
+  char *url = (char *)arg;	/* The URL which get_physical() returned */
+  char *command=NULL;		/* The whole command */
+  char *eol;			/* End of line if found */
+  char *start_of_data;		/* Start of body of reply */
+  int status;			/* tcp return */
+  int bytes_already_read;
+  char crlf[3];			/* A CR LF equivalent string */
+  HTStream *target;		/* Unconverted data */
+  HTFormat format_in;		/* Format arriving in the message */
+  int do_head = 0;		/* Whether or not we should do a head */
+  int do_post = 0;		/* ARE WE posting ? */
+  char *METHOD;
+
+  BOOL had_header;		/* Have we had at least one header? */
+  char *line_buffer;
+  char *line_kept_clean;
+  BOOL extensions;		/* Assume good HTTP server */
+  int compressed;
+  char line[INIT_LINE_SIZE];
+  char temp[80];
+  BOOL first_Accept = TRUE;
+
+  int length, doing_redirect, rv;
+  int already_retrying = 0;
+  int len = 0;
+
+  if (anAnchor->isHEAD)
+      do_head = TRUE;
+  else if (anAnchor->post_data)
+      do_post = TRUE;
+
+  if (!url)
+    {
+      status = -3;
+      _HTProgress ("Bad request.");
+      goto done;
+    }
+  if (!*url) 
+    {
+      status = -2;
+      _HTProgress ("Bad request.");
+      goto done;
+    }
+
+  sprintf(crlf, "%c%c", CR, LF);
+
+  /* At this point, we're talking HTTP/1.0. */
+  extensions = YES;
+
+ try_again:
+  /* All initializations are moved down here from up above,
+     so we can start over here... */
+  eol = 0;
+  bytes_already_read = 0;
+  had_header = NO;
+  length = 0;
+  doing_redirect = 0;
+  permanent_redirection = FALSE;
+  redirect_post_content = FALSE;
+  compressed = 0;
+  target = NULL;
+  line_buffer = NULL;
+  line_kept_clean = NULL;
+
+  if (!strncmp(url, "https", 5))
+    {
+      HTAlert("This client does not contain support for HTTPS URLs.");
+      status = HT_NO_DATA;
+      goto done;
+    }
+  status = HTDoConnect (arg, "HTTP", HTTP_PORT, &s);
+  if (status == HT_INTERRUPTED)
+    {
+      /* Interrupt cleanly. */
+      if (TRACE)
+          fprintf (stderr,
+                   "HTTP: Interrupted on connect; recovering cleanly.\n");
+      _HTProgress ("Connection interrupted.");
+      /* status already == HT_INTERRUPTED */
+      goto done;
+    }
+  if (status < 0) 
+    {
+      if (TRACE) 
+          fprintf(stderr, 
+            "HTTP: Unable to connect to remote host for `%s' (errno = %d).\n",
+	    url, SOCKET_ERRNO);
+      HTAlert("Unable to connect to remote host.");
+      status = HT_NO_DATA;
+      goto done;
+    }
+
+  /*	Ask that node for the document,
+  **	omitting the host name & anchor
+  */
+  {
+    char * p1 = (HTParse(url, "", PARSE_PATH|PARSE_PUNCTUATION));
+
+    if (do_post) {
+        METHOD = "POST";
+        StrAllocCopy(command, "POST ");
+    } else if (do_head) {
+        METHOD = "HEAD";
+        StrAllocCopy(command, "HEAD ");
+    } else {
+        METHOD = "GET";
+        StrAllocCopy(command, "GET ");
+    }
+
+    /* if we are using a proxy gateway don't copy in the first slash
+     * of say: /gopher://a;lkdjfl;ajdf;lkj/;aldk/adflj
+     * so that just gopher://.... is sent.
+     */
+    if (using_proxy)
+        StrAllocCat(command, p1+1);
+    else
+        StrAllocCat(command, p1);
+    FREE(p1);
+  }
+  if (extensions) 
+    {
+      StrAllocCat(command, " ");
+      StrAllocCat(command, HTTP_VERSION);
+    }
+
+  StrAllocCat(command, crlf);	/* CR LF, as in rfc 977 */
+
+  if (extensions) 
+    {
+      int n, i;
+      char * host = NULL;
+      extern char * language; /* Lynx's preferred language - FM */
+      extern char * pref_charset; /* Lynx's preferred character set - MM */
+
+      if ((host = HTParse(anAnchor->address, "", PARSE_HOST)) != NULL) {
+	  sprintf(line, "Host: %s%c%c", host, CR,LF);
+	  StrAllocCat(command, line);
+	  FREE(host);
+      }
+
+      if (!HTPresentations)
+          HTFormatInit();
+      n = HTList_count(HTPresentations);
+
+      first_Accept = TRUE;
+      len = 0;
+      for (i = 0; i < n; i++) {
+          HTPresentation *pres =
+			(HTPresentation *)HTList_objectAt(HTPresentations, i);
+          if (pres->rep_out == WWW_PRESENT) {
+	      if (pres->rep != WWW_SOURCE &&
+		  strcasecomp(HTAtom_name(pres->rep), "www/mime") &&
+		  strcasecomp(HTAtom_name(pres->rep), "www/compressed")) {
+		  if (pres->quality < 1.0) {
+		      if (pres->maxbytes > 0) {
+		          sprintf(temp, ";q=%4.3f;mxb=%d",
+			  		pres->quality, pres->maxbytes);
+		      } else {
+		          sprintf(temp, ";q=%4.3f", pres->quality);
+		      }
+		  } else if (pres->maxbytes > 0) {
+		      sprintf(temp, ";mxb=%d", pres->maxbytes);
+		  } else {
+		      temp[0] = '\0';
+		  }
+                  sprintf(line, "%s%s%s",
+		  		(first_Accept ?
+				   "Accept: " : ", "),
+				HTAtom_name(pres->rep),
+				temp);
+		  len += strlen(line);
+		  if (len > 1000 && !first_Accept) {
+		      StrAllocCat(command, crlf);
+		      sprintf(line, "Accept: %s%s",
+		      		    HTAtom_name(pres->rep),
+				    temp);
+		      len = strlen(line);
+		  }
+                  StrAllocCat(command, line);
+		  first_Accept = FALSE;
+	      }
+          }
+      }
+      sprintf(line, "%s*/*;q=0.001%c%c",
+      		    (first_Accept ?
+		       "Accept: " : ", "), CR, LF);
+      StrAllocCat(command, line);
+      first_Accept = FALSE;
+      len = 0;
+
+      sprintf(line, "Accept-Encoding: %s, %s%c%c",
+      		    "gzip", "compress", CR, LF);
+      StrAllocCat(command, line);
+
+      if (language && *language) {
+          sprintf(line, "Accept-Language: %s%c%c", language, CR, LF);
+	  StrAllocCat(command, line);
+      }
+
+      if (pref_charset && *pref_charset) {
+	  StrAllocCat(command, "Accept-Charset: ");
+	  strcpy(line, pref_charset);
+	  if (line[strlen(line)-1] == ',')
+	      line[strlen(line)-1] = '\0';
+	  for (i = 0; line[i]; i++)
+	      line[i] = TOLOWER(line[i]);
+	  if (strstr(line, "iso-8859-1") == NULL)
+	      strcat(line, ", iso-8859-1;q=0.001");
+	  if (strstr(line, "us-ascii") == NULL)
+	      strcat(line, ", us-ascii;q=0.001");
+	  StrAllocCat(command, line);
+	  sprintf(line, "%c%c", CR, LF);
+	  StrAllocCat(command, line);
+      }
+
+      /*
+       * When reloading give no-cache pragma to proxy server to make
+       * it refresh its cache. -- Ari L. <luotonen@dxcern.cern.ch>
+       *
+       * Also send it as a Cache-Control header for HTTP/1.1. - FM
+       */
+      if (reloading) {
+          sprintf(line, "Pragma: no-cache%c%c", CR, LF);
+          StrAllocCat(command, line);
+          sprintf(line, "Cache-Control: no-cache%c%c", CR, LF);
+          StrAllocCat(command, line);
+      }
+      reloading = FALSE;           /* Now turn it off again if on */
+
+      if (LYUserAgent && *LYUserAgent) {
+          sprintf(line, "User-Agent: %s%c%c", LYUserAgent, CR, LF);
+      } else {
+          sprintf(line, "User-Agent: %s/%s  libwww-FM/%s%c%c",
+                  HTAppName ? HTAppName : "unknown",
+		  HTAppVersion ? HTAppVersion : "0.0",
+		  HTLibraryVersion, CR, LF);
+      }
+      StrAllocCat(command, line);
+
+      if (personal_mail_address && !LYNoFromHeader) {
+          sprintf(line, "From: %s%c%c", personal_mail_address, CR,LF);
+          StrAllocCat(command, line);
+      }
+
+      if (!(LYUserSpecifiedURL ||
+      	    LYNoRefererHeader || LYNoRefererForThis) &&
+         strcmp((char *)HTLoadedDocumentURL(), "")) {
+          StrAllocCat(command, "Referer: ");
+          StrAllocCat(command, (char *)HTLoadedDocumentURL());
+          sprintf(line, "%c%c", CR, LF);
+          StrAllocCat(command, line);
+      }
+
+      {
+        char *docname;
+        char *hostname;
+        char *colon;
+        int portnumber;
+        char *auth;
+
+        docname = HTParse(url, "", PARSE_PATH);
+        hostname = HTParse(url, "", PARSE_HOST);
+        if (hostname &&
+            NULL != (colon = strchr(hostname, ':'))) 
+          {
+            *(colon++) = '\0';	/* Chop off port number */
+            portnumber = atoi(colon);
+          }
+        else if (!strncmp(url, "https", 5))
+	  {
+	    portnumber = HTTPS_PORT;
+	  }
+        else 
+	  {
+	    portnumber = HTTP_PORT;
+	  }
+
+        if ((auth=HTAA_composeAuth(hostname, portnumber, docname)) != NULL &&
+	     *auth != '\0') {
+	    /*
+	     *  If auth is not NULL nor zero-length, it's
+	     *  an Authorization header to be included. - FM
+	     */ 
+            sprintf(line, "%s%c%c", auth, CR, LF);
+            StrAllocCat(command, line);
+	    if (TRACE)
+                fprintf(stderr, "HTTP: Sending authorization: %s\n", auth);
+	} else if (auth && *auth == '\0') {
+	    /*
+	     *  If auth is a zero-length string, the user either
+	     *  cancelled or goofed at the username and password
+	     *  prompt. - FM
+	     */
+	    HTAlert("Can't proceed without a username and password.");
+	    FREE(command);
+	    FREE(hostname);
+	    FREE(docname);
+	    status = HT_NO_DATA;
+	    goto done;
+        } else {
+	    if (TRACE)
+                fprintf(stderr, "HTTP: Not sending authorization (yet)\n");
+        }
+        FREE(hostname);
+        FREE(docname);
+      }
+    }
+
+  if (do_post)
+    {
+      if (TRACE)
+          fprintf (stderr, "HTTP: Doing post, content-type '%s'\n",
+                   anAnchor->post_content_type);
+      sprintf (line, "Content-type: %s%c%c",
+               anAnchor->post_content_type ? anAnchor->post_content_type 
+							: "lose", CR, LF);
+      StrAllocCat(command, line);
+      {
+        int content_length;
+        if (!anAnchor->post_data)
+          content_length = 4; /* 4 == "lose" :-) */
+        else
+          content_length = strlen (anAnchor->post_data);
+        sprintf (line, "Content-length: %d%c%c",
+                 content_length, CR, LF);
+        StrAllocCat(command, line);
+      }
+
+      StrAllocCat(command, crlf);	/* Blank line means "end" */
+
+      StrAllocCat(command, anAnchor->post_data);
+    }
+
+  StrAllocCat(command, crlf);	/* Blank line means "end" */
+
+  if (TRACE)
+      fprintf (stderr, "Writing:\n%s----------------------------------\n",
+               command);
+
+  _HTProgress ("Sending HTTP request.");
+
+  status = HTTP_NETWRITE(s, command, (int)strlen(command));
+  FREE(command);
+  if (status <= 0) 
+    {
+      if (status == 0)
+        {
+          if (TRACE)
+              fprintf (stderr, "HTTP: Got status 0 in initial write\n");
+          /* Do nothing. */
+        }
+      else if 
+        ((SOCKET_ERRNO == ENOTCONN || SOCKET_ERRNO == ECONNRESET ||
+	  SOCKET_ERRNO == EPIPE) &&
+         !already_retrying &&
+         /* Don't retry if we're posting. */ !do_post)
+          {
+            /* Arrrrgh, HTTP 0/1 compability problem, maybe. */
+            if (TRACE)
+                fprintf (stderr, 
+                 "HTTP: BONZO ON WRITE Trying again with HTTP0 request.\n");
+            _HTProgress ("Retrying as HTTP0 request.");
+            (void)NETCLOSE(s);
+            extensions = NO;
+            already_retrying = 1;
+            goto try_again;
+          }
+      else
+        {
+          if (TRACE)
+              fprintf (stderr,
+	   "HTTP: Hit unexpected network WRITE error; aborting connection.\n");
+          (void)NETCLOSE(s);
+          status = -1;
+          HTAlert("Unexpected network write error; connection aborted.");
+          goto done;
+        }
+    }
+
+  if (TRACE)
+      fprintf (stderr, "HTTP: WRITE delivered OK\n");
+  _HTProgress ("HTTP request sent; waiting for response.");
+
+  /*	Read the first line of the response
+   **	-----------------------------------
+   */
+  
+  {
+    /* Get numeric status etc */
+    BOOL end_of_file = NO;
+    int buffer_length = INIT_LINE_SIZE;
+
+    line_buffer = (char *) malloc(buffer_length * sizeof(char));
+
+    do 
+      {	/* Loop to read in the first line */
+        /* Extend line buffer if necessary for those crazy WAIS URLs ;-) */
+        if (buffer_length - length < LINE_EXTEND_THRESH) 
+          {
+            buffer_length = buffer_length + buffer_length;
+            line_buffer = 
+              (char *) realloc(line_buffer, buffer_length * sizeof(char));
+          }
+        if (TRACE)
+            fprintf (stderr, "HTTP: Trying to read %d\n",
+                     buffer_length - length - 1);
+        status = HTTP_NETREAD(s, line_buffer + length,
+                              buffer_length - length - 1);
+        if (TRACE)
+            fprintf (stderr, "HTTP: Read %d\n", status);
+        if (status <= 0)
+          {
+            /* Retry if we get nothing back too; 
+               bomb out if we get nothing twice. */
+            if (status == HT_INTERRUPTED)
+              {
+                if (TRACE)
+                    fprintf (stderr, "HTTP: Interrupted initial read.\n");
+                _HTProgress ("Connection interrupted.");
+                status = HT_INTERRUPTED;
+                goto clean_up;
+              }
+            else if 
+              (status < 0 &&
+               (SOCKET_ERRNO == ENOTCONN || SOCKET_ERRNO == ECONNRESET || 
+		     SOCKET_ERRNO == EPIPE) && !already_retrying && !do_post)
+              {
+                /* Arrrrgh, HTTP 0/1 compability problem, maybe. */
+                if (TRACE)
+                    fprintf (stderr,
+		    	"HTTP: BONZO Trying again with HTTP0 request.\n");
+                (void)NETCLOSE(s);
+                FREE(line_buffer);
+                FREE(line_kept_clean);
+
+                extensions = NO;
+                already_retrying = 1;
+                _HTProgress ("Retrying as HTTP0 request.");
+                goto try_again;
+              }
+            else
+              {
+                if (TRACE)
+                    fprintf (stderr,
+  "HTTP: Hit unexpected network read error; aborting connection; status %d.\n",
+			   status);
+                HTAlert("Unexpected network read error; connection aborted.");
+                (void)NETCLOSE(s);
+                status = -1;
+                goto clean_up;
+              }
+          }
+
+        bytes_already_read += status;
+        sprintf (line, "Read %d bytes of data.", bytes_already_read);
+        HTProgress (line);
+
+#ifdef UCX  /* UCX returns -1 on EOF */
+        if (status == 0 || status == -1) 
+#else
+        if (status == 0)
+#endif
+          {
+            end_of_file = YES;
+            break;
+          }
+        line_buffer[length+status] = 0;
+
+        if (line_buffer)
+          {
+            FREE(line_kept_clean);
+            line_kept_clean = (char *)malloc (buffer_length * sizeof (char));
+            memcpy(line_kept_clean, line_buffer, buffer_length);
+          }
+
+        eol = strchr(line_buffer + length, LF);
+        /* Do we *really* want to do this? */
+        if (eol && eol != line_buffer && *(eol-1) == CR) 
+            *(eol-1) = ' '; 
+
+        length = length + status;
+
+        /* Do we really want to do *this*? */
+        if (eol) 
+            *eol = 0;		/* Terminate the line */
+      }
+    /* All we need is the first line of the response.  If it's a HTTP/1.0
+       response, then the first line will be absurdly short and therefore
+       we can safely gate the number of bytes read through this code
+       (as opposed to below) to ~1000. */
+    /* Well, let's try 100. */
+    while (!eol && !end_of_file && bytes_already_read < 100);
+  } /* Scope of loop variables */
+
+
+  /*	We now have a terminated unfolded line. Parse it.
+   **	-------------------------------------------------
+   */
+  if (TRACE)
+      fprintf(stderr, "HTTP: Rx: %s\n", line_buffer);
+
+/* Kludge to work with old buggy servers and the VMS Help gateway.
+** They can't handle the third word, so we try again without it.
+*/
+  if (extensions &&       /* Old buggy server or Help gateway? */
+      (0==strncmp(line_buffer,"<TITLE>Bad File Request</TITLE>",31) ||
+       0==strncmp(line_buffer,"Address should begin with",25) ||
+       0==strncmp(line_buffer,"<TITLE>Help ",12) ||
+       0==strcmp(line_buffer,
+       		 "Document address invalid or access not authorised"))) {
+      FREE(line_buffer);
+      FREE(line_kept_clean);
+      extensions = NO;
+      already_retrying = 1;
+      if (TRACE)
+          fprintf(stderr, "HTTP: close socket %d to retry with HTTP0\n", s);
+      (void)NETCLOSE(s);
+      /* print a progress message */
+      _HTProgress ("Retrying as HTTP0 request.");
+      goto try_again;
+  }
+
+
+  {
+    int fields;
+    char server_version[VERSION_LENGTH+1];
+    int server_status;
+
+    server_version[0] = 0;
+
+    fields = sscanf(line_buffer, "%20s %d",
+                    server_version,
+                    &server_status);
+
+    if (TRACE)
+        fprintf (stderr, "HTTP: Scanned %d fields from line_buffer\n", fields);
+
+    if (http_error_file) {     /* Make the status code externally available */
+	FILE *error_file;
+#ifdef SERVER_STATUS_ONLY
+	error_file = fopen(http_error_file, "w");
+	if (error_file) {		/* Managed to open the file */
+	    fprintf(error_file, "error=%d\n", server_status);
+#else
+	error_file = fopen(http_error_file, "a");
+	if (error_file) {		/* Managed to open the file */
+	    fprintf(error_file, "   URL=%s (%s)\n", url, METHOD);
+	    fprintf(error_file, "STATUS=%s\n", line_buffer);
+#endif /* SERVER_STATUS_ONLY */
+	    fclose(error_file);
+	}
+    }
+
+    /*
+     *  Rule out a non-HTTP/1.n reply as best we can.
+     */
+    if (fields < 2 || !server_version[0] || server_version[0] != 'H' ||
+        server_version[1] != 'T' || server_version[2] != 'T' ||
+        server_version[3] != 'P' || server_version[4] != '/' ||
+        server_version[6] != '.') {
+	/*
+	 *  Ugh! An HTTP0 reply,
+	 */
+        HTAtom * encoding;
+
+        if (TRACE)
+            fprintf (stderr, "--- Talking HTTP0.\n");
+
+        format_in = HTFileFormat(url, &encoding);
+	/*
+	 *  Treat all plain text as HTML.
+         *  This sucks but its the only solution without
+         *  without looking at content.
+         */
+        if (!strncmp(HTAtom_name(format_in), "text/plain",10)) {
+            if (TRACE)
+                fprintf(stderr,
+                           "HTTP: format_in being changed to text/HTML\n");
+            format_in = WWW_HTML;
+        }
+
+        start_of_data = line_kept_clean;
+    } else {
+        /*
+	 *  Set up to decode full HTTP/1.n response. - FM
+	 */
+        format_in = HTAtom_for("www/mime");
+        if (TRACE)
+            fprintf (stderr, "--- Talking HTTP1.\n");
+
+        /*
+	 *  We set start_of_data to "" when !eol here because there
+         *  will be a put_block done below; we do *not* use the value
+         *  of start_of_data (as a pointer) in the computation of
+         *  length (or anything else) when !eol.  Otherwise, set the
+	 *  value of length to what we have beyond eol (i.e., beyond
+	 *  the status line). - FM
+	 */
+        start_of_data = eol ? eol + 1 : "";
+        length = eol ? length - (start_of_data - line_buffer) : 0;
+
+	/*
+	 *  Trim trailing spaces in line_buffer so that we can use
+	 *  it in messages which include the status line. - FM
+	 */
+	while (line_buffer[strlen(line_buffer)-1] == ' ')
+	       line_buffer[strlen(line_buffer)-1] = '\0';
+
+	/*
+	 *  Take appropriate actions based on the status. - FM
+	 */
+        switch (server_status/100) {
+	  case 1:
+	    /*
+	     *  HTTP/1.1 Informational statuses.
+	     *  100 Continue.
+	     *  101 Switching Protocols.
+	     *  > 101 is unknown.
+	     *  We should never get these, and they have only
+	     *  the status line and possibly other headers,
+	     *  so we'll deal with them by showing the full
+	     *  header to the user as text/plain. - FM
+	     */
+	    HTAlert("Got unexpected Informational Status.");
+	    do_head = TRUE;
+	    break;
+
+          case 2:
+	    /*
+	     *  Good: Got MIME object! (Successful) - FM
+	     */
+	    switch (server_status) {
+	      case 204:
+	        /*
+		 *  No Content.
+		 */
+	        HTAlert(line_buffer);
+	        status = HT_NO_DATA;
+	        goto done;
+		break;
+
+	      case 205:
+	        /*
+		 *  Reset Content.  The server has fulfilled the
+		 *  request but nothing is returned and we should
+		 *  reset any form content.  We'll instruct the
+		 *  user to do that, and restore the current
+		 *  document. - FM
+		 */
+	        HTAlert("Request fulfilled.  Reset Content.");
+	        status = HT_NO_DATA;
+	        goto done;
+		break;
+
+	      case 206:
+	        /*
+		 *  Partial Content.  We didn't send a Range
+		 *  so something went wrong somewhere.  Show
+		 *  the status message and restore the current
+		 *  document. - FM
+		 */
+	        HTAlert(line_buffer);
+                (void)NETCLOSE(s);
+	        status = HT_NO_DATA;
+	        goto done;
+		break;
+
+	      default:
+	        /*
+		 *  200 OK.
+		 *  201 Created.
+		 *  202 Accepted.
+		 *  203 Non-Authoritative Information.
+		 *  > 206 is unknown.
+		 *  All should return something to display.
+		 */
+		HTProgress(line_buffer);
+	    } /* case 2 switch */
+            break;
+
+          case 3:
+	    /*
+	     *  Various forms of Redirection. - FM
+	     *  300 Multiple Choices.
+	     *  301 Moved Permanently.
+	     *  302 Moved Temporarily.
+	     *  303 See Other.
+	     *  304 Not Modified.
+	     *  305 Use Proxy.
+	     *  > 305 is unknown.
+	     */
+	    if (no_url_redirection || do_head || keep_mime_headers) {
+	        /*
+		 *  If any of these flags are set, we do not redirect,
+		 *  but instead show what was returned to the user as
+		 *  text/plain. - FM
+		 */
+		HTProgress(line_buffer);
+	        break;
+	    }
+
+	    if (server_status == 300) { /* Multiple Choices */
+	        /*
+		 *  For client driven content negotiation.  The server
+		 *  should be sending some way for the user-agent to
+		 *  make a selection, so we'll show the user whatever
+		 *  the server returns.  There might be a Location:
+		 *  header with the server's preference present, but
+		 *  the choice should be up to the user, someday based
+		 *  on an Alternates: header, and a body always should
+		 *  be present with descriptions and links for the
+		 *  choices (i.e., we use the latter, for now). - FM
+		 */
+		HTAlert(line_buffer);
+		break;
+	    }
+
+	    if (server_status == 304) { /* Not Modified */
+	        /*
+		 *  We didn't send an "If-Modified-Since" header,
+		 *  so this status is inappropriate.  We'll deal
+		 *  with it by showing the full header to the user
+		 *  as text/plain. - FM
+		 */
+		HTAlert("Got unexpected 304 Not Modified status.");
+	        do_head = TRUE;
+		break;
+	    }
+
+	    if (server_status == 305) { /* Use Proxy */
+		/*
+		 *  We don't want to compound proxying, so if we
+		 *  got this from a proxy, just show any message
+		 *  to the user. - FM
+		 */
+		if (using_proxy) {
+		    HTAlert("Got redirection to a proxy from the proxy!");
+		    break;
+		}
+	    } else if (server_status > 305) {
+	        /*
+		 *  Show user the content, if any, for redirection
+		 *  statuses we don't know. - FM
+		 */
+		HTAlert(line_buffer);
+		break;
+	    }
+
+            /*
+	     *  We do not load the file, but read the headers for
+	     *  the "Location:", check out that redirecting_url
+	     *  and if it's acceptible (e.g., not a telnet URL
+	     *  when we have that disabled), initiate a new fetch.
+	     *  If that's another redirecting_url, we'll repeat the
+	     *  checks, and fetch initiations if acceptible, until
+	     *  we reach the actual URL, or the redirection limit
+	     *  set in HTAccess.c is exceeded.  If the status was 301,
+	     *  indicating that the relocation is permanent, we set
+	     *  the permanent_redirection flag to make it permanent
+	     *  for the current anchor tree (i.e., will persist until
+	     *  the tree is freed or the client exits).  If the
+	     *  redirection would include POST content, we seek
+	     *  confirmation from an interactive user, and otherwise
+	     *  refuse the redirection.  We also don't allow permanent
+	     *  redirection if we keep POST content.  If we don't find
+	     *  the Location header or it's value is zero-length, we
+	     *  display whatever the server returned, and the user
+	     *  should RELOAD that to try again, or make a selection
+	     *  from it if it contains links, or Left-Arrow to the
+	     *  previous document. - FM
+	     */
+	    {
+	      char *cp;
+
+	      if ((dump_output_immediately || traversal) &&
+	      	  do_post && server_status != 303) {
+		  /*
+		   *  Don't redirect POST content without approval
+		   *  from an interactive user. - FM
+		   */
+		  (void)NETCLOSE(s);
+		  status = -1;
+		  HTAlert(
+		       "Redirection of POST content requires user approval.");
+		  if (traversal)
+		      HTProgress(line_buffer);
+		  goto clean_up;
+	      }
+
+	      /*
+	       *  Get the rest of the headers and data, if
+	       *  any, and then close the connection. - FM
+	       */
+	      while ((status = HTTP_NETREAD(s, line_buffer,
+	      				    INIT_LINE_SIZE)) > 0) {
+	          line_buffer[status] = '\0';
+		  StrAllocCat(line_kept_clean, line_buffer);
+	      }
+	      (void)NETCLOSE(s);
+              if (status == HT_INTERRUPTED) {
+		  /*
+		   *  Impatient user. - FM
+		   */
+                  if (TRACE)
+                      fprintf (stderr, "HTTP: Interrupted followup read.\n");
+                  _HTProgress ("Connection interrupted.");
+                  status = HT_INTERRUPTED;
+                  goto clean_up;
+              }
+	      doing_redirect = 1;
+	      if (server_status == 301) { /* Moved Permanently */
+	          HTProgress(line_buffer);
+		  if (do_post) {
+	              /*
+		       *  Don't make the redirection permanent
+		       *  if we have POST content. - FM
+		       */
+		      if (TRACE)
+		          fprintf(stderr,
+   "HTTP: Have POST content. Treating 301 (Permanent) as 302 (Temporary).\n");
+		      HTAlert(
+	 "Have POST content. Treating Permanent Redirection as Temporary.\n");
+		  } else {
+	              permanent_redirection = TRUE;
+		  }
+	      }
+
+	      /*
+	       *  Look for the "Location:" in the headers. - FM
+	       */
+	      cp = line_kept_clean;
+	      while (*cp) {
+	        if (*cp != 'l' && *cp != 'L') {
+		    cp++;
+	        } else if (!strncasecomp(cp, "Location:", 9)) {
+	            char *cp1 = NULL, *cp2 = NULL;
+	            cp += 9;
+		    /*
+		     *  Trim leading spaces. - FM
+		     */
+		    while (*cp == ' ')
+		        cp++;
+		    /*
+		     *  Accept CRLF, LF, or CR as end of header. - FM
+		     */
+		    if (((cp1 = strchr(cp, LF)) != NULL) ||
+		        (cp2 = strchr(cp, CR)) != NULL) {
+			if (*cp1) {
+			    *cp1 = '\0';
+			    if ((cp2 = strchr(cp, CR)) != NULL)
+			        *cp2 = '\0';
+			} else {
+			    *cp2 = '\0';
+			}
+			if (*cp == '\0') {
+			    /*
+			     *  The "Location:" value is zero-length, and
+			     *  thus is probably something in the body, so
+			     *  we'll show the user what was returned. - FM
+			     */
+			    if (TRACE)
+			        fprintf(stderr,
+					"HTTP: 'Location:' is zero-length!\n");
+			    if (cp1)
+			        *cp1 = LF;
+			    if (cp2)
+			        *cp2 = CR;
+			    doing_redirect = 0;
+			    permanent_redirection = FALSE;
+			    start_of_data = line_kept_clean;
+			    length = strlen(start_of_data);
+			    HTAlert(
+			       "Got redirection with a bad Location header.");
+			    HTProgress(line_buffer);
+			    break;
+			}
+
+			/*
+			 *  Load the new URL into redirecting_url,
+			 *  which will be checked in LYGetFile.c
+			 *  for restrictions before seeking the
+			 *  document at that Location. - FM
+			 */
+		        StrAllocCopy(redirecting_url, cp);
+			HTProgress(line_buffer);
+                        if (TRACE)
+                            fprintf(stderr,
+			    	    "HTTP: Picked up location '%s'\n", cp);
+			if (cp1)
+		            *cp1 = LF;
+		        if (cp2)
+		            *cp2 = CR;
+			if (server_status == 305) { /* Use Proxy */
+			    /*
+			     *  Make sure the proxy field ends with
+			     *  a slash. - FM
+			     */
+			    if (redirecting_url[strlen(redirecting_url)-1]
+			    	!= '/')
+				StrAllocCat(redirecting_url, "/");
+			    /*
+			     *  Append our URL. - FM
+			     */
+			    StrAllocCat(redirecting_url, anAnchor->address);
+			    if (TRACE)
+			        fprintf(stderr,
+					"HTTP: Proxy URL is '%s'\n",
+					redirecting_url);
+			}
+			if (!do_post || server_status == 303) {
+			    /*
+			     *  We don't have POST content (nor support PUT
+			     *  or DELETE), or the status is "See Other" and
+			     *  we can convert to GET, so go back and check
+			     *  out the new URL. - FM
+			     */
+		            status = HT_REDIRECTING;
+		            goto clean_up;
+			}
+			/*
+			 *  Make sure the user wants to redirect
+			 *  the POST content. - FM
+			 */
+			if (!HTConfirm(
+				"Redirection for POST content. Proceed?")) {
+			    doing_redirect = 0;
+			    FREE(redirecting_url);
+			    status = HT_NO_DATA;
+			    goto clean_up;
+			}
+			/*
+			 *  Set the flag to retain the POST content
+			 *  and go back to check out the URL. - FM
+			 */
+			redirect_post_content = TRUE;
+		        status = HT_REDIRECTING;
+		        goto clean_up;
+		    }
+	            break;
+	        } else {
+		    /*
+		     *  Keep looking for the Location header. - FM
+		     */
+	            cp++;
+	        }
+	      }
+
+	      /*
+	       *  If we get to here, we didn't find the Location
+	       *  header, so we'll show the user what we got, if
+	       *  anything. - FM
+	       */
+              if (TRACE)
+                  fprintf (stderr, "HTTP: Failed to pick up location.\n");
+	      doing_redirect = 0;
+	      permanent_redirection = FALSE;
+	      start_of_data = line_kept_clean;
+	      length = strlen(start_of_data);
+	      HTAlert("Got redirection with no Location header.");
+	      HTProgress(line_buffer);
+	      break;
+	   }
+
+          case 4:
+	    /*
+	     *  "I think I goofed!" (Client Error) - FM
+	     */
+            switch (server_status) {
+              case 401:  /* Unauthorized */
+	        /*
+		 *  Authorization for orgin server required.
+		 *  If we can set up authorization based on
+		 *  the WWW-Authenticate header, and the user
+		 *  provides a username and password, try again.
+		 *  Otherwise, issue a statusline message and
+		 *  restore the current document.  - FM
+		 */
+		if (HTAA_shouldRetryWithAuth(start_of_data, length, NULL, s)) 
+                  {
+ 		    extern char *authentication_info[2];
+
+                    (void)NETCLOSE(s);
+                    if (dump_output_immediately && !authentication_info[0]) {
+                        fprintf(stderr,
+		      		"HTTP: Access authorization required.\n");
+                        fprintf(stderr,
+		      		"       Use the -auth=id:pw parameter.\n");
+                        status = HT_NO_DATA;
+                        goto clean_up;
+                    }
+
+                    if (TRACE) 
+                        fprintf(stderr, "%s %d %s\n",
+                              "HTTP: close socket", s,
+                              "to retry with Access Authorization");
+
+                    _HTProgress (
+		    	"Retrying with access authorization information.");
+		    FREE(line_buffer);
+		    FREE(line_kept_clean);
+                    goto try_again;
+                    break;
+                  }
+		else
+		  {
+		    HTAlert(
+	"Can't retry with authorization!  Contact the server's WebMaster.");
+		    (void)NETCLOSE(s);
+                    status = -1;
+                    goto clean_up;
+		  }
+
+	      case 407:
+	        /*
+		 *  Proxy Authentication Required.  We'll handle
+		 *  Proxy-Authenticate headers and retry with a
+		 *  Proxy-Authorization header, someday, but for
+		 *  now, apologized to the user and restore the
+		 *  current document. - FM
+		 */
+	        HTAlert(
+		 "Proxy Authentication Required.  Sorry, not yet supported.");
+                (void)NETCLOSE(s);
+	        status = HT_NO_DATA;
+	        goto done;
+		break;
+
+	      case 408:
+	        /*
+		 *  Request Timeout.  Show the status message
+		 *  and restore the current document. - FM
+		 */
+	        HTAlert(line_buffer);
+                (void)NETCLOSE(s);
+	        status = HT_NO_DATA;
+	        goto done;
+		break;
+
+              default:
+                /*
+		 *  400 Bad Request.
+		 *  402 Payment Required.
+		 *  403 Forbidden.
+		 *  404 Not Found.
+		 *  405 Method Not Allowed.
+		 *  406 Not Acceptable.
+		 *  409 Conflict.
+		 *  410 Gone.
+		 *  411 Length Required.
+		 *  412 Precondition Failed.
+		 *  413 Request Entity Too Large.
+		 *  414 Request-URI Too Long.
+		 *  415 Unsupported Media Type.
+		 *  416 List Response (for content negotiation).
+		 *  > 416 is unknown.
+		 *  Show the status message, and display
+		 *  the returned text if we are not doing
+		 *  a traversal. - FM
+		 */
+		HTAlert(line_buffer);
+		if (traversal) {
+		    (void)NETCLOSE(s);
+		    status = -1;
+		    goto clean_up;
+		}
+                break;
+            } /* case 4 switch */
+            break;
+
+          case 5:
+	    /*
+	     *  "I think YOU goofed!" (server error)
+	     *  500 Internal Server Error
+	     *  501 Not Implemented
+	     *  502 Bad Gateway
+	     *  503 Service Unavailable
+	     *  504 Gateway Timeout
+	     *  505 HTTP Version Not Supported
+	     *  > 505 is unknown.
+	     *  Should always include a message, which
+	     *  we always should display. - FM
+	     */
+	    HTAlert(line_buffer);
+            break;
+
+          default:
+	    /*
+	     *  Bad or unknown server_status number.
+	     *  Take a chance and hope there is
+	     *  something to display. - FM
+	     */
+            HTAlert("Unknown status reply from server!");
+	    HTAlert(line_buffer);
+	    if (traversal) {
+		(void)NETCLOSE(s);
+		status = -1;
+		goto clean_up;
+	    }
+            break;
+        } /* Switch on server_status/100 */
+
+      }	/* Full HTTP reply */
+  } /* scope of fields */
+
+  /* Set up the stream stack to handle the body of the message */
+  if (do_head || keep_mime_headers) {
+      /* It was a HEAD request, or we want the headers and source */
+      start_of_data = line_kept_clean;
+      length = strlen(start_of_data);
+      format_in = HTAtom_for("text/plain");
+  }
+
+  target = HTStreamStack(format_in,
+                         format_out,
+                         sink, anAnchor);
+
+  if (!target || target == NULL) 
+    {
+      char buffer[1024];	/* @@@@@@@@ */
+
+      (void)NETCLOSE(s);
+      sprintf(buffer, "Sorry, no known way of converting %s to %s.",
+              HTAtom_name(format_in), HTAtom_name(format_out));
+      _HTProgress (buffer);
+      status = -1;
+      goto clean_up;
+    }
+
+  /* Recycle the first chunk of data, in all cases. */
+  (*target->isa->put_block)(target, start_of_data, length);
+
+  /* Go pull the bulk of the data down. */
+  rv = HTCopy(s, NULL, target);
+
+  if (rv == -1)
+    {
+      /* Intentional interrupt before data were received, not an error */
+      /* (*target->isa->_abort)(target, NULL); */ /* already done in HTCopy */
+      status = HT_INTERRUPTED;
+      (void)NETCLOSE(s);
+      goto clean_up;
+    }
+  if (rv == -2 && !already_retrying && !do_post)
+    { 
+      /* Aw hell, a REAL error, maybe cuz it's a dumb HTTP0 server */
+      if (TRACE)
+          fprintf (stderr, "HTTP: Trying again with HTTP0 request.\n");
+      /* May as well consider it an interrupt -- right? */
+      (*target->isa->_abort)(target, NULL);
+      (void)NETCLOSE(s);
+      FREE(line_buffer);
+      FREE(line_kept_clean);
+      extensions = NO;
+      already_retrying = 1;
+      _HTProgress ("Retrying as HTTP0 request.");
+      goto try_again;
+    }
+
+  /* 
+   * Close socket if partial transmission (was freed on abort)
+   * Free if complete transmission (socket was closed before return)
+   */
+  if (rv == HT_INTERRUPTED)
+    {
+      (void)NETCLOSE(s);
+    }
+  else
+      (*target->isa->_free)(target);
+
+  if (doing_redirect)
+    /*
+     * We already jumped over all this if the "case 3:" code worked
+     * above, but we'll check here as a backup in case it fails. - FM
+     */
+    {
+      /* Lou's old comment:  - FM */
+      /* OK, now we've got the redirection URL temporarily stored
+         in external variable redirecting_url, exported from HTMIME.c,
+         since there's no straightforward way to do this in the library
+         currently.  Do the right thing. */
+      status = HT_REDIRECTING;
+    }
+  else
+    {
+      /* If any data were received, treat as a complete transmission */
+      status = HT_LOADED;
+    }
+
+  /*	Clean up
+   */
+clean_up:
+  FREE(line_buffer);
+  FREE(line_kept_clean);
+
+done:
+  /* Clear out on exit, just in case. */
+  do_head = 0;
+  do_post = 0;
+  return status;
+}
+
+
+/*	Protocol descriptor
+*/
+
+#ifdef GLOBALDEF_IS_MACRO
+#define _HTTP_C_GLOBALDEF_1_INIT { "http", HTLoadHTTP, 0}
+GLOBALDEF (HTProtocol,HTTP,_HTTP_C_GLOBALDEF_1_INIT);
+#define _HTTP_C_GLOBALDEF_2_INIT { "https", HTLoadHTTP, 0}
+GLOBALDEF (HTProtocol,HTTPS,_HTTP_C_GLOBALDEF_2_INIT);
+#else
+GLOBALDEF PUBLIC HTProtocol HTTP = { "http", HTLoadHTTP, 0 };
+GLOBALDEF PUBLIC HTProtocol HTTPS = { "https", HTLoadHTTP, 0 };
+#endif /* GLOBALDEF_IS_MACRO */
diff --git a/WWW/Library/Implementation/HTTP.h b/WWW/Library/Implementation/HTTP.h
new file mode 100644
index 00000000..c97369fb
--- /dev/null
+++ b/WWW/Library/Implementation/HTTP.h
@@ -0,0 +1,28 @@
+/*                     /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTTP.html
+                                HYPERTEXT TRANFER PROTOCOL
+                                             
+ */
+#ifndef HTTP_H
+#define HTTP_H
+
+#include "HTAccess.h"
+
+#ifdef GLOBALREF_IS_MACRO
+extern GLOBALREF (HTProtocol,HTTP);
+extern GLOBALREF (HTProtocol,HTTPS);
+#else
+GLOBALREF HTProtocol HTTP;
+GLOBALREF HTProtocol HTTPS;
+#endif /* GLOBALREF_IS_MACRO */
+
+#define URL_GET_METHOD  1
+#define URL_POST_METHOD 2
+#define URL_MAIL_METHOD 3
+
+#endif /* HTTP_H */
+
+/*
+
+   end of HTTP module definition
+   
+    */
diff --git a/WWW/Library/Implementation/HTTelnet.c b/WWW/Library/Implementation/HTTelnet.c
new file mode 100644
index 00000000..a9bf8527
--- /dev/null
+++ b/WWW/Library/Implementation/HTTelnet.c
@@ -0,0 +1,579 @@
+/*		Telnet Acees, Roligin, etc			HTTelnet.c
+**		==========================
+**
+** Authors
+**	TBL	Tim Berners-Lee timbl@info.cern.ch
+**	JFG	Jean-Francois Groff jgh@next.com
+**	DD	Denis DeLaRoca (310) 825-4580  <CSP1DWD@mvs.oac.ucla.edu>
+** History
+**       8 Jun 92 Telnet hopping prohibited as telnet is not secure (TBL)
+**	26 Jun 92 When over DECnet, suppressed FTP, Gopher and News. (JFG)
+**	 6 Oct 92 Moved HTClientHost and logfile into here. (TBL)
+**	17 Dec 92 Tn3270 added, bug fix. (DD)
+**	 2 Feb 93 Split from HTAccess.c. Registration.(TBL)
+*/
+
+#include "HTUtils.h"
+#include "tcp.h"
+
+/* Implements:
+*/
+#include "HTTelnet.h"
+
+#include "HTParse.h"
+#include "HTAnchor.h"
+#include "HTTP.h"
+#include "HTFile.h"
+/*#include <errno.h> included by tcp.h -- FM */
+/*#include <stdio.h> included by HTUtils.h -- FM */
+
+#include "HText.h"
+
+#include "HTAccess.h"
+#include "HTAlert.h"
+#ifndef VMS
+#include "../../../userdefs.h"  /* for TELNET_COMMAND and RLOGIN_COMMAND */
+#endif /* not VMS */
+
+#include "LYLeaks.h"
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+#define HT_NO_DATA -9999
+
+
+/*	Telnet or "rlogin" access
+**	-------------------------
+*/
+PRIVATE int remote_session ARGS2(char *, access, char *, host)
+{
+	char * user = host;
+	char * password = NULL;
+ 	char * cp;
+	char * hostname;
+	char * port;
+	char   command[256];
+	enum _login_protocol { telnet, rlogin, tn3270 } login_protocol =
+		strcmp(access, "rlogin") == 0 ? rlogin :
+		strcmp(access, "tn3270") == 0 ? tn3270 : telnet;
+#ifdef VMS
+	extern int DCLsystem PARAMS((char *command));
+#define system(a) DCLsystem(a) /* use LYCurses.c routines for spawns */
+#endif /* VMS */
+
+	/*
+	 *	Modified to allow for odd chars in a username only if exists.
+	 *	05-28-94 Lynx 2-3-1 Garrett Arch Blythe
+	 */
+	/* prevent telnet://hostname;rm -rf *  URL's (VERY BAD) 
+	 *  *cp=0;  / * terminate at any ;,<,>,`,|,",' or space or return
+	 *  or tab to prevent security whole 
+	 */
+	for(cp = (strchr(host, '@') ? strchr(host, '@') : host); *cp != '\0';
+		cp++)	{
+	    if(!isalnum(*cp) && *cp != '_' && *cp != '-' &&
+				*cp != ':' && *cp != '.' && *cp != '@') {
+	        *cp = '\0';
+	        break;
+	    }
+	}
+
+	hostname = strchr(host, '@');
+
+	if (hostname) {
+	    *hostname++ = '\0';	/* Split */
+	} else {
+	    hostname = host;
+	    user = NULL;	/* No user specified */
+	}
+
+	port = strchr(hostname, ':');
+	if (port)
+	    *port++ = '\0';	/* Split */
+
+    if (!hostname || *hostname == '\0') {
+        if (TRACE)
+	    fprintf(stderr, "HTTelnet: No host specified!\n");
+	return HT_NO_DATA;
+    }
+
+    if (user) {
+        password = strchr(user, ':');
+	if (password) {
+	    *password++ = '\0';
+	}
+    }
+
+/* If the person is already telnetting etc, forbid hopping */
+/* This is a security precaution, for us and remote site */
+
+	if (HTSecure) {
+
+#ifdef TELNETHOPPER_MAIL
+	    sprintf(command, 
+	      "finger @%s | mail -s \"**telnethopper %s\" tbl@dxcern.cern.ch",
+	       HTClientHost, HTClientHost);
+	    system(command);
+#endif
+	    printf("\n\nSorry, but the service you have selected is one\n");
+	    printf("to which you have to log in.  If you were running www\n");
+	    printf("on your own computer, you would be automatically connected.\n");
+	    printf("For security reasons, this is not allowed when\n");
+	    printf("you log in to this information service remotely.\n\n");
+
+	    printf("You can manually connect to this service using %s\n",
+		   access);
+	    printf("to host %s", hostname);
+	    if (user) printf(", user name %s", user);
+	    if (password) printf(", password %s", password);
+	    if (port) printf(", port %s", port);
+	    printf(".\n\n");
+	    return HT_NO_DATA;
+	}
+
+/* Not all telnet servers get it even if user name is specified
+** so we always tell the guy what to log in as
+*/
+        if (user && login_protocol != rlogin)
+	    printf("When you are connected, log in as:  %s\n", user);
+        if (password && login_protocol != rlogin)
+	    printf("                  The password is:  %s\n", password);
+
+/*
+ *	NeXTSTEP is the implied version of the NeXT operating system.
+ *		You may need to define this yourself.
+ */
+#if     defined(NeXT) && defined(NeXTSTEP) && NeXTSTEP<=20100
+	sprintf(command, "%s%s%s %s %s", TELNET_COMMAND,
+		user ? " -l " : "",
+		user ? user : "",
+		hostname,
+		port ? port : "");
+
+	if (TRACE)
+	    fprintf(stderr, "HTTelnet: Command is: %s\n\n", command);
+	system(command);
+	return HT_NO_DATA;		/* Ok - it was done but no data */
+#define TELNET_DONE
+#endif
+
+/* Most unix machines suppport username only with rlogin */
+#if defined(unix)
+#ifndef TELNET_DONE
+	if (login_protocol == rlogin) {
+	    sprintf(command, "%s %s%s%s", RLOGIN_COMMAND,
+		hostname,
+		user ? " -l " : "",
+		user ? user : "");
+
+	} else if (login_protocol == tn3270) {
+	    sprintf(command, "%s %s %s", TN3270_COMMAND,
+		hostname,
+		port ? port : "");
+
+	} else {  /* TELNET */
+	    sprintf(command, "%s %s %s", TELNET_COMMAND,
+		hostname,
+		port ? port : "");
+	}
+
+	if (TRACE)
+	    fprintf(stderr, "HTTelnet: Normal: Command is: %s\n\n", command);
+	system(command);
+	return HT_NO_DATA;		/* Ok - it was done but no data */
+#define TELNET_DONE
+#endif /* !TELNET_DONE */
+#endif /* unix */
+
+/* VMS varieties */
+#if defined(MULTINET)
+	if (login_protocol == rlogin) {
+	    sprintf(command, "RLOGIN%s%s%s%s%s %s",  /*lm 930713 */
+		user ? "/USERNAME=\"" : "",
+		user ? user : "",
+		user ? "\"" : "",
+		port ? "/PORT=" : "",
+		port ? port : "",
+		hostname);
+
+	} else if (login_protocol == tn3270) {
+	    sprintf(command, "TELNET/TN3270 %s%s %s",
+		port ? "/PORT=" : "",
+		port ? port : "",
+		hostname);
+
+	} else {  /* TELNET */
+	    sprintf(command, "TELNET %s%s %s",
+		port ? "/PORT=" : "",
+		port ? port : "",
+		hostname);
+	}
+
+	if (TRACE)
+	    fprintf(stderr, "HTTelnet: Command is: %s\n\n", command);
+	system(command);
+	return HT_NO_DATA;		/* Ok - it was done but no data */
+#define TELNET_DONE
+#endif /* MULTINET */
+
+#if defined(WIN_TCP)
+        {
+            char *cp;
+    
+	    if ((cp=getenv("WINTCP_COMMAND_STYLE")) != NULL &&
+                0==strncasecomp(cp, "VMS", 3)) { /* VMS command syntax */ 
+	        if (login_protocol == rlogin) {
+	            sprintf(command, "RLOGIN%s%s%s%s%s %s",  /*lm 930713 */
+		        user ? "/USERNAME=\"" : "",
+		        user ? user : "",
+			user ? "\"" : "",
+		        port ? "/PORT=" : "",
+		        port ? port : "",
+		        hostname);
+
+	        } else if (login_protocol == tn3270) {
+	            sprintf(command, "TELNET/TN3270 %s%s %s",
+		        port ? "/PORT=" : "",
+		        port ? port : "",
+		        hostname);
+
+	        } else {  /* TELNET */
+	            sprintf(command, "TELNET %s%s %s",
+		        port ? "/PORT=" : "",
+		        port ? port : "",
+		        hostname);
+	        }
+
+            } else { /* UNIX command syntax */
+	       if (login_protocol == rlogin) {
+	           sprintf(command, "RLOGIN %s%s%s%s%s", 
+		       hostname,
+		       user ? " -l " : "",
+		       user ? "\"" : "",
+		       user ? user : "",
+		       user ? "\"" : "");
+
+	        } else if (login_protocol == tn3270) {
+	            sprintf(command, "TN3270 %s %s",
+		        hostname,
+		        port ? port : "");
+
+	        } else {  /* TELNET */
+	            sprintf(command, "TELNET %s %s",
+		        hostname,
+		        port ? port : "");
+	        }
+            }
+
+	    if (TRACE)
+	        fprintf(stderr, "HTTelnet: Command is: %s\n\n", command);
+	    system(command);
+	    return HT_NO_DATA;		/* Ok - it was done but no data */
+        }
+#define TELNET_DONE
+#endif /* WIN_TCP */
+
+#ifdef UCX
+	if (login_protocol == rlogin) {
+	    sprintf(command, "RLOGIN%s%s%s %s %s",
+		user ? "/USERNAME=\"" : "",
+		user ? user : "",
+		user ? "\"" : "",
+		hostname,
+		port ? port : "");
+
+	} else if (login_protocol == tn3270) {
+	    sprintf(command, "TN3270 %s %s",
+		hostname,
+		port ? port : "");
+
+	} else {  /* TELNET */
+	    sprintf(command, "TELNET %s %s",
+		hostname,
+		port ? port : "");
+	}
+
+	if (TRACE)
+	    fprintf(stderr, "HTTelnet: Command is: %s\n\n", command);
+	system(command);
+	return HT_NO_DATA;		/* Ok - it was done but no data */
+#define TELNET_DONE
+#endif /* UCX */
+
+#ifdef CMU_TCP
+	if (login_protocol == telnet) {
+	    sprintf(command, "TELNET %s%s %s",
+		port ? "/PORT=" : "",
+		port ? port : "",
+		hostname);
+	    if (TRACE)
+	        fprintf(stderr, "HTTelnet: Command is: %s\n\n", command);
+	    system(command);
+	}
+	else {
+	    extern int LYgetch NOPARAMS;
+	    extern BOOLEAN HadVMSInterrupt;
+
+	    printf(
+	"\nSorry, this browser was compiled without the %s access option.\n",
+		access);
+	    printf("\nPress <return> to return to Lynx.");
+	    LYgetch();
+	    HadVMSInterrupt = FALSE;
+	}
+	return HT_NO_DATA;		/* Ok - it was done but no data */
+#define TELNET_DONE
+#endif /* CMU_TCP */
+
+#ifdef SOCKETSHR_TCP
+  {
+    char *cp;
+
+    if (getenv("MULTINET_SOCKET_LIBRARY") != NULL) {
+	if (login_protocol == rlogin) {
+	    sprintf(command, "MULTINET RLOGIN%s%s%s%s %s",  /*lm 930713 */
+		user ? "/USERNAME=" : "",
+		user ? user : "",
+		port ? "/PORT=" : "",
+		port ? port : "",
+		hostname);
+
+	} else if (login_protocol == tn3270) {
+	    sprintf(command, "MULTINET TELNET/TN3270 %s%s %s",
+		port ? "/PORT=" : "",
+		port ? port : "",
+		hostname);
+
+	} else {  /* TELNET */
+	    sprintf(command, "MULTINET TELNET %s%s %s",
+		port ? "/PORT=" : "",
+		port ? port : "",
+		hostname);
+	}
+
+	if (TRACE)
+	    fprintf(stderr, "HTTelnet: Command is: %s\n\n", command);
+	system(command);
+	return HT_NO_DATA;		/* Ok - it was done but no data */
+    }
+    else if ((cp=getenv("WINTCP_COMMAND_STYLE")) != NULL) {
+        if (0==strncasecomp(cp, "VMS", 3)) { /* VMS command syntax */ 
+	    if (login_protocol == rlogin) {
+	        sprintf(command, "RLOGIN%s%s%s%s %s",  /*lm 930713 */
+		    user ? "/USERNAME=" : "",
+		    user ? user : "",
+		    port ? "/PORT=" : "",
+		    port ? port : "",
+		    hostname);
+	    } else if (login_protocol == tn3270) {
+	        sprintf(command, "TELNET/TN3270 %s%s %s",
+		    port ? "/PORT=" : "",
+		    port ? port : "",
+		    hostname);
+	    } else {  /* TELNET */
+	        sprintf(command, "TELNET %s%s %s",
+		    port ? "/PORT=" : "",
+		    port ? port : "",
+		    hostname);
+	    }
+        } else { /* UNIX command syntax */
+	    if (login_protocol == rlogin) {
+	        sprintf(command, "RLOGIN %s%s%s", 
+		    hostname,
+		    user ? " -l " : "",
+		    user ? user : "");
+	    } else if (login_protocol == tn3270) {
+	        sprintf(command, "TN3270 %s %s",
+		    hostname,
+		    port ? port : "");
+	    } else {  /* TELNET */
+	        sprintf(command, "TELNET %s %s",
+		    hostname,
+		    port ? port : "");
+	    }
+        }
+
+	if (TRACE)
+	    fprintf(stderr, "HTTelnet: Command is: %s\n\n", command);
+	system(command);
+	return HT_NO_DATA;		/* Ok - it was done but no data */
+    }
+    else if (getenv("UCX$DEVICE") != NULL) {
+	if (login_protocol == rlogin) {
+	    sprintf(command, "RLOGIN%s%s %s %s",
+		user ? "/USERNAME=" : "",
+		user ? user : "",
+		hostname,
+		port ? port : "");
+
+	} else if (login_protocol == tn3270) {
+	    sprintf(command, "TN3270 %s %s",
+		hostname,
+		port ? port : "");
+
+	} else {  /* TELNET */
+	    sprintf(command, "TELNET %s %s",
+		hostname,
+		port ? port : "");
+	}
+
+	if (TRACE)
+	    fprintf(stderr, "HTTelnet: Command is: %s\n\n", command);
+	system(command);
+	return HT_NO_DATA;		/* Ok - it was done but no data */
+    }
+    else if (getenv("CMUTEK_ROOT") != NULL) {
+    	if (login_protocol == telnet) {
+    	    sprintf(command, "TELNET %s%s %s",
+    		port ? "/PORT=" : "",
+    		port ? port : "",
+    		hostname);
+    	    if (TRACE)
+	        fprintf(stderr, "HTTelnet: Command is: %s\n\n", command);
+    	    system(command);
+    	}
+    	else {
+    	    extern int LYgetch NOPARAMS;
+	    extern BOOLEAN HadVMSInterrupt;
+
+	    printf(
+	  "\nSorry, this browser was compiled without the %s access option.\n",
+    		access);
+    	    printf("\nPress <return> to return to Lynx.");
+    	    LYgetch();
+	    HadVMSInterrupt = FALSE;
+    	}
+    	return HT_NO_DATA;		/* Ok - it was done but no data */
+    }
+    else {
+	if (login_protocol == telnet) {
+	    sprintf(command, "TELNET %s%s %s",
+		port ? "/PORT=" : "",
+		port ? port : "",
+		hostname);
+	    if (TRACE)
+	        fprintf(stderr, "HTTelnet: Command is: %s\n\n", command);
+	    system(command);
+	}
+	else {
+	    extern int LYgetch NOPARAMS;
+	    extern BOOLEAN HadVMSInterrupt;
+
+	    printf(
+	  "\nSorry, this browser was compiled without the %s access option.\n",
+		access);
+	    printf("\nPress <return> to return to Lynx.");
+	    LYgetch();
+	    HadVMSInterrupt = FALSE;
+	}
+	return HT_NO_DATA;		/* Ok - it was done but no data */
+    }
+  }
+#define TELNET_DONE
+#endif /* SOCKETSHR_TCP */
+
+#ifdef VM
+#define SIMPLE_TELNET
+#endif
+#ifdef SIMPLE_TELNET
+	if (login_protocol == telnet) {			/* telnet only */
+	    sprintf(command, "TELNET  %s",	/* @@ Bug: port ignored */
+		hostname);
+	    if (TRACE)
+	        fprintf(stderr, "HTTelnet: Command is: %s\n\n", command);
+	    system(command);
+	    return HT_NO_DATA;		/* Ok - it was done but no data */
+	}
+#endif
+
+#ifndef TELNET_DONE
+	printf(
+	"\nSorry, this browser was compiled without the %s access option.\n",
+		access);
+	printf(
+	"\nTo access the information you must %s to %s", access, hostname);
+	if (port)
+	    printf(" (port %s)", port);
+	if (user)
+	    printf("\nlogging in with username %s", user);
+	printf(".\n");
+	{
+	    extern int LYgetch NOPARAMS;
+
+	    printf("\nPress <return> to return to Lynx.");
+	    fflush(stdout);
+	    LYgetch();
+#ifdef VMS
+	    {
+		extern BOOLEAN HadVMSInterrupt;
+		HadVMSInterrupt = FALSE;
+	    }
+#endif /* VMS */
+        }
+	return HT_NO_DATA;
+#endif /* !TELNET_DONE */
+}
+
+/*	"Load a document" -- establishes a session
+**	------------------------------------------
+**
+** On entry,
+**	addr		must point to the fully qualified hypertext reference.
+**
+** On exit,
+**	returns		<0	Error has occured.
+**			>=0	Value of file descriptor or socket to be used
+**				 to read data.
+**	*pFormat	Set to the format of the file, if known.
+**			(See WWW.h)
+**
+*/
+PRIVATE int HTLoadTelnet
+ARGS4
+(
+ CONST char *,		addr,
+ HTParentAnchor *,	anchor,
+ HTFormat,		format_out,
+ HTStream *,		sink			/* Ignored */
+)
+{
+    char * access;
+    
+    char * host;
+    int status;
+    
+    if (sink) {
+        if (TRACE)
+	    fprintf(stderr,
+	   "HTTelnet: Can't output a live session -- must be interactive!\n");
+	return HT_NO_DATA;
+    }
+    access =  HTParse(addr, "file:", PARSE_ACCESS);
+    
+    host = HTParse(addr, "", PARSE_HOST);
+    if (!host || *host == '\0') {
+	status = HT_NO_DATA;
+        if (TRACE)
+	    fprintf(stderr, "HTTelnet: No host specified!\n");
+    } else {
+        status = remote_session(access, host);
+    }
+
+    FREE(host);	
+    FREE(access);
+    return status;
+}
+
+
+#ifdef GLOBALDEF_IS_MACRO
+#define _HTTELNET_C_1_INIT { "telnet", HTLoadTelnet, NULL }
+#define _HTTELNET_C_2_INIT { "rlogin", HTLoadTelnet, NULL }
+#define _HTTELNET_C_3_INIT { "tn3270", HTLoadTelnet, NULL }
+GLOBALDEF (HTProtocol, HTTelnet, _HTTELNET_C_1_INIT );
+GLOBALDEF (HTProtocol, HTRlogin, _HTTELNET_C_2_INIT );
+GLOBALDEF (HTProtocol, HTTn3270, _HTTELNET_C_3_INIT );
+#else
+GLOBALDEF PUBLIC HTProtocol HTTelnet = { "telnet", HTLoadTelnet, NULL };
+GLOBALDEF PUBLIC HTProtocol HTRlogin = { "rlogin", HTLoadTelnet, NULL };
+GLOBALDEF PUBLIC HTProtocol HTTn3270 = { "tn3270", HTLoadTelnet, NULL };
+#endif /* GLOBALDEF_IS_MACRO */
diff --git a/WWW/Library/Implementation/HTTelnet.h b/WWW/Library/Implementation/HTTelnet.h
new file mode 100644
index 00000000..4c5de744
--- /dev/null
+++ b/WWW/Library/Implementation/HTTelnet.h
@@ -0,0 +1,24 @@
+/*                 /Net/dxcern/userd/timbl/hypertext/WWW/Library/Implementation/HTTelnet.html
+                            TELNET AND SIMILAR ACCESS METHODS
+                                             
+ */
+
+#ifndef HTTELNET_H
+#define HTTELNET_H
+
+#include "HTAccess.h"
+
+#ifdef GLOBALREF_IS_MACRO
+extern GLOBALREF(HTProtocol,HTTelnet);
+extern GLOBALREF(HTProtocol,HTRlogin);
+extern GLOBALREF(HTProtocol,HTTn3270);
+#else
+GLOBALREF HTProtocol HTTelnet;
+GLOBALREF HTProtocol HTRlogin;
+GLOBALREF HTProtocol HTTn3270;
+#endif /* GLOBALREF_IS_MACRO */
+#endif /* HTTELNET_H */
+
+/*
+
+   end */
diff --git a/WWW/Library/Implementation/HTUU.c b/WWW/Library/Implementation/HTUU.c
new file mode 100644
index 00000000..68cad23b
--- /dev/null
+++ b/WWW/Library/Implementation/HTUU.c
@@ -0,0 +1,207 @@
+
+/* MODULE							HTUU.c
+**			UUENCODE AND UUDECODE
+**
+** ACKNOWLEDGEMENT:
+**	This code is taken from rpem distribution, and was originally
+**	written by Mark Riordan.
+**
+** AUTHORS:
+**	MR	Mark Riordan	riordanmr@clvax1.cl.msu.edu
+**	AL	Ari Luotonen	luotonen@dxcern.cern.ch
+**
+** HISTORY:
+**	Added as part of the WWW library and edited to conform
+**	with the WWW project coding standards by:	AL  5 Aug 1993
+**	Originally written by:				MR 12 Aug 1990
+**	Original header text:
+** -------------------------------------------------------------
+**  File containing routines to convert a buffer
+**  of bytes to/from RFC 1113 printable encoding format.
+**
+**  This technique is similar to the familiar Unix uuencode
+**  format in that it maps 6 binary bits to one ASCII
+**  character (or more aptly, 3 binary bytes to 4 ASCII
+**  characters).  However, RFC 1113 does not use the same
+**  mapping to printable characters as uuencode.
+**
+**  Mark Riordan   12 August 1990 and 17 Feb 1991.
+**  This code is hereby placed in the public domain.
+** -------------------------------------------------------------
+**
+** BUGS:
+**
+**
+*/
+
+#include "HTUtils.h"
+#include "HTUU.h"
+
+#include "LYLeaks.h"
+
+PRIVATE char six2pr[64] = {
+    'A','B','C','D','E','F','G','H','I','J','K','L','M',
+    'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
+    'a','b','c','d','e','f','g','h','i','j','k','l','m',
+    'n','o','p','q','r','s','t','u','v','w','x','y','z',
+    '0','1','2','3','4','5','6','7','8','9','+','/'
+};
+
+PRIVATE unsigned char pr2six[256];
+
+
+/*--- function HTUU_encode -----------------------------------------------
+ *
+ *   Encode a single line of binary data to a standard format that
+ *   uses only printing ASCII characters (but takes up 33% more bytes).
+ *
+ *    Entry    bufin    points to a buffer of bytes.  If nbytes is not
+ *                      a multiple of three, then the byte just beyond
+ *                      the last byte in the buffer must be 0.
+ *             nbytes   is the number of bytes in that buffer.
+ *                      This cannot be more than 48.
+ *             bufcoded points to an output buffer.  Be sure that this
+ *                      can hold at least 1 + (4*nbytes)/3 characters.
+ *
+ *    Exit     bufcoded contains the coded line.  The first 4*nbytes/3 bytes
+ *                      contain printing ASCII characters representing
+ *                      those binary bytes. This may include one or
+ *                      two '=' characters used as padding at the end.
+ *                      The last byte is a zero byte.
+ *             Returns the number of ASCII characters in "bufcoded".
+ */
+PUBLIC int HTUU_encode ARGS3(unsigned char *,	bufin,
+			     unsigned int,	nbytes,
+			     char *,		bufcoded)
+{
+/* ENC is the basic 1 character encoding function to make a char printing */
+#define ENC(c) six2pr[c]
+
+   register char *outptr = bufcoded;
+   unsigned int i;
+   /* This doesn't seem to be needed (AL):   register unsigned char *inptr  = bufin; */
+
+   for (i=0; i<nbytes; i += 3) {
+      *(outptr++) = ENC(*bufin >> 2);            /* c1 */
+      *(outptr++) = ENC(((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)); /*c2*/
+      *(outptr++) = ENC(((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03));/*c3*/
+      *(outptr++) = ENC(bufin[2] & 077);         /* c4 */
+
+      bufin += 3;
+   }
+
+   /* If nbytes was not a multiple of 3, then we have encoded too
+    * many characters.  Adjust appropriately.
+    */
+   if(i == nbytes+1) {
+      /* There were only 2 bytes in that last group */
+      outptr[-1] = '=';
+   } else if(i == nbytes+2) {
+      /* There was only 1 byte in that last group */
+      outptr[-1] = '=';
+      outptr[-2] = '=';
+   }
+   *outptr = '\0';
+   return(outptr - bufcoded);
+}
+
+
+/*--- function HTUU_decode ------------------------------------------------
+ *
+ *  Decode an ASCII-encoded buffer back to its original binary form.
+ *
+ *    Entry    bufcoded    points to a uuencoded string.  It is 
+ *                         terminated by any character not in
+ *                         the printable character table six2pr, but
+ *                         leading whitespace is stripped.
+ *             bufplain    points to the output buffer; must be big
+ *                         enough to hold the decoded string (generally
+ *                         shorter than the encoded string) plus
+ *                         as many as two extra bytes used during
+ *                         the decoding process.
+ *             outbufsize  is the maximum number of bytes that
+ *                         can fit in bufplain.
+ *
+ *    Exit     Returns the number of binary bytes decoded.
+ *             bufplain    contains these bytes.
+ */
+PUBLIC int HTUU_decode ARGS3(char *,		bufcoded,
+			     unsigned char *,	bufplain,
+			     int,		outbufsize)
+{
+/* single character decode */
+#define DEC(c) pr2six[c]
+#define MAXVAL 63
+
+   static int first = 1;
+
+   int nbytesdecoded, j;
+   register char *bufin = bufcoded;
+   register unsigned char *bufout = bufplain;
+   register int nprbytes;
+
+   /* If this is the first call, initialize the mapping table.
+    * This code should work even on non-ASCII machines.
+    */
+   if(first) {
+      first = 0;
+      for(j=0; j<256; j++) pr2six[j] = MAXVAL+1;
+
+      for(j=0; j<64; j++) pr2six[(unsigned char)six2pr[j]] = (unsigned char)j;
+#if 0
+      pr2six['A']= 0; pr2six['B']= 1; pr2six['C']= 2; pr2six['D']= 3; 
+      pr2six['E']= 4; pr2six['F']= 5; pr2six['G']= 6; pr2six['H']= 7; 
+      pr2six['I']= 8; pr2six['J']= 9; pr2six['K']=10; pr2six['L']=11; 
+      pr2six['M']=12; pr2six['N']=13; pr2six['O']=14; pr2six['P']=15; 
+      pr2six['Q']=16; pr2six['R']=17; pr2six['S']=18; pr2six['T']=19; 
+      pr2six['U']=20; pr2six['V']=21; pr2six['W']=22; pr2six['X']=23; 
+      pr2six['Y']=24; pr2six['Z']=25; pr2six['a']=26; pr2six['b']=27; 
+      pr2six['c']=28; pr2six['d']=29; pr2six['e']=30; pr2six['f']=31; 
+      pr2six['g']=32; pr2six['h']=33; pr2six['i']=34; pr2six['j']=35; 
+      pr2six['k']=36; pr2six['l']=37; pr2six['m']=38; pr2six['n']=39; 
+      pr2six['o']=40; pr2six['p']=41; pr2six['q']=42; pr2six['r']=43; 
+      pr2six['s']=44; pr2six['t']=45; pr2six['u']=46; pr2six['v']=47; 
+      pr2six['w']=48; pr2six['x']=49; pr2six['y']=50; pr2six['z']=51; 
+      pr2six['0']=52; pr2six['1']=53; pr2six['2']=54; pr2six['3']=55; 
+      pr2six['4']=56; pr2six['5']=57; pr2six['6']=58; pr2six['7']=59; 
+      pr2six['8']=60; pr2six['9']=61; pr2six['+']=62; pr2six['/']=63;
+#endif
+   }
+
+   /* Strip leading whitespace. */
+
+   while(*bufcoded==' ' || *bufcoded == '\t') bufcoded++;
+
+   /* Figure out how many characters are in the input buffer.
+    * If this would decode into more bytes than would fit into
+    * the output buffer, adjust the number of input bytes downwards.
+    */
+   bufin = bufcoded;
+   while(pr2six[(unsigned char)*(bufin++)] <= MAXVAL);
+   nprbytes = bufin - bufcoded - 1;
+   nbytesdecoded = ((nprbytes+3)/4) * 3;
+   if(nbytesdecoded > outbufsize) {
+      nprbytes = (outbufsize*4)/3;
+   }
+
+   bufin = bufcoded;
+   
+   while (nprbytes > 0) {
+      *(bufout++) = (unsigned char) (DEC(*bufin) << 2 | DEC(bufin[1]) >> 4);
+      *(bufout++) = (unsigned char) (DEC(bufin[1]) << 4 | DEC(bufin[2]) >> 2);
+      *(bufout++) = (unsigned char) (DEC(bufin[2]) << 6 | DEC(bufin[3]));
+      bufin += 4;
+      nprbytes -= 4;
+   }
+   
+   if(nprbytes & 03) {
+      if(pr2six[bufin[-2]] > MAXVAL) {
+         nbytesdecoded -= 2;
+      } else {
+         nbytesdecoded -= 1;
+      }
+   }
+
+   return(nbytesdecoded);
+}
+
diff --git a/WWW/Library/Implementation/HTUU.h b/WWW/Library/Implementation/HTUU.h
new file mode 100644
index 00000000..05874e24
--- /dev/null
+++ b/WWW/Library/Implementation/HTUU.h
@@ -0,0 +1,29 @@
+/*                              ENCODING TO PRINTABLE CHARACTERS
+                                             
+   File module provides functions HTUU_encode() and HTUU_decode() which convert a buffer
+   of bytes to/from RFC 1113 printable encoding format. This technique is similar to the
+   familiar Unix uuencode format in that it maps 6 binary bits to one ASCII character (or
+   more aptly, 3 binary bytes to 4 ASCII characters).  However, RFC 1113 does not use the
+   same mapping to printable characters as uuencode.
+   
+ */
+
+#ifndef HTUU_H
+#define HTUU_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+
+PUBLIC int HTUU_encode PARAMS((unsigned char *bufin,
+                               unsigned int nbytes,
+                               char *bufcoded));
+
+PUBLIC int HTUU_decode PARAMS((char *bufcoded,
+                               unsigned char *bufplain,
+                               int outbufsize));
+
+#endif
+/*
+
+   End of file.  */
diff --git a/WWW/Library/Implementation/HTUtils.h b/WWW/Library/Implementation/HTUtils.h
new file mode 100644
index 00000000..3eb54ea0
--- /dev/null
+++ b/WWW/Library/Implementation/HTUtils.h
@@ -0,0 +1,311 @@
+/*                                                    Utitlity macros for the W3 code library
+                                  MACROS FOR GENERAL USE
+                                             
+   Generates: HTUtils.h
+   
+   See also: the system dependent file "tcp.h"
+   
+ */
+
+#ifndef DEBUG
+#define DEBUG   /* Noone ever turns this off as trace is too important */
+#endif          /* Keeep option for really small memory applications tho */
+                
+#ifndef HTUTILS_H
+#define HTUTILS_H
+
+#ifdef _WINDOWS                         /* SCW */
+#include "windef.h"
+#define BOOLEAN_DEFINED
+#endif
+
+#ifdef SHORT_NAMES
+#define WWW_TraceFlag HTTrFlag
+#endif
+
+/*
+
+Debug message control.
+
+ */
+#ifndef STDIO_H
+#include <stdio.h>
+#define STDIO_H
+#endif
+
+#ifdef DEBUG
+#define TRACE (WWW_TraceFlag)
+#define PROGRESS(str) printf(str)
+        extern int WWW_TraceFlag;
+#else
+#define TRACE 0
+#define PROGRESS(str) /* nothing for now */
+#endif
+
+#define CTRACE if(TRACE)fprintf
+#define tfp stderr
+
+/*
+
+  ERROR TYPE
+  
+   This is passed back when streams are aborted. It might be nice to have some structure
+   of error messages, numbers, and recursive pointers to reasons. Curently this is a
+   placeholder for something more sophisticated.
+   
+ */
+typedef void * HTError;                 /* Unused at present -- best definition? */
+
+/*
+
+Standard C library for malloc() etc
+
+ */
+#ifdef DGUX
+#include <stdlib.h>
+#endif /* DGUX */
+
+#ifdef vax
+#ifdef unix
+#define ultrix  /* Assume vax+unix=ultrix */
+#endif /* unix */
+#endif /* vax */
+
+#ifndef VMS
+#ifndef ultrix
+
+#ifdef NeXT
+#include <libc.h>       /* NeXT */
+#endif /* NeXT */
+#ifndef MACH /* Vincent.Cate@furmint.nectar.cs.cmu.edu */
+#ifndef __STRICT_BSD__
+#include <stdlib.h>
+#endif /* !__STRICT_BSD__ */
+#endif /* !MACH */
+
+#else /* ultrix: */
+
+#include <malloc.h>
+#include <memory.h>
+#include <stdio.h>
+#include <stdlib.h>   /* ANSI */   /* BSN */
+
+#endif /* !ultrix */
+#else   /* VMS: */
+
+#include <stdlib.h>
+#include <unixlib.h>
+#include <ctype.h>
+#if defined(VAXC) && !defined(__DECC)
+#define malloc	VAXC$MALLOC_OPT
+#define calloc	VAXC$CALLOC_OPT
+#define free	VAXC$FREE_OPT
+#define cfree	VAXC$CFREE_OPT
+#define realloc	VAXC$REALLOC_OPT
+#endif /* VAXC && !__DECC */
+
+#endif /* !VMS */
+
+/*
+
+Macros for declarations
+
+ */
+#define PUBLIC                  /* Accessible outside this module     */
+#define PRIVATE static          /* Accessible only within this module */
+
+#ifdef __STDC__
+#define CONST const             /* "const" only exists in STDC */
+#define NOPARAMS (void)
+#define PARAMS(parameter_list) parameter_list
+#define NOARGS (void)
+#define ARGS1(t,a) \
+                (t a)
+#define ARGS2(t,a,u,b) \
+                (t a, u b)
+#define ARGS3(t,a,u,b,v,c) \
+                (t a, u b, v c)
+#define ARGS4(t,a,u,b,v,c,w,d) \
+                (t a, u b, v c, w d)
+#define ARGS5(t,a,u,b,v,c,w,d,x,e) \
+                (t a, u b, v c, w d, x e)
+#define ARGS6(t,a,u,b,v,c,w,d,x,e,y,f) \
+                (t a, u b, v c, w d, x e, y f)
+#define ARGS7(t,a,u,b,v,c,w,d,x,e,y,f,z,g) \
+                (t a, u b, v c, w d, x e, y f, z g)
+#define ARGS8(t,a,u,b,v,c,w,d,x,e,y,f,z,g,s,h) \
+                (t a, u b, v c, w d, x e, y f, z g, s h)
+#define ARGS9(t,a,u,b,v,c,w,d,x,e,y,f,z,g,s,h,r,i) \
+                (t a, u b, v c, w d, x e, y f, z g, s h, r i)
+#define ARGS10(t,a,u,b,v,c,w,d,x,e,y,f,z,g,s,h,r,i,q,j) \
+                (t a, u b, v c, w d, x e, y f, z g, s h, r i, q j)
+
+#else  /* not ANSI */
+
+#ifndef _WINDOWS
+#define CONST
+#endif
+#define NOPARAMS ()
+#define PARAMS(parameter_list) ()
+#define NOARGS ()
+#define ARGS1(t,a) (a) \
+                t a;
+#define ARGS2(t,a,u,b) (a,b) \
+                t a; u b;
+#define ARGS3(t,a,u,b,v,c) (a,b,c) \
+                t a; u b; v c;
+#define ARGS4(t,a,u,b,v,c,w,d) (a,b,c,d) \
+                t a; u b; v c; w d;
+#define ARGS5(t,a,u,b,v,c,w,d,x,e) (a,b,c,d,e) \
+                t a; u b; v c; w d; x e;
+#define ARGS6(t,a,u,b,v,c,w,d,x,e,y,f) (a,b,c,d,e,f) \
+                t a; u b; v c; w d; x e; y f;
+#define ARGS7(t,a,u,b,v,c,w,d,x,e,y,f,z,g) (a,b,c,d,e,f,g) \
+                t a; u b; v c; w d; x e; y f; z g;
+#define ARGS8(t,a,u,b,v,c,w,d,x,e,y,f,z,g,s,h) (a,b,c,d,e,f,g,h) \
+                t a; u b; v c; w d; x e; y f; z g; s h;
+#define ARGS9(t,a,u,b,v,c,w,d,x,e,y,f,z,g,s,h,r,i) (a,b,c,d,e,f,g,h,i) \
+                t a; u b; v c; w d; x e; y f; z g; s h; r i;
+#define ARGS10(t,a,u,b,v,c,w,d,x,e,y,f,z,g,s,h,r,i,q,j) (a,b,c,d,e,f,g,h,i,j) \
+                t a; u b; v c; w d; x e; y f; z g; s h; r i; q j;
+                
+        
+#endif /* __STDC__ (ANSI) */
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+/*
+
+Booleans
+
+ */
+/* Note: GOOD and BAD are already defined (differently) on RS6000 aix */
+/* #define GOOD(status) ((status)38;1)   VMS style status: test bit 0         */
+/* #define BAD(status)  (!GOOD(status))  Bit 0 set if OK, otherwise clear   */
+
+#ifndef _WINDOWS
+#ifndef BOOLEAN_DEFINED
+        typedef char    BOOLEAN;                /* Logical value */
+#ifndef CURSES
+#ifndef TRUE
+#define TRUE    (BOOLEAN)1
+#define FALSE   (BOOLEAN)0
+#endif
+#endif   /*  CURSES  */
+#endif   /* _WINDOWS */
+#define BOOLEAN_DEFINED
+#endif
+
+#ifndef BOOL
+#define BOOL BOOLEAN
+#endif
+#ifndef YES
+#define YES (BOOLEAN)1
+#define NO (BOOLEAN)0
+#endif
+
+#define TCP_PORT 80     /* Allocated to http by Jon Postel/ISI 24-Jan-92 */
+#define OLD_TCP_PORT 2784       /* Try the old one if no answer on 80 */
+#define DNP_OBJ 80      /* This one doesn't look busy, but we must check */
+                        /* That one was for decnet */
+
+/*      Inline Function WHITE: Is character c white space? */
+/*      For speed, include all control characters */
+
+#define WHITE(c) (((unsigned char)(TOASCII(c))) <= 32)
+
+
+/*
+
+Sucess (>=0) and failure (<0) codes
+
+ */
+
+#define HT_REDIRECTING 29996
+#define HT_LOADED 29997                 /* Instead of a socket */
+#define HT_INTERRUPTED -29998
+#define HT_NOT_LOADED -29999
+#define HT_OK           0               /* Generic success*/
+
+#define HT_NO_ACCESS    -10             /* Access not available */
+#define HT_FORBIDDEN    -11             /* Access forbidden */
+#define HT_INTERNAL     -12             /* Weird -- should never happen. */
+#define HT_BAD_EOF      -12             /* Premature EOF */
+
+
+#include "HTString.h"   /* String utilities */
+
+#ifndef va_arg
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#endif
+
+/*
+
+Out Of Memory checking for malloc() return:
+
+ */
+#ifndef __FILE__
+#define __FILE__ ""
+#define __LINE__ ""
+#endif
+
+#include "LYexit.h"
+
+#define outofmem(file, func) \
+ { fprintf(stderr, "%s %s: out of memory.\nProgram aborted.\n", file, func); \
+  exit(-1);}
+/* extern void outofmem PARAMS((const char *fname, const char *func)); */
+
+
+/*
+
+  WHO PUT THESE IN AND WHAT ARE THEY ANYWAY?
+  
+ */
+#ifdef THEY_WILL_BE_REMOVED
+extern void msg_init PARAMS((int height));
+extern void msg_printf PARAMS((int y, const char *fmt, ...));
+extern void msg_exit PARAMS((int wait_for_key));
+#endif
+
+/*
+
+Upper- and Lowercase macros
+
+   The problem here is that toupper(x) is not defined officially unless isupper(x) is.
+   These macros are CERTAINLY needed on #if defined(pyr) || define(mips) or BDSI
+   platforms. For safefy, we make them mandatory.
+   
+ */
+#include <ctype.h>
+#include <string.h>
+
+#ifndef TOLOWER
+  /* Pyramid and Mips can't uppercase non-alpha */
+#define TOLOWER(c) (isupper((unsigned char)c) ? tolower((unsigned char)c) : (c))
+#define TOUPPER(c) (islower((unsigned char)c) ? toupper((unsigned char)c) : (c))
+#endif /* ndef TOLOWER */
+
+/*
+
+The local equivalents of CR and LF
+
+   We can check for these after net ascii text has been converted to the local
+   representation. Similarly, we include them in strings to be sent as net ascii after
+   translation.
+   
+ */
+#define LF   FROMASCII('\012')  /* ASCII line feed LOCAL EQUIVALENT */
+#define CR   FROMASCII('\015')  /* Will be converted to ^M for transmission */
+
+#endif /* HTUTILS_H */
+
+/*
+
+   end of utilities */
diff --git a/WWW/Library/Implementation/HTVMSUtils.c b/WWW/Library/Implementation/HTVMSUtils.c
new file mode 100644
index 00000000..3d97115f
--- /dev/null
+++ b/WWW/Library/Implementation/HTVMSUtils.c
@@ -0,0 +1,1220 @@
+
+/* MODULE							HTVMSUtil.c
+**		VMS Utility Routines
+**
+** AUTHORS:
+**	MD	Mark Donszelmann    duns@vxdeop.cern.ch
+**
+** HISTORY:
+**	14 Nov 93  MD	Written
+**
+** BUGS:
+**
+**
+*/
+
+#include "HTUtils.h"
+#include "tcp.h"
+#include "HTFormat.h"
+#include "HTStream.h"
+#include "HTVMSUtils.h"
+/*#include <stdio.h> included by HTUtils.h -- FM */
+/*#include <unixlib.h> included by HTUtils.h -- FM */
+#include <ssdef.h>
+#include <jpidef.h>
+#include <prvdef.h>
+#include <acldef.h>
+#include <chpdef.h>
+#include <descrip.h>
+#include <lib$routines.h>
+#include <starlet.h>
+#include <rmsdef.h>
+
+#include "LYLeaks.h"
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+#define INFINITY 512            	/* File name length @@ FIXME */
+
+PUBLIC BOOL HTVMSFileVersions=FALSE; /* Include version numbers in listing? */
+
+typedef struct {
+   unsigned long BufferLength : 16;
+   unsigned long ItemCode : 16;
+   unsigned long BufferAddress : 32;
+   unsigned long ReturnLengthAddress : 32;
+} ItemStruct;
+
+extern CONST char * HTHostName NOPARAMS;
+
+/* PUBLIC							HTVMS_authSysPrv()
+**		CHECKS IF THIS PROCESS IS AUTHORIZED TO ENABLE SYSPRV
+** ON ENTRY:
+**	No arguments.
+**
+** ON EXIT:
+**	returns	YES if SYSPRV is authorized
+*/
+PUBLIC BOOL HTVMS_authSysPrv NOARGS
+{
+unsigned long Result;
+ItemStruct ItemList[2];
+unsigned long Length;
+unsigned long Buffer[2];
+
+  /* fill Item */
+  ItemList[0].BufferLength = sizeof(Buffer);
+  ItemList[0].BufferAddress = (unsigned long)Buffer;
+  ItemList[0].ReturnLengthAddress = (unsigned long)&Length;
+  ItemList[0].ItemCode = JPI$_AUTHPRIV;
+
+  /* terminate list */
+  ItemList[1].ItemCode = 0;
+  ItemList[1].BufferLength = 0;
+
+  /* call system */
+  Result = sys$getjpiw(0, 0, 0, ItemList, 0, 0, 0);
+
+  if (Result != SS$_NORMAL)
+     return(NO);  
+
+  if (Buffer[0] & PRV$M_SYSPRV)
+     return(YES);
+
+  return(NO);  
+}
+
+
+
+/* PUBLIC							HTVMS_enableSysPrv()
+**		ENABLES SYSPRV
+** ON ENTRY:
+**	No arguments.
+**
+** ON EXIT:
+**	
+*/
+PUBLIC void HTVMS_enableSysPrv NOARGS
+{
+unsigned long Result;
+unsigned long Prv[2], PreviousPrv[2];
+
+   Prv[0] = PRV$M_SYSPRV;
+   Prv[1] = 0;
+   Result = sys$setprv(1,&Prv,0,&PreviousPrv);
+
+   if (TRACE) {
+      if (Result == SS$_NORMAL) {
+         if (!(PreviousPrv[0] & PRV$M_SYSPRV)) {
+            fprintf(stderr, "HTVMS_enableSysPrv: Enabled SYSPRV\n");
+         }
+      }
+   }
+}
+
+
+
+/* PUBLIC							HTVMS_disableSysPrv()
+**		DISABLES SYSPRV
+** ON ENTRY:
+**	No arguments.
+**
+** ON EXIT:
+**	
+*/
+PUBLIC void HTVMS_disableSysPrv NOARGS
+{
+unsigned long Result;
+unsigned long Prv[2], PreviousPrv[2];
+
+   Prv[0] = PRV$M_SYSPRV;
+   Prv[1] = 0;
+   Result = sys$setprv(0,&Prv,0,&PreviousPrv);
+
+   if (TRACE) {
+      if (Result == SS$_NORMAL) {
+         if (PreviousPrv[0] & PRV$M_SYSPRV) {
+            fprintf(stderr, "HTVMS_disableSysPrv: Disabled SYSPRV\n");
+         }
+      }
+   }
+}
+
+
+
+/* PUBLIC							HTVMS_checkAccess()
+**		CHECKS ACCESS TO FILE FOR CERTAIN USER
+** ON ENTRY:
+**	FileName	The file to be accessed
+**	UserName	Name of the user to check access for.
+**			User nobody, represented by "" is given NO for an answer
+**	Method		Name of the method to be chceked
+**
+** ON EXIT:
+**	returns YES if access is allowed
+**	
+*/
+PUBLIC BOOL HTVMS_checkAccess ARGS3(
+	CONST char *, FileName,
+	CONST char *, UserName,
+	CONST char *, Method)
+{
+unsigned long Result;
+ItemStruct ItemList[2];
+unsigned long Length;
+unsigned long Buffer;
+unsigned long ObjType;
+
+char *VmsName;
+
+struct dsc$descriptor_s FileNameDesc;
+struct dsc$descriptor_s UserNameDesc;
+
+char *colon;
+
+   /* user nobody should access as from account under which server is running */
+   if (0 == strcmp(UserName,""))
+      return(NO);
+
+   /* check Filename and convert */
+   colon = strchr(FileName,':');
+   if (colon)
+      VmsName = HTVMS_name("",colon+1);
+   else
+      VmsName = HTVMS_name("",FileName);
+
+   /* check for GET */
+   if (0 == strcmp(Method,"GET"))
+   {
+     /* fill Item */
+     ItemList[0].BufferLength = sizeof(Buffer);
+     ItemList[0].BufferAddress = (unsigned long)&Buffer;
+     ItemList[0].ReturnLengthAddress = (unsigned long)&Length;
+     ItemList[0].ItemCode = CHP$_FLAGS;
+
+     /* terminate list */
+     ItemList[1].ItemCode = 0;
+     ItemList[1].BufferLength = 0;
+
+     /* fill input */
+     ObjType = ACL$C_FILE;
+     Buffer = CHP$M_READ;
+     UserNameDesc.dsc$w_length = strlen(UserName);
+     UserNameDesc.dsc$b_dtype = DSC$K_DTYPE_T;
+     UserNameDesc.dsc$b_class = DSC$K_CLASS_S;
+     UserNameDesc.dsc$a_pointer = (char *)UserName;
+     FileNameDesc.dsc$w_length = strlen(VmsName);
+     FileNameDesc.dsc$b_dtype = DSC$K_DTYPE_T;
+     FileNameDesc.dsc$b_class = DSC$K_CLASS_S;
+     FileNameDesc.dsc$a_pointer = VmsName;
+
+     /* call system */
+     Result = sys$check_access(&ObjType,&FileNameDesc,&UserNameDesc,ItemList);
+
+     if (Result == SS$_NORMAL)
+        return(YES);
+     else
+        return(NO);
+   }
+
+   return(NO);
+}
+
+
+
+/* PUBLIC							HTVMS_wwwName()
+**		CONVERTS VMS Name into WWW Name 
+** ON ENTRY:
+**	vmsname		VMS file specification (NO NODE)
+**
+** ON EXIT:
+**	returns 	www file specification
+**
+** EXAMPLES:
+**	vmsname				wwwname
+**	DISK$USER 			disk$user
+**	DISK$USER: 			/disk$user/
+**	DISK$USER:[DUNS] 		/disk$user/duns
+**	DISK$USER:[DUNS.ECHO] 		/disk$user/duns/echo
+**	[DUNS] 				duns
+**	[DUNS.ECHO] 			duns/echo
+**	[DUNS.ECHO.-.TRANS] 		duns/echo/../trans
+**	[DUNS.ECHO.--.TRANS] 		duns/echo/../../trans
+**	[.DUNS] 			duns
+**	[.DUNS.ECHO] 			duns/echo
+**	[.DUNS.ECHO]TEST.COM 		duns/echo/test.com 
+**	TEST.COM 			test.com
+**
+**	
+*/
+PUBLIC char * HTVMS_wwwName ARGS1(
+	char *, vmsname)
+{
+static char wwwname[256];
+char *src, *dst;
+int dir;
+   dst = wwwname;
+   src = vmsname;
+   dir = 0;
+   if (strchr(src,':')) *(dst++) = '/';
+   for ( ; *src != '\0' ; src++)
+   {
+      switch(*src)
+      {
+         case ':':  *(dst++) = '/'; break;
+         case '-': if (dir)
+	 	   {
+	 	      if ((*(src-1)=='[' || *(src-1)=='.' || *(src-1)=='-') && 
+		          (*(src+1)=='.' || *(src+1)=='-'))
+		      {
+		          *(dst++) = '/';
+                          *(dst++) = '.'; 
+                          *(dst++) = '.';
+		      }
+		      else
+		          *(dst++) = '-';
+		   }
+		   else
+		   {
+		      if (*(src-1) == ']') *(dst++) = '/';
+		      *(dst++) = '-';
+		   }
+                   break;
+         case '.': if (dir)
+                   {
+                      if (*(src-1) != '[') *(dst++) = '/';
+                   }
+                   else
+		   {
+		      if (*(src-1) == ']') *(dst++) = '/';
+                      *(dst++) = '.';
+		   }
+                   break;
+         case '[': dir = 1; break;
+         case ']': dir = 0; break;
+         default:  if (*(src-1) == ']') *(dst++) = '/';
+                   *(dst++) = *src; 
+                   break;
+      }
+   }
+   *(dst++) = '\0';
+   return(wwwname);
+}
+
+
+/* PUBLIC							HTVMS_name()
+**		CONVERTS WWW name into a VMS name
+** ON ENTRY:
+**	nn		Node Name (optional)
+**	fn		WWW file name
+**
+** ON EXIT:
+**	returns 	vms file specification
+**
+** Bug:	Returns pointer to static -- non-reentrant
+*/
+PUBLIC char * HTVMS_name ARGS2(
+	CONST char *, nn, 
+	CONST char *, fn)
+{
+
+/*	We try converting the filename into Files-11 syntax. That is, we assume
+**	first that the file is, like us, on a VMS node. We try remote
+**	(or local) DECnet access. Files-11, VMS, VAX and DECnet
+**	are trademarks of Digital Equipment Corporation. 
+**	The node is assumed to be local if the hostname WITHOUT DOMAIN
+**	matches the local one. @@@
+*/
+    static char vmsname[INFINITY];	/* returned */
+    char * filename = (char*)malloc(strlen(fn)+1);
+    char * nodename = (char*)malloc(strlen(nn)+2+1);	/* Copies to hack */
+    char *second;		/* 2nd slash */
+    char *last;			/* last slash */
+    
+    char * hostname = (char *)HTHostName();
+
+    if (!filename || !nodename) outofmem(__FILE__, "HTVMSname");
+    strcpy(filename, fn);
+    strcpy(nodename, "");	/* On same node? Yes if node names match */
+    if (strncmp(nn,"localhost",9)) {
+        char *p, *q;
+        for (p=hostname, q=(char *)nn;
+	     *p && *p!='.' && *q && *q!='.'; p++, q++){
+	    if (TOUPPER(*p)!=TOUPPER(*q)) {
+	        strcpy(nodename, nn);
+		q = strchr(nodename, '.');	/* Mismatch */
+		if (q) *q=0;			/* Chop domain */
+		strcat(nodename, "::");		/* Try decnet anyway */
+		break;
+	    }
+	}
+    }
+
+    second = strchr(filename+1, '/');		/* 2nd slash */
+    last = strrchr(filename, '/');	/* last slash */
+        
+    if (!second) {				/* Only one slash */
+	sprintf(vmsname, "%s%s", nodename, filename + 1);
+    } else if(second==last) {		/* Exactly two slashes */
+	*second = 0;		/* Split filename from disk */
+	sprintf(vmsname, "%s%s:%s", nodename, filename+1, second+1);
+	*second = '/';	/* restore */
+    } else { 				/* More than two slashes */
+	char * p;
+	*second = 0;		/* Split disk from directories */
+	*last = 0;		/* Split dir from filename */
+	sprintf(vmsname, "%s%s:[%s]%s",
+		nodename, filename+1, second+1, last+1);
+	*second = *last = '/';	/* restore filename */
+	for (p=strchr(vmsname, '['); *p!=']'; p++)
+	    if (*p=='/') *p='.';	/* Convert dir sep.  to dots */
+    }
+    FREE(nodename);
+    FREE(filename);
+    return vmsname;
+}
+
+/*
+**	The code below is for directory browsing by VMS Curses clients.
+**	It is based on the newer WWWLib's HTDirBrw.c. - Foteos Macrides
+*/
+PUBLIC int HTStat ARGS2(
+	CONST char *, filename, 
+	stat_t *, info)
+{
+   /* 
+      the following stuff does not work in VMS with a normal stat...
+      -->   /disk$user/duns/www if www is a directory
+		is statted like: 	/disk$user/duns/www.dir 
+		after a normal stat has failed
+      -->   /disk$user/duns	if duns is a toplevel directory
+		is statted like:	/disk$user/000000/duns.dir
+      -->   /disk$user since disk$user is a device
+		is statted like:	/disk$user/000000/000000.dir
+      -->   /			
+		searches all devices, no solution yet...
+      -->   /vxcern!/disk$cr/wwwteam/login.com
+		is not statted but granted with fake information...
+   */
+int Result;
+int Len;
+char *Ptr, *Ptr2;
+char Name[256];
+
+   /* try normal stat... */
+   Result = stat((char *)filename,info);
+   if (Result == 0)
+      return(Result);
+
+   /* make local copy */
+   strcpy(Name,filename);
+
+#ifdef NOT_USED
+   /* if filename contains a node specification (! or ::), we will try to access
+      the file via DECNET, but we do not stat it..., just return success 
+      with some fake information... */
+   if (HTVMS_checkDecnet(Name))
+   {
+      /* set up fake info, only the one we use... */
+      info->st_dev = NULL;
+      info->st_ino[0] = 0;
+      info->st_ino[1] = 0;
+      info->st_ino[2] = 0;
+      info->st_mode = S_IFREG | S_IREAD;	/* assume it is a regular Readable file */
+      info->st_nlink = NULL;
+      info->st_uid = 0;
+      info->st_gid = 0;
+      info->st_rdev = 0;
+      info->st_size = 0;
+      info->st_atime = time(NULL);
+      info->st_mtime = time(NULL);
+      info->st_ctime = time(NULL);
+
+      return(0);
+   }
+#endif /* NOT_USED */
+
+   /* failed,so do device search in case root is requested */
+   if (!strcmp(Name,"/"))
+   {  /* root requested */
+      return(-1);
+   }
+   
+   /* failed so this might be a directory, add '.dir' */
+   Len = strlen(Name);
+   if (Name[Len-1] == '/')
+      Name[Len-1] = '\0';
+   
+   /* fail in case of device */
+   Ptr = strchr(Name+1,'/');
+   if ((Ptr == NULL) && (Name[0] == '/'))
+   {  /* device only... */
+      strcat(Name,"/000000/000000");
+   }
+   
+   if (Ptr != NULL)
+   {  /* correct filename in case of toplevel dir */
+      Ptr2 = strchr(Ptr+1,'/');
+      if ((Ptr2 == NULL) && (Name[0] == '/'))
+      {
+         char End[256];
+         strcpy(End,Ptr);
+         *(Ptr+1) = '\0';
+         strcat(Name,"000000");
+         strcat(Name,End);
+      }
+   }
+
+   /* try in case a file on toplevel directory or .DIR was alreadyt specified */
+   Result = stat(Name,info);
+   if (Result == 0)
+      return(Result);
+
+   /* add .DIR and try again */
+   strcat(Name,".dir");
+   Result = stat(Name,info);
+   return(Result);
+}
+
+/*** "dirent.h" ***/
+/* #include <types.h>	already in tcp.h */
+
+#ifndef	_POSIX_SOURCE
+#define	d_ino	d_fileno	/* compatability */
+#ifndef	NULL
+#define	NULL	0
+#endif
+#endif	/* !_POSIX_SOURCE */
+
+typedef	struct __dirdesc {
+#if 0
+	int	dd_fd;		/* file descriptor */
+	long	dd_loc;		/* buf offset of entry from last readddir() */
+	long	dd_size;	/* amount of valid data in buffer */
+	long	dd_bsize;	/* amount of entries read at a time */
+	long	dd_off;		/* Current offset in dir (for telldir) */
+	char	*dd_buf;	/* directory data buffer */
+#endif
+	long 	context;	/* context descriptor for LIB$FIND_FILE calls */
+	char	dirname[255+1];	/* keeps the directory name, including *.* */
+	struct dsc$descriptor_s dirname_desc;	/* descriptor of dirname */
+} DIR;
+
+PRIVATE	DIR *HTVMSopendir(char *dirname);
+PRIVATE	struct dirent *HTVMSreaddir(DIR *dirp);
+PRIVATE	int HTVMSclosedir(DIR *dirp);
+#if 0
+#ifndef	_POSIX_SOURCE
+extern	void seekdir(/* DIR *dirp, int loc */);
+extern	long telldir(/* DIR *dirp */);
+#endif	/* POSIX_SOURCE */
+extern	void rewinddir(/* DIR *dirp */);
+
+#ifndef	lint
+#define	rewinddir(dirp)	seekdir((dirp), (long)0)
+#endif
+#endif /* not defined for VMS */
+
+/*** #include "sys_dirent.h" ***/
+/*** "sys_dirent.h" ***/
+struct	dirent {
+#if 0
+	off_t		d_off;		/* offset of next disk dir entry */
+#endif
+	unsigned long	d_fileno;	/* file number of entry */
+#if 0
+	unsigned short	d_reclen;	/* length of this record */
+#endif
+	unsigned short	d_namlen;	/* length of string in d_name */
+	char		d_name[255+1];	/* name (up to MAXNAMLEN + 1) */
+};
+
+#ifndef	_POSIX_SOURCE
+/*
+ * It's unlikely to change, but make sure that sizeof d_name above is
+ * at least MAXNAMLEN + 1 (more may be added for padding).
+ */
+#define	MAXNAMLEN	255
+/*
+ * The macro DIRSIZ(dp) gives the minimum amount of space required to represent
+ * a directory entry.  For any directory entry dp->d_reclen >= DIRSIZ(dp).
+ * Specific filesystem types may use this macro to construct the value
+ * for d_reclen.
+ */
+#undef	DIRSIZ
+#define	DIRSIZ(dp) \
+	(((sizeof(struct dirent) - (MAXNAMLEN+1) + ((dp)->d_namlen+1)) +3) & ~3)
+
+#endif	/* !_POSIX_SOURCE */
+
+
+PRIVATE DIR *HTVMSopendir(char *dirname)
+{
+static DIR dir;
+char *closebracket;
+long status;
+struct dsc$descriptor_s entryname_desc;
+struct dsc$descriptor_s dirname_desc;
+char DirEntry[256];
+char VMSentry[256];
+char UnixEntry[256];
+int index;
+char *dot;
+
+   /* check if directory exists */
+   /* dirname can look like /disk$user/duns/www/test/multi    */
+   /* or like               /disk$user/duns/www/test/multi/   */
+   /* DirEntry should look like     disk$user:[duns.www.test]multi in both cases */
+   /* dir.dirname should look like  disk$user:[duns.www.test.multi] */
+   strcpy(UnixEntry,dirname);
+   if (UnixEntry[strlen(UnixEntry)-1] != '/')
+      strcat(UnixEntry,"/");
+
+   strcpy(DirEntry, HTVMS_name("",UnixEntry));
+   strcpy(dir.dirname, DirEntry);
+   index = strlen(DirEntry) - 1;
+
+   if (DirEntry[index] == ']')
+      DirEntry[index] = '\0';
+
+   if ((dot = strrchr(DirEntry,'.')) == NULL)
+   {  /* convert disk$user:[duns] into disk$user:[000000]duns.dir */
+      char *openbr = strrchr(DirEntry,'[');
+      if (!openbr)
+      { /* convert disk$user: into disk$user:[000000]000000.dir */
+         strcpy(dir.dirname, DirEntry);
+         strcat(dir.dirname, "[000000]");
+         strcat(DirEntry,"[000000]000000.dir");
+      }
+      else
+      {
+         char End[256];
+         strcpy(End,openbr+1);
+         *(openbr+1) = '\0';
+         strcat(DirEntry,"000000]");
+         strcat(DirEntry,End);
+         strcat(DirEntry,".dir");
+      }
+   }
+   else
+   {
+      *dot = ']';   
+      strcat(DirEntry,".dir");
+   }
+
+   dir.context = 0;
+   dirname_desc.dsc$w_length = strlen(DirEntry);
+   dirname_desc.dsc$b_dtype = DSC$K_DTYPE_T;
+   dirname_desc.dsc$b_class = DSC$K_CLASS_S;
+   dirname_desc.dsc$a_pointer = (char *)&(DirEntry);
+
+   /* look for the directory */
+   entryname_desc.dsc$w_length = 255;
+   entryname_desc.dsc$b_dtype = DSC$K_DTYPE_T;
+   entryname_desc.dsc$b_class = DSC$K_CLASS_S;
+   entryname_desc.dsc$a_pointer = VMSentry;
+
+   status = lib$find_file(&(dirname_desc), 
+                          &entryname_desc, 
+                          &(dir.context),
+                          0,0,0,0);
+   if (!(status & 0x01))
+   { /* directory not found */
+      return(NULL);
+   }
+
+#if 0
+   /* now correct dirname, which looks like disk$user:[duns.www.test]multi */
+   /* and should look like disk$user:[duns.www.test.multi] */
+   closebracket = strchr(dir.dirname,']');
+   *closebracket = '.';
+   closebracket = strstr(dir.dirname,".dir");
+   *closebracket = '\0';
+   strcat(dir.dirname,"]");
+#endif
+
+   if (HTVMSFileVersions)
+       strcat(dir.dirname,"*.*;*");
+   else
+       strcat(dir.dirname,"*.*");
+   dir.context = 0;
+   dir.dirname_desc.dsc$w_length = strlen(dir.dirname);
+   dir.dirname_desc.dsc$b_dtype = DSC$K_DTYPE_T;
+   dir.dirname_desc.dsc$b_class = DSC$K_CLASS_S;
+   dir.dirname_desc.dsc$a_pointer = (char *)&(dir.dirname);
+   return(&dir);
+}
+
+PRIVATE struct dirent *HTVMSreaddir(DIR *dirp)
+{
+static struct dirent entry;
+long status;
+struct dsc$descriptor_s entryname_desc;
+char *space, *slash;
+char VMSentry[256];
+char *UnixEntry;
+
+   entryname_desc.dsc$w_length = 255;
+   entryname_desc.dsc$b_dtype = DSC$K_DTYPE_T;
+   entryname_desc.dsc$b_class = DSC$K_CLASS_S;
+   entryname_desc.dsc$a_pointer = VMSentry;
+
+   status = lib$find_file(&(dirp->dirname_desc), 
+                          &entryname_desc, 
+                          &(dirp->context),
+                          0,0,0,0);
+   if (status == RMS$_NMF)
+   { /* no more files */
+      return(NULL);
+   }
+   else
+   { /* ok */
+      if (!(status & 0x01)) return(0);
+      if (HTVMSFileVersions)
+          space = strchr(VMSentry,' ');
+      else
+          space = strchr(VMSentry,';');
+      if (space)
+         *space = '\0';
+
+      /* convert to unix style... */
+      UnixEntry = HTVMS_wwwName(VMSentry);
+      slash = strrchr(UnixEntry,'/') + 1;
+      strcpy(entry.d_name,slash);
+      entry.d_namlen = strlen(entry.d_name);
+      entry.d_fileno = 1;
+      return(&entry);
+   }
+}
+
+PRIVATE int HTVMSclosedir(DIR *dirp)
+{
+long status;
+
+   status = lib$find_file_end(&(dirp->context));
+   if (!(status & 0x01)) exit(status);
+   dirp->context = 0;
+   return(0);
+}
+
+#include "HTAnchor.h"
+#include "HTParse.h"
+#include "HTBTree.h"
+#include "HTFile.h"	/* For HTFileFormat() */
+#include "HTAlert.h"
+/*
+**  Hypertext object building machinery.
+*/
+#include "HTML.h"
+#define PUTC(c) (*targetClass.put_character)(target, c)
+#define PUTS(s) (*targetClass.put_string)(target, s)
+#define START(e) (*targetClass.start_element)(target, e, 0, 0, 0)
+#define END(e) (*targetClass.end_element)(target, e, 0)
+#define FREE_TARGET (*targetClass._free)(target)
+#define ABORT_TARGET (*targetClass._free)(target)
+struct _HTStructured {
+	CONST HTStructuredClass *	isa;
+	/* ... */
+};
+
+#define STRUCT_DIRENT struct dirent
+
+PRIVATE char * months[12] = {
+    "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
+};
+
+typedef struct _VMSEntryInfo {
+    char *       filename;
+    char *       type;
+    char *       date;
+    unsigned int size;
+    BOOLEAN      display;  /* show this entry? */
+} VMSEntryInfo;
+
+PRIVATE void free_VMSEntryInfo_struct_contents ARGS1(VMSEntryInfo *,entry_info)
+{
+    if (entry_info) {
+	FREE(entry_info->filename);
+	FREE(entry_info->type);
+	FREE(entry_info->date);
+    }
+   /* dont free the struct */
+}
+
+#define FILE_BY_NAME 0 
+#define FILE_BY_TYPE 1
+#define FILE_BY_SIZE 2
+#define FILE_BY_DATE 3
+extern BOOLEAN HTfileSortMethod;  /* specifies the method of sorting */
+
+PUBLIC int compare_VMSEntryInfo_structs ARGS2(VMSEntryInfo *,entry1, 
+					      VMSEntryInfo *,entry2)
+{
+    int i, status;
+    char date1[16], date2[16], time1[8], time2[8], month[4];
+
+    switch(HTfileSortMethod)
+      {
+        case FILE_BY_SIZE:
+			/* both equal or both 0 */
+                        if(entry1->size == entry2->size)
+			    return(strcasecomp(entry1->filename, 
+					       entry2->filename));
+			else
+			    if(entry1->size > entry2->size)
+				return(1);
+			    else
+				return(-1);
+                        break;
+        case FILE_BY_TYPE:
+                        if(entry1->type && entry2->type) {
+                            status = strcasecomp(entry1->type, entry2->type);
+			    if(status)
+				return(status);
+			    /* else fall to filename comparison */
+			}
+                        return (strcasecomp(entry1->filename, 
+					    entry2->filename));
+                        break;
+        case FILE_BY_DATE:
+                        if(entry1->date && entry2->date) {
+			    /*
+			    ** Make sure we have the correct length. - FM
+			    */
+			    if (strlen(entry1->date) != 12 ||
+			        strlen(entry2->date) != 12) {
+				return (strcasecomp(entry1->filename, 
+						    entry2->filename));
+			    }
+			    /*
+			    ** Set up for sorting in reverse
+			    ** chronological order. - FM
+			    */
+			    if (entry1->date[7] != ' ') {
+			        strcpy(date1, "9999");
+				strcpy(time1, (char *)&entry1->date[7]);
+			    } else {
+				strcpy(date1, (char *)&entry1->date[8]);
+			        strcpy(time1, "00:00");
+			    }
+			    strncpy(month, entry1->date, 3);
+			    month[3] = '\0';
+			    for (i = 0; i < 12; i++) {
+			        if (!strcasecomp(month, months[i])) {
+				    break;
+				}
+			    }
+			    i++;
+			    sprintf(month, "%s%d", (i < 10 ? "0" : ""), i);
+			    strcat(date1, month);
+			    strncat(date1, (char *)&entry1->date[4], 2);
+			    date1[8] = '\0';
+			    if (date1[6] == ' ') {
+			        date1[6] = '0';
+			    }
+			    strcat(date1, time1);
+			    if (entry2->date[7] != ' ') {
+			        strcpy(date2, "9999");
+				strcpy(time2, (char *)&entry2->date[7]);
+			    } else {
+				strcpy(date2, (char *)&entry2->date[8]);
+			        strcpy(time2, "00:00");
+			    }
+			    strncpy(month, entry2->date, 3);
+			    month[3] = '\0';
+			    for (i = 0; i < 12; i++) {
+			        if (!strcasecomp(month, months[i])) {
+				    break;
+				}
+			    }
+			    i++;
+			    sprintf(month, "%s%d", (i < 10 ? "0" : ""), i);
+			    strcat(date2, month);
+			    strncat(date2, (char *)&entry2->date[4], 2);
+			    date2[8] = '\0';
+			    if (date2[6] == ' ') {
+			        date2[6] = '0';
+			    }
+			    strcat(date2, time2);
+			    /*
+			    ** Do the comparison. - FM
+			    */
+                            status = strcasecomp(date2, date1);
+			    if(status)
+				return(status);
+			    /* else fall to filename comparison */
+			}
+                        return (strcasecomp(entry1->filename,
+					    entry2->filename));
+                        break;
+        case FILE_BY_NAME:
+        default:
+                        return (strcmp(entry1->filename, 
+					    entry2->filename));
+      }
+}
+
+
+/*						    	HTVMSBrowseDir()
+**
+**	This function generates a directory listing as an HTML-object
+**	for local file URL's.  It assumes the first two elements of
+**	of the path are a device followed by a directory:
+**
+**		file://localhost/device/directory[/[foo]]
+**
+**	Will not accept 000000 as a directory name.
+**	Will offer links to parent through the top directory, unless
+**	a terminal slash was included in the calling URL.
+**
+**	Returns HT_LOADED on success, HTLoadError() messages on error.
+**
+**	Developed for Lynx by Foteos Macrides (macrides@sci.wfeb.edu).
+*/
+PUBLIC int HTVMSBrowseDir ARGS4(
+	CONST char *,		address,
+	HTParentAnchor *,	anchor,
+	HTFormat,		format_out,
+	HTStream *,		sink
+)
+{
+    HTStructured* target;
+    HTStructuredClass targetClass;
+    char *pathname = HTParse(address, "", PARSE_PATH + PARSE_PUNCTUATION);
+    char *tail = NULL;
+    char *title = NULL;
+    char *header = NULL;
+    char *parent = NULL;
+    char *relative = NULL;
+    char *cp, *cp1;
+    int  pathend;
+    DIR *dp;
+    struct stat file_info;
+    time_t NowTime;
+    static char ThisYear[8];
+    VMSEntryInfo *entry_info=0;
+    char string_buffer[64];
+    extern BOOLEAN no_dotfiles, show_dotfiles;
+
+    HTUnEscape(pathname);
+    CTRACE(stderr,"HTVMSBrowseDir: Browsing `%s\'\n", pathname);
+
+    /*
+     *  Require at least two elements (presumably a device and directory)
+     *  and disallow the device root (000000 directory).  Symbolic paths
+     *  (e.g., sys$help) should have been translated and expanded (e.g.,
+     *  to /sys$sysroot/syshlp) before calling this routine.
+     */
+    if (((*pathname != '/') ||
+    	 (cp=strchr(pathname+1, '/')) == NULL ||
+	 *(cp+1) == '\0' ||
+	 0==strncmp((cp+1), "000000", 6)) ||
+        (dp=HTVMSopendir(pathname)) == NULL) {
+        FREE(pathname);
+    	return HTLoadError(sink, 403, "Could not access directory.");
+    }
+
+    /*
+     *  Set up the output stream.
+     */
+    _HTProgress ("Building directory listing...");
+    target = HTML_new(anchor, format_out, sink);
+    targetClass = *(target->isa);
+
+    /*
+     *  Set up the offset string of the anchor reference,
+     *  and strings for the title and header.
+     */
+    cp = strrchr(pathname, '/');  /* find lastslash */
+    StrAllocCopy(tail, (cp+1)); /* take slash off the beginning */
+    if (*tail != '\0') {
+        StrAllocCopy(title, tail);
+	*cp = '\0';
+	if ((cp1=strrchr(pathname, '/')) != NULL &&
+	    cp1 != pathname &&
+	    strncmp((cp1+1), "000000", 6))
+	    StrAllocCopy(parent, (cp1+1));
+	*cp = '/';
+    } else {
+        pathname[strlen(pathname)-1] = '\0';
+	cp = strrchr(pathname, '/');
+	StrAllocCopy(title, (cp+1));
+	pathname[strlen(pathname)] = '/';
+    }
+    StrAllocCopy(header, pathname);
+
+    /*
+     *  Initialize path name for HTStat().
+     */
+    pathend = strlen(pathname);
+    if (*(pathname+pathend-1) != '/') {
+	StrAllocCat(pathname, "/");
+	pathend++;
+    }
+    
+    /*
+     *  Output the title and header.
+     */
+    START(HTML_HTML);
+    PUTS("\n");
+    START(HTML_HEAD);
+    PUTS("\n");
+    HTUnEscape(title);
+    START(HTML_TITLE);
+    PUTS(title);
+    PUTS(" directory");
+    END(HTML_TITLE);
+    PUTS("\n");
+    FREE(title);
+    END(HTML_HEAD);
+    PUTS("\n");
+    START(HTML_BODY);
+    PUTS("\n");
+    HTUnEscape(header);
+    START(HTML_H1);
+    PUTS(header);
+    END(HTML_H1);
+    PUTS("\n");
+    if (HTDirReadme == HT_DIR_README_TOP) {
+        FILE * fp;
+	if (header[strlen(header)-1] != '/')
+	    StrAllocCat(header, "/");
+	StrAllocCat(header, HT_DIR_README_FILE);
+        if ((fp = fopen(header,  "r")) != NULL) {
+	    START(HTML_PRE);
+	    for(;;) {
+	        char c = fgetc(fp);
+	        if (c == (char)EOF)
+		    break;
+#ifdef NOTDEFINED
+	        switch (c) {
+	    	    case '&':
+		    case '<':
+		    case '>':
+			PUTC('&');
+			PUTC('#');
+			PUTC((char)(c / 10));
+			PUTC((char) (c % 10));
+			PUTC(';');
+			break;
+		    default:
+			PUTC(c);
+	        }
+#else
+		PUTC(c);
+#endif /* NOTDEFINED */
+	    }
+	    END(HTML_PRE);
+	    fclose(fp);
+        } 
+    }
+    FREE(header);
+    if (parent) {
+	relative = (char*) malloc(strlen(tail) + 4);
+	if (relative == NULL)
+		outofmem(__FILE__, "HTVMSBrowseDir");
+	sprintf(relative, "%s/..", tail);
+	HTStartAnchor(target, "", relative);
+	PUTS("Up to ");
+	HTUnEscape(parent);
+	PUTS(parent);
+	END(HTML_A);
+	START(HTML_P);
+	PUTS("\n");
+	FREE(relative);
+	FREE(parent);
+    }
+
+    /*
+     *  Set up the date comparison.
+     */
+    NowTime = time(NULL);
+    strcpy(ThisYear, (char *)ctime(&NowTime)+20);
+    ThisYear[4] = '\0';
+
+    /*
+     * Now, generate the Btree and put it out to the output stream.
+     */
+    {
+	char dottest = 2;	/* To avoid two strcmp() each time */
+	STRUCT_DIRENT *dirbuf;
+	HTBTree *bt;
+
+	/* Set up sort key and initialize BTree */
+	bt = HTBTree_new((HTComparer) compare_VMSEntryInfo_structs);
+
+	/* Build tree */
+	while ((dirbuf = HTVMSreaddir(dp))) {
+	    HTAtom *encoding = NULL;
+	    HTFormat format;
+
+	    /* Skip if not used */
+	    if (!dirbuf->d_ino)	{
+		continue;
+	    }
+	    
+	    /* Current and parent directories are never shown in list */
+	    if (dottest && (!strcmp(dirbuf->d_name, ".") ||
+			    !strcmp(dirbuf->d_name, ".."))) {
+		dottest--;
+		continue;
+	    }
+
+	    /* Don't show the selective enabling file
+	     * unless version numbers are included */
+	    if (!strcasecomp(dirbuf->d_name, HT_DIR_ENABLE_FILE)) {
+		continue;
+	    }
+
+	    /* Skip files beginning with a dot? */
+	    if ((no_dotfiles || !show_dotfiles) && *dirbuf->d_name == '.') {
+		continue;
+	    }
+
+	    /* OK, make an lstat() and get a key ready. */
+	    *(pathname+pathend) = '\0';
+	    StrAllocCat(pathname, dirbuf->d_name);
+	    if (HTStat(pathname, &file_info)) {
+		/* for VMS the failure here means the file is not readable...
+		   we however continue to browse through the directory... */
+                continue;
+	    }
+            entry_info = (VMSEntryInfo *)malloc(sizeof(VMSEntryInfo));    
+	    if (entry_info == NULL)
+		outofmem(__FILE__, "HTVMSBrowseDir");
+	    entry_info->type = 0;
+	    entry_info->size = 0;
+	    entry_info->date = 0;
+	    entry_info->filename = 0;
+	    entry_info->display = TRUE;
+
+	    /* Get the type */
+	    format = HTFileFormat(dirbuf->d_name, &encoding);
+	    if(!strncmp(HTAtom_name(format), "application",11)) 
+	      {
+		   cp = HTAtom_name(format) + 12;
+		   if(!strncmp(cp,"x-",2))
+			cp+=2;
+	      }
+	    else
+		cp = HTAtom_name(format);
+	    StrAllocCopy(entry_info->type, cp);
+
+	    StrAllocCopy(entry_info->filename, dirbuf->d_name);
+	    if ((file_info.st_mode & S_IFMT) == S_IFDIR) {
+	        /* strip .DIR part... */
+                char *dot;
+                dot = strstr(entry_info->filename, ".DIR");
+                if (dot)
+                   *dot = '\0';
+		StrAllocCopy(entry_info->type, "Directory");
+	    }
+
+	    /* Get the date */
+	    {
+	        char *t = (char *)ctime((CONST time_t *)&file_info.st_ctime);
+		*(t+24) = '\0';
+
+	        StrAllocCopy(entry_info->date, (t+4));
+		*((entry_info->date)+7) = '\0';
+		if ((atoi((t+19))) < atoi(ThisYear))
+		    StrAllocCat(entry_info->date,  (t+19));
+		else {
+		    StrAllocCat(entry_info->date, (t+11));
+		    *((entry_info->date)+12) = '\0';
+		}
+	    }
+
+	    /* Get the size */
+	    if ((file_info.st_mode & S_IFMT) != S_IFDIR)
+	        entry_info->size = (unsigned int)file_info.st_size;
+	    else
+	        entry_info->size = 0;
+
+	    /* Now, update the BTree etc. */
+	    if(entry_info->display)
+	      {
+		 CTRACE(stderr,"Adding file to BTree: %s\n",
+						      entry_info->filename);
+	         HTBTree_add(bt, (VMSEntryInfo *)entry_info); 
+	      }
+
+	} /* End while HTVMSreaddir() */
+
+	FREE(pathname);
+	HTVMSclosedir(dp);
+
+	START(HTML_PRE);
+	/*
+	 * Run through the BTree printing out in order
+	 */
+	{
+	    HTBTElement * ele;
+	    int i;
+	    for (ele = HTBTree_next(bt, NULL);
+		 ele != NULL;
+		 ele = HTBTree_next(bt, ele))
+	    {
+		entry_info = (VMSEntryInfo *)HTBTree_object(ele);
+
+		/* Output the date */
+		if(entry_info->date) 
+		       {
+		             PUTS(entry_info->date);
+		             PUTS("  ");
+		       }
+		else
+			PUTS("     * ");
+
+		/* Output the type */
+		if(entry_info->type) 
+		  {
+		    for(i = 0; entry_info->type[i] != '\0' && i < 15; i++)
+		        PUTC(entry_info->type[i]);
+		    for(; i < 17; i++)
+		        PUTC(' ');
+
+		  }
+
+		/* Output the link for the name */
+		HTDirEntry(target, tail, entry_info->filename);  
+		PUTS(entry_info->filename);
+		END(HTML_A);
+
+                /* Output the size */
+		if(entry_info->size) 
+		  {
+		          if(entry_info->size < 1024)
+			      sprintf(string_buffer,"  %d bytes",
+							entry_info->size);
+			  else
+			      sprintf(string_buffer,"  %dKb",
+							entry_info->size/1024);
+			  PUTS(string_buffer);
+		  }
+
+		PUTC('\n'); /* end of this entry */
+
+		free_VMSEntryInfo_struct_contents(entry_info);
+	    }
+	}
+
+	HTBTreeAndObject_free(bt);
+
+    } /* End of both BTree loops */
+
+    /*
+     *  Complete the output stream.
+     */
+    END(HTML_PRE);
+    PUTS("\n");
+    END(HTML_BODY);
+    PUTS("\n");
+    END(HTML_HTML);
+    PUTS("\n");
+    FREE(tail);
+    FREE_TARGET;
+
+    return HT_LOADED;
+
+} /* End of directory reading section */
diff --git a/WWW/Library/Implementation/HTVMSUtils.h b/WWW/Library/Implementation/HTVMSUtils.h
new file mode 100644
index 00000000..e055d672
--- /dev/null
+++ b/WWW/Library/Implementation/HTVMSUtils.h
@@ -0,0 +1,116 @@
+/*             VMS specific routines
+                                             
+ */
+
+#ifndef HTVMSUTIL_H
+#define HTVMSUTIL_H
+
+#include <stat.h>
+
+extern BOOL HTVMSFileVersions;	/* Include version numbers in listing? */
+
+/* PUBLIC							HTVMS_authSysPrv()
+**		CHECKS IF THIS PROCESS IS AUTHORIZED TO ENABLE SYSPRV
+** ON ENTRY:
+**	No arguments.
+**
+** ON EXIT:
+**	returns	YES if SYSPRV is authorized
+*/
+PUBLIC BOOL HTVMS_authSysPrv NOPARAMS;
+
+
+/* PUBLIC							HTVMS_enableSysPrv()
+**		ENABLES SYSPRV
+** ON ENTRY:
+**	No arguments.
+**
+** ON EXIT:
+**	
+*/
+PUBLIC void HTVMS_enableSysPrv NOPARAMS;
+
+
+/* PUBLIC							HTVMS_disableSysPrv()
+**		DISABLES SYSPRV
+** ON ENTRY:
+**	No arguments.
+**
+** ON EXIT:
+**	
+*/
+PUBLIC void HTVMS_disableSysPrv NOPARAMS;
+
+/* PUBLIC							HTVMS_checkAccess()
+**		CHECKS ACCESS TO FILE FOR CERTAIN USER
+** ON ENTRY:
+**	FileName	The file to be accessed
+**	UserName	Name of the user to check access for
+**
+** ON EXIT:
+**	returns YES if access is allowed
+**	
+*/
+PUBLIC BOOL HTVMS_checkAccess PARAMS((
+	CONST char * FileName,
+	CONST char * UserName,
+	CONST char * Method));
+
+
+/* PUBLIC							HTVMS_wwwName()
+**		CONVERTS VMS Name into WWW Name 
+** ON ENTRY:
+**	vmsname		VMS file specification (NO NODE)
+**
+** ON EXIT:
+**	returns 	www file specification
+**
+** EXAMPLES:
+**	vmsname				wwwname
+**	DISK$USER 			disk$user
+**	DISK$USER: 			/disk$user/
+**	DISK$USER:[DUNS] 		/disk$user/duns
+**	DISK$USER:[DUNS.ECHO] 		/disk$user/duns/echo
+**	[DUNS] 				duns
+**	[DUNS.ECHO] 			duns/echo
+**	[DUNS.ECHO.-.TRANS] 		duns/echo/../trans
+**	[DUNS.ECHO.--.TRANS] 		duns/echo/../../trans
+**	[.DUNS] 			duns
+**	[.DUNS.ECHO] 			duns/echo
+**	[.DUNS.ECHO]TEST.COM 		duns/echo/test.com 
+**	TEST.COM 			test.com
+**
+**	
+*/
+PUBLIC char * HTVMS_wwwName PARAMS((
+	char * vmsname));
+
+/* PUBLIC							HTVMS_name()
+**		CONVERTS WWW name into a VMS name
+** ON ENTRY:
+**	nn		Node Name (optional)
+**	fn		WWW file name
+**
+** ON EXIT:
+**	returns 	vms file specification
+**
+** Bug:	Returns pointer to static -- non-reentrant
+*/
+PUBLIC char * HTVMS_name PARAMS((
+	CONST char * nn, 
+	CONST char * fn));
+
+PUBLIC int HTStat PARAMS((
+	CONST char * filename,
+        stat_t * info));
+
+PUBLIC int HTVMSBrowseDir PARAMS((
+	CONST char * address,
+	HTParentAnchor * anchor,
+	HTFormat format_out,
+	HTStream * sink));
+
+#endif /* not HTVMSUTIL_H */
+/*
+
+   End of file HTVMSUtil.h.  */
diff --git a/WWW/Library/Implementation/HTVMS_WaisProt.c b/WWW/Library/Implementation/HTVMS_WaisProt.c
new file mode 100644
index 00000000..ee3a51c0
--- /dev/null
+++ b/WWW/Library/Implementation/HTVMS_WaisProt.c
@@ -0,0 +1,2501 @@
+/*							HTVMS_WAISProt.c
+**
+**	Adaptation for Lynx by F.Macrides (macrides@sci.wfeb.edu)
+**
+**	31-May-1994 FM	Initial version.
+**
+**----------------------------------------------------------------------*/
+
+/*
+**	Routines originally from WProt.c -- FM
+**
+**----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE:
+   No guarantees or restrictions.  See the readme file for the full standard
+   disclaimer.	
+  
+   3.26.90	Harry Morris, morris@think.com
+   3.30.90  Harry Morris 
+   -	removed chunk code from WAISSearchAPDU,
+   -	added makeWAISQueryType1Query() and readWAISType1Query() which replace
+   makeWAISQueryTerms() and makeWAISQueryDocs().
+   4.11.90  HWM - generalized conditional includes (see c-dialect.h)
+   - renamed makeWAISType1Query() to makeWAISTextQuery()
+   renamed readWAISType1Query() to readWAISTextQuery()
+   5.29.90  TS - fixed bug in makeWAISQueryDocs
+   added CSTFreeWAISFoo functions
+*/
+
+#define _C_WAIS_protocol_
+
+/*	This file implements the Z39.50 extensions required for WAIS 
+*/
+
+#include "HTUtils.h"
+#include "tcp.h"
+#include "HTVMS_WaisUI.h"
+#include "HTVMS_WaisProt.h"
+
+#include "LYLeaks.h"
+
+
+/* very rough estimates of the size of an object */
+#define DefWAISInitResponseSize		(size_t)200
+#define DefWAISSearchSize			(size_t)3000
+#define DefWAISSearchResponseSize	(size_t)6000
+#define DefWAISPresentSize			(size_t)1000
+#define DefWAISPresentResponseSize	(size_t)6000
+#define DefWAISDocHeaderSize		(size_t)500
+#define DefWAISShortHeaderSize		(size_t)200
+#define DefWAISLongHeaderSize		(size_t)800
+#define DefWAISDocTextSize			(size_t)6000
+#define DefWAISDocHeadlineSize		(size_t)500
+#define DefWAISDocCodeSize			(size_t)500
+
+#define RESERVE_SPACE_FOR_WAIS_HEADER(len)	\
+     if (*len > 0)				\
+     	*len -= header_len;
+
+/*----------------------------------------------------------------------*/
+
+static unsigned long userInfoTagSize _AP((data_tag tag,
+					  unsigned long length));
+
+static unsigned long
+userInfoTagSize(tag,length)
+data_tag tag;
+unsigned long length;
+/* return the number of bytes required to write the user info tag and
+   length 
+ */
+{
+  unsigned long size;
+
+  /* calculate bytes required to represent tag.  max tag is 16K */
+  size = writtenCompressedIntSize(tag);
+  size += writtenCompressedIntSize(length);
+      
+  return(size);
+}   
+
+/*----------------------------------------------------------------------*/
+
+static char* writeUserInfoHeader _AP((data_tag tag,long infoSize,	
+				      long estHeaderSize,char* buffer,
+				      long* len));
+
+static char*
+writeUserInfoHeader(tag,infoSize,estHeaderSize,buffer,len)
+data_tag tag;
+long infoSize;
+long estHeaderSize;
+char* buffer;
+long* len;
+/* write the tag and size, making sure the info fits.  return the true end
+   of the info (after adjustment) note that the argument infoSize includes
+   estHeaderSize.  Note that the argument len is the number of bytes remaining
+   in the buffer.  Since we write the tag and size at the begining of the
+   buffer (in space that we reserved) we don't want to pass len the calls which
+   do that writing. 
+ */
+{
+  long dummyLen = 100;		/* plenty of space for a tag and size */
+  char* buf = buffer;
+  long realSize = infoSize - estHeaderSize;
+  long realHeaderSize = userInfoTagSize(tag,realSize);
+
+  if (buffer == NULL || *len == 0)
+    return(NULL);
+  
+  /* write the tag */
+  buf = writeTag(tag,buf,&dummyLen);
+  
+  /* see if the if the header size was correct. if not,
+     we have to shift the info to fit the real header size */
+  if (estHeaderSize != realHeaderSize)
+    {				/* make sure there is enough space */
+      CHECK_FOR_SPACE_LEFT(realHeaderSize - estHeaderSize,len);
+      memmove(buffer + realHeaderSize,buffer + estHeaderSize,(size_t)(realSize));
+    }
+   
+  /* write the size */
+  writeCompressedInteger(realSize,buf,&dummyLen);
+  
+  /* return the true end of buffer */
+  return(buffer + realHeaderSize + realSize);
+}
+
+/*----------------------------------------------------------------------*/
+
+static char* readUserInfoHeader _AP((data_tag* tag,unsigned long* num,
+				     char* buffer));
+
+static char*
+readUserInfoHeader(tag,num,buffer)
+data_tag* tag;
+unsigned long* num;
+char* buffer;
+/* read the tag and size */
+{
+  char* buf = buffer;
+  buf = readTag(tag,buf);
+  buf = readCompressedInteger(num,buf);
+  return(buf); 
+}
+
+/*----------------------------------------------------------------------*/
+
+WAISInitResponse* 
+makeWAISInitResponse(chunkCode,
+		     chunkIDLen,
+		     chunkMarker,
+		     highlightMarker,
+		     deHighlightMarker,
+		     newLineChars)
+long chunkCode;
+long chunkIDLen;
+char* chunkMarker;
+char* highlightMarker;
+char* deHighlightMarker;
+char* newLineChars;
+/* create a WAIS init response object */
+{
+  WAISInitResponse* init = (WAISInitResponse*)s_malloc((size_t)sizeof(WAISInitResponse));
+
+  init->ChunkCode = chunkCode;	/* note: none are copied! */
+  init->ChunkIDLength = chunkIDLen;
+  init->ChunkMarker = chunkMarker;
+  init->HighlightMarker = highlightMarker;
+  init->DeHighlightMarker = deHighlightMarker;
+  init->NewlineCharacters = newLineChars;
+  
+  return(init);
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+freeWAISInitResponse(init)
+WAISInitResponse* init;
+/* free an object made with makeWAISInitResponse */
+{
+  s_free(init->ChunkMarker);
+  s_free(init->HighlightMarker);
+  s_free(init->DeHighlightMarker);
+  s_free(init->NewlineCharacters);
+  s_free(init);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+writeInitResponseInfo(init,buffer,len)
+InitResponseAPDU* init;
+char* buffer;
+long* len;
+/* write an init response object */
+{
+  unsigned long header_len = userInfoTagSize(DT_UserInformationLength,
+					     DefWAISInitResponseSize);
+  char* buf = buffer + header_len;
+  WAISInitResponse* info = (WAISInitResponse*)init->UserInformationField;
+  unsigned long size;
+  
+  RESERVE_SPACE_FOR_WAIS_HEADER(len);
+    
+  buf = writeNum(info->ChunkCode,DT_ChunkCode,buf,len);
+  buf = writeNum(info->ChunkIDLength,DT_ChunkIDLength,buf,len);
+  buf = writeString(info->ChunkMarker,DT_ChunkMarker,buf,len);
+  buf = writeString(info->HighlightMarker,DT_HighlightMarker,buf,len);
+  buf = writeString(info->DeHighlightMarker,DT_DeHighlightMarker,buf,len);
+  buf = writeString(info->NewlineCharacters,DT_NewlineCharacters,buf,len);
+  
+  /* now write the header and size */
+  size = buf - buffer; 
+  buf = writeUserInfoHeader(DT_UserInformationLength,size,header_len,buffer,len);
+  
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readInitResponseInfo(info,buffer)
+void** info;
+char* buffer;
+/* read an init response object */
+{
+  char* buf = buffer;
+  unsigned long size; 
+  unsigned long headerSize;
+  data_tag tag;
+  long chunkCode,chunkIDLen;
+  char* chunkMarker = NULL;
+  char* highlightMarker = NULL;
+  char* deHighlightMarker = NULL;
+  char* newLineChars = NULL;
+  
+  chunkCode = chunkIDLen = UNUSED;
+  
+  buf = readUserInfoHeader(&tag,&size,buf);
+  headerSize = buf - buffer;
+    
+  while (buf < (buffer + size + headerSize))
+    { data_tag tag = peekTag(buf);
+      switch (tag)
+	{ case DT_ChunkCode:
+	    buf = readNum(&chunkCode,buf);
+	    break;
+	  case DT_ChunkIDLength:
+	    buf = readNum(&chunkIDLen,buf);
+	    break;
+	  case DT_ChunkMarker:
+	    buf = readString(&chunkMarker,buf);
+	    break;
+	  case DT_HighlightMarker:
+	    buf = readString(&highlightMarker,buf);
+	    break;
+	  case DT_DeHighlightMarker:
+	    buf = readString(&deHighlightMarker,buf);
+	    break;
+	  case DT_NewlineCharacters:
+	    buf = readString(&newLineChars,buf);
+	    break;
+	  default:
+	    s_free(highlightMarker);
+	    s_free(deHighlightMarker);
+	    s_free(newLineChars);
+	    REPORT_READ_ERROR(buf);
+	    break;
+	  }
+    }
+  	  
+  *info = (void *)makeWAISInitResponse(chunkCode,chunkIDLen,chunkMarker,
+			       highlightMarker,deHighlightMarker,
+			       newLineChars);
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+WAISSearch* 
+makeWAISSearch(seedWords,
+	       docs,
+	       textList,
+	       dateFactor,
+	       beginDateRange,
+	       endDateRange,
+	       maxDocsRetrieved)
+char* seedWords;
+DocObj** docs;
+char** textList;
+long dateFactor;
+char* beginDateRange;
+char* endDateRange;
+long maxDocsRetrieved;
+
+/* create a type 3 query object */
+{ 
+  WAISSearch* query = (WAISSearch*)s_malloc((size_t)sizeof(WAISSearch));
+
+  query->SeedWords = seedWords;	/* not copied! */
+  query->Docs = docs;		/* not copied! */
+  query->TextList = textList;	/* not copied! */
+  query->DateFactor = dateFactor;
+  query->BeginDateRange = beginDateRange;
+  query->EndDateRange = endDateRange;
+  query->MaxDocumentsRetrieved = maxDocsRetrieved;
+  
+  return(query);
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+freeWAISSearch(query)
+WAISSearch* query;
+
+/* destroy an object made with makeWAISSearch() */
+{
+  void* ptr = NULL;
+  long i;
+  
+  s_free(query->SeedWords);
+  
+  if (query->Docs != NULL)
+    for (i = 0,ptr = (void *)query->Docs[i]; ptr != NULL; ptr = (void *)query->Docs[++i])
+      freeDocObj((DocObj*)ptr);
+  s_free(query->Docs);
+   
+  if (query->TextList != NULL)	/* XXX revisit when textlist is fully defined */
+    for (i = 0,ptr = (void *)query->TextList[i]; ptr != NULL; ptr = (void *)query->TextList[++i])
+      s_free(ptr);
+  s_free(query->TextList);
+
+  s_free(query->BeginDateRange);
+  s_free(query->EndDateRange);
+  s_free(query);
+}
+
+/*----------------------------------------------------------------------*/
+
+DocObj* 
+makeDocObjUsingWholeDocument(docID,type)
+any* docID;
+char* type;
+
+/* construct a document object using byte chunks - only for use by
+   servers */
+{
+  DocObj* doc = (DocObj*)s_malloc((size_t)sizeof(DocObj));
+  doc->DocumentID = docID;		/* not copied! */
+  doc->Type = type;		/* not copied! */
+  doc->ChunkCode = CT_document;
+  return(doc);
+}
+
+/*----------------------------------------------------------------------*/
+
+DocObj* 
+makeDocObjUsingLines(docID,type,start,end)
+any* docID;
+char* type;
+long start;
+long end;
+
+/* construct a document object using line chunks - only for use by
+   servers */
+{
+  DocObj* doc = (DocObj*)s_malloc((size_t)sizeof(DocObj));
+  doc->ChunkCode = CT_line;
+  doc->DocumentID = docID;		/* not copied */
+  doc->Type = type;		/* not copied! */
+  doc->ChunkStart.Pos = start;
+  doc->ChunkEnd.Pos = end;
+  return(doc);
+}
+
+/*----------------------------------------------------------------------*/
+
+DocObj* 
+makeDocObjUsingBytes(docID,type,start,end)
+any* docID;
+char* type;
+long start;
+long end;
+
+/* construct a document object using byte chunks - only for use by
+   servers */
+{
+  DocObj* doc = (DocObj*)s_malloc((size_t)sizeof(DocObj));
+  doc->ChunkCode = CT_byte;
+  doc->DocumentID = docID;		/* not copied */
+  doc->Type = type;		/* not copied! */
+  doc->ChunkStart.Pos = start;
+  doc->ChunkEnd.Pos = end;
+  return(doc);
+}
+
+/*----------------------------------------------------------------------*/
+
+DocObj* 
+makeDocObjUsingParagraphs(docID,type,start,end)
+any* docID;
+char* type;
+any* start;
+any* end;
+
+/* construct a document object using byte chunks - only for use by
+   servers */
+{
+  DocObj* doc = (DocObj*)s_malloc((size_t)sizeof(DocObj));
+  doc->ChunkCode = CT_paragraph;
+  doc->DocumentID = docID;		/* not copied */
+  doc->Type = type;
+  doc->ChunkStart.ID = start; 
+  doc->ChunkEnd.ID = end; 
+  return(doc);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+freeDocObj(doc)
+DocObj* doc;
+
+/* free a docObj */
+{
+  freeAny(doc->DocumentID);
+  s_free(doc->Type);
+  if (doc->ChunkCode == CT_paragraph)
+    { freeAny(doc->ChunkStart.ID);
+      freeAny(doc->ChunkEnd.ID);
+    }
+  s_free(doc);
+}
+
+/*----------------------------------------------------------------------*/
+
+static char* writeDocObj _AP((DocObj* doc,char* buffer,long* len));
+
+static char*
+writeDocObj(doc,buffer,len)
+DocObj* doc;
+char* buffer;
+long* len;
+
+/* write as little as we can about the doc obj */
+{
+  char* buf = buffer;
+  
+  /* we alwasy have to write the id, but its tag depends on if its a chunk */
+  if (doc->ChunkCode == CT_document)
+    buf = writeAny(doc->DocumentID,DT_DocumentID,buf,len);
+  else
+    buf = writeAny(doc->DocumentID,DT_DocumentIDChunk,buf,len);
+  
+  if (doc->Type != NULL)
+    buf = writeString(doc->Type,DT_TYPE,buf,len);
+  
+  switch (doc->ChunkCode)
+    { case CT_document:
+	/* do nothing - there is no chunk data */
+	break;
+      case CT_byte:
+      case CT_line:
+	buf = writeNum(doc->ChunkCode,DT_ChunkCode,buf,len);
+	buf = writeNum(doc->ChunkStart.Pos,DT_ChunkStartID,buf,len);
+	buf = writeNum(doc->ChunkEnd.Pos,DT_ChunkEndID,buf,len);
+	break;
+      case CT_paragraph:
+	buf = writeNum(doc->ChunkCode,DT_ChunkCode,buf,len);
+	buf = writeAny(doc->ChunkStart.ID,DT_ChunkStartID,buf,len);
+	buf = writeAny(doc->ChunkEnd.ID,DT_ChunkEndID,buf,len);
+	break;
+      default:
+	panic("Implementation error: unknown chuck type %ld",
+	      doc->ChunkCode);
+	break;
+      }
+   
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+static char* readDocObj _AP((DocObj** doc,char* buffer));
+
+static char*
+readDocObj(doc,buffer)
+DocObj** doc;
+char* buffer;
+
+/* read whatever we have about the new document */
+{
+  char* buf = buffer;
+  data_tag tag;
+  
+  *doc = (DocObj*)s_malloc((size_t)sizeof(DocObj));
+  
+  tag = peekTag(buf);
+  buf = readAny(&((*doc)->DocumentID),buf);
+  
+  if (tag == DT_DocumentID)
+    { (*doc)->ChunkCode = CT_document;
+      tag = peekTag(buf);
+      if (tag == DT_TYPE)	/* XXX depends on DT_TYPE != what comes next */
+	buf = readString(&((*doc)->Type),buf);
+      /* ChunkStart and ChunkEnd are undefined */
+    }
+  else if (tag == DT_DocumentIDChunk)
+    { boolean readParagraphs = false; /* for cleanup */
+      tag = peekTag(buf);
+      if (tag == DT_TYPE)	/* XXX depends on DT_TYPE != CT_FOO */
+	buf = readString(&((*doc)->Type),buf);
+      buf = readNum(&((*doc)->ChunkCode),buf);
+      switch ((*doc)->ChunkCode)
+	{ case CT_byte:
+	  case CT_line:
+	    buf = readNum(&((*doc)->ChunkStart.Pos),buf);
+	    buf = readNum(&((*doc)->ChunkEnd.Pos),buf);
+	    break;
+	  case CT_paragraph:
+	    readParagraphs = true;
+	    buf = readAny(&((*doc)->ChunkStart.ID),buf);
+	    buf = readAny(&((*doc)->ChunkEnd.ID),buf);
+	    break;
+	  default:
+	    freeAny((*doc)->DocumentID);
+	    if (readParagraphs)
+	      { freeAny((*doc)->ChunkStart.ID);
+		freeAny((*doc)->ChunkEnd.ID);
+	      }
+	    s_free(doc);
+	    REPORT_READ_ERROR(buf);
+	    break;
+	  }
+    }
+  else
+    { freeAny((*doc)->DocumentID);
+      s_free(*doc);
+      REPORT_READ_ERROR(buf);
+    }
+  return(buf);  
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+writeSearchInfo(query,buffer,len)
+SearchAPDU* query;
+char* buffer;
+long* len;
+
+/* write out a WAIS query (type 1 or 3) */
+{
+  if (strcmp(query->QueryType,QT_TextRetrievalQuery) == 0)
+    { return(writeAny((any*)query->Query,DT_Query,buffer,len));
+    }
+  else
+    { unsigned long header_len = userInfoTagSize(DT_UserInformationLength,
+						 DefWAISSearchSize); 
+      char* buf = buffer + header_len;
+      WAISSearch* info = (WAISSearch*)query->Query;
+      unsigned long size;
+      long i;
+  
+      RESERVE_SPACE_FOR_WAIS_HEADER(len);
+       
+      buf = writeString(info->SeedWords,DT_SeedWords,buf,len);
+
+      if (info->Docs != NULL)
+      { for (i = 0; info->Docs[i] != NULL; i++)
+	  { buf = writeDocObj(info->Docs[i],buf,len);
+	  }
+	}
+   
+      /* XXX text list */
+ 
+      buf = writeNum(info->DateFactor,DT_DateFactor,buf,len);
+      buf = writeString(info->BeginDateRange,DT_BeginDateRange,buf,len);
+      buf = writeString(info->EndDateRange,DT_EndDateRange,buf,len);
+      buf = writeNum(info->MaxDocumentsRetrieved,DT_MaxDocumentsRetrieved,buf,len);
+  
+      /* now write the header and size */
+      size = buf - buffer; 
+      buf = writeUserInfoHeader(DT_UserInformationLength,size,header_len,buffer,len);
+   
+      return(buf);
+    }
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+readSearchInfo(info,buffer)
+void** info;
+char* buffer;
+
+/* read a WAIS query (type 1 or 3) */
+{
+  data_tag type = peekTag(buffer);
+  if (type == DT_Query)		/* this is a type 1 query */
+    { char* buf = buffer;
+      any* query = NULL;
+      buf = readAny(&query,buf);
+      *info = (void *)query;
+      return(buf);
+    }
+  else				/* a type 3 query */
+    { char* buf = buffer;
+      unsigned long size; 
+      unsigned long headerSize;
+      data_tag tag;
+      char* seedWords = NULL;
+      char* beginDateRange = NULL;
+      char* endDateRange = NULL;
+      long dateFactor,maxDocsRetrieved;
+      char** textList = NULL; 
+      DocObj** docIDs = NULL;
+      DocObj* doc = NULL;
+      long docs = 0;
+      long i;
+      void* ptr = NULL;
+
+      dateFactor = maxDocsRetrieved = UNUSED;
+  
+      buf = readUserInfoHeader(&tag,&size,buf);
+      headerSize = buf - buffer;
+  
+      while (buf < (buffer + size + headerSize))
+	{ data_tag tag = peekTag(buf);
+	  switch (tag)
+	    { case DT_SeedWords:
+		buf = readString(&seedWords,buf);
+		break;
+	      case DT_DocumentID:
+	      case DT_DocumentIDChunk:
+		if (docIDs == NULL) /* create a new doc list */
+		  { docIDs = (DocObj**)s_malloc((size_t)sizeof(DocObj*) * 2);
+		  }
+		else		/* grow the doc list */
+		  { docIDs = (DocObj**)s_realloc((char*)docIDs,(size_t)(sizeof(DocObj*) * (docs + 2)));
+		  }
+		buf = readDocObj(&doc,buf);
+		if (buf == NULL) 
+		  { s_free(seedWords);
+		    s_free(beginDateRange);
+		    s_free(endDateRange);
+		    if (docIDs != NULL)
+		      for (i = 0,ptr = (void *)docIDs[i]; ptr != NULL; ptr = (void *)docIDs[++i])
+			freeDocObj((DocObj*)ptr);
+		    s_free(docIDs);
+		    /* XXX should also free textlist when it is fully defined */
+		  }
+		RETURN_ON_NULL(buf);
+		docIDs[docs++] = doc; /* put it in the list */
+		docIDs[docs] = NULL;
+		break;
+	      case DT_TextList:
+		/* XXX */
+		break;
+	      case DT_DateFactor:
+		buf = readNum(&dateFactor,buf);
+		break;
+	      case DT_BeginDateRange:
+		buf = readString(&beginDateRange,buf);
+		break;
+	      case DT_EndDateRange:
+		buf = readString(&endDateRange,buf);
+		break;
+	      case DT_MaxDocumentsRetrieved:
+		buf = readNum(&maxDocsRetrieved,buf);
+		break;
+	      default:
+		s_free(seedWords);
+		s_free(beginDateRange);
+		s_free(endDateRange);
+		if (docIDs != NULL)
+		  for (i = 0,ptr = (void *)docIDs[i]; ptr != NULL; ptr = (void *)docIDs[++i])
+		    freeDocObj((DocObj*)ptr);
+		s_free(docIDs);
+		/* XXX should also free textlist when it is fully defined */
+		REPORT_READ_ERROR(buf);
+		break;
+	      }
+	}
+  	  
+      *info = (void *)makeWAISSearch(seedWords,docIDs,textList,
+				     dateFactor,beginDateRange,endDateRange,
+				     maxDocsRetrieved);
+      return(buf);
+    }
+}
+
+/*----------------------------------------------------------------------*/
+
+WAISDocumentHeader*
+makeWAISDocumentHeader(docID,
+		       versionNumber,
+		       score,
+		       bestMatch,
+		       docLen,
+		       lines,
+		       types,
+		       source,
+		       date,
+		       headline,
+		       originCity)
+any* docID;
+long versionNumber;
+long score;
+long bestMatch;
+long docLen;
+long lines;
+char** types;
+char* source;
+char* date;
+char* headline;
+char* originCity;
+
+/* construct a standard document header, note that no fields are copied!
+   if the application needs to save these fields, it should copy them,
+   or set the field in this object to NULL before freeing it.
+ */
+{
+  WAISDocumentHeader* header = 
+    (WAISDocumentHeader*)s_malloc((size_t)sizeof(WAISDocumentHeader));
+
+  header->DocumentID = docID;
+  header->VersionNumber = versionNumber;
+  header->Score = score;
+  header->BestMatch = bestMatch;
+  header->DocumentLength = docLen;
+  header->Lines = lines;
+  header->Types = types;
+  header->Source = source;
+  header->Date = date;
+  header->Headline = headline;
+  header->OriginCity = originCity;
+  
+  return(header);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+freeWAISDocumentHeader(header)
+WAISDocumentHeader* header;
+
+{
+  freeAny(header->DocumentID);
+  doList((void**)header->Types,fs_free); /* can't use the macro here ! */
+  s_free(header->Types);
+  s_free(header->Source);
+  s_free(header->Date);
+  s_free(header->Headline);
+  s_free(header->OriginCity);
+  s_free(header);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+writeWAISDocumentHeader(header,buffer,len)
+WAISDocumentHeader* header;
+char* buffer;
+long* len;
+{
+  unsigned long header_len = userInfoTagSize(DT_DocumentHeaderGroup ,
+					     DefWAISDocHeaderSize);
+  char* buf = buffer + header_len;
+  unsigned long size;
+  
+  RESERVE_SPACE_FOR_WAIS_HEADER(len);
+   
+  buf = writeAny(header->DocumentID,DT_DocumentID,buf,len);
+  buf = writeNum(header->VersionNumber,DT_VersionNumber,buf,len);
+  buf = writeNum(header->Score,DT_Score,buf,len);
+  buf = writeNum(header->BestMatch,DT_BestMatch,buf,len);
+  buf = writeNum(header->DocumentLength,DT_DocumentLength,buf,len);
+  buf = writeNum(header->Lines,DT_Lines,buf,len);
+  if (header->Types != NULL)
+    { long size;
+      char* ptr = NULL;
+      long i;
+      buf = writeTag(DT_TYPE_BLOCK,buf,len);
+      for (i = 0,size = 0,ptr = header->Types[i]; ptr != NULL; ptr = header->Types[++i])
+	{ long typeSize = strlen(ptr);
+	  size += writtenTagSize(DT_TYPE);
+	  size += writtenCompressedIntSize(typeSize);
+	  size += typeSize; 
+	}
+      buf = writeCompressedInteger((unsigned long)size,buf,len);
+      for (i = 0,ptr = header->Types[i]; ptr != NULL; ptr = header->Types[++i])
+	buf = writeString(ptr,DT_TYPE,buf,len);
+    }
+  buf = writeString(header->Source,DT_Source,buf,len);
+  buf = writeString(header->Date,DT_Date,buf,len);
+  buf = writeString(header->Headline,DT_Headline,buf,len);
+  buf = writeString(header->OriginCity,DT_OriginCity,buf,len);
+  
+  /* now write the header and size */
+  size = buf - buffer; 
+  buf = writeUserInfoHeader(DT_DocumentHeaderGroup,size,header_len,buffer,len);
+
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readWAISDocumentHeader(header,buffer)
+WAISDocumentHeader** header;
+char* buffer;
+{
+  char* buf = buffer;
+  unsigned long size; 
+  unsigned long headerSize;
+  data_tag tag;
+  any* docID = NULL;
+  long versionNumber,score,bestMatch,docLength,lines;
+  char** types = NULL;
+  char *source = NULL;
+  char *date = NULL;
+  char *headline = NULL;
+  char *originCity = NULL;
+  
+  versionNumber = score = bestMatch = docLength = lines = UNUSED;
+  
+  buf = readUserInfoHeader(&tag,&size,buf);
+  headerSize = buf - buffer;
+    
+  while (buf < (buffer + size + headerSize))
+    { data_tag tag = peekTag(buf);
+      switch (tag)
+	{ case DT_DocumentID:
+	    buf = readAny(&docID,buf);
+	    break;
+	  case DT_VersionNumber:
+	    buf = readNum(&versionNumber,buf);
+	    break;
+	  case DT_Score:
+	    buf = readNum(&score,buf);
+	    break;
+	  case DT_BestMatch:
+	    buf = readNum(&bestMatch,buf);
+	    break;
+	  case DT_DocumentLength:
+	    buf = readNum(&docLength,buf);
+	    break;
+	  case DT_Lines:
+	    buf = readNum(&lines,buf);
+	    break;
+	  case DT_TYPE_BLOCK:
+	    { unsigned long size = -1;
+	      long numTypes = 0;
+	      buf = readTag(&tag,buf);
+	      buf = readCompressedInteger(&size,buf);
+	      while (size > 0)
+		{ char* type = NULL;
+		  char* originalBuf = buf;
+		  buf = readString(&type,buf);
+		  types = (char**)s_realloc(types,(size_t)(sizeof(char*) * (numTypes + 2)));
+		  types[numTypes++] = type;
+		  types[numTypes] = NULL;
+		  size -= (buf - originalBuf);
+		}
+	    }
+	  case DT_Source:
+	    buf = readString(&source,buf);
+	    break;
+	  case DT_Date:
+	    buf = readString(&date,buf);
+	    break;
+	  case DT_Headline:
+	    buf = readString(&headline,buf);
+	    break;
+	  case DT_OriginCity:
+	    buf = readString(&originCity,buf);
+	    break;
+	  default:
+	    freeAny(docID);
+	    s_free(source);
+	    s_free(date);
+	    s_free(headline);
+	    s_free(originCity);
+	    REPORT_READ_ERROR(buf);
+	    break;
+	  }
+    }
+  	  
+  *header = makeWAISDocumentHeader(docID,versionNumber,score,bestMatch,
+				   docLength,lines,types,source,date,headline,
+				   originCity);
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+WAISDocumentShortHeader*
+makeWAISDocumentShortHeader(docID,
+			    versionNumber,
+			    score,
+			    bestMatch,
+			    docLen,
+			    lines)
+any* docID;
+long versionNumber;
+long score;
+long bestMatch;
+long docLen;
+long lines;
+/* construct a short document header, note that no fields are copied!
+   if the application needs to save these fields, it should copy them,
+   or set the field in this object to NULL before freeing it.
+ */
+{
+  WAISDocumentShortHeader* header = 
+    (WAISDocumentShortHeader*)s_malloc((size_t)sizeof(WAISDocumentShortHeader));
+
+  header->DocumentID = docID;
+  header->VersionNumber = versionNumber;
+  header->Score = score;
+  header->BestMatch = bestMatch;
+  header->DocumentLength = docLen;
+  header->Lines = lines;
+  
+  return(header);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+freeWAISDocumentShortHeader(header)
+WAISDocumentShortHeader* header;
+{
+  freeAny(header->DocumentID);
+  s_free(header);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+writeWAISDocumentShortHeader(header,buffer,len)
+WAISDocumentShortHeader* header;
+char* buffer;
+long* len;
+{
+  unsigned long header_len = userInfoTagSize(DT_DocumentShortHeaderGroup ,
+					     DefWAISShortHeaderSize);
+  char* buf = buffer + header_len;
+  unsigned long size;
+  
+  RESERVE_SPACE_FOR_WAIS_HEADER(len);
+   
+  buf = writeAny(header->DocumentID,DT_DocumentID,buf,len);
+  buf = writeNum(header->VersionNumber,DT_VersionNumber,buf,len);
+  buf = writeNum(header->Score,DT_Score,buf,len);
+  buf = writeNum(header->BestMatch,DT_BestMatch,buf,len);
+  buf = writeNum(header->DocumentLength,DT_DocumentLength,buf,len);
+  buf = writeNum(header->Lines,DT_Lines,buf,len);
+  
+  /* now write the header and size */
+  size = buf - buffer; 
+  buf = writeUserInfoHeader(DT_DocumentShortHeaderGroup,size,header_len,buffer,len);
+
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readWAISDocumentShortHeader(header,buffer)
+WAISDocumentShortHeader** header;
+char* buffer;
+{
+  char* buf = buffer;
+  unsigned long size; 
+  unsigned long headerSize;
+  data_tag tag;
+  any* docID = NULL;
+  long versionNumber,score,bestMatch,docLength,lines;
+  
+  versionNumber = score = bestMatch = docLength = lines = UNUSED;
+  
+  buf = readUserInfoHeader(&tag,&size,buf);
+  headerSize = buf - buffer;
+    
+  while (buf < (buffer + size + headerSize))
+    { data_tag tag = peekTag(buf);
+      switch (tag)
+	{ case DT_DocumentID:
+	    buf = readAny(&docID,buf);
+	    break;
+	  case DT_VersionNumber:
+	    buf = readNum(&versionNumber,buf);
+	    break;
+	  case DT_Score:
+	    buf = readNum(&score,buf);
+	    break;
+	  case DT_BestMatch:
+	    buf = readNum(&bestMatch,buf);
+	    break;
+	  case DT_DocumentLength:
+	    buf = readNum(&docLength,buf);
+	    break;
+	  case DT_Lines:
+	    buf = readNum(&lines,buf);
+	    break;
+	  default:
+	    freeAny(docID);
+	    REPORT_READ_ERROR(buf);
+	    break;
+	  }
+    }
+  	  
+  *header = makeWAISDocumentShortHeader(docID,versionNumber,score,bestMatch,
+					docLength,lines);
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+WAISDocumentLongHeader*
+makeWAISDocumentLongHeader(docID,
+			   versionNumber,
+			   score,
+			   bestMatch,
+			   docLen,
+			   lines,
+			   types,
+			   source,
+			   date,
+			   headline,
+			   originCity,
+			   stockCodes,
+			   companyCodes,
+			   industryCodes)
+any* docID;
+long versionNumber;
+long score;
+long bestMatch;
+long docLen;
+long lines;
+char** types;
+char* source;
+char* date;
+char* headline;
+char* originCity;
+char* stockCodes;
+char* companyCodes;
+char* industryCodes;
+/* construct a long document header, note that no fields are copied!
+   if the application needs to save these fields, it should copy them,
+   or set the field in this object to NULL before freeing it.
+ */
+{
+  WAISDocumentLongHeader* header = 
+    (WAISDocumentLongHeader*)s_malloc((size_t)sizeof(WAISDocumentLongHeader));
+
+  header->DocumentID = docID;
+  header->VersionNumber = versionNumber;
+  header->Score = score;
+  header->BestMatch = bestMatch;
+  header->DocumentLength = docLen;
+  header->Lines = lines;
+  header->Types = types;
+  header->Source = source;
+  header->Date = date;
+  header->Headline = headline;
+  header->OriginCity = originCity;
+  header->StockCodes = stockCodes;
+  header->CompanyCodes = companyCodes;
+  header->IndustryCodes = industryCodes;
+  
+  return(header);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+freeWAISDocumentLongHeader(header)
+WAISDocumentLongHeader* header;
+{
+  freeAny(header->DocumentID);
+  doList((void**)header->Types,fs_free); /* can't use the macro here! */
+  s_free(header->Source);
+  s_free(header->Date);
+  s_free(header->Headline);
+  s_free(header->OriginCity);
+  s_free(header->StockCodes);
+  s_free(header->CompanyCodes);
+  s_free(header->IndustryCodes);
+  s_free(header);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+writeWAISDocumentLongHeader(header,buffer,len)
+WAISDocumentLongHeader* header;
+char* buffer;
+long* len;
+{
+  unsigned long header_len = userInfoTagSize(DT_DocumentLongHeaderGroup ,
+					     DefWAISLongHeaderSize);
+  char* buf = buffer + header_len;
+  unsigned long size;
+  
+  RESERVE_SPACE_FOR_WAIS_HEADER(len);
+   
+  buf = writeAny(header->DocumentID,DT_DocumentID,buf,len);
+  buf = writeNum(header->VersionNumber,DT_VersionNumber,buf,len);
+  buf = writeNum(header->Score,DT_Score,buf,len);
+  buf = writeNum(header->BestMatch,DT_BestMatch,buf,len);
+  buf = writeNum(header->DocumentLength,DT_DocumentLength,buf,len);
+  buf = writeNum(header->Lines,DT_Lines,buf,len);
+  if (header->Types != NULL)
+    { long size;
+      char* ptr = NULL;
+      long i;
+      buf = writeTag(DT_TYPE_BLOCK,buf,len);
+      for (i = 0,size = 0,ptr = header->Types[i]; ptr != NULL; ptr = header->Types[++i])
+	{ long typeSize = strlen(ptr);
+	  size += writtenTagSize(DT_TYPE);
+	  size += writtenCompressedIntSize(typeSize);
+	  size += typeSize; 
+	}
+      buf = writeCompressedInteger((unsigned long)size,buf,len);
+      for (i = 0,ptr = header->Types[i]; ptr != NULL; ptr = header->Types[++i])
+	buf = writeString(ptr,DT_TYPE,buf,len);
+    }
+  buf = writeString(header->Source,DT_Source,buf,len);
+  buf = writeString(header->Date,DT_Date,buf,len);
+  buf = writeString(header->Headline,DT_Headline,buf,len);
+  buf = writeString(header->OriginCity,DT_OriginCity,buf,len);
+  buf = writeString(header->StockCodes,DT_StockCodes,buf,len);
+  buf = writeString(header->CompanyCodes,DT_CompanyCodes,buf,len);
+  buf = writeString(header->IndustryCodes,DT_IndustryCodes,buf,len);
+  
+  /* now write the header and size */
+  size = buf - buffer; 
+  buf = writeUserInfoHeader(DT_DocumentLongHeaderGroup,size,header_len,buffer,len);
+
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readWAISDocumentLongHeader(header,buffer)
+WAISDocumentLongHeader** header;
+char* buffer;
+{
+  char* buf = buffer;
+  unsigned long size; 
+  unsigned long headerSize;
+  data_tag tag;
+  any* docID;
+  long versionNumber,score,bestMatch,docLength,lines;
+  char **types;
+  char *source,*date,*headline,*originCity,*stockCodes,*companyCodes,*industryCodes;
+  
+  docID = NULL;
+  versionNumber = score = bestMatch = docLength = lines = UNUSED;
+  types = NULL;
+  source = date = headline = originCity = stockCodes = companyCodes = industryCodes = NULL;
+  
+  buf = readUserInfoHeader(&tag,&size,buf);
+  headerSize = buf - buffer;
+    
+  while (buf < (buffer + size + headerSize))
+    { data_tag tag = peekTag(buf);
+      switch (tag)
+	{ case DT_DocumentID:
+	    buf = readAny(&docID,buf);
+	    break;
+	  case DT_VersionNumber:
+	    buf = readNum(&versionNumber,buf);
+	    break;
+	  case DT_Score:
+	    buf = readNum(&score,buf);
+	    break;
+	  case DT_BestMatch:
+	    buf = readNum(&bestMatch,buf);
+	    break;
+	  case DT_DocumentLength:
+	    buf = readNum(&docLength,buf);
+	    break;
+	  case DT_Lines:
+	    buf = readNum(&lines,buf);
+	    break;
+	  case DT_TYPE_BLOCK:
+	    { unsigned long size = -1;
+	      long numTypes = 0;
+	      buf = readTag(&tag,buf);
+	      readCompressedInteger(&size,buf);
+	      while (size > 0)
+		{ char* type = NULL;
+		  char* originalBuf = buf;
+		  buf = readString(&type,buf);
+		  types = (char**)s_realloc(types,(size_t)(sizeof(char*) * (numTypes + 2)));
+		  types[numTypes++] = type;
+		  types[numTypes] = NULL;
+		  size -= (buf - originalBuf);
+		}
+	    }
+	  case DT_Source:
+	    buf = readString(&source,buf);
+	    break;
+	  case DT_Date:
+	    buf = readString(&date,buf);
+	    break;
+	  case DT_Headline:
+	    buf = readString(&headline,buf);
+	    break;
+	  case DT_OriginCity:
+	    buf = readString(&originCity,buf);
+	    break;
+	  case DT_StockCodes:
+	    buf = readString(&stockCodes,buf);
+	    break;
+	  case DT_CompanyCodes:
+	    buf = readString(&companyCodes,buf);
+	    break;
+	  case DT_IndustryCodes:
+	    buf = readString(&industryCodes,buf);
+	    break;
+	  default:
+	    freeAny(docID);
+	    s_free(source);
+	    s_free(date);
+	    s_free(headline);
+	    s_free(originCity);
+	    s_free(stockCodes);
+	    s_free(companyCodes);
+	    s_free(industryCodes);
+	    REPORT_READ_ERROR(buf);
+	    break;
+	  }
+    }
+  	  
+  *header = makeWAISDocumentLongHeader(docID,versionNumber,score,bestMatch,
+				       docLength,lines,types,source,date,headline,
+				       originCity,stockCodes,companyCodes,
+				       industryCodes);
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+WAISSearchResponse*
+makeWAISSearchResponse(seedWordsUsed,
+		       docHeaders,
+		       shortHeaders,
+		       longHeaders,
+		       text,
+		       headlines,
+		       codes,
+		       diagnostics)
+char* seedWordsUsed;
+WAISDocumentHeader** docHeaders;
+WAISDocumentShortHeader** shortHeaders;
+WAISDocumentLongHeader** longHeaders;
+WAISDocumentText** text;
+WAISDocumentHeadlines** headlines;
+WAISDocumentCodes** codes;
+diagnosticRecord** diagnostics;
+{
+  WAISSearchResponse* response = (WAISSearchResponse*)s_malloc((size_t)sizeof(WAISSearchResponse));
+  
+  response->SeedWordsUsed = seedWordsUsed;
+  response->DocHeaders = docHeaders;
+  response->ShortHeaders = shortHeaders;
+  response->LongHeaders = longHeaders;
+  response->Text = text;
+  response->Headlines = headlines;
+  response->Codes = codes;
+  response->Diagnostics = diagnostics;
+  
+  return(response);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+freeWAISSearchResponse(response)
+WAISSearchResponse* response;
+{
+  void* ptr = NULL;
+  long i;
+
+  s_free(response->SeedWordsUsed);
+
+  if (response->DocHeaders != NULL)
+    for (i = 0,ptr = (void *)response->DocHeaders[i]; ptr != NULL; ptr = (void *)response->DocHeaders[++i])
+      freeWAISDocumentHeader((WAISDocumentHeader*)ptr);
+  s_free(response->DocHeaders);
+   
+  if (response->ShortHeaders != NULL)
+    for (i = 0,ptr = (void *)response->ShortHeaders[i]; ptr != NULL; ptr = (void *)response->ShortHeaders[++i])
+      freeWAISDocumentShortHeader((WAISDocumentShortHeader*)ptr);
+  s_free(response->ShortHeaders);
+   
+  if (response->LongHeaders != NULL)
+    for (i = 0,ptr = (void *)response->LongHeaders[i]; ptr != NULL; ptr = (void *)response->LongHeaders[++i])
+      freeWAISDocumentLongHeader((WAISDocumentLongHeader*)ptr);
+  s_free(response->LongHeaders);
+   
+  if (response->Text != NULL)
+    for (i = 0,ptr = (void *)response->Text[i]; ptr != NULL; ptr = (void *)response->Text[++i])
+      freeWAISDocumentText((WAISDocumentText*)ptr);
+  s_free(response->Text);
+   
+  if (response->Headlines != NULL)
+    for (i = 0,ptr = (void *)response->Headlines[i]; ptr != NULL; ptr = (void *)response->Headlines[++i])
+      freeWAISDocumentHeadlines((WAISDocumentHeadlines*)ptr);
+  s_free(response->Headlines);
+   
+  if (response->Codes != NULL)
+    for (i = 0,ptr = (void *)response->Codes[i]; ptr != NULL; ptr = (void *)response->Codes[++i])
+      freeWAISDocumentCodes((WAISDocumentCodes*)ptr);
+  s_free(response->Codes);
+   
+  if (response->Diagnostics != NULL)
+    for (i = 0,ptr = (void *)response->Diagnostics[i]; ptr != NULL; ptr = (void *)response->Diagnostics[++i])
+      freeDiag((diagnosticRecord*)ptr);
+  s_free(response->Diagnostics);
+  
+  s_free(response);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+writeSearchResponseInfo(query,buffer,len)
+SearchResponseAPDU* query;
+char* buffer;
+long* len;
+{
+  unsigned long header_len = userInfoTagSize(DT_UserInformationLength,
+					     DefWAISSearchResponseSize);
+  char* buf = buffer + header_len;
+  WAISSearchResponse* info = (WAISSearchResponse*)query->DatabaseDiagnosticRecords;
+  unsigned long size;
+  void* header = NULL;
+  long i;
+  
+  RESERVE_SPACE_FOR_WAIS_HEADER(len);
+  
+  buf = writeString(info->SeedWordsUsed,DT_SeedWordsUsed,buf,len);
+  
+  /* write out all the headers */
+  if (info->DocHeaders != NULL)
+    { for (i = 0,header = (void *)info->DocHeaders[i]; header != NULL; header = (void *)info->DocHeaders[++i])
+	buf = writeWAISDocumentHeader((WAISDocumentHeader*)header,buf,len);
+      }
+   
+  if (info->ShortHeaders != NULL)
+    { for (i = 0,header = (void *)info->ShortHeaders[i]; header != NULL; header = (void *)info->ShortHeaders[++i])
+	buf = writeWAISDocumentShortHeader((WAISDocumentShortHeader*)header,buf,len);
+      }
+
+  if (info->LongHeaders != NULL)
+    { for (i = 0,header = (void *)info->LongHeaders[i]; header != NULL; header = (void *)info->LongHeaders[++i])
+	buf = writeWAISDocumentLongHeader((WAISDocumentLongHeader*)header,buf,len);
+      }
+
+  if (info->Text != NULL)
+    { for (i = 0,header = (void *)info->Text[i]; header != NULL; header = (void *)info->Text[++i])
+	buf = writeWAISDocumentText((WAISDocumentText*)header,buf,len);
+      }
+
+  if (info->Headlines != NULL)
+    { for (i = 0,header = (void *)info->Headlines[i]; header != NULL; header = (void *)info->Headlines[++i])
+	buf = writeWAISDocumentHeadlines((WAISDocumentHeadlines*)header,buf,len);
+      }
+
+  if (info->Codes != NULL)
+    { for (i = 0,header = (void *)info->Codes[i]; header != NULL;header = (void *)info->Codes[++i])
+	buf = writeWAISDocumentCodes((WAISDocumentCodes*)header,buf,len);
+      }
+
+  if (info->Diagnostics != NULL)
+    { for (i = 0, header = (void *)info->Diagnostics[i]; header != NULL; header = (void *)info->Diagnostics[++i])
+	buf = writeDiag((diagnosticRecord*)header,buf,len);
+      }
+   
+  /* now write the header and size */
+  size = buf - buffer; 
+  buf = writeUserInfoHeader(DT_UserInformationLength,size,header_len,buffer,len);
+  
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+static void
+cleanUpWaisSearchResponse _AP((char* buf,char* seedWordsUsed,
+			       WAISDocumentHeader** docHeaders,
+			       WAISDocumentShortHeader** shortHeaders,
+			       WAISDocumentLongHeader** longHeaders,
+			       WAISDocumentText** text,
+			       WAISDocumentHeadlines** headlines,
+			       WAISDocumentCodes** codes,
+			       diagnosticRecord**diags));
+
+static void
+cleanUpWaisSearchResponse (buf,seedWordsUsed,docHeaders,shortHeaders,
+			   longHeaders,text,headlines,codes,diags)
+char* buf;
+char* seedWordsUsed;
+WAISDocumentHeader** docHeaders;
+WAISDocumentShortHeader** shortHeaders;
+WAISDocumentLongHeader** longHeaders;
+WAISDocumentText** text;
+WAISDocumentHeadlines** headlines;
+WAISDocumentCodes** codes;
+diagnosticRecord** diags;
+/* if buf is NULL, we have just gotten a read error, and need to clean up 
+   any state we have built.  If not, then everything is going fine, and
+   we should just hang loose
+ */
+{
+  void* ptr = NULL;
+  long i;
+
+  if (buf == NULL)						
+   { s_free(seedWordsUsed);				
+     if (docHeaders != NULL)				
+       for (i = 0,ptr = (void *)docHeaders[i]; ptr != NULL; 
+	    ptr = (void *)docHeaders[++i])		
+	 freeWAISDocumentHeader((WAISDocumentHeader*)ptr);	
+     s_free(docHeaders);				
+     if (shortHeaders != NULL)	
+       for (i = 0,ptr = (void *)shortHeaders[i]; ptr != NULL;
+	    ptr = (void *)shortHeaders[++i])	
+	 freeWAISDocumentShortHeader((WAISDocumentShortHeader*)ptr);
+     s_free(shortHeaders);						
+     if (longHeaders != NULL)				
+       for (i = 0,ptr = (void *)longHeaders[i]; ptr != NULL; 
+	    ptr = (void *)longHeaders[++i])	
+	 freeWAISDocumentLongHeader((WAISDocumentLongHeader*)ptr);
+     s_free(longHeaders);				
+     if (text != NULL)					
+       for (i = 0,ptr = (void *)text[i]; ptr != NULL; ptr = (void *)text[++i])
+	 freeWAISDocumentText((WAISDocumentText*)ptr);	
+     s_free(text);					
+     if (headlines != NULL)					
+       for (i = 0,ptr = (void *)headlines[i]; ptr != NULL;
+	    ptr = (void *)headlines[++i])		
+	 freeWAISDocumentHeadlines((WAISDocumentHeadlines*)ptr);	
+     s_free(headlines);						
+     if (codes != NULL)				     
+       for (i = 0,ptr = (void *)codes[i]; ptr != NULL; 
+	    ptr = (void *)codes[++i])				
+	 freeWAISDocumentCodes((WAISDocumentCodes*)ptr);	 
+     s_free(codes);					
+     if (diags != NULL)				      	
+       for (i = 0,ptr = (void *)diags[i]; ptr != NULL; 
+	    ptr = (void *)diags[++i])	 
+	 freeDiag((diagnosticRecord*)ptr);	     
+     s_free(diags);
+   }
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readSearchResponseInfo(info,buffer)
+void** info;
+char* buffer;
+{
+  char* buf = buffer;
+  unsigned long size; 
+  unsigned long headerSize;
+  data_tag tag;
+  void* header = NULL;
+  WAISDocumentHeader** docHeaders = NULL;
+  WAISDocumentShortHeader** shortHeaders = NULL;
+  WAISDocumentLongHeader** longHeaders = NULL;
+  WAISDocumentText** text = NULL;
+  WAISDocumentHeadlines** headlines = NULL;
+  WAISDocumentCodes** codes = NULL;
+  long numDocHeaders,numLongHeaders,numShortHeaders,numText,numHeadlines;
+  long numCodes;
+  char* seedWordsUsed = NULL;
+  diagnosticRecord** diags = NULL;
+  diagnosticRecord* diag = NULL;
+  long numDiags = 0;
+  
+  numDocHeaders = numLongHeaders = numShortHeaders = numText = numHeadlines = numCodes = 0;
+  
+  buf = readUserInfoHeader(&tag,&size,buf);
+  headerSize = buf - buffer;
+    
+  while (buf < (buffer + size + headerSize))
+   { data_tag tag = peekTag(buf);
+     switch (tag)
+      { case DT_SeedWordsUsed:
+      	  buf = readString(&seedWordsUsed,buf);
+      	  break;
+      	case DT_DatabaseDiagnosticRecords:
+      	  if (diags == NULL) /* create a new diag list */
+      	   { diags = (diagnosticRecord**)s_malloc((size_t)sizeof(diagnosticRecord*) * 2);
+      	   }
+      	  else /* grow the diag list */
+      	   { diags = (diagnosticRecord**)s_realloc((char*)diags,(size_t)(sizeof(diagnosticRecord*) * (numDiags + 2)));
+      	   }
+      	  buf = readDiag(&diag,buf);
+      	  diags[numDiags++] = diag; /* put it in the list */
+      	  diags[numDiags] = NULL;
+      	  break;
+      	case DT_DocumentHeaderGroup:
+  		  if (docHeaders == NULL) /* create a new header list */
+  		   { docHeaders = (WAISDocumentHeader**)s_malloc((size_t)sizeof(WAISDocumentHeader*) * 2);
+  		   }
+  		  else /* grow the doc list */
+  		   { docHeaders = (WAISDocumentHeader**)s_realloc((char*)docHeaders,(size_t)(sizeof(WAISDocumentHeader*) * (numDocHeaders + 2)));
+  		   }
+  		  buf = readWAISDocumentHeader((WAISDocumentHeader**)&header,buf);
+		  cleanUpWaisSearchResponse(buf,seedWordsUsed,docHeaders,shortHeaders,longHeaders,text,headlines,codes,diags);
+  		  RETURN_ON_NULL(buf);
+  		  docHeaders[numDocHeaders++] = 
+		    (WAISDocumentHeader*)header; /* put it in the list */
+  		  docHeaders[numDocHeaders] = NULL;
+  		  break;
+  		case DT_DocumentShortHeaderGroup:
+  		  if (shortHeaders == NULL) /* create a new header list */
+  		   { shortHeaders = (WAISDocumentShortHeader**)s_malloc((size_t)sizeof(WAISDocumentShortHeader*) * 2);
+  		   }
+  		  else /* grow the doc list */
+  		   { shortHeaders = (WAISDocumentShortHeader**)s_realloc((char*)shortHeaders,(size_t)(sizeof(WAISDocumentShortHeader*) * (numShortHeaders + 2)));
+  		   }
+  		  buf = readWAISDocumentShortHeader((WAISDocumentShortHeader**)&header,buf);
+		  cleanUpWaisSearchResponse(buf,seedWordsUsed,docHeaders,shortHeaders,longHeaders,text,headlines,codes,diags);
+  		  RETURN_ON_NULL(buf);
+  		  shortHeaders[numShortHeaders++] = 
+		    (WAISDocumentShortHeader*)header; /* put it in the list */
+  		  shortHeaders[numShortHeaders] = NULL;
+  		  break;
+  		case DT_DocumentLongHeaderGroup:
+  		  if (longHeaders == NULL) /* create a new header list */
+  		   { longHeaders = (WAISDocumentLongHeader**)s_malloc((size_t)sizeof(WAISDocumentLongHeader*) * 2);
+  		   }
+  		  else /* grow the doc list */
+  		   { longHeaders = (WAISDocumentLongHeader**)s_realloc((char*)longHeaders,(size_t)(sizeof(WAISDocumentLongHeader*) * (numLongHeaders + 2)));
+  		   }
+  		  buf = readWAISDocumentLongHeader((WAISDocumentLongHeader**)&header,buf);
+		  cleanUpWaisSearchResponse(buf,seedWordsUsed,docHeaders,shortHeaders,longHeaders,text,headlines,codes,diags);
+  		  RETURN_ON_NULL(buf);
+  		  longHeaders[numLongHeaders++] = 
+		    (WAISDocumentLongHeader*)header; /* put it in the list */
+  		  longHeaders[numLongHeaders] = NULL;
+  		  break;
+        case DT_DocumentTextGroup:
+  		  if (text == NULL) /* create a new list */
+  		   { text = (WAISDocumentText**)s_malloc((size_t)sizeof(WAISDocumentText*) * 2);
+  		   }
+  		  else /* grow the list */
+  		   { text = (WAISDocumentText**)s_realloc((char*)text,(size_t)(sizeof(WAISDocumentText*) * (numText + 2)));
+  		   }
+  		  buf = readWAISDocumentText((WAISDocumentText**)&header,buf);
+		  cleanUpWaisSearchResponse(buf,seedWordsUsed,docHeaders,shortHeaders,longHeaders,text,headlines,codes,diags);
+  		  RETURN_ON_NULL(buf);
+  		  text[numText++] = 
+		    (WAISDocumentText*)header; /* put it in the list */
+  		  text[numText] = NULL;
+  		  break;
+  		case DT_DocumentHeadlineGroup:
+  		  if (headlines == NULL) /* create a new list */
+  		   { headlines = (WAISDocumentHeadlines**)s_malloc((size_t)sizeof(WAISDocumentHeadlines*) * 2);
+  		   }
+  		  else /* grow the list */
+  		   { headlines = (WAISDocumentHeadlines**)s_realloc((char*)headlines,(size_t)(sizeof(WAISDocumentHeadlines*) * (numHeadlines + 2)));
+  		   }
+  		  buf = readWAISDocumentHeadlines((WAISDocumentHeadlines**)&header,buf);
+		  cleanUpWaisSearchResponse(buf,seedWordsUsed,docHeaders,shortHeaders,longHeaders,text,headlines,codes,diags);
+  		  RETURN_ON_NULL(buf);
+  		  headlines[numHeadlines++] = 
+		    (WAISDocumentHeadlines*)header; /* put it in the list */
+  		  headlines[numHeadlines] = NULL;
+  		  break;
+  		case DT_DocumentCodeGroup:
+  		  if (codes == NULL) /* create a new list */
+  		   { codes = (WAISDocumentCodes**)s_malloc((size_t)sizeof(WAISDocumentCodes*) * 2);
+  		   }
+  		  else /* grow the list */
+  		   { codes = (WAISDocumentCodes**)s_realloc((char*)codes,(size_t)(sizeof(WAISDocumentCodes*) * (numCodes + 2)));
+  		   }
+  		  buf = readWAISDocumentCodes((WAISDocumentCodes**)&header,buf);
+		  cleanUpWaisSearchResponse(buf,seedWordsUsed,docHeaders,shortHeaders,longHeaders,text,headlines,codes,diags);
+  		  RETURN_ON_NULL(buf);
+  		  codes[numCodes++] = 
+		    (WAISDocumentCodes*)header; /* put it in the list */
+  		  codes[numCodes] = NULL;
+  		  break;
+        default:
+          cleanUpWaisSearchResponse(buf,seedWordsUsed,docHeaders,shortHeaders,longHeaders,text,headlines,codes,diags);
+          REPORT_READ_ERROR(buf);
+          break;
+      }
+   }
+  	  
+  *info = (void *)makeWAISSearchResponse(seedWordsUsed,docHeaders,shortHeaders,
+				 longHeaders,text,headlines,codes,diags);
+  
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+WAISDocumentText*
+makeWAISDocumentText(docID,versionNumber,documentText)
+any* docID;
+long versionNumber;
+any* documentText;
+{
+  WAISDocumentText* docText = (WAISDocumentText*)s_malloc((size_t)sizeof(WAISDocumentText));
+
+  docText->DocumentID = docID;
+  docText->VersionNumber = versionNumber;
+  docText->DocumentText = documentText;
+  
+  return(docText);
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+freeWAISDocumentText(docText)
+WAISDocumentText* docText;
+{
+  freeAny(docText->DocumentID);
+  freeAny(docText->DocumentText);
+  s_free(docText);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+writeWAISDocumentText(docText,buffer,len)
+WAISDocumentText* docText;
+char* buffer;
+long* len;
+{
+  unsigned long header_len = userInfoTagSize(DT_DocumentTextGroup,
+											DefWAISDocTextSize);
+  char* buf = buffer + header_len;
+  unsigned long size;
+  
+  RESERVE_SPACE_FOR_WAIS_HEADER(len);
+
+  buf = writeAny(docText->DocumentID,DT_DocumentID,buf,len);
+  buf = writeNum(docText->VersionNumber,DT_VersionNumber,buf,len);
+  buf = writeAny(docText->DocumentText,DT_DocumentText,buf,len);
+  
+  /* now write the header and size */
+  size = buf - buffer; 
+  buf = writeUserInfoHeader(DT_DocumentTextGroup,size,header_len,buffer,len);
+
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+readWAISDocumentText(docText,buffer)
+WAISDocumentText** docText;
+char* buffer;
+{
+  char* buf = buffer;
+  unsigned long size; 
+  unsigned long headerSize;
+  data_tag tag;
+  any *docID,*documentText;
+  long versionNumber;
+  
+  docID = documentText = NULL;
+  versionNumber = UNUSED;
+  
+  buf = readUserInfoHeader(&tag,&size,buf);
+  headerSize = buf - buffer;
+    
+  while (buf < (buffer + size + headerSize))
+   { data_tag tag = peekTag(buf);
+     switch (tag)
+      { case DT_DocumentID:
+  		  buf = readAny(&docID,buf);
+  		  break;
+  		case DT_VersionNumber:
+  		  buf = readNum(&versionNumber,buf);
+  		  break;
+  		case DT_DocumentText:
+  		  buf = readAny(&documentText,buf);
+  		  break;
+        default:
+          freeAny(docID);
+          freeAny(documentText);
+          REPORT_READ_ERROR(buf);
+          break;
+      }
+   }
+  	  
+  *docText = makeWAISDocumentText(docID,versionNumber,documentText);
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+WAISDocumentHeadlines*
+makeWAISDocumentHeadlines(docID,
+			  versionNumber,
+			  source,
+			  date,
+			  headline,
+			  originCity)
+any* docID;
+long versionNumber;
+char* source;
+char* date;
+char* headline;
+char* originCity;
+{
+  WAISDocumentHeadlines* docHeadline =
+    (WAISDocumentHeadlines*)s_malloc((size_t)sizeof(WAISDocumentHeadlines));
+
+  docHeadline->DocumentID = docID;
+  docHeadline->VersionNumber = versionNumber;
+  docHeadline->Source = source;
+  docHeadline->Date = date;
+  docHeadline->Headline = headline;
+  docHeadline->OriginCity = originCity;
+  
+  return(docHeadline);
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+freeWAISDocumentHeadlines(docHeadline)
+WAISDocumentHeadlines* docHeadline;
+{
+  freeAny(docHeadline->DocumentID);
+  s_free(docHeadline->Source);
+  s_free(docHeadline->Date);
+  s_free(docHeadline->Headline);
+  s_free(docHeadline->OriginCity);
+  s_free(docHeadline);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+writeWAISDocumentHeadlines(docHeadline,buffer,len)
+WAISDocumentHeadlines* docHeadline;
+char* buffer;
+long* len;
+{
+  unsigned long header_len = userInfoTagSize(DT_DocumentHeadlineGroup,
+											DefWAISDocHeadlineSize);
+  char* buf = buffer + header_len;
+  unsigned long size;
+  
+  RESERVE_SPACE_FOR_WAIS_HEADER(len);
+
+  buf = writeAny(docHeadline->DocumentID,DT_DocumentID,buf,len);
+  buf = writeNum(docHeadline->VersionNumber,DT_VersionNumber,buf,len);
+  buf = writeString(docHeadline->Source,DT_Source,buf,len);
+  buf = writeString(docHeadline->Date,DT_Date,buf,len);
+  buf = writeString(docHeadline->Headline,DT_Headline,buf,len);
+  buf = writeString(docHeadline->OriginCity,DT_OriginCity,buf,len);
+  
+  /* now write the header and size */
+  size = buf - buffer; 
+  buf = writeUserInfoHeader(DT_DocumentHeadlineGroup,size,header_len,buffer,len);
+
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+readWAISDocumentHeadlines(docHeadline,buffer)
+WAISDocumentHeadlines** docHeadline;
+char* buffer;
+{
+  char* buf = buffer;
+  unsigned long size; 
+  unsigned long headerSize;
+  data_tag tag;
+  any* docID;
+  long versionNumber;
+  char *source,*date,*headline,*originCity;
+  
+  docID = NULL;
+  versionNumber = UNUSED;
+  source = date = headline = originCity = NULL;
+  
+  buf = readUserInfoHeader(&tag,&size,buf);
+  headerSize = buf - buffer;
+    
+  while (buf < (buffer + size + headerSize))
+   { data_tag tag = peekTag(buf);
+     switch (tag)
+      { case DT_DocumentID:
+  		  buf = readAny(&docID,buf);
+  		  break;
+  		case DT_VersionNumber:
+  		  buf = readNum(&versionNumber,buf);
+  		  break;
+  		case DT_Source:
+  		  buf = readString(&source,buf);
+  		  break;
+  		case DT_Date:
+  		  buf = readString(&date,buf);
+  		  break;
+  		case DT_Headline:
+  		  buf = readString(&headline,buf);
+  		  break;
+  		case DT_OriginCity:
+  		  buf = readString(&originCity,buf);
+  		  break;
+        default:
+          freeAny(docID);
+          s_free(source);
+          s_free(date);
+          s_free(headline);
+          s_free(originCity);
+          REPORT_READ_ERROR(buf);
+          break;
+      }
+   }
+  	  
+  *docHeadline = makeWAISDocumentHeadlines(docID,versionNumber,source,date,
+  									       headline,originCity);
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+WAISDocumentCodes*
+makeWAISDocumentCodes(docID,
+		      versionNumber,
+		      stockCodes,
+		      companyCodes,
+		      industryCodes)
+any* docID;
+long versionNumber;
+char* stockCodes;
+char* companyCodes;
+char* industryCodes;
+{
+  WAISDocumentCodes* docCodes = (WAISDocumentCodes*)s_malloc((size_t)sizeof(WAISDocumentCodes));
+
+  docCodes->DocumentID = docID;
+  docCodes->VersionNumber = versionNumber;
+  docCodes->StockCodes = stockCodes;
+  docCodes->CompanyCodes = companyCodes;
+  docCodes->IndustryCodes = industryCodes;
+  
+  return(docCodes);
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+freeWAISDocumentCodes(docCodes)
+WAISDocumentCodes* docCodes;
+{
+  freeAny(docCodes->DocumentID);
+  s_free(docCodes->StockCodes);
+  s_free(docCodes->CompanyCodes);
+  s_free(docCodes->IndustryCodes);
+  s_free(docCodes);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+writeWAISDocumentCodes(docCodes,buffer,len)
+WAISDocumentCodes* docCodes;
+char* buffer;
+long* len;
+{
+  unsigned long header_len = userInfoTagSize(DT_DocumentCodeGroup ,
+											DefWAISDocCodeSize);
+  char* buf = buffer + header_len;
+  unsigned long size;
+  
+  RESERVE_SPACE_FOR_WAIS_HEADER(len);
+
+  buf = writeAny(docCodes->DocumentID,DT_DocumentID,buf,len);
+  buf = writeNum(docCodes->VersionNumber,DT_VersionNumber,buf,len);
+  buf = writeString(docCodes->StockCodes,DT_StockCodes,buf,len);
+  buf = writeString(docCodes->CompanyCodes,DT_CompanyCodes,buf,len);
+  buf = writeString(docCodes->IndustryCodes,DT_IndustryCodes,buf,len);
+  
+  /* now write the header and size */
+  size = buf - buffer; 
+  buf = writeUserInfoHeader(DT_DocumentCodeGroup,size,header_len,buffer,len);
+
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+readWAISDocumentCodes(docCodes,buffer)
+WAISDocumentCodes** docCodes;
+char* buffer;
+{
+  char* buf = buffer;
+  unsigned long size; 
+  unsigned long headerSize;
+  data_tag tag;
+  any* docID;
+  long versionNumber;
+  char *stockCodes,*companyCodes,*industryCodes;
+  
+  docID = NULL;
+  versionNumber = UNUSED;
+  stockCodes = companyCodes = industryCodes = NULL;
+  
+  buf = readUserInfoHeader(&tag,&size,buf);
+  headerSize = buf - buffer;
+    
+  while (buf < (buffer + size + headerSize))
+   { data_tag tag = peekTag(buf);
+     switch (tag)
+      { case DT_DocumentID:
+  		  buf = readAny(&docID,buf);
+  		  break;
+  		case DT_VersionNumber:
+  		  buf = readNum(&versionNumber,buf);
+  		  break;
+  		case DT_StockCodes:
+  		  buf = readString(&stockCodes,buf);
+  		  break;
+  		case DT_CompanyCodes:
+  		  buf = readString(&companyCodes,buf);
+  		  break;
+  		case DT_IndustryCodes:
+  		  buf = readString(&industryCodes,buf);
+  		  break;
+        default:
+          freeAny(docID);
+          s_free(stockCodes);
+          s_free(companyCodes);
+          s_free(industryCodes);
+          REPORT_READ_ERROR(buf);
+          break;
+      }
+   }
+  	  
+  *docCodes = makeWAISDocumentCodes(docID,versionNumber,stockCodes,
+  									companyCodes,industryCodes);
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+writePresentInfo(present,buffer,len)
+PresentAPDU* present;
+char* buffer;
+long* len;
+{
+  /* The WAIS protocol doesn't use present info */
+  return(buffer);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+readPresentInfo(info,buffer)
+void** info;
+char* buffer;
+{
+  /* The WAIS protocol doesn't use present info */
+  *info = NULL;
+  return(buffer);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+writePresentResponseInfo(response,buffer,len)
+PresentResponseAPDU* response;
+char* buffer;
+long* len;
+{
+  /* The WAIS protocol doesn't use presentResponse info */
+  return(buffer);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+readPresentResponseInfo(info,buffer)
+void** info;
+char* buffer;
+{
+  /* The WAIS protocol doesn't use presentResponse info */
+  *info = NULL;
+  return(buffer);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* support for type 1 queries */
+
+/* new use values (for the chunk types) */
+#define	BYTE		"wb"
+#define	LINE		"wl"
+#define	PARAGRAPH	"wp"
+#define DATA_TYPE	"wt"
+
+/* WAIS supports the following semantics for type 1 queries:
+       
+     1.  retrieve the header/codes from a document:
+
+            System_Control_Number = docID
+            Data Type = type (optional)
+            And
+
+     2.  retrieve a fragment of the text of a document:
+
+            System_Control_Number = docID
+            Data Type = type (optional)
+            And
+	    	Chunk >= start
+	    	And
+	    	Chunk < end
+	    	And
+
+   		Information from multiple documents may be requested by using 
+   		groups of the above joined by:
+
+            OR
+
+   		( XXX does an OR come after every group but the first, or do they
+              all come at the end? )
+              
+        ( XXX return type could be in the element set)
+*/
+
+static query_term** makeWAISQueryTerms _AP((DocObj** docs));
+   
+static query_term**
+makeWAISQueryTerms(docs)
+DocObj** docs;
+/* given a null terminated list of docObjs, construct the appropriate
+   query of the form given above
+ */
+{
+  query_term** terms = NULL;
+  long numTerms = 0;
+  DocObj* doc = NULL;
+  long i;
+
+  if (docs == NULL)
+    return((query_term**)NULL);
+
+  terms = (query_term**)s_malloc((size_t)(sizeof(query_term*) * 1));
+  terms[numTerms] = NULL;
+
+  /* loop through the docs making terms for them all */
+  for (i = 0,doc = docs[i]; doc != NULL; doc = docs[++i])
+    { any* type = NULL;
+
+      if (doc->Type != NULL)
+	type = stringToAny(doc->Type);
+
+      if (doc->ChunkCode == CT_document) /* a whole document */
+	{ terms = (query_term**)s_realloc((char*)terms,
+					  (size_t)(sizeof(query_term*) * 
+						   (numTerms + 3 + 1)));
+	  terms[numTerms++] = makeAttributeTerm(SYSTEM_CONTROL_NUMBER,
+						EQUAL,IGNORE,IGNORE,
+						IGNORE,IGNORE,doc->DocumentID);
+	  if (type != NULL)
+	   { terms[numTerms++] = makeAttributeTerm(DATA_TYPE,EQUAL,
+						   IGNORE,IGNORE,IGNORE,
+						   IGNORE,type);
+	     terms[numTerms++] = makeOperatorTerm(AND);
+           }
+	  terms[numTerms] = NULL;
+	}
+      else			/* a document fragment */
+	{	char chunk_att[ATTRIBUTE_SIZE];
+		any* startChunk = NULL;
+		any* endChunk = NULL;
+ 
+		terms = (query_term**)s_realloc((char*)terms,
+						(size_t)(sizeof(query_term*) * 
+							 (numTerms + 7 + 1)));
+
+		switch (doc->ChunkCode)
+		  { case CT_byte:
+		    case CT_line:
+		      { char start[20],end[20];
+			(doc->ChunkCode == CT_byte) ?
+			  strncpy(chunk_att,BYTE,ATTRIBUTE_SIZE) :
+			strncpy(chunk_att,LINE,ATTRIBUTE_SIZE);	
+			sprintf(start,"%ld",doc->ChunkStart.Pos);
+			startChunk = stringToAny(start);
+			sprintf(end,"%ld",doc->ChunkEnd.Pos);
+			endChunk = stringToAny(end);
+		      }
+		      break;
+		    case CT_paragraph:
+		      strncpy(chunk_att,PARAGRAPH,ATTRIBUTE_SIZE);
+		      startChunk = doc->ChunkStart.ID;
+		      endChunk = doc->ChunkEnd.ID;
+		      break;
+		    default:
+		      /* error */
+		      break;
+		    }
+
+		terms[numTerms++] = makeAttributeTerm(SYSTEM_CONTROL_NUMBER,
+						      EQUAL,IGNORE,IGNORE,
+						      IGNORE,
+						      IGNORE,doc->DocumentID);
+		if (type != NULL)
+		 { terms[numTerms++] = makeAttributeTerm(DATA_TYPE,EQUAL,IGNORE,
+							 IGNORE,IGNORE,IGNORE,
+							 type);
+		   terms[numTerms++] = makeOperatorTerm(AND);
+		 }
+		terms[numTerms++] = makeAttributeTerm(chunk_att,
+						      GREATER_THAN_OR_EQUAL,
+						      IGNORE,IGNORE,IGNORE, 
+						      IGNORE,
+						      startChunk);
+		terms[numTerms++] = makeOperatorTerm(AND);
+		terms[numTerms++] = makeAttributeTerm(chunk_att,LESS_THAN,
+						      IGNORE,IGNORE,IGNORE,
+						      IGNORE,
+						      endChunk);
+		terms[numTerms++] = makeOperatorTerm(AND);
+		terms[numTerms] = NULL;
+
+		if (doc->ChunkCode == CT_byte || doc->ChunkCode == CT_line)
+		  { freeAny(startChunk);
+		    freeAny(endChunk);
+		  }
+	      }
+      
+      freeAny(type);
+      
+     if (i != 0) /* multiple independent queries, need a disjunction */
+	{ terms = (query_term**)s_realloc((char*)terms,
+					  (size_t)(sizeof(query_term*) * 
+						   (numTerms + 1 + 1)));
+	  terms[numTerms++] = makeOperatorTerm(OR);
+	  terms[numTerms] = NULL;
+	}
+    }
+
+  return(terms);
+}
+
+/*----------------------------------------------------------------------*/
+
+static DocObj** makeWAISQueryDocs _AP((query_term** terms));
+
+static DocObj** 
+makeWAISQueryDocs(terms)
+query_term** terms;
+/* given a list of terms in the form given above, convert them to 
+   DocObjs.
+ */
+{
+  query_term* docTerm = NULL;
+  query_term* fragmentTerm = NULL;
+  DocObj** docs = NULL;
+  DocObj* doc = NULL;
+  long docNum,termNum;
+
+  docNum = termNum = 0;
+  
+  docs = (DocObj**)s_malloc((size_t)(sizeof(DocObj*) * 1));
+  docs[docNum] = NULL;
+
+  /* translate the terms into DocObjs */
+  while (true)
+    {	      
+      query_term* typeTerm = NULL;
+      char* type = NULL;
+      long startTermOffset;
+
+      docTerm = terms[termNum];
+     
+      if (docTerm == NULL)
+	break;			/* we're done converting */;
+
+      typeTerm = terms[termNum + 1]; /* get the lead Term if it exists */
+
+      if (strcmp(typeTerm->Use,DATA_TYPE) == 0)	/* we do have a type */
+       { startTermOffset = 3;	
+	 type = anyToString(typeTerm->Term);
+       }
+      else 				   	/* no type */
+       { startTermOffset = 1;
+	 typeTerm = NULL;
+	 type = NULL;
+       }
+
+      /* grow the doc list */
+      docs = (DocObj**)s_realloc((char*)docs,(size_t)(sizeof(DocObj*) * 
+						      (docNum + 1 + 1)));
+
+      /* figure out what kind of docObj to build - and build it */
+      fragmentTerm = terms[termNum + startTermOffset];
+      if (fragmentTerm != NULL && fragmentTerm->TermType == TT_Attribute)
+	{			/* build a document fragment */
+	  query_term* startTerm = fragmentTerm;
+	  query_term* endTerm = terms[termNum + startTermOffset + 2]; 
+
+	  if (strcmp(startTerm->Use,BYTE) == 0){ /* a byte chunk */
+	    doc = makeDocObjUsingBytes(duplicateAny(docTerm->Term),
+				       type,
+				       anyToLong(startTerm->Term),
+				       anyToLong(endTerm->Term));
+           log_write("byte");
+	  }else if (strcmp(startTerm->Use,LINE) == 0){ /* a line chunk */
+	    doc = makeDocObjUsingLines(duplicateAny(docTerm->Term),
+				       type,
+				       anyToLong(startTerm->Term),
+				       anyToLong(endTerm->Term));
+            log_write("line");
+	  }else{
+	log_write("chunk");			/* a paragraph chunk */
+	    doc = makeDocObjUsingParagraphs(duplicateAny(docTerm->Term),
+					    type,
+					    duplicateAny(startTerm->Term),
+					    duplicateAny(endTerm->Term));
+}
+	  termNum += (startTermOffset + 4);	/* point to next term */
+	}
+      else			/* build a full document */
+	{ 
+	  doc = makeDocObjUsingWholeDocument(duplicateAny(docTerm->Term),
+					     type);
+log_write("whole doc");
+	  termNum += startTermOffset;	/* point to next term */
+	}
+     
+      docs[docNum++] = doc;	/* insert the new document */
+	 
+      docs[docNum] = NULL;	/* keep the doc list terminated */
+
+	 
+      if (terms[termNum] != NULL)
+	termNum++; /* skip the OR operator it necessary */
+      else
+	break; /* we are done */
+    }
+
+  return(docs);
+}
+
+/*----------------------------------------------------------------------*/
+
+any* 
+makeWAISTextQuery(docs)
+DocObj** docs;
+/* given a list of DocObjs, return an any whose contents is the corresponding
+   type 1 query
+ */
+{
+  any *buf = NULL;
+  query_term** terms = NULL;
+  
+  terms = makeWAISQueryTerms(docs);
+  buf = writeQuery(terms);
+  
+  doList((void**)terms,freeTerm);
+  s_free(terms);
+  
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+DocObj** 
+readWAISTextQuery(buf)
+any* buf;
+/* given an any whose contents are type 1 queries of the WAIS sort, 
+   construct a list of the corresponding DocObjs
+ */
+{
+  query_term** terms = NULL;
+  DocObj** docs = NULL;
+  
+  terms = readQuery(buf);
+  docs = makeWAISQueryDocs(terms);
+  
+  doList((void**)terms,freeTerm);
+  s_free(terms);
+  
+  return(docs);
+}
+
+/*----------------------------------------------------------------------*/
+/* Customized free WAIS object routines:                                */
+/*                                                                      */
+/*   This set of procedures is for applications to free a WAIS object   */
+/*   which was made with makeWAISFOO.                                   */
+/*   Each procedure frees only the memory that was allocated in its     */
+/*   associated makeWAISFOO routine, thus it's not necessary for the    */
+/*   caller to assign nulls to the pointer fields of the WAIS object.  */
+/*----------------------------------------------------------------------*/
+
+void 
+CSTFreeWAISInitResponse(init)
+WAISInitResponse* init;
+/* free an object made with makeWAISInitResponse */
+{
+  s_free(init);
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+CSTFreeWAISSearch(query)
+WAISSearch* query;
+/* destroy an object made with makeWAISSearch() */
+{ 
+  s_free(query);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+CSTFreeDocObj(doc)
+DocObj* doc;
+/* free a docObj */
+{ 
+    s_free(doc);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+CSTFreeWAISDocumentHeader(header)
+WAISDocumentHeader* header;
+{ 
+    s_free(header);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+CSTFreeWAISDocumentShortHeader(header)
+WAISDocumentShortHeader* header;
+{ 
+  s_free(header);
+}
+/*----------------------------------------------------------------------*/
+
+void
+CSTFreeWAISDocumentLongHeader(header)
+WAISDocumentLongHeader* header;
+{
+  s_free(header);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+CSTFreeWAISSearchResponse(response)
+WAISSearchResponse* response;
+{ 
+  s_free(response);
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+CSTFreeWAISDocumentText(docText)
+WAISDocumentText* docText;
+{ 
+  s_free(docText);
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+CSTFreeWAISDocHeadlines(docHeadline)
+WAISDocumentHeadlines* docHeadline;
+{ 
+  s_free(docHeadline);
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+CSTFreeWAISDocumentCodes(docCodes)
+WAISDocumentCodes* docCodes;
+{
+  s_free(docCodes);
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+CSTFreeWAISTextQuery(query)
+any* query;
+{
+   freeAny(query);
+}
+
+/*----------------------------------------------------------------------*/
+
+
+/*
+**	Routines originally from WMessage.c -- FM
+**
+**----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE
+   No guarantees or restrictions.  See the readme file for the full standard
+   disclaimer.    
+   3.26.90
+*/
+
+/* This file is for reading and writing the wais packet header.
+ * Morris@think.com
+ */
+
+/* to do:
+ *  add check sum
+ *  what do you do when checksum is wrong?
+ */
+
+/*---------------------------------------------------------------------*/
+
+void 
+readWAISPacketHeader(msgBuffer,header_struct)
+char* msgBuffer;
+WAISMessage *header_struct;
+{
+  /* msgBuffer is a string containing at least HEADER_LENGTH bytes. */
+		    
+  memmove(header_struct->msg_len,msgBuffer,(size_t)10); 
+  header_struct->msg_type = char_downcase((unsigned long)msgBuffer[10]);
+  header_struct->hdr_vers = char_downcase((unsigned long)msgBuffer[11]);
+  memmove(header_struct->server,(void*)(msgBuffer + 12),(size_t)10);
+  header_struct->compression = char_downcase((unsigned long)msgBuffer[22]);
+  header_struct->encoding = char_downcase((unsigned long)msgBuffer[23]);
+  header_struct->msg_checksum = char_downcase((unsigned long)msgBuffer[24]);
+}
+ 
+/*---------------------------------------------------------------------*/
+
+/* this modifies the header argument.  See wais-message.h for the different
+ * options for the arguments.
+ */
+ 
+void
+writeWAISPacketHeader(header,
+		      dataLen,
+		      type,
+		      server,
+		      compression,
+		      encoding,
+		      version)
+char* header;
+long dataLen;
+long type;
+char* server;
+long compression;
+long encoding;
+long version;
+/* Puts together the new wais before-the-z39-packet header. */
+{
+  char lengthBuf[11];
+  char serverBuf[11];
+
+  long serverLen = strlen(server);
+  if (serverLen > 10)
+    serverLen = 10;
+
+  sprintf(lengthBuf, "%010ld", dataLen);  
+  strncpy(header,lengthBuf,10);
+
+  header[10] = type & 0xFF; 
+  header[11] = version & 0xFF;
+
+  strncpy(serverBuf,server,serverLen);       
+  strncpy((char*)(header + 12),serverBuf,serverLen);
+
+  header[22] = compression & 0xFF;    
+  header[23] = encoding & 0xFF;    
+  header[24] = '0'; /* checkSum(header + HEADER_LENGTH,dataLen);   XXX the result must be ascii */	
+}              
+              
+/*---------------------------------------------------------------------*/
+
diff --git a/WWW/Library/Implementation/HTVMS_WaisProt.h b/WWW/Library/Implementation/HTVMS_WaisProt.h
new file mode 100644
index 00000000..1a4e83d5
--- /dev/null
+++ b/WWW/Library/Implementation/HTVMS_WaisProt.h
@@ -0,0 +1,376 @@
+/*							HTVMS_WAISProt.h
+**
+**	Adaptation for Lynx by F.Macrides (macrides@sci.wfeb.edu)
+**
+**	31-May-1994 FM	Initial version.
+**
+**----------------------------------------------------------------------*/
+
+/*
+**	Routines originally from WProt.h -- FM
+**
+**----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE:
+   No guarantees or restrictions.  See the readme file for the full standard
+   disclaimer.	
+  
+   3.26.90	Harry Morris, morris@think.com
+   3.30.90  Harry Morris 
+   			-	removed chunk code from WAISSearchAPDU,
+   			-	added makeWAISQueryType1Query() and readWAISType1Query() which
+   				replace makeWAISQueryTerms() and makeWAISQueryDocs().
+   4.11.90  HWM - added definitions of wais element set names
+   4.14.90  HWM - changed symbol for relevance feedback query from QT_3 to 
+   				  QT_RelevanceFeedbackQuery added QT_TextRetrievalQuery as a
+   				  synonym for QT_BooleanQuery
+				- renamed makeWAISType1Query() to makeWAISTextQuery()
+				  renamed readWAISType1Query() to readWAISTextQuery()
+   5.29.90  TS - added CSTFreeWAISFoo functions
+*/
+
+#ifndef _H_WAIS_protocol_
+
+#define _H_WAIS_protocol_
+
+/*----------------------------------------------------------------------*/
+/* Data types / constants */
+
+/* date factor constants */
+#define	DF_INDEPENDENT		1
+#define DF_LATER		2
+#define DF_EARLIER		3
+#define DF_SPECIFIED_RANGE	4
+
+/* chunk types */
+#define CT_document		0
+#define CT_byte			1
+#define CT_line			2
+#define CT_paragraph	3
+
+/* relevance feedback query */
+#define QT_RelevanceFeedbackQuery	"3"
+#define QT_TextRetrievalQuery		QT_BooleanQuery
+
+/* new data tags */
+#define DT_UserInformationLength	(data_tag)99
+#define	DT_ChunkCode			(data_tag)100
+#define	DT_ChunkIDLength		(data_tag)101
+#define	DT_ChunkMarker			(data_tag)102
+#define	DT_HighlightMarker		(data_tag)103
+#define	DT_DeHighlightMarker		(data_tag)104
+#define	DT_NewlineCharacters		(data_tag)105
+#define	DT_SeedWords			(data_tag)106
+#define	DT_DocumentIDChunk		(data_tag)107
+#define	DT_ChunkStartID			(data_tag)108
+#define	DT_ChunkEndID			(data_tag)109
+#define	DT_TextList			(data_tag)110
+#define	DT_DateFactor			(data_tag)111
+#define	DT_BeginDateRange		(data_tag)112
+#define	DT_EndDateRange			(data_tag)113
+#define	DT_MaxDocumentsRetrieved	(data_tag)114
+#define	DT_SeedWordsUsed		(data_tag)115
+#define	DT_DocumentID			(data_tag)116
+#define	DT_VersionNumber		(data_tag)117
+#define	DT_Score			(data_tag)118
+#define	DT_BestMatch			(data_tag)119
+#define	DT_DocumentLength		(data_tag)120
+#define	DT_Source			(data_tag)121
+#define	DT_Date				(data_tag)122
+#define	DT_Headline			(data_tag)123
+#define	DT_OriginCity			(data_tag)124
+#define	DT_PresentStartByte		(data_tag)125
+#define	DT_TextLength			(data_tag)126
+#define	DT_DocumentText			(data_tag)127
+#define	DT_StockCodes			(data_tag)128
+#define	DT_CompanyCodes			(data_tag)129
+#define	DT_IndustryCodes		(data_tag)130
+
+/* added by harry */
+#define DT_DocumentHeaderGroup		(data_tag)150
+#define DT_DocumentShortHeaderGroup	(data_tag)151
+#define DT_DocumentLongHeaderGroup	(data_tag)152
+#define DT_DocumentTextGroup		(data_tag)153
+#define DT_DocumentHeadlineGroup 	(data_tag)154
+#define DT_DocumentCodeGroup		(data_tag)155
+#define DT_Lines					(data_tag)131 
+#define	DT_TYPE_BLOCK				(data_tag)132
+#define DT_TYPE						(data_tag)133
+
+/* wais element sets */
+#define ES_DocumentHeader		"Document Header"
+#define ES_DocumentShortHeader	"Document Short Header"
+#define ES_DocumentLongHeader	"Document Long Header"
+#define ES_DocumentText			"Document Text"
+#define ES_DocumentHeadline		"Document Headline"
+#define ES_DocumentCodes		"Document Codes"
+
+typedef struct DocObj { /* specifies a section of a document */
+	any*	DocumentID;
+	char*   Type;
+	long	ChunkCode;
+	union {
+		long	Pos;
+		any*	ID;
+	} ChunkStart;
+	union {
+		long	Pos;
+		any*	ID;
+	} ChunkEnd;
+	} DocObj;
+	
+/*----------------------------------------------------------------------*/
+/* WAIS APDU extensions */
+
+typedef struct WAISInitResponse {
+	long				ChunkCode;
+	long				ChunkIDLength;
+	char*				ChunkMarker;
+	char*				HighlightMarker;
+	char* 				DeHighlightMarker;
+	char*				NewlineCharacters;
+	/* XXX  need to add UpdateFrequency and Update Time */
+	} WAISInitResponse;
+
+typedef struct WAISSearch {
+	char*				SeedWords;
+	DocObj**			Docs;
+	char**				TextList;
+	long				DateFactor;
+	char*				BeginDateRange;
+	char*				EndDateRange;
+	long				MaxDocumentsRetrieved;
+	} WAISSearch;
+
+typedef struct WAISDocumentHeader {
+	any*				DocumentID;
+	long				VersionNumber;
+	long				Score;     
+	long				BestMatch; 
+	long				DocumentLength;
+	long 				Lines;
+	char**				Types;
+	char*				Source;
+	char*				Date;
+	char*				Headline;
+	char*				OriginCity;
+	} WAISDocumentHeader;
+
+typedef struct WAISDocumentShortHeader {
+	any*				DocumentID;
+	long				VersionNumber;
+	long				Score;     
+	long				BestMatch; 
+	long				DocumentLength;
+	long 				Lines;
+ 	} WAISDocumentShortHeader;
+ 
+typedef struct WAISDocumentLongHeader {
+	any*				DocumentID;
+	long				VersionNumber;
+	long				Score;     
+	long				BestMatch; 
+	long				DocumentLength;
+	long 				Lines;
+	char**				Types;
+	char*				Source;
+	char*				Date;
+	char*				Headline;
+	char*				OriginCity;
+	char*				StockCodes;
+	char* 				CompanyCodes;
+	char*				IndustryCodes;
+ 	} WAISDocumentLongHeader;
+
+typedef struct WAISDocumentText {
+	any*				DocumentID;
+	long				VersionNumber;
+	any*				DocumentText;
+	} WAISDocumentText;
+	
+typedef struct WAISDocumentHeadlines {
+	any*				DocumentID;
+	long				VersionNumber;
+	char*				Source;
+	char*				Date;
+	char*				Headline;
+	char*				OriginCity;
+	} WAISDocumentHeadlines;
+	
+typedef struct WAISDocumentCodes {
+	any*				DocumentID;
+	long				VersionNumber;
+	char*				StockCodes;
+	char*				CompanyCodes;
+	char*				IndustryCodes;
+	} WAISDocumentCodes;
+	
+typedef struct WAISSearchResponse {
+	char*			       		SeedWordsUsed;
+	WAISDocumentHeader** 		DocHeaders;
+	WAISDocumentShortHeader** 	ShortHeaders;
+	WAISDocumentLongHeader** 	LongHeaders;
+	WAISDocumentText**			Text;
+	WAISDocumentHeadlines**		Headlines;
+	WAISDocumentCodes**			Codes;
+	diagnosticRecord**			Diagnostics;
+	} WAISSearchResponse;
+
+/*----------------------------------------------------------------------*/
+/* Functions */
+
+DocObj* makeDocObjUsingWholeDocument _AP((any* aDocID,char* type));
+DocObj* makeDocObjUsingBytes _AP((any* aDocID,char* type,long start,long end));
+DocObj* makeDocObjUsingLines _AP((any* aDocID,char* type,long start,long end));
+DocObj* makeDocObjUsingParagraphs _AP((any* aDocID,char* type,any* start,any* end));
+void freeDocObj _AP((DocObj* doc));
+
+WAISInitResponse* makeWAISInitResponse _AP((long chunkCode,long chunkIDLen,
+					    char* chunkMarker,char* highlightMarker,
+					    char* deHighlightMarker,char* newLineChars));
+void freeWAISInitResponse _AP((WAISInitResponse* init));
+
+WAISSearch* makeWAISSearch _AP((
+	char* seedWords,DocObj** docs,char** textList,
+	long dateFactor,char* beginDateRange,char* endDateRange,
+	long maxDocsRetrieved));
+void freeWAISSearch _AP((WAISSearch* query));
+
+WAISDocumentHeader* makeWAISDocumentHeader _AP((
+	any* aDocID,long versionNumber,long score,long bestMatch,long docLen,
+	long lines,char** types,char* source,char* date,char* headline,char* originCity));
+void freeWAISDocumentHeader _AP((WAISDocumentHeader* header));
+char* writeWAISDocumentHeader _AP((WAISDocumentHeader* header,char* buffer,long* len));
+char* readWAISDocumentHeader _AP((WAISDocumentHeader** header,char* buffer));
+
+WAISDocumentShortHeader* makeWAISDocumentShortHeader _AP((
+	any* aDocID,long versionNumber,long score,long bestMatch,long docLen,long lines));
+void freeWAISDocumentShortHeader _AP((WAISDocumentShortHeader* header));
+char* writeWAISDocumentShortHeader _AP((WAISDocumentShortHeader* header,
+                                   char* buffer,long* len));
+char* readWAISDocumentShortHeader _AP((WAISDocumentShortHeader** header,char* buffer));
+
+WAISDocumentLongHeader* makeWAISDocumentLongHeader _AP((
+	any* aDocID,long versionNumber,long score,long bestMatch,long docLen,
+	long lines,char** types,char* source,char* date, char* headline,char* originCity,
+	char* stockCodes,char* companyCodes,char* industryCodes));
+void freeWAISDocumentLongHeader _AP((WAISDocumentLongHeader* header));
+char* writeWAISDocumentLongHeader _AP((WAISDocumentLongHeader* header,char* buffer,long* len));
+char* readWAISDocumentLongHeader _AP((WAISDocumentLongHeader** header,char* buffer));
+
+WAISSearchResponse* makeWAISSearchResponse _AP((
+	char* seedWordsUsed,WAISDocumentHeader** docHeaders,
+	WAISDocumentShortHeader** shortHeaders,
+	WAISDocumentLongHeader** longHeaders,
+	WAISDocumentText** text,WAISDocumentHeadlines** headlines,
+	WAISDocumentCodes** codes,
+	diagnosticRecord** diagnostics));
+void freeWAISSearchResponse _AP((WAISSearchResponse* response));
+
+WAISDocumentText* makeWAISDocumentText _AP((any* aDocID,long versionNumber,
+				       any* documentText));
+void freeWAISDocumentText _AP((WAISDocumentText* docText));
+char* writeWAISDocumentText _AP((WAISDocumentText* docText,char* buffer,long* len));
+char* readWAISDocumentText _AP((WAISDocumentText** docText,char* buffer));
+
+WAISDocumentHeadlines* makeWAISDocumentHeadlines _AP((
+	any* aDocID,long versionNumber,char* source,char* date,char* headline,
+	char* originCity));
+void freeWAISDocumentHeadlines _AP((WAISDocumentHeadlines* docHeadline));
+char* writeWAISDocumentHeadlines _AP((WAISDocumentHeadlines* docHeadline,char* buffer,long* len));
+char* readWAISDocumentHeadlines _AP((WAISDocumentHeadlines** docHeadline,char* buffer));
+
+WAISDocumentCodes* makeWAISDocumentCodes _AP((
+	any* aDocID,long versionNumber,char* stockCodes,char* companyCodes,
+	char* industryCodes));
+void freeWAISDocumentCodes _AP((WAISDocumentCodes* docCodes));
+char* writeWAISDocumentCodes _AP((WAISDocumentCodes* docCodes,char* buffer,long* len));
+char* readWAISDocumentCodes _AP((WAISDocumentCodes** docCodes,char* buffer));
+
+any* makeWAISTextQuery _AP((DocObj** docs));
+DocObj** readWAISTextQuery _AP((any* terms));
+
+void CSTFreeWAISInitResponse _AP((WAISInitResponse* init));
+void CSTFreeWAISSearch _AP((WAISSearch* query));
+void CSTFreeDocObj _AP((DocObj* doc));
+void CSTFreeWAISDocumentHeader _AP((WAISDocumentHeader* header));
+void CSTFreeWAISDocumentShortHeader _AP((WAISDocumentShortHeader* header));
+void CSTFreeWAISDocumentLongHeader _AP((WAISDocumentLongHeader* header));
+void CSTFreeWAISSearchResponse _AP((WAISSearchResponse* response));
+void CSTFreeWAISDocumentText _AP((WAISDocumentText* docText));
+void CSTFreeWAISDocHeadlines _AP((WAISDocumentHeadlines* docHeadline));
+void CSTFreeWAISDocumentCodes _AP((WAISDocumentCodes* docCodes));
+void CSTFreeWAISTextQuery _AP(( any* query));
+
+/*----------------------------------------------------------------------*/
+
+#endif /* ndef _H_WAIS_protocol_ */
+
+
+/*
+**	Routines originally from WMessage.h -- FM
+**
+**----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE
+   No guarantees or restrictions.  See the readme file for the full standard
+   disclaimer.    
+   3.26.90
+*/
+
+/* wais-message.h
+ *
+ * This is the header outside of WAIS Z39.50 messages.  The header will be
+ * printable ascii, so as to be transportable.  This header will precede each
+ * Z39.50 APDU, or zero-length message if it is an ACK or NACK.  Be sure to
+ * change hdr_vers current value if you change the structure of the header.
+ *
+ * The characters in the header are case insensitive so that the systems from
+ * the past that only handle one case can at least read the header.
+ *
+ * 7.5.90 HWM - added constants
+ * 7/5/90 brewster added funtion prototypes and comments
+ * 11/30/90 HWM - went to version 2 (inits and typed retrieval)
+ */
+
+#ifndef WMESSAGE_H
+#define WMESSAGE_H
+
+typedef struct wais_header {
+        char    msg_len[10];    /* length in bytes of following message */
+        char    msg_type;       /* type of message: 'z'=Z39.50 APDU,
+                                   'a'=ACK, 'n'=NACK */
+        char    hdr_vers;       /* version of this header, currently = '2' */
+        char    server[10];     /* name or address of server */
+        char    compression;    /* <sp>=no compression, 'u'=unix compress */
+        char    encoding;       /* <sp>=no encoding, 'h'=hexize, 
+				   'u'=uuencode */
+        char    msg_checksum;   /* XOR of every byte of message */
+        } WAISMessage;
+
+#define HEADER_LENGTH 	25	/* number of bytes needed to write a 
+				   wais-header (not sizeof(wais_header)) */
+
+#define HEADER_VERSION 	(long)'2'
+
+/* message type */
+#define Z3950			'z'  
+#define ACK			'a'  
+#define	NAK			'n'  
+
+/* compression */
+#define NO_COMPRESSION 		' ' 
+#define UNIX_COMPRESSION 	'u' 
+
+/* encoding */
+#define NO_ENCODING		' '  
+#define HEX_ENCODING	'h'  /* Swartz 4/3 encoding */
+#define IBM_HEXCODING	'i'	 /* same as h but uses characters acceptable for IBM mainframes */
+#define UUENCODE		'u'  
+
+
+void readWAISPacketHeader _AP((char* msgBuffer,WAISMessage *header_struct));
+long getWAISPacketLength _AP((WAISMessage* header));
+void writeWAISPacketHeader _AP((char* header,long dataLen,long type,
+				char* server,long compression,	
+				long encoding,long version));
+
+#endif /* ndef WMESSAGE_H */
diff --git a/WWW/Library/Implementation/HTVMS_WaisUI.c b/WWW/Library/Implementation/HTVMS_WaisUI.c
new file mode 100644
index 00000000..39f2bd0b
--- /dev/null
+++ b/WWW/Library/Implementation/HTVMS_WaisUI.c
@@ -0,0 +1,2448 @@
+/*							HTVMS_WAISUI.c
+**
+**	Adaptation for Lynx by F.Macrides (macrides@sci.wfeb.edu)
+**
+**	30-May-1994 FM	Initial version.
+**
+/*----------------------------------------------------------------------*/
+
+/*
+**	Routines originally from UI.c -- FM
+**
+**----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE:
+   No guarantees or restrictions.  See the readme file for the full standard
+   disclaimer.
+
+   Brewster@think.com
+*/
+
+/* 
+ * this is a simple ui toolkit for building other ui's on top.
+ * -brewster
+ * 
+ * top level functions:
+ *   generate_search_apdu
+ *   generate_retrieval_apdu
+ *   interpret_message
+ *
+ */
+
+/* to do:
+ *   generate multiple queries for long documents.
+ *     this will crash if the file being retrieved is larger than 100k.
+ *   do log_write()
+ *   
+ */
+
+#include "HTUtils.h"
+#include "tcp.h"
+#include "HTVMS_WaisUI.h"
+#include "HTVMS_WaisProt.h"
+#include "HTTCP.h"
+/*#include <stdio> included by HTUtils.h -- FM */
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <stdarg.h>
+
+#include "LYexit.h"
+#include "LYLeaks.h"
+
+void
+log_write(s)
+char *s;
+{
+    return;
+}
+
+/*----------------------------------------------------------------------*/
+
+/* returns a pointer in the buffer of the first free byte.
+   if it overflows, then NULL is returned 
+ */
+char *
+generate_search_apdu(buff,
+		     buff_len,
+		     seed_words,
+		     database_name,
+		     docobjs,
+		     maxDocsRetrieved)
+char* buff;     /* buffer to hold the apdu */
+long *buff_len;    /* length of the buffer changed to reflect new data written */
+char *seed_words;    /* string of the seed words */
+char *database_name;
+DocObj** docobjs;
+long maxDocsRetrieved;
+{
+  /* local variables */
+
+  SearchAPDU *search3;
+  char  *end_ptr;
+  static char *database_names[2] = {"", 0};
+  any refID;
+  WAISSearch *query;
+  refID.size = 1;
+  refID.bytes = "3";
+
+  database_names[0] = database_name;
+  query = makeWAISSearch(seed_words,
+                         docobjs, /* DocObjsPtr */
+                         0,
+                         1,     /* DateFactor */
+                         0,     /* BeginDateRange */
+                         0,     /* EndDateRange */
+                         maxDocsRetrieved
+                         );
+
+  search3 = makeSearchAPDU(30, 
+			   5000, /* should be large */
+			   30,
+                           1,	/* replace indicator */
+                           "",	/* result set name */
+                           database_names, /* database name */   
+                           QT_RelevanceFeedbackQuery, /* query_type */
+                           0,   /* element name */
+                           NULL, /* reference ID */
+                           query);
+
+  end_ptr = writeSearchAPDU(search3, buff, buff_len);
+
+  CSTFreeWAISSearch(query);
+  freeSearchAPDU(search3);
+  return(end_ptr);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* returns a pointer into the buffer of the next free byte.
+   if it overflowed, then NULL is returned
+ */
+
+char *
+generate_retrieval_apdu(buff,
+			buff_len,
+			docID,
+			chunk_type,
+			start,
+			end,
+			type,
+			database_name)
+char *buff;
+long *buff_len;    /* length of the buffer changed to reflect new data written */
+any *docID;
+long chunk_type;
+long start;
+long end;
+char *type;
+char *database_name;
+{
+  SearchAPDU *search;
+  char  *end_ptr;
+
+  static char *database_names[2];
+  static char *element_names[3];
+  any refID;
+
+  DocObj *DocObjs[2];
+  any *query;			/* changed from char* by brewster */
+
+  if(NULL == type)
+    type = s_strdup("TEXT");
+
+  database_names[0] = database_name;
+  database_names[1] = NULL;
+
+  element_names[0] = " ";
+  element_names[1] = ES_DocumentText;
+  element_names[2] = NULL;
+
+  refID.size = 1;
+  refID.bytes = "3";
+  
+  switch(chunk_type){
+  case CT_line: 
+    DocObjs[0] = makeDocObjUsingLines(docID, type, start, end);
+    break;
+  case CT_byte:
+    DocObjs[0] = makeDocObjUsingBytes(docID, type, start, end);
+    break;
+  }
+  DocObjs[1] = NULL;
+
+  query = makeWAISTextQuery(DocObjs);   
+  search = makeSearchAPDU( 10, 16, 15, 
+			  1,	/* replace indicator */
+			  "FOO", /* result set name */
+			  database_names, /* database name */   
+			  QT_TextRetrievalQuery, /* query_type */
+			  element_names, /* element name */
+			  &refID, /* reference ID */
+			  query);
+  end_ptr = writeSearchAPDU(search, buff, buff_len);
+  CSTFreeWAISTextQuery(query);
+  freeSearchAPDU(search);
+  return(end_ptr);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* this is a safe version of unix 'read' it does all the checking
+ * and looping necessary
+ * to those trying to modify the transport code to use non-UNIX streams:
+ *  This is the function to modify!
+ */
+long read_from_stream(d,buf,nbytes)
+int d;				/* this is the stream */
+char *buf;
+long nbytes;
+{
+  long didRead;
+  long toRead = nbytes;
+  long totalRead = 0;		/* paranoia */
+
+  while (toRead > 0){
+    didRead = NETREAD (d, buf, (int)toRead);
+    if(didRead == HT_INTERRUPTED)
+      return(HT_INTERRUPTED);
+    if(didRead == -1)		/* error*/
+      return(-1);
+    if(didRead == 0)		/* eof */
+      return(-2);		/* maybe this should return 0? */
+    toRead -= didRead;
+    buf += didRead;
+    totalRead += didRead;
+  }
+  if(totalRead != nbytes)	/* we overread for some reason */
+    return(- totalRead);	/* bad news */    
+  return(totalRead);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* returns the length of the response, 0 if an error */
+
+long 
+transport_message(connection,
+		  request_message,
+		  request_length,
+		  response_message,
+		  response_buffer_length)
+int connection;
+char *request_message;
+long request_length;
+char *response_message;
+long response_buffer_length;
+{
+  WAISMessage header;
+  long response_length;
+  int rv;
+
+  
+  /* Write out message. Read back header. Figure out response length. */
+  
+  if( request_length + HEADER_LENGTH !=
+      NETWRITE(connection,request_message,
+   		  (int)( request_length +HEADER_LENGTH)) )
+    return 0;
+
+  /* read for the first '0' */
+
+  while(1){
+    rv = read_from_stream(connection, response_message, 1);
+    if (rv == HT_INTERRUPTED)
+      return HT_INTERRUPTED;
+    if (rv < 0)
+      return 0;
+    if('0' == response_message[0])
+      break;
+  }
+
+  rv = read_from_stream(connection, response_message + 1, HEADER_LENGTH -1);
+  if (rv == HT_INTERRUPTED)
+    return HT_INTERRUPTED;
+  if (rv < 0)
+    return 0;
+
+  readWAISPacketHeader(response_message, &header);
+  {
+    char length_array[11];
+    strncpy(length_array, header.msg_len, 10);
+    length_array[10] = '\0';
+    response_length = atol(length_array);
+    /*
+      if(verbose){
+      printf("WAIS header: '%s' length_array: '%s'\n", 
+      response_message, length_array);
+      }
+      */
+    if(response_length > response_buffer_length){
+      /* we got a message that is too long, therefore empty the message out,
+	 and return 0 */
+      long i;
+      for(i = 0; i < response_length; i++){
+	rv = read_from_stream(connection, 
+			      response_message + HEADER_LENGTH,
+			      1);
+	if (rv == HT_INTERRUPTED)
+	  return HT_INTERRUPTED;
+	if (rv < 0)
+	  return 0;
+      }
+      return(0);
+    }
+  }
+  rv = read_from_stream(connection, 
+			response_message + HEADER_LENGTH,
+			response_length);
+  if (rv == HT_INTERRUPTED)
+    return HT_INTERRUPTED;
+  if (rv < 0)
+    return 0;
+  return(response_length);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* returns the number of bytes writen.  0 if an error */
+long
+interpret_message(request_message,request_length,
+		  response_message,
+		  response_buffer_length,
+		  connection,
+		  verbose)
+char *request_message;
+long request_length; /* length of the buffer */
+char *response_message;
+long response_buffer_length;
+int connection;
+boolean verbose;
+{
+  long response_length;
+
+  /* ?
+  if(verbose){
+    printf ("sending");
+    if(hostname_internal && strlen(hostname_internal) > 0)
+      printf(" to host %s", hostname_internal);
+    if(service_name && strlen(service_name) > 0)
+      printf(" for service %s", service_name);
+    printf("\n");
+    twais_dsply_rsp_apdu(request_message + HEADER_LENGTH, 
+			 request_length);
+  }
+
+  */
+
+  writeWAISPacketHeader(request_message,
+			request_length,
+			(long)'z',	/* Z39.50 */
+			"wais      ", /* server name */
+			(long)NO_COMPRESSION,	/* no compression */
+			(long)NO_ENCODING,(long)HEADER_VERSION);
+  if(connection != 0) {
+    response_length = transport_message(connection, request_message,
+					request_length,
+					response_message,
+					response_buffer_length);
+    if (response_length == HT_INTERRUPTED)
+      return(HT_INTERRUPTED);
+  }
+  else
+      return(0);
+
+  return(response_length);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* modifies the string to exclude all seeker codes. sets length to
+   the new length. */
+char *delete_seeker_codes(string,length)
+char *string;
+long *length;
+{
+  long original_count; /* index into the original string */
+  long new_count = 0; /* index into the collapsed string */
+  for(original_count = 0; original_count < *length; original_count++){
+    if(27 == string[original_count]){
+      /* then we have an escape code */
+      /* if the next letter is '(' or ')', then ignore two letters */
+      if('(' == string[original_count + 1] ||
+    ')' == string[original_count + 1])
+     original_count += 1;    /* it is a term marker */
+      else original_count += 4; /* it is a paragraph marker */
+    }
+    else string[new_count++] = string[original_count];
+  }
+  *length = new_count;
+  return(string);
+}
+  
+/*----------------------------------------------------------------------*/
+
+#if defined(VMS) && defined(__GNUC__)			/* 10-AUG-1995 [pr] */
+/*
+  Workaround for an obscure bug in gcc's 2.6.[123] and 2.7.0 vax/vms port;
+  sometimes global variables will end up not being defined properly,
+  causing first gas to assume they're routines, then the linker to complain
+  about unresolved symbols, and finally the program to reference the wrong
+  objects (provoking ACCVIO).  It's triggered by the specific ordering of
+  variable usage in the source code, hence rarely appears.  This bug is
+  fixed in gcc 2.7.1, and was not present in 2.6.0 and earlier.
+
+   Make a reference to VAXCRTL's _ctype_[], and also one to this dummy
+   variable itself to prevent any "defined but not used" warning.
+ */
+static __const void *__const ctype_dummy[] = { &_ctype_, &ctype_dummy };
+#endif /* VMS && __GNUC__ */
+
+/* returns a pointer to a string with good stuff */
+char *trim_junk(headline)
+char *headline;
+{
+  long length = strlen(headline) + 1; /* include the trailing null */
+  long i;
+  headline = delete_seeker_codes(headline, &length);
+  /* delete leading spaces */
+  for(i=0; i < strlen(headline); i++){
+    if(isprint(headline[i])){
+      break;
+    }
+  }
+  headline = headline + i;
+  /* delete trailing stuff */
+  for(i=strlen(headline) - 1 ; i > 0; i--){
+    if(isprint(headline[i])){
+      break;
+    }
+    headline[i] = '\0';
+  }
+  return(headline);
+}
+
+/*----------------------------------------------------------------------*/
+
+
+/*
+**	Routines originally from ZProt.c -- FM
+**
+**----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE:`
+   No guarantees or restrictions.  See the readme file for the full standard
+   disclaimer.	
+  
+   3.26.90	Harry Morris, morris@think.com
+   3.30.90  Harry Morris - Changed any->bits to any->bytes
+   4.11.90  HWM - generalized conditional includes (see c-dialect.h)
+*/
+
+#define RESERVE_SPACE_FOR_HEADER(spaceLeft)		\
+	*spaceLeft -= HEADER_LEN;
+	
+#define RELEASE_HEADER_SPACE(spaceLeft)			\
+	if (*spaceLeft > 0)				\
+	  *spaceLeft += HEADER_LEN;
+	
+/*----------------------------------------------------------------------*/
+
+InitResponseAPDU* 
+makeInitResponseAPDU(result,
+		     search,
+		     present,
+		     deleteIt,
+		     accessControl,
+		     resourceControl,
+		     prefSize,
+		     maxMsgSize,
+		     auth,
+		     id,
+		     name,
+		     version,
+		     refID,
+		     userInfo)
+boolean result;
+boolean search;
+boolean present;
+boolean deleteIt;
+boolean accessControl;
+boolean resourceControl;
+long prefSize;
+long maxMsgSize;
+char* auth;
+char* id;
+char* name;
+char* version;
+any* refID;
+void* userInfo;
+/* build an initResponse APDU with user specified information */
+{ 
+  InitResponseAPDU* init = (InitResponseAPDU*)s_malloc((size_t)sizeof(InitResponseAPDU));
+
+  init->PDUType = initResponseAPDU;
+  init->Result = result;
+  init->willSearch = search;
+  init->willPresent = present;
+  init->willDelete = deleteIt;
+  init->supportAccessControl = accessControl;
+  init->supportResourceControl = resourceControl;
+  init->PreferredMessageSize = prefSize;
+  init->MaximumRecordSize = maxMsgSize;
+  init->IDAuthentication = s_strdup(auth);
+  init->ImplementationID = s_strdup(id);
+  init->ImplementationName = s_strdup(name);
+  init->ImplementationVersion = s_strdup(version);
+  init->ReferenceID = duplicateAny(refID);
+  init->UserInformationField = userInfo; /* not copied! */
+  
+  return(init);
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+freeInitResponseAPDU(init)
+InitResponseAPDU* init;
+/* free an initAPDU */
+{
+  s_free(init->IDAuthentication);
+  s_free(init->ImplementationID);
+  s_free(init->ImplementationName);
+  s_free(init->ImplementationVersion);
+  freeAny(init->ReferenceID);
+  s_free(init);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+writeInitResponseAPDU(init,buffer,len)
+InitResponseAPDU* init;
+char* buffer;
+long* len;
+/* write the initResponse to a buffer, adding system information */
+{ 
+  char* buf = buffer + HEADER_LEN; /* leave room for the header-length-indicator */
+  long size;
+  bit_map* optionsBM = NULL;
+
+  RESERVE_SPACE_FOR_HEADER(len);
+  
+  buf = writePDUType(init->PDUType,buf,len);
+  buf = writeBoolean(init->Result,buf,len);
+  buf = writeProtocolVersion(buf,len);
+  
+  optionsBM = makeBitMap((unsigned long)5,init->willSearch,init->willPresent,
+                         init->willDelete,init->supportAccessControl,
+                         init->supportResourceControl);
+  buf = writeBitMap(optionsBM,DT_Options,buf,len);
+  freeBitMap(optionsBM);
+
+  buf = writeNum(init->PreferredMessageSize,DT_PreferredMessageSize,buf,len);
+  buf = writeNum(init->MaximumRecordSize,DT_MaximumRecordSize,buf,len);
+  buf = writeString(init->IDAuthentication,DT_IDAuthentication,buf,len);
+  buf = writeString(init->ImplementationID,DT_ImplementationID,buf,len);
+  buf = writeString(init->ImplementationName,DT_ImplementationName,buf,len);
+  buf = writeString(init->ImplementationVersion,DT_ImplementationVersion,buf,len);
+  buf = writeAny(init->ReferenceID,DT_ReferenceID,buf,len);
+  
+  /* go back and write the header-length-indicator */
+  RELEASE_HEADER_SPACE(len);
+  size = buf - buffer - HEADER_LEN; 
+  writeBinaryInteger(size,HEADER_LEN,buffer,len);
+
+  if (init->UserInformationField != NULL)
+    buf = writeInitResponseInfo(init,buf,len);   
+    
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+readInitResponseAPDU(init,buffer)
+InitResponseAPDU** init;
+char* buffer;
+{
+  char* buf = buffer;
+  boolean search,present,delete,accessControl,resourceControl;
+  long prefSize,maxMsgSize;
+  char *auth,*id,*name,*version;
+  long size; 
+  pdu_type pduType;
+  bit_map* versionBM = NULL;
+  bit_map* optionsBM = NULL;
+  boolean result;
+  any *refID = NULL;
+  void* userInfo = NULL;
+  
+  auth = id = name = version = NULL;
+  refID = NULL;
+  
+  /* read required part */
+  buf = readBinaryInteger(&size,HEADER_LEN,buf); 
+  buf = readPDUType(&pduType,buf);
+  buf = readBoolean(&result,buf);
+  buf = readBitMap(&versionBM,buf); 
+  buf = readBitMap(&optionsBM,buf);
+  buf = readNum(&prefSize,buf);
+  buf = readNum(&maxMsgSize,buf);
+  
+  /* decode optionsBM */
+  search = bitAtPos(0,optionsBM);
+  present = bitAtPos(1,optionsBM);
+  delete = bitAtPos(2,optionsBM);
+  accessControl = bitAtPos(3,optionsBM);
+  resourceControl = bitAtPos(4,optionsBM);
+  
+  /* read optional part */
+  while (buf < (buffer + size + HEADER_LEN)) 
+    { data_tag tag = peekTag(buf);
+      switch (tag)
+	{ case DT_IDAuthentication:
+	    buf = readString(&auth,buf);
+	    break;
+	  case DT_ImplementationID:
+	    buf = readString(&id,buf);
+	    break;
+	  case DT_ImplementationName:
+	    buf = readString(&name,buf);
+	    break;
+	  case DT_ImplementationVersion:
+	    buf = readString(&version,buf);
+	    break;
+	  case DT_ReferenceID:
+	    buf = readAny(&refID,buf);
+	    break;
+	  default:
+	    freeBitMap(versionBM);
+	    freeBitMap(optionsBM);
+	    s_free(auth);
+	    s_free(id);
+	    s_free(name);
+	    s_free(version);
+	    freeAny(refID);
+	    REPORT_READ_ERROR(buf);
+	    break;
+	  }
+    }
+
+  buf = readInitResponseInfo(&userInfo,buf);
+  if (buf == NULL)
+    { freeBitMap(versionBM);
+      freeBitMap(optionsBM);
+      s_free(auth);
+      s_free(id);
+      s_free(name);
+      s_free(version);
+      freeAny(refID);
+    }
+  RETURN_ON_NULL(buf);
+  
+  /* construct the basic init object */
+  *init = makeInitResponseAPDU(result,
+			       search,present,delete,accessControl,resourceControl,
+			       prefSize,maxMsgSize,auth,id,name,version,refID,userInfo);
+			 	 			        	
+  freeBitMap(versionBM);
+  freeBitMap(optionsBM);
+  s_free(auth);
+  s_free(id);
+  s_free(name);
+  s_free(version);
+  freeAny(refID);
+  
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+InitResponseAPDU* 
+replyToInitAPDU(init,result,userInfo)
+InitAPDU* init;
+boolean result;
+void* userInfo;
+/* respond to an init message in the default way - echoing back
+   the init info 
+ */
+{
+  InitResponseAPDU* initResp;
+  initResp = makeInitResponseAPDU(result,
+				  init->willSearch,init->willPresent,init->willDelete,
+				  init->supportAccessControl,init->supportResourceControl,
+				  init->PreferredMessageSize,init->MaximumRecordSize,
+				  init->IDAuthentication,defaultImplementationID(),defaultImplementationName(),
+				  defaultImplementationVersion(),
+				  init->ReferenceID,userInfo);
+  return(initResp);
+}
+
+/*----------------------------------------------------------------------*/
+
+SearchAPDU* 
+makeSearchAPDU(small,
+	       large,
+	       medium,
+	       replace,
+	       name,
+	       databases,
+	       type,
+	       elements,
+	       refID,
+	       queryInfo)
+long small;
+long large;
+long medium;
+boolean replace;
+char* name;
+char** databases;
+char* type;
+char** elements;
+any* refID;
+void* queryInfo;
+{
+  char* ptr = NULL;
+  long i;
+  SearchAPDU* query = (SearchAPDU*)s_malloc((size_t)sizeof(SearchAPDU));
+  query->PDUType = searchAPDU;
+  query->SmallSetUpperBound = small;
+  query->LargeSetLowerBound = large;
+  query->MediumSetPresentNumber = medium;
+  query->ReplaceIndicator = replace;
+  query->ResultSetName = s_strdup(name);
+  query->DatabaseNames = NULL; 
+  if (databases != NULL)
+    { for (i = 0, ptr = databases[i]; ptr != NULL; ptr = databases[++i])
+	{ if (query->DatabaseNames == NULL)
+	    query->DatabaseNames = (char**)s_malloc((size_t)(sizeof(char*) * 2));
+        else
+          query->DatabaseNames = (char**)s_realloc((char*)query->DatabaseNames,
+						   (size_t)(sizeof(char*) * (i + 2)));
+	    query->DatabaseNames[i] = s_strdup(ptr);
+	    query->DatabaseNames[i+1] = NULL;
+	  }
+      }
+  query->QueryType = s_strdup(type);
+  query->ElementSetNames = NULL; 
+  if (elements != NULL)
+    { for (i = 0, ptr = elements[i]; ptr != NULL; ptr = elements[++i])
+	{ if (query->ElementSetNames == NULL)
+	    query->ElementSetNames = (char**)s_malloc((size_t)(sizeof(char*) * 2));
+        else
+          query->ElementSetNames = (char**)s_realloc((char*)query->ElementSetNames,
+						     (size_t)(sizeof(char*) * (i + 2)));
+	    query->ElementSetNames[i] = s_strdup(ptr);
+	    query->ElementSetNames[i+1] = NULL;
+	  }
+      }
+  query->ReferenceID = duplicateAny(refID);
+  query->Query = queryInfo;	/* not copied! */
+  return(query);
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+freeSearchAPDU(query)
+SearchAPDU* query;
+{
+  s_free(query->ResultSetName);
+  s_free(query->QueryType);
+  doList((void**)query->DatabaseNames,fs_free); /* can't use the macro here ! */
+  s_free(query->DatabaseNames);
+  doList((void**)query->ElementSetNames,fs_free); /* can't use the macro here ! */
+  s_free(query->ElementSetNames);
+  freeAny(query->ReferenceID);
+  s_free(query);
+}
+
+/*----------------------------------------------------------------------*/
+
+#define DB_DELIMITER 	"\037" 	/* hex 1F occurs between each database name */
+#define ES_DELIMITER_1 	"\037" 	/* separates database name from element name */
+#define ES_DELIMITER_2 	"\036" 	/* hex 1E separates <db,es> groups from one another */
+
+char* 
+writeSearchAPDU(query,buffer,len)
+SearchAPDU* query;
+char* buffer;
+long* len;
+{ 
+  char* buf = buffer + HEADER_LEN; /* leave room for the header-length-indicator */
+  long size,i;
+  char* ptr = NULL;
+  char* scratch = NULL;
+
+  RESERVE_SPACE_FOR_HEADER(len);
+  
+  buf = writePDUType(query->PDUType,buf,len);
+  buf = writeBinaryInteger(query->SmallSetUpperBound,(size_t)3,buf,len);
+  buf = writeBinaryInteger(query->LargeSetLowerBound,(size_t)3,buf,len);
+  buf = writeBinaryInteger(query->MediumSetPresentNumber,(size_t)3,buf,len);
+  buf = writeBoolean(query->ReplaceIndicator,buf,len);
+  buf = writeString(query->ResultSetName,DT_ResultSetName,buf,len);
+  /* write database names */
+  if (query->DatabaseNames != NULL)
+    { for (i = 0,scratch = NULL, ptr = query->DatabaseNames[i]; ptr != NULL; ptr = query->DatabaseNames[++i])
+	{ if (scratch == NULL)
+	    scratch = s_strdup(ptr);
+        else
+	  { size_t newScratchSize = (size_t)(strlen(scratch) + strlen(ptr) + 2);
+	    scratch = (char*)s_realloc(scratch,newScratchSize);
+	    s_strncat(scratch,DB_DELIMITER,2,newScratchSize);
+	    s_strncat(scratch,ptr,strlen(ptr) + 1,newScratchSize);
+	  }
+	  }
+	buf = writeString(scratch,DT_DatabaseNames,buf,len);
+	s_free(scratch);
+      }
+  buf = writeString(query->QueryType,DT_QueryType,buf,len);
+  /* write element set names */
+  if (query->ElementSetNames != NULL)
+    { for (i = 0,scratch = NULL, ptr = query->ElementSetNames[i]; ptr != NULL; ptr = query->ElementSetNames[++i])
+	{ if (scratch == NULL)
+	    { if (query->ElementSetNames[i+1] == NULL) /* there is a single element set name */
+		{ scratch = (char*)s_malloc((size_t)strlen(ptr) + 2);
+		  strncpy(scratch,ES_DELIMITER_1,2);
+		  s_strncat(scratch,ptr,strlen(ptr) + 1,strlen(ptr) + 2);
+		}
+	    else		/* this is the first of a series of element set names */
+	      { size_t newScratchSize = (size_t)(strlen(ptr) + strlen(query->ElementSetNames[i + 1]) + 2);
+		scratch = s_strdup(ptr); /* the database name */
+		ptr = query->ElementSetNames[++i]; /* the element set name */
+		scratch = (char*)s_realloc(scratch,newScratchSize);
+		s_strncat(scratch,ES_DELIMITER_1,2,newScratchSize);
+		s_strncat(scratch,ptr,strlen(ptr) + 1,newScratchSize); 
+	      }
+	      }
+        else
+	  { char* esPtr = query->ElementSetNames[++i]; /* the element set name */
+	    size_t newScratchSize = (size_t)(strlen(scratch) + strlen(ptr) + strlen(esPtr) + 3);
+	    scratch = (char*)s_realloc(scratch,newScratchSize);
+	    s_strncat(scratch,ES_DELIMITER_2,2,newScratchSize);
+	    s_strncat(scratch,ptr,strlen(ptr) + 1,newScratchSize);
+	    s_strncat(scratch,ES_DELIMITER_1,2,newScratchSize);
+	    s_strncat(scratch,esPtr,strlen(esPtr) + 1,newScratchSize); 
+	  }
+	  }
+	buf = writeString(scratch,DT_ElementSetNames,buf,len);
+	s_free(scratch);
+      }						
+  buf = writeAny(query->ReferenceID,DT_ReferenceID,buf,len);
+    
+  /* go back and write the header-length-indicator */
+  RELEASE_HEADER_SPACE(len);
+  size = buf - buffer - HEADER_LEN; 
+  writeBinaryInteger(size,HEADER_LEN,buffer,len);
+
+  if (query->Query != NULL)
+    buf = writeSearchInfo(query,buf,len);    
+    
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+SearchResponseAPDU* 
+makeSearchResponseAPDU(result,count,recordsReturned,nextPos,resultStatus,
+		       presentStatus,refID,records)
+long result;
+long count;
+long recordsReturned;
+long nextPos;
+long resultStatus;
+long presentStatus;
+any* refID;
+void* records;
+{
+  SearchResponseAPDU* query = (SearchResponseAPDU*)s_malloc((size_t)sizeof(SearchResponseAPDU));
+  query->PDUType = searchResponseAPDU;
+  query->SearchStatus = result;
+  query->ResultCount = count;
+  query->NumberOfRecordsReturned = recordsReturned;
+  query->NextResultSetPosition = nextPos;
+  query->ResultSetStatus = resultStatus;
+  query->PresentStatus = presentStatus;
+  query->ReferenceID = duplicateAny(refID);
+  query->DatabaseDiagnosticRecords = records;
+  return(query);  
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+freeSearchResponseAPDU(queryResponse)
+SearchResponseAPDU* queryResponse;
+{
+  freeAny(queryResponse->ReferenceID);
+  s_free(queryResponse);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+writeSearchResponseAPDU(queryResponse,buffer,len)
+SearchResponseAPDU* queryResponse;
+char* buffer;
+long* len;
+{
+  char* buf = buffer + HEADER_LEN; /* leave room for the header-length-indicator */
+  long size;
+
+  RESERVE_SPACE_FOR_HEADER(len);
+  
+  buf = writePDUType(queryResponse->PDUType,buf,len);
+  buf = writeBinaryInteger(queryResponse->SearchStatus,(size_t)1,buf,len);
+  buf = writeBinaryInteger(queryResponse->ResultCount,(size_t)3,buf,len);
+  buf = writeBinaryInteger(queryResponse->NumberOfRecordsReturned,(size_t)3,buf,len);
+  buf = writeBinaryInteger(queryResponse->NextResultSetPosition,(size_t)3,buf,len);
+  buf = writeNum(queryResponse->ResultSetStatus,DT_ResultSetStatus,buf,len);
+  buf = writeNum(queryResponse->PresentStatus,DT_PresentStatus,buf,len);
+  buf = writeAny(queryResponse->ReferenceID,DT_ReferenceID,buf,len);
+    
+  /* go back and write the header-length-indicator */
+  RELEASE_HEADER_SPACE(len);
+  size = buf - buffer - HEADER_LEN; 
+  writeBinaryInteger(size,HEADER_LEN,buffer,len);
+
+  if (queryResponse->DatabaseDiagnosticRecords != NULL)
+    buf = writeSearchResponseInfo(queryResponse,buf,len);    
+    
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+readSearchResponseAPDU(queryResponse,buffer)
+SearchResponseAPDU** queryResponse;
+char* buffer;
+{
+  char* buf = buffer;
+  long size;
+  pdu_type pduType;
+  long result,count,recordsReturned,nextPos;
+  long resultStatus,presentStatus;
+  any *refID = NULL;
+  void* userInfo = NULL;
+  
+  /* read required part */
+  buf = readBinaryInteger(&size,HEADER_LEN,buf); 
+  buf = readPDUType(&pduType,buf);
+  buf = readBinaryInteger(&result,(size_t)1,buf);
+  buf = readBinaryInteger(&count,(size_t)3,buf);
+  buf = readBinaryInteger(&recordsReturned,(size_t)3,buf);
+  buf = readBinaryInteger(&nextPos,(size_t)3,buf);
+  
+  resultStatus = presentStatus = UNUSED;
+  refID = NULL;
+
+  /* read optional part */
+  while (buf < (buffer + size + HEADER_LEN)) 
+    { data_tag tag = peekTag(buf);
+      switch (tag)
+	{ case DT_ResultSetStatus:
+	    buf = readNum(&resultStatus,buf);
+	    break;
+	  case DT_PresentStatus:
+	    buf = readNum(&presentStatus,buf);
+	    break;
+	  case DT_ReferenceID:
+	    buf = readAny(&refID,buf);
+	    break;
+	  default:
+	    freeAny(refID);
+	    REPORT_READ_ERROR(buf);
+	    break;
+	  }
+    }
+  
+  buf = readSearchResponseInfo(&userInfo,buf);
+  if (buf == NULL)
+    freeAny(refID);
+  RETURN_ON_NULL(buf);
+  
+  /* construct the search object */
+  *queryResponse = makeSearchResponseAPDU(result,count,recordsReturned,nextPos,
+					  (long)resultStatus,(long)presentStatus,refID,userInfo);
+
+  freeAny(refID);
+  
+  return(buf);
+}
+
+
+/*
+**	Routines originally from ZUtil.c -- FM
+**
+**----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE:
+   No guarantees or restrictions.  See the readme file for the full standard
+   disclaimer.	
+  
+   3.26.90	Harry Morris, morris@think.com
+   3.30.90  Harry Morris - Changed any->bits to any->bytes
+   4.11.90  HWM - fixed include file names, changed 
+   				- writeCompressedIntegerWithPadding() to
+                  writeCompressedIntWithPadding()
+                - generalized conditional includes (see c-dialect.h)
+   3.7.91   Jonny Goldman.  Replaced "short" in makeBitMap with "int" line 632.
+*/
+
+char* readErrorPosition = NULL; /* pos where buf stoped making sense */
+
+/*----------------------------------------------------------------------*/
+/* A note on error handling 
+   read - these are low level routines, they do not check the type tags
+   which (sometimes) preceed the data (this is done by the higher
+   level functions which call these functions).  There is no 
+   attempt made to check that the reading does not exceed the read
+   buffer.  Such cases should be very rare and usually will be 
+   caught by the calling functions. (note - it is unlikely that 
+   a series of low level reads will go far off the edge without
+   triggering a type error.  However, it is possible for a single
+   bad read in an array function (eg. readAny) to attempt to read a 
+   large ammount, possibly causing a segmentation violation or out
+   of memory condition.
+ */
+/*----------------------------------------------------------------------*/
+
+diagnosticRecord* 
+makeDiag(surrogate,code,addInfo)
+boolean surrogate;
+char* code;
+char* addInfo;
+{
+  diagnosticRecord* diag = 
+    (diagnosticRecord*)s_malloc((size_t)sizeof(diagnosticRecord));
+  
+  diag->SURROGATE = surrogate;
+  memcpy(diag->DIAG,code,DIAGNOSTIC_CODE_SIZE);
+  diag->ADDINFO = s_strdup(addInfo); 
+
+  return(diag);
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+freeDiag(diag)
+diagnosticRecord* diag;
+{ 
+  if (diag != NULL)
+    { if (diag->ADDINFO != NULL)
+	s_free(diag->ADDINFO);
+	s_free(diag);
+      }
+}
+
+/*----------------------------------------------------------------------*/
+
+#define END_OF_RECORD	0x1D
+
+char* 
+writeDiag(diag,buffer,len)
+diagnosticRecord* diag;
+char* buffer;
+long* len;
+/* diagnostics (as per Appendix D) have a very weird format - this changes
+   in SR-1
+ */
+{
+  char* buf = buffer;
+  long  length;
+  
+  if (diag == NULL)		/* handle unspecified optional args */
+    return(buf);
+
+  buf = writeTag(DT_DatabaseDiagnosticRecords,buf,len);
+  CHECK_FOR_SPACE_LEFT(0,len);
+  
+  length = 3; 
+  if (diag->ADDINFO != NULL)
+    length += strlen(diag->ADDINFO);
+    
+  if (length >= 0xFFFF )	/* make sure the length is reasonable */
+    { length = 0xFFFF - 1;
+      diag->ADDINFO[0xFFFF - 3 - 1] = '\0';
+    }
+   
+  buf = writeBinaryInteger(length,2,buf,len);
+
+  CHECK_FOR_SPACE_LEFT(1,len);
+  buf[0] = diag->DIAG[0]; 
+  buf++;
+  
+  CHECK_FOR_SPACE_LEFT(1,len);
+  buf[0] = diag->DIAG[1];
+  buf++;
+  
+  if (length > 3)
+    { CHECK_FOR_SPACE_LEFT(3,len);
+      memcpy(buf,diag->ADDINFO,(size_t)length - 3);
+      buf += length - 3;
+    }
+   
+  CHECK_FOR_SPACE_LEFT(1,len);
+  buf[0] = diag->SURROGATE;
+  buf++;
+  
+  CHECK_FOR_SPACE_LEFT(1,len);
+  buf[0] = END_OF_RECORD;
+  buf++;
+
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+readDiag(diag,buffer)
+diagnosticRecord** diag;
+char* buffer;
+{
+  char* buf = buffer;
+  diagnosticRecord* d 
+    = (diagnosticRecord*)s_malloc((size_t)sizeof(diagnosticRecord));
+  data_tag tag;
+  long len;
+  
+  buf = readTag(&tag,buf);
+  
+  buf = readBinaryInteger(&len,2,buf);
+  
+  d->DIAG[0] = buf[0];
+  d->DIAG[1] = buf[1];
+  d->DIAG[2] = '\0';
+    
+  if (len > 3)
+    { d->ADDINFO = (char*)s_malloc((size_t)(len - 3 + 1));
+      memcpy(d->ADDINFO,(char*)(buf + 2),(size_t)(len - 3));
+      d->ADDINFO[len - 3] = '\0';
+    }
+  else
+    d->ADDINFO = NULL;
+    
+  d->SURROGATE = buf[len - 1];
+  
+  *diag = d;
+
+  return(buf + len + 1);
+}
+
+/*----------------------------------------------------------------------*/
+
+#define continueBit	0x80
+#define dataMask	0x7F
+#define dataBits	7
+
+char*
+writeCompressedInteger(num,buf,len)
+unsigned long num;
+char* buf;
+long* len;
+/* write a binary integer in the format described on p. 40.
+   this might be sped up 
+*/
+{
+  char byte;
+  long i;
+  unsigned long size;
+  
+  size = writtenCompressedIntSize(num);
+  CHECK_FOR_SPACE_LEFT(size,len);
+  
+  for (i = size - 1; i >= 0; i--)
+    { byte = num & dataMask;
+      if (i != (size-1))	/* turn on continue bit */
+	byte = (char)(byte | continueBit);
+      buf[i] = byte;
+      num = num >> dataBits;	/* don't and here */
+    }
+   
+  return(buf + size);
+} 
+
+/*----------------------------------------------------------------------*/
+
+char*
+readCompressedInteger(num,buf)
+unsigned long *num;
+char* buf;
+/* read a binary integer in the format described on p. 40.
+   this might be sped up 
+*/
+{
+  long i = 0;
+  unsigned char byte;
+
+  *num = 0;
+  
+  do 
+    { byte = buf[i++];
+      *num = *num << dataBits;
+      *num += (byte & dataMask);
+    }
+  while (byte & continueBit);
+
+  return(buf + i);
+} 
+
+/*----------------------------------------------------------------------*/
+
+#define pad	128 /* high bit is set */
+
+char*
+writeCompressedIntWithPadding(num,size,buffer,len)
+unsigned long num;
+unsigned long size;
+char* buffer;
+long* len;
+/* Like writeCompressedInteger, except writes padding (128) to make
+   sure that size bytes are used.  This can be read correctly by 
+   readCompressedInteger()
+*/
+{
+  char* buf = buffer;
+  unsigned long needed,padding;
+  long i;
+    
+  CHECK_FOR_SPACE_LEFT(size,len);
+  
+  needed = writtenCompressedIntSize(num);
+  padding = size - needed;
+  i = padding - 1;
+
+  for (i = padding - 1;i >= 0;i--)
+    { buf[i] = pad;
+    }
+  
+  buf = writeCompressedInteger(num,buf + padding,len);
+  
+  return(buf);
+} 
+
+/*----------------------------------------------------------------------*/
+
+unsigned long
+writtenCompressedIntSize(num)
+unsigned long num;
+/* return the number of bytes needed to represnet the value num in
+   compressed format.  curently limited to 4 bytes
+ */
+{
+  if (num < CompressedInt1Byte) 
+    return(1);
+  else if (num < CompressedInt2Byte) 
+    return(2);
+  else if (num < CompressedInt3Byte)
+    return(3);
+  else
+    return(4);    
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+writeTag(tag,buf,len)
+data_tag tag;
+char* buf;
+long* len;
+/* write out a data tag */
+{ 
+  return(writeCompressedInteger(tag,buf,len));
+} 
+
+/*----------------------------------------------------------------------*/
+
+char*
+readTag(tag,buf)
+data_tag* tag;
+char* buf;
+/* read a data tag */
+{ 
+  return(readCompressedInteger(tag,buf));
+} 
+
+/*----------------------------------------------------------------------*/
+
+unsigned long 
+writtenTagSize(tag)
+data_tag tag;
+{ 
+  return(writtenCompressedIntSize(tag));
+}
+
+/*----------------------------------------------------------------------*/
+
+data_tag
+peekTag(buf)
+char* buf;
+/* read a data tag without advancing the buffer */
+{
+  data_tag tag;
+  readTag(&tag,buf);
+  return(tag);
+} 
+
+/*----------------------------------------------------------------------*/
+
+any* 
+makeAny(size,data)
+unsigned long size;
+char* data;
+{
+  any* a = (any*)s_malloc((size_t)sizeof(any));
+  a->size = size;
+  a->bytes = data;
+  return(a);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+freeAny(a)
+any* a;
+/* destroy an any and its associated data. Assumes a->bytes was
+   allocated using the s_malloc family of libraries 
+ */
+{
+  if (a != NULL)
+    { if (a->bytes != NULL)
+	s_free(a->bytes);
+      s_free(a);
+    }
+}
+
+/*----------------------------------------------------------------------*/
+
+any* 
+duplicateAny(a)
+any* a;
+{
+  any* copy = NULL;
+
+  if (a == NULL)
+    return(NULL);
+
+  copy = (any*)s_malloc((size_t)sizeof(any));
+  copy->size = a->size;
+  if (a->bytes == NULL)
+    copy->bytes = NULL;
+  else
+    { copy->bytes = (char*)s_malloc((size_t)copy->size);
+      memcpy(copy->bytes,a->bytes,(size_t)copy->size);
+    }
+  return(copy);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+writeAny(a,tag,buffer,len)
+any* a;
+data_tag tag;
+char* buffer;
+long* len;
+/* write an any + tag and size info */
+{
+  char* buf = buffer;
+
+  if (a == NULL)		/* handle unspecified optional args */
+    return(buf);
+  
+  /* write the tags */
+  buf = writeTag(tag,buf,len);
+  buf = writeCompressedInteger(a->size,buf,len);
+
+  /* write the bytes */
+  CHECK_FOR_SPACE_LEFT(a->size,len);
+  memcpy(buf,a->bytes,(size_t)a->size);
+
+  return(buf+a->size);
+}
+
+/*----------------------------------------------------------------------*/
+
+
+char *readAny(anAny,buffer)
+any** anAny;
+char* buffer;
+/* read an any + tag and size info */
+{
+  char *buf;
+  any* a;
+  data_tag tag;
+
+
+
+a=(any*)s_malloc((size_t)sizeof(any));
+
+  buf=buffer;
+  
+  buf = readTag(&tag,buf);
+  
+  buf = readCompressedInteger(&a->size,buf);
+
+  /* now simply copy the bytes */
+  a->bytes = (char*)s_malloc((size_t)a->size);
+  memcpy(a->bytes,buf,(size_t)a->size);
+  *anAny = a;
+
+  return(buf+a->size);
+}
+
+/*----------------------------------------------------------------------*/
+
+unsigned long 
+writtenAnySize(tag,a)
+data_tag tag;
+any* a;
+{
+  unsigned long size;
+
+  if (a == NULL)
+    return(0);
+
+  size = writtenTagSize(tag);
+  size += writtenCompressedIntSize(a->size);
+  size += a->size;
+  return(size);
+}
+
+/*----------------------------------------------------------------------*/
+
+any*
+stringToAny(s)
+char* s;
+{
+  any* a = NULL;
+  
+  if (s == NULL)
+    return(NULL);
+    
+  a = (any*)s_malloc((size_t)sizeof(any));
+  a->size = strlen(s);
+  a->bytes = (char*)s_malloc((size_t)a->size);
+  memcpy(a->bytes,s,(size_t)a->size);
+  return(a);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+anyToString(a)
+any* a;
+{
+  char* s = NULL;
+  
+  if (a == NULL)
+    return(NULL);
+    
+  s = s_malloc((size_t)(a->size + 1));
+  memcpy(s,a->bytes,(size_t)a->size);
+  s[a->size] = '\0';
+  return(s);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+writeString(s,tag,buffer,len)
+char* s;
+data_tag tag;
+char* buffer;
+long* len;
+/* Write a C style string.  The terminating null is not written. 
+   This function is not part of the Z39.50 spec.  It is provided
+   for the convienience of those wishing to pass C strings in 
+   the place of an any.
+ */
+{
+  char* buf = buffer;
+  any* data = NULL;
+  if (s == NULL)
+    return(buffer);		/* handle unused optional item before making an any */
+  data = (any*)s_malloc((size_t)sizeof(any)); 
+  data->size = strlen(s);
+  data->bytes = s;		/* save a copy here by not using stringToAny() */
+  buf = writeAny(data,tag,buf,len);
+  s_free(data);			/* don't use freeAny() since it will free s too */
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+readString(s ,buffer)
+char** s ;
+char* buffer;
+/* Read an any and convert it into a C style string.
+   This function is not part of the Z39.50 spec.  It is provided
+   for the convienience of those wishing to pass C strings in 
+   the place of an any. 
+ */
+{
+  any* data = NULL;
+  char* buf = readAny(&data,buffer);
+  *s = anyToString(data);
+  freeAny(data);
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+unsigned long 
+writtenStringSize(tag,s)
+data_tag tag;
+char* s;
+{
+  unsigned long size;
+
+  if (s == NULL)
+   return(0);
+
+  size = writtenTagSize(tag);
+  size += writtenCompressedIntSize(size);
+  size += strlen(s);
+  return(size);
+}
+
+/*----------------------------------------------------------------------*/
+
+any* 
+longToAny(num)
+long num;
+/* a convienience function */
+{
+  char s[40];
+
+  sprintf(s,"%ld",num);
+  
+  return(stringToAny(s));
+}
+
+/*----------------------------------------------------------------------*/
+
+long
+anyToLong(a)
+any* a;
+/* a convienience function */
+{
+  long num;
+  char* str = NULL;
+  str = anyToString(a);
+  sscanf(str,"%ld",&num);	/* could check the result and return
+				   an error */
+  s_free(str);
+  return(num);
+}
+ 
+/*----------------------------------------------------------------------*/
+
+#define bitsPerByte	8
+
+bit_map*
+makeBitMap(unsigned long numBits, ...)
+/* construct and return a bitmap with numBits elements */
+{
+  va_list ap;
+  long i,j;
+  bit_map* bm = NULL;
+
+  va_start(ap,numBits);
+  
+  bm = (bit_map*)s_malloc((size_t)sizeof(bit_map));
+  bm->size = (unsigned long)ceil((double)numBits / bitsPerByte); 
+  bm->bytes = (char*)s_malloc((size_t)bm->size);
+  
+  /* fill up the bits */
+  for (i = 0; i < bm->size; i++) /* iterate over bytes */
+    { char byte = 0;
+      for (j = 0; j < bitsPerByte; j++) /* iterate over bits */
+	{ if ((i * bitsPerByte + j) < numBits)
+	    { boolean bit = false;
+	      bit = (boolean)va_arg(ap,boolean); 
+	      if (bit)
+	        { byte = byte | (1 << (bitsPerByte - j - 1));
+	        }
+	    }
+	  }
+      bm->bytes[i] = byte;
+    }
+
+  va_end(ap);
+  return(bm);
+}
+
+
+/*----------------------------------------------------------------------*/
+
+void
+freeBitMap(bm)
+bit_map* bm;
+/* destroy a bit map created by makeBitMap() */
+{
+  s_free(bm->bytes);
+  s_free(bm);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* use this routine to interpret a bit map.  pos specifies the bit 
+   number.  bit 0 is the Leftmost bit of the first byte.  
+   Could do bounds checking.
+ */
+
+boolean
+bitAtPos(pos,bm)
+long pos;
+bit_map* bm;
+{
+  if (pos > bm->size*bitsPerByte)
+    return false;
+  else
+    return((bm->bytes[(pos / bitsPerByte)] & 
+	    (0x80>>(pos % bitsPerByte))) ?
+	   true : false);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+writeBitMap(bm,tag,buffer,len)
+bit_map* bm;
+data_tag tag;
+char* buffer;
+long* len;
+/* write a bitmap + type and size info */
+{ 
+  return(writeAny((any*)bm,tag,buffer,len));
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readBitMap(bm,buffer)
+bit_map** bm;
+char* buffer;
+/* read a bitmap + type and size info */
+{
+	char *c;
+
+
+
+c=readAny((any**)bm,buffer);
+
+  return(c);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+writeByte(byte,buf,len)
+unsigned long byte;
+char* buf;
+long* len;
+{
+  CHECK_FOR_SPACE_LEFT(1,len);
+  buf[0] = byte & 0xFF; /* we really only want the first byte */
+  return(buf + 1);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+readByte(byte,buf)
+unsigned char* byte;
+char* buf;
+{
+  *byte = buf[0];
+  return(buf + 1);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+writeBoolean(flag,buf,len)
+boolean flag;
+char* buf;
+long* len;
+{
+  return(writeByte(flag,buf,len));
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+readBoolean(flag,buffer)
+boolean* flag;
+char* buffer;
+{
+  unsigned char byte;
+  char* buf = readByte(&byte,buffer);
+  *flag = (byte == true) ? true : false;
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+writePDUType(pduType,buf,len)
+pdu_type pduType;
+char* buf;
+long* len;
+/* PDUType is a single byte */
+{
+  return(writeBinaryInteger((long)pduType,(unsigned long)1,buf,len));
+} 
+
+/*----------------------------------------------------------------------*/
+
+char*
+readPDUType(pduType,buf)
+pdu_type* pduType;
+char* buf;
+/* PDUType is a single byte */
+{
+  return(readBinaryInteger((long*)pduType,(unsigned long)1,buf));
+} 
+
+/*----------------------------------------------------------------------*/
+
+pdu_type
+peekPDUType(buf)
+char* buf;
+/* read the next pdu without advancing the buffer, Note that this 
+   function is to be used on a buffer that is known to contain an
+   APDU.  The pdu_type is written HEADER_LEN bytes into the buffer
+ */
+{
+  pdu_type pdu;
+  readPDUType(&pdu,buf + HEADER_LEN);
+  return(pdu);
+}
+
+/*----------------------------------------------------------------------*/
+
+#define BINARY_INTEGER_BYTES	sizeof(long) /* the number of bytes used by
+						a "binary integer" */
+char*
+writeBinaryInteger(num,size,buf,len)
+long num;
+unsigned long size;
+char* buf;
+long* len;
+/* write out first size bytes of num - no type info
+  XXX should this take unsigned longs instead ???  */
+{
+  long i;
+  char byte;
+
+  if (size < 1 || size > BINARY_INTEGER_BYTES)
+    return(NULL);		/* error */
+
+  CHECK_FOR_SPACE_LEFT(size,len);
+
+  for (i = size - 1; i >= 0; i--)
+    { byte = (char)(num & 255);
+      buf[i] = byte;
+      num = num >> bitsPerByte; /* don't and here */
+    }
+
+  return(buf + size);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readBinaryInteger(num,size,buf)
+long* num;
+unsigned long size;
+char* buf;
+/* read in first size bytes of num - no type info
+  XXX this should take unsigned longs instead !!! */
+{
+  long i;
+  unsigned char byte;
+
+  if (size < 1 || size > BINARY_INTEGER_BYTES)
+    return(buf);		/* error */
+  *num = 0;
+
+  for (i = 0; i < size; i++)
+    { byte = buf[i];
+      *num = *num << bitsPerByte;
+      *num += byte;
+    }
+
+  return(buf + size);
+}
+
+/*----------------------------------------------------------------------*/
+
+unsigned long 
+writtenCompressedBinIntSize(num)
+long num;
+/* return the number of bytes needed to represent the value num.
+   currently limited to max of 4 bytes 
+   Only compresses for positive nums - negatives get whole 4 bytes
+ */
+{
+  if (num < 0L)
+    return(4);
+  else if (num < 256L)		/* 2**8 */
+    return(1);
+  else if (num < 65536L)	/* 2**16 */
+    return(2);
+  else if (num < 16777216L)	/* 2**24 */
+    return(3);
+  else
+    return(4);
+}
+ 
+/*----------------------------------------------------------------------*/
+
+char*
+writeNum(num,tag,buffer,len)
+long num;
+data_tag tag;
+char* buffer;
+long* len;
+/* write a binary integer + size and tag info */
+{
+  char* buf = buffer;
+  long size = writtenCompressedBinIntSize(num);
+  
+  if (num == UNUSED)
+    return(buffer);
+    
+  buf = writeTag(tag,buf,len);
+  buf = writeCompressedInteger(size,buf,len); 
+  buf = writeBinaryInteger(num,(unsigned long)size,buf,len); 
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+readNum(num,buffer)
+long* num;
+char* buffer;
+/* read a binary integer + size and tag info */
+{
+  char* buf = buffer;
+  data_tag tag;
+  unsigned long size;
+  unsigned long val;
+  
+  buf = readTag(&tag,buf);
+  buf = readCompressedInteger(&val,buf);
+  size = (unsigned long)val;
+  buf = readBinaryInteger(num,size,buf);
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+unsigned long 
+writtenNumSize(tag,num)
+data_tag tag;
+long num;
+{
+  long dataSize = writtenCompressedBinIntSize(num);
+  long size;
+  
+  size = writtenTagSize(tag); /* space for the tag */
+  size += writtenCompressedIntSize(dataSize); /* space for the size */
+  size += dataSize; /* space for the data */
+  
+  return(size);
+}
+
+/*----------------------------------------------------------------------*/
+
+typedef void (voidfunc)();
+
+void
+doList(list,func)
+void** list;
+voidfunc *func;
+/* call func on each element of the NULL terminated list of pointers */
+{
+  register long i;
+  register void* ptr = NULL;
+  if (list == NULL)
+    return;
+  for (i = 0,ptr = list[i]; ptr != NULL; ptr = list[++i])
+    (*func)(ptr);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+writeProtocolVersion(buf,len)
+char* buf;
+long* len;
+/* write a bitmap describing the protocols available */
+{
+  static bit_map* version = NULL;
+
+  if (version == NULL)
+   { version = makeBitMap((unsigned long)1,true); /* version 1! */
+   }
+    
+  return(writeBitMap(version,DT_ProtocolVersion,buf,len));
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+defaultImplementationID()
+{
+  static char	ImplementationID[] = "TMC";
+  return(ImplementationID);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+defaultImplementationName()
+{
+  static char ImplementationName[] = "Thinking Machines Corporation Z39.50";
+  return(ImplementationName);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+defaultImplementationVersion()
+{
+  static char	ImplementationVersion[] = "2.0A";
+  return(ImplementationVersion);
+}
+
+/*----------------------------------------------------------------------*/
+
+
+/*
+**	Routines originally from ZType1.c -- FM
+**
+**----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE:
+   No guarantees or restrictions.  See the readme file for the full standard
+   disclaimer.	
+  
+   3.26.90	Harry Morris, morris@think.com
+   4.11.90  HWM - generalized conditional includes (see c-dialect.h)
+*/
+/*----------------------------------------------------------------------*/
+
+query_term*
+makeAttributeTerm(use,
+		  relation,
+		  position,
+		  structure,
+		  truncation,
+		  completeness,
+		  term)
+char* use;
+char* relation;
+char* position;
+char* structure;
+char* truncation;
+char* completeness;
+any* term;
+{
+  query_term* qt = (query_term*)s_malloc((size_t)sizeof(query_term));
+
+  qt->TermType = TT_Attribute;
+
+  /* copy in the attributes */
+  strncpy(qt->Use,use,ATTRIBUTE_SIZE);
+  strncpy(qt->Relation,relation,ATTRIBUTE_SIZE);
+  strncpy(qt->Position,position,ATTRIBUTE_SIZE);
+  strncpy(qt->Structure,structure,ATTRIBUTE_SIZE);
+  strncpy(qt->Truncation,truncation,ATTRIBUTE_SIZE);
+  strncpy(qt->Completeness,completeness,ATTRIBUTE_SIZE);
+
+  qt->Term = duplicateAny(term);
+
+  qt->ResultSetID = NULL;
+
+  return(qt);
+}
+
+/*----------------------------------------------------------------------*/
+
+query_term*
+makeResultSetTerm(resultSet)
+any* resultSet;
+{ 
+  query_term* qt = (query_term*)s_malloc((size_t)sizeof(query_term));
+
+  qt->TermType = TT_ResultSetID;
+
+  qt->ResultSetID = duplicateAny(resultSet);
+
+  qt->Term = NULL;
+  
+  return(qt);
+}
+
+/*----------------------------------------------------------------------*/
+
+query_term* 
+makeOperatorTerm(operatorCode)
+char* operatorCode;
+{
+  query_term* qt = (query_term*)s_malloc((size_t)sizeof(query_term));
+
+  qt->TermType = TT_Operator;
+
+  strncpy(qt->Operator,operatorCode,OPERATOR_SIZE);
+
+  qt->Term = NULL;
+  qt->ResultSetID = NULL;
+
+  return(qt);
+}
+
+/*----------------------------------------------------------------------*/
+
+void 
+freeTerm(qt)
+query_term* qt;
+{
+  switch (qt->TermType)
+    { case TT_Attribute:
+	freeAny(qt->Term);
+	break;
+      case TT_ResultSetID:
+	freeAny(qt->ResultSetID);
+	break;
+      case TT_Operator:
+	/* do nothing */
+	break;
+      default:
+	panic("Implementation error: Unknown term type %ld",
+	      qt->TermType);
+	break;
+      }
+  s_free(qt);
+}
+
+/*----------------------------------------------------------------------*/
+
+#define ATTRIBUTE_LIST_SIZE	ATTRIBUTE_SIZE * 6
+#define AT_DELIMITER	" "
+
+char* 
+writeQueryTerm(qt,buffer,len)
+query_term* qt;
+char* buffer;
+long* len;
+{
+  char* buf = buffer;
+  char attributes[ATTRIBUTE_LIST_SIZE];
+
+  switch (qt->TermType)
+    { case TT_Attribute:
+	strncpy(attributes,qt->Use,ATTRIBUTE_LIST_SIZE); 
+	s_strncat(attributes,AT_DELIMITER,sizeof(AT_DELIMITER) + 1,ATTRIBUTE_LIST_SIZE);
+	s_strncat(attributes,qt->Relation,ATTRIBUTE_SIZE,ATTRIBUTE_LIST_SIZE); 
+	s_strncat(attributes,AT_DELIMITER,sizeof(AT_DELIMITER) + 1,ATTRIBUTE_LIST_SIZE);
+	s_strncat(attributes,qt->Position,ATTRIBUTE_SIZE,ATTRIBUTE_LIST_SIZE); 
+	s_strncat(attributes,AT_DELIMITER,sizeof(AT_DELIMITER) + 1,ATTRIBUTE_LIST_SIZE);
+	s_strncat(attributes,qt->Structure,ATTRIBUTE_SIZE,ATTRIBUTE_LIST_SIZE); 
+	s_strncat(attributes,AT_DELIMITER,sizeof(AT_DELIMITER) + 1,ATTRIBUTE_LIST_SIZE);
+	s_strncat(attributes,qt->Truncation,ATTRIBUTE_SIZE,ATTRIBUTE_LIST_SIZE); 
+	s_strncat(attributes,AT_DELIMITER,sizeof(AT_DELIMITER) + 1,ATTRIBUTE_LIST_SIZE);
+	s_strncat(attributes,qt->Completeness,ATTRIBUTE_SIZE,ATTRIBUTE_LIST_SIZE);
+	buf = writeString(attributes,DT_AttributeList,buf,len);
+	buf = writeAny(qt->Term,DT_Term,buf,len);
+	break;
+      case TT_ResultSetID:
+	buf = writeAny(qt->ResultSetID,DT_ResultSetID,buf,len);
+	break;
+      case TT_Operator:
+	buf = writeString(qt->Operator,DT_Operator,buf,len);
+	break;
+      default:
+	panic("Implementation error: Unknown term type %ld",
+	      qt->TermType);
+	break;
+      }
+
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+char* 
+readQueryTerm(qt,buffer)
+query_term** qt;
+char* buffer;
+{
+  char* buf = buffer;
+  char  *attributeList = NULL;
+  char* operator = NULL;
+  any* 	term;
+  char* use = NULL;
+  char* relation = NULL;
+  char* position = NULL;
+  char* structure = NULL;
+  char* truncation = NULL;
+  char* completeness;
+  any*	resultSetID = NULL;
+  data_tag tag;
+
+  
+  tag = peekTag(buffer);
+
+  switch(tag)
+    { case DT_AttributeList:
+	buf = readString(&attributeList,buf);
+	buf = readAny(&term,buf);
+	use = strtok(attributeList,AT_DELIMITER);
+	relation = strtok(NULL,AT_DELIMITER);
+	position = strtok(NULL,AT_DELIMITER);
+	structure = strtok(NULL,AT_DELIMITER);
+	truncation = strtok(NULL,AT_DELIMITER);
+	completeness = strtok(NULL,AT_DELIMITER);
+	*qt = makeAttributeTerm(use,relation,position,structure,
+				truncation,completeness,term);
+	s_free(attributeList);
+	freeAny(term);
+	break;
+      case DT_ResultSetID:
+	buf = readAny(&resultSetID,buf);
+	*qt = makeResultSetTerm(resultSetID);	
+	freeAny(resultSetID);
+	break;
+      case DT_Operator:
+	buf = readString(&operator,buf);
+	*qt = makeOperatorTerm(operator);
+	s_free(operator);
+	break;
+      default:
+	REPORT_READ_ERROR(buf);
+	break;
+      }
+  
+  return(buf);
+}
+
+/*----------------------------------------------------------------------*/
+
+static unsigned long getQueryTermSize _AP((query_term* qt));
+
+static unsigned long
+getQueryTermSize(qt)
+query_term* qt;
+/* figure out how many bytes it will take to write this query */
+{
+  unsigned long size;
+  static char attributes[] = "11 22 33 44 55 66"; /* we just need this to 
+						     calculate its written
+						     size */
+
+  switch (qt->TermType)
+    { case TT_Attribute:
+	size = writtenStringSize(DT_AttributeList,attributes);
+	size += writtenAnySize(DT_Term,qt->Term);
+	break;
+      case TT_ResultSetID:
+	size = writtenAnySize(DT_ResultSetID,qt->ResultSetID);
+	break;
+      case TT_Operator:
+	size = writtenStringSize(DT_Operator,qt->Operator);
+	break;
+      default:
+	panic("Implementation error: Unknown term type %ld",
+	      qt->TermType);
+	break;
+      }
+
+  return(size);
+}
+
+/*----------------------------------------------------------------------*/
+
+/* A query is simply a null terminated list of query terms. For 
+   transmission, a query is written into an any which is sent as
+   the user information field. */
+
+any*
+writeQuery(terms)
+query_term** terms;
+{
+  any* info = NULL;
+  char* writePos = NULL;
+  char* data = NULL;
+  unsigned long size = 0;
+  long remaining = 0;
+  long i;
+  query_term* qt = NULL;
+
+  if (terms == NULL)
+    return(NULL);
+
+  /* calculate the size of write buffer */
+  for (i = 0,qt = terms[i]; qt != NULL; qt = terms[++i])
+    size += getQueryTermSize(qt);
+
+  data = (char*)s_malloc((size_t)size);
+
+  /* write the terms */
+  writePos = data;
+  remaining = size;
+  for (i = 0,qt = terms[i]; qt != NULL; qt = terms[++i])
+    writePos = writeQueryTerm(qt,writePos,&remaining);
+
+  info = makeAny(size,data);
+
+  return(info);
+}
+
+/*----------------------------------------------------------------------*/
+
+query_term**
+readQuery(info)
+any *info;
+{
+  char* readPos = info->bytes;
+  query_term** terms = NULL;
+  query_term* qt = NULL;
+  long numTerms = 0L;
+char tmp[100];
+
+sprintf(tmp,"readquery: bytes: %ld",info->size);
+log_write(tmp);
+
+  while (readPos < info->bytes + info->size)
+    { readPos = readQueryTerm(&qt,readPos);
+
+      if (terms == NULL)
+	{ terms = (query_term**)s_malloc((size_t)(sizeof(query_term*)*2));
+	}
+      else
+	{ terms = 
+	    (query_term**)s_realloc((char*)terms,
+				    (size_t)(sizeof(query_term*)*(numTerms+2)));
+	  }
+if(qt==NULL)
+	log_write("qt = null");
+      terms[numTerms++] = qt;
+      terms[numTerms] = NULL;
+    }
+
+  return(terms);
+}
+
+/*----------------------------------------------------------------------*/
+
+
+/*
+**	Routines originally from panic.c -- FM
+**
+**----------------------------------------------------------------------*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE:
+   No guarantees or restrictions.  See the readme file for the full standard
+   disclaimer.
+
+   Morris@think.com
+*/
+
+/* panic is an error system interface.  On the Mac, it will pop
+ * up a little window to explain the problem.
+ * On a unix box, it will print out the error and call perror()
+ */
+ 
+/*----------------------------------------------------------------------*/
+
+static void exitAction _AP((long error));
+
+static void
+exitAction(error)
+long error;
+{
+  long i;
+  for (i = 0; i < 100000; i++)
+    ;
+  exit(0);
+}
+
+/*----------------------------------------------------------------------*/
+
+#define PANIC_HEADER "Fatal Error:  "
+
+void
+panic(char *format, ...)
+{
+  va_list ap;			/* the variable arguments */
+
+  fprintf(stderr,PANIC_HEADER);
+  va_start(ap, format);		/* init ap */
+  vfprintf(stderr,format,ap);	/* print the contents */
+  va_end(ap);			/* free ap */
+  fflush(stderr);
+  
+  exitAction(0);
+}
+
+/*----------------------------------------------------------------------*/
+
+
+/*
+**	Routines originally from cutil.c -- FM
+**
+**----------------------------------------------------------------------*/
+/* Wide AREA INFORMATION SERVER SOFTWARE	
+   No guarantees or restrictions.  See the readme file for the full standard
+   disclaimer.  
+  
+   3.26.90	Harry Morris, morris@think.com
+   4.11.90  HWM - generalized conditional includes (see c-dialect.h)
+*/
+
+#include <varargs.h>
+
+
+/*----------------------------------------------------------------------*/
+
+void
+fs_checkPtr(ptr)
+void* ptr;
+/* If the ptr is NULL, give an error */
+{ 
+  if (ptr == NULL)
+    panic("checkPtr found a NULL pointer");
+}
+
+/*----------------------------------------------------------------------*/
+
+void*
+fs_malloc(size)
+size_t size;
+/* does safety checks and optional accounting */
+{ 
+  register void* ptr = NULL;
+
+  ptr = (void*)calloc((size_t)size,(size_t)1);
+  s_checkPtr(ptr);
+  
+  return(ptr);
+}
+
+/*----------------------------------------------------------------------*/
+
+void*
+fs_realloc(ptr,size)
+void* ptr;
+size_t size;
+/* does safety checks and optional accounting 
+   note - we don't know how big ptr's memory is, so we can't ensure
+   that any new memory allocated is NULLed!
+ */
+{ 
+  register void* nptr = NULL;
+  
+  if (ptr == NULL)		/* this is really a malloc */
+    return(s_malloc(size));
+    
+  nptr = (void*)realloc(ptr,size);
+  s_checkPtr(ptr);
+   
+  return(nptr);
+}
+
+/*----------------------------------------------------------------------*/
+
+void
+fs_free(ptr)
+void* ptr;
+/* does safety checks and optional accounting */
+{
+  if (ptr != NULL)		/* some non-ansi compilers/os's cant handle freeing null */
+    {				/* if we knew the size of this block of memory, we could clear it - oh well */
+      free(ptr);
+      ptr = NULL;
+    }
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+s_strdup(s)
+char* s;
+
+/* return a copy of s.  This is identical to the standard library routine
+   strdup(), except that it is safe.  If s == NULL or malloc fails, 
+   appropriate action is taken.
+ */
+{
+  unsigned long len;
+  char* copy = NULL;
+  
+  if (s == NULL)		/* saftey check to postpone stupid errors */
+    return(NULL);
+    
+  len = strlen(s);		/* length of string - terminator */
+  copy = (char*)s_malloc((size_t)(sizeof(char)*(len + 1)));
+  strncpy(copy,s,len + 1);
+  return(copy);
+}
+
+/*----------------------------------------------------------------------*/
+
+char*
+fs_strncat(dst,src,maxToAdd,maxTotal)
+char* dst;
+   char* src;
+   size_t maxToAdd;
+   size_t maxTotal;
+
+/* like strncat, except the fourth argument limits the maximum total 
+   length of the resulting string
+ */
+{
+  size_t dstSize = strlen(dst);
+  size_t srcSize = strlen(src);
+  
+  if (dstSize + srcSize < maxTotal) /* use regular old strncat */
+    return(strncat(dst,src,maxToAdd));
+  else
+    { size_t truncateTo = maxTotal - dstSize - 1;
+      char   saveChar = src[truncateTo];
+      char*  result = NULL;
+      src[truncateTo] = '\0';
+      result = strncat(dst,src,maxToAdd);
+      src[truncateTo] = saveChar;
+      return(result);
+    }
+}
+
+/*----------------------------------------------------------------------*/
+
+char char_downcase(long_ch)
+unsigned long long_ch;
+{
+  unsigned char ch = long_ch & 0xFF; /* just want one byte */
+  /* when ansi is the way of the world, this can be tolower */
+  return (((ch >= 'A') && (ch <= 'Z')) ? (ch + 'a' -'A') : ch);
+}
+
+char *string_downcase(word)
+char *word;
+{
+  long i = 0;
+  while(word[i] != '\0'){
+    word[i] = char_downcase((unsigned long)word[i]);
+    i++;
+  }
+  return(word);
+}
+
+/*----------------------------------------------------------------------*/
+
diff --git a/WWW/Library/Implementation/HTVMS_WaisUI.h b/WWW/Library/Implementation/HTVMS_WaisUI.h
new file mode 100644
index 00000000..6f492b87
--- /dev/null
+++ b/WWW/Library/Implementation/HTVMS_WaisUI.h
@@ -0,0 +1,674 @@
+/*							HTVMS_WAISUI.h
+**
+**	Adaptation for Lynx by F.Macrides (macrides@sci.wfeb.edu)
+**
+**	31-May-1994 FM	Initial version.
+*/
+
+#ifndef HTVMSWAIS_H
+#define HTVMSWAIS_H
+
+#ifndef __STDLIB_LOADED
+#include <stdlib.h>
+#endif /* __STDLIB_LOADED */
+
+#define _AP(args) ()
+
+void	log_write _AP((char *));
+
+/*
+**	Routines originally from Panic.h -- FM
+**
+**----------------------------------------------------------------------*/
+
+void	panic (char* format,...); 
+
+/*----------------------------------------------------------------------*/
+
+
+/*
+**	Routines originally from CUtil.h -- FM
+**
+**----------------------------------------------------------------------*/
+
+/* types and constants */
+
+#ifndef boolean
+#define boolean unsigned long
+#endif /* boolean */
+
+#ifndef true
+#define true 	(boolean)1L
+#endif /* true */
+
+#ifndef false
+#define false 	(boolean)0L   /* used to be (!true), but broke 
+				 some compilers */
+#endif /* false */
+
+#ifndef TRUE
+#define TRUE	true
+#endif /* TRUE */
+
+#ifndef FALSE
+#define FALSE	false
+#endif /* FALSE */
+
+/*----------------------------------------------------------------------*/
+/* functions */
+
+/* enhanced memory handling functions - don't call them directly, use the
+   macros below */
+void	fs_checkPtr _AP((void* ptr));
+void*	fs_malloc _AP((size_t size));
+void*	fs_realloc _AP((void* ptr,size_t size));
+void	fs_free _AP((void* ptr));
+char* 	fs_strncat _AP((char* dst,char* src,size_t maxToAdd,size_t maxTotal));
+
+/* macros for memory functions.  call these in your program.  */
+#define s_checkPtr(ptr) 	fs_checkPtr(ptr)
+#define s_malloc(size)	      	fs_malloc(size)
+#define s_realloc(ptr,size)	fs_realloc((ptr),(size))
+#define s_free(ptr)		{ fs_free((char*)ptr); ptr = NULL; }
+#define s_strncat(dst,src,maxToAdd,maxTotal)	fs_strncat((dst),(src),(maxToAdd),(maxTotal))
+
+char* 	s_strdup _AP((char* s));
+
+#define IS_DELIMITER	1
+#define	NOT_DELIMITER	!IS_DELIMITER
+
+char char_downcase _AP((unsigned long ch));
+char *string_downcase _AP((char* word));
+
+/*----------------------------------------------------------------------*/
+
+
+
+/*
+**	Routines originally from ZUtil.c -- FM
+**
+**----------------------------------------------------------------------*/
+
+/* Data types / constants */
+
+/* bytes to leave for the header size info */
+#define HEADER_LEN	(size_t)2 
+
+typedef long pdu_type;
+
+#define	initAPDU			(pdu_type)20
+#define	initResponseAPDU		(pdu_type)21
+#define	searchAPDU			(pdu_type)22
+#define	searchResponseAPDU		(pdu_type)23
+#define	presentAPDU			(pdu_type)24
+#define	presentResponseAPDU		(pdu_type)25
+#define	deteteAPDU			(pdu_type)26
+#define	deleteResponseAPDU		(pdu_type)27
+#define	accessControlAPDU		(pdu_type)28
+#define	accessControlResponseAPDU	(pdu_type)29
+#define	resourceControlAPDU		(pdu_type)30
+#define	resourceControlResponseAPDU	(pdu_type)31
+
+typedef struct any {	/* an any is a non-ascii string of characters */
+	unsigned long	size; 
+	char*			bytes;
+	} any;
+	
+typedef any	bit_map; 	/* a bit_map is a group of packed bits */
+
+typedef unsigned long data_tag;
+
+#define DT_PDUType			(data_tag)1 	
+#define	DT_ReferenceID			(data_tag)2
+#define	DT_ProtocolVersion		(data_tag)3
+#define	DT_Options			(data_tag)4
+#define	DT_PreferredMessageSize		(data_tag)5
+#define	DT_MaximumRecordSize		(data_tag)6
+#define	DT_IDAuthentication		(data_tag)7
+#define	DT_ImplementationID		(data_tag)8
+#define	DT_ImplementationName		(data_tag)9
+#define	DT_ImplementationVersion	(data_tag)10
+#define	DT_UserInformationField		(data_tag)11
+#define	DT_Result			(data_tag)12
+#define	DT_SmallSetUpperBound		(data_tag)13
+#define	DT_LargeSetLowerBound		(data_tag)14
+#define	DT_MediumSetPresentNumber	(data_tag)15
+#define	DT_ReplaceIndicator		(data_tag)16
+#define	DT_ResultSetName		(data_tag)17
+#define	DT_DatabaseNames		(data_tag)18
+#define	DT_ElementSetNames 		(data_tag)19
+#define	DT_QueryType			(data_tag)20
+#define	DT_Query			(data_tag)21
+#define	DT_SearchStatus			(data_tag)22
+#define	DT_ResultCount			(data_tag)23
+#define	DT_NumberOfRecordsReturned	(data_tag)24
+#define	DT_NextResultSetPosition	(data_tag)25
+#define	DT_ResultSetStatus		(data_tag)26
+#define	DT_PresentStatus		(data_tag)27
+#define	DT_DatabaseDiagnosticRecords	(data_tag)28
+#define	DT_NumberOfRecordsRequested	(data_tag)29
+#define	DT_ResultSetStartPosition	(data_tag)30
+#define	DT_ResultSetID			(data_tag)31
+#define	DT_DeleteOperation		(data_tag)32
+#define	DT_DeleteStatus			(data_tag)33
+#define	DT_NumberNotDeleted		(data_tag)34
+#define	DT_BulkStatuses			(data_tag)35
+#define	DT_DeleteMSG			(data_tag)36
+#define	DT_SecurityChallenge		(data_tag)37
+#define	DT_SecurityChallengeResponse	(data_tag)38
+#define	DT_SuspendedFlag		(data_tag)39
+#define	DT_ResourceReport		(data_tag)40
+#define	DT_PartialResultsAvailable	(data_tag)41
+#define	DT_ContinueFlag			(data_tag)42
+#define	DT_ResultSetWanted		(data_tag)43
+
+#define UNUSED	-1
+
+/* number of bytes required to represent the following sizes in compressed 
+   integer format
+ */
+#define CompressedInt1Byte	128 		/* 2 ^ 7 */
+#define CompressedInt2Byte	16384 		/* 2 ^ 14 */
+#define CompressedInt3Byte	2097152 	/* 2 ^ 21 */
+/* others may follow ... */
+
+/* types of query */
+#define QT_0	"0"	/* query whose non-standard format has been agreed upon
+			   client and server */
+/* values for InitAPDU option element */
+#define	WILL_USE		TRUE
+#define WILL_NOT_USE		FALSE
+#define WILL_SUPPORT		TRUE
+#define WILL_NOT_SUPPORT	FALSE
+
+/* values for InitResponseAPDU result element */
+#define ACCEPT	TRUE
+#define REJECT	FALSE
+
+/* values for SearchAPDU replace indicator element */
+#define ON	TRUE
+#define OFF	FALSE
+
+/* values for SearchResponseAPDU search status element */
+#define	SUCCESS	0 /* intuitive huh? */
+#define FAILURE	1
+
+/* values for SearchResponseAPDU result set status element */
+#define	SUBSET	1
+#define INTERIM	2
+#define NONE	3
+
+/* values for SearchResponseAPDU present status element */
+/* SUCCESS already defined */
+#define PARTIAL_1	1
+#define PARTIAL_2	2
+#define PARTIAL_3	3
+#define PARTIAL_4	4
+#define PS_NONE		5 /* can't use NONE since it was used by result 
+			     set status */
+
+#define DIAGNOSTIC_CODE_SIZE	(size_t)3
+
+typedef struct diagnosticRecord 
+ { boolean	SURROGATE;
+   char		DIAG[DIAGNOSTIC_CODE_SIZE];
+   char* 	ADDINFO;
+ } diagnosticRecord;
+
+#define D_PermanentSystemError	       "S1"
+#define D_TemporarySystemError	       "S2"
+#define D_UnsupportedSearch	       "S3"
+#define D_TermsOnlyStopWords	       "S5"
+#define D_TooManyArgumentWords	       "S6"
+#define D_TooManyBooleanOperators      "S7"
+#define D_TooManyTruncatedWords	       "S8"
+#define D_TooMany IncompleteSubfields  "S9"
+#define D_TruncatedWordsTooShort       "SA"
+#define D_InvalidFormatForRecordNumber "SB"
+#define D_TooManyCharactersInSearch    "SC"
+#define D_TooManyRecordsRetrieved      "SD"
+#define D_PresentRequestOutOfRange     "SF"
+#define D_SystemErrorInPresentRecords  "SG"
+#define D_RecordNotAuthorizedToBeSent  "SH"
+#define D_RecordExceedsPrefMessageSize "SI"
+#define D_RecordExceedsMaxRecordSize   "SJ"
+#define D_ResultSetNotSuppAsSearchTerm "SK"
+#define D_OnlyOneRsltSetAsSrchTermSupp "SL"
+#define D_OnlyANDingOfASnglRsltSetSupp "SM"
+#define D_RsltSetExistsNoReplace       "SN"
+#define D_ResultSetNamingNotSupported  "SO"
+#define D_CombinationDatabasesNotSupp  "SP"
+#define D_ElementSetNamesNotSupported  "SQ"
+#define D_ElementSetNameNotValid       "SR"
+#define D_OnlyASingleElmntSetNameSupp  "SS"
+#define D_ResultSetDeletedByTarget     "ST"
+#define D_ResultSetIsInUse             "SU"
+#define D_DatabasesIsLocked            "SV"
+#define D_TerminatedByNoContinueResp   "SW"
+#define D_ResultSetDoesNotExist        "SX"
+#define D_ResExNoResultsAvailable      "SY"
+#define D_ResExUnpredictableResults    "SZ"
+#define D_ResExValidSubsetOfResults    "T1"
+#define D_AccessControlFailure         "T2"
+#define D_SecurityNotIssuedReqTerm     "T3"
+#define D_SecurityNotBeIssuedRecNotInc "T4"
+
+/*----------------------------------------------------------------------*/
+
+/* for internal error handling */
+
+extern char* readErrorPosition; 	/* pos where buf stoped making sense */
+
+/* the following are macros so that they can return OUT of the function
+   which calls them
+ */
+ 
+#define RETURN_ON_NULL(var) 					\
+	if (var == NULL) 				     	\
+	  return(NULL); /* jump out of caller */
+
+#define REPORT_READ_ERROR(pos) 					\
+	{ readErrorPosition = (pos);				\
+	  return(NULL); /* jump out of caller */		\
+    }
+
+#define CHECK_FOR_SPACE_LEFT(spaceNeeded,spaceLeft)		\
+	{ if (*spaceLeft >= spaceNeeded)			\
+	    (*spaceLeft) -= spaceNeeded;			\
+	  else							\
+	   { *spaceLeft = 0; 					\
+	     return(NULL); /* jump out of the caller */ 	\
+	   }							\
+	}
+
+/*----------------------------------------------------------------------*/
+
+diagnosticRecord* makeDiag _AP((boolean surrogate,char* code,char* addInfo));
+void freeDiag _AP((diagnosticRecord* diag));
+char* writeDiag _AP((diagnosticRecord* diag,char* buffer,long* len));
+char* readDiag _AP((diagnosticRecord** diag,char* buffer));
+
+char* writeCompressedInteger _AP((unsigned long num,char* buf,long* len));
+char* readCompressedInteger _AP((unsigned long *num,char* buf));
+char* writeCompressedIntWithPadding _AP((unsigned long num,unsigned long size,
+					 char* buffer,long* len));
+unsigned long writtenCompressedIntSize _AP((unsigned long num));
+
+char* writeTag _AP((data_tag tag,char* buf,long* len));
+char* readTag _AP((data_tag* tag,char* buf));
+data_tag peekTag _AP((char* buf));
+unsigned long writtenTagSize _AP((data_tag tag));
+
+any* makeAny _AP((unsigned long size,char* data));
+void freeAny _AP((any* a));
+any* duplicateAny _AP((any* a));
+char* writeAny _AP((any* a,data_tag tag,char* buffer,long* len));
+char* readAny _AP((any** anAny,char* buffer));
+unsigned long writtenAnySize _AP((data_tag tag,any* a));
+
+any* stringToAny _AP((char* s));
+char* anyToString _AP((any* a));
+unsigned long writtenStringSize _AP((data_tag tag,char* s));
+
+any* longToAny _AP((long Num));
+long anyToLong _AP((any* a));
+
+char* writeString _AP((char* s,data_tag tag,char* buffer,long* len));
+char* readString _AP((char** s,char* buffer));
+
+bit_map* makeBitMap (unsigned long numBits,...);
+
+void freeBitMap _AP((bit_map* bm));
+boolean bitAtPos _AP((long pos,bit_map* bm));
+char* writeBitMap _AP((bit_map* bm,data_tag tag,char* buffer,long* len));
+char* readBitMap _AP((bit_map** bm,char* buffer));
+
+char* writeByte _AP((unsigned long byte,char* buf,long* len));
+char* readByte _AP((unsigned char* byte,char* buf));
+
+char* writeBoolean _AP((boolean flag,char* buf,long* len));
+char* readBoolean _AP((boolean* flag,char* buf));
+
+char* writePDUType _AP((pdu_type pduType,char* buf,long* len));
+char* readPDUType _AP((pdu_type* pduType,char* buf));
+pdu_type peekPDUType _AP((char* buf));
+
+char* writeBinaryInteger _AP((long num,unsigned long size,
+			      char* buf,long* len));
+char* readBinaryInteger _AP((long* num,unsigned long size,char* buf));
+unsigned long writtenCompressedBinIntSize _AP((long num));
+
+char* writeNum _AP((long num,data_tag tag,char* buffer,long* len));
+char* readNum _AP((long* num,char* buffer));
+unsigned long  writtenNumSize _AP((data_tag tag,long num));
+
+void doList _AP((void** list,void (*func)()));
+
+char* writeProtocolVersion _AP((char* buf,long* len));
+char* defaultImplementationID _AP((void));
+char* defaultImplementationName _AP((void));
+char* defaultImplementationVersion _AP((void));
+
+/*----------------------------------------------------------------------*/
+
+
+/*
+**	Routines originally from ZType1.c -- FM
+**
+**----------------------------------------------------------------------*/
+
+/* This file implements the type 1 query defined in appendices B & C
+   of the SR 1 spec.
+ */
+
+/*----------------------------------------------------------------------*/
+/* types and constants */
+
+/* new data tags */
+#define	DT_AttributeList	(data_tag)44
+#define DT_Term			(data_tag)45
+#define DT_Operator		(data_tag)46
+
+#define QT_BooleanQuery	"1"		/* standard boolean query */
+
+/* general attribute code - use in place of any attribute */
+#define IGNORE	"ig"
+
+/* use value codes */
+#define	UV_ISBN	"ub"
+#define	CORPORATE_NAME	"uc"
+#define	ISSN	"us"
+#define	PERSONAL_NAME	"up"
+#define	SUBJECT	"uj"
+#define	TITLE	"ut"
+#define	GEOGRAPHIC_NAME	"ug"
+#define	CODEN	"ud"
+#define	SUBJECT_SUBDIVISION	"ue"
+#define	SERIES_TITLE	"uf"
+#define	MICROFORM_GENERATION	"uh"
+#define	PLACE_OF_PUBLICATION	"ui"
+#define	NUC_CODE	"uk"
+#define	LANGUAGE	"ul"
+#define	COMBINATION_OF_USE_VALUES	"um"
+#define	SYSTEM_CONTROL_NUMBER	"un"
+#define	DATE	"uo"
+#define	LC_CONTROL_NUMBER	"ur"
+#define	MUSIC_PUBLISHERS_NUMBER	"uu"
+#define	GOVERNMENT_DOCUMENTS_NUMBER	"uv"
+#define	SUBJECT_CLASSIFICATION	"uw"
+#define	RECORD_TYPE	"uy"
+
+/* relation value codes */
+#define	EQUAL	"re"
+#define	GREATER_THAN	"rg"
+#define	GREATER_THAN_OR_EQUAL	"ro"
+#define	LESS_THAN	"rl"
+#define	LESS_THAN_OR_EQUAL	"rp"
+#define	NOT_EQUAL	"rn"
+
+/* position value codes */
+#define	FIRST_IN_FIELD	"pf"
+#define	FIRST_IN_SUBFIELD	"ps"
+#define	FIRST_IN_A_SUBFIELD	"pa"
+#define	FIRST_IN_NOT_A_SUBFIELD	"pt"
+#define	ANY_POSITION_IN_FIELD	"py"
+
+/* structure value codes */
+#define	PHRASE	"sp"
+#define	WORD	"sw"
+#define	KEY	"sk"
+#define	WORD_LIST	"sl"
+
+/* truncation value codes */
+#define	NO_TRUNCATION	"tn"
+#define	RIGHT_TRUNCATION	"tr"
+#define	PROC_NUM_INCLUDED_IN_SEARCH_ARG	"ti"
+
+/* completeness value codes */
+#define	INCOMPLETE_SUBFIELD	"ci"
+#define	COMPLETE_SUBFIELD	"cs"
+#define	COMPLETEFIELD	"cf"
+
+/* operator codes */
+#define AND	"a"
+#define OR	"o"
+#define AND_NOT	"n"
+
+/* term types */
+#define TT_Attribute		1
+#define	TT_ResultSetID		2
+#define	TT_Operator			3
+
+#define ATTRIBUTE_SIZE		3
+#define OPERATOR_SIZE		2
+
+typedef struct query_term {
+  /* type */
+  long	TermType;
+  /* for term */
+  char	Use[ATTRIBUTE_SIZE];
+  char	Relation[ATTRIBUTE_SIZE];
+  char	Position[ATTRIBUTE_SIZE];
+  char	Structure[ATTRIBUTE_SIZE];
+  char	Truncation[ATTRIBUTE_SIZE];
+  char	Completeness[ATTRIBUTE_SIZE];
+  any*	Term;
+  /* for result set */
+  any*	ResultSetID;
+  /* for operator */
+  char	Operator[OPERATOR_SIZE];
+} query_term;
+
+/*----------------------------------------------------------------------*/
+/* functions */
+
+query_term* makeAttributeTerm _AP((
+        char* use,char* relation,char* position,char* structure,
+	char* truncation,char* completeness,any* term));
+query_term* makeResultSetTerm _AP((any* resultSet));
+query_term* makeOperatorTerm _AP((char* operatorCode));
+void freeTerm _AP((query_term* qt));
+char* writeQueryTerm _AP((query_term* qt,char* buffer,long* len));
+char* readQueryTerm _AP((query_term** qt,char* buffer));
+any* writeQuery _AP((query_term** terms));
+query_term** readQuery _AP((any* info));
+
+/*----------------------------------------------------------------------*/
+
+
+/*
+**	Routines originally from UI.c -- FM
+**
+**----------------------------------------------------------------------*/
+
+char *
+generate_search_apdu _AP((char* buff,  /* buffer to hold the apdu */
+			  long *buff_len, /* number of bytes written to the buffer */
+			  char *seed_words, /* string of the seed words */
+			  char *database_name,
+			  DocObj** docobjs,
+			  long maxDocsRetrieved
+			  ));
+
+char *
+generate_retrieval_apdu _AP((char *buff, 
+			     long *buff_len, 
+			     any *docID,
+			     long chunk_type,
+			     long start_line, long end_line,
+			     char *type,
+			     char *database_name));
+
+
+long
+interpret_message _AP((char *request_message,
+		       long request_length,
+		       char *response_message,
+		       long response_buffer_length, /* length of the buffer (modified)*/
+		       FILE *connection,
+		       boolean verbose));
+
+
+void
+display_text_record_completely _AP((WAISDocumentText *record, 
+			       boolean quote_string_quotes));
+
+char *trim_junk _AP((char *headline));
+
+
+
+/*
+**	Routines originally from ZProt.c -- FM
+**
+**----------------------------------------------------------------------*/
+
+/* APDU types */
+
+typedef struct InitAPDU {
+	pdu_type		PDUType;
+	boolean			willSearch,willPresent,willDelete;
+	boolean			supportAccessControl,supportResourceControl;
+	long			PreferredMessageSize;
+	long			MaximumRecordSize;
+	char*			IDAuthentication;
+	char*			ImplementationID;
+	char* 			ImplementationName;
+	char*			ImplementationVersion;
+	any*			ReferenceID;
+	void*			UserInformationField;
+	} InitAPDU;
+
+typedef struct InitResponseAPDU {
+	pdu_type		PDUType;
+	boolean			Result;
+	boolean			willSearch,willPresent,willDelete;
+	boolean			supportAccessControl,supportResourceControl;
+	long			PreferredMessageSize;
+	long 			MaximumRecordSize;
+	char*			IDAuthentication;
+	char*			ImplementationID;
+	char* 			ImplementationName;
+	char*			ImplementationVersion;
+	any*			ReferenceID;
+	void*			UserInformationField;
+	} InitResponseAPDU;
+
+typedef struct SearchAPDU {
+	pdu_type		PDUType;
+	long	 		SmallSetUpperBound;
+	long			LargeSetLowerBound;
+	long	 		MediumSetPresentNumber;
+	boolean 		ReplaceIndicator;
+	char*			ResultSetName;
+	char**			DatabaseNames;   
+	char*			QueryType;
+	char**			ElementSetNames;  
+	any*			ReferenceID;
+	void*			Query;
+	} SearchAPDU;
+
+typedef struct SearchResponseAPDU {
+	pdu_type		PDUType;
+	long			SearchStatus;
+	long			ResultCount;
+	long			NumberOfRecordsReturned;
+	long		 	NextResultSetPosition;
+	long			ResultSetStatus;
+	long 			PresentStatus;
+	any*			ReferenceID;
+	void*			DatabaseDiagnosticRecords;
+	} SearchResponseAPDU;
+
+typedef struct PresentAPDU {
+	pdu_type		PDUType;
+	long			NumberOfRecordsRequested;
+	long			ResultSetStartPosition;
+	char*		 	ResultSetID;
+	char*			ElementSetNames;
+	any*			ReferenceID;
+	void*			PresentInfo;
+	} PresentAPDU;
+
+typedef struct PresentResponseAPDU {
+	pdu_type		PDUType;
+	boolean			PresentStatus;
+	long			NumberOfRecordsReturned;
+	long			NextResultSetPosition;
+	any*			ReferenceID;
+	void*			DatabaseDiagnosticRecords;
+	} PresentResponseAPDU;
+
+/*----------------------------------------------------------------------*/
+/* Functions */
+
+InitAPDU* makeInitAPDU _AP((boolean search,boolean present,boolean deleteIt,
+			    boolean accessControl,boolean resourceControl,
+			    long prefMsgSize,long maxMsgSize,
+			    char* auth,char* id,char* name, char* version,
+			    any* refID,void* userInfo));
+void freeInitAPDU _AP((InitAPDU* init));
+char* writeInitAPDU _AP((InitAPDU* init,char* buffer,long* len));
+char* readInitAPDU _AP((InitAPDU** init,char* buffer));
+
+InitResponseAPDU* makeInitResponseAPDU _AP((boolean result,
+					    boolean search,boolean present,boolean deleteIt,
+					    boolean accessControl,boolean resourceControl,
+					    long prefMsgSize,long maxMsgSize,
+					    char* auth,char* id,char* name, char* version,
+					    any* refID,void* userInfo));
+void freeInitResponseAPDU _AP((InitResponseAPDU* init));
+char* writeInitResponseAPDU _AP((InitResponseAPDU* init,char* buffer,long* len));
+char* readInitResponseAPDU _AP((InitResponseAPDU** init,char* buffer));
+InitResponseAPDU* replyToInitAPDU _AP((InitAPDU* init,boolean result,void* userInfo));
+
+SearchAPDU* makeSearchAPDU _AP((long small,long large, long medium,
+				boolean replace,char* name,char** databases,
+				char* type,char** elements,any* refID,void* queryInfo));
+void freeSearchAPDU _AP((SearchAPDU* query));
+char* writeSearchAPDU _AP((SearchAPDU* query,char* buffer,long* len));
+char* readSearchAPDU _AP((SearchAPDU** query,char* buffer));
+
+SearchResponseAPDU* makeSearchResponseAPDU _AP((long result,long count,
+						long recordsReturned,long nextPos,
+						long resultStatus,long presentStatus,
+						any* refID,void* records));
+void freeSearchResponseAPDU _AP((SearchResponseAPDU* queryResponse));
+char* writeSearchResponseAPDU _AP((SearchResponseAPDU* queryResponse,char* buffer,long* len));
+char* readSearchResponseAPDU _AP((SearchResponseAPDU** queryResponse,char* buffer));
+
+PresentAPDU* makePresentAPDU _AP((long recsReq, long startPos,
+				  char* resultID,any* refID,void* info));
+void freePresentAPDU _AP((PresentAPDU* present));
+char* writePresentAPDU _AP((PresentAPDU* present,char* buffer,long* len));
+char* readPresentAPDU _AP((PresentAPDU** present,char* buffer));
+
+PresentResponseAPDU* makePresentResponseAPDU _AP((boolean status,long recsRet,
+						  long nextPos,any* refID,
+						  void* records));
+void freePresentResponseAPDU _AP((PresentResponseAPDU* present));
+char* writePresentResponseAPDU _AP((PresentResponseAPDU* present,char* buffer,long* len));
+char* readPresentResponseAPDU _AP((PresentResponseAPDU** present,char* buffer));
+
+/*----------------------------------------------------------------------*/
+/* user extension hooks: */
+
+extern char* writeInitInfo _AP((InitAPDU* init,char* buffer,long* len));
+extern char* readInitInfo _AP((void** info,char* buffer));
+
+extern char* writeInitResponseInfo _AP((InitResponseAPDU* init,char* buffer,long* len));
+extern char* readInitResponseInfo _AP((void** info,char* buffer));
+
+extern char* writeSearchInfo _AP((SearchAPDU* query,char* buffer,long* len));
+extern char* readSearchInfo _AP((void** info,char* buffer));
+
+extern char* writeSearchResponseInfo _AP((SearchResponseAPDU* query,char* buffer,long* len));
+extern char* readSearchResponseInfo _AP((void** info,char* buffer));
+
+extern char* writePresentInfo _AP((PresentAPDU* present,char* buffer,long* len));
+extern char* readPresentInfo _AP((void** info,char* buffer));
+
+extern char* writePresentResponseInfo _AP((PresentResponseAPDU* present,char* buffer,long* len));
+extern char* readPresentResponseInfo _AP((void** info,char* buffer));
+
+
+#endif /* HTVMSWAIS_H */
diff --git a/WWW/Library/Implementation/HTWAIS.c b/WWW/Library/Implementation/HTWAIS.c
new file mode 100644
index 00000000..e0659f17
--- /dev/null
+++ b/WWW/Library/Implementation/HTWAIS.c
@@ -0,0 +1,1103 @@
+/*	WorldWideWeb - Wide Area Informaion Server Access	HTWAIS.c
+**	==================================================
+**
+**	This module allows a WWW server or client to read data from a
+**	remote  WAIS
+**  server, and provide that data to a WWW client in hypertext form.
+**  Source files, once retrieved, are stored and used to provide
+**  information about the index when that is acessed.
+**
+** Authors
+**	BK	Brewster Kahle, Thinking Machines, <Brewster@think.com>
+**	TBL	Tim Berners-Lee, CERN <timbl@info.cern.ch>
+**	FM	Foteos Macrides, WFEB <macrides@sci.wfeb.edu>
+**
+** History
+**	   Sep 91	TBL adapted shell-ui.c (BK) with HTRetrieve.c from WWW.
+**	   Feb 91	TBL Generated HTML cleaned up a bit (quotes, escaping)
+**			    Refers to lists of sources. 
+**	   Mar 93	TBL Lib 2.0 compatible module made.
+**	   May 94	FM  Added DIRECT_WAIS support for VMS.
+**
+** Bugs
+**	Uses C stream i/o to read and write sockets, which won't work
+**	on VMS TCP systems.
+**
+**	Should cache connections.
+**
+**	ANSI C only as written
+**
+** Bugs fixed
+**      NT Nathan Torkington (Nathan.Torkington@vuw.ac.nz)
+**
+** WAIS comments:
+**
+**	1.	Separate directories for different system's .o would help
+**	2.	Document ids are rather long!
+**
+** WWW Address mapping convention:
+**
+**	/servername/database/type/length/document-id
+**
+**	/servername/database?word+word+word
+*/
+/* WIDE AREA INFORMATION SERVER SOFTWARE:
+   No guarantees or restrictions.  See the readme file for the full standard
+   disclaimer.
+
+   Brewster@think.com
+*/
+
+#include "HTUtils.h"
+#include "tcp.h"
+#include "HTParse.h"
+#include "HTAccess.h"		/* We implement a protocol */
+#include "HTML.h"		/* The object we will generate */
+#include "HTFormat.h"
+#include "HTTCP.h"
+#include "HTCJK.h"
+#include "HTAlert.h"
+/* #include "HTWSRC.h"	*/	/* Need some bits from here */
+/* #include "ParseWSRC.h" */
+
+
+/*			From WAIS
+**			---------
+*/
+
+#ifdef VMS
+#include "HTVMS_WaisUI.h"
+#include "HTVMS_WaisProt.h"
+#else
+#include <ui.h>
+#endif /* VMS */
+
+#define MAX_MESSAGE_LEN 100000
+#define CHARS_PER_PAGE 10000 /* number of chars retrieved in each request */
+
+#define WAISSEARCH_DATE "Fri Jul 19 1991"
+
+
+/*			FROM WWW
+**			--------
+*/
+#include "LYLeaks.h"
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+extern int HTCheckForInterrupt NOPARAMS;
+
+#define DIRECTORY "/cnidr.org:210/directory-of-servers"
+/* #define DIRECTORY "/quake.think.com:210/directory-of-servers" */
+
+#define BIG 1024	/* identifier size limit  @@@@@ */
+
+#define BUFFER_SIZE 4096	/* Arbitrary size for efficiency */
+
+#define HEX_ESCAPE '%'
+
+extern HTCJKlang HTCJK;
+
+extern int WWW_TraceFlag;	/* Control diagnostic output */
+extern FILE * logfile;		/* Log file output */
+
+PRIVATE BOOL	as_gate;	/* Client is using us as gateway */
+
+PRIVATE char	line[2048];	/* For building strings to display */
+				/* Must be able to take id */
+
+#define PUTC(c) (*target->isa->put_character)(target, c)
+#define PUTS(s) (*target->isa->put_string)(target, s)
+#define START(e) (*target->isa->start_element)(target, e, 0, 0, 0)
+#define END(e) (*target->isa->end_element)(target, e, 0)
+#define FREE_TARGET (*target->isa->_free)(target)
+
+struct _HTStructured {
+	CONST HTStructuredClass *	isa;
+	/* ... */
+};
+
+struct _HTStream {
+	CONST HTStreamClass *	isa;
+	/* ... */
+};
+
+
+/* ------------------------------------------------------------------------ */
+/* ---------------- Local copy of connect_to_server calls ----------------- */
+/* ------------------------------------------------------------------------ */
+
+/* Returns 1 on success, 0 on fail, -1 on interrupt. */
+static int fd_mosaic_connect_to_server ARGS3(char *, host_name, long, port,
+long *, fd)
+{
+  /* New version. */
+  char dummy[256];
+  int status;
+
+  sprintf (dummy, "wais://%s:%d/", host_name, port);
+
+  status = HTDoConnect (dummy, "WAIS", 210, (int *)fd);
+  if (status == HT_INTERRUPTED)
+    {
+      if (TRACE)
+        fprintf (stderr, "===WAIS=== interrupted in connect\n");
+      HTAlert ("Connection interrupted.");
+      return -1;
+    }
+  if (status < 0)
+    return 0;
+  return 1;
+}
+
+/* Returns 1 on success, 0 on fail, -1 on interrupt. */
+#ifdef VMS
+static int mosaic_connect_to_server ARGS3(char *, host_name, long, port,
+					  long *, fdp)
+#else
+static int mosaic_connect_to_server ARGS3(char *, host_name, long, port,
+					  FILE **, fp)
+#endif /* VMS */
+{
+#ifndef VMS
+  FILE* file;
+#endif /* VMS */
+  long fd;
+  int rv;
+  
+  rv = fd_mosaic_connect_to_server (host_name, port, &fd);
+  if(rv == 0) 
+    {
+      HTAlert ("Could not connect to WAIS server.");
+      return 0;
+    }
+  else if (rv == -1)
+    {
+      HTAlert ("Connection interrupted.");
+      return -1;
+    }
+
+#ifndef VMS
+  if ((file = fdopen(fd,"r+")) == NULL) 
+    {
+      HTAlert ("Could not open WAIS connection for reading.");
+      return 0;
+    }
+
+  *fp = file;
+#else
+  *fdp = fd;
+#endif /* VMS */
+  return 1;
+}
+
+
+/* ------------------------------------------------------------------------ */
+/* ------------------------------------------------------------------------ */
+
+
+/*								showDiags
+*/
+/* modified from Jonny G's version in ui/question.c */
+
+void showDiags ARGS2(
+	HTStream *, 		target,
+	diagnosticRecord **, 	d)
+{
+  long i;
+
+  for (i = 0; d[i] != NULL; i++) {
+    if (d[i]->ADDINFO != NULL) {
+      PUTS("Diagnostic code is ");
+      PUTS(d[i]->DIAG);
+      PUTC(' ');
+      PUTS(d[i]->ADDINFO);
+      PUTC('\n'); ;
+    }
+  }
+}
+
+/*	Matrix of allowed characters in filenames
+**	-----------------------------------------
+*/
+
+PRIVATE BOOL acceptable[256];
+PRIVATE BOOL acceptable_inited = NO;
+
+PRIVATE void init_acceptable NOARGS
+{
+    unsigned int i;
+    char * good = 
+      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./-_$";
+    for(i=0; i<256; i++) acceptable[i] = NO;
+    for(;*good; good++) acceptable[(unsigned int)*good] = YES;
+    acceptable_inited = YES;
+}
+
+/*	Transform file identifier into WWW address
+**	------------------------------------------
+**
+**
+** On exit,
+**	returns		nil if error
+**			pointer to malloced string (must be freed) if ok
+*/
+char * WWW_from_archie ARGS1 (char *, file)
+{
+    char * end;
+    char * result;
+    char * colon;
+    for(end=file; *end > ' '; end++);	/* assumes ASCII encoding*/
+    result = (char *)malloc(10 + (end-file));
+    if (!result) return result;		/* Malloc error */
+    strcpy(result, "file://");
+    strncat(result, file, end-file);
+    colon = strchr(result+7, ':');	/* Expect colon after host */
+    if (colon) {
+	for(; colon[0]; colon[0]=colon[1], colon++);	/* move down */
+    }
+    return result;
+} /* WWW_from_archie */
+
+/*	Transform document identifier into URL
+**	--------------------------------------
+**
+** Bugs: A static buffer of finite size is used!
+**	The format of the docid MUST be good!
+**
+** On exit,
+**	returns		nil if error
+**			pointer to malloced string (must be freed) if ok
+*/
+PRIVATE char hex [17] = "0123456789ABCDEF";
+extern char from_hex PARAMS((char a));			/* In HTWSRC @@ */
+
+PRIVATE char * WWW_from_WAIS ARGS1(any *, docid)
+
+{
+    static char buf[BIG];
+    char * q = buf;
+    char * p = (docid->bytes);
+    int i, l;
+    if (TRACE) {
+	char *p;
+	fprintf(stderr, "WAIS id (%d bytes) is ", (int)docid->size);
+	for(p=docid->bytes; p<docid->bytes+docid->size; p++) {
+	    if ((*p >= ' ') && (*p<= '~')) /* Assume ASCII! */
+		fprintf(stderr, "%c", *p);
+	    else
+		fprintf(stderr, "<%x>", (unsigned)*p);
+	}
+	fprintf(stderr, "\n");
+    }	 
+    for (p=docid->bytes; (p<docid->bytes+docid->size) && (q<&buf[BIG]);) {
+	if (TRACE) fprintf(stderr, "    Record type %d, length %d\n",
+		p[0], p[1]);
+        if (*p>10) {
+	    if(TRACE)
+	        fprintf(stderr, "Eh? DOCID record type of %d!\n", *p);
+	    return 0;
+	}
+	{	/* Bug fix -- allow any byte value 15 Apr 93 */
+	    unsigned int i = (unsigned) *p++;
+	    
+	    if (i > 99) {
+		*q++ = (i/100) + '0';
+		i = i % 100;
+	    }
+	    if (i > 9) {
+		*q++ = (i/10) + '0';
+		i = i % 10;
+	    }
+	    *q++ = i + '0';	/* Record type */
+	}
+	*q++ = '=';		/* Separate */
+	l = *p++;		/* Length */
+	for(i=0; i<l; i++, p++){
+	    if (!acceptable[*p]) {
+		*q++ = HEX_ESCAPE;	/* Means hex commming */
+		*q++ = hex[(*p) >> 4];
+		*q++ = hex[(*p) & 15];
+	    }
+	    else *q++ = *p;
+	}
+	*q++= ';';		/* Terminate field */
+    }
+    *q++ = 0;			/* Terminate string */
+    if (TRACE) fprintf(stderr, "WWW form of id: %s\n", buf); 
+    {
+        char * result = (char *)malloc(strlen(buf)+1);
+	if (!result)
+	    outofmem(__FILE__, "WWW_from_WAIS");
+	strcpy(result, buf);
+	return result;
+    }
+} /* WWW_from_WAIS */
+
+
+/*	Transform URL into WAIS document identifier
+**	-------------------------------------------
+**
+** On entry,
+**	docname		points to valid name produced originally by
+**			WWW_from_WAIS
+** On exit,
+**	docid->size	is valid
+**	docid->bytes	is malloced and must later be freed.
+*/
+PRIVATE any * WAIS_from_WWW ARGS2 (any *, docid, char *, docname)
+{
+    char *z; 	/* Output pointer */
+    char *sor;	/* Start of record - points to size field. */
+    char *p; 	/* Input pointer */
+    char *q; 	/* Poisition of "=" */
+    char *s; 	/* Position of semicolon */
+    int n;	/* size */
+    if (TRACE) fprintf(stderr, "WWW id (to become WAIS id): %s\n", docname); 
+    for(n=0, p = docname; *p; p++) {	/* Count sizes of strings */
+        n++;
+	if (*p == ';')  n--;		/* Not converted */
+	else if (*p == HEX_ESCAPE) n=n-2;	/* Save two bytes */
+        docid->size = n;
+    }
+    
+    if (!(docid->bytes = (char *) malloc(docid->size))) /* result record */
+	    outofmem(__FILE__, "WAIS_from_WWW");
+    z = docid->bytes;
+    
+    for(p = docname; *p; ) {	/* Convert of strings */
+    				/* Record type */
+				
+	*z = 0;			/* Initialize record type */
+	while (*p >= '0' && *p <= '9') {
+	    *z = *z*10 + (*p++ - '0');	/* Decode decimal record type */
+	}
+	z++;
+	if (*p != '=') return 0;
+	q = p;
+	
+/*        *z++ = *p++ - '0';
+	q = strchr(p , '=');
+	if (!q) return 0;
+*/
+	s = strchr(q, ';');	/* (Check only) */
+	if (!s) return 0;	/* Bad! No ';';	*/
+        sor = z;		/* Remember where the size field was */
+	z++;			/* Skip record size for now	*/
+	for(p=q+1; *p!=';' ; ) {
+	   if (*p == HEX_ESCAPE) {
+	        char c;
+		unsigned int b;
+		p++;
+	        c = *p++;
+		b =   from_hex(c);
+		c = *p++;
+		if (!c) break;	/* Odd number of chars! */
+		*z++ = (b<<4) + from_hex(c);
+	    } else {
+	        *z++ = *p++;	/* Record */
+	    }
+	}
+	*sor = (z-sor-1);	/* Fill in size -- not counting size itself */
+	p++;			/* After semicolon: start of next record */
+    }
+    
+    if (TRACE) {
+	char *p;
+	fprintf(stderr, "WAIS id (%d bytes) is ", (int)docid->size);
+	for(p=docid->bytes; p<docid->bytes+docid->size; p++) {
+	    if ((*p >= ' ') && (*p<= '~')) /* Assume ASCII! */
+		fprintf(stderr, "%c", *p);
+	    else
+		fprintf(stderr, "<%x>", (unsigned)*p);
+	}
+	fprintf(stderr, "\n");
+    }	 
+    return docid;		/* Ok */
+    
+} /* WAIS_from_WWW */
+
+
+/*	Send a plain text record to the client		output_text_record()
+**	--------------------------------------
+*/
+
+PRIVATE void output_text_record ARGS4(
+    HTStream *,			target,
+    WAISDocumentText *,		record,
+    boolean,			quote_string_quotes,
+    boolean,                    binary)
+{
+  long count;
+  /* printf(" Text\n");
+     print_any("     DocumentID:  ", record->DocumentID);
+     printf("     VersionNumber:  %d\n", record->VersionNumber);
+     */
+
+  if (binary) {
+    (*target->isa->put_block)(target,
+			      record->DocumentText->bytes,
+			      record->DocumentText->size);
+    return;
+  }
+
+  for(count = 0; count < record->DocumentText->size; count++){
+    long ch = (unsigned char)record->DocumentText->bytes[count];
+    if (ch == 27) {	/* What is this in for? Tim */
+
+	    /* then we have an escape code */
+	    /* if the next letter is '(' or ')', then ignore two letters */
+	    if('(' == record->DocumentText->bytes[count + 1] ||
+		')' == record->DocumentText->bytes[count + 1])
+	    count += 1;             /* it is a term marker */
+	    else count += 4;		/* it is a paragraph marker */
+    } else if (ch == '\n' || ch == '\r') {
+	    PUTC('\n');
+    } else if (HTCJK != NOCJK || ch == '\t' || isprint(ch)){
+	    PUTC(ch);
+    } 
+  }
+} /* output text record */
+
+
+
+/*	Format A Search response for the client		display_search_response
+**	---------------------------------------
+*/
+/* modified from tracy shen's version in wutil.c
+ * displays either a text record or a set of headlines.
+ */
+void
+display_search_response ARGS4(
+    HTStructured *,		target,
+    SearchResponseAPDU *,	response,
+    char *,			database,
+    char *,	 		keywords)
+{
+  WAISSearchResponse  *info;
+  long i, k;
+  
+  BOOL archie =  strstr(database, "archie")!=0;	/* Specical handling */
+  
+  if (TRACE) fprintf(stderr, "HTWAIS: Displaying search response\n");
+  PUTS("Index ");
+  START(HTML_EM);
+  PUTS(database);
+  END(HTML_EM);
+  sprintf(line, " contains the following %d item%s relevant to \"",
+	  (int)(response->NumberOfRecordsReturned),
+	  response->NumberOfRecordsReturned ==1 ? "" : "s");
+  PUTS(line);
+  START(HTML_EM);
+  PUTS(keywords);
+  END(HTML_EM);
+  PUTS("\".\n");
+  PUTS("The first figure after each entry is its relative score, ");
+  PUTS("the second is the number of lines in the item.");
+  START(HTML_BR);
+  START(HTML_BR);
+  PUTS("\n");
+  START(HTML_OL);
+
+  if ( response->DatabaseDiagnosticRecords != 0 ) {
+    info = (WAISSearchResponse *)response->DatabaseDiagnosticRecords;
+    i =0; 
+
+    if (info->Diagnostics != NULL)
+      showDiags((HTStream*)target, info->Diagnostics);
+
+    if ( info->DocHeaders != 0 ) {
+      for (k=0; info->DocHeaders[k] != 0; k++ ) {
+	WAISDocumentHeader* head = info->DocHeaders[k];
+	char * headline = trim_junk(head->Headline);
+	any * docid = head->DocumentID;
+	char * docname;			/* printable version of docid */
+	i++;
+
+/*	Make a printable string out of the document id.
+*/
+	if (TRACE) fprintf(stderr, 
+		"HTWAIS:  %2ld: Score: %4ld, lines:%4ld '%s'\n", 
+	       i,
+	       (long int)(info->DocHeaders[k]->Score),
+	       (long int)(info->DocHeaders[k]->Lines),
+	       headline);
+
+	START(HTML_LI);
+
+	if (archie) {
+	    char * www_name = WWW_from_archie(headline);
+	    if (www_name) {
+		HTStartAnchor(target, NULL, www_name);
+		PUTS(headline);
+		
+		END(HTML_A);
+		FREE(www_name);
+	    } else {
+		 PUTS(headline);
+		 PUTS(" (bad file name)");
+	    }
+	} else { /* Not archie */
+	    docname =  WWW_from_WAIS(docid);
+	    if (docname) {
+		char * dbname = HTEscape(database, URL_XPALPHAS);
+		sprintf(line, "/%s/%s/%d/%s",		/* W3 address */
+				    dbname,
+		    head->Types ? head->Types[0] : "TEXT",
+		    (int)(head->DocumentLength),
+		    docname);
+		HTStartAnchor(target, NULL, ( (head->Types) 
+		      && (!strcmp(head->Types[0], "URL"))) ? 
+			      headline : line); /* NT, Sep 93 */
+		PUTS(headline);
+		END(HTML_A);
+		FREE(dbname);
+		FREE(docname);
+	    } else {
+		 PUTS("(bad doc id)");
+	    }
+	  }
+
+	sprintf(line, "%5ld  %5ld  ",
+	    head->Score,
+	    head->Lines);
+	PUTS( line);
+      } /* next document header */
+    } /* if there were any document headers */
+    
+    if ( info->ShortHeaders != 0 ) {
+      k =0;
+      while (info->ShortHeaders[k] != 0 ) {
+	i++;
+	PUTS( "(Short Header record, can't display)");
+      }
+    }
+    if ( info->LongHeaders != 0 ) {
+      k =0;
+      while (info->LongHeaders[k] != 0) {
+	i++;
+	PUTS( "\nLong Header record, can't display\n");
+      }
+    }
+    if ( info->Text != 0 ) {
+      k =0;
+      while (info->Text[k] != 0) {
+	i++;
+	PUTS( "\nText record\n");
+	output_text_record((HTStream*)target, info->Text[k++], false, false);
+      }
+    }
+    if ( info->Headlines != 0 ) {
+      k =0;
+      while (info->Headlines[k] != 0) {
+	i++;
+	PUTS( "\nHeadline record, can't display\n");
+	/* dsply_headline_record( info->Headlines[k++]); */
+      }
+    }
+    if ( info->Codes != 0 ) {
+      k =0;
+      while (info->Codes[k] != 0) {
+	i++;
+	PUTS( "\nCode record, can't display\n");
+	/* dsply_code_record( info->Codes[k++]); */
+      }
+    }
+  }				/* Loop: display user info */
+  END(HTML_OL);
+  PUTC('\n'); ;
+}
+
+
+
+
+/*		Load by name					HTLoadWAIS
+**		============
+**
+**	This renders any object or search as required
+*/
+PUBLIC int HTLoadWAIS ARGS4(
+	CONST char *,		arg,
+	HTParentAnchor *,	anAnchor,
+	HTFormat,		format_out,
+	HTStream*,		sink)
+
+#define MAX_KEYWORDS_LENGTH 1000
+#define MAX_SERVER_LENGTH 1000
+#define MAX_DATABASE_LENGTH 1000
+#define MAX_SERVICE_LENGTH 1000
+#define MAXDOCS 200
+
+{
+    static CONST char * error_header =
+"<h1>Access error</h1>\nThe following error occured in accesing a WAIS server:<P>\n";
+    char * key;			  /* pointer to keywords in URL */
+    char* request_message = NULL; /* arbitrary message limit */
+    char* response_message = NULL; /* arbitrary message limit */
+    long request_buffer_length;	/* how of the request is left */
+    SearchResponseAPDU  *retrieval_response = 0;
+    char keywords[MAX_KEYWORDS_LENGTH + 1];
+    char *server_name;	
+    char *wais_database = NULL;		/* name of current database */
+    char *www_database;			/* Same name escaped */
+    char *service;
+    char *doctype;
+    char *doclength;
+    long document_length;
+    char *docname;
+#ifdef VMS
+    long connection = 0;
+#else
+    FILE *connection = NULL;
+#endif /* VMS */
+    char * names;		/* Copy of arg to be hacked up */
+    BOOL ok = NO;
+    int return_status = HT_LOADED;
+    int rv;
+    
+    extern FILE * connect_to_server();
+    
+    if (!acceptable_inited) init_acceptable();
+    
+        
+/*	Decipher and check syntax of WWW address:
+**	----------------------------------------
+**
+**	First we remove the "wais:" if it was spcified.  920110
+*/  
+    names = HTParse(arg, "", PARSE_HOST | PARSE_PATH | PARSE_PUNCTUATION);
+    key = strchr(names, '?');
+    
+    if (key) {
+    	char * p;
+	*key++ = 0;	/* Split off keywords */
+	for (p=key; *p; p++) if (*p == '+') *p = ' ';
+	HTUnEscape(key);
+    }
+    if (names[0]== '/') {
+	server_name = names+1;
+	if (as_gate =(*server_name == '/'))
+	    server_name++;	/* Accept one or two */
+	www_database = strchr(server_name,'/');
+	if (www_database) {
+	    *www_database++ = 0;		/* Separate database name */
+	    doctype = strchr(www_database, '/');
+	    if (key) ok = YES;	/* Don't need doc details */
+	    else if (doctype) {	/* If not search parse doc details */
+		*doctype++ = 0;	/* Separate rest of doc address */
+		doclength = strchr(doctype, '/');
+		if(doclength) {
+		    *doclength++ = 0;
+		    document_length = atol(doclength);
+		    if (document_length) {
+			docname=strchr(doclength, '/');
+			if (docname) {
+			    *docname++ = 0;
+			    ok = YES;	/* To avoid a goto! */
+			} /* if docname */
+		    } /* if document_length valid */
+		} /* if doclength */
+	    } else { /* no doctype?  Assume index required */
+	        if (!key) key = "";
+		ok = YES;
+	    } /* if doctype */
+	} /* if database */
+     }
+     
+     if (!ok)
+	 return HTLoadError(sink, 500, "Syntax error in WAIS URL");
+
+     if (TRACE) fprintf(stderr, "HTWAIS: Parsed OK\n");
+     
+     service = strchr(names, ':');
+     if (service)  *service++ = 0;
+     else service = "210";
+     
+     if (server_name[0] == 0)
+#ifdef VMS
+        connection = 0;
+#else
+        connection = NULL;
+#endif /* VMS */
+
+     else if (!(key && !*key))
+    {
+      int status;
+      if (TRACE)
+        fprintf (stderr, "===WAIS=== calling mosaic_connect_to_server\n");
+      status = mosaic_connect_to_server
+        			(server_name, atoi(service), &connection);
+      if (status == 0)
+        {
+          if (TRACE)
+            fprintf (stderr, "===WAIS=== connection failed\n");
+          FREE(names);
+          return HT_NOT_LOADED;
+        }
+      else if (status == -1)
+        {
+          if (TRACE)
+            fprintf (stderr, "===WAIS=== connection interrupted\n");
+          FREE(names);
+          return HT_INTERRUPTED;
+        }
+    }
+
+    StrAllocCopy(wais_database,www_database);
+    HTUnEscape(wais_database);
+    
+	/* This below fixed size stuff is terrible */
+#ifdef VMS
+    if (!(request_message =
+    	  (char*)calloc((size_t)MAX_MESSAGE_LEN*sizeof(char),1)))
+	outofmem(__FILE__, "HTLoadWAIS");
+    if (!(response_message =
+    	  (char*)calloc((size_t)MAX_MESSAGE_LEN*sizeof(char),1)))
+	outofmem(__FILE__, "HTLoadWAIS");
+#else
+    request_message = (char*)s_malloc((size_t)MAX_MESSAGE_LEN * sizeof(char));
+    response_message = (char*)s_malloc((size_t)MAX_MESSAGE_LEN * sizeof(char));
+#endif /* VMS */
+
+/*	If keyword search is performed but there are no keywords,
+**	the user has followed a link to the index itself. It would be
+**	appropriate at this point to send him the .SRC file - how?
+*/
+
+    if (key && !*key) {				/* I N D E X */
+    
+#ifdef CACHE_FILE_PREFIX
+	char filename[256];
+	FILE * fp;
+#endif
+	HTStructured * target = HTML_new(anAnchor, format_out, sink);
+	
+	START(HTML_HEAD);
+	PUTS("\n");
+	HTStartIsIndex(target, "Enter WAIS query: ", NULL);
+	PUTS("\n");
+
+	{
+	    START(HTML_TITLE);
+	    PUTS(wais_database);
+	    PUTS(" (WAIS Index)");
+	    END(HTML_TITLE);
+	    PUTS("\n");
+	    END(HTML_HEAD);
+	    PUTS("\n");
+	    
+	    START(HTML_H1);
+	    PUTS("WAIS Index: ");
+	    START(HTML_EM);
+	    PUTS(wais_database);
+	    END(HTML_EM);
+	    END(HTML_H1);
+	    PUTS("\n");
+	    PUTS("This is a link for searching the ");
+	    START(HTML_EM);
+	    PUTS(wais_database);
+	    END(HTML_EM);
+	    PUTS(" WAIS Index.\n"); 
+	    
+	}
+	/* If we have seen a source file for this database, use that:
+	*/
+
+#ifdef CACHE_FILE_PREFIX
+	sprintf(filename, "%sWSRC-%s:%s:%.100s.txt",
+		CACHE_FILE_PREFIX,
+		server_name, service, www_database);
+
+	fp = fopen(filename, "r");	/* Have we found this already? */
+	if (TRACE) fprintf(stderr,
+		"HTWAIS: Description of server %s %s.\n",
+		filename,
+		fp ? "exists already" : "does NOT exist!");
+
+	if (fp) {
+	    char c;
+	    START(HTML_PRE);		/* Preformatted description */
+	    PUTS("\n");
+	    while((c=getc(fp))!=EOF) PUTC(c);	/* Transfer file */
+	    END(HTML_PRE);
+	    fclose(fp);
+#endif
+	START(HTML_P);
+	PUTS("\nEnter the 's'earch command and then specify search words.\n");
+	
+	FREE_TARGET;
+	
+    } else if (key) {					/* S E A R C H */
+	char *p;
+	HTStructured * target;
+	
+	strncpy(keywords, key, MAX_KEYWORDS_LENGTH);
+	while(p=strchr(keywords, '+')) *p = ' ';
+    
+        /* Send advance title to get something fast to the other end */
+	
+	target = HTML_new(anAnchor, format_out, sink);
+	
+	START(HTML_HEAD);
+	PUTS("\n");
+	HTStartIsIndex(target, "Enter WAIS query: ", NULL);
+	PUTS("\n");
+	START(HTML_TITLE);
+	PUTS(keywords);
+	PUTS(" (in ");
+	PUTS(wais_database);
+	PUTS(")");
+	END(HTML_TITLE);
+	PUTS("\n");
+	END(HTML_HEAD);
+	PUTS("\n");
+	
+	START(HTML_H1);
+	PUTS("WAIS Search of \"");
+	START(HTML_EM);
+	PUTS(keywords);
+	END(HTML_EM);
+	PUTS("\" in: ");
+	START(HTML_EM);
+	PUTS(wais_database);
+	END(HTML_EM);
+	END(HTML_H1);
+	PUTS("\n");
+
+	request_buffer_length = MAX_MESSAGE_LEN; /* Amount left */
+	if (TRACE) fprintf(stderr, "HTWAIS: Search for `%s' in `%s'\n",
+		keywords, wais_database);
+	if(NULL ==
+	generate_search_apdu(request_message + HEADER_LENGTH, 
+				&request_buffer_length, 
+				keywords, wais_database, NULL, MAXDOCS)) {
+#ifdef VMS
+	    HTAlert ("HTWAIS: Request too large.");
+	    return_status = HT_NOT_LOADED;
+	    FREE_TARGET;
+	    goto CleanUp;
+#else
+	    panic("request too large");
+#endif /* VMS */
+        }
+	
+	HTProgress("Searching WAIS database...");
+	rv = interpret_message (request_message, 
+				MAX_MESSAGE_LEN - request_buffer_length, 
+				response_message,
+				MAX_MESSAGE_LEN,
+				connection,
+				false	/* true verbose */
+			       );
+
+	if (rv == HT_INTERRUPTED) {
+	    HTAlert ("Search interrupted.");
+	    return_status = HT_INTERRUPTED;
+	    FREE_TARGET;
+	    goto CleanUp;
+	}
+
+	else if(!rv) {
+#ifdef VMS
+	    HTAlert ("HTWAIS: Return message too large.");
+	    return_status = HT_NOT_LOADED;
+	    FREE_TARGET;
+	    goto CleanUp;
+#else
+	    panic("returned message too large");
+#endif /* VMS */
+    
+        } else {	/* returned message ok */
+	
+	    SearchResponseAPDU  *query_response = 0;
+	    readSearchResponseAPDU(&query_response,
+	    	response_message + HEADER_LENGTH);
+	    display_search_response(target, 
+	    	query_response, wais_database, keywords);
+	    if (query_response->DatabaseDiagnosticRecords)
+		freeWAISSearchResponse(
+			query_response->DatabaseDiagnosticRecords);         
+	    freeSearchResponseAPDU( query_response);
+	}	/* returned message not too large */
+    
+	FREE_TARGET;
+
+    } else {			/* D O C U M E N T    F E T C H */
+    
+	HTFormat format_in;
+	boolean binary;     /* how to transfer stuff coming over */
+	HTStream * target;
+	long count;
+	any   doc_chunk;
+	any * docid = &doc_chunk;
+	if (TRACE) fprintf(stderr,
+		"HTWAIS: Retrieve document id `%s' type `%s' length %ld\n",
+		docname, doctype, document_length);
+		
+	format_in = 
+	  !strcmp(doctype, "WSRC") ? HTAtom_for("application/x-wais-source") :
+	  !strcmp(doctype, "TEXT") ? HTAtom_for("text/plain") :
+	  !strcmp(doctype, "HTML") ? HTAtom_for("text/html") :
+	  !strcmp(doctype, "GIF")  ? HTAtom_for("image/gif") :
+	   		             HTAtom_for("application/octet-stream");
+	binary = 
+	  0 != strcmp(doctype, "WSRC") &&
+	  0 != strcmp(doctype, "TEXT") &&
+	  0 != strcmp(doctype, "HTML") ;
+
+
+	target = HTStreamStack(format_in, format_out, sink, anAnchor);
+	if (!target) return HTLoadError(sink, 500,
+		"Can't convert format of WAIS document");
+/*	Decode hex or litteral format for document ID
+*/	
+	WAIS_from_WWW(docid, docname);
+
+	
+/*	Loop over slices of the document
+*/	
+	for(count = 0; 
+	    count * CHARS_PER_PAGE < document_length;
+	    count++){
+#ifdef VMS
+          char *type = NULL;
+	  
+	  StrAllocCopy(type, doctype);
+#else
+	  char *type = s_strdup(doctype);	/* Gets freed I guess */
+#endif /* VMS */
+	  request_buffer_length = MAX_MESSAGE_LEN; /* Amount left */
+	  if (TRACE) fprintf(stderr, "HTWAIS: Slice number %ld\n", count);
+
+            if(HTCheckForInterrupt())
+              {
+                HTAlert ("Data transfer interrupted.");
+                (*target->isa->_abort)(target, NULL);
+#ifdef VMS
+		FREE(type);
+#endif /* VMS */
+		return_status = HT_NOT_LOADED;
+		goto CleanUp;
+              }
+
+	  if(0 ==
+	      generate_retrieval_apdu(request_message + HEADER_LENGTH,
+		    &request_buffer_length, 
+		    docid, 
+		    CT_byte,
+		    count * CHARS_PER_PAGE,
+		    ((count + 1) * CHARS_PER_PAGE <= document_length ?
+		             (count + 1) * CHARS_PER_PAGE :
+			     document_length),
+		    type,
+		    wais_database
+		    )) {
+#ifdef VMS
+		    HTAlert ("HTWAIS: Request too long.");
+		    return_status = HT_NOT_LOADED;
+		    FREE_TARGET;
+		    FREE(type);
+		    FREE(docid->bytes);
+		    goto CleanUp;
+#else
+		    panic("request too long");
+#endif /* VMS */
+		}
+	  
+	  /*	Actually do the transaction given by request_message */
+	  HTProgress("Fetching WAIS document...");
+	  rv = interpret_message (request_message, 
+				  MAX_MESSAGE_LEN - request_buffer_length, 
+				  response_message,
+				  MAX_MESSAGE_LEN,
+				  connection,
+				  false /* true verbose */	
+			         );
+
+	  if (rv == HT_INTERRUPTED)
+	    {
+		HTAlert ("Data transfer interrupted.");
+		return_status = HT_INTERRUPTED;
+		FREE_TARGET;
+		FREE(type);
+		FREE(docid->bytes);
+		goto CleanUp;
+	    }
+
+	  else if (!rv)
+	    {
+#ifdef VMS
+		HTAlert ("HTWAIS: Return message too large.");
+		return_status = HT_NOT_LOADED;
+		FREE_TARGET;
+		FREE(type);
+		FREE(docid->bytes);
+		goto CleanUp;
+#else
+	        panic("Returned message too large");
+#endif /* VMS */
+	    }
+
+	  /* 	Parse the result which came back into memory.
+	  */
+	  readSearchResponseAPDU(&retrieval_response, 
+				 response_message + HEADER_LENGTH);
+
+	  if(NULL == ((WAISSearchResponse *)
+	  	retrieval_response->DatabaseDiagnosticRecords)->Text){
+		/* display_search_response(target, retrieval_response,
+					wais_database, keywords); */
+		PUTS("No text was returned!\n");
+		/* panic("No text was returned"); */
+	  } else {
+	  
+		output_text_record(target,
+		   ((WAISSearchResponse *)
+		    retrieval_response->DatabaseDiagnosticRecords)->Text[0],
+		false, binary);
+	  
+	  } /* If text existed */
+	  
+#ifdef VMS
+	  FREE(type);
+#endif /* VMS */
+	}	/* Loop over slices */
+
+	FREE_TARGET;
+	FREE(docid->bytes);
+
+	freeWAISSearchResponse( retrieval_response->DatabaseDiagnosticRecords); 
+	freeSearchResponseAPDU( retrieval_response);
+
+    } /* If document rather than search */
+
+
+
+CleanUp:
+/*	(This postponed until later,  after a timeout:)
+*/
+#ifdef VMS
+    if (connection)
+        NETCLOSE((int)connection);
+#else
+    if (connection)
+        fclose(connection);
+#endif /* VMS */
+    FREE(wais_database);
+#ifdef VMS
+    FREE(request_message);
+    FREE(response_message);
+#else
+    s_free(request_message);
+    s_free(response_message);
+#endif /* VMS */
+    FREE(names);
+    return (return_status);
+}
+
+#ifdef GLOBALDEF_IS_MACRO
+#define _HTWAIS_C_1_INIT { "wais", HTLoadWAIS, NULL }
+GLOBALDEF(HTProtocol, HTWAIS, _HTWAIS_C_1_INIT);
+#else
+GLOBALDEF PUBLIC HTProtocol HTWAIS = { "wais", HTLoadWAIS, NULL };
+#endif /* GLOBALDEF_IS_MACRO */
diff --git a/WWW/Library/Implementation/HTWAIS.h b/WWW/Library/Implementation/HTWAIS.h
new file mode 100644
index 00000000..4aa885dc
--- /dev/null
+++ b/WWW/Library/Implementation/HTWAIS.h
@@ -0,0 +1,44 @@
+/*                                                    WAIS protocol module for the W3 library
+                                 WAIS PROTOCOL INTERFACE
+                                             
+   This module does not actually perform the WAIS protocol directly, but it does using one
+   or more libraries of the freeWAIS distribution. The ui.a library came with the old free
+   WAIS from TMC,  the client.a and wais.a libraries are needed from the freeWAIS from
+   CNIDR.
+   
+   If you include this module in the library, you must also
+   
+      Register the HTWAIS protocol at initialisation (e.g. HTInit or HTSInit) by compiling
+      it with -DDIRECT_WAIS
+      
+      Link with the WAIS libraries
+      
+   The wais source files are parsed by a separate and independent module, HTWSRC .   You
+   can include HTWSRC without including direct wais using this module, and your WWW code
+   will be able to read source files, and access WAIS indexes through a gateway.
+   
+   A WAIS-WWW gateway is just a normal W3 server with a libwww compiled with this module.
+   
+   Anyways, this interface won't change much:
+   
+ */
+#ifndef HTWAIS_H
+#define HTWAIS_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+#include "HTAccess.h"
+
+#ifdef GLOBALREF_IS_MACRO
+extern GLOBALREF(HTProtocol, HTWAIS);
+#else
+GLOBALREF HTProtocol HTWAIS;
+#endif /* GLOBALDEF_IS_MACRO */
+
+#endif /* HTWAIS_H */
+
+/*
+                                                                  Tim BL
+                                                                            
+*/
diff --git a/WWW/Library/Implementation/HTWSRC.c b/WWW/Library/Implementation/HTWSRC.c
new file mode 100644
index 00000000..cca6394d
--- /dev/null
+++ b/WWW/Library/Implementation/HTWSRC.c
@@ -0,0 +1,478 @@
+/*			Parse WAIS Source file			HTWSRC.c
+**			======================
+**
+**	This module parses a stream with WAIS source file
+**	format information on it and creates a structured stream.
+**	That structured stream is then converted into whatever.
+**
+**	3 June 93	Bug fix: Won't crash if no description
+*/
+
+#include "HTUtils.h"
+#include "tcp.h"
+
+#include "HTWSRC.h"
+
+
+/* #include <sys/types.h>	already in tcp.h */
+/* #include <sys/stat.h>  	this too         */
+/* #include <stdio.h> included in HTUtils.h -- FM */
+#include "HTML.h"
+#include "HTParse.h"
+
+#include "LYLeaks.h"
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+#define BIG 10000		/* Arbitrary limit to value length */
+#define PARAM_MAX BIG
+#define CACHE_PERIOD (7*86400)	/* Time to keep .src file in seconds */
+
+#define HEX_ESCAPE '%'
+
+struct _HTStructured {
+	CONST HTStructuredClass *	isa;
+	/* ... */
+};
+
+#define PUTC(c) (*me->target->isa->put_character)(me->target, c)
+#define PUTS(s) (*me->target->isa->put_string)(me->target, s)
+#define START(e) (*me->target->isa->start_element)(me->target, e, 0, 0, 0)
+#define END(e) (*me->target->isa->end_element)(me->target, e, 0)
+
+
+/*	Here are the parameters which can be specified in a  source file
+*/
+PRIVATE CONST char* par_name[] = {
+	"version", 
+	"ip-address",
+#define PAR_IP_NAME 2
+	"ip-name", 
+#define PAR_TCP_PORT 3
+	"tcp-port", 
+#define PAR_DATABASE_NAME 4
+	"database-name",
+#define PAR_COST 5
+	"cost", 
+#define PAR_COST_UNIT 6
+	"cost-unit", 
+#define PAR_FREE 7
+	"free",	
+#define PAR_MAINTAINER 8
+	"maintainer", 	
+#define PAR_DESCRIPTION 9
+	"description",
+	"keyword-list", 	
+	"source",
+	"window-geometry",
+	"configuration",
+	"script",
+	"update-time",
+	"contact-at",
+	"last-contacted",
+	"confidence",
+	"num-docs-to-request",
+	"font",
+	"font-size",
+#define PAR_UNKNOWN 22
+	"unknown",
+	0,				/* Terminate list */
+#define PAR_COUNT 23
+} ;
+
+
+enum tokenstate { beginning, before_tag, colon, before_value,
+		value, bracketed_value, quoted_value, escape_in_quoted, done };
+
+
+/*		Stream Object
+**		------------
+**
+**	The target is the structured stream down which the
+**	parsed results will go.
+**
+**	all the static stuff below should go in here to make it reentrant
+*/
+
+struct _HTStream {
+	CONST HTStreamClass *	isa;
+	HTStructured *		target;
+	char *			par_value[PAR_COUNT];
+	enum tokenstate 	state;
+	char 			param[BIG+1];
+	int			param_number;
+	int			param_count;
+};
+
+
+
+
+PUBLIC CONST char * hex = "0123456789ABCDEF";
+
+/*	Decode one hex character
+*/
+
+PUBLIC char from_hex ARGS1(char, c)
+{
+    return 		  (c>='0')&&(c<='9') ? c-'0'
+			: (c>='A')&&(c<='F') ? c-'A'+10
+			: (c>='a')&&(c<='f') ? c-'a'+10
+			:		       0;
+}
+
+
+/*			State machine
+**			-------------
+**
+** On entry,
+**	me->state	is a valid state (see WSRC_init)
+**	c		is the next character
+** On exit,
+** 	returns	1	Done with file
+**		0	Continue. me->state is updated if necessary.
+**		-1	Syntax error error
+*/
+
+
+/*		Treat One Character
+**		-------------------
+*/
+PRIVATE void WSRCParser_put_character ARGS2(HTStream*, me, char, c)
+{
+    switch (me->state) {
+    case beginning:
+        if (c=='(') me->state = before_tag;
+	break;
+	
+    case before_tag:
+        if (c==')') {
+	    me->state = done;
+	    return;			/* Done with input file */
+	} else if (c==':') {
+	    me->param_count = 0;
+	    me->state = colon;
+	}				/* Ignore other text */
+	break;
+
+    case colon:
+        if (WHITE(c)) {
+	    me->param[me->param_count++] = 0;	/* Terminate */
+	    for(me->param_number = 0; par_name[me->param_number]; me->param_number++) {
+		if (0==strcmp(par_name[me->param_number], me->param)) {
+		    break;
+		}
+	    }
+	    if (!par_name[me->param_number]) {	/* Unknown field */
+	        if (TRACE) fprintf(stderr,
+		    "HTWSRC: Unknown field `%s' in source file\n",
+		    me->param);
+		me->param_number = PAR_UNKNOWN;
+		me->state = before_value;	/* Could be better ignore */
+		return;
+	    }
+	    me->state = before_value;
+	} else {
+	    if (me->param_count < PARAM_MAX)  me->param[me->param_count++] = c;
+	}
+	break;
+	
+    case before_value:
+        if (c==')') {
+	    me->state = done;
+	    return;			/* Done with input file */
+	}
+	if (WHITE(c)) return;		/* Skip white space */
+	me->param_count = 0;
+	if (c=='"') {
+	    me->state = quoted_value;
+	    break;
+	}
+	me->state = (c=='"') ? quoted_value : 
+		    (c=='(') ? bracketed_value : value;
+	me->param[me->param_count++] = c;	/* Don't miss first character */
+	break;
+
+    case value:
+        if (WHITE(c)) {
+	    me->param[me->param_count] = 0;
+	    StrAllocCopy(me->par_value[me->param_number], me->param);
+	    me->state = before_tag;
+	} else {
+	    if (me->param_count < PARAM_MAX)  me->param[me->param_count++] = c;
+	}
+	break;
+
+    case bracketed_value:
+        if (c==')') {
+	    me->param[me->param_count] = 0;
+	    StrAllocCopy(me->par_value[me->param_number], me->param);
+	    me->state = before_tag;
+	    break;
+	}
+        if (me->param_count < PARAM_MAX)  me->param[me->param_count++] = c;
+	break;
+	
+    case quoted_value:
+        if (c=='"') {
+	    me->param[me->param_count] = 0;
+	    StrAllocCopy(me->par_value[me->param_number], me->param);
+	    me->state = before_tag;
+	    break;
+	}
+	
+	if (c=='\\') {		/* Ignore escape but switch state */
+	    me->state = escape_in_quoted;
+	    break;
+	}
+	/* Fall through! */
+
+    case escape_in_quoted:
+        if (me->param_count < PARAM_MAX)  me->param[me->param_count++] = c;
+	me->state = quoted_value;
+	break;
+	
+    case done:				/* Ignore anything after EOF */
+	return;
+
+    } /* switch me->state */
+}
+
+
+/*			Open Cache file
+**			===============
+**
+**   Bugs: Maybe for filesystem-challenged platforms (MSDOS for example) we
+**   should make a hash code for the filename.
+*/
+
+#ifdef CACHE_FILE_PREFIX
+PRIVATE BOOL write_cache ARGS1(HTStream *, me)
+{
+    FILE * fp;
+    char cache_file_name[256];
+    char * www_database;
+    if (!me->par_value[PAR_DATABASE_NAME]
+    	|| !me->par_value[PAR_IP_NAME]
+	) return NO;
+    
+    www_database = HTEscape(me->par_value[PAR_DATABASE_NAME], URL_XALPHAS);
+    sprintf(cache_file_name, "%sWSRC-%s:%s:%.100s.txt",
+    	CACHE_FILE_PREFIX,
+	me->par_value[PAR_IP_NAME],
+	me->par_value[PAR_TCP_PORT] ? me->par_value[PAR_TCP_PORT] : "210",
+	www_database);
+    FREE(www_database);
+    fp = fopen(cache_file_name, "w");
+    if (!fp) return NO;
+    
+    if (me->par_value[PAR_DESCRIPTION])
+        fputs(me->par_value[PAR_DESCRIPTION], fp);
+    else 
+        fputs("Description not available\n", fp);
+    fclose(fp);
+    return YES;
+}
+#endif
+
+/*			Output equivalent HTML
+**			----------------------
+**
+*/
+
+void give_parameter ARGS2(HTStream *, me, int, p)
+{
+    PUTS(par_name[p]);
+    if (me->par_value[p]) {
+	PUTS(": ");
+	PUTS(me->par_value[p]);
+	PUTS("; ");
+    } else {
+        PUTS(" NOT GIVEN in source file; ");
+    }
+}
+
+
+/*			Generate Outout
+**			===============
+*/
+PRIVATE void WSRC_gen_html ARGS2(HTStream *, me, BOOL, source_file)
+
+{
+    if (me->par_value[PAR_DATABASE_NAME]) {
+	char * shortname = 0;
+	int l;
+	StrAllocCopy(shortname, me->par_value[PAR_DATABASE_NAME]);
+	l = strlen(shortname);
+	if ( l > 4 && !strcasecomp(shortname + l -4, ".src")) {
+	    shortname[l-4] = 0;	/* Chop of .src -- boring! */
+	}
+	
+	START(HTML_HEAD);
+	PUTS("\n");
+	START(HTML_TITLE);
+	PUTS(shortname);
+	PUTS(source_file ? " WAIS source file" : " index");
+	END(HTML_TITLE);
+	PUTS("\n");
+	END(HTML_HEAD);
+    
+	START(HTML_H1);
+	PUTS(shortname);
+	PUTS(source_file ? " description" : " index");
+	END(HTML_H1);
+	PUTS("\n");
+	FREE(shortname);
+    }
+    
+    START(HTML_DL);		/* Definition list of details */
+    
+    if (source_file) {
+	START(HTML_DT);
+	PUTS("Access links");
+	START(HTML_DD);
+	if (me->par_value[PAR_IP_NAME] &&
+	    me->par_value[PAR_DATABASE_NAME]) {
+    
+	    char WSRC_address[256];
+	    char * www_database;
+	    www_database = HTEscape(me->par_value[PAR_DATABASE_NAME],
+	    	URL_XALPHAS);
+	    sprintf(WSRC_address, "wais://%s%s%s/%s",
+		me->par_value[PAR_IP_NAME],
+		me->par_value[PAR_TCP_PORT] ? ":" : "",
+		me->par_value[PAR_TCP_PORT] ? me->par_value[PAR_TCP_PORT] :"",
+		www_database);
+	
+	    HTStartAnchor(me->target, NULL, WSRC_address);
+	    PUTS("Direct access");
+	    END(HTML_A);
+	    /** Proxy will be used if defined, so let user know that - FM **/
+	    PUTS(" (or via proxy server, if defined), or");
+	    START(HTML_BR);
+	    /** Offer W3 Consortium gateway - FM **/
+	    sprintf(WSRC_address, "http://www.w3.org:8001/%s%s%s/%s",
+		me->par_value[PAR_IP_NAME],
+		me->par_value[PAR_TCP_PORT] ? ":" : "",
+		me->par_value[PAR_TCP_PORT] ? me->par_value[PAR_TCP_PORT] :"",
+		www_database);
+	    HTStartAnchor(me->target, NULL, WSRC_address);
+	    PUTS("through W3 Consortium gateway");
+	    END(HTML_A);
+
+	    FREE(www_database);
+	    
+	} else {
+	    give_parameter(me, PAR_IP_NAME);
+	    give_parameter(me, PAR_DATABASE_NAME);
+	}
+    
+    } /* end if source_file */
+    
+    if (me->par_value[PAR_MAINTAINER]) {
+	START(HTML_DT);
+	PUTS("Maintainer");
+	START(HTML_DD);
+	PUTS(me->par_value[PAR_MAINTAINER]);
+    }
+    if (me->par_value[PAR_IP_NAME]) {
+    	START(HTML_DT);
+    	PUTS("Host");
+    	START(HTML_DD);
+    	PUTS(me->par_value[PAR_IP_NAME]);
+    }
+
+    END(HTML_DL);
+
+    if (me->par_value[PAR_DESCRIPTION]) {
+	START(HTML_PRE);		/* Preformatted description */
+	PUTS(me->par_value[PAR_DESCRIPTION]);
+	END(HTML_PRE);
+    }
+    
+    (*me->target->isa->_free)(me->target);
+    
+    return;
+} /* generate html */
+
+
+PRIVATE void WSRCParser_put_string ARGS2(HTStream *, context, CONST char*, str)
+{
+    CONST char *p;
+    for(p=str; *p; p++)
+        WSRCParser_put_character(context, *p);
+}
+
+
+PRIVATE void WSRCParser_write ARGS3(
+		HTStream *, 	context,
+		CONST char*, 	str,
+		int, 		l)
+{
+    CONST char *p;
+    CONST char *e = str+l;
+    for(p=str; p<e; p++)
+        WSRCParser_put_character(context, *p);
+}
+
+
+PRIVATE void WSRCParser_free ARGS1(HTStream *, me)
+{
+    WSRC_gen_html(me, YES);
+#ifdef CACHE_FILE_PREFIX
+    write_cache(me);
+#endif
+    {
+	int p;
+	for (p = 0; par_name[p]; p++) {	/* Clear out old values */
+	    FREE(me->par_value[p]);
+	}
+    }
+    FREE(me);
+}
+
+PRIVATE void WSRCParser_abort ARGS2(HTStream *, me, HTError, e)
+{
+    WSRCParser_free(me);
+}
+
+
+/*		Stream subclass		-- method routines
+**		---------------
+*/
+
+HTStreamClass WSRCParserClass = {
+	"WSRCParser",
+	WSRCParser_free,
+	WSRCParser_abort,
+	WSRCParser_put_character,
+ 	WSRCParser_put_string,
+	WSRCParser_write
+
+};
+
+
+/*		Converter from WAIS Source to whatever
+**		--------------------------------------
+*/
+PUBLIC HTStream* HTWSRCConvert ARGS3(
+	HTPresentation *,	pres,
+	HTParentAnchor *,	anchor,	
+	HTStream *,		sink)
+{
+    HTStream * me = (HTStream*) malloc(sizeof(*me));
+    if (!me) outofmem(__FILE__, "HTWSRCConvert");
+
+    me->isa = &WSRCParserClass;
+    me->target = HTML_new(anchor, pres->rep_out, sink);
+
+    {
+	int p;
+	for(p=0; p < PAR_COUNT; p++) {	/* Clear out parameter values */
+	    me->par_value[p] = 0;
+	}
+    }
+    me->state = beginning;
+
+    return me;
+}
+
diff --git a/WWW/Library/Implementation/HTWSRC.h b/WWW/Library/Implementation/HTWSRC.h
new file mode 100644
index 00000000..2e247a67
--- /dev/null
+++ b/WWW/Library/Implementation/HTWSRC.h
@@ -0,0 +1,46 @@
+/*                                                             A parser for WAIS source files
+                                 WAIS SOURCE FILE PARSER
+                                             
+   This converter returns a stream object into which a WAIS source file can be written.
+   The result is put via a structured stream into whatever format was required for the
+   output stream.
+   
+   See also: HTWAIS protocol interface module
+   
+ */
+#ifndef HTWSRC_H
+#define HTWSRC_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+
+#include "HTFormat.h"
+
+extern  HTStream* HTWSRCConvert PARAMS((
+        HTPresentation *        pres,
+        HTParentAnchor *        anchor,
+        HTStream *              sink));
+
+/*
+
+Escaping Strings
+
+   HTDeSlash takes out the invlaid characters in a URL path ELEMENT by converting them
+   into hex-escaped characters. HTEnSlash does the reverse.
+   
+   Each returns a pointer to a newly allocated string which must eventually be freed by
+   the caller.
+   
+ */
+extern char * HTDeSlash PARAMS((CONST char * str));
+
+extern char * HTEnSlash PARAMS((CONST char * str));
+
+#endif
+
+/*
+
+                                                                                    Tim BL
+                                                                                          
+    */
diff --git a/WWW/Library/Implementation/HTWriter.c b/WWW/Library/Implementation/HTWriter.c
new file mode 100644
index 00000000..03a55648
--- /dev/null
+++ b/WWW/Library/Implementation/HTWriter.c
@@ -0,0 +1,189 @@
+/*		FILE WRITER			HTWrite.c
+**		===========
+**
+*/
+#include "HTUtils.h"
+#include "tcp.h"
+
+#include "HTWriter.h"
+
+#define BUFFER_SIZE 4096	/* Tradeoff */
+
+/*#include <stdio.h> included by HTUtils.h -- FM */
+
+#include "LYLeaks.h"
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+/*		HTML Object
+**		-----------
+*/
+
+struct _HTStream {
+	CONST HTStreamClass *	isa;
+
+	int	soc;
+	char	*write_pointer;
+	char 	buffer[BUFFER_SIZE];
+#ifdef NOT_ASCII
+	BOOL	make_ascii;	/* Are we writing to the net? */
+#endif
+};
+
+
+/*	Write the buffer out to the socket
+**	----------------------------------
+*/
+
+PRIVATE void flush ARGS1(HTStream *, me)
+{
+    char *read_pointer 	= me->buffer;
+    char *write_pointer = me->write_pointer;
+
+#ifdef NOT_ASCCII
+    if (me->make_ascii) {
+    	char * p;
+	for(p = me->buffer; p < me->write_pointer; p++)
+	    *p = TOASCII(*p);
+    }
+#endif
+    while (read_pointer < write_pointer) {
+        int status;
+	status = NETWRITE(me->soc, me->buffer,  /* Put timeout? @@@ */
+			write_pointer - read_pointer);
+	if (status<0) {
+	    if(TRACE) fprintf(stderr,
+	    "HTWrite: Error: write() on socket returns %d !!!\n", status);
+	    return;
+	}
+	read_pointer = read_pointer + status;
+    }
+    me->write_pointer = me->buffer;
+}
+
+
+/*_________________________________________________________________________
+**
+**			A C T I O N 	R O U T I N E S
+*/
+
+/*	Character handling
+**	------------------
+*/
+
+PRIVATE void HTWriter_put_character ARGS2(HTStream *, me, char, c)
+{
+    if (me->write_pointer == &me->buffer[BUFFER_SIZE]) flush(me);
+    *me->write_pointer++ = c;
+}
+
+
+
+/*	String handling
+**	---------------
+**
+**	Strings must be smaller than this buffer size.
+*/
+PRIVATE void HTWriter_put_string ARGS2(HTStream *, me, CONST char*, s)
+{
+    int l = strlen(s);
+    if (me->write_pointer + l > &me->buffer[BUFFER_SIZE]) flush(me);
+    strcpy(me->write_pointer, s);
+    me->write_pointer = me->write_pointer + l;
+}
+
+
+/*	Buffer write.  Buffers can (and should!) be big.
+**	------------
+*/
+PRIVATE void HTWriter_write ARGS3(HTStream *, me, CONST char*, s, int, l)
+{
+ 
+    CONST char *read_pointer 	= s;
+    CONST char *write_pointer = s+l;
+
+    flush(me);		/* First get rid of our buffer */
+
+    while (read_pointer < write_pointer) {
+        int status = NETWRITE(me->soc, (char *)read_pointer,
+			write_pointer - read_pointer);
+	if (status<0) {
+	    if(TRACE) fprintf(stderr,
+	    "HTWriter_write: Error on socket output stream!!!\n");
+	    return;
+	}
+	read_pointer = read_pointer + status;
+    }
+}
+
+
+
+
+/*	Free an HTML object
+**	-------------------
+**
+**	Note that the SGML parsing context is freed, but the created object is not,
+**	as it takes on an existence of its own unless explicitly freed.
+*/
+PRIVATE void HTWriter_free ARGS1(HTStream *, me)
+{
+    flush(me);
+    NETCLOSE(me->soc);
+    FREE(me);
+}
+
+PRIVATE void HTWriter_abort ARGS2(HTStream *, me, HTError, e)
+{
+    HTWriter_free(me);
+}
+
+
+/*	Structured Object Class
+**	-----------------------
+*/
+PRIVATE CONST HTStreamClass HTWriter = /* As opposed to print etc */
+{		
+	"SocketWriter",
+	HTWriter_free,
+	HTWriter_abort,
+	HTWriter_put_character, HTWriter_put_string,
+	HTWriter_write
+}; 
+
+
+/*	Subclass-specific Methods
+**	-------------------------
+*/
+
+PUBLIC HTStream* HTWriter_new ARGS1(int, soc)
+{
+    HTStream* me = (HTStream*)malloc(sizeof(*me));
+    if (me == NULL) outofmem(__FILE__, "HTML_new");
+    me->isa = &HTWriter;       
+    
+#ifdef NOT_ASCII
+    me->make_ascii = NO;
+#endif    
+    me->soc = soc;
+    me->write_pointer = me->buffer;
+    return me;
+}
+
+/*	Subclass-specific Methods
+**	-------------------------
+*/
+
+PUBLIC HTStream* HTASCIIWriter ARGS1(int, soc)
+{
+    HTStream* me = (HTStream*)malloc(sizeof(*me));
+    if (me == NULL) outofmem(__FILE__, "HTML_new");
+    me->isa = &HTWriter;       
+
+#ifdef NOT_ASCII
+    me->make_ascii = YES;
+#endif    
+    me->soc = soc;
+    me->write_pointer = me->buffer;
+    return me;
+}
+
diff --git a/WWW/Library/Implementation/HTWriter.h b/WWW/Library/Implementation/HTWriter.h
new file mode 100644
index 00000000..9fa5f8c6
--- /dev/null
+++ b/WWW/Library/Implementation/HTWriter.h
@@ -0,0 +1,28 @@
+/*  */
+
+/*              Unix File or Socket Writer                      HTWriter.c
+**              --------------------------
+**
+**      This version of the stream object just writes to a socket.
+**      The socket is assumed open and closed afterward.
+**
+**      There are two versions (identical on ASCII machines)
+**      one of which converts to ASCII on output.
+**
+**      Bugs:
+**              strings written must be less than buffer size.
+*/
+
+#ifndef HTWRITE_H
+#define HTWRITE_H
+
+#include "HTStream.h"
+
+extern HTStream * HTWriter_new PARAMS((int soc));
+
+extern HTStream * HTASCIIWriter PARAMS((int soc));
+
+#endif
+/*
+
+    */
diff --git a/WWW/Library/Implementation/HText.h b/WWW/Library/Implementation/HText.h
new file mode 100644
index 00000000..7b65751a
--- /dev/null
+++ b/WWW/Library/Implementation/HText.h
@@ -0,0 +1,262 @@
+/*                                                           Rich Hypertext object for libWWW
+                                  RICH HYPERTEXT OBJECT
+                                             
+ */
+
+/*
+
+   This is the C interface to the Objective-C (or whatever) Style-oriented HyperText
+   class. It is used when a style-oriented text object is available or craeted in order to
+   display hypertext.
+   
+ */
+#ifndef HTEXT_H
+#define HTEXT_H
+#include "HTAnchor.h"
+#include "HTStyle.h"
+#include "HTStream.h"
+#include "SGML.h"
+
+#ifdef SHORT_NAMES
+#define HTMainText                      HTMaText
+#define HTMainAnchor                    HtMaAnch
+#define HText_new                       HTHTNew
+#define HText_new2                      HTHTNew2
+#define HText_free                      HTHTFree
+#define HText_beginAppend               HTHTBeAp
+#define HText_endAppend                 HTHTEnAp
+#define HText_setStyle                  HTHTSeSt
+#define HText_appendCharacter           HTHTApCh
+#define HText_appendImage               HTHTApIm
+#define HText_appendText                HTHTApTe
+#define HText_appendParagraph           HTHTApPa
+#define HText_beginAnchor               HTHTBeAn
+#define HText_endAnchor                 HTHTEnAn
+#define HText_dump                      HTHTDump
+#define HText_nodeAnchor                HTHTNoAn
+#define HText_select                    HTHTSele
+#define HText_selectAnchor              HTHTSeAn
+#define HText_applyStyle                HTHTApSt
+#define HText_updateStyle               HTHTUpSt
+#define HText_selectionStyle            HTHTStyl
+#define HText_replaceSel                HTHTRepl
+#define HText_applyToSimilar            HTHTApTo
+#define HText_selectUnstyled            HTHTSeUn
+#define HText_unlinkSelection           HTHTUnSe
+#define HText_linkSelTo                 HTHTLiSe
+#define HText_referenceSelected         HTHTRefS
+#endif
+
+#ifndef THINK_C
+#ifndef HyperText               /* Objective C version defined HyperText */
+typedef struct _HText HText;    /* Normal Library */
+#endif
+#else
+class CHyperText;               /* Mac Think-C browser hook */
+typedef CHyperText HText;
+#endif
+
+extern HText * HTMainText;              /* Pointer to current main text */
+extern HTParentAnchor * HTMainAnchor;   /* Pointer to current text's anchor */
+
+/*
+
+Creation and deletion
+
+  HTEXT_NEW: CREATE HYPERTEXT OBJECT
+  
+   There are several methods depending on how much you want to specify. The output stream
+   is used with objects which need to output the hypertext to a stream.  The structure is
+   for objects which need to refer to the structure which is kep by the creating stream.
+   
+ */
+ extern HText * HText_new PARAMS((HTParentAnchor * anchor));
+
+ extern HText * HText_new2 PARAMS((HTParentAnchor * anchor,
+                                HTStream * output_stream));
+
+ extern HText * HText_new3 PARAMS((HTParentAnchor * anchor,
+                                HTStream * output_stream,
+                                HTStructured * structure));
+
+/*
+
+  FREE HYPERTEXT OBJECT
+  
+ */
+extern void     HText_free PARAMS((HText * me));
+
+
+/*
+
+Object Building methods
+
+   These are used by a parser to build the text in an object HText_beginAppend must be
+   called, then any combination of other append calls, then HText_endAppend. This allows
+   optimised handling using buffers and caches which are flushed at the end.
+   
+ */
+extern void HText_beginAppend PARAMS((HText * text));
+
+extern void HText_endAppend PARAMS((HText * text));
+
+/*
+
+  SET THE STYLE FOR FUTURE TEXT
+  
+ */
+
+extern void HText_setStyle PARAMS((HText * text, HTStyle * style));
+
+/*
+
+  ADD ONE CHARACTER
+  
+ */
+extern void HText_appendCharacter PARAMS((HText * text, char ch));
+
+/*
+
+  ADD A ZERO-TERMINATED STRING
+  
+ */
+
+extern void HText_appendText PARAMS((HText * text, CONST char * str));
+
+/*
+
+  NEW PARAGRAPH
+  
+   and similar things
+   
+ */
+extern void HText_appendParagraph PARAMS((HText * text));
+
+extern void HText_appendLineBreak PARAMS((HText * text));
+
+extern void HText_appendHorizontalRule PARAMS((HText * text));
+
+
+
+/*
+
+  START/END SENSITIVE TEXT
+  
+ */
+
+/*
+
+   The anchor object is created and passed to HText_beginAnchor. The senstive text is
+   added to the text object, and then HText_endAnchor is called. Anchors may not be
+   nested.
+   
+ */
+extern void HText_beginAnchor PARAMS((HText * text, HTChildAnchor * anc));
+extern void HText_endAnchor PARAMS((HText * text));
+
+
+/*
+
+  APPEND AN INLINE IMAGE
+  
+   The image is handled by the creation of an anchor whose destination is the image
+   document to be included. The semantics is the intended inline display of the image.
+   
+   An alternative implementation could be, for example, to begin an anchor, append the
+   alternative text or "IMAGE", then end the anchor. This would simply generate some text
+   linked to the image itself as a separate document.
+   
+ */
+extern void HText_appendImage PARAMS((
+        HText *         text,
+        HTChildAnchor * anc,
+        CONST char *    alternative_text,
+        int             alignment,
+        BOOL            isMap));
+
+/*
+
+  DUMP DIAGNOSTICS TO STDERR
+  
+ */
+
+extern void HText_dump PARAMS((HText * me));
+
+/*
+
+  RETURN THE ANCHOR ASSOCIATED WITH THIS NODE
+  
+ */
+extern HTParentAnchor * HText_nodeAnchor PARAMS((HText * me));
+
+
+/*
+
+Browsing functions
+
+ */
+
+
+/*
+
+  BRING TO FRONT AND HIGHLIGHT IT
+  
+ */
+
+
+extern BOOL HText_select PARAMS((HText * text));
+extern BOOL HText_selectAnchor PARAMS((HText * text, HTChildAnchor* anchor));
+
+/*
+
+Editing functions
+
+   These are called from the application. There are many more functions not included here
+   from the orginal text object. These functions NEED NOT BE IMPLEMENTED in a browser
+   which cannot edit.
+   
+ */
+/*      Style handling:
+*/
+/*      Apply this style to the selection
+*/
+extern void HText_applyStyle PARAMS((HText * me, HTStyle *style));
+
+/*      Update all text with changed style.
+*/
+extern void HText_updateStyle PARAMS((HText * me, HTStyle *style));
+
+/*      Return style of  selection
+*/
+extern HTStyle * HText_selectionStyle PARAMS((
+        HText * me,
+        HTStyleSheet* sheet));
+
+/*      Paste in styled text
+*/
+extern void HText_replaceSel PARAMS((HText * me,
+        CONST char *aString,
+        HTStyle* aStyle));
+
+/*      Apply this style to the selection and all similarly formatted text
+**      (style recovery only)
+*/
+extern void HTextApplyToSimilar PARAMS((HText * me, HTStyle *style));
+
+/*      Select the first unstyled run.
+**      (style recovery only)
+*/
+extern void HTextSelectUnstyled PARAMS((HText * me, HTStyleSheet *sheet));
+
+
+/*      Anchor handling:
+*/
+extern void             HText_unlinkSelection PARAMS((HText * me));
+extern HTAnchor *       HText_referenceSelected PARAMS((HText * me));
+extern HTAnchor *       HText_linkSelTo PARAMS((HText * me, HTAnchor* anchor));
+
+
+#endif /* HTEXT_H */
+/*
+
+   end */
diff --git a/WWW/Library/Implementation/LYLeaks.h b/WWW/Library/Implementation/LYLeaks.h
new file mode 100644
index 00000000..4109be80
--- /dev/null
+++ b/WWW/Library/Implementation/LYLeaks.h
@@ -0,0 +1,149 @@
+#ifndef __LYLEAKS_H
+/*
+ *	Avoid include redundancy
+ *	Include only if finding memory leaks.
+ */
+#define __LYLEAKS_H
+
+/*
+ *	Copyright (c) 1994, University of Kansas, All Rights Reserved
+ *
+ *	Include File:	LYLeaks.h
+ *	Purpose:	Header to convert requests for allocation to Lynx
+ *				custom functions to track memory leaks.
+ *	Remarks/Portability/Dependencies/Restrictions:
+ *		For the stdlib.h allocation functions to be overriden by the
+ *			Lynx memory tracking functions all modules allocating,
+ *			freeing, or resizing memory must have LY_FIND_LEAKS
+ *			defined before including this file.
+ *		This header file should be included in every source file which
+ *			does any memory manipulation through use of the
+ *			stdlib.h memory functions.
+ *		For proper reporting of memory leaks, the function LYLeaks
+ *			should be registered for execution by atexit as the
+ *			very first executable statement in main.
+ *		This code is slow and should not be used except in debugging
+ *			circumstances (don't define LY_FIND_LEAKS).
+ *		If you are using LY_FIND_LEAKS and don't want the LYLeak*
+ *			memory functions to be used in a certain file,
+ *			define NO_MEMORY_TRACKING before including this file.
+ *		The only safe way to call the LYLeak* functions is to use
+ *			the below macros because they depend on the static
+ *			string created by __FILE__ to not be dynamic in
+ *			nature (don't free it and assume will exist at all
+ *			times during execution).
+ *	Revision History:
+ *		05-26-94	created for Lynx 2-3-1, Garrett Arch Blythe
+ */
+
+/*
+ *	Required includes
+ */
+#include <stdlib.h>
+#include "HTUtils.h"
+
+/*
+ *	Constant defines
+ */
+#define MAX_CONTENT_LENGTH 50
+#ifdef VMS
+#define LEAKAGE_SINK "sys$login:Lynx.leaks"
+#else
+#define LEAKAGE_SINK "Lynx.leaks"
+#endif /* VMS */
+
+/*
+ *	Data structures
+ */
+typedef struct SourceLocation_tag	{
+	/*
+	 *	The file name and line number of where an event took place.
+	 */
+	CONST char *cp_FileName;
+	short ssi_LineNumber;
+}
+SourceLocation;
+
+typedef struct AllocationList_tag	{
+	/*
+	 *	A singly linked list.
+	 */
+	struct AllocationList_tag *ALp_Next;
+
+	/*
+	 *	The memory pointer allocated.
+	 *	If set to NULL, then an invalid request was made.
+	 *	The invalid pointer also.
+	 */
+	void *vp_Alloced;
+	void *vp_BadRequest;
+
+	/*
+	 *	The size in bytes of the allocated memory.
+	 */
+	size_t st_Bytes;
+
+	/*
+	 *	The source location of specific event (calloc, malloc, free).
+	 *	realloc kept separate since will track last realloc on pointer.
+	 */
+	SourceLocation SL_memory;
+	SourceLocation SL_realloc;
+}
+AllocationList;
+
+/*
+ *	Global variable declarations
+ */
+
+/*
+ *	Macros
+ */
+#if defined(LY_FIND_LEAKS) && !defined(NO_MEMORY_TRACKING)
+/*
+ *	Only use these macros if we are to track memory allocations.
+ *	The reason for using a macro instead of a define is that we want
+ *		to track where the initial allocation took place or where
+ *		the last reallocation took place.
+ *	Track where the allocation took place by the __FILE__ and __LINE__
+ *		defines which are automatic to the compiler.
+ */
+#ifdef malloc
+#undef malloc
+#endif /* malloc */
+#define malloc(st_bytes) LYLeakMalloc(st_bytes, __FILE__, __LINE__)
+
+#ifdef calloc
+#undef calloc
+#endif /* calloc */
+#define calloc(st_number, st_bytes) LYLeakCalloc(st_number, st_bytes, \
+	__FILE__, __LINE__)
+
+#ifdef realloc
+#undef realloc
+#endif /* realloc */
+#define realloc(vp_alloced, st_newbytes) LYLeakRealloc(vp_alloced, \
+	st_newbytes, __FILE__, __LINE__)
+
+#ifdef free
+#undef free
+#endif /* free */
+#define free(vp_alloced) LYLeakFree(vp_alloced, __FILE__, __LINE__)
+
+#endif /* LY_FIND_LEAKS && !NO_MEMORY_TRACKING */
+
+/*
+ *	Function declarations
+ *	See the appropriate source file for usage.
+ */
+PUBLIC void LYLeaks NOPARAMS;
+PUBLIC void *LYLeakMalloc PARAMS((size_t st_bytes, CONST char *cp_File, CONST
+	short ssi_Line));
+PUBLIC void *LYLeakCalloc PARAMS((size_t st_number, size_t st_bytes, CONST char
+	*cp_File, CONST short ssi_Line));
+PUBLIC void *LYLeakRealloc PARAMS((void *vp_alloced, size_t st_newbytes, CONST
+	char *cp_File, CONST short ssi_Line));
+PUBLIC void LYLeakFree PARAMS((void *vp_alloced, CONST char *cp_File, CONST
+	short ssi_Line));
+
+#endif /* __LYLEAKS_H */
diff --git a/WWW/Library/Implementation/LYexit.h b/WWW/Library/Implementation/LYexit.h
new file mode 100644
index 00000000..4d7ca529
--- /dev/null
+++ b/WWW/Library/Implementation/LYexit.h
@@ -0,0 +1,54 @@
+#ifndef __LYEXIT_H
+/*
+ *	Avoid include redundancy
+ */
+#define __LYEXIT_H
+
+/*
+ *	Copyright (c) 1994, University of Kansas, All Rights Reserved
+ *
+ *	Include File:	LYexit.h
+ *	Purpose:	Provide an atexit function for libraries without such.
+ *	Remarks/Portability/Dependencies/Restrictions:
+ *		Include this header in every file that you have an exit or
+ *			atexit statment.
+ *	Revision History:
+ *		06-15-94	created Lynx 2-3-1 Garrett Arch Blythe
+ */
+
+/*
+ *	Required includes
+ */
+#include <stdlib.h>
+#include "HTUtils.h"
+
+/*
+ *	Constant defines
+ */
+#define exit LYexit
+#define atexit LYatexit
+#define ATEXITSIZE 32
+
+/*
+ *	Data structures
+ */
+
+/*
+ *	Global variable declarations
+ */
+
+/*
+ *	Macros
+ */
+
+/*
+ *	Function declarations
+ */
+extern void LYexit PARAMS((int status));
+#ifdef __STDC__
+extern int LYatexit(void (*function)());
+#else
+extern int LYatexit();
+#endif /* __STDC__ */
+
+#endif /* __LYEXIT_H */
diff --git a/WWW/Library/Implementation/Makefile b/WWW/Library/Implementation/Makefile
new file mode 100644
index 00000000..7f028976
--- /dev/null
+++ b/WWW/Library/Implementation/Makefile
@@ -0,0 +1,489 @@
+# Makefile generated by imake - do not edit!
+# $XConsortium: imake.c,v 1.51 89/12/12 12:37:30 jim Exp $
+#
+# The cpp used on this machine replaces all newlines and multiple tabs and
+# spaces in a macro expansion with a single space.  Imake tries to compensate
+# for this, but is not always successful.
+#
+
+###########################################################################
+# Makefile generated from "Imake.tmpl" and </tmp/IIf.a00214>
+# $XConsortium: Imake.tmpl,v 1.77 89/12/18 17:01:37 jim Exp $
+#
+# Platform-specific parameters may be set in the appropriate .cf
+# configuration files.  Site-wide parameters may be set in the file
+# site.def.  Full rebuilds are recommended if any parameters are changed.
+#
+# If your C preprocessor doesn't define any unique symbols, you'll need
+# to set BOOTSTRAPCFLAGS when rebuilding imake (usually when doing
+# "make Makefile", "make Makefiles", or "make World").
+#
+# If you absolutely can't get imake to work, you'll need to set the
+# variables at the top of each Makefile as well as the dependencies at the
+# bottom (makedepend will do this automatically).
+#
+
+###########################################################################
+# platform-specific configuration parameters - edit sun.cf to change
+
+# platform:  $XConsortium: sun.cf,v 1.38 89/12/23 16:10:10 jim Exp $
+# operating system:  SunOS 4.0.3
+
+###########################################################################
+# site-specific configuration parameters - edit site.def to change
+
+            SHELL = /bin/sh
+
+              TOP = .
+      CURRENT_DIR = .
+
+               AR = ar clq
+  BOOTSTRAPCFLAGS =
+               CC = cc
+
+         COMPRESS = compress
+              CPP = /lib/cpp $(STD_CPP_DEFINES)
+    PREPROCESSCMD = cc -E $(STD_CPP_DEFINES)
+          INSTALL = install
+               LD = ld
+             LINT = lint
+      LINTLIBFLAG = -C
+         LINTOPTS = -axz
+               LN = ln -s
+             MAKE = make
+               MV = mv
+               CP = cp
+           RANLIB = ranlib
+  RANLIBINSTFLAGS =
+               RM = rm -f
+     STD_INCLUDES =
+  STD_CPP_DEFINES =
+      STD_DEFINES =
+ EXTRA_LOAD_FLAGS =
+  EXTRA_LIBRARIES =
+             TAGS = ctags
+
+    SHAREDCODEDEF = -DSHAREDCODE
+         SHLIBDEF = -DSUNSHLIB
+
+    PROTO_DEFINES =
+
+     INSTPGMFLAGS =
+
+     INSTBINFLAGS = -m 0755
+     INSTUIDFLAGS = -m 4755
+     INSTLIBFLAGS = -m 0664
+     INSTINCFLAGS = -m 0444
+     INSTMANFLAGS = -m 0444
+     INSTDATFLAGS = -m 0444
+    INSTKMEMFLAGS = -m 4755
+
+          DESTDIR =
+
+     TOP_INCLUDES = -I$(INCROOT)
+
+      CDEBUGFLAGS = -O
+        CCOPTIONS =
+      COMPATFLAGS =
+
+      ALLINCLUDES = $(STD_INCLUDES) $(TOP_INCLUDES) $(INCLUDES) $(EXTRA_INCLUDES)
+       ALLDEFINES = $(ALLINCLUDES) $(STD_DEFINES) $(PROTO_DEFINES) $(DEFINES) $(COMPATFLAGS)
+           CFLAGS = $(CDEBUGFLAGS) $(CCOPTIONS) $(ALLDEFINES)
+        LINTFLAGS = $(LINTOPTS) -DLINT $(ALLDEFINES)
+           LDLIBS = $(SYS_LIBRARIES) $(EXTRA_LIBRARIES)
+        LDOPTIONS = $(CDEBUGFLAGS) $(CCOPTIONS)
+   LDCOMBINEFLAGS = -X -r
+
+        MACROFILE = sun.cf
+           RM_CMD = $(RM) *.CKP *.ln *.BAK *.bak *.o core errs ,* *~ *.a .emacs_* tags TAGS make.log MakeOut
+
+    IMAKE_DEFINES =
+
+         IRULESRC = $(CONFIGDIR)
+        IMAKE_CMD = $(IMAKE) -DUseInstalled -I$(IRULESRC) $(IMAKE_DEFINES)
+
+     ICONFIGFILES = $(IRULESRC)/Imake.tmpl $(IRULESRC)/Imake.rules \
+			$(IRULESRC)/Project.tmpl $(IRULESRC)/site.def \
+			$(IRULESRC)/$(MACROFILE) $(EXTRA_ICONFIGFILES)
+
+###########################################################################
+# X Window System Build Parameters
+# $XConsortium: Project.tmpl,v 1.63 89/12/18 16:46:44 jim Exp $
+
+###########################################################################
+# X Window System make variables; this need to be coordinated with rules
+# $XConsortium: Project.tmpl,v 1.63 89/12/18 16:46:44 jim Exp $
+
+          PATHSEP = /
+        USRLIBDIR = $(DESTDIR)/usr/lib
+           BINDIR = $(DESTDIR)/usr/bin/X11
+          INCROOT = $(DESTDIR)/usr/include
+     BUILDINCROOT = $(TOP)
+      BUILDINCDIR = $(BUILDINCROOT)/X11
+      BUILDINCTOP = ..
+           INCDIR = $(INCROOT)/X11
+           ADMDIR = $(DESTDIR)/usr/adm
+           LIBDIR = $(USRLIBDIR)/X11
+        CONFIGDIR = $(LIBDIR)/config
+       LINTLIBDIR = $(USRLIBDIR)/lint
+
+          FONTDIR = $(LIBDIR)/fonts
+         XINITDIR = $(LIBDIR)/xinit
+           XDMDIR = $(LIBDIR)/xdm
+           AWMDIR = $(LIBDIR)/awm
+           TWMDIR = $(LIBDIR)/twm
+           GWMDIR = $(LIBDIR)/gwm
+          MANPATH = $(DESTDIR)/usr/man
+    MANSOURCEPATH = $(MANPATH)/man
+           MANDIR = $(MANSOURCEPATH)n
+        LIBMANDIR = $(MANSOURCEPATH)3
+      XAPPLOADDIR = $(LIBDIR)/app-defaults
+
+        SOXLIBREV = 4.2
+          SOXTREV = 4.0
+         SOXAWREV = 4.0
+        SOOLDXREV = 4.0
+         SOXMUREV = 4.0
+        SOXEXTREV = 4.0
+
+       FONTCFLAGS = -t
+
+     INSTAPPFLAGS = $(INSTDATFLAGS)
+
+            IMAKE = imake
+           DEPEND = makedepend
+              RGB = rgb
+            FONTC = bdftosnf
+        MKFONTDIR = mkfontdir
+        MKDIRHIER = /bin/sh $(BINDIR)/mkdirhier.sh
+
+        CONFIGSRC = $(TOP)/config
+        CLIENTSRC = $(TOP)/clients
+          DEMOSRC = $(TOP)/demos
+           LIBSRC = $(TOP)/lib
+          FONTSRC = $(TOP)/fonts
+       INCLUDESRC = $(TOP)/X11
+        SERVERSRC = $(TOP)/server
+          UTILSRC = $(TOP)/util
+        SCRIPTSRC = $(UTILSRC)/scripts
+       EXAMPLESRC = $(TOP)/examples
+       CONTRIBSRC = $(TOP)/../contrib
+           DOCSRC = $(TOP)/doc
+           RGBSRC = $(TOP)/rgb
+        DEPENDSRC = $(UTILSRC)/makedepend
+         IMAKESRC = $(CONFIGSRC)
+         XAUTHSRC = $(LIBSRC)/Xau
+          XLIBSRC = $(LIBSRC)/X
+           XMUSRC = $(LIBSRC)/Xmu
+       TOOLKITSRC = $(LIBSRC)/Xt
+       AWIDGETSRC = $(LIBSRC)/Xaw
+       OLDXLIBSRC = $(LIBSRC)/oldX
+      XDMCPLIBSRC = $(LIBSRC)/Xdmcp
+      BDFTOSNFSRC = $(FONTSRC)/bdftosnf
+     MKFONTDIRSRC = $(FONTSRC)/mkfontdir
+     EXTENSIONSRC = $(TOP)/extensions
+
+  DEPEXTENSIONLIB =
+     EXTENSIONLIB = -lXext
+
+          DEPXLIB = $(DEPEXTENSIONLIB)
+             XLIB = $(EXTENSIONLIB) -lX11
+
+      DEPXAUTHLIB = $(USRLIBDIR)/libXau.a
+         XAUTHLIB =  -lXau
+
+        DEPXMULIB =
+           XMULIB = -lXmu
+
+       DEPOLDXLIB =
+          OLDXLIB = -loldX
+
+      DEPXTOOLLIB =
+         XTOOLLIB = -lXt
+
+        DEPXAWLIB =
+           XAWLIB = -lXaw
+
+ LINTEXTENSIONLIB = $(USRLIBDIR)/llib-lXext.ln
+         LINTXLIB = $(USRLIBDIR)/llib-lX11.ln
+          LINTXMU = $(USRLIBDIR)/llib-lXmu.ln
+        LINTXTOOL = $(USRLIBDIR)/llib-lXt.ln
+          LINTXAW = $(USRLIBDIR)/llib-lXaw.ln
+
+          DEPLIBS = $(DEPXAWLIB) $(DEPXMULIB) $(DEPXTOOLLIB) $(DEPXLIB)
+
+         DEPLIBS1 = $(DEPLIBS)
+         DEPLIBS2 = $(DEPLIBS)
+         DEPLIBS3 = $(DEPLIBS)
+
+###########################################################################
+# Imake rules for building libraries, programs, scripts, and data files
+# rules:  $XConsortium: Imake.rules,v 1.67 89/12/18 17:14:15 jim Exp $
+
+###########################################################################
+# start of Imakefile
+
+#  Make WWW under unix for a.n.other unix system (bsd)
+#   Use this as a template
+
+TK_WWW_SOURCE_PATH=/a/dxcern/userd/tbl/hypertext/WWW/TkWWW/Tcl
+
+TK_WWW_INSTALL_PATH=/a/dxcern/userd/tbl/hypertext/WWW/TkWWW/$(WWW_MACH)
+
+TK_WWW_HOME_PAGE=http://www.w3.org/default.html
+TK_WWW_START_PAGE=$(TK_WWW_HOME_PAGE)
+
+CC = gcc -fno-builtin -Wall
+
+CDEBUGFLAGS = -O3 -pipe
+
+COMPATFLAGS =   -I/afs/athena.mit.edu/course/other/cdsdev/www-compat
+CCOPTIONS =
+
+BINDIR = $(TK_WWW_INSTALL_PATH)
+
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = unix_x
+
+# The ASIS repository's name for the machine we are on
+ASIS_MACH = hardware/os
+
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = /tmp
+
+#	Common Makefile for W3 Library Code
+#	-----------------------------------
+#
+#	(c) CERN 1990, 1991 -- see Copyright.html for conditions
+#
+# This file should be invariant between systems.
+#	DEPENDENCIES NOT COMPLETE
+
+#
+#	make		Compile and link the software (private version)
+#	make clean	Remove intermediate files
+
+WC = $(WWW)/Library
+CMN = $(WWW)/Library/Implementation/
+
+# Where shall we put the objects and built library?
+
+LOB = $(WTMP)/Library/$(WWW_MACH)
+
+# Bug: This path, if relative, is taken relative to the directory
+# in which this makefile is, not the pwd.  This screws up the
+# recursive invocation
+
+VC = 2.14
+
+CFLAGS2 = $(CFLAGS) -I$(CMN)
+
+CERNLIBBIN = $(WWW)/bin
+
+COMMON = $(LOB)/HTParse.o $(LOB)/HTAccess.o $(LOB)/HTTP.o \
+	$(LOB)/HTFile.o	$(LOB)/HTFTP.o $(LOB)/HTTCP.o \
+	$(LOB)/SGML.o $(LOB)/HTMLDTD.o $(LOB)/HTChunk.o \
+	$(LOB)/HTPlain.o $(LOB)/HTWriter.o $(LOB)/HTFWriter.o \
+	$(LOB)/HTMLGen.o \
+	$(LOB)/HTAtom.o $(LOB)/HTAnchor.o $(LOB)/HTStyle.o \
+	$(LOB)/HTList.o $(LOB)/HTString.o $(LOB)/HTAlert.o \
+	$(LOB)/HTRules.o $(LOB)/HTFormat.o $(LOB)/HTInit.o $(LOB)/HTMIME.o \
+	$(LOB)/HTHistory.o $(LOB)/HTNews.o $(LOB)/HTGopher.o \
+	$(LOB)/HTTelnet.o  $(LOB)/HTWSRC.o $(HTWAIS)
+
+CFILES = $(CMN)HTParse.c $(CMN)HTAccess.c $(CMN)HTTP.c $(CMN)HTFile.c \
+	$(CMN)HTFTP.c   $(CMN)HTTCP.c     $(CMN)SGML.c	\
+	$(CMN)HTMLDTD.c \
+	$(CMN)HTPlain.c	$(CMN)HTWriter.c  $(CMN)HTFWriter.c $(CMN)HTMLGen.c	\
+	$(CMN)HTChunk.c $(CMN)HTAtom.c   $(CMN)HTAnchor.c $(CMN)HTStyle.c \
+	$(CMN)HTList.c  $(CMN)HTString.c $(CMN)HTAlert.c $(CMN)HTRules.c \
+	$(CMN)HTFormat.c $(CMN)HTInit.c $(CMN)HTMIME.c $(CMN)HTHistory.c \
+	$(CMN)HTNews.c  $(CMN)HTGopher.c $(CMN)HTTelnet.c \
+	$(CMN)HTWAIS.c  $(CMN)HTWSRC.c
+
+HFILES = $(CMN)HTParse.h $(CMN)HTAccess.h $(CMN)HTTP.h $(CMN)HTFile.h \
+	$(CMN)HTFTP.h $(CMN)HTTCP.h \
+	$(CMN)SGML.h $(CMN)HTML.h $(CMN)HTMLDTD.h $(CMN)HTChunk.h \
+	$(CMN)HTPlain.h		$(CMN)HTWriter.h \
+	$(CMN)HTFWriter.h 	$(CMN)HTMLGen.h	\
+	$(CMN)HTStream.h \
+	$(CMN)HTAtom.h $(CMN)HTAnchor.h $(CMN)HTStyle.h \
+	$(CMN)HTList.h \
+	$(CMN)HTString.h $(CMN)HTAlert.h $(CMN)HTRules.h \
+	$(CMN)HTFormat.h $(CMN)HTInit.h \
+	$(CMN)HTMIME.h $(CMN)HTHistory.h $(CMN)HTNews.h \
+	$(CMN)HTGopher.h \
+	$(CMN)HTUtils.h $(CMN)tcp.h $(CMN)WWW.h $(CMN)HText.h \
+	$(CMN)HTTelnet.h \
+	$(CMN)HTWAIS.h  $(CMN)HTWSRC.h
+
+SOURCES = $(CFILES) $(HFILES) $(CMN)Version.make $(CMN)CommonMakefile \
+	$(WWW)/README.txt $(WWW)/Copyright.txt $(WWW)/BUILD
+SPECIFIC = $(WWW)/All
+
+#	Library
+#
+#  On SGI, ranlib is unnecessary and does not exist so we ignore errors
+# for that step
+all: $(LOB)/libwww.a
+	$(MV) $(LOB)/libwww.a $(WC)/$(WWW_MACH)
+
+$(LOB)/libwww.a : $(COMMON)
+	ar r $(LOB)/libwww.a $(COMMON)
+	-ranlib $(LOB)/libwww.a
+
+#	Clean up everything generatable except final products
+clean ::
+	$(RM) $(LOB)
+
+#	Clean up everything generatable including final products
+
+cleanall :: clean
+	$(RM) $(LOB)/libwww.a
+
+# 			Common code
+#			-----------
+
+#	Directory for object files - .created checks it exists
+
+OE = $(LOB)/.created
+$(OE) :
+	-mkdir $(WTMP)
+	-mkdir $(WTMP)/Library
+	-mkdir $(WTMP)/Library/$(WWW_MACH)
+	touch $@
+
+$(LOB)/HTList.o : $(OE) $(CMN)HTList.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTList.c
+
+$(LOB)/HTAnchor.o : $(OE) $(CMN)HTAnchor.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAnchor.c
+
+$(LOB)/HTFormat.o : $(OE) $(CMN)HTFormat.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFormat.c
+
+$(LOB)/HTInit.o : $(OE) $(CMN)HTInit.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTInit.c
+
+$(LOB)/HTMIME.o : $(OE) $(CMN)HTMIME.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMIME.c
+
+$(LOB)/HTHistory.o : $(OE) $(CMN)HTHistory.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTHistory.c
+
+$(LOB)/HTNews.o : $(OE) $(CMN)HTNews.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTNews.c
+
+$(LOB)/HTGopher.o : $(OE) $(CMN)HTGopher.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTGopher.c
+
+$(LOB)/HTTelnet.o : $(OE) $(CMN)HTTelnet.c $(CMN)HTUtils.h $(CMN)HTTelnet.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTelnet.c
+
+$(LOB)/HTStyle.o : $(OE) $(CMN)HTStyle.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTStyle.c
+
+$(LOB)/HTAtom.o : $(OE) $(CMN)HTAtom.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAtom.c
+
+$(LOB)/HTChunk.o : $(OE) $(CMN)HTChunk.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTChunk.c
+
+$(LOB)/HTString.o : $(OE) $(CMN)HTString.c $(CMN)HTUtils.h $(CMN)Version.make
+	$(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTString.c
+
+$(LOB)/HTAlert.o : $(OE) $(CMN)HTAlert.c $(CMN)HTUtils.h $(CMN)Version.make
+	$(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTAlert.c
+
+$(LOB)/HTRules.o : $(OE) $(CMN)HTRules.c $(CMN)HTUtils.h $(CMN)Version.make
+	$(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTRules.c
+
+$(LOB)/SGML.o : $(OE) $(CMN)SGML.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)SGML.c
+
+$(LOB)/HTMLGen.o : $(OE) $(CMN)HTMLGen.c $(CMN)HTUtils.h $(CMN)HTMLDTD.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMLGen.c
+
+$(LOB)/HTMLDTD.o : $(OE) $(CMN)HTMLDTD.c $(CMN)SGML.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMLDTD.c
+
+$(LOB)/HTPlain.o : $(OE) $(CMN)HTPlain.c $(CMN)HTPlain.h $(CMN)HTStream.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTPlain.c
+
+$(LOB)/HTWAIS.o : $(OE) $(CMN)HTWAIS.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(WAISINC) $(CMN)HTWAIS.c
+
+$(LOB)/HTWSRC.o : $(OE) $(CMN)HTWSRC.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTWSRC.c
+
+$(LOB)/HTWriter.o : $(OE) $(CMN)HTWriter.c $(CMN)HTWriter.h $(CMN)HTStream.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTWriter.c
+
+$(LOB)/HTFWriter.o : $(OE) $(CMN)HTFWriter.c $(CMN)HTFWriter.h $(CMN)HTStream.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFWriter.c
+
+#	Communications & Files
+
+$(LOB)/HTTP.o : $(OE) $(CMN)HTTP.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTP.c
+
+$(LOB)/HTTCP.o : $(OE) $(CMN)HTTCP.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTCP.c
+
+$(LOB)/HTFile.o : $(OE) $(CMN)HTFile.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFile.c
+
+$(LOB)/HTFTP.o : $(OE) $(CMN)HTFTP.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFTP.c
+
+$(LOB)/HTAccess.o : $(OE)  $(CMN)HTAccess.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAccess.c
+
+$(LOB)/HTParse.o : $(OE) $(CMN)HTParse.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTParse.c
+
+###########################################################################
+# common rules for all Makefiles - do not edit
+
+emptyrule::
+
+clean::
+	$(RM_CMD) \#*
+
+Makefile::
+	-@if [ -f Makefile ]; then \
+	echo "	$(RM) Makefile.bak; $(MV) Makefile Makefile.bak"; \
+	$(RM) Makefile.bak; $(MV) Makefile Makefile.bak; \
+	else exit 0; fi
+	$(IMAKE_CMD) -DTOPDIR=$(TOP) -DCURDIR=$(CURRENT_DIR)
+
+tags::
+	$(TAGS) -w *.[ch]
+	$(TAGS) -xw *.[ch] > TAGS
+
+saber:
+	#load $(ALLDEFINES) $(SRCS)
+
+osaber:
+	#load $(ALLDEFINES) $(OBJS)
+
+###########################################################################
+# empty rules for directories that do not have SUBDIRS - do not edit
+
+install::
+	@echo "install in $(CURRENT_DIR) done"
+
+install.man::
+	@echo "install.man in $(CURRENT_DIR) done"
+
+Makefiles::
+
+includes::
+
+###########################################################################
+# dependencies generated by makedepend
+
diff --git a/WWW/Library/Implementation/SGML.c b/WWW/Library/Implementation/SGML.c
new file mode 100644
index 00000000..9cddd35a
--- /dev/null
+++ b/WWW/Library/Implementation/SGML.c
@@ -0,0 +1,2145 @@
+/*			General SGML Parser code		SGML.c
+**			========================
+**
+**	This module implements an HTStream object. To parse an
+**	SGML file, create this object which is a parser. The object
+**	is (currently) created by being passed a DTD structure,
+**	and a target HTStructured oject at which to throw the parsed stuff.
+**	
+**	 6 Feb 93  Binary seraches used. Intreface modified.
+*/
+#include "HTUtils.h"
+#include "tcp.h"		/* For FROMASCII */
+
+#include "SGML.h"
+#include "HTMLDTD.h"
+#include "HTCJK.h"
+
+#include <ctype.h>
+/*#include <stdio.h> included in HTUtils.h -- FM */
+#include "HTChunk.h"
+
+#include "LYLeaks.h"
+
+#define INVALID (-1)
+
+#define FREE(x) if (x) {free(x); x = NULL;}
+
+PUBLIC HTCJKlang HTCJK = NOCJK;		/* CJK enum value.		*/
+PUBLIC BOOL HTPassEightBitRaw = FALSE;	/* Pass 161-172,174-255 raw.	*/ 
+PUBLIC BOOL HTPassEightBitNum = FALSE;	/* Pass ^ numeric entities raw.	*/
+PUBLIC BOOL HTPassHighCtrlRaw = FALSE;	/* Pass 127-160,173,&#127; raw.	*/
+PUBLIC BOOL HTPassHighCtrlNum = FALSE;	/* Pass &#128;-&#159; raw.	*/
+
+extern BOOLEAN LYCheckForCSI PARAMS((HTStructured *target, char **url));
+extern void LYDoCSI PARAMS((char *url, CONST char *comment, char **csi));
+
+/*	The State (context) of the parser
+**
+**	This is passed with each call to make the parser reentrant
+**
+*/
+
+#define MAX_ATTRIBUTES 36	/* Max number of attributes per element */
+
+	
+/*		Element Stack
+**		-------------
+**	This allows us to return down the stack reselcting styles.
+**	As we return, attribute values will be garbage in general.
+*/
+typedef struct _HTElement HTElement;
+struct _HTElement {
+	HTElement *	next;	/* Previously nested element or 0 */
+	HTTag*		tag;	/* The tag at this level  */
+};
+
+
+/*	Internal Context Data Structure
+**	-------------------------------
+*/
+struct _HTStream {
+
+    CONST HTStreamClass *	isa;		/* inherited from HTStream */
+    
+    CONST SGML_dtd 		*dtd;
+    HTStructuredClass		*actions;	/* target class  */
+    HTStructured		*target;	/* target object */
+
+    HTTag 			*current_tag;
+    int 			current_attribute_number;
+    HTChunk			*string;
+    HTElement			*element_stack;
+    enum sgml_state { S_text, S_litteral,
+    		S_tag, S_tag_gap, S_attr, S_attr_gap, S_equals, S_value,
+		S_ero, S_cro,
+		S_exclamation, S_comment, S_doctype, S_marked,
+		S_sgmlent, S_sgmlele, S_sgmlatt,
+		S_squoted, S_dquoted, S_end, S_entity,
+		S_esc,    S_dollar,    S_paren,    S_nonascii_text,
+		S_dollar_paren,
+		S_esc_sq, S_dollar_sq, S_paren_sq, S_nonascii_text_sq,
+		S_dollar_paren_sq,
+		S_esc_dq, S_dollar_dq, S_paren_dq, S_nonascii_text_dq,
+		S_dollar_paren_dq,
+		S_in_kanji, S_junk_tag} state;
+#ifdef CALLERDATA
+    void *			callerData;
+#endif /* CALLERDATA */
+    BOOL present[MAX_ATTRIBUTES];	/* Flags: attribute is present? */
+    char * value[MAX_ATTRIBUTES];	/* malloc'd strings or NULL if none */
+
+    BOOL			lead_exclamation;
+    BOOL			first_dash;
+    BOOL			end_comment;
+    BOOL			doctype_bracket;
+    BOOL			first_bracket;
+    BOOL			second_bracket;
+
+    char *			recover;
+    int				recover_index;
+    char *			include;
+    int				include_index;
+    char *			url;
+    char *			csi;
+    int				csi_index;
+} ;
+
+
+#define PUTC(ch) ((*context->actions->put_character)(context->target, ch))
+
+extern BOOL historical_comments;
+extern BOOL minimal_comments;
+extern BOOL soft_dquotes;
+
+/*	Handle Attribute
+**	----------------
+*/
+/* PUBLIC CONST char * SGML_default = "";   ?? */
+
+PRIVATE void handle_attribute_name ARGS2(
+	HTStream *,	context,
+	CONST char *,	s)
+{
+
+    HTTag * tag = context->current_tag;
+    attr * attributes = tag->attributes;
+
+    int high, low, i, diff;		/* Binary search for attribute name */
+    for (low = 0, high = tag->number_of_attributes;
+    	 high > low;
+	 diff < 0 ? (low = i+1) : (high = i)) {
+	i = (low + (high-low)/2);
+	diff = strcasecomp(attributes[i].name, s);
+	if (diff == 0) {		/* success: found it */
+    	    context->current_attribute_number = i;
+	    context->present[i] = YES;
+	    FREE(context->value[i]);
+	    return;
+	} /* if */
+	
+    } /* for */
+    
+    if (TRACE)
+	fprintf(stderr, "SGML: Unknown attribute %s for tag %s\n",
+	    s, context->current_tag->name);
+    context->current_attribute_number = INVALID;	/* Invalid */
+}
+
+
+/*	Handle attribute value
+**	----------------------
+*/
+PRIVATE void handle_attribute_value ARGS2(
+	HTStream *,	context,
+	CONST char *,	s)
+{
+    if (context->current_attribute_number != INVALID) {
+	StrAllocCopy(context->value[context->current_attribute_number], s);
+    } else {
+        if (TRACE)
+	    fprintf(stderr, "SGML: Attribute value %s ignored\n", s);
+    }
+    context->current_attribute_number = INVALID; /* can't have two assignments! */
+}
+
+
+/*	Handle entity
+**	-------------
+**
+** On entry,
+**	s	contains the entity name zero terminated
+** Bugs:
+**	If the entity name is unknown, the terminator is treated as
+**	a printable non-special character in all cases, even if it is '<'
+** Bug-fix:
+**	Modified SGML_character() so we only come here with terminator
+**	as '\0' and check a FoundEntity flag. -- Foteos Macrides
+*/
+
+PRIVATE BOOL FoundEntity = FALSE;
+
+PRIVATE void handle_entity ARGS2(
+	HTStream *,	context,
+	char,		term)
+{
+    CONST char ** entities = context->dtd->entity_names;
+    CONST char *s = context->string->data;
+    int high, low, i, diff;
+
+    /*
+    **  Use Lynx special characters directly for nbsp, ensp, emsp,
+    **  thinsp, and shy so we go through the HTML_put_character()
+    **  filters instead of using HTML_put_string(). - FM
+    */
+    if (!strcmp(s, "nbsp")) {
+        PUTC(1);
+	FoundEntity = TRUE;
+	return;
+    }
+    if (!strcmp(s, "ensp") || !strcmp(s, "emsp") || !strcmp(s, "thinsp")) {
+        PUTC(2);
+	FoundEntity = TRUE;
+	return;
+    }
+    if (!strcmp(s, "shy")) {
+        PUTC(7);
+	FoundEntity = TRUE;
+	return;
+    }
+
+    /*
+    **  Handle all other entities normally. - FM
+    */
+    FoundEntity = FALSE;
+    for (low = 0, high = context->dtd->number_of_entities;
+    	 high > low;
+	 diff < 0 ? (low = i+1) : (high = i)) {  /* Binary serach */
+	i = (low + (high-low)/2);
+	diff = strcmp(entities[i], s);	/* Case sensitive! */
+	if (diff == 0) {		/* success: found it */
+	    (*context->actions->put_entity)(context->target, i);
+	    FoundEntity = TRUE;
+	    return;
+	}
+    }
+    /*
+    **  If entity string not found, display as text.
+    */
+    if (TRACE)
+	fprintf(stderr, "SGML: Unknown entity %s\n", s); 
+    PUTC('&');
+    {
+	CONST char *p;
+	for (p = s; *p; p++) {
+	    PUTC(*p);
+	}
+    }
+    if (term != '\0')
+        PUTC(term);
+}
+
+
+/*	Handle comment
+**	--------------
+*/
+PRIVATE void handle_comment ARGS1(
+	HTStream *,		context)
+{
+    CONST char *s = context->string->data;
+
+    if (TRACE)
+	fprintf(stderr, "SGML Comment:\n<%s>\n", s);
+
+    if (context->csi == NULL &&
+        strncmp(s, "!--#", 4) == 0 &&
+        LYCheckForCSI(context->target, (char **)&context->url) == TRUE) {
+	LYDoCSI(context->url, s, (char **)&context->csi);
+    }
+
+    return;
+}
+
+
+/*	Handle identifier
+**	-----------------
+*/
+PRIVATE void handle_identifier ARGS1(
+	HTStream *,		context)
+{
+    CONST char *s = context->string->data;
+
+    if (TRACE)
+	fprintf(stderr, "SGML Identifier\n<%s>\n", s);
+
+    return;
+}
+
+
+/*	Handle doctype
+**	--------------
+*/
+PRIVATE void handle_doctype ARGS1(
+	HTStream *,		context)
+{
+    CONST char *s = context->string->data;
+
+    if (TRACE)
+	fprintf(stderr, "SGML Doctype\n<%s>\n", s);
+
+    return;
+}
+
+
+/*	Handle marked
+**	-------------
+*/
+PRIVATE void handle_marked ARGS1(
+	HTStream *,		context)
+{
+    CONST char *s = context->string->data;
+
+    if (TRACE)
+	fprintf(stderr, "SGML Marked Section:\n<%s>\n", s);
+
+    return;
+}
+
+
+/*	Handle sgmlent
+**	--------------
+*/
+PRIVATE void handle_sgmlent ARGS1(
+	HTStream *,		context)
+{
+    CONST char *s = context->string->data;
+
+    if (TRACE)
+	fprintf(stderr, "SGML Entity Declaration:\n<%s>\n", s);
+
+    return;
+}
+
+
+/*	Handle sgmlent
+**	--------------
+*/
+PRIVATE void handle_sgmlele ARGS1(
+	HTStream *,		context)
+{
+    CONST char *s = context->string->data;
+
+    if (TRACE)
+	fprintf(stderr, "SGML Element Declaration:\n<%s>\n", s);
+
+    return;
+}
+
+
+/*	Handle sgmlatt
+**	--------------
+*/
+PRIVATE void handle_sgmlatt ARGS1(
+	HTStream *,		context)
+{
+    CONST char *s = context->string->data;
+
+    if (TRACE)
+	fprintf(stderr, "SGML Attribute Declaration:\n<%s>\n", s);
+
+    return;
+}
+
+
+/*	End element
+**	-----------
+*/
+PRIVATE void end_element ARGS2(
+	HTStream *,	context,
+	HTTag *,	old_tag)
+{
+    if (TRACE)
+        fprintf(stderr, "SGML: End </%s>\n", old_tag->name);
+    if (old_tag->contents == SGML_EMPTY) {
+        if (TRACE)
+	    fprintf(stderr, "SGML: Illegal end tag </%s> found.\n",
+	    		    old_tag->name);
+	return;
+    }
+#ifdef WIND_DOWN_STACK
+    while (context->element_stack) { /* Loop is error path only */
+#else
+    if (context->element_stack) { /* Substitute and remove one stack element */ 
+#endif /* WIND_DOWN_STACK */
+	HTElement * N = context->element_stack;
+	HTTag * t = N->tag;
+	
+	if (old_tag != t) {		/* Mismatch: syntax error */
+	    if (context->element_stack->next) {	/* This is not the last level */
+		if (TRACE) fprintf(stderr,
+	    	"SGML: Found </%s> when expecting </%s>. </%s> assumed.\n",
+		    old_tag->name, t->name, t->name);
+	    } else {			/* last level */
+		if (TRACE) fprintf(stderr,
+	            "SGML: Found </%s> when expecting </%s>. </%s> Ignored.\n",
+		    old_tag->name, t->name, old_tag->name);
+	        return;			/* Ignore */
+	    }
+	}
+	
+	context->element_stack = N->next;		/* Remove from stack */
+	FREE(N);
+	(*context->actions->end_element)(context->target,
+		 t - context->dtd->tags, (char **)&context->include);
+#ifdef WIND_DOWN_STACK
+	if (old_tag == t)
+	    return;  /* Correct sequence */
+#else
+	return;
+#endif /* WIND_DOWN_STACK */
+	
+	/* Syntax error path only */
+	
+    }
+    if (TRACE)
+        fprintf(stderr, "SGML: Extra end tag </%s> found and ignored.\n",
+			old_tag->name);
+}
+
+
+/*	Start a element
+*/
+PRIVATE void start_element ARGS1(
+	HTStream *,	context)
+{
+    HTTag * new_tag = context->current_tag;
+    
+    if (TRACE)
+        fprintf(stderr, "SGML: Start <%s>\n", new_tag->name);
+    (*context->actions->start_element)(
+    	context->target,
+	new_tag - context->dtd->tags,
+	context->present,
+	(CONST char**) context->value,  /* coerce type for think c */
+	(char **)&context->include);
+    if (new_tag->contents != SGML_EMPTY) {		/* i.e. tag not empty */
+	HTElement * N = (HTElement *)malloc(sizeof(HTElement));
+        if (N == NULL)
+	    outofmem(__FILE__, "start_element");
+	N->next = context->element_stack;
+	N->tag = new_tag;
+	context->element_stack = N;
+    }
+}
+
+
+/*		Find Tag in DTD tag list
+**		------------------------
+**
+** On entry,
+**	dtd	points to dtd structire including valid tag list
+**	string	points to name of tag in question
+**
+** On exit,
+**	returns:
+**		NULL		tag not found
+**		else		address of tag structure in dtd
+*/
+PUBLIC HTTag * SGMLFindTag ARGS2(
+	CONST SGML_dtd*,	dtd,
+	CONST char *,		string)
+{
+    int high, low, i, diff;
+    for (low = 0, high=dtd->number_of_tags;
+    	 high > low;
+	 diff < 0 ? (low = i+1) : (high = i)) {  /* Binary serach */
+	i = (low + (high-low)/2);
+	diff = strcasecomp(dtd->tags[i].name, string);	/* Case insensitive */
+	if (diff == 0) {		/* success: found it */
+	    return &dtd->tags[i];
+	}
+    }
+    return NULL;
+}
+
+/*________________________________________________________________________
+**			Public Methods
+*/
+
+
+/*	Could check that we are back to bottom of stack! @@  */
+
+PUBLIC void SGML_free  ARGS1(
+	HTStream *,	context)
+{
+    int i;
+    HTElement * cur;
+    HTElement * next;
+
+    (*context->actions->_free)(context->target);
+    HTChunkFree(context->string);
+
+    /* free strings */
+    for (i = 0; i < MAX_ATTRIBUTES; i++) 
+	FREE(context->value[i]);
+
+    FREE(context->recover);
+    FREE(context->include);
+    FREE(context->url);
+    FREE(context->csi);
+
+    cur = context->element_stack;
+    while (cur) {
+        next = cur->next;
+	FREE(cur);
+	cur = next;
+    }
+
+    FREE(context);
+}
+
+PUBLIC void SGML_abort ARGS2(
+	HTStream *,	context,
+	HTError, 	e)
+{
+    int i;
+
+    (*context->actions->_abort)(context->target, e);
+    HTChunkFree(context->string);
+
+    /* free strings */
+    for (i = 0; i < MAX_ATTRIBUTES; i++) 
+	FREE(context->value[i]);
+
+    FREE(context->recover);
+    FREE(context->include);
+    FREE(context->url);
+    FREE(context->csi);
+
+    FREE(context);
+}
+
+
+/*	Read and write user callback handle
+**	-----------------------------------
+**
+**   The callbacks from the SGML parser have an SGML context parameter.
+**   These calls allow the caller to associate his own context with a
+**   particular SGML context.
+*/
+
+#ifdef CALLERDATA		  
+PUBLIC void* SGML_callerData ARGS1(
+	HTStream *,	context)
+{
+    return context->callerData;
+}
+
+PUBLIC void SGML_setCallerData ARGS2(
+	HTStream *,	context,
+	void*,		data)
+{
+    context->callerData = data;
+}
+#endif /* CALLERDATA */
+
+PUBLIC void SGML_character ARGS2(
+	HTStream *,	context,
+	char,		c)
+{
+    CONST SGML_dtd *dtd	=	context->dtd;
+    HTChunk	*string = 	context->string;
+    CONST char * EntityName;
+    extern int current_char_set;
+    extern char *LYchar_set_names[];
+    extern CONST char * HTMLGetEntityName PARAMS((int i));
+
+top:
+    /*
+    **  Ignore low ISO 646 7-bit control characters
+    **  if HTCJK is not set. - FM
+    */
+    if ((unsigned char)c < 32 &&
+	c != 9 && c != 10 && c != 13 &&
+	HTCJK == NOCJK)
+        return;
+
+    /*
+    **  Ignore 127 if we don't have HTPassHighCtrlRaw
+    **  or HTCJK set. - FM
+    */
+    if (c == 127 &&
+        !(HTPassHighCtrlRaw || HTCJK != NOCJK))
+        return;
+
+    /*
+    **  Ignore 8-bit control characters 128 - 159 if
+    **  neither HTPassHighCtrlRaw nor HTCJK is set. - FM
+    */
+    if ((unsigned char)c > 127 && (unsigned char)c < 160 &&
+	!(HTPassHighCtrlRaw || HTCJK != NOCJK))
+        return;
+
+    /*
+    **  Handle character based on context->state.
+    */
+    switch(context->state) {
+
+    case S_in_kanji:
+	context->state = S_text;
+	PUTC(c);
+	break;
+
+    case S_text:
+	if (HTCJK != NOCJK && (c & 0200) != 0) {
+	    /*
+	    **  Setting up for Kanji multibyte handling (based on
+	    **  Takuya ASADA's (asada@three-a.co.jp) CJK Lynx). - FM
+	    */
+	    context->state = S_in_kanji;
+	    PUTC(c);
+	    break;
+	} else if (HTCJK != NOCJK && c == '\033') {
+	    /*
+	    **  Setting up for CJK escape sequence handling (based on
+	    **  Takuya ASADA's (asada@three-a.co.jp) CJK Lynx). - FM
+	    */
+	    context->state = S_esc;
+	    PUTC(c);
+	    break;
+	}
+	if (c == '&' && (!context->element_stack ||
+			 (context->element_stack->tag  &&
+	    		  (context->element_stack->tag->contents ==
+			  		SGML_MIXED ||
+			   context->element_stack->tag->contents ==
+			      		SGML_RCDATA)))) {
+	    /*
+	    **  Setting up for possible entity, without the leading '&'. - FM
+	    */
+	    string->size = 0;
+	    context->state = S_ero;
+	} else if (c == '<') {
+	    /*
+	    **  Setting up for possible tag. - FM
+	    */
+	    string->size = 0;
+	    context->state = (context->element_stack &&
+	    		context->element_stack->tag  &&
+			context->element_stack->tag->contents == SGML_LITTERAL)
+	      				 ?
+	    		      S_litteral : S_tag;
+	/*
+	**  Convert 160 (nbsp) to Lynx special character if
+	**  neither HTPassHighCtrlRaw nor HTCJK is set. - FM
+	*/
+	} else if ((unsigned char)c == 160 &&
+		   !(HTPassHighCtrlRaw || HTCJK != NOCJK)) {
+            PUTC(1);
+	/*
+	**  Convert 173 (shy) to Lynx special character if
+	**  neither HTPassHighCtrlRaw nor HTCJK is set. - FM
+	*/
+	} else if ((unsigned char)c == 173 &&
+		   !(HTPassHighCtrlRaw || HTCJK != NOCJK)) {
+            PUTC(7);
+	/*
+	**  If it's any other (> 160) 8-bit chararcter, and
+	**  we have not set HTPassEightBitRaw nor HTCJK, nor
+	**  have the "ISO Latin 1" character set selected,
+	**  back translate for our character set. - FM
+	*/
+	} else if ((unsigned char)c > 160 &&
+		   !(HTPassEightBitRaw || HTCJK != NOCJK) &&
+		   strncmp(LYchar_set_names[current_char_set],
+		   	   "ISO Latin 1", 11)) {
+	    int i;
+	    int value;
+
+	    string->size = 0;
+	    value = (int)((unsigned char)c - 160);
+	    EntityName = HTMLGetEntityName(value);
+	    for (i = 0; EntityName[i]; i++)
+	        HTChunkPutc(string, EntityName[i]);
+	    HTChunkTerminate(string);
+	    handle_entity(context, '\0');
+	    string->size = 0;
+	    if (!FoundEntity)
+	        PUTC(';');
+	/*
+	**  If we get to here, pass the character. - FM
+	*/
+	} else {
+	    PUTC(c);
+	}
+	break;
+
+    /*	
+    **  In litteral mode, waits only for specific end tag (for
+    **  compatibility with old servers, and for Lynx). - FM
+    */
+    case S_litteral :
+	HTChunkPutc(string, c);
+	if (TOUPPER(c) != ((string->size == 1) ?
+					   '/' :
+			context->element_stack->tag->name[string->size-2])) {
+	    int i;
+	    
+	    /*
+	    **  If complete match, end litteral.
+	    */
+	    if ((c == '>') &&
+	        (!context->element_stack->tag->name[string->size-2])) {
+		end_element(context, context->element_stack->tag);
+		string->size = 0;
+		context->current_attribute_number = INVALID;
+		context->state = S_text;
+		break;
+	    }
+	    /*
+	    **  If Mismatch: recover string.
+	    */
+	    PUTC('<');
+	    for (i = 0; i < string->size; i++)	/* recover */
+	       PUTC(string->data[i]);
+	    string->size = 0;
+	    context->state = S_text;	
+	}
+        break;
+
+    /*
+    **  Character reference (numeric entity) or named entity.
+    */
+    case S_ero:
+   	if (c == '#') {
+	    /*
+	    **  Setting up for possible numeric entity.
+	    */
+	    context->state = S_cro;  /* &# is Char Ref Open */ 
+	    break;
+	}
+	context->state = S_entity;   /* Fall through! */
+	
+    /*
+    **  Handle possible named entity.
+    */
+    case S_entity:
+	if ((unsigned char)c < 127 && isalnum((unsigned char)c)) {
+	    /*
+	    **  Accept valid ASCII character. - FM
+	    */
+	    HTChunkPutc(string, c);
+	} else if (string->size == 0) {
+	    /*
+	    **  It was an ampersand that's just text, so output
+	    **  the ampersand and recycle this character. - FM
+	    */
+	    PUTC('&');
+	    context->state = S_text;
+	    goto top;
+	} else {
+	    /*
+	    **  Terminate entity name and try to handle it. - FM
+	    */
+	    HTChunkTerminate(string);
+	    handle_entity(context, '\0');
+	    string->size = 0;
+	    context->state = S_text;
+	    /*
+	    **  Don't eat the terminator if we didn't find the
+	    **  entity name and therefore sent the raw string
+	    **  via handle_entity(), or if the terminator is
+	    **  not the "standard" semi-colon for HTML. - FM
+	    */
+	    if (!FoundEntity || c != ';')
+	        goto top;
+	}
+	break;
+
+    /*
+    **  Handle possible numeric entity.
+    */
+    case S_cro:
+	if ((unsigned char)c < 127 && isdigit((unsigned char)c)) {
+	    /*
+	    **  Accept only valid ASCII digits. - FM
+	    */
+	    HTChunkPutc(string, c);	/* accumulate a character NUMBER */
+	} else if (string->size == 0) {
+	    /*
+	    **  No digits following the "&#" so recover
+	    **  them and recycle the character. - FM
+	    */
+	    PUTC('&');
+	    PUTC('#');
+	    context->state = S_text;
+	    goto top;
+	} else if ((unsigned char)c > 127 || isalnum((unsigned char)c)) {
+	    /*
+	    **  We have digit(s), but not a valid terminator,
+	    **  so recover the "&#" and digit(s) and recycle
+	    **  the character. - FM
+	    */
+	    int i;
+	    PUTC('&');
+	    PUTC('#');
+	    for (i = 0; i < string->size; i++)	/* recover */
+	       PUTC(string->data[i]);
+	    string->size = 0;
+	    context->state = S_text;
+	    goto top;
+	} else {
+	    /*
+	    **  Terminate the numeric entity and try to handle it. - FM
+	    */
+	    int value, i;
+	    HTChunkTerminate(string);
+	    if (sscanf(string->data, "%d", &value) == 1) {
+	        if (value == 8482) {
+		    /*
+		    **  trade  Treat as reg. - FM
+		    */
+		    value = 174;
+		}
+	        /*
+		** Show the numeric entity if the value:
+		**  (1) Is greater than 255 (until we support Unicode).
+		**  (2) Is less than 32, and not valid or we don't
+		**	have HTCJK set.
+		**  (3) Is 127 and we don't have HTPassHighCtrlRaw or
+		**	HTCJK set.
+		**  (4) Is 128 - 159 and we don't have HTPassHighCtrlNum
+		**	set.
+		** - FM
+		*/
+	        if ((value > 255) ||
+		    (value < 32 &&
+		     value != 9 && value != 10 && value != 13 &&
+		     HTCJK == NOCJK) ||
+		    (value == 127 &&
+		     !(HTPassHighCtrlRaw || HTCJK != NOCJK)) ||
+		    (value > 127 && value < 160 &&
+		     !HTPassHighCtrlNum)) {
+		    if (value == 8194 || value == 8195 || value == 8201) {
+		        /*
+			**  ensp, emsp or thinsp. - FM
+			*/
+			PUTC(2);
+			break;
+		    }
+		    if (value == 8211 || value == 8212) {
+		        /*
+			**  ndash or mdash. - FM
+			*/
+			PUTC('-');
+			break;
+		    }
+		    /*
+		    **  Unhandled or llegal value.  Recover the "&#"
+		    **  and digit(s), and recycle the terminator. - FM
+		    */
+		    PUTC('&');
+		    PUTC('#');
+		    string->size--;
+		    for (i = 0; i < string->size; i++)	/* recover */
+		        PUTC(string->data[i]);
+		    string->size = 0;
+		    context->state = S_text;
+		    goto top;
+		} else if (value == 160) {
+		    /*
+		    **  Use Lynx special character for 160 (nbsp). - FM
+		    */
+		    PUTC(1);
+		} else if (value == 173) {
+		    /*
+		    **  Use Lynx special character for 173 (shy) - FM
+		    */
+		    PUTC(7);
+		} else if (value < 161 || HTPassEightBitNum ||
+			   !strncmp(LYchar_set_names[current_char_set],
+			   	    "ISO Latin 1", 11)) {
+		    /*
+		    **  No conversion needed. - FM
+		    */
+	            PUTC(FROMASCII((char)value));
+		} else {
+		    /*
+		    **  Convert and handle as named entity. - FM
+		    */
+		    value -= 160;
+		    EntityName = HTMLGetEntityName(value);
+		    if (EntityName && EntityName[0] != '\0') {
+			string->size = 0;
+			for (i = 0; EntityName[i]; i++)
+			    HTChunkPutc(string, EntityName[i]);
+			HTChunkTerminate(string);
+			handle_entity(context, '\0');
+			/*
+			**  Add a semi-colon if something went wrong
+			**  and handle_entity() sent the string. - FM
+			*/
+			if (!FoundEntity) {
+			    PUTC(';');
+			}
+		    } else {
+		        /*
+			**  Our conversion failed, so recover the "&#"
+			**  and digit(s), and recycle the terminator. - FM
+			*/
+			PUTC('&');
+			PUTC('#');
+			string->size--;
+			for (i = 0; i < string->size; i++)	/* recover */
+			    PUTC(string->data[i]);
+			string->size = 0;
+			context->state = S_text;
+			goto top;
+		    }
+		}
+		/*
+		**  If we get to here, we succeeded.  Hoorah!!! - FM
+		*/
+		string->size = 0;
+		context->state = S_text;
+		/*
+		**  Don't eat the terminator if it's not
+		**  the "standard" semi-colon for HTML. - FM
+		*/
+		if (c != ';')
+		    goto top;
+	    } else {
+	        /*
+		**  Not an entity, and don't know why not, so add the
+		**  terminator to the string, output the "&#", and
+		**  process the string via the recover element. - FM
+		*/
+		string->size--;
+		HTChunkPutc(string, c);
+		HTChunkTerminate(string);
+	        PUTC('&');
+	        PUTC('#');
+		if (context->recover == NULL) {
+		    StrAllocCopy(context->recover, string->data);
+		    context->recover_index = 0;
+		} else {
+		    StrAllocCat(context->recover, string->data);
+		}
+		string->size = 0;
+		context->state = S_text;
+		break;
+	    }
+	}
+	break;
+
+    /*
+    **  Tag
+    */	    
+    case S_tag:					/* new tag */
+	if ((unsigned char)c < 127 && isalnum((unsigned char)c)) {
+	    /*
+	    **  Add valid ASCII character. - FM
+	    */
+	    HTChunkPutc(string, c);
+        } else if (c == '!' && !string->size) {	/* <! */
+	    /*
+	    **  Terminate and set up for possible comment,
+	    **  identifier, declaration, or marked section. - FM
+	    */
+	    context->state = S_exclamation;
+	    context->lead_exclamation = TRUE;
+	    context->doctype_bracket = FALSE;
+	    context->first_bracket = FALSE;
+	    HTChunkPutc(string, c);
+	    break;
+        } else if (WHITE(c) && !string->size) {	/* <WHITE */
+	    /*
+	    **  Recover the '<' and WHITE character. - FM
+	    */
+	    context->state = S_text;
+	    PUTC('<');
+	    goto top;
+	} else {				/* End of tag name */
+	    /*
+	    **  Try to handle tag. - FM
+	    */
+	    HTTag * t;
+	    if (c == '/') {
+		if (TRACE)
+		    if (string->size!=0)
+		        fprintf(stderr,"SGML: `<%s/' found!\n", string->data);
+		context->state = S_end;
+		break;
+	    }
+	    HTChunkTerminate(string) ;
+
+	    t = SGMLFindTag(dtd, string->data);
+	    if (!t) {
+	        if (c == ':' && 0 == strcasecomp(string->data, "URL")) {
+		    /*
+		    **  Treat <URL: as text rather than a junk tag,
+		    **  so we display it and the URL (Lynxism 8-). - FM
+		    */
+		    int i;
+		    PUTC('<');
+		    for (i = 0; i < 3; i++)	/* recover */
+		        PUTC(string->data[i]);
+		    PUTC(c);
+		    if (TRACE)
+		        fprintf(stderr, "SGML: Treating <%s%c as text\n",
+		    			string->data, c);
+		    string->size = 0;
+		    context->state = S_text;	
+		} else {
+		    if (TRACE)
+		        fprintf(stderr, "SGML: *** Unknown element %s\n",
+		    			string->data);
+		    context->state = (c == '>') ? S_text : S_junk_tag;
+		}
+		break;
+	    }
+	    context->current_tag = t;
+	    
+	    /* 
+	    **  Clear out attributes.
+	    */
+	    {
+	        int i;
+	        for (i=0; i< context->current_tag->number_of_attributes; i++)
+	    	    context->present[i] = NO;
+	    }
+	    string->size = 0;
+	    context->current_attribute_number = INVALID;
+	    
+	    if (c == '>') {
+		if (context->current_tag->name)
+		    start_element(context);
+		context->state = S_text;
+	    } else {
+	        context->state = S_tag_gap;
+	    }
+	}
+	break;
+
+    case S_exclamation:
+        if (context->lead_exclamation && c == '-') {
+	    /*
+	    **  Set up for possible comment. - FM
+	    */
+	    context->lead_exclamation = FALSE;
+	    context->first_dash = TRUE;
+	    HTChunkPutc(string, c);
+	    break;
+	}
+	if (context->lead_exclamation && c == '[') {
+	    /*
+	    **  Set up for possible marked section. - FM
+	    */
+	    context->lead_exclamation = FALSE;
+	    context->first_bracket = TRUE;
+	    context->second_bracket = FALSE;
+	    HTChunkPutc(string, c);
+	    context->state = S_marked;
+	    break;
+	}
+	if (context->first_dash && c == '-') {
+	    /*
+	    **  Set up to handle comment. - FM
+	    */
+	    context->lead_exclamation = FALSE;
+	    context->first_dash = FALSE;
+	    context->end_comment = FALSE;
+	    HTChunkPutc(string, c);
+	    context->state = S_comment;
+	    break;
+	}
+	context->lead_exclamation = FALSE;
+	context->first_dash = FALSE;
+	if (c == '>') {
+	    /*
+	    **  Try to handle identifier. - FM
+	    */
+	    HTChunkTerminate(string);
+	    handle_identifier(context);
+	    string->size = 0;
+	    context->state = S_text;
+	    break;
+	}
+	if (WHITE(c)) {
+	    if (string->size == 8 &&
+	        !strncasecomp(string->data, "!DOCTYPE", 8)) {
+		/*
+		**  Set up for DOCTYPE declaration. - FM
+		*/
+		HTChunkPutc(string, c);
+		context->doctype_bracket = FALSE;
+		context->state = S_doctype;
+	        break;
+	    }
+	    if (string->size == 7 &&
+	        !strncasecomp(string->data, "!ENTITY", 7)) {
+		/*
+		**  Set up for ENTITY declaration. - FM
+		*/
+		HTChunkPutc(string, c);
+		context->first_dash = FALSE;
+		context->end_comment = TRUE;
+		context->state = S_sgmlent;
+		break;
+	    }
+	    if (string->size == 8 &&
+	        !strncasecomp(string->data, "!ELEMENT", 8)) {
+		/*
+		**  Set up for ELEMENT declaration. - FM
+		*/
+		HTChunkPutc(string, c);
+		context->first_dash = FALSE;
+		context->end_comment = TRUE;
+		context->state = S_sgmlele;
+		break;
+	    }
+	    if (string->size == 8 &&
+	        !strncasecomp(string->data, "!ATTLIST", 8)) {
+		/*
+		**  Set up for ATTLIST declaration. - FM
+		*/
+		HTChunkPutc(string, c);
+		context->first_dash = FALSE;
+		context->end_comment = TRUE;
+		context->state = S_sgmlatt;
+		break;
+	    }
+	}
+	HTChunkPutc(string, c);
+	break;
+
+    case S_comment:		/* Expecting comment. - FM */
+        if (historical_comments) {
+	    /*
+	    **  Any '>' terminates. - FM
+	    */
+	    if (c == '>') {
+	        HTChunkTerminate(string);
+		handle_comment(context);
+		string->size = 0;
+		context->end_comment = FALSE;
+		context->first_dash = FALSE;
+		context->state = S_text;
+		break;
+	    }
+	    HTChunkPutc(string, c);
+	    break;
+	}
+        if (!context->first_dash && c == '-') {
+	    HTChunkPutc(string, c);
+	    context->first_dash = TRUE;
+	    break;
+	}
+	if (context->first_dash && c == '-') {
+	    HTChunkPutc(string, c);
+	    context->first_dash = FALSE;
+	    if (!context->end_comment)
+	        context->end_comment = TRUE;
+	    else if (!minimal_comments)
+	        /*
+		**  Validly treat '--' pairs as successive comments
+		**  (for minimal, any "--WHITE>" terminates). - FM
+		*/
+	        context->end_comment = FALSE;
+	    break;
+	}
+	if (context->end_comment && c == '>') {
+	    /*
+	    **  Terminate and handle the comment. - FM
+	    */
+	    HTChunkTerminate(string);
+	    handle_comment(context);
+	    string->size = 0;
+	    context->end_comment = FALSE;
+	    context->first_dash = FALSE;
+	    context->state = S_text;
+	    break;
+	}
+	context->first_dash = FALSE;
+	if (context->end_comment && !isspace(c))
+	    context->end_comment = FALSE;
+	HTChunkPutc(string, c);
+	break;
+
+    case S_doctype:		/* Expecting DOCTYPE. - FM */
+        if (context->doctype_bracket) {
+	    HTChunkPutc(string, c);
+	    if (c == ']')
+	        context->doctype_bracket = FALSE;
+	    break;
+	}
+	if (c == '[' && WHITE(string->data[string->size - 1])) {
+	    HTChunkPutc(string, c);
+	    context->doctype_bracket = TRUE;
+	    break;
+	}
+	if (c == '>') {
+	    HTChunkTerminate(string);
+	    handle_doctype(context);
+	    string->size = 0;
+	    context->state = S_text;
+	    break;
+	}
+	HTChunkPutc(string, c);
+	break;
+
+    case S_marked:		/* Expecting marked section. - FM */
+        if (context->first_bracket && c == '[') {
+	    HTChunkPutc(string, c);
+	    context->first_bracket = FALSE;
+	    context->second_bracket = TRUE;
+	    break;
+	}
+	if (context->second_bracket && c == ']' &&
+	    string->data[string->size - 1] == ']') {
+	    HTChunkPutc(string, c);
+	    context->second_bracket = FALSE;
+	    break;
+	}
+	if (!context->second_bracket && c == '>') {
+	    HTChunkTerminate(string);
+	    handle_marked(context);
+	    string->size = 0;
+	    context->state = S_text;
+	    break;
+	}
+	HTChunkPutc(string, c);
+	break;
+
+    case S_sgmlent:		/* Expecting ENTITY. - FM */
+        if (!context->first_dash && c == '-') {
+	    HTChunkPutc(string, c);
+	    context->first_dash = TRUE;
+	    break;
+	}
+	if (context->first_dash && c == '-') {
+	    HTChunkPutc(string, c);
+	    context->first_dash = FALSE;
+	    if (!context->end_comment)
+	        context->end_comment = TRUE;
+	    else
+	        context->end_comment = FALSE;
+	    break;
+	}
+	if (context->end_comment && c == '>') {
+	    HTChunkTerminate(string);
+	    handle_sgmlent(context);
+	    string->size = 0;
+	    context->end_comment = FALSE;
+	    context->first_dash = FALSE;
+	    context->state = S_text;
+	    break;
+	}
+	context->first_dash = FALSE;
+	HTChunkPutc(string, c);
+	break;
+
+    case S_sgmlele:		/* Expecting ELEMENT. - FM */
+        if (!context->first_dash && c == '-') {
+	    HTChunkPutc(string, c);
+	    context->first_dash = TRUE;
+	    break;
+	}
+	if (context->first_dash && c == '-') {
+	    HTChunkPutc(string, c);
+	    context->first_dash = FALSE;
+	    if (!context->end_comment)
+	        context->end_comment = TRUE;
+	    else
+	        context->end_comment = FALSE;
+	    break;
+	}
+	if (context->end_comment && c == '>') {
+	    HTChunkTerminate(string);
+	    handle_sgmlele(context);
+	    string->size = 0;
+	    context->end_comment = FALSE;
+	    context->first_dash = FALSE;
+	    context->state = S_text;
+	    break;
+	}
+	context->first_dash = FALSE;
+	HTChunkPutc(string, c);
+	break;
+
+    case S_sgmlatt:		/* Expecting ATTLIST. - FM */
+        if (!context->first_dash && c == '-') {
+	    HTChunkPutc(string, c);
+	    context->first_dash = TRUE;
+	    break;
+	}
+	if (context->first_dash && c == '-') {
+	    HTChunkPutc(string, c);
+	    context->first_dash = FALSE;
+	    if (!context->end_comment)
+	        context->end_comment = TRUE;
+	    else
+	        context->end_comment = FALSE;
+	    break;
+	}
+	if (context->end_comment && c == '>') {
+	    HTChunkTerminate(string);
+	    handle_sgmlatt(context);
+	    string->size = 0;
+	    context->end_comment = FALSE;
+	    context->first_dash = FALSE;
+	    context->state = S_text;
+	    break;
+	}
+	context->first_dash = FALSE;
+	HTChunkPutc(string, c);
+	break;
+
+    case S_tag_gap:		/* Expecting attribute or '>' */
+	if (WHITE(c))
+	    break;		/* Gap between attributes */
+	if (c == '>') {		/* End of tag */
+	    if (context->current_tag->name)
+	    	start_element(context);
+	    context->state = S_text;
+	    break;
+	}
+	HTChunkPutc(string, c);
+	context->state = S_attr; /* Get attribute */
+	break;
+	
+   				/* accumulating value */
+    case S_attr:
+	if (WHITE(c) || (c == '>') || (c == '=')) {	/* End of word */
+	    HTChunkTerminate(string);
+	    handle_attribute_name(context, string->data);
+	    string->size = 0;
+	    if (c == '>') {				/* End of tag */
+		if (context->current_tag->name)
+		    start_element(context);
+		context->state = S_text;
+		break;
+	    }
+	    context->state = (c == '=' ?  S_equals: S_attr_gap);
+	} else {
+	    HTChunkPutc(string, c);
+	}
+	break;
+		
+    case S_attr_gap:		/* Expecting attribute or '=' or '>' */
+	if (WHITE(c))
+	    break;		/* Gap after attribute */
+	if (c == '>') {		/* End of tag */
+	    if (context->current_tag->name)
+	        start_element(context);
+	    context->state = S_text;
+	    break;
+	} else if (c == '=') {
+	    context->state = S_equals;
+	    break;
+	}
+	HTChunkPutc(string, c);
+	context->state = S_attr;		/* Get next attribute */
+	break;
+	
+    case S_equals:		/* After attr = */ 
+	if (WHITE(c))
+	    break;		/* Before attribute value */
+	if (c == '>') {		/* End of tag */
+	    if (TRACE)
+	        fprintf(stderr, "SGML: found = but no value\n");
+	    if (context->current_tag->name)
+	        start_element(context);
+	    context->state = S_text;
+	    break;
+	    
+	} else if (c == '\'') {
+	    context->state = S_squoted;
+	    break;
+
+	} else if (c == '"') {
+	    context->state = S_dquoted;
+	    break;
+	}
+	HTChunkPutc(string, c);
+	context->state = S_value;
+	break;
+	
+    case S_value:
+	if (WHITE(c) || (c == '>')) {		/* End of word */
+	    HTChunkTerminate(string) ;
+	    handle_attribute_value(context, string->data);
+	    string->size = 0;
+	    if (c == '>') {		/* End of tag */
+		if (context->current_tag->name)
+		    start_element(context);
+		context->state = S_text;
+		break;
+	    }
+	    else context->state = S_tag_gap;
+	} else {
+	    HTChunkPutc(string, c);
+	}
+	break;
+		
+    case S_squoted:		/* Quoted attribute value */
+	if (c == '\'') {	/* End of attribute value */
+	    HTChunkTerminate(string) ;
+	    handle_attribute_value(context, string->data);
+	    string->size = 0;
+	    context->state = S_tag_gap;
+	} else if (c == '\033') {
+	    /*
+	    **  Setting up for possible single quotes in CJK escape
+	    **  sequences. - Takuya ASADA (asada@three-a.co.jp)
+	    */
+	    context->state = S_esc_sq;
+	    HTChunkPutc(string, c);
+	} else {
+	    HTChunkPutc(string, c);
+	}
+	break;
+	
+    case S_dquoted:		/* Quoted attribute value */
+	if (c == '"' ||		/* Valid end of attribute value */
+	    (soft_dquotes &&	/*  If emulating old Netscape bug, treat '>' */
+	     c == '>')) {	/*  as a co-terminator of dquoted and tag    */
+	    HTChunkTerminate(string) ;
+	    handle_attribute_value(context, string->data);
+	    string->size = 0;
+	    context->state = S_tag_gap;
+	    if (c == '>')	/* We emulated the Netscape bug, so we go  */
+	        goto top;	/* back and treat it as the tag terminator */
+	} else if (c == '\033') {
+	    /*
+	    **  Setting up for possible double quotes in CJK escape
+	    **  sequences. - Takuya ASADA (asada@three-a.co.jp)
+	    */
+	    context->state = S_esc_dq;
+	    HTChunkPutc(string, c);
+	} else {
+	    HTChunkPutc(string, c);
+	}
+	break;
+	
+    case S_end:					/* </ */
+	if ((unsigned char)c < 127 && isalnum((unsigned char)c))
+	    HTChunkPutc(string, c);
+	else {				/* End of end tag name */
+	    HTTag * t=0;
+	    HTChunkTerminate(string) ;
+	    if (!*string->data)	{	/* Empty end tag */
+		if (context->element_stack)
+	            t = context->element_stack->tag;
+	    } else {
+		t = SGMLFindTag(dtd, string->data);
+	    }
+	    if (!t) {
+		if (TRACE)
+		    fprintf(stderr, "Unknown end tag </%s>\n", string->data); 
+	    } else {
+		BOOL tag_OK = (c == '>' || WHITE(c));
+	        context->current_tag = t;
+		if (tag_OK &&
+		    (!strcasecomp(string->data, "DD") ||
+		     !strcasecomp(string->data, "DT") ||
+		     !strcasecomp(string->data, "LI") ||
+		     !strcasecomp(string->data, "LH") ||
+		     !strcasecomp(string->data, "TD") ||
+		     !strcasecomp(string->data, "TH") ||
+		     !strcasecomp(string->data, "TR") ||
+		     !strcasecomp(string->data, "THEAD") ||
+		     !strcasecomp(string->data, "TFOOT") ||
+		     !strcasecomp(string->data, "TBODY") ||
+		     !strcasecomp(string->data, "COLGROUP"))) {
+		    /*
+		    **  Don't treat these end tags as invalid,
+		    **  nor act on them. - FM
+		    */
+		    if (TRACE)
+		        fprintf(stderr,
+				"SGML: `</%s%c' found!  Ignoring it.\n",
+				string->data, c);
+		    string->size = 0;
+		    context->current_attribute_number = INVALID;
+		    if (c != '>') {
+			context->state = S_junk_tag;
+		    } else {
+			context->state = S_text;
+		    }
+		    break;
+		} else if (tag_OK &&
+			   !strcasecomp(string->data, "P")) {
+		    /*
+		    **  Treat a P end tag like a P start tag (Ugh,
+		    **  what a hack! 8-). - FM
+		    */
+		    if (TRACE)
+		        fprintf(stderr,
+				"SGML: `</%s%c' found!  Treating as '<%s%c'.\n",
+				string->data, c, string->data, c);
+		    {
+		        int i;
+			for (i = 0;
+			     i < context->current_tag->number_of_attributes;
+			     i++) {
+			    context->present[i] = NO;
+			}
+		    }
+		    string->size = 0;
+		    context->current_attribute_number = INVALID;
+		    if (context->current_tag->name)
+			start_element(context);
+		    if (c != '>') {
+			context->state = S_junk_tag;
+		    } else {
+			context->state = S_text;
+		    }
+		    break;
+		} else if (tag_OK &&
+			   !strcasecomp(string->data, "FONT")) {
+		    /*
+		    **  Treat a FONT end tag as a FONT start tag with
+		    **  a dummy END attribute.  It's too likely to be
+		    **  interdigited and mess up the parsing, so we've
+		    **  declared FONT as SGML_EMPTY and will handle the
+		    **  end tag in HTML_start_element. - FM
+		    */
+		    if (TRACE)
+		        fprintf(stderr,
+				"SGML: `</%s%c' found!  Treating as '<%s%c'.\n",
+				string->data, c, string->data, c);
+		    {
+		        int i;
+			for (i = 0;
+			     i < context->current_tag->number_of_attributes;
+			     i++) {
+			    context->present[i] = (i == HTML_FONT_END);
+			}
+		    }
+		    string->size = 0;
+		    context->current_attribute_number = INVALID;
+		    if (context->current_tag->name)
+			start_element(context);
+		    if (c != '>') {
+			context->state = S_junk_tag;
+		    } else {
+			context->state = S_text;
+		    }
+		    break;
+		} else {
+		    /*
+		    **  Handle all other end tags normally. - FM
+		    */
+		    end_element( context, context->current_tag);
+		}
+	    }
+
+	    string->size = 0;
+	    context->current_attribute_number = INVALID;
+	    if (c != '>') {
+		if (TRACE && !WHITE(c))
+		    fprintf(stderr,"SGML: `</%s%c' found!\n", string->data, c);
+		context->state = S_junk_tag;
+	    } else {
+	        context->state = S_text;
+	    }
+	}
+	break;
+
+
+    case S_esc:		/* Expecting '$'or '(' following CJK ESC. */
+	if (c == '$') {
+	    context->state = S_dollar;
+	} else if (c == '(') {
+	    context->state = S_paren;
+	} else {
+	    context->state = S_text;
+	}
+	PUTC(c);
+	break;
+
+    case S_dollar:	/* Expecting '@', 'B', 'A' or '(' after CJK "ESC$". */
+	if (c == '@' || c == 'B' || c == 'A') {
+	    context->state = S_nonascii_text;
+	} else if (c == '(') {
+	    context->state = S_dollar_paren;
+	}
+	PUTC(c);
+	break;
+
+    case S_dollar_paren: /* Expecting 'C' after CJK "ESC$(". */
+	if (c == 'C') {
+	    context->state = S_nonascii_text;
+	} else {
+	    context->state = S_text;
+	}
+	PUTC(c);
+	break;
+
+    case S_paren:	/* Expecting 'B', 'J', 'T' or 'I' after CJK "ESC(". */
+	if (c == 'B' || c == 'J' || c == 'T') {
+	    context->state = S_text;
+	} else if (c == 'I') {
+	    context->state = S_nonascii_text;
+	} else {
+	    context->state = S_text;
+	}
+	PUTC(c);
+	break;
+
+    case S_nonascii_text: /* Expecting CJK ESC after non-ASCII text. */
+	if (c == '\033') {
+	    context->state = S_esc;
+	}
+	PUTC(c);
+	break;
+
+    case S_esc_sq:	/* Expecting '$'or '(' following CJK ESC. */
+	if (c == '$') {
+	    context->state = S_dollar_sq;
+	} else if (c == '(') {
+	    context->state = S_paren_sq;
+	} else {
+	    context->state = S_squoted;
+	}
+	HTChunkPutc(string, c);
+	break;
+
+    case S_dollar_sq:	/* Expecting '@', 'B', 'A' or '(' after CJK "ESC$". */
+	if (c == '@' || c == 'B' || c == 'A') {
+	    context->state = S_nonascii_text_sq;
+	} else if (c == '(') {
+	    context->state = S_dollar_paren_sq;
+	}
+	HTChunkPutc(string, c);
+	break;
+
+    case S_dollar_paren_sq: /* Expecting 'C' after CJK "ESC$(". */
+	if (c == 'C') {
+	    context->state = S_nonascii_text_sq;
+	} else {
+	    context->state = S_squoted;
+	}
+	HTChunkPutc(string, c);
+	break;
+
+    case S_paren_sq:	/* Expecting 'B', 'J', 'T' or 'I' after CJK "ESC(". */
+	if (c == 'B' || c == 'J' || c == 'T') {
+	    context->state = S_squoted;
+	} else if (c == 'I') {
+	    context->state = S_nonascii_text_sq;
+	} else {
+	    context->state = S_squoted;
+	}
+	HTChunkPutc(string, c);
+	break;
+
+    case S_nonascii_text_sq: /* Expecting CJK ESC after non-ASCII text. */
+	if (c == '\033') {
+	    context->state = S_esc_sq;
+	}
+	HTChunkPutc(string, c);
+	break;
+
+    case S_esc_dq:		/* Expecting '$'or '(' following CJK ESC. */
+	if (c == '$') {
+	    context->state = S_dollar_dq;
+	} else if (c == '(') {
+	    context->state = S_paren_dq;
+	} else {
+	    context->state = S_dquoted;
+	}
+	HTChunkPutc(string, c);
+	break;
+
+    case S_dollar_dq:	/* Expecting '@', 'B', 'A' or '(' after CJK "ESC$". */
+	if (c == '@' || c == 'B' || c == 'A') {
+	    context->state = S_nonascii_text_dq;
+	} else if (c == '(') {
+	    context->state = S_dollar_paren_dq;
+	}
+	HTChunkPutc(string, c);
+	break;
+
+    case S_dollar_paren_dq: /* Expecting 'C' after CJK "ESC$(". */
+	if (c == 'C') {
+	    context->state = S_nonascii_text_dq;
+	} else {
+	    context->state = S_dquoted;
+	}
+	HTChunkPutc(string, c);
+	break;
+
+    case S_paren_dq:	/* Expecting 'B', 'J', 'T' or 'I' after CJK "ESC(". */
+	if (c == 'B' || c == 'J' || c == 'T') {
+	    context->state = S_dquoted;
+	} else if (c == 'I') {
+	    context->state = S_nonascii_text_dq;
+	} else {
+	    context->state = S_dquoted;
+	}
+	HTChunkPutc(string, c);
+	break;
+
+    case S_nonascii_text_dq: /* Expecting CJK ESC after non-ASCII text. */
+	if (c == '\033') {
+	    context->state = S_esc_dq;
+	}
+	HTChunkPutc(string, c);
+	break;
+
+    case S_junk_tag:
+	if (c == '>') {
+	    context->state = S_text;
+	}
+    } /* switch on context->state */
+
+    /*
+    **  Check whether we've added anything to the recover buffer. - FM
+    */
+    if (context->recover != NULL) {
+        if (context->recover[context->recover_index] == '\0') {
+	    FREE(context->recover);
+	    context->recover_index = 0;
+	} else {
+	    c = context->recover[context->recover_index];
+	    context->recover_index++;
+	    goto top;
+	}
+    }
+
+    /*
+    **  Check whether an external function has added
+    **  anything to the include buffer. - FM
+    */
+    if (context->include != NULL) {
+        if (context->include[context->include_index] == '\0') {
+	    FREE(context->include);
+	    context->include_index = 0;
+	} else {
+	    c = context->include[context->include_index];
+	    context->include_index++;
+	    goto top;
+	}
+    }
+
+    /*
+    **  Check whether an external function has added
+    **  anything to the csi buffer. - FM
+    */
+    if (context->csi != NULL) {
+        if (context->csi[context->csi_index] == '\0') {
+	    FREE(context->csi);
+	    context->csi_index = 0;
+	} else {
+	    c = context->csi[context->csi_index];
+	    context->csi_index++;
+	    goto top;
+	}
+    }
+}  /* SGML_character */
+
+
+PUBLIC void SGML_string ARGS2(
+	HTStream *,	context,
+	CONST char*,	str)
+{
+    CONST char *p;
+    for (p = str; *p; p++)
+        SGML_character(context, *p);
+}
+
+
+PUBLIC void SGML_write ARGS3(
+	HTStream *,	context,
+	CONST char*,	str,
+	int,		l)
+{
+    CONST char *p;
+    CONST char *e = str+l;
+    for (p = str; p < e; p++)
+        SGML_character(context, *p);
+}
+
+/*_______________________________________________________________________
+*/
+
+/*	Structured Object Class
+**	-----------------------
+*/
+PUBLIC CONST HTStreamClass SGMLParser = 
+{		
+	"SGMLParser",
+	SGML_free,
+	SGML_abort,
+	SGML_character, 
+	SGML_string,
+	SGML_write,
+}; 
+
+/*	Create SGML Engine
+**	------------------
+**
+** On entry,
+**	dtd		represents the DTD, along with
+**	actions		is the sink for the data as a set of routines.
+**
+*/
+
+PUBLIC HTStream* SGML_new  ARGS2(
+	CONST SGML_dtd *,	dtd,
+	HTStructured *,		target)
+{
+    int i;
+    HTStream* context = (HTStream *) malloc(sizeof(*context));
+    if (!context)
+        outofmem(__FILE__, "SGML_begin");
+
+    context->isa = &SGMLParser;
+    context->string = HTChunkCreate(128);	/* Grow by this much */
+    context->dtd = dtd;
+    context->target = target;
+    context->actions = (HTStructuredClass*)(((HTStream*)target)->isa);
+    					/* Ugh: no OO */
+    context->state = S_text;
+    context->element_stack = 0;			/* empty */
+#ifdef CALLERDATA		  
+    context->callerData = (void*) callerData;
+#endif /* CALLERDATA */
+    for (i = 0; i < MAX_ATTRIBUTES; i++)
+        context->value[i] = 0;
+
+    context->lead_exclamation = FALSE;
+    context->first_dash = FALSE;
+    context->end_comment = FALSE;
+    context->doctype_bracket = FALSE;
+    context->first_bracket = FALSE;
+    context->second_bracket = FALSE;
+    context->recover = NULL;
+
+    context->recover_index = 0;
+    context->include = NULL;
+    context->include_index = 0;
+    context->url = NULL;
+    context->csi = NULL;
+    context->csi_index = 0;
+
+    return context;
+}
+
+/*		Asian character conversion functions
+**		====================================
+**
+**	Added 24-Mar-96 by FM, based on:
+**
+////////////////////////////////////////////////////////////////////////
+Copyright (c) 1993 Electrotechnical Laboratry (ETL)
+
+Permission to use, copy, modify, and distribute this material 
+for any purpose and without fee is hereby granted, provided 
+that the above copyright notice and this permission notice 
+appear in all copies, and that the name of ETL not be 
+used in advertising or publicity pertaining to this 
+material without the specific, prior written permission 
+of an authorized representative of ETL.
+ETL MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY 
+OF THIS MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS", 
+WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
+/////////////////////////////////////////////////////////////////////////
+Content-Type:	program/C; charset=US-ASCII
+Program:	SJIS.c
+Author:		Yutaka Sato <ysato@etl.go.jp>
+Description:
+History:
+	930923	extracted from codeconv.c of cosmos
+///////////////////////////////////////////////////////////////////////
+*/
+
+PUBLIC int TREAT_SJIS = 1;
+
+PUBLIC void JISx0201TO0208_EUC ARGS4(
+	register unsigned char,		IHI,
+	register unsigned char,		ILO,
+	register unsigned char *,	OHI,
+	register unsigned char *,	OLO)
+{
+    static char *table[] = { 
+	"\xA1\xA3", "\xA1\xD6", "\xA1\xD7", "\xA1\xA2", "\xA1\xA6", "\xA5\xF2",
+	"\xA5\xA1", "\xA5\xA3", "\xA5\xA5", "\xA5\xA7", "\xA5\xA9",
+	"\xA5\xE3", "\xA5\xE5", "\xA5\xE7", "\xA5\xC3", "\xA1\xBC",
+	"\xA5\xA2", "\xA5\xA4", "\xA5\xA6", "\xA5\xA8", "\xA5\xAA",
+	"\xA5\xAB", "\xA5\xAD", "\xA5\xAF", "\xA5\xB1", "\xA5\xB3",
+	"\xA5\xB5", "\xA5\xB7", "\xA5\xB9", "\xA5\xBB", "\xA5\xBD",
+	"\xA5\xBF", "\xA5\xC1", "\xA5\xC4", "\xA5\xC6", "\xA5\xC8",
+	"\xA5\xCA", "\xA5\xCB", "\xA5\xCC", "\xA5\xCD", "\xA5\xCE",
+	"\xA5\xCF", "\xA5\xD2", "\xA5\xD5", "\xA5\xD8", "\xA5\xDB",
+	"\xA5\xDE", "\xA5\xDF", "\xA5\xE0", "\xA5\xE1", "\xA5\xE2",
+	"\xA5\xE4", "\xA5\xE6", "\xA5\xE8", "\xA5\xE9", "\xA5\xEA",
+	"\xA5\xEB", "\xA5\xEC", "\xA5\xED", "\xA5\xEF", "\xA5\xF3",
+	"\xA1\xAB", "\xA1\xAC"
+    };
+
+    if ((IHI == 0x8E) && (ILO >= 0xA1) && (ILO <= 0xDF)) {
+	*OHI = table[ILO - 0xA1][0];
+	*OLO = table[ILO - 0xA1][1];
+    } else {
+	*OHI = IHI;
+	*OLO = ILO;
+    }
+}
+
+PUBLIC unsigned char * SJIS_TO_JIS1 ARGS3(
+	register unsigned char,		HI,
+	register unsigned char,		LO,
+	register unsigned char *,	JCODE)
+{
+    HI -= (HI <= 0x9F) ? 0x71 : 0xB1;
+    HI = (HI << 1) + 1;
+    if (0x7F < LO)
+	LO--;
+    if (0x9E <= LO) {
+	LO -= 0x7D;
+	HI++;
+    } else {
+        LO -= 0x1F;
+    }
+    JCODE[0] = HI;
+    JCODE[1] = LO;
+    return JCODE;
+}
+
+PUBLIC unsigned char * JIS_TO_SJIS1 ARGS3(
+	register unsigned char,		HI,
+	register unsigned char,		LO,
+	register unsigned char *,	SJCODE)
+{
+    if (HI & 1)
+	LO += 0x1F;
+    else
+	LO += 0x7D;
+    if (0x7F <= LO)
+	LO++;
+
+    HI = ((HI - 0x21) >> 1) + 0x81;
+    if (0x9F < HI)
+	HI += 0x40;
+    SJCODE[0] = HI;
+    SJCODE[1] = LO;
+    return SJCODE;
+}
+
+PUBLIC unsigned char * EUC_TO_SJIS1 ARGS3(
+	unsigned char,			HI,
+	unsigned char,			LO,
+	register unsigned char *,	SJCODE)
+{
+    if (HI == 0x8E) JISx0201TO0208_EUC(HI, LO, &HI, &LO);
+    JIS_TO_SJIS1(HI&0x7F, LO&0x7F, SJCODE);
+    return SJCODE;
+}
+
+PUBLIC void JISx0201TO0208_SJIS ARGS3(
+	register unsigned char,		I,
+	register unsigned char *,	OHI,
+	register unsigned char *,	OLO)
+{
+    unsigned char SJCODE[2];
+
+    JISx0201TO0208_EUC('\x8E', I, OHI, OLO);
+    JIS_TO_SJIS1(*OHI&0x7F, *OLO&0x7F, SJCODE);
+    *OHI = SJCODE[0];
+    *OLO = SJCODE[1];
+}
+
+PUBLIC unsigned char * SJIS_TO_EUC1 ARGS3(
+	unsigned char,		HI,
+	unsigned char,		LO,
+	unsigned char *,	EUC)
+{
+    SJIS_TO_JIS1(HI, LO, EUC);
+    EUC[0] |= 0x80;
+    EUC[1] |= 0x80;
+    return EUC;
+}
+
+PUBLIC unsigned char * SJIS_TO_EUC ARGS2(
+	unsigned char *,	src,
+	unsigned char *,	dst)
+{
+    register unsigned char hi, lo, *sp, *dp;
+    register int in_sjis = 0;
+
+    for (sp = src, dp = dst; (0 != (hi = sp[0]));) {
+	lo = sp[1];
+	if (TREAT_SJIS && IS_SJIS(hi, lo, in_sjis)) {
+	    SJIS_TO_JIS1(hi,lo,dp);
+	    dp[0] |= 0x80;
+	    dp[1] |= 0x80;
+	    dp += 2;
+	    sp += 2;
+	} else {
+	    *dp++ = *sp++;
+	}
+    }
+    *dp = 0;
+    return dst;
+}
+
+PUBLIC unsigned char * EUC_TO_SJIS ARGS2(
+	unsigned char *,	src,
+	unsigned char *,	dst)
+{
+    register unsigned char *sp, *dp;
+
+    for (sp = src, dp = dst; *sp;) {
+	if (*sp & 0x80) {
+	    if (sp[1] && (sp[1] & 0x80)) {
+		JIS_TO_SJIS1(sp[0]&0x7F, sp[1]&0x7F, dp);
+		dp += 2;
+		sp += 2;
+	    } else {
+	        sp++;
+	    }
+	} else {
+	    *dp++ = *sp++;
+	}
+    }
+    *dp = 0;
+    return dst;
+}
+
+PUBLIC unsigned char * EUC_TO_JIS ARGS4(
+	unsigned char *,	src,
+	unsigned char *,	dst,
+	CONST char *,		toK,
+	CONST char *,		toA)
+{
+    register unsigned char kana_mode = 0;
+    register unsigned char cch;
+    register unsigned char *sp = src;
+    register unsigned char *dp = dst;
+    register int i;
+
+    while (0 != (cch = *sp++)) {
+	if (cch & 0x80) {
+	    if (!kana_mode) {
+		kana_mode = ~kana_mode;
+		for (i = 0; toK[i]; i++) {
+		    *dp++ = (unsigned char)toK[i];
+		}
+	    }
+	    if (*sp & 0x80) {
+		*dp++ = cch & ~0x80;
+		*dp++ = *sp++ & ~0x80;
+	    }
+	} else {
+	    if (kana_mode) {
+		kana_mode = ~kana_mode;
+		for (i = 0; toA[i]; i++) {
+		    *dp++ = (unsigned char)toA[i];
+		    *dp = '\0';
+		}
+	    }
+	    *dp++ = cch;
+	}
+    }
+    if (kana_mode) {
+	for (i = 0; toA[i]; i++) {
+	    *dp++ = (unsigned char)toA[i];
+	}
+    }
+
+    if (dp)
+        *dp = 0;
+    return dst;
+}
+
+PUBLIC unsigned char * TO_EUC ARGS2(
+	unsigned char *,	jis,
+	unsigned char *,	euc)
+{
+    register unsigned char *s, *d, c, jis_stat;
+    register to1B, to2B;
+    register int in_sjis = 0;
+
+    s = jis;
+    d = euc;
+    jis_stat = 0;
+    to2B = TO_2BCODE;
+    to1B = TO_1BCODE;
+
+    while (0 != (c = *s++)) {
+	if (c == ESC) {
+	    if (*s == to2B) {
+		if ((s[1] == 'B') || (s[1] == '@') || (s[1] == 'A')) {
+		    jis_stat = 0x80;
+		    s += 2;
+		    continue;
+		} else if ((s[1] == '(') && s[2] && (s[2] == 'C')) {
+		    jis_stat = 0x80;
+		    s += 3;
+		    continue;
+		}
+	    } else {
+		if (*s == to1B) {
+		    if ((s[1]=='B') || (s[1]=='J') ||
+			(s[1]=='H') || (s[1]=='T')) {
+			jis_stat = 0;
+			s += 2;
+			continue;
+		    }
+		}
+	    }
+	}
+	if (IS_SJIS(c,*s,in_sjis)) {
+	    SJIS_TO_EUC1(c, *s, d);
+	    d += 2;
+	    s++;
+	} else {
+	    if (jis_stat && (0x20 < c)) {
+		*d++ = jis_stat | c;
+	    } else {
+	        *d++ = c;
+	    }
+	}
+    }
+    *d = 0;
+    return euc;
+}
+
+PUBLIC void TO_SJIS ARGS2(
+	unsigned char *,	any,
+	unsigned char *,	sjis)
+{
+    unsigned char *euc;
+
+    if (!any || !sjis)
+       return;
+
+    euc = (unsigned char*)malloc(strlen((CONST char *)any)+1);
+    if (euc == NULL)
+	outofmem(__FILE__, "TO_SJIS");
+
+    TO_EUC(any, euc);
+    EUC_TO_SJIS(euc, sjis);
+    FREE(euc);
+}
+
+PUBLIC void TO_JIS ARGS2(
+	unsigned char *,	any,
+	unsigned char *,	jis)
+{
+    unsigned char *euc;
+
+    if (!any || !jis)
+       return;
+
+    euc = (unsigned char*)malloc(strlen((CONST char *)any)+1);
+    if (euc == NULL)
+	outofmem(__FILE__, "TO_JIS");
+
+    TO_EUC(any, euc);
+    EUC_TO_JIS(euc, jis, TO_KANJI, TO_ASCII);
+    FREE(euc);
+}
diff --git a/WWW/Library/Implementation/SGML.h b/WWW/Library/Implementation/SGML.h
new file mode 100644
index 00000000..5fa799d5
--- /dev/null
+++ b/WWW/Library/Implementation/SGML.h
@@ -0,0 +1,202 @@
+/*                                                SGML parse and stream definition for libwww
+                               SGML AND STRUCTURED STREAMS
+                                             
+   The SGML parser is a state machine. It is called for every character
+   
+   of the input stream. The DTD data structure contains pointers
+   
+   to functions which are called to implement the actual effect of the
+   
+   text read. When these functions are called, the attribute structures pointed to by the
+   DTD are valid, and the function is passed a pointer to the curent tag structure, and an
+   "element stack" which represents the state of nesting within SGML elements.
+   
+   The following aspects are from Dan Connolly's suggestions:  Binary search, Strcutured
+   object scheme basically, SGML content enum type.
+   
+   (c) Copyright CERN 1991 - See Copyright.html
+   
+ */
+#ifndef SGML_H
+#define SGML_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* HTUTILS_H */
+#include "HTStream.h"
+
+/*
+
+SGML content types
+
+ */
+typedef enum _SGMLContent{
+  SGML_EMPTY,    /* no content */
+  SGML_LITTERAL, /* character data. Recognised excat close tag only. litteral
+                    Old www server compatibility only! Not SGML */
+  SGML_CDATA,    /* character data. recognize </ only */
+  SGML_RCDATA,   /* replaceable character data. recognize </ and &ref; */
+  SGML_MIXED,    /* elements and parsed character data. recognize all markup */
+  SGML_ELEMENT   /* any data found will be returned as an error*/
+  } SGMLContent;
+
+
+typedef struct {
+    char *      name;           /* The (constant) name of the attribute */
+                                /* Could put type info in here */
+} attr;
+
+
+/*              A tag structure describes an SGML element.
+**              -----------------------------------------
+**
+**
+**      name            is the string which comes after the tag opener "<".
+**
+**      attributes      points to a zero-terminated array
+**                      of attribute names.
+**
+**      litteral        determines how the SGML engine parses the charaters
+**                      within the element. If set, tag openers are ignored
+**                      except for that which opens a matching closing tag.
+**
+*/
+typedef struct _tag HTTag;
+struct _tag{
+    char *      name;                   /* The name of the tag */
+    attr *      attributes;             /* The list of acceptable attributes */
+    int         number_of_attributes;   /* Number of possible attributes */
+    SGMLContent contents;               /* End only on end tag @@ */
+};
+
+
+
+
+/*              DTD Information
+**              ---------------
+**
+** Not the whole DTD, but all this parser usues of it.
+*/
+typedef struct {
+    HTTag *             tags;           /* Must be in strcmp order by name */
+    int                 number_of_tags;
+    CONST char **       entity_names;   /* Must be in strcmp order by name */
+    int                 number_of_entities;
+} SGML_dtd;
+
+
+/*      SGML context passed to parsers
+*/
+typedef struct _HTSGMLContext *HTSGMLContext;   /* Hidden */
+
+
+/*__________________________________________________________________________
+*/
+
+/*
+
+Structured Object definition
+
+   A structured object is something which can reasonably be represented in SGML.  I'll
+   rephrase that.  A structured object is am ordered tree-structured arrangement of data
+   which is representable as text.The SGML parer outputs to a Structured object. A
+   Structured object can output its contents to another Structured Object. It's a kind of
+   typed stream. The architecure is largely Dan Conolly's. Elements and entities are
+   passed to the sob by number, implying a knowledge of the DTD. Knowledge of the SGML
+   syntax is not here, though.
+   
+   Superclass: HTStream
+   
+   The creation methods will vary on the type of Structured Object.Maybe the callerData is
+   enough info to pass along.
+   
+ */
+typedef struct _HTStructured HTStructured;
+
+typedef struct _HTStructuredClass{
+
+        char*  name;                            /* Just for diagnostics */
+
+        void (*_free) PARAMS((
+                HTStructured*   me));
+
+        void (*_abort) PARAMS((
+                HTStructured*   me,
+                HTError         e));
+                
+        void (*put_character) PARAMS((
+                HTStructured*   me,
+                char            ch));
+                                
+        void (*put_string) PARAMS((
+                HTStructured*   me,
+                CONST char *    str));
+                
+        void (*_write) PARAMS((
+                HTStructured*   me,
+                CONST char *    str,
+                int             len));
+                
+        void (*start_element) PARAMS((
+                HTStructured*   me,
+                int             element_number,
+                CONST BOOL*     attribute_present,
+                CONST char**    attribute_value,
+		char **		include));
+                
+        void (*end_element) PARAMS((
+                HTStructured*   me,
+                int             element_number,
+		char **		include));
+
+        void (*put_entity) PARAMS((
+                HTStructured*   me,
+                int             entity_number));
+                
+}HTStructuredClass;
+
+/*
+
+Find a Tag by Name
+
+   Returns a pointer to the tag within the DTD.
+   
+ */
+extern HTTag * SGMLFindTag PARAMS((CONST SGML_dtd* dtd, CONST char * string));
+
+
+/*
+
+Create an SGML parser
+
+ */
+/*
+** On entry,
+**      dtd             must point to a DTD structure as defined above
+**      callbacks       must point to user routines.
+**      callData        is returned in callbacks transparently.
+** On exit,
+**              The default tag starter has been processed.
+*/
+
+
+extern HTStream* SGML_new PARAMS((
+        CONST SGML_dtd *                dtd,
+        HTStructured *          target));
+
+extern CONST HTStreamClass SGMLParser;
+
+
+#endif  /* SGML_H */
+
+
+
+
+
+
+
+
+
+/*
+
+    */
diff --git a/WWW/Library/Implementation/Version.make b/WWW/Library/Implementation/Version.make
new file mode 100644
index 00000000..4b4b380f
--- /dev/null
+++ b/WWW/Library/Implementation/Version.make
@@ -0,0 +1 @@
+VC = 2.14
diff --git a/WWW/Library/Implementation/crypt.c b/WWW/Library/Implementation/crypt.c
new file mode 100644
index 00000000..ffc466c7
--- /dev/null
+++ b/WWW/Library/Implementation/crypt.c
@@ -0,0 +1,129 @@
+/*
+ * UFC-crypt: ultra fast crypt(3) implementation
+ *
+ * Copyright (C) 1991, 1992, Free Software Foundation, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * @(#)crypt.c	2.19 5/28/92
+ *
+ * Semiportable C version
+ *
+ */
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif
+
+#include "ufc-crypt.h"
+
+#include "LYLeaks.h"
+
+#ifdef _UFC_32_
+
+/*
+ * 32 bit version
+ */
+
+extern long32 _ufc_keytab[16][2];
+extern long32 _ufc_sb0[], _ufc_sb1[], _ufc_sb2[], _ufc_sb3[];
+
+#define SBA(sb, v) (*(long32*)((char*)(sb)+(v)))
+
+static ufc_long ary[4];
+
+ufc_long *_ufc_doit(l1, l2, r1, r2, itr)
+  ufc_long l1, l2, r1, r2, itr;
+  { int i;
+    long32 s, *k;
+    register long32 *sb0 = _ufc_sb0;
+    register long32 *sb1 = _ufc_sb1;
+    register long32 *sb2 = _ufc_sb2;
+    register long32 *sb3 = _ufc_sb3;
+
+    while(itr--) {
+      k = &_ufc_keytab[0][0];
+      for(i=8; i--; ) {
+	s = *k++ ^ r1;
+	l1 ^= SBA(sb1, s & 0xffff); l2 ^= SBA(sb1, (s & 0xffff)+4);  
+        l1 ^= SBA(sb0, s >>= 16);   l2 ^= SBA(sb0, (s)         +4); 
+        s = *k++ ^ r2; 
+        l1 ^= SBA(sb3, s & 0xffff); l2 ^= SBA(sb3, (s & 0xffff)+4);
+        l1 ^= SBA(sb2, s >>= 16);   l2 ^= SBA(sb2, (s)         +4);
+
+        s = *k++ ^ l1; 
+        r1 ^= SBA(sb1, s & 0xffff); r2 ^= SBA(sb1, (s & 0xffff)+4);  
+        r1 ^= SBA(sb0, s >>= 16);   r2 ^= SBA(sb0, (s)         +4); 
+        s = *k++ ^ l2; 
+        r1 ^= SBA(sb3, s & 0xffff); r2 ^= SBA(sb3, (s & 0xffff)+4);  
+        r1 ^= SBA(sb2, s >>= 16);   r2 ^= SBA(sb2, (s)         +4);
+      } 
+      s=l1; l1=r1; r1=s; s=l2; l2=r2; r2=s;
+    }
+    ary[0] = l1; ary[1] = l2; ary[2] = r1; ary[3] = r2;
+    return ary;
+  }
+
+#endif
+
+#ifdef _UFC_64_
+
+/*
+ * 64 bit version
+ */
+
+extern long64 _ufc_keytab[16];
+extern long64 _ufc_sb0[], _ufc_sb1[], _ufc_sb2[], _ufc_sb3[];
+
+#define SBA(sb, v) (*(long64*)((char*)(sb)+(v)))
+
+static ufc_long ary[4];
+
+ufc_long *_ufc_doit(l1, l2, r1, r2, itr)
+  ufc_long l1, l2, r1, r2, itr;
+  { int i;
+    long64 l, r, s, *k;
+    register long64 *sb0 = _ufc_sb0;
+    register long64 *sb1 = _ufc_sb1;
+    register long64 *sb2 = _ufc_sb2;
+    register long64 *sb3 = _ufc_sb3;
+
+    l = (((long64)l1) << 32) | ((long64)l2);
+    r = (((long64)r1) << 32) | ((long64)r2);
+
+    while(itr--) {
+      k = &_ufc_keytab[0];
+      for(i=8; i--; ) {
+	s = *k++ ^ r;
+	l ^= SBA(sb3, (s >>  0) & 0xffff);
+        l ^= SBA(sb2, (s >> 16) & 0xffff);
+        l ^= SBA(sb1, (s >> 32) & 0xffff);
+        l ^= SBA(sb0, (s >> 48) & 0xffff);
+
+	s = *k++ ^ l;
+	r ^= SBA(sb3, (s >>  0) & 0xffff);
+        r ^= SBA(sb2, (s >> 16) & 0xffff);
+        r ^= SBA(sb1, (s >> 32) & 0xffff);
+        r ^= SBA(sb0, (s >> 48) & 0xffff);
+      } 
+      s=l; l=r; r=s;
+    }
+
+    ary[0] = l >> 32; ary[1] = l & 0xffffffff;
+    ary[2] = r >> 32; ary[3] = r & 0xffffffff;
+    return ary;
+  }
+
+#endif
diff --git a/WWW/Library/Implementation/crypt_util.c b/WWW/Library/Implementation/crypt_util.c
new file mode 100644
index 00000000..9ed7e95d
--- /dev/null
+++ b/WWW/Library/Implementation/crypt_util.c
@@ -0,0 +1,981 @@
+/*
+ * UFC-crypt: ultra fast crypt(3) implementation
+ *
+ * Copyright (C) 1991, 1992, Free Software Foundation, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * @(#)crypt_util.c	2.40 09/21/92
+ *
+ * Support routines
+ *
+ */
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif
+
+#ifdef DEBUG
+/*#include <stdio.h>  included by HTUTils.h - FM */
+#endif
+
+#ifndef STATIC
+#define STATIC static
+#endif
+
+#ifndef DOS
+#include "patchlevel.h"
+#include "ufc-crypt.h"
+#else
+/*
+ * Thanks to greg%wind@plains.NoDak.edu (Greg W. Wettstein)
+ * for DOS patches
+ */
+#include "pl.h"
+#include "ufc.h"
+#endif
+
+#include "LYLeaks.h"
+
+static char patchlevel_str[] = PATCHLEVEL;
+
+/* 
+ * Permutation done once on the 56 bit 
+ *  key derived from the original 8 byte ASCII key.
+ */
+static int pc1[56] = { 
+  57, 49, 41, 33, 25, 17,  9,  1, 58, 50, 42, 34, 26, 18,
+  10,  2, 59, 51, 43, 35, 27, 19, 11,  3, 60, 52, 44, 36,
+  63, 55, 47, 39, 31, 23, 15,  7, 62, 54, 46, 38, 30, 22,
+  14,  6, 61, 53, 45, 37, 29, 21, 13,  5, 28, 20, 12,  4
+};
+
+/*
+ * How much to rotate each 28 bit half of the pc1 permutated
+ *  56 bit key before using pc2 to give the i' key
+ */
+static int rots[16] = { 
+  1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 
+};
+
+/* 
+ * Permutation giving the key 
+ * of the i' DES round 
+ */
+static int pc2[48] = { 
+  14, 17, 11, 24,  1,  5,  3, 28, 15,  6, 21, 10,
+  23, 19, 12,  4, 26,  8, 16,  7, 27, 20, 13,  2,
+  41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48,
+  44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32
+};
+
+/*
+ * The E expansion table which selects
+ * bits from the 32 bit intermediate result.
+ */
+static int esel[48] = { 
+  32,  1,  2,  3,  4,  5,  4,  5,  6,  7,  8,  9,
+   8,  9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17,
+  16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25,
+  24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32,  1
+};
+static int e_inverse[64];
+
+/* 
+ * Permutation done on the 
+ * result of sbox lookups 
+ */
+static int perm32[32] = {
+  16,  7, 20, 21, 29, 12, 28, 17,  1, 15, 23, 26,  5, 18, 31, 10,
+  2,   8, 24, 14, 32, 27,  3,  9, 19, 13, 30,  6, 22, 11,  4, 25
+};
+
+/* 
+ * The sboxes
+ */
+static int sbox[8][4][16]= {
+        { { 14,  4, 13,  1,  2, 15, 11,  8,  3, 10,  6, 12,  5,  9,  0,  7 },
+          {  0, 15,  7,  4, 14,  2, 13,  1, 10,  6, 12, 11,  9,  5,  3,  8 },
+          {  4,  1, 14,  8, 13,  6,  2, 11, 15, 12,  9,  7,  3, 10,  5,  0 },
+          { 15, 12,  8,  2,  4,  9,  1,  7,  5, 11,  3, 14, 10,  0,  6, 13 }
+        },
+
+        { { 15,  1,  8, 14,  6, 11,  3,  4,  9,  7,  2, 13, 12,  0,  5, 10 },
+          {  3, 13,  4,  7, 15,  2,  8, 14, 12,  0,  1, 10,  6,  9, 11,  5 },
+          {  0, 14,  7, 11, 10,  4, 13,  1,  5,  8, 12,  6,  9,  3,  2, 15 },
+          { 13,  8, 10,  1,  3, 15,  4,  2, 11,  6,  7, 12,  0,  5, 14,  9 }
+        },
+
+        { { 10,  0,  9, 14,  6,  3, 15,  5,  1, 13, 12,  7, 11,  4,  2,  8 },
+          { 13,  7,  0,  9,  3,  4,  6, 10,  2,  8,  5, 14, 12, 11, 15,  1 },
+          { 13,  6,  4,  9,  8, 15,  3,  0, 11,  1,  2, 12,  5, 10, 14,  7 },
+          {  1, 10, 13,  0,  6,  9,  8,  7,  4, 15, 14,  3, 11,  5,  2, 12 }
+        },
+
+        { {  7, 13, 14,  3,  0,  6,  9, 10,  1,  2,  8,  5, 11, 12,  4, 15 },
+          { 13,  8, 11,  5,  6, 15,  0,  3,  4,  7,  2, 12,  1, 10, 14,  9 },
+          { 10,  6,  9,  0, 12, 11,  7, 13, 15,  1,  3, 14,  5,  2,  8,  4 },
+          {  3, 15,  0,  6, 10,  1, 13,  8,  9,  4,  5, 11, 12,  7,  2, 14 }
+        },
+
+        { {  2, 12,  4,  1,  7, 10, 11,  6,  8,  5,  3, 15, 13,  0, 14,  9 },
+          { 14, 11,  2, 12,  4,  7, 13,  1,  5,  0, 15, 10,  3,  9,  8,  6 },
+          {  4,  2,  1, 11, 10, 13,  7,  8, 15,  9, 12,  5,  6,  3,  0, 14 },
+          { 11,  8, 12,  7,  1, 14,  2, 13,  6, 15,  0,  9, 10,  4,  5,  3 }
+        },
+
+        { { 12,  1, 10, 15,  9,  2,  6,  8,  0, 13,  3,  4, 14,  7,  5, 11 },
+          { 10, 15,  4,  2,  7, 12,  9,  5,  6,  1, 13, 14,  0, 11,  3,  8 },
+          {  9, 14, 15,  5,  2,  8, 12,  3,  7,  0,  4, 10,  1, 13, 11,  6 },
+          {  4,  3,  2, 12,  9,  5, 15, 10, 11, 14,  1,  7,  6,  0,  8, 13 }
+        },
+
+        { {  4, 11,  2, 14, 15,  0,  8, 13,  3, 12,  9,  7,  5, 10,  6,  1 },
+          { 13,  0, 11,  7,  4,  9,  1, 10, 14,  3,  5, 12,  2, 15,  8,  6 },
+          {  1,  4, 11, 13, 12,  3,  7, 14, 10, 15,  6,  8,  0,  5,  9,  2 },
+          {  6, 11, 13,  8,  1,  4, 10,  7,  9,  5,  0, 15, 14,  2,  3, 12 }
+        },
+
+        { { 13,  2,  8,  4,  6, 15, 11,  1, 10,  9,  3, 14,  5,  0, 12,  7 },
+          {  1, 15, 13,  8, 10,  3,  7,  4, 12,  5,  6, 11,  0, 14,  9,  2 },
+          {  7, 11,  4,  1,  9, 12, 14,  2,  0,  6, 10, 13, 15,  3,  5,  8 },
+          {  2,  1, 14,  7,  4, 10,  8, 13, 15, 12,  9,  0,  3,  5,  6, 11 }
+        }
+};
+
+/* 
+ * This is the initial 
+ * permutation matrix
+ */
+static int initial_perm[64] = { 
+  58, 50, 42, 34, 26, 18, 10,  2, 60, 52, 44, 36, 28, 20, 12, 4,
+  62, 54, 46, 38, 30, 22, 14,  6, 64, 56, 48, 40, 32, 24, 16, 8,
+  57, 49, 41, 33, 25, 17,  9,  1, 59, 51, 43, 35, 27, 19, 11, 3,
+  61, 53, 45, 37, 29, 21, 13,  5, 63, 55, 47, 39, 31, 23, 15, 7
+};
+
+/* 
+ * This is the final 
+ * permutation matrix
+ */
+static int final_perm[64] = {
+  40,  8, 48, 16, 56, 24, 64, 32, 39,  7, 47, 15, 55, 23, 63, 31,
+  38,  6, 46, 14, 54, 22, 62, 30, 37,  5, 45, 13, 53, 21, 61, 29,
+  36,  4, 44, 12, 52, 20, 60, 28, 35,  3, 43, 11, 51, 19, 59, 27,
+  34,  2, 42, 10, 50, 18, 58, 26, 33,  1, 41,  9, 49, 17, 57, 25
+};
+
+/* 
+ * The 16 DES keys in BITMASK format 
+ */
+#ifdef _UFC_32_
+long32 _ufc_keytab[16][2];
+#endif
+#ifdef _UFC_64_
+long64 _ufc_keytab[16];
+#endif
+
+#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
+#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
+
+/* Macro to set a bit (0..23) */
+#define BITMASK(i) ( (1L<<(11L-(i)%12L+3L)) << ((i)<12L?16L:0L) )
+
+/*
+ * sb arrays:
+ *
+ * Workhorses of the inner loop of the DES implementation.
+ * They do sbox lookup, shifting of this  value, 32 bit
+ * permutation and E permutation for the next round.
+ *
+ * Kept in 'BITMASK' format.
+ */
+
+#ifdef _UFC_32_
+long32 _ufc_sb0[8192], _ufc_sb1[8192], _ufc_sb2[8192], _ufc_sb3[8192];
+static long32 *sb[4] = {_ufc_sb0, _ufc_sb1, _ufc_sb2, _ufc_sb3}; 
+#endif
+
+#ifdef _UFC_64_
+long64 _ufc_sb0[4096], _ufc_sb1[4096], _ufc_sb2[4096], _ufc_sb3[4096];
+static long64 *sb[4] = {_ufc_sb0, _ufc_sb1, _ufc_sb2, _ufc_sb3}; 
+#endif
+
+/* 
+ * eperm32tab: do 32 bit permutation and E selection
+ *
+ * The first index is the byte number in the 32 bit value to be permuted
+ *  -  second  -   is the value of this byte
+ *  -  third   -   selects the two 32 bit values
+ *
+ * The table is used and generated internally in init_des to speed it up
+ */
+static ufc_long eperm32tab[4][256][2];
+
+/* 
+ * do_pc1: permform pc1 permutation in the key schedule generation.
+ *
+ * The first   index is the byte number in the 8 byte ASCII key
+ *  -  second    -      -    the two 28 bits halfs of the result
+ *  -  third     -   selects the 7 bits actually used of each byte
+ *
+ * The result is kept with 28 bit per 32 bit with the 4 most significant
+ * bits zero.
+ */
+static ufc_long do_pc1[8][2][128];
+
+/*
+ * do_pc2: permform pc2 permutation in the key schedule generation.
+ *
+ * The first   index is the septet number in the two 28 bit intermediate values
+ *  -  second    -    -  -  septet values
+ *
+ * Knowledge of the structure of the pc2 permutation is used.
+ *
+ * The result is kept with 28 bit per 32 bit with the 4 most significant
+ * bits zero.
+ */
+static ufc_long do_pc2[8][128];
+
+/*
+ * efp: undo an extra e selection and do final
+ *      permutation giving the DES result.
+ * 
+ *      Invoked 6 bit a time on two 48 bit values
+ *      giving two 32 bit longs.
+ */
+static ufc_long efp[16][64][2];
+
+/*
+ * revfinal: undo final permutation and do E expension.
+ *
+ *           Invoked 6 bit a time on DES output
+ *           giving 4 32 bit longs.
+ */
+static ufc_long revfinal[11][64][4];
+
+
+static unsigned char bytemask[8]  = {
+  0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
+};
+
+static ufc_long longmask[32] = {
+  0x80000000, 0x40000000, 0x20000000, 0x10000000,
+  0x08000000, 0x04000000, 0x02000000, 0x01000000,
+  0x00800000, 0x00400000, 0x00200000, 0x00100000,
+  0x00080000, 0x00040000, 0x00020000, 0x00010000,
+  0x00008000, 0x00004000, 0x00002000, 0x00001000,
+  0x00000800, 0x00000400, 0x00000200, 0x00000100,
+  0x00000080, 0x00000040, 0x00000020, 0x00000010,
+  0x00000008, 0x00000004, 0x00000002, 0x00000001
+};
+
+#ifdef DEBUG
+
+pr_bits(a, n)
+  ufc_long *a;
+  int n;
+  { ufc_long i, j, t, tmp;
+    n /= 8;
+    for(i = 0; i < n; i++) {
+      tmp=0;
+      for(j = 0; j < 8; j++) {
+	t=8*i+j;
+	tmp|=(a[t/24] & BITMASK(t % 24))?bytemask[j]:0;
+      }
+      (void)printf("%02x ",tmp);
+    }
+    printf(" ");
+  }
+
+static set_bits(v, b)
+  ufc_long v;
+  ufc_long *b;
+  { ufc_long i;
+    *b = 0;
+    for(i = 0; i < 24; i++) {
+      if(v & longmask[8 + i])
+	*b |= BITMASK(i);
+    }
+  }
+
+#endif
+
+/*
+ * Silly rewrite of 'bzero'. I do so
+ * because some machines don't have
+ * bzero and some don't have memset.
+ */
+
+STATIC void clearmem(start, cnt)
+  char *start;
+  int cnt;
+  { while(cnt--)
+      *start++ = '\0';
+  }
+
+static int initialized = 0;
+
+/* lookup a 6 bit value in sbox */
+
+#define s_lookup(i,s) sbox[(i)][(((s)>>4) & 0x2)|((s) & 0x1)][((s)>>1) & 0xf];
+
+/*
+ * Initialize unit - may be invoked directly
+ * by fcrypt users.
+ */
+
+void init_des()
+  { int comes_from_bit;
+    int bit, sg;
+    ufc_long j;
+    ufc_long mask1, mask2;
+
+    /*
+     * Create the do_pc1 table used
+     * to affect pc1 permutation
+     * when generating keys
+     */
+    for(bit = 0; bit < 56; bit++) {
+      comes_from_bit  = pc1[bit] - 1;
+      mask1 = bytemask[comes_from_bit % 8 + 1];
+      mask2 = longmask[bit % 28 + 4];
+      for(j = 0; j < 128; j++) {
+	if(j & mask1) 
+	  do_pc1[comes_from_bit / 8][bit / 28][j] |= mask2;
+      }
+    }
+
+    /*
+     * Create the do_pc2 table used
+     * to affect pc2 permutation when
+     * generating keys
+     */
+    for(bit = 0; bit < 48; bit++) {
+      comes_from_bit  = pc2[bit] - 1;
+      mask1 = bytemask[comes_from_bit % 7 + 1];
+      mask2 = BITMASK(bit % 24);
+      for(j = 0; j < 128; j++) {
+	if(j & mask1)
+	  do_pc2[comes_from_bit / 7][j] |= mask2;
+      }
+    }
+
+    /* 
+     * Now generate the table used to do combined
+     * 32 bit permutation and e expansion
+     *
+     * We use it because we have to permute 16384 32 bit
+     * longs into 48 bit in order to initialize sb.
+     *
+     * Looping 48 rounds per permutation becomes 
+     * just too slow...
+     *
+     */
+
+    clearmem((char*)eperm32tab, sizeof(eperm32tab));
+
+    for(bit = 0; bit < 48; bit++) {
+      ufc_long mask1,comes_from;
+	
+      comes_from = perm32[esel[bit]-1]-1;
+      mask1      = bytemask[comes_from % 8];
+	
+      for(j = 256; j--;) {
+	if(j & mask1)
+	  eperm32tab[comes_from / 8][j][bit / 24] |= BITMASK(bit % 24);
+      }
+    }
+    
+    /* 
+     * Create the sb tables:
+     *
+     * For each 12 bit segment of an 48 bit intermediate
+     * result, the sb table precomputes the two 4 bit
+     * values of the sbox lookups done with the two 6
+     * bit halves, shifts them to their proper place,
+     * sends them through perm32 and finally E expands
+     * them so that they are ready for the next
+     * DES round.
+     *
+     */
+    for(sg = 0; sg < 4; sg++) {
+      int j1, j2;
+      int s1, s2;
+    
+      for(j1 = 0; j1 < 64; j1++) {
+	s1 = s_lookup(2 * sg, j1);
+	for(j2 = 0; j2 < 64; j2++) {
+	  ufc_long to_permute, inx;
+    
+	  s2         = s_lookup(2 * sg + 1, j2);
+	  to_permute = (((ufc_long)s1 << 4)  | 
+	               (ufc_long)s2) << (24 - 8 * (ufc_long)sg);
+
+#ifdef _UFC_32_
+	  inx = ((j1 << 6)  | j2) << 1;
+	  sb[sg][inx  ]  = eperm32tab[0][(to_permute >> 24) & 0xff][0];
+	  sb[sg][inx+1]  = eperm32tab[0][(to_permute >> 24) & 0xff][1];
+	  sb[sg][inx  ] |= eperm32tab[1][(to_permute >> 16) & 0xff][0];
+	  sb[sg][inx+1] |= eperm32tab[1][(to_permute >> 16) & 0xff][1];
+  	  sb[sg][inx  ] |= eperm32tab[2][(to_permute >>  8) & 0xff][0];
+	  sb[sg][inx+1] |= eperm32tab[2][(to_permute >>  8) & 0xff][1];
+	  sb[sg][inx  ] |= eperm32tab[3][(to_permute)       & 0xff][0];
+	  sb[sg][inx+1] |= eperm32tab[3][(to_permute)       & 0xff][1];
+#endif
+#ifdef _UFC_64_
+	  inx = ((j1 << 6)  | j2);
+	  sb[sg][inx]  = 
+	    ((long64)eperm32tab[0][(to_permute >> 24) & 0xff][0] << 32) |
+	     (long64)eperm32tab[0][(to_permute >> 24) & 0xff][1];
+	  sb[sg][inx] |=
+	    ((long64)eperm32tab[1][(to_permute >> 16) & 0xff][0] << 32) |
+	     (long64)eperm32tab[1][(to_permute >> 16) & 0xff][1];
+  	  sb[sg][inx] |= 
+	    ((long64)eperm32tab[2][(to_permute >>  8) & 0xff][0] << 32) |
+	     (long64)eperm32tab[2][(to_permute >>  8) & 0xff][1];
+	  sb[sg][inx] |=
+	    ((long64)eperm32tab[3][(to_permute)       & 0xff][0] << 32) |
+	     (long64)eperm32tab[3][(to_permute)       & 0xff][1];
+#endif
+	}
+      }
+    }  
+
+    /* 
+     * Create an inverse matrix for esel telling
+     * where to plug out bits if undoing it
+     */
+    for(bit=48; bit--;) {
+      e_inverse[esel[bit] - 1     ] = bit;
+      e_inverse[esel[bit] - 1 + 32] = bit + 48;
+    }
+
+    /* 
+     * create efp: the matrix used to
+     * undo the E expansion and effect final permutation
+     */
+    clearmem((char*)efp, sizeof efp);
+    for(bit = 0; bit < 64; bit++) {
+      int o_bit, o_long;
+      ufc_long word_value, mask1, mask2;
+      int comes_from_f_bit, comes_from_e_bit;
+      int comes_from_word, bit_within_word;
+
+      /* See where bit i belongs in the two 32 bit long's */
+      o_long = bit / 32; /* 0..1  */
+      o_bit  = bit % 32; /* 0..31 */
+
+      /* 
+       * And find a bit in the e permutated value setting this bit.
+       *
+       * Note: the e selection may have selected the same bit several
+       * times. By the initialization of e_inverse, we only look
+       * for one specific instance.
+       */
+      comes_from_f_bit = final_perm[bit] - 1;         /* 0..63 */
+      comes_from_e_bit = e_inverse[comes_from_f_bit]; /* 0..95 */
+      comes_from_word  = comes_from_e_bit / 6;        /* 0..15 */
+      bit_within_word  = comes_from_e_bit % 6;        /* 0..5  */
+
+      mask1 = longmask[bit_within_word + 26];
+      mask2 = longmask[o_bit];
+
+      for(word_value = 64; word_value--;) {
+	if(word_value & mask1)
+	  efp[comes_from_word][word_value][o_long] |= mask2;
+      }
+    }
+
+    
+    /*
+     * Create revfinal: an array to undo final
+     * the effects of efp
+     */
+    clearmem((char*)revfinal, sizeof(revfinal));
+    for(bit = 0; bit < 96; bit++) {
+      int ibit = initial_perm[esel[bit % 48] - 1 + ((bit >= 48) ? 32 : 0)] - 1;
+      mask1 = bytemask[ibit % 6 +  2];
+      mask2 = BITMASK(bit % 24);
+      for(j = 64; j--;) {
+        if(j & mask1) {
+          revfinal[ibit / 6][j][bit / 24] |= mask2;
+        }
+      }
+    }
+
+    initialized++;
+  }
+
+/* 
+ * Process the elements of the sb table permuting the
+ * bits swapped in the expansion by the current salt.
+ */
+
+#ifdef _UFC_32_
+STATIC void shuffle_sb(k, saltbits)
+  long32 *k;
+  ufc_long saltbits;
+  { ufc_long j;
+    long32 x;
+    for(j=4096; j--;) {
+      x = (k[0] ^ k[1]) & (long32)saltbits;
+      *k++ ^= x;
+      *k++ ^= x;
+    }
+  }
+#endif
+
+#ifdef _UFC_64_
+STATIC void shuffle_sb(k, saltbits)
+  long64 *k;
+  ufc_long saltbits;
+  { ufc_long j;
+    long64 x;
+    for(j=4096; j--;) {
+      x = ((*k >> 32) ^ *k) & (long64)saltbits;
+      *k++ ^= (x << 32) | x;
+    }
+  }
+#endif
+
+/* 
+ * Setup the unit for a new salt
+ * Hopefully we'll not see a new salt in each crypt call.
+ */
+
+static unsigned char current_salt[3] = "&&"; /* invalid value */
+static ufc_long current_saltbits = 0;
+static int direction = 0;
+
+STATIC void setup_salt(s)
+  char *s;
+  { ufc_long i, j, saltbits;
+
+    if(!initialized)
+      init_des();
+
+    if(s[0] == current_salt[0] && s[1] == current_salt[1])
+      return;
+    current_salt[0] = s[0]; current_salt[1] = s[1];
+    
+    /* 
+     * This is the only crypt change to DES:
+     * entries are swapped in the expansion table
+     * according to the bits set in the salt.
+     */
+    saltbits = 0;
+    for(i = 0; i < 2; i++) {
+      long c=ascii_to_bin(s[i]);
+#ifdef notdef
+      /* 
+       * Some applications do rely on illegal
+       * salts. It seems that UFC-crypt behaves
+       * identically to standard crypt 
+       * implementations on illegal salts -- glad
+       */
+      if(c < 0 || c > 63)
+	c = 0;
+#endif
+      for(j = 0; j < 6; j++) {
+	if((c >> j) & 0x1)
+	  saltbits |= BITMASK(6 * i + j);
+      }
+    }
+
+    /*
+     * Permute the sb table values
+     * to reflect the changed e
+     * selection table
+     */
+    shuffle_sb(_ufc_sb0, current_saltbits ^ saltbits); 
+    shuffle_sb(_ufc_sb1, current_saltbits ^ saltbits);
+    shuffle_sb(_ufc_sb2, current_saltbits ^ saltbits);
+    shuffle_sb(_ufc_sb3, current_saltbits ^ saltbits);
+
+    current_saltbits = saltbits;
+  }
+
+STATIC void ufc_mk_keytab(key)
+  char *key;
+  { ufc_long v1, v2, *k1;
+    int i;
+#ifdef _UFC_32_
+    long32 v, *k2 = &_ufc_keytab[0][0];
+#endif
+#ifdef _UFC_64_
+    long64 v, *k2 = &_ufc_keytab[0];
+#endif
+
+    v1 = v2 = 0; k1 = &do_pc1[0][0][0];
+    for(i = 8; i--;) {
+      v1 |= k1[*key   & 0x7f]; k1 += 128;
+      v2 |= k1[*key++ & 0x7f]; k1 += 128;
+    }
+
+    for(i = 0; i < 16; i++) {
+      k1 = &do_pc2[0][0];
+
+      v1 = (v1 << rots[i]) | (v1 >> (28 - rots[i]));
+      v  = k1[(v1 >> 21) & 0x7f]; k1 += 128;
+      v |= k1[(v1 >> 14) & 0x7f]; k1 += 128;
+      v |= k1[(v1 >>  7) & 0x7f]; k1 += 128;
+      v |= k1[(v1      ) & 0x7f]; k1 += 128;
+
+#ifdef _UFC_32_
+      *k2++ = v;
+      v = 0;
+#endif
+#ifdef _UFC_64_
+      v <<= 32;
+#endif
+
+      v2 = (v2 << rots[i]) | (v2 >> (28 - rots[i]));
+      v |= k1[(v2 >> 21) & 0x7f]; k1 += 128;
+      v |= k1[(v2 >> 14) & 0x7f]; k1 += 128;
+      v |= k1[(v2 >>  7) & 0x7f]; k1 += 128;
+      v |= k1[(v2      ) & 0x7f];
+
+      *k2++ = v;
+    }
+
+    direction = 0;
+  }
+
+/* 
+ * Undo an extra E selection and do final permutations
+ */
+
+ufc_long *_ufc_dofinalperm(l1, l2, r1, r2)
+  ufc_long l1,l2,r1,r2;
+  { ufc_long v1, v2, x;
+    static ufc_long ary[2];
+
+    x = (l1 ^ l2) & current_saltbits; l1 ^= x; l2 ^= x;
+    x = (r1 ^ r2) & current_saltbits; r1 ^= x; r2 ^= x;
+
+    v1=v2=0; l1 >>= 3; l2 >>= 3; r1 >>= 3; r2 >>= 3;
+
+    v1 |= efp[15][ r2         & 0x3f][0]; v2 |= efp[15][ r2 & 0x3f][1];
+    v1 |= efp[14][(r2 >>= 6)  & 0x3f][0]; v2 |= efp[14][ r2 & 0x3f][1];
+    v1 |= efp[13][(r2 >>= 10) & 0x3f][0]; v2 |= efp[13][ r2 & 0x3f][1];
+    v1 |= efp[12][(r2 >>= 6)  & 0x3f][0]; v2 |= efp[12][ r2 & 0x3f][1];
+
+    v1 |= efp[11][ r1         & 0x3f][0]; v2 |= efp[11][ r1 & 0x3f][1];
+    v1 |= efp[10][(r1 >>= 6)  & 0x3f][0]; v2 |= efp[10][ r1 & 0x3f][1];
+    v1 |= efp[ 9][(r1 >>= 10) & 0x3f][0]; v2 |= efp[ 9][ r1 & 0x3f][1];
+    v1 |= efp[ 8][(r1 >>= 6)  & 0x3f][0]; v2 |= efp[ 8][ r1 & 0x3f][1];
+
+    v1 |= efp[ 7][ l2         & 0x3f][0]; v2 |= efp[ 7][ l2 & 0x3f][1];
+    v1 |= efp[ 6][(l2 >>= 6)  & 0x3f][0]; v2 |= efp[ 6][ l2 & 0x3f][1];
+    v1 |= efp[ 5][(l2 >>= 10) & 0x3f][0]; v2 |= efp[ 5][ l2 & 0x3f][1];
+    v1 |= efp[ 4][(l2 >>= 6)  & 0x3f][0]; v2 |= efp[ 4][ l2 & 0x3f][1];
+
+    v1 |= efp[ 3][ l1         & 0x3f][0]; v2 |= efp[ 3][ l1 & 0x3f][1];
+    v1 |= efp[ 2][(l1 >>= 6)  & 0x3f][0]; v2 |= efp[ 2][ l1 & 0x3f][1];
+    v1 |= efp[ 1][(l1 >>= 10) & 0x3f][0]; v2 |= efp[ 1][ l1 & 0x3f][1];
+    v1 |= efp[ 0][(l1 >>= 6)  & 0x3f][0]; v2 |= efp[ 0][ l1 & 0x3f][1];
+
+    ary[0] = v1; ary[1] = v2;
+    return ary;
+  }
+
+/* 
+ * crypt only: convert from 64 bit to 11 bit ASCII 
+ * prefixing with the salt
+ */
+
+STATIC char *output_conversion(v1, v2, salt)
+  ufc_long v1, v2;
+  char *salt;
+  { static char outbuf[14];
+    int i, s, shf;
+
+    outbuf[0] = salt[0];
+    outbuf[1] = salt[1] ? salt[1] : salt[0];
+
+    for(i = 0; i < 5; i++) {
+      shf = (26 - 6 * i); /* to cope with MSC compiler bug */
+      outbuf[i + 2] = bin_to_ascii((v1 >> shf) & 0x3f);
+    }
+
+    s  = (v2 & 0xf) << 2;
+    v2 = (v2 >> 2) | ((v1 & 0x3) << 30);
+
+    for(i = 5; i < 10; i++) {
+      shf = (56 - 6 * i);
+      outbuf[i + 2] = bin_to_ascii((v2 >> shf) & 0x3f);
+    }
+
+    outbuf[12] = bin_to_ascii(s);
+    outbuf[13] = 0;
+
+    return outbuf;
+  }
+
+ufc_long *_ufc_doit();
+
+/* 
+ * UNIX crypt function
+ */
+   
+char *crypt(key, salt)
+  char *key, *salt;
+  { ufc_long *s;
+    char ktab[9];
+
+    /*
+     * Hack DES tables according to salt
+     */
+    setup_salt(salt);
+
+    /*
+     * Setup key schedule
+     */
+    clearmem(ktab, sizeof ktab);
+    (void)strncpy(ktab, key, 8);
+    ufc_mk_keytab(ktab);
+
+    /*
+     * Go for the 25 DES encryptions
+     */
+    s = _ufc_doit((ufc_long)0, (ufc_long)0, 
+		  (ufc_long)0, (ufc_long)0, (ufc_long)25);
+    /*
+     * Do final permutations
+     */
+    s = _ufc_dofinalperm(s[0], s[1], s[2], s[3]);
+
+    /*
+     * And convert back to 6 bit ASCII
+     */
+    return output_conversion(s[0], s[1], salt);
+  }
+
+/* 
+ * To make fcrypt users happy.
+ * They don't need to call init_des.
+ */
+
+char *fcrypt(key, salt)
+  char *key;
+  char *salt;
+  { return crypt(key, salt);
+  }
+
+/* 
+ * UNIX encrypt function. Takes a bitvector
+ * represented by one byte per bit and
+ * encrypt/decrypt according to edflag
+ */
+
+void encrypt(block, edflag)
+  char *block;
+  int edflag;
+  { ufc_long l1, l2, r1, r2, *s;
+    int i;
+
+    /*
+     * Undo any salt changes to E expansion
+     */
+    setup_salt("..");
+
+    /*
+     * Reverse key table if
+     * changing operation (encrypt/decrypt)
+     */
+    if((edflag == 0) != (direction == 0)) {
+      for(i = 0; i < 8; i++) {
+#ifdef _UFC_32_
+	long32 x;
+	x = _ufc_keytab[15-i][0]; 
+        _ufc_keytab[15-i][0] = _ufc_keytab[i][0]; 
+        _ufc_keytab[i][0] = x;
+
+	x = _ufc_keytab[15-i][1]; 
+        _ufc_keytab[15-i][1] = _ufc_keytab[i][1]; 
+        _ufc_keytab[i][1] = x;
+#endif
+#ifdef _UFC_64_
+	long64 x;
+	x = _ufc_keytab[15-i];
+	_ufc_keytab[15-i] = _ufc_keytab[i];
+	_ufc_keytab[i] = x;
+#endif
+      }
+      direction = edflag;
+    }
+
+    /*
+     * Do initial permutation + E expansion
+     */
+    i = 0;
+    for(l1 = 0; i < 24; i++) {
+      if(block[initial_perm[esel[i]-1]-1])
+	l1 |= BITMASK(i);
+    }
+    for(l2 = 0; i < 48; i++) {
+      if(block[initial_perm[esel[i]-1]-1])
+	l2 |= BITMASK(i-24);
+    }
+
+    i = 0;
+    for(r1 = 0; i < 24; i++) {
+      if(block[initial_perm[esel[i]-1+32]-1])
+	r1 |= BITMASK(i);
+    }
+    for(r2 = 0; i < 48; i++) {
+      if(block[initial_perm[esel[i]-1+32]-1])
+	r2 |= BITMASK(i-24);
+    }
+
+    /*
+     * Do DES inner loops + final conversion
+     */
+    s = _ufc_doit(l1, l2, r1, r2, (ufc_long)1);
+    /*
+     * Do final permutations
+     */
+    s = _ufc_dofinalperm(s[0], s[1], s[2], s[3]);
+
+    /*
+     * And convert to bit array
+     */
+    l1 = s[0]; r1 = s[1];
+    for(i = 0; i < 32; i++) {
+      *block++ = (l1 & longmask[i]) != 0;
+    }
+    for(i = 0; i < 32; i++) {
+      *block++ = (r1 & longmask[i]) != 0;
+    }
+    
+  }
+
+/* 
+ * UNIX setkey function. Take a 64 bit DES
+ * key and setup the machinery.
+ */
+
+void setkey(key)
+  char *key;
+  { int i,j;
+    unsigned char c;
+    unsigned char ktab[8];
+
+    setup_salt(".."); /* be sure we're initialized */
+
+    for(i = 0; i < 8; i++) {
+      for(j = 0, c = 0; j < 8; j++)
+	c = c << 1 | *key++;
+      ktab[i] = c >> 1;
+    }
+    
+    ufc_mk_keytab(ktab);
+  }
+
+/* 
+ * Ultrix crypt16 function, thanks to pcl@convex.oxford.ac.uk (Paul Leyland)
+ */
+   
+char *crypt16(key, salt)
+  char *key, *salt;
+  { ufc_long *s, *t;
+    char ktab[9], ttab[9];
+    static char q[14], res[25];
+    /*
+     * Hack DES tables according to salt
+     */
+    setup_salt(salt);
+    
+    /*
+     * Setup key schedule
+     */
+    clearmem(ktab, sizeof ktab);
+    (void)strncpy(ktab, key, 8);
+    ufc_mk_keytab(ktab);
+    
+    /*
+     * Go for first 20 DES encryptions
+     */
+    s = _ufc_doit((ufc_long)0, (ufc_long)0, 
+		  (ufc_long)0, (ufc_long)0, (ufc_long)20);
+    
+    /*
+     * And convert back to 6 bit ASCII
+     */
+    strcpy (res, output_conversion(s[0], s[1], salt));
+    
+    clearmem(ttab, sizeof ttab);
+    if (strlen (key) > 8) (void)strncpy(ttab, key+8, 8);
+    ufc_mk_keytab(ttab);
+    
+    /*
+     * Go for second 5 DES encryptions
+     */
+    t = _ufc_doit((ufc_long)0, (ufc_long)0, 
+		  (ufc_long)0, (ufc_long)0, (ufc_long)5);
+    /*
+     * And convert back to 6 bit ASCII
+     */
+    strcpy (q, output_conversion(t[0], t[1], salt));
+    strcpy (res+13, q+2);
+    
+    clearmem(ktab, sizeof ktab);
+    (void)strncpy(ktab, key, 8);
+    ufc_mk_keytab(ktab);
+    
+    return res;
+  }
+
+/*
+ * Experimental -- not supported -- may choke your dog
+ */
+
+void ufc_setup_password(cookie, s)
+  long *cookie;
+  char *s;
+  { char c;
+    int i;
+    ufc_long x;
+    ufc_long dl1, dl2, dr1, dr2;
+
+    setup_salt(s);
+    dl1 = dl2 = dr1 = dr2 = 0;
+    for(i = 0, s += 2; c = *s++; i++) {
+      int x = ascii_to_bin(c);
+      dl1 |= revfinal[i][x][0];
+      dl2 |= revfinal[i][x][1];
+      dr1 |= revfinal[i][x][2];
+      dr2 |= revfinal[i][x][3];
+    }
+    x = (dl1 ^ dl2) & current_saltbits;
+    x = (dr1 ^ dr2) & current_saltbits;
+    cookie[0] = dl1 ^ x; cookie[1] = dl2 ^ x;
+    cookie[2] = dr1 ^ x; cookie[3] = dr2 ^ x;
+  }
+
+void ufc_do_pw(cookie, guess)
+  long *cookie;  
+  char *guess;
+  { char ktab[9];
+    ufc_long *s;
+    clearmem(ktab, sizeof ktab);
+    (void)strncpy(ktab, guess, 8);
+    ufc_mk_keytab(ktab);
+    s = _ufc_doit((ufc_long)0, (ufc_long)0, 
+		  (ufc_long)0, (ufc_long)0, (ufc_long)25);
+    cookie[0] = s[0];    cookie[1] = s[1];
+    cookie[2] = s[2];    cookie[3] = s[3];
+  }
diff --git a/WWW/Library/Implementation/getline.c b/WWW/Library/Implementation/getline.c
new file mode 100644
index 00000000..7f6ff038
--- /dev/null
+++ b/WWW/Library/Implementation/getline.c
@@ -0,0 +1,74 @@
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+/* CHANGED FOR VMS */
+
+/*
+ * <getline.c>
+ */
+
+#include "HTUtils.h"
+#include "tcp.h"
+#include <stddef.h>
+
+#include "LYLeaks.h"
+
+/* Read up to (and including) a newline from STREAM into *LINEPTR
+   (and null-terminate it). *LINEPTR is a pointer returned from malloc (or
+   NULL), pointing to *N characters of space.  It is realloc'd as
+   necessary.  Returns the number of characters read (not including the
+   null terminator), or -1 on error or EOF.  */
+
+int getline(char **lineptr, size_t *n, FILE *stream)
+{
+static char line[256];
+char *ptr;
+unsigned int len;
+
+   if (lineptr == NULL || n == NULL)
+   {
+      SOCKET_ERRNO = EINVAL;
+      return -1;
+   }
+
+   if (ferror (stream))
+      return -1;
+
+   if (feof(stream))
+      return -1;
+     
+   fgets(line,256,stream);
+
+   ptr = strchr(line,'\n');   
+   if (ptr)
+      *ptr = '\0';
+
+   len = strlen(line);
+   
+   if ((len+1) < 256)
+   {
+      ptr = realloc(*lineptr, 256);
+      if (ptr == NULL)
+         return(-1);
+      *lineptr = ptr;
+      *n = 256;
+   }
+
+   strcpy(*lineptr,line); 
+   return(len);
+}
diff --git a/WWW/Library/Implementation/getpass.c b/WWW/Library/Implementation/getpass.c
new file mode 100644
index 00000000..a7f0de7b
--- /dev/null
+++ b/WWW/Library/Implementation/getpass.c
@@ -0,0 +1,64 @@
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+/* CHANGED FOR VMS */
+
+/*
+ * <getpass.c>
+ */
+
+#include "HTUtils.h"
+/*#include <stdio.h>  included by HTUtils.h -- FM */
+#include <descrip.h>
+#include <psldef.h>
+#include <iodef.h>
+#include <starlet.h>
+
+#include "LYLeaks.h"
+
+PUBLIC char * getpass ARGS1(CONST char *, prompt)
+{
+  static char *buf;
+
+  int result;
+  $DESCRIPTOR(devnam,"SYS$INPUT");
+  int chan;
+  int promptlen;
+  struct {
+     short result;
+     short count;
+     int   info;
+  } iosb;
+
+  promptlen = strlen(prompt);
+
+  buf = (char *)malloc(256);
+  if (buf == NULL)
+     return(NULL);  
+
+  result = sys$assign(&devnam, &chan, PSL$C_USER, 0, 0);
+
+  result = sys$qiow(0, chan, IO$_READPROMPT | IO$M_PURGE |IO$M_NOECHO, &iosb, 0, 0,
+                    buf, 255, 0, 0, prompt, promptlen);
+
+  buf[iosb.count] = '\0';
+
+  result = sys$dassgn(chan);
+
+  return buf;
+}
diff --git a/WWW/Library/Implementation/patchlevel.h b/WWW/Library/Implementation/patchlevel.h
new file mode 100644
index 00000000..c37c8654
--- /dev/null
+++ b/WWW/Library/Implementation/patchlevel.h
@@ -0,0 +1,24 @@
+/*
+ * UFC-crypt: ultra fast crypt(3) implementation
+ *
+ * Copyright (C) 1991, 1992, Free Software Foundation, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * @(#)patchlevel.h	1.11 7/15/92
+ *
+ */
+
+#define PATCHLEVEL "UFC-crypt, patchlevel 1e, @(#)patchlevel.h	1.11 7/15/92"
diff --git a/WWW/Library/Implementation/tcp.h b/WWW/Library/Implementation/tcp.h
new file mode 100644
index 00000000..2e5ada8d
--- /dev/null
+++ b/WWW/Library/Implementation/tcp.h
@@ -0,0 +1,524 @@
+/*                System dependencies in the W3 library
+                                   SYSTEM DEPENDENCIES
+                                             
+   System-system differences for TCP include files and macros. This
+   file includes for each system the files necessary for network and
+   file I/O.  It should be used in conjunction with HTUtils.h to help
+   ensure portability across as many platforms and flavors of platforms
+   as possible.
+   
+  AUTHORS
+  
+  TBL                Tim Berners-Lee, W3 project, CERN, <timbl@info.cern.ch>
+  EvA                     Eelco van Asperen <evas@cs.few.eur.nl>
+  MA                      Marc Andreessen NCSA
+  AT                      Aleksandar Totic <atotic@ncsa.uiuc.edu>
+  SCW                     Susan C. Weber <sweber@kyle.eitech.com>
+                         
+  HISTORY:
+  22 Feb 91               Written (TBL) as part of the WWW library.
+  16 Jan 92               PC code from EvA
+  22 Apr 93               Merged diffs bits from xmosaic release
+  29 Apr 93               Windows/NT code from SCW
+  20 May 94		  A.Harper Add support for VMS CMU TCP/IP transport
+   3 Oct 94		  A.Harper Add support for VMS SOCKETSHR/NETLIB
+  15 Jul 95               S. Bjorndahl Gnu C for VMS Globaldef/ref support
+
+*/
+
+#ifndef TCP_H
+#define TCP_H
+
+#ifndef HTUTILS_H
+#include "HTUtils.h"
+#endif /* !HTUTILS_H */
+
+/*
+
+Default values
+
+   These values may be reset and altered by system-specific sections
+   later on.  there are also a bunch of defaults at the end .
+   
+ */
+/* Default values of those: */
+#define NETCLOSE close      /* Routine to close a TCP-IP socket         */
+#define NETREAD  HTDoRead       /* Routine to read from a TCP-IP socket     */
+#define NETWRITE write      /* Routine to write to a TCP-IP socket      */
+#define SOCKET_READ read    /* normal socket read routine */
+#define IOCTL ioctl	    /* normal ioctl routine for sockets */
+#define SOCKET_ERRNO errno	    /* normal socket errno */
+
+/* Unless stated otherwise, */
+#define SELECT                  /* Can handle >1 channel.               */
+#define GOT_SYSTEM              /* Can call shell with string           */
+
+#ifdef unix
+#define GOT_PIPE
+#endif /* unix */
+
+typedef struct sockaddr_in SockA;  /* See netinet/in.h */
+
+
+#ifndef STDIO_H
+#include <stdio.h>
+#define STDIO_H
+#endif /* !STDIO_H */
+
+#ifdef _AIX
+#define AIX
+#endif /* _AIX */
+#ifdef AIX
+#define unix
+#endif /* AIX */
+
+#ifdef _IBMR2
+#define USE_DIRENT              /* sys V style directory open */
+#endif /* _IBMR2 */
+
+/* Solaris. */
+#if defined(sun) && defined(__svr4__) && !defined(USE_DIRENT)
+#define USE_DIRENT              /* sys V style directory open */
+#endif /* sun && __svr4__ && !USE_DIRENT */
+
+#ifdef __alpha
+#define USE_DIRENT
+#endif /* __alpha */
+
+#ifndef USE_DIRENT
+#ifdef SVR4
+#define USE_DIRENT
+#endif /* SVR4 */
+#endif /* !USE_DIRENT */
+
+#ifndef SOLARIS2
+#include <string.h>             /* For bzero etc */
+#endif /* !SOLARIS2 */
+
+/* Use builtin strdup when appropriate. */
+#if defined(ultrix) || defined(VMS) || defined(NeXT)
+extern char *strdup ();
+#endif /* ultrix || VMS || NeXT */
+
+/*
+
+  M ACROS FOR CONVERTING CHARACTERS
+
+ */
+#ifndef TOASCII
+#define TOASCII(c) (c)
+#define FROMASCII(c) (c)
+#endif /* !TOASCII */
+
+
+/*
+
+VAX/VMS
+
+   Under VMS, there are many versions of TCP-IP. Define one if you do
+   not use Digital's UCX product:
+   
+  UCX                     DEC's "Ultrix connection" (default)
+  CMU_TCP                 Available via FTP from sacusr.mp.usbr.gov
+  SOCKETSHR		  Eckhart Meyer's interface to NETLIB
+  WIN_TCP                 From Wollongong, now GEC software.
+  MULTINET                From SRI, now from TGV Inv.
+  DECNET                  Cern's TCP socket emulation over DECnet
+                           
+   The last three do not interfere with the
+   unix i/o library, and so they need special calls to read, write and
+   close sockets. In these cases the socket number is a VMS channel
+   number, so we make the @@@ HORRIBLE @@@ assumption that a channel
+   number will be greater than 10 but a unix file descriptor less than
+   10.  It works.
+   
+ */
+#ifdef VMS 
+
+#ifdef UCX
+#undef IOCTL
+#define IOCTL HTioctl
+#endif /* UCX */
+
+#ifdef WIN_TCP
+#undef SOCKET_READ
+#undef NETWRITE
+#undef NETCLOSE
+#undef IOCTL
+#define SOCKET_READ(s,b,l)  ((s)>10 ? netread((s),(b),(l)) : read((s),(b),(l)))
+#define NETWRITE(s,b,l) ((s)>10 ? netwrite((s),(b),(l)) : write((s),(b),(l)))
+#define NETCLOSE(s)     ((s)>10 ? netclose(s) : close(s))
+#define IOCTL(a,b,c) -1 /* disables ioctl function	      */
+#define NO_IOCTL	/* flag to check if ioctl is disabled */
+#endif /* WIN_TCP */
+
+#ifdef CMU_TCP
+#undef SOCKET_READ
+#undef NETREAD
+#undef NETWRITE
+#undef NETCLOSE
+#define SOCKET_READ(s,b,l) (cmu_get_sdc((s)) != 0 ? cmu_read((s),(b),(l)) : read((s),(b),(l)))
+#define NETREAD(s,b,l) (cmu_get_sdc((s)) != 0 ? HTDoRead((s),(b),(l)) : read((s),(b),(l)))
+#define NETWRITE(s,b,l) (cmu_get_sdc((s)) != 0 ? cmu_write((s),(b),(l)) : write((s),(b),(l)))
+#define NETCLOSE(s) (cmu_get_sdc((s)) != 0 ? cmu_close((s)) : close((s)))
+#endif /* CMU_TCP */
+
+#ifdef MULTINET
+#undef NETCLOSE
+#undef SOCKET_READ
+#undef NETWRITE
+#undef IOCTL
+#undef SOCKET_ERRNO
+#define SOCKET_READ(s,b,l)  ((s)>10 ? socket_read((s),(b),(l)) : \
+				read((s),(b),(l)))
+#define NETWRITE(s,b,l) ((s)>10 ? socket_write((s),(b),(l)) : \
+                                write((s),(b),(l)))
+#define NETCLOSE(s)     ((s)>10 ? socket_close(s) : close(s))
+#define IOCTL socket_ioctl
+#define SOCKET_ERRNO socket_errno
+#endif /* MULTINET */
+
+#ifdef SOCKETSHR_TCP
+#undef SOCKET_READ
+#undef NETREAD
+#undef NETWRITE
+#undef NETCLOSE
+#undef IOCTL
+#define SOCKET_READ(s,b,l) (si_get_sdc((s)) != 0 ? si_read((s),(b),(l)) : \
+                                read((s),(b),(l)))
+#define NETREAD(s,b,l) (si_get_sdc((s)) != 0 ? HTDoRead((s),(b),(l)) : \
+                                read((s),(b),(l)))
+#define NETWRITE(s,b,l) (si_get_sdc((s)) != 0 ? si_write((s),(b),(l)) : \
+                                write((s),(b),(l)))
+#define NETCLOSE(s) (si_get_sdc((s)) != 0 ? si_close((s)) : close((s)))
+#define IOCTL si_ioctl
+#endif /* SOCKETSHR_TCP */
+
+#include <string.h>
+
+#ifndef STDIO_H
+#include <stdio.h>
+#define STDIO_H
+#endif /* !STDIO_H */
+
+#include <file.h>
+#include <stat.h>
+#include <unixio.h>
+#include <unixlib.h>
+
+#define INCLUDES_DONE
+
+#ifdef MULTINET  /* Include from standard Multinet directories */
+#ifndef __SOCKET_TYPEDEFS
+#define __SOCKET_TYPEDEFS 1
+#endif /* !__SOCKET_TYPEDEFS */
+#include <time.h>
+#include <types.h>
+#ifdef __TIME_T
+#define __TYPES 1
+#define __TYPES_LOADED 1
+#endif /* __TIME_T */
+#ifdef __SOCKET_TYPEDEFS
+#undef __SOCKET_TYPEDEFS
+#endif /* __SOCKET_TYPEDEFS */
+#include "multinet_root:[multinet.include.sys]types.h"
+#ifndef __SOCKET_TYPEDEFS
+#define __SOCKET_TYPEDEFS 1
+#endif /* !__SOCKET_TYPEDEFS */
+#include "multinet_root:[multinet.include]errno.h"
+#ifdef __TYPES
+#define __TIME_T 1
+#endif /* __TYPE */
+#ifdef __TIME_LOADED
+#define __TIME 1  /* to avoid double definitions in in.h */
+#endif /* __TIME_LOADED */
+#include "multinet_root:[multinet.include.sys]time.h"
+#include "multinet_root:[multinet.include.sys]socket.h"
+#include "multinet_root:[multinet.include.netinet]in.h"
+#include "multinet_root:[multinet.include.arpa]inet.h"
+#include "multinet_root:[multinet.include]netdb.h"
+#include "multinet_root:[multinet.include.sys]ioctl.h"
+#endif /* MULTINET */
+
+
+#ifdef DECNET
+#include <types.h>
+#include <errno.h>
+#include <time.h>
+#include "types.h"  /* for socket.h */
+#include "socket.h"
+#include "dn"
+#include "dnetdb"
+/* #include "vms.h" */
+#endif /* DECNET */
+
+
+#ifdef UCX
+#include <types.h>
+#include <errno.h>
+#include <time.h>
+#include <socket.h>
+#include <in.h>
+#include <inet.h>
+#if defined(TCPWARE) && !defined(__DECC)
+#include "tcpware_include:netdb.h"
+#include "tcpware_include:ucx$inetdef.h"
+#else
+#include <netdb.h>
+#include <ucx$inetdef.h>
+#endif /* TCPWARE */
+#endif /* UCX */
+
+
+#ifdef CMU_TCP
+#include <types.h>
+#include <errno.h>
+#include "cmuip_root:[syslib]time.h"
+#include "cmuip_root:[syslib]socket.h"
+#include <in.h>
+#include <inet.h>
+#include <netdb.h>
+#include "cmuip_root:[syslib]ioctl.h"
+#endif /* CMU_TCP */
+
+
+#ifdef SOCKETSHR_TCP
+#include <types.h>
+#include <errno.h>
+#include <time.h>
+#include <socket.h>
+#include <in.h>
+#include <inet.h>
+#include <netdb.h>
+#include "socketshr_library:socketshr.h"
+#include "socketshr_library:ioctl.h"
+#endif /* SOCKETSHR_TCP */
+
+#ifdef WIN_TCP
+#include <types.h>
+#include <errno.h>
+#include <time.h>
+#include <socket.h>
+#include <in.h>
+#include <inet.h>
+#include <netdb.h>
+#ifndef NO_IOCTL
+#include <ioctl.h>
+#endif /* !NO_IOCTL */
+#endif /* WIN_TCP */
+
+
+#define TCP_INCLUDES_DONE
+
+/*
+
+   On VMS machines, the linker needs to be told to put global data sections into
+ a data
+   segment using these storage classes. (MarkDonszelmann)
+  
+ */
+#if defined(VAXC) && !defined(__DECC)
+#define GLOBALDEF globaldef
+#define GLOBALREF globalref
+#else
+#ifdef __GNUC__		/* this added by Sterling Bjorndahl */
+#define GLOBALREF_IS_MACRO 1
+#define GLOBALDEF_IS_MACRO 1
+#include <gnu_hacks.h>	/* defines GLOBALREF and GLOBALDEF for GNUC on VMS */
+#endif  /* __GNUC__ */
+#endif /* VAXC && !DECC */
+
+#endif /* VMS */
+
+/*
+ * On non-VMS machines and for DECC on VMS, the GLOBALDEF and GLOBALREF
+ * storage types default to normal C storage types.
+ */
+#ifndef GLOBALREF
+#define GLOBALDEF
+#define GLOBALREF extern
+#endif /* !GLOBALREF */
+
+
+/*
+SCO ODT unix version
+ */
+#ifdef SCO
+#define sco
+#endif /* SCO */
+#ifdef sco
+#include <sys/fcntl.h>
+#define USE_DIRENT
+#endif /* sco */
+
+/*
+Intergraph CLIX
+ */
+#ifdef CLIX
+#include <sys/fcntl.h>
+#define USE_DIRENT
+#endif /* CLIX */
+
+#ifdef ISC
+#ifndef NO_UNISTD_H
+#include <sys/unistd.h>
+#endif /* !NO_UNISTD_H */
+#else
+#if !defined(NO_UNISTD_H) && !defined(VMS)
+#include <unistd.h>
+#endif /* !NO_UNISTD_H && !VMS */
+#endif /* ISC */
+
+#if defined(SVR4) || defined(UNIXWARE)
+#include <sys/fcntl.h>
+#ifndef NO_FILIO_H	/* BSD Interactive doesn't have filio.h. */
+#include <sys/filio.h>
+#endif /* !NO_FILIO_H */
+#endif /* SVR4 || UNIXWARE */
+
+/*
+SOLARIS 2
+ */
+#ifdef SOLARIS2
+#include <sys/filio.h>
+#endif /* SOLARIS2 */
+
+/*
+MIPS unix
+ */
+/* Mips hack (bsd4.3/sysV mixture...) */
+#ifdef mips
+extern int errno;
+#endif /* mips */
+
+/*
+Regular BSD unix versions
+=========================
+   These are a default unix where not already defined specifically.
+ */
+#ifndef INCLUDES_DONE
+#include <sys/types.h>
+/* #include <streams/streams.h>                 not ultrix */
+#ifndef SOLARIS2
+#include <string.h>
+#endif /* !SOLARIS2 */
+#include <errno.h>          /* independent */
+#ifdef SCO
+#include <sys/timeb.h>
+#include <time.h>
+#endif /* SCO */
+#ifdef AIX
+#include <time.h>
+#endif /* AIX */
+#include <sys/time.h>       /* independent */
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/file.h>       /* For open() etc */
+#if defined(NeXT) || defined(sony_news)
+#ifndef mode_t
+typedef unsigned short mode_t;
+#endif /* !mode_t */
+#ifndef pid_t
+typedef int pid_t;
+#endif /* !pid_t */
+#ifndef S_ISREG
+#define S_ISREG(m) (((m) & 0170000) == 0100000)
+#endif /* S_ISREG */
+#ifndef WEXITSTATUS
+#ifdef sony_news
+#define WEXITSTATUS(s) WIFEXITED(s)
+#else
+#define WEXITSTATUS(s) (((s).w_status >> 8) & 0377)
+#endif /* sony_news */
+#endif /* !WEXITSTATUS */
+#ifndef WTERMSIG
+#ifdef sony_news
+#define WTERMSIG(s) (s).w_termsig
+#else
+#define WTERMSIG(s) (((s).w_status >> 8) & 0177)
+#endif /* sony_news */
+#endif /* !WTERMSIG */
+#endif /* NeXT || sony_news */
+#define INCLUDES_DONE
+#endif  /* Normal includes */
+
+/* Interactive UNIX for i386 and i486 -- Thanks to jeffrey@itm.itm.org */
+#ifdef ISC
+#include <net/errno.h>
+#include <sys/types.h>
+#include <sys/tty.h>
+#include <sys/sioctl.h>
+#include <sys/bsdtypes.h>
+#include <sys/fcntl.h>
+#ifndef MERGE
+#define MERGE
+#include <sys/pty.h>
+#undef MERGE
+#else
+#include <sys/pty.h>
+#endif /* !MERGE */
+#ifndef USE_DIRENT
+#define USE_DIRENT	/* sys V style directory open */
+#endif /* USE_DIRENT */
+#include <sys/dirent.h>
+#endif /* ISC */
+
+/*	Directory reading stuff - BSD or SYS V
+*/
+#if defined(UNIX) && !defined(unix)
+#define unix
+#endif /* UNIX && !unix */
+
+#ifdef unix                    /* if this is to compile on a UNIX machine */
+#define GOT_READ_DIR 1    /* if directory reading functions are available */
+#ifdef USE_DIRENT             /* sys v version */
+#include <dirent.h>
+#define direct dirent
+#else
+#include <sys/dir.h>
+#endif /* USE_DIRENT */
+#if defined(sun) && defined(__svr4__)
+#include <sys/fcntl.h>
+#include <limits.h>
+#else
+#if defined(__hpux) || defined(LINUX) || defined (__FreeBSD__) 
+#include <limits.h>
+#endif /* __hpux || LINUX || __FreeBSD__ */
+#endif /* sun && __svr4__ */
+#if !defined(MAXINT) && defined(INT_MAX)
+#define MAXINT INT_MAX
+#endif /* !MAXINT && INT_MAX */
+#endif /* unix */
+
+/*
+Defaults
+========
+  INCLUDE FILES FOR TCP
+ */
+#ifndef TCP_INCLUDES_DONE
+#ifndef NO_IOCTL
+#include <sys/ioctl.h> /* EJB */
+#endif /* !NO_IOCTL */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#ifndef __hpux /* this may or may not be good -marc */
+#include <arpa/inet.h>      /* Must be after netinet/in.h */
+#endif /* !__hpux */
+#include <netdb.h>
+#endif  /* TCP includes */
+
+/*
+  MACROS FOR MANIPULATING MASKS FOR SELECT()
+ */
+#ifdef SELECT
+#ifndef FD_SET
+typedef unsigned int fd_set;
+#define FD_SET(fd,pmask) (*(pmask)) |=  (1<<(fd))
+#define FD_CLR(fd,pmask) (*(pmask)) &= ~(1<<(fd))
+#define FD_ZERO(pmask)   (*(pmask))=0
+#define FD_ISSET(fd,pmask) (*(pmask) & (1<<(fd)))
+#endif  /* !FD_SET */
+#endif  /* SELECT */
+
+
+#endif /* TCP_H */
diff --git a/WWW/Library/Implementation/ufc-crypt.h b/WWW/Library/Implementation/ufc-crypt.h
new file mode 100644
index 00000000..13da8b47
--- /dev/null
+++ b/WWW/Library/Implementation/ufc-crypt.h
@@ -0,0 +1,108 @@
+/*
+ * UFC-crypt: ultra fast crypt(3) implementation
+ *
+ * Copyright (C) 1991, 1992, Free Software Foundation, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ 
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * @(#)ufc-crypt.h	1.16 09/21/92
+ *
+ * Definitions of datatypes 
+ * 
+ */
+
+/* 
+ * Requirements for datatypes:
+ * 
+ * A datatype 'ufc_long' of at least 32 bit
+ * *and*
+ *   A type 'long32' of exactly 32 bits (_UFC_32_)
+ *   *or*
+ *   A type 'long64' of exactly 64 bits (_UFC_64_)
+ *
+ * 'int' is assumed to be at least 8 bit
+ */
+
+/*
+ * #ifdef's for various architectures
+ */
+
+#ifdef cray
+/* thanks to <hutton@opus.sdsc.edu> (Tom Hutton)  for testing */
+typedef unsigned long ufc_long;
+typedef unsigned long long64;
+#define _UFC_64_
+#endif
+
+#ifdef convex
+/* thanks to pcl@convex.oxford.ac.uk (Paul Leyland) for testing */
+typedef unsigned long ufc_long;
+typedef long long     long64;
+#define _UFC_64_
+#endif
+
+#ifdef ksr
+/* 
+ * Note - the KSR machine does not define a unique symbol
+ * which we can check. So you MUST add '-Dksr' to your Makefile.
+ * Thanks to lijewski@theory.tc.cornell.edu (Mike Lijewski) for
+ * the patch.
+ */
+typedef unsigned long ufc_long;
+typedef unsigned long long64;
+#define _UFC_64_
+#endif
+
+/*
+ * For debugging 64 bit code etc with 'gcc'
+ */
+
+#ifdef GCC3232
+typedef unsigned long ufc_long;
+typedef unsigned long long32;
+#define _UFC_32_
+#endif
+
+#ifdef GCC3264
+typedef unsigned long ufc_long;
+typedef long long     long64;
+#define _UFC_64_
+#endif
+
+#ifdef GCC6432
+typedef long long ufc_long;
+typedef unsigned long long32;
+#define _UFC_32_
+#endif
+
+#ifdef GCC6464
+typedef long long     ufc_long;
+typedef long long     long64;
+#define _UFC_64_
+#endif
+
+/*
+ * Catch all for 99.95% of all UNIX machines
+ */
+
+#ifndef _UFC_64_
+#ifndef _UFC_32_
+#define _UFC_32_
+typedef unsigned long ufc_long;
+typedef unsigned long long32;
+#endif
+#endif
+
+
diff --git a/WWW/Library/apollo_m68k/Makefile b/WWW/Library/apollo_m68k/Makefile
new file mode 100644
index 00000000..20aa9ac8
--- /dev/null
+++ b/WWW/Library/apollo_m68k/Makefile
@@ -0,0 +1,38 @@
+#  Make WWW for apollo   NOTE WWW macro changed for unwritable source tree **
+#
+
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = apollo_m68k
+
+# For ASIS installation, the ASIS code for the machine/os
+ASIS_MACH = apollo-68k/sr-10.3
+
+
+CFLAGS =  -O  -DDEBUG
+LFLAGS =
+CC = cc
+LFLAGS =
+
+# Directory for installed binary:
+BINDIR = /usr/local/bin
+
+
+
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = ../..
+#WWW = /user/timbl/hypertext/WWW
+
+#  Where should temporary (object) files go?
+WTMP = ../..
+
+
+# Where is the W3 object library?
+# LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH)
+LIBDIR = /tmp/WWWLibrary_Build
+
+include $(WWW)/Library/Implementation/CommonMakefile
+
+
+
+
diff --git a/WWW/Library/clix/Makefile b/WWW/Library/clix/Makefile
new file mode 100644
index 00000000..e1996d27
--- /dev/null
+++ b/WWW/Library/clix/Makefile
@@ -0,0 +1,30 @@
+#  Make WWW under unix for a.n.other unix system (bsd)
+#   Use this as a template
+
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = clix
+
+# The ASIS repository's name for the machine we are on
+ASIS_MACH = hardware/os
+
+
+CFLAGS =  -O -DDEBUG -DUSG -DUNIX -DCLIX
+LFLAGS =
+CC = cc
+
+# Directory for installed binary:
+BINDIR = /usr/local/bin
+
+# Where is the W3 object library to be installed (not normally done)?
+LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH)
+
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = ../..
+
+
+include $(WWW)/Library/Implementation/CommonMakefile
+
diff --git a/WWW/Library/convex/Makefile b/WWW/Library/convex/Makefile
new file mode 100644
index 00000000..fcd28786
--- /dev/null
+++ b/WWW/Library/convex/Makefile
@@ -0,0 +1,32 @@
+#  Make WWW under ConvexOS
+
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = convex
+
+# The ASIS repository's name for the machine we are on
+ASIS_MACH = hardware/os
+
+
+CFLAGS =  -O -DDEBUG -Dunix
+LFLAGS =
+CC = cc
+
+# Directory for installed binary:
+BINDIR = /usr/local/bin
+
+# Where is the W3 object library to be installed (not normally done)?
+LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH)
+
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = ../..
+
+# HTWAIS = $(LOB)/HTWAIS.o
+# WAIS = YES
+# WAISINC = -I/tmp/freeWAIS-0.202/ir
+# WAISCFLAGS = -DDIRECT_WAIS 
+
+include $(WWW)/Library/Implementation/CommonMakefile
diff --git a/WWW/Library/decstation/Makefile b/WWW/Library/decstation/Makefile
new file mode 100644
index 00000000..b35d278c
--- /dev/null
+++ b/WWW/Library/decstation/Makefile
@@ -0,0 +1,23 @@
+#	Platform-specific Makefile for W3 Library	(decstation)
+#	-----------------------------------------
+#
+
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = ../..
+
+
+#CFLAGS = 
+CFLAGS =  -O
+CC = cc
+#LFLAGS = -lresolv
+LFLAGS = 
+
+ASIS_MACH = dec-station/ultrix-4.2
+WWW_MACH = decstation
+
+# Directory for installed binary:
+LIBDIR = /usr/local/lib
+
+include $(WWW)/Library/Implementation/CommonMakefile
diff --git a/WWW/Library/duns/Makefile b/WWW/Library/duns/Makefile
new file mode 100644
index 00000000..4852817e
--- /dev/null
+++ b/WWW/Library/duns/Makefile
@@ -0,0 +1,489 @@
+# Makefile generated by imake - do not edit!
+# $XConsortium: imake.c,v 1.51 89/12/12 12:37:30 jim Exp $
+#
+# The cpp used on this machine replaces all newlines and multiple tabs and
+# spaces in a macro expansion with a single space.  Imake tries to compensate
+# for this, but is not always successful.
+#
+
+###########################################################################
+# Makefile generated from "Imake.tmpl" and </tmp/IIf.a00214>
+# $XConsortium: Imake.tmpl,v 1.77 89/12/18 17:01:37 jim Exp $
+#
+# Platform-specific parameters may be set in the appropriate .cf
+# configuration files.  Site-wide parameters may be set in the file
+# site.def.  Full rebuilds are recommended if any parameters are changed.
+#
+# If your C preprocessor doesn't define any unique symbols, you'll need
+# to set BOOTSTRAPCFLAGS when rebuilding imake (usually when doing
+# "make Makefile", "make Makefiles", or "make World").
+#
+# If you absolutely can't get imake to work, you'll need to set the
+# variables at the top of each Makefile as well as the dependencies at the
+# bottom (makedepend will do this automatically).
+#
+
+###########################################################################
+# platform-specific configuration parameters - edit sun.cf to change
+
+# platform:  $XConsortium: sun.cf,v 1.38 89/12/23 16:10:10 jim Exp $
+# operating system:  SunOS 4.0.3
+
+###########################################################################
+# site-specific configuration parameters - edit site.def to change
+
+            SHELL = /bin/sh
+
+              TOP = .
+      CURRENT_DIR = .
+
+               AR = ar clq
+  BOOTSTRAPCFLAGS =
+               CC = cc
+
+         COMPRESS = compress
+              CPP = /lib/cpp $(STD_CPP_DEFINES)
+    PREPROCESSCMD = cc -E $(STD_CPP_DEFINES)
+          INSTALL = install
+               LD = ld
+             LINT = lint
+      LINTLIBFLAG = -C
+         LINTOPTS = -axz
+               LN = ln -s
+             MAKE = make
+               MV = mv
+               CP = cp
+           RANLIB = ranlib
+  RANLIBINSTFLAGS =
+               RM = rm -f
+     STD_INCLUDES =
+  STD_CPP_DEFINES =
+      STD_DEFINES =
+ EXTRA_LOAD_FLAGS =
+  EXTRA_LIBRARIES =
+             TAGS = ctags
+
+    SHAREDCODEDEF = -DSHAREDCODE
+         SHLIBDEF = -DSUNSHLIB
+
+    PROTO_DEFINES =
+
+     INSTPGMFLAGS =
+
+     INSTBINFLAGS = -m 0755
+     INSTUIDFLAGS = -m 4755
+     INSTLIBFLAGS = -m 0664
+     INSTINCFLAGS = -m 0444
+     INSTMANFLAGS = -m 0444
+     INSTDATFLAGS = -m 0444
+    INSTKMEMFLAGS = -m 4755
+
+          DESTDIR =
+
+     TOP_INCLUDES = -I$(INCROOT)
+
+      CDEBUGFLAGS = -O
+        CCOPTIONS =
+      COMPATFLAGS =
+
+      ALLINCLUDES = $(STD_INCLUDES) $(TOP_INCLUDES) $(INCLUDES) $(EXTRA_INCLUDES)
+       ALLDEFINES = $(ALLINCLUDES) $(STD_DEFINES) $(PROTO_DEFINES) $(DEFINES) $(COMPATFLAGS)
+           CFLAGS = $(CDEBUGFLAGS) $(CCOPTIONS) $(ALLDEFINES)
+        LINTFLAGS = $(LINTOPTS) -DLINT $(ALLDEFINES)
+           LDLIBS = $(SYS_LIBRARIES) $(EXTRA_LIBRARIES)
+        LDOPTIONS = $(CDEBUGFLAGS) $(CCOPTIONS)
+   LDCOMBINEFLAGS = -X -r
+
+        MACROFILE = sun.cf
+           RM_CMD = $(RM) *.CKP *.ln *.BAK *.bak *.o core errs ,* *~ *.a .emacs_* tags TAGS make.log MakeOut
+
+    IMAKE_DEFINES =
+
+         IRULESRC = $(CONFIGDIR)
+        IMAKE_CMD = $(IMAKE) -DUseInstalled -I$(IRULESRC) $(IMAKE_DEFINES)
+
+     ICONFIGFILES = $(IRULESRC)/Imake.tmpl $(IRULESRC)/Imake.rules \
+			$(IRULESRC)/Project.tmpl $(IRULESRC)/site.def \
+			$(IRULESRC)/$(MACROFILE) $(EXTRA_ICONFIGFILES)
+
+###########################################################################
+# X Window System Build Parameters
+# $XConsortium: Project.tmpl,v 1.63 89/12/18 16:46:44 jim Exp $
+
+###########################################################################
+# X Window System make variables; this need to be coordinated with rules
+# $XConsortium: Project.tmpl,v 1.63 89/12/18 16:46:44 jim Exp $
+
+          PATHSEP = /
+        USRLIBDIR = $(DESTDIR)/usr/lib
+           BINDIR = $(DESTDIR)/usr/bin/X11
+          INCROOT = $(DESTDIR)/usr/include
+     BUILDINCROOT = $(TOP)
+      BUILDINCDIR = $(BUILDINCROOT)/X11
+      BUILDINCTOP = ..
+           INCDIR = $(INCROOT)/X11
+           ADMDIR = $(DESTDIR)/usr/adm
+           LIBDIR = $(USRLIBDIR)/X11
+        CONFIGDIR = $(LIBDIR)/config
+       LINTLIBDIR = $(USRLIBDIR)/lint
+
+          FONTDIR = $(LIBDIR)/fonts
+         XINITDIR = $(LIBDIR)/xinit
+           XDMDIR = $(LIBDIR)/xdm
+           AWMDIR = $(LIBDIR)/awm
+           TWMDIR = $(LIBDIR)/twm
+           GWMDIR = $(LIBDIR)/gwm
+          MANPATH = $(DESTDIR)/usr/man
+    MANSOURCEPATH = $(MANPATH)/man
+           MANDIR = $(MANSOURCEPATH)n
+        LIBMANDIR = $(MANSOURCEPATH)3
+      XAPPLOADDIR = $(LIBDIR)/app-defaults
+
+        SOXLIBREV = 4.2
+          SOXTREV = 4.0
+         SOXAWREV = 4.0
+        SOOLDXREV = 4.0
+         SOXMUREV = 4.0
+        SOXEXTREV = 4.0
+
+       FONTCFLAGS = -t
+
+     INSTAPPFLAGS = $(INSTDATFLAGS)
+
+            IMAKE = imake
+           DEPEND = makedepend
+              RGB = rgb
+            FONTC = bdftosnf
+        MKFONTDIR = mkfontdir
+        MKDIRHIER = /bin/sh $(BINDIR)/mkdirhier.sh
+
+        CONFIGSRC = $(TOP)/config
+        CLIENTSRC = $(TOP)/clients
+          DEMOSRC = $(TOP)/demos
+           LIBSRC = $(TOP)/lib
+          FONTSRC = $(TOP)/fonts
+       INCLUDESRC = $(TOP)/X11
+        SERVERSRC = $(TOP)/server
+          UTILSRC = $(TOP)/util
+        SCRIPTSRC = $(UTILSRC)/scripts
+       EXAMPLESRC = $(TOP)/examples
+       CONTRIBSRC = $(TOP)/../contrib
+           DOCSRC = $(TOP)/doc
+           RGBSRC = $(TOP)/rgb
+        DEPENDSRC = $(UTILSRC)/makedepend
+         IMAKESRC = $(CONFIGSRC)
+         XAUTHSRC = $(LIBSRC)/Xau
+          XLIBSRC = $(LIBSRC)/X
+           XMUSRC = $(LIBSRC)/Xmu
+       TOOLKITSRC = $(LIBSRC)/Xt
+       AWIDGETSRC = $(LIBSRC)/Xaw
+       OLDXLIBSRC = $(LIBSRC)/oldX
+      XDMCPLIBSRC = $(LIBSRC)/Xdmcp
+      BDFTOSNFSRC = $(FONTSRC)/bdftosnf
+     MKFONTDIRSRC = $(FONTSRC)/mkfontdir
+     EXTENSIONSRC = $(TOP)/extensions
+
+  DEPEXTENSIONLIB =
+     EXTENSIONLIB = -lXext
+
+          DEPXLIB = $(DEPEXTENSIONLIB)
+             XLIB = $(EXTENSIONLIB) -lX11
+
+      DEPXAUTHLIB = $(USRLIBDIR)/libXau.a
+         XAUTHLIB =  -lXau
+
+        DEPXMULIB =
+           XMULIB = -lXmu
+
+       DEPOLDXLIB =
+          OLDXLIB = -loldX
+
+      DEPXTOOLLIB =
+         XTOOLLIB = -lXt
+
+        DEPXAWLIB =
+           XAWLIB = -lXaw
+
+ LINTEXTENSIONLIB = $(USRLIBDIR)/llib-lXext.ln
+         LINTXLIB = $(USRLIBDIR)/llib-lX11.ln
+          LINTXMU = $(USRLIBDIR)/llib-lXmu.ln
+        LINTXTOOL = $(USRLIBDIR)/llib-lXt.ln
+          LINTXAW = $(USRLIBDIR)/llib-lXaw.ln
+
+          DEPLIBS = $(DEPXAWLIB) $(DEPXMULIB) $(DEPXTOOLLIB) $(DEPXLIB)
+
+         DEPLIBS1 = $(DEPLIBS)
+         DEPLIBS2 = $(DEPLIBS)
+         DEPLIBS3 = $(DEPLIBS)
+
+###########################################################################
+# Imake rules for building libraries, programs, scripts, and data files
+# rules:  $XConsortium: Imake.rules,v 1.67 89/12/18 17:14:15 jim Exp $
+
+###########################################################################
+# start of Imakefile
+
+#  Make WWW under unix for a.n.other unix system (bsd)
+#   Use this as a template
+
+TK_WWW_SOURCE_PATH=/a/dxcern/userd/tbl/hypertext/WWW/TkWWW/Tcl
+
+TK_WWW_INSTALL_PATH=/a/dxcern/userd/tbl/hypertext/WWW/TkWWW/$(WWW_MACH)
+
+TK_WWW_HOME_PAGE=http://www.w3.org/default.html
+TK_WWW_START_PAGE=$(TK_WWW_HOME_PAGE)
+
+CC = gcc -fno-builtin -Wall
+
+CDEBUGFLAGS = -O3 -pipe
+
+COMPATFLAGS =   -I/afs/athena.mit.edu/course/other/cdsdev/www-compat
+CCOPTIONS =
+
+BINDIR = $(TK_WWW_INSTALL_PATH)
+
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = unix.x
+
+# The ASIS repository's name for the machine we are on
+ASIS_MACH = hardware/os
+
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = /tmp
+
+#	Common Makefile for W3 Library Code
+#	-----------------------------------
+#
+#	(c) CERN 1990, 1991 -- see Copyright.html for conditions
+#
+# This file should be invariant between systems.
+#	DEPENDENCIES NOT COMPLETE
+
+#
+#	make		Compile and link the software (private version)
+#	make clean	Remove intermediate files
+
+WC = $(WWW)/Library
+CMN = $(WWW)/Library/Implementation/
+
+# Where shall we put the objects and built library?
+
+LOB = $(WTMP)/Library/$(WWW_MACH)
+
+# Bug: This path, if relative, is taken relative to the directory
+# in which this makefile is, not the pwd.  This screws up the
+# recursive invocation
+
+VC = 2.14
+
+CFLAGS2 = $(CFLAGS) -I$(CMN)
+
+CERNLIBBIN = $(WWW)/bin
+
+COMMON = $(LOB)/HTParse.o $(LOB)/HTAccess.o $(LOB)/HTTP.o \
+	$(LOB)/HTFile.o	$(LOB)/HTFTP.o $(LOB)/HTTCP.o \
+	$(LOB)/SGML.o $(LOB)/HTMLDTD.o $(LOB)/HTChunk.o \
+	$(LOB)/HTPlain.o $(LOB)/HTWriter.o $(LOB)/HTFWriter.o \
+	$(LOB)/HTMLGen.o \
+	$(LOB)/HTAtom.o $(LOB)/HTAnchor.o $(LOB)/HTStyle.o \
+	$(LOB)/HTList.o $(LOB)/HTString.o $(LOB)/HTAlert.o \
+	$(LOB)/HTRules.o $(LOB)/HTFormat.o $(LOB)/HTInit.o $(LOB)/HTMIME.o \
+	$(LOB)/HTHistory.o $(LOB)/HTNews.o $(LOB)/HTGopher.o \
+	$(LOB)/HTTelnet.o  $(LOB)/HTWSRC.o $(HTWAIS)
+
+CFILES = $(CMN)HTParse.c $(CMN)HTAccess.c $(CMN)HTTP.c $(CMN)HTFile.c \
+	$(CMN)HTFTP.c   $(CMN)HTTCP.c     $(CMN)SGML.c	\
+	$(CMN)HTMLDTD.c \
+	$(CMN)HTPlain.c	$(CMN)HTWriter.c  $(CMN)HTFWriter.c $(CMN)HTMLGen.c	\
+	$(CMN)HTChunk.c $(CMN)HTAtom.c   $(CMN)HTAnchor.c $(CMN)HTStyle.c \
+	$(CMN)HTList.c  $(CMN)HTString.c $(CMN)HTAlert.c $(CMN)HTRules.c \
+	$(CMN)HTFormat.c $(CMN)HTInit.c $(CMN)HTMIME.c $(CMN)HTHistory.c \
+	$(CMN)HTNews.c  $(CMN)HTGopher.c $(CMN)HTTelnet.c \
+	$(CMN)HTWAIS.c  $(CMN)HTWSRC.c
+
+HFILES = $(CMN)HTParse.h $(CMN)HTAccess.h $(CMN)HTTP.h $(CMN)HTFile.h \
+	$(CMN)HTFTP.h $(CMN)HTTCP.h \
+	$(CMN)SGML.h $(CMN)HTML.h $(CMN)HTMLDTD.h $(CMN)HTChunk.h \
+	$(CMN)HTPlain.h		$(CMN)HTWriter.h \
+	$(CMN)HTFWriter.h 	$(CMN)HTMLGen.h	\
+	$(CMN)HTStream.h \
+	$(CMN)HTAtom.h $(CMN)HTAnchor.h $(CMN)HTStyle.h \
+	$(CMN)HTList.h \
+	$(CMN)HTString.h $(CMN)HTAlert.h $(CMN)HTRules.h \
+	$(CMN)HTFormat.h $(CMN)HTInit.h \
+	$(CMN)HTMIME.h $(CMN)HTHistory.h $(CMN)HTNews.h \
+	$(CMN)HTGopher.h \
+	$(CMN)HTUtils.h $(CMN)tcp.h $(CMN)WWW.h $(CMN)HText.h \
+	$(CMN)HTTelnet.h \
+	$(CMN)HTWAIS.h  $(CMN)HTWSRC.h
+
+SOURCES = $(CFILES) $(HFILES) $(CMN)Version.make $(CMN)CommonMakefile \
+	$(WWW)/README.txt $(WWW)/Copyright.txt $(WWW)/BUILD
+SPECIFIC = $(WWW)/All
+
+#	Library
+#
+#  On SGI, ranlib is unnecessary and does not exist so we ignore errors
+# for that step
+all: $(LOB)/libwww.a
+	$(MV) $(LOB)/libwww.a $(WC)/$(WWW_MACH)
+
+$(LOB)/libwww.a : $(COMMON)
+	ar r $(LOB)/libwww.a $(COMMON)
+	-ranlib $(LOB)/libwww.a
+
+#	Clean up everything generatable except final products
+clean ::
+	$(RM) $(LOB)
+
+#	Clean up everything generatable including final products
+
+cleanall :: clean
+	$(RM) $(LOB)/libwww.a
+
+# 			Common code
+#			-----------
+
+#	Directory for object files - .created checks it exists
+
+OE = $(LOB)/.created
+$(OE) :
+	-mkdir $(WTMP)
+	-mkdir $(WTMP)/Library
+	-mkdir $(WTMP)/Library/$(WWW_MACH)
+	touch $@
+
+$(LOB)/HTList.o : $(OE) $(CMN)HTList.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTList.c
+
+$(LOB)/HTAnchor.o : $(OE) $(CMN)HTAnchor.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAnchor.c
+
+$(LOB)/HTFormat.o : $(OE) $(CMN)HTFormat.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFormat.c
+
+$(LOB)/HTInit.o : $(OE) $(CMN)HTInit.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTInit.c
+
+$(LOB)/HTMIME.o : $(OE) $(CMN)HTMIME.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMIME.c
+
+$(LOB)/HTHistory.o : $(OE) $(CMN)HTHistory.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTHistory.c
+
+$(LOB)/HTNews.o : $(OE) $(CMN)HTNews.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTNews.c
+
+$(LOB)/HTGopher.o : $(OE) $(CMN)HTGopher.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTGopher.c
+
+$(LOB)/HTTelnet.o : $(OE) $(CMN)HTTelnet.c $(CMN)HTUtils.h $(CMN)HTTelnet.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTelnet.c
+
+$(LOB)/HTStyle.o : $(OE) $(CMN)HTStyle.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTStyle.c
+
+$(LOB)/HTAtom.o : $(OE) $(CMN)HTAtom.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAtom.c
+
+$(LOB)/HTChunk.o : $(OE) $(CMN)HTChunk.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTChunk.c
+
+$(LOB)/HTString.o : $(OE) $(CMN)HTString.c $(CMN)HTUtils.h $(CMN)Version.make
+	$(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTString.c
+
+$(LOB)/HTAlert.o : $(OE) $(CMN)HTAlert.c $(CMN)HTUtils.h $(CMN)Version.make
+	$(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTAlert.c
+
+$(LOB)/HTRules.o : $(OE) $(CMN)HTRules.c $(CMN)HTUtils.h $(CMN)Version.make
+	$(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTRules.c
+
+$(LOB)/SGML.o : $(OE) $(CMN)SGML.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)SGML.c
+
+$(LOB)/HTMLGen.o : $(OE) $(CMN)HTMLGen.c $(CMN)HTUtils.h $(CMN)HTMLDTD.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMLGen.c
+
+$(LOB)/HTMLDTD.o : $(OE) $(CMN)HTMLDTD.c $(CMN)SGML.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMLDTD.c
+
+$(LOB)/HTPlain.o : $(OE) $(CMN)HTPlain.c $(CMN)HTPlain.h $(CMN)HTStream.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTPlain.c
+
+$(LOB)/HTWAIS.o : $(OE) $(CMN)HTWAIS.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(WAISINC) $(CMN)HTWAIS.c
+
+$(LOB)/HTWSRC.o : $(OE) $(CMN)HTWSRC.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTWSRC.c
+
+$(LOB)/HTWriter.o : $(OE) $(CMN)HTWriter.c $(CMN)HTWriter.h $(CMN)HTStream.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTWriter.c
+
+$(LOB)/HTFWriter.o : $(OE) $(CMN)HTFWriter.c $(CMN)HTFWriter.h $(CMN)HTStream.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFWriter.c
+
+#	Communications & Files
+
+$(LOB)/HTTP.o : $(OE) $(CMN)HTTP.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTP.c
+
+$(LOB)/HTTCP.o : $(OE) $(CMN)HTTCP.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTCP.c
+
+$(LOB)/HTFile.o : $(OE) $(CMN)HTFile.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFile.c
+
+$(LOB)/HTFTP.o : $(OE) $(CMN)HTFTP.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFTP.c
+
+$(LOB)/HTAccess.o : $(OE)  $(CMN)HTAccess.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAccess.c
+
+$(LOB)/HTParse.o : $(OE) $(CMN)HTParse.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTParse.c
+
+###########################################################################
+# common rules for all Makefiles - do not edit
+
+emptyrule::
+
+clean::
+	$(RM_CMD) \#*
+
+Makefile::
+	-@if [ -f Makefile ]; then \
+	echo "	$(RM) Makefile.bak; $(MV) Makefile Makefile.bak"; \
+	$(RM) Makefile.bak; $(MV) Makefile Makefile.bak; \
+	else exit 0; fi
+	$(IMAKE_CMD) -DTOPDIR=$(TOP) -DCURDIR=$(CURRENT_DIR)
+
+tags::
+	$(TAGS) -w *.[ch]
+	$(TAGS) -xw *.[ch] > TAGS
+
+saber:
+	#load $(ALLDEFINES) $(SRCS)
+
+osaber:
+	#load $(ALLDEFINES) $(OBJS)
+
+###########################################################################
+# empty rules for directories that do not have SUBDIRS - do not edit
+
+install::
+	@echo "install in $(CURRENT_DIR) done"
+
+install.man::
+	@echo "install.man in $(CURRENT_DIR) done"
+
+Makefiles::
+
+includes::
+
+###########################################################################
+# dependencies generated by makedepend
+
diff --git a/WWW/Library/freebsd/Makefile b/WWW/Library/freebsd/Makefile
new file mode 100644
index 00000000..04fd07ab
--- /dev/null
+++ b/WWW/Library/freebsd/Makefile
@@ -0,0 +1,27 @@
+#  Make WWW under FreeBSD
+#
+
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = freebsd
+
+# The ASIS repository's name for the machine we are on
+ASIS_MACH = i386/FreeBSD
+
+
+CFLAGS += -O -DDEBUG
+LFLAGS =
+
+# Directory for installed binary:
+BINDIR = /usr/local/bin
+
+# Where is the W3 object library to be installed (not normally done)?
+LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH)
+
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = ../..
+
+include $(WWW)/Library/Implementation/CommonMakefile
diff --git a/WWW/Library/isc/Makefile b/WWW/Library/isc/Makefile
new file mode 100644
index 00000000..29de6885
--- /dev/null
+++ b/WWW/Library/isc/Makefile
@@ -0,0 +1,30 @@
+#  Make WWW under unix for a.n.other unix system (bsd)
+#   Use this as a template
+
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = isc
+
+# The ASIS repository's name for the machine we are on
+ASIS_MACH = intel/isc
+
+
+CFLAGS =  -DDEBUG -O -DISC -Dvfork=fork
+LFLAGS =
+CC = cc
+
+# Directory for installed binary:
+BINDIR = /usr/local/bin
+
+# Where is the W3 object library to be installed (not normally done)?
+LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH)
+
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = ../..
+
+
+include $(WWW)/Library/Implementation/CommonMakefile
+
diff --git a/WWW/Library/mips/Makefile b/WWW/Library/mips/Makefile
new file mode 100644
index 00000000..1c84cbe0
--- /dev/null
+++ b/WWW/Library/mips/Makefile
@@ -0,0 +1,29 @@
+# Makefile for WWW under svr4
+#
+
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = mips
+
+# The ASIS repository's name for the machine we are on
+ASIS_MACH = mips/mips
+
+
+#CFLAGS =  -DDEBUG -systype svr3 -DMIPS -DNO_BCOPY
+CFLAGS =  -O -DDEBUG -systype svr3  -I/svr3/usr/include/bsd
+LFLAGS =
+CC = cc
+
+# Directory for installed binary:
+BINDIR = /usr/local/bin
+
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = ../..
+
+# Where is the W3 object library?
+LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH)
+
+include $(WWW)/Library/Implementation/CommonMakefile
diff --git a/WWW/Library/netbsd/Makefile b/WWW/Library/netbsd/Makefile
new file mode 100644
index 00000000..ae92760a
--- /dev/null
+++ b/WWW/Library/netbsd/Makefile
@@ -0,0 +1,29 @@
+#  Make WWW under NetBSD
+#
+
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = netbsd
+
+# The ASIS repository's name for the machine we are on
+ASIS_MACH = i386/NetBSD
+
+
+CFLAGS = -O -DUSE_DIRENT
+LFLAGS =
+CC = cc
+
+# Directory for installed binary:
+BINDIR = /usr/local/bin
+
+# Where is the W3 object library to be installed (not normally done)?
+LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH)
+
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = ../..
+
+include $(WWW)/Library/Implementation/CommonMakefile
+
diff --git a/WWW/Library/next/Makefile b/WWW/Library/next/Makefile
new file mode 100644
index 00000000..bd35c89a
--- /dev/null
+++ b/WWW/Library/next/Makefile
@@ -0,0 +1,40 @@
+#	Platform-specific Makefile for W3 Library	(NeXT)
+#	-----------------------------------------
+#
+#	Library compiled with fudge to alow XMOSAIC to
+#	pick up binary files... for now.
+
+WWW = ../..
+
+#  Where should temporary (object) files go? Normally, WTMP = $(WWW)
+#WTMP = /tmp
+WTMP = $(WWW)
+
+# For MACH 3.0 it seems -bsd is needed to order to define errno
+# in /usr/include/bsd/sys/errno.h. But __STRICT_BSD__ is needed for
+# errno.
+
+CFLAGS =  -Wall -O -DXMOSAIC_HACK
+
+#	Yes please, I want direct WAIS access
+#
+#WAIS = ../../../freeWAIS
+#WAISINC = -I$(WAIS)/ir
+#WAISCFLAGS = -DDIRECT_WAIS
+#WAISLIB =  $(WAIS)/bin/client.a $(WAIS)/bin/wais.a
+#  $(WAIS)/bin/inv.a $(WAIS)/bin/wais.a
+#HTWAIS = $(WTMP)/Library/$(WWW_MACH)/HTWAIS.o
+
+CC = cc
+#	For testing memory leaks only!   Use /NextDeveloper/MallocDebug app
+LFLAGS = -lMallocDebug
+#LFLAGS =
+
+WWW_MACH = next
+ASIS_MACH = next/2.0
+
+
+# Directory for installed binary:
+LIBDIR = /usr/local/lib
+
+include $(WWW)/Library/Implementation/CommonMakefile
diff --git a/WWW/Library/osf/Makefile b/WWW/Library/osf/Makefile
new file mode 100644
index 00000000..a81457a6
--- /dev/null
+++ b/WWW/Library/osf/Makefile
@@ -0,0 +1,23 @@
+#	Platform-specific Makefile for W3 Library	(decstation)
+#	-----------------------------------------
+#
+
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = ../..
+
+
+#CFLAGS = 
+CFLAGS = -DSYS5 
+CC = cc -O
+#LFLAGS =
+LFLAGS = 
+
+ASIS_MACH = alpha/osf1
+WWW_MACH = osf
+
+# Directory for installed binary:
+LIBDIR = /usr/local/lib
+
+include $(WWW)/Library/Implementation/CommonMakefile
diff --git a/WWW/Library/ptx/Makefile b/WWW/Library/ptx/Makefile
new file mode 100644
index 00000000..772d6c6a
--- /dev/null
+++ b/WWW/Library/ptx/Makefile
@@ -0,0 +1,29 @@
+#  Make WWW under Sequent's DYNIX/ptx
+#
+
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = ptx
+
+# The ASIS repository's name for the machine we are on
+ASIS_MACH = Sequent/ptx
+
+
+CFLAGS =  -O -DDEBUG -DUSE_DIRENT -DSVR4 -DNO_IOCTL -DUSE_FCNTL
+LFLAGS =
+CC = cc
+
+# Directory for installed binary:
+BINDIR = /usr/local/bin
+
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = ../..
+
+
+# Where is the W3 object library?
+LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH)
+
+include $(WWW)/Library/Implementation/CommonMakefile
diff --git a/WWW/Library/rs6000/Makefile b/WWW/Library/rs6000/Makefile
new file mode 100644
index 00000000..334a5a4a
--- /dev/null
+++ b/WWW/Library/rs6000/Makefile
@@ -0,0 +1,29 @@
+#  Make WWW under unix for rs6000 with no gcc
+#
+
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = rs6000
+
+# For ASIS
+ASIS_MACH = ibm-rs6000/aix-3.2
+
+# Directory for installed binary:
+BINDIR = /usr/local/bin
+
+# The AIX compiler does not define unix... AIX will do it and avoid realloc bug
+
+CFLAGS = -O -DDEBUG 
+CC = cc
+LFLAGS =
+
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = $(WWW)
+
+# Where is the W3 object library?
+LIBDIR = /usr/local/lib
+
+include $(WWW)/Library/Implementation/CommonMakefile
diff --git a/WWW/Library/sco/Makefile b/WWW/Library/sco/Makefile
new file mode 100644
index 00000000..a00a948b
--- /dev/null
+++ b/WWW/Library/sco/Makefile
@@ -0,0 +1,33 @@
+#  Make WWW under unix for a.n.other unix system (bsd)
+#   Use this as a template
+
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = sco
+
+# The ASIS repository's name for the machine we are on
+#  SCO does not presently have ranlib.  Ignore the error
+#  message about that when the CommonMakefile tries to
+#  invoke it.
+ASIS_MACH = intel/sco
+
+
+CFLAGS =  -O -DDEBUG -DSVR4
+LFLAGS =
+CC = cc
+
+# Directory for installed binary:
+BINDIR = /usr/local/bin
+
+# Where is the W3 object library to be installed (not normally done)?
+LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH)
+
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = ../..
+
+
+include $(WWW)/Library/Implementation/CommonMakefile
+
diff --git a/WWW/Library/sgi/Makefile b/WWW/Library/sgi/Makefile
new file mode 100644
index 00000000..c91bdd66
--- /dev/null
+++ b/WWW/Library/sgi/Makefile
@@ -0,0 +1,30 @@
+#  Make WWW for Silicon Graphics
+#
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = sgi
+
+# Architecutre in ASIS scheme
+#  SGI does not presently have ranlib.  Ignore the error
+#  message about that when the CommonMakefile tries to
+#  invoke it.
+ASIS_MACH = sgi/iris-3.5
+
+CFLAGS = -DDEBUG -O
+CC = cc
+LFLAGS =
+
+# Directory for installed binary:
+BINDIR = /usr/local/bin
+
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = ../..
+
+
+# Where is the W3 object library?
+LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH)
+
+include $(WWW)/Library/Implementation/CommonMakefile
diff --git a/WWW/Library/snake/Makefile b/WWW/Library/snake/Makefile
new file mode 100644
index 00000000..06db3012
--- /dev/null
+++ b/WWW/Library/snake/Makefile
@@ -0,0 +1,33 @@
+#  Make WWW under unix for HP 700 or 800 (Snake) using cc
+#
+
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = snake
+
+# Distribution point for ASIS repository
+ASIS_MACH = hp-700/hpux-8.0
+
+CFLAGS = -O -DDEBUG
+
+#	Link with BSD library for getwd()
+LFLAGS = -lBSD
+
+#CC = cc
+#CC = gcc
+
+# Directory for installed binary:
+BINDIR = /usr/local/bin
+
+
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = ../..
+
+
+# Where is the W3 object library?
+LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH)
+
+include $(WWW)/Library/Implementation/CommonMakefile
diff --git a/WWW/Library/solaris2/Makefile b/WWW/Library/solaris2/Makefile
new file mode 100644
index 00000000..a390cae9
--- /dev/null
+++ b/WWW/Library/solaris2/Makefile
@@ -0,0 +1,29 @@
+#  Make WWW under unix for sun 4
+#
+
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = solaris2
+
+# The ASIS repository's name for the machine we are on
+ASIS_MACH = sun-4/sunos-5.2
+
+
+CFLAGS =  -O -DDEBUG -DNGROUPS=16 -Dd_namlen=d_reclen -DNO_BCOPY -DSOLARIS2 -DSVR4 -DUSE_DIRENT
+LFLAGS =
+
+# Directory for installed binary:
+BINDIR = /usr/local/bin
+
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = ../..
+
+
+# Where is the W3 object library?
+LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH)
+
+include $(WWW)/Library/Implementation/CommonMakefile
+
diff --git a/WWW/Library/sun3/Makefile b/WWW/Library/sun3/Makefile
new file mode 100644
index 00000000..ee9c6288
--- /dev/null
+++ b/WWW/Library/sun3/Makefile
@@ -0,0 +1,29 @@
+#  Make WWW under unix for sun 3
+#
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = sun3
+
+# For ASIS installation, the ASIS code for the machine/os
+ASIS_MACH = sun-3/sunos-4.1.1
+
+# Directory for installed binary:
+BINDIR = /usr/local/bin
+
+CFLAGS = -DDEBUG -O
+LFLAGS =
+
+# Directory for installed binary:
+BINDIR = /usr/local/bin
+
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = ../..
+
+
+# Where is the W3 object library?
+LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH)
+
+include $(WWW)/Library/Implementation/CommonMakefile
diff --git a/WWW/Library/sun4/Makefile b/WWW/Library/sun4/Makefile
new file mode 100644
index 00000000..cf14decb
--- /dev/null
+++ b/WWW/Library/sun4/Makefile
@@ -0,0 +1,29 @@
+#  Make WWW under unix for sun 4
+#
+
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = sun4
+
+# The ASIS repository's name for the machine we are on
+ASIS_MACH = sun-4/sunos-4.1.1
+
+
+CFLAGS =  -DDEBUG -O
+LFLAGS =
+
+# Directory for installed binary:
+BINDIR = /usr/local/bin
+
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = ../..
+
+
+# Where is the W3 object library?
+LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH)
+
+include $(WWW)/Library/Implementation/CommonMakefile
+
diff --git a/WWW/Library/svr4/Makefile b/WWW/Library/svr4/Makefile
new file mode 100644
index 00000000..a97e6656
--- /dev/null
+++ b/WWW/Library/svr4/Makefile
@@ -0,0 +1,29 @@
+#  Make WWW under svr4
+#
+
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = svr4
+
+# The ASIS repository's name for the machine we are on
+ASIS_MACH = generic/svr4
+
+
+CFLAGS =  -O -DDEBUG -DUSE_DIRENT -DSVR4
+LFLAGS =
+CC = cc
+
+# Directory for installed binary:
+BINDIR = /usr/local/bin
+
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = ../..
+
+
+# Where is the W3 object library?
+LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH)
+
+include $(WWW)/Library/Implementation/CommonMakefile
diff --git a/WWW/Library/unix/Makefile b/WWW/Library/unix/Makefile
new file mode 100644
index 00000000..c8d32fd9
--- /dev/null
+++ b/WWW/Library/unix/Makefile
@@ -0,0 +1,30 @@
+#  Make WWW under unix for a.n.other unix system (bsd)
+#   Use this as a template
+
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = unix
+
+# The ASIS repository's name for the machine we are on
+ASIS_MACH = hardware/os
+
+
+CFLAGS =  -O -DDEBUG
+LFLAGS =
+CC = cc
+
+# Directory for installed binary:
+BINDIR = /usr/local/bin
+
+# Where is the W3 object library to be installed (not normally done)?
+LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH)
+
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = ../..
+
+
+include $(WWW)/Library/Implementation/CommonMakefile
+
diff --git a/WWW/Library/unix_x/Makefile b/WWW/Library/unix_x/Makefile
new file mode 100644
index 00000000..23c12453
--- /dev/null
+++ b/WWW/Library/unix_x/Makefile
@@ -0,0 +1,491 @@
+# Makefile generated by imake - do not edit!
+# $XConsortium: imake.c,v 1.51 89/12/12 12:37:30 jim Exp $
+#
+# The cpp used on this machine replaces all newlines and multiple tabs and
+# spaces in a macro expansion with a single space.  Imake tries to compensate
+# for this, but is not always successful.
+#
+
+###########################################################################
+# Makefile generated from "Imake.tmpl" and </tmp/IIf.a02602>
+# $XConsortium: Imake.tmpl,v 1.77 89/12/18 17:01:37 jim Exp $
+#
+# Platform-specific parameters may be set in the appropriate .cf
+# configuration files.  Site-wide parameters may be set in the file
+# site.def.  Full rebuilds are recommended if any parameters are changed.
+#
+# If your C preprocessor doesn't define any unique symbols, you'll need
+# to set BOOTSTRAPCFLAGS when rebuilding imake (usually when doing
+# "make Makefile", "make Makefiles", or "make World").
+#
+# If you absolutely can't get imake to work, you'll need to set the
+# variables at the top of each Makefile as well as the dependencies at the
+# bottom (makedepend will do this automatically).
+#
+
+###########################################################################
+# platform-specific configuration parameters - edit sun.cf to change
+
+# platform:  $XConsortium: sun.cf,v 1.38 89/12/23 16:10:10 jim Exp $
+# operating system:  SunOS 4.1.1
+
+###########################################################################
+# site-specific configuration parameters - edit site.def to change
+
+# site:  $XConsortium: site.def,v 1.21 89/12/06 11:46:50 jim Exp $
+
+            SHELL = /bin/sh
+
+              TOP = ../../../.
+      CURRENT_DIR = ./../Library/unix_x
+
+               AR = ar cq
+  BOOTSTRAPCFLAGS =
+               CC = gcc -DNOSTDHDRS -fstrength-reduce -fpcc-struct-return -fwritable-strings -traditional
+
+         COMPRESS = compress
+              CPP = /lib/cpp $(STD_CPP_DEFINES)
+    PREPROCESSCMD = gcc -DNOSTDHDRS -fstrength-reduce -fpcc-struct-return -fwritable-strings -traditional -E $(STD_CPP_DEFINES)
+          INSTALL = install
+               LD = ld
+             LINT = lint
+      LINTLIBFLAG = -C
+         LINTOPTS = -axz
+               LN = ln -s
+             MAKE = make
+               MV = mv
+               CP = cp
+           RANLIB = ranlib
+  RANLIBINSTFLAGS =
+               RM = rm -f
+     STD_INCLUDES =
+  STD_CPP_DEFINES =
+      STD_DEFINES =
+ EXTRA_LOAD_FLAGS =
+  EXTRA_LIBRARIES =
+             TAGS = ctags
+
+    SHAREDCODEDEF = -DSHAREDCODE
+         SHLIBDEF = -DSUNSHLIB
+
+    PROTO_DEFINES =
+
+     INSTPGMFLAGS =
+
+     INSTBINFLAGS = -m 0755
+     INSTUIDFLAGS = -m 4755
+     INSTLIBFLAGS = -m 0664
+     INSTINCFLAGS = -m 0444
+     INSTMANFLAGS = -m 0444
+     INSTDATFLAGS = -m 0444
+    INSTKMEMFLAGS = -m 4755
+
+          DESTDIR =
+
+     TOP_INCLUDES = -I$(INCROOT)
+
+      CDEBUGFLAGS = -O
+        CCOPTIONS =
+      COMPATFLAGS =
+
+      ALLINCLUDES = $(STD_INCLUDES) $(TOP_INCLUDES) $(INCLUDES) $(EXTRA_INCLUDES)
+       ALLDEFINES = $(ALLINCLUDES) $(STD_DEFINES) $(PROTO_DEFINES) $(DEFINES) $(COMPATFLAGS)
+           CFLAGS = $(CDEBUGFLAGS) $(CCOPTIONS) $(ALLDEFINES)
+        LINTFLAGS = $(LINTOPTS) -DLINT $(ALLDEFINES)
+           LDLIBS = $(SYS_LIBRARIES) $(EXTRA_LIBRARIES)
+        LDOPTIONS = $(CDEBUGFLAGS) $(CCOPTIONS)
+   LDCOMBINEFLAGS = -X -r
+
+        MACROFILE = sun.cf
+           RM_CMD = $(RM) *.CKP *.ln *.BAK *.bak *.o core errs ,* *~ *.a .emacs_* tags TAGS make.log MakeOut
+
+    IMAKE_DEFINES =
+
+         IRULESRC = $(CONFIGDIR)
+        IMAKE_CMD = $(IMAKE) -DUseInstalled -I$(IRULESRC) $(IMAKE_DEFINES)
+
+     ICONFIGFILES = $(IRULESRC)/Imake.tmpl $(IRULESRC)/Imake.rules \
+			$(IRULESRC)/Project.tmpl $(IRULESRC)/site.def \
+			$(IRULESRC)/$(MACROFILE) $(EXTRA_ICONFIGFILES)
+
+###########################################################################
+# X Window System Build Parameters
+# $XConsortium: Project.tmpl,v 1.63 89/12/18 16:46:44 jim Exp $
+
+###########################################################################
+# X Window System make variables; this need to be coordinated with rules
+# $XConsortium: Project.tmpl,v 1.63 89/12/18 16:46:44 jim Exp $
+
+          PATHSEP = /
+        USRLIBDIR = $(DESTDIR)/usr/lib
+           BINDIR = $(DESTDIR)/usr/bin/X11
+          INCROOT = $(DESTDIR)/usr/include
+     BUILDINCROOT = $(TOP)
+      BUILDINCDIR = $(BUILDINCROOT)/X11
+      BUILDINCTOP = ..
+           INCDIR = $(INCROOT)/X11
+           ADMDIR = $(DESTDIR)/usr/adm
+           LIBDIR = $(USRLIBDIR)/X11
+        CONFIGDIR = $(LIBDIR)/config
+       LINTLIBDIR = $(USRLIBDIR)/lint
+
+          FONTDIR = $(LIBDIR)/fonts
+         XINITDIR = $(LIBDIR)/xinit
+           XDMDIR = $(LIBDIR)/xdm
+           AWMDIR = $(LIBDIR)/awm
+           TWMDIR = $(LIBDIR)/twm
+           GWMDIR = $(LIBDIR)/gwm
+          MANPATH = $(DESTDIR)/usr/man
+    MANSOURCEPATH = $(MANPATH)/man
+           MANDIR = $(MANSOURCEPATH)n
+        LIBMANDIR = $(MANSOURCEPATH)3
+      XAPPLOADDIR = $(LIBDIR)/app-defaults
+
+        SOXLIBREV = 4.2
+          SOXTREV = 4.0
+         SOXAWREV = 4.0
+        SOOLDXREV = 4.0
+         SOXMUREV = 4.0
+        SOXEXTREV = 4.0
+
+       FONTCFLAGS = -t
+
+     INSTAPPFLAGS = $(INSTDATFLAGS)
+
+            IMAKE = imake
+           DEPEND = makedepend
+              RGB = rgb
+            FONTC = bdftosnf
+        MKFONTDIR = mkfontdir
+        MKDIRHIER = /bin/sh $(BINDIR)/mkdirhier.sh
+
+        CONFIGSRC = $(TOP)/config
+        CLIENTSRC = $(TOP)/clients
+          DEMOSRC = $(TOP)/demos
+           LIBSRC = $(TOP)/lib
+          FONTSRC = $(TOP)/fonts
+       INCLUDESRC = $(TOP)/X11
+        SERVERSRC = $(TOP)/server
+          UTILSRC = $(TOP)/util
+        SCRIPTSRC = $(UTILSRC)/scripts
+       EXAMPLESRC = $(TOP)/examples
+       CONTRIBSRC = $(TOP)/../contrib
+           DOCSRC = $(TOP)/doc
+           RGBSRC = $(TOP)/rgb
+        DEPENDSRC = $(UTILSRC)/makedepend
+         IMAKESRC = $(CONFIGSRC)
+         XAUTHSRC = $(LIBSRC)/Xau
+          XLIBSRC = $(LIBSRC)/X
+           XMUSRC = $(LIBSRC)/Xmu
+       TOOLKITSRC = $(LIBSRC)/Xt
+       AWIDGETSRC = $(LIBSRC)/Xaw
+       OLDXLIBSRC = $(LIBSRC)/oldX
+      XDMCPLIBSRC = $(LIBSRC)/Xdmcp
+      BDFTOSNFSRC = $(FONTSRC)/bdftosnf
+     MKFONTDIRSRC = $(FONTSRC)/mkfontdir
+     EXTENSIONSRC = $(TOP)/extensions
+
+  DEPEXTENSIONLIB = $(USRLIBDIR)/libXext.a
+     EXTENSIONLIB =  -lXext
+
+          DEPXLIB = $(DEPEXTENSIONLIB)
+             XLIB = $(EXTENSIONLIB) -lX11
+
+      DEPXAUTHLIB = $(USRLIBDIR)/libXau.a
+         XAUTHLIB =  -lXau
+
+        DEPXMULIB =
+           XMULIB = -lXmu
+
+       DEPOLDXLIB =
+          OLDXLIB = -loldX
+
+      DEPXTOOLLIB =
+         XTOOLLIB = -lXt
+
+        DEPXAWLIB =
+           XAWLIB = -lXaw
+
+ LINTEXTENSIONLIB = $(USRLIBDIR)/llib-lXext.ln
+         LINTXLIB = $(USRLIBDIR)/llib-lX11.ln
+          LINTXMU = $(USRLIBDIR)/llib-lXmu.ln
+        LINTXTOOL = $(USRLIBDIR)/llib-lXt.ln
+          LINTXAW = $(USRLIBDIR)/llib-lXaw.ln
+
+          DEPLIBS = $(DEPXAWLIB) $(DEPXMULIB) $(DEPXTOOLLIB) $(DEPXLIB)
+
+         DEPLIBS1 = $(DEPLIBS)
+         DEPLIBS2 = $(DEPLIBS)
+         DEPLIBS3 = $(DEPLIBS)
+
+###########################################################################
+# Imake rules for building libraries, programs, scripts, and data files
+# rules:  $XConsortium: Imake.rules,v 1.67 89/12/18 17:14:15 jim Exp $
+
+###########################################################################
+# start of Imakefile
+
+#  Make WWW under unix for a.n.other unix system (bsd)
+#   Use this as a template
+
+TK_WWW_SOURCE_PATH=/a/dxcern/userd/tbl/hypertext/WWW/TkWWW/Tcl
+
+TK_WWW_INSTALL_PATH=/a/dxcern/userd/tbl/hypertext/WWW/TkWWW/$WWW_MACH
+
+TK_WWW_HOME_PAGE=http://www.w3.org/default.html
+TK_WWW_START_PAGE=$(TK_WWW_HOME_PAGE)
+
+CC = gcc -fno-builtin -Wall
+
+CDEBUGFLAGS = -O3 -pipe
+
+COMPATFLAGS =   -I/afs/athena.mit.edu/course/other/cdsdev/www-compat
+CCOPTIONS =
+
+BINDIR = $(TK_WWW_INSTALL_PATH)
+
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = unix_x
+
+# The ASIS repository's name for the machine we are on
+ASIS_MACH = hardware/os
+
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = /tmp
+
+#	Common Makefile for W3 Library Code
+#	-----------------------------------
+#
+#	(c) CERN 1990, 1991 -- see Copyright.html for conditions
+#
+# This file should be invariant between systems.
+#	DEPENDENCIES NOT COMPLETE
+
+#
+#	make		Compile and link the software (private version)
+#	make clean	Remove intermediate files
+
+WC = $(WWW)/Library
+CMN = $(WWW)/Library/Implementation/
+
+# Where shall we put the objects and built library?
+
+LOB = $(WTMP)/Library/$(WWW_MACH)
+
+# Bug: This path, if relative, is taken relative to the directory
+# in which this makefile is, not the pwd.  This screws up the
+# recursive invocation
+
+VC = 2.14
+
+CFLAGS2 = $(CFLAGS) -I$(CMN)
+
+CERNLIBBIN = $(WWW)/bin
+
+COMMON = $(LOB)/HTParse.o $(LOB)/HTAccess.o $(LOB)/HTTP.o \
+	$(LOB)/HTBTree.o \
+	$(LOB)/HTFile.o	$(LOB)/HTFTP.o $(LOB)/HTTCP.o \
+	$(LOB)/SGML.o $(LOB)/HTMLDTD.o $(LOB)/HTChunk.o \
+	$(LOB)/HTPlain.o $(LOB)/HTWriter.o $(LOB)/HTFWriter.o \
+	$(LOB)/HTMLGen.o \
+	$(LOB)/HTAtom.o $(LOB)/HTAnchor.o $(LOB)/HTStyle.o \
+	$(LOB)/HTList.o $(LOB)/HTString.o $(LOB)/HTAlert.o \
+	$(LOB)/HTRules.o $(LOB)/HTFormat.o $(LOB)/HTInit.o $(LOB)/HTMIME.o \
+	$(LOB)/HTHistory.o $(LOB)/HTNews.o $(LOB)/HTGopher.o \
+	$(LOB)/HTTelnet.o  $(LOB)/HTWSRC.o $(HTWAIS)
+
+CFILES = $(CMN)HTParse.c $(CMN)HTAccess.c $(CMN)HTTP.c $(CMN)HTBTree.c \
+	$(CMN)HTFile.c \
+	$(CMN)HTFTP.c   $(CMN)HTTCP.c     $(CMN)SGML.c	\
+	$(CMN)HTMLDTD.c \
+	$(CMN)HTPlain.c	$(CMN)HTWriter.c  $(CMN)HTFWriter.c $(CMN)HTMLGen.c	\
+	$(CMN)HTChunk.c $(CMN)HTAtom.c   $(CMN)HTAnchor.c $(CMN)HTStyle.c \
+	$(CMN)HTList.c  $(CMN)HTString.c $(CMN)HTAlert.c $(CMN)HTRules.c \
+	$(CMN)HTFormat.c $(CMN)HTInit.c $(CMN)HTMIME.c $(CMN)HTHistory.c \
+	$(CMN)HTNews.c  $(CMN)HTGopher.c $(CMN)HTTelnet.c \
+	$(CMN)HTWAIS.c  $(CMN)HTWSRC.c
+
+HFILES = $(CMN)HTParse.h $(CMN)HTAccess.h $(CMN)HTTP.h $(CMN)HTBTree.h \
+	$(CMN)HTFile.h \
+	$(CMN)HTFTP.h $(CMN)HTTCP.h \
+	$(CMN)SGML.h $(CMN)HTML.h $(CMN)HTMLDTD.h $(CMN)HTChunk.h \
+	$(CMN)HTPlain.h		$(CMN)HTWriter.h \
+	$(CMN)HTFWriter.h 	$(CMN)HTMLGen.h	\
+	$(CMN)HTStream.h \
+	$(CMN)HTAtom.h $(CMN)HTAnchor.h $(CMN)HTStyle.h \
+	$(CMN)HTList.h \
+	$(CMN)HTString.h $(CMN)HTAlert.h $(CMN)HTRules.h \
+	$(CMN)HTFormat.h $(CMN)HTInit.h \
+	$(CMN)HTMIME.h $(CMN)HTHistory.h $(CMN)HTNews.h \
+	$(CMN)HTGopher.h \
+	$(CMN)HTUtils.h $(CMN)tcp.h $(CMN)WWW.h $(CMN)HText.h \
+	$(CMN)HTTelnet.h \
+	$(CMN)HTWAIS.h  $(CMN)HTWSRC.h
+
+SOURCES = $(CFILES) $(HFILES) $(CMN)Version.make $(CMN)CommonMakefile \
+	$(WWW)/README.txt $(WWW)/Copyright.txt $(WWW)/BUILD
+SPECIFIC = $(WWW)/All
+
+#	Library
+#
+#  On SGI, ranlib is unnecessary and does not exist so we ignore errors
+# for that step
+all: $(LOB)/libwww.a
+	$(MV) $(LOB)/libwww.a $(WC)/$(WWW_MACH)
+
+$(LOB)/libwww.a : $(COMMON)
+	ar r $(LOB)/libwww.a $(COMMON)
+	-ranlib $(LOB)/libwww.a
+
+#	Clean up everything generatable except final products
+clean ::
+	$(RM) $(LOB)
+
+#	Clean up everything generatable including final products
+
+cleanall :: clean
+	$(RM) $(LOB)/libwww.a
+
+# 			Common code
+#			-----------
+
+#	Directory for object files - .created checks it exists
+
+OE = $(LOB)/.created
+$(OE) :
+	-mkdir $(WTMP)
+	-mkdir $(WTMP)/Library
+	-mkdir $(WTMP)/Library/$(WWW_MACH)
+	touch $@
+
+$(LOB)/HTList.o : $(OE) $(CMN)HTList.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTList.c
+
+$(LOB)/HTAnchor.o : $(OE) $(CMN)HTAnchor.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAnchor.c
+
+$(LOB)/HTFormat.o : $(OE) $(CMN)HTFormat.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFormat.c
+
+$(LOB)/HTInit.o : $(OE) $(CMN)HTInit.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTInit.c
+
+$(LOB)/HTMIME.o : $(OE) $(CMN)HTMIME.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMIME.c
+
+$(LOB)/HTHistory.o : $(OE) $(CMN)HTHistory.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTHistory.c
+
+$(LOB)/HTNews.o : $(OE) $(CMN)HTNews.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTNews.c
+
+$(LOB)/HTGopher.o : $(OE) $(CMN)HTGopher.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTGopher.c
+
+$(LOB)/HTTelnet.o : $(OE) $(CMN)HTTelnet.c $(CMN)HTUtils.h $(CMN)HTTelnet.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTelnet.c
+
+$(LOB)/HTStyle.o : $(OE) $(CMN)HTStyle.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTStyle.c
+
+$(LOB)/HTAtom.o : $(OE) $(CMN)HTAtom.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAtom.c
+
+$(LOB)/HTChunk.o : $(OE) $(CMN)HTChunk.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTChunk.c
+
+$(LOB)/HTString.o : $(OE) $(CMN)HTString.c $(CMN)HTUtils.h $(CMN)Version.make
+	$(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTString.c
+
+$(LOB)/HTAlert.o : $(OE) $(CMN)HTAlert.c $(CMN)HTUtils.h $(CMN)Version.make
+	$(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTAlert.c
+
+$(LOB)/HTRules.o : $(OE) $(CMN)HTRules.c $(CMN)HTUtils.h $(CMN)Version.make
+	$(CC) -c -o $@ $(CFLAGS2) -DVC=\"$(VC)\" $(CMN)HTRules.c
+
+$(LOB)/SGML.o : $(OE) $(CMN)SGML.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)SGML.c
+
+$(LOB)/HTMLGen.o : $(OE) $(CMN)HTMLGen.c $(CMN)HTUtils.h $(CMN)HTMLDTD.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMLGen.c
+
+$(LOB)/HTMLDTD.o : $(OE) $(CMN)HTMLDTD.c $(CMN)SGML.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTMLDTD.c
+
+$(LOB)/HTPlain.o : $(OE) $(CMN)HTPlain.c $(CMN)HTPlain.h $(CMN)HTStream.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTPlain.c
+
+$(LOB)/HTWAIS.o : $(OE) $(CMN)HTWAIS.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(WAISINC) $(CMN)HTWAIS.c
+
+$(LOB)/HTWSRC.o : $(OE) $(CMN)HTWSRC.c $(CMN)HTUtils.h $(CMN)HTList.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTWSRC.c
+
+$(LOB)/HTWriter.o : $(OE) $(CMN)HTWriter.c $(CMN)HTWriter.h $(CMN)HTStream.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTWriter.c
+
+$(LOB)/HTFWriter.o : $(OE) $(CMN)HTFWriter.c $(CMN)HTFWriter.h $(CMN)HTStream.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFWriter.c
+
+#	Communications & Files
+
+$(LOB)/HTTP.o : $(OE) $(CMN)HTTP.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTP.c
+
+$(LOB)/HTTCP.o : $(OE) $(CMN)HTTCP.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTTCP.c
+
+$(LOB)/HTBTree.o : $(OE) $(CMN)HTBTree.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTBTree.c
+
+$(LOB)/HTFile.o : $(OE) $(CMN)HTFile.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFile.c
+
+$(LOB)/HTFTP.o : $(OE) $(CMN)HTFTP.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTFTP.c
+
+$(LOB)/HTAccess.o : $(OE)  $(CMN)HTAccess.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTAccess.c
+
+$(LOB)/HTParse.o : $(OE) $(CMN)HTParse.c $(CMN)HTUtils.h
+	$(CC) -c -o $@ $(CFLAGS2) $(CMN)HTParse.c
+
+###########################################################################
+# common rules for all Makefiles - do not edit
+
+emptyrule::
+
+clean::
+	$(RM_CMD) \#*
+
+Makefile::
+	-@if [ -f Makefile ]; then \
+	echo "	$(RM) Makefile.bak; $(MV) Makefile Makefile.bak"; \
+	$(RM) Makefile.bak; $(MV) Makefile Makefile.bak; \
+	else exit 0; fi
+	$(IMAKE_CMD) -DTOPDIR=$(TOP) -DCURDIR=$(CURRENT_DIR)
+
+tags::
+	$(TAGS) -w *.[ch]
+	$(TAGS) -xw *.[ch] > TAGS
+
+###########################################################################
+# empty rules for directories that do not have SUBDIRS - do not edit
+
+install::
+	@echo "install in $(CURRENT_DIR) done"
+
+install.man::
+	@echo "install.man in $(CURRENT_DIR) done"
+
+Makefiles::
+
+includes::
+
+###########################################################################
+# dependencies generated by makedepend
+
diff --git a/WWW/Library/vax_ultrix/Makefile b/WWW/Library/vax_ultrix/Makefile
new file mode 100644
index 00000000..2caf766c
--- /dev/null
+++ b/WWW/Library/vax_ultrix/Makefile
@@ -0,0 +1,33 @@
+#  Make WWW under ultrix with gcc
+#
+
+
+# For W3 distribution, machine type for subdirectories
+WWW_MACH = vax_ultrix
+
+# For ASIS installation, the ASIS code for the machine/os
+ASIS_MACH = none
+
+CC = gcc
+CFLAGS = -O  -DDEBUG -Wall
+LFLAGS = -O
+
+#	This is bug fix for out-of-date ultrix on cernvax
+# LFLAGS = -O pfcode.o -lresolv
+
+# Directory for installed binary:
+BINDIR = /usr/local/unix
+
+
+#_________________ OK if normal W3 distribution
+# Where is the WWW source root?
+WWW = ../..
+
+#  Where should temporary (object) files go?
+WTMP = /tmp
+
+
+# Where is the W3 object library?
+LIBDIR = $(WWW)/Library/Implementation/$(WWW_MACH)
+
+include $(WWW)/Library/Implementation/CommonMakefile
diff --git a/WWW/Library/vms/COPYING.LIB b/WWW/Library/vms/COPYING.LIB
new file mode 100644
index 00000000..eb685a5e
--- /dev/null
+++ b/WWW/Library/vms/COPYING.LIB
@@ -0,0 +1,481 @@
+		  GNU LIBRARY GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+                    675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+		  GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+     Appendix: How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/WWW/Library/vms/descrip.mms b/WWW/Library/vms/descrip.mms
new file mode 100644
index 00000000..b178e538
--- /dev/null
+++ b/WWW/Library/vms/descrip.mms
@@ -0,0 +1,258 @@
+!	Make WorldWideWeb LIBRARY under VMS
+!       =======================================================
+!
+! History:
+!  14 Aug 91 (TBL)	Reconstituted
+!  25 Jun 92 (JFG)	Added TCP socket emulation over DECnet
+!  07 Sep 93 (MD)	Remade for version 2.09a
+!  10 Dec 93 (FM)	Upgrade for version 2.14 with Lynx v2.1
+!  13 Dec 93 (FM)	Added conditional compilations for VAXC vs. DECC
+!			(MMS can't handle a MODULE list as large as the
+!			 WWWLibrary has become, so this just illustrates
+!			 how you'd set it up if it could 8-).
+!  26 Oct 94 (RLD)	Updated to work with VAX/VMS v5.5-1 and AXP/VMS v6.1
+!  31 Oct 94 (RLD)      Updated for Lynx v2.3.4, supporting OpenCMU and
+!                       TCPWare
+!  18 Nov 94 (FM)	Updated for SOCKETSHR/NETLIB
+!  07 Dec 94 (FM)	Updated for DECC/VAX, VAXC/VAX and DECC/AXP
+!  03 May 95 (FM)	Include /NoMember for DECC (not the default on AXP,
+!			and the code assumes byte alignment).
+!  07 Jul 95 (FM)	Added GNUC support.
+!
+! Bugs:
+!	The dependencies are anything but complete - they were
+!	just enough to allow the files to be compiled.
+!
+! Instructions:
+! 	Copy [WWW.LIBRARY.VMS]DESCRIP.MMS into [WWW.LIBRARY.IMPLEMENTATION]
+!	Use the correct command line for your TCP/IP implementation,
+!	inside the IMPLEMENTATION directory:
+!
+!	$ MMS/MACRO=(MULTINET=1)		for VAXC - MultiNet
+!	$ MMS/MACRO=(WIN_TCP=1)			for VAXC - Wollongong TCP/IP
+!	$ MMS/MACRO=(UCX=1)			for VAXC - UCX
+!	$ MMS/MACRO=(CMU_TCP=1)			for VAXC - OpenCMU TCP/IP
+!	$ MMS/MACRO=(SOCKETSHR_TCP=1)		for VAXC - SOCKETSHR/NETLIB
+!	$ MMS/MACRO=(TCPWARE=1)			for VAXC - TCPWare TCP/IP
+!	$ MMS/MACRO=(DECNET=1)		for VAXC - socket emulation over DECnet
+!
+!	$ MMS/MACRO=(MULTINET=1,DEC_C=1)	for DECC - MultiNet
+!	$ MMS/MACRO=(WIN_TCP=1,DEC_C=1)		for DECC - Wollongong TCP/IP
+!	$ MMS/MACRO=(UCX=1,DEC_C=1)		for DECC - UCX
+!	$ MMS/MACRO=(CMU_TCP=1,DEC_C=1)		for DECC - OpenCMU TCP/IP
+!	$ MMS/MACRO=(SOCKETSHR_TCP=1,DEC_C=1)	for DECC - SOCKETSHR/NETLIB
+!	$ MMS/MACRO=(TCPWARE=1,DEC_C=1)		for DECC - TCPWare TCP/IP
+!	$ MMS/MACRO=(DECNET=1,DEC_C=1)	for DECC - socket emulation over DECnet
+!
+!	$ MMS/MACRO=(MULTINET=1,GNU_C=1)	for GNUC - MultiNet
+!	$ MMS/MACRO=(WIN_TCP=1,GNU_C=1)		for GNUC - Wollongong TCP/IP
+!	$ MMS/MACRO=(UCX=1,GNU_C=1)		for GNUC - UCX
+!	$ MMS/MACRO=(CMU_TCP=1,GNU_C=1)		for GNUC - OpenCMU TCP/IP
+!	$ MMS/MACRO=(SOCKETSHR_TCP=1,GNU_C=1)	for GNUC - SOCKETSHR/NETLIB
+!	$ MMS/MACRO=(TCPWARE=1,GNU_C=1)		for GNUC - TCPWare TCP/IP
+!	$ MMS/MACRO=(DECNET=1,GNU_C=1)	for GNUC - socket emulation over DECnet
+!
+! To compile with debug mode:
+!
+!	$ MMS/MACRO=(MULTINET=1, DEBUG=1)	for Multinet
+!
+!
+! If you are on HEP net and want to build using the really latest sources on
+! PRIAM:: then define an extra macro U=PRIAM::, e.g.
+!
+!	$ MMS/MACRO=(MULTINET=1, U=PRIAM::)	for Multinet
+!
+! This will copy the sources from PRIAM as necessary. You can also try
+!
+!	$ MMS/MACRO=(U=PRIAM::) descrip.mms
+!
+! to update this file.
+
+
+.include Version.make
+
+! debug flags
+.ifdef DEBUG
+DEBUGFLAGS = /Debug /NoOptimize
+.endif
+
+! defines valid for all compilations
+EXTRADEFINES = DEBUG, ACCESS_AUTH, VC="""$(VC)"""
+
+! DECC flags for all compilations
+.ifdef DEC_C
+DCFLAGS = /NoMember /Warning=(disable=implicitfunc)
+.endif
+
+.ifdef UCX
+TCP = UCX
+.ifdef DEC_C
+CFLAGS = /decc/Prefix=All $(DEBUGFLAGS) $(DCFLAGS) /Define=($(EXTRADEFINES), UCX)
+.else
+CFLAGS = $(DEBUGFLAGS) /Define=($(EXTRADEFINES), UCX)
+.endif
+.endif
+
+.ifdef TCPWARE
+TCP = TCPWARE
+.ifdef DEC_C
+CFLAGS = /decc/Prefix=All $(DEBUGFLAGS) $(DCFLAGS) /Define=($(EXTRADEFINES), UCX, TCPWARE)
+.else
+CFLAGS = $(DEBUGFLAGS) /Define = ($(EXTRADEFINES), UCX, TCPWARE)
+.endif
+.endif
+
+.ifdef MULTINET
+TCP = MULTINET
+.ifdef DEC_C
+CFLAGS = /decc/Prefix=ANSI $(DEBUGFLAGS) $(DCFLAGS) /Define=(_DECC_V4_SOURCE, __SOCKET_TYPEDEFS, $(EXTRADEFINES), MULTINET)
+.else
+CFLAGS = $(DEBUGFLAGS) /Define = ($(EXTRADEFINES), MULTINET)
+.endif
+.endif
+
+.ifdef WIN_TCP
+TCP = WIN_TCP
+.ifdef DEC_C
+CFLAGS = /decc/Prefix=ANSI $(DEBUGFLAGS) $(DCFLAGS) /Define=($(EXTRADEFINES), WIN_TCP)
+.else
+CFLAGS = $(DEBUGFLAGS) /Define = ($(EXTRADEFINES), WIN_TCP)
+.endif
+.endif
+
+.ifdef CMU_TCP
+TCP = CMU_TCP
+.ifdef DEC_C
+CFLAGS = /decc/Prefix=ANSI $(DEBUGFLAGS) $(DCFLAGS) /Define=($(EXTRADEFINES), CMU_TCP)
+.else
+CFLAGS = $(DEBUGFLAGS) /Define = ($(EXTRADEFINES), CMU_TCP)
+.endif
+.endif
+
+.ifdef SOCKETSHR_TCP
+TCP = SOCKETSHR_TCP
+.ifdef DEC_C
+CFLAGS = /decc/Prefix=ANSI $(DEBUGFLAGS) $(DCFLAGS) /Define=($(EXTRADEFINES), SOCKETSHR_TCP)
+.else
+CFLAGS = $(DEBUGFLAGS) /Define = ($(EXTRADEFINES), SOCKETSHR_TCP)
+.endif
+.endif
+
+.ifdef DECNET
+TCP = DECNET
+.ifdef DEC_C
+CFLAGS = /decc/Prefix=All $(DEBUGFLAGS) $(DCFLAGS) /Define=($(EXTRADEFINES), DECNET)
+.else
+CFLAGS = $(DEBUGFLAGS) /Define = ($(EXTRADEFINES), DECNET)
+.endif
+.endif
+
+.ifdef TCP
+.else
+TCP = MULTINET			! (Default to MULTINET)
+.ifdef DEC_C
+CFLAGS = /decc/Prefix=ANSI $(DEBUGFLAGS) $(DCFLAGS) /Define=(_DECC_V4_SOURCE, __SOCKET_TYPEDEFS, $(EXTRADEFINES), MULTINET)
+.else
+CFLAGS = $(DEBUGFLAGS) /Define = ($(EXTRADEFINES), MULTINET)
+.endif
+.endif
+
+.ifdef GNU_C
+CC = gcc
+.endif
+
+!HEADERS = HTUtils.h, HTStream.h, tcp.h, HText.h -
+!        HTParse.h, HTAccess.h, HTTP.h, HTFile.h, -
+!	HTBTree.h, HTTCP.h, SGML.h, -
+!	HTML.h, HTMLDTD.h, HTChunk.h, HTPlain.h, -
+!	HTWriter.h, HTFwriter.h, HTMLGen.h, -
+!	HTAtom.h, HTAnchor.h, HTStyle.h, -
+!	HTList.h, HTString.h, HTAlert.h, -
+!	HTRules.h, HTFormat.h, HTInit.h, -
+!	HTMIME.h, HTHistory.h, HTTelnet.h, -
+!	HTFinger.h, HTAABrow.h, HTAAFile.h, -
+!	HTAAProt.h, HTAAServ.h,  HTAAUtil.h, -
+!	HTAssoc.h, HTPasswd.h, HTAuth.h, HTUU.h, -
+!	HTVMSUtils.h, ufc-crypt.h, patchlevel.h
+
+MODULES = HTParse, HTAccess, HTTP, HTFile, HTBTree, HTFTP, HTTCP, HTString, -
+	SGML, HTMLDTD, HTChunk, HTPlain, HTWriter, HTFWriter, HTMLGen, -
+	HTAtom, HTAnchor, HTStyle, HTList, HTAlert, HTRules, HTFormat, -
+	HTInit, HTMIME, HTHistory, HTNews, HTGopher, HTTelnet, HTFinger, -
+	HTWSRC, HTAAUtil, HTAABrow, HTAAServ, HTAAFile, HTPasswd, HTGroup, -
+	HTACL, HTAuth, HTAAProt, HTAssoc, HTLex, HTUU, HTVMSUtils, getpass, -
+	getline, crypt, crypt_util, HTWAIS, HTVMS_WaisUI, HTVMS_WaisProt
+
+!.ifdef DECNET  ! Strip FTP, Gopher, News, WAIS
+!HEADERS = $(COMMON_HEADERS)
+!MODULES = $(COMMON_MODULES)
+!.else
+!HEADERS = $(COMMON_HEADERS), $(EXTRA_HEADERS), $(WAIS_HEADER)
+!MODULES = $(COMMON_MODULES), $(EXTRA_MODULES), $(WAIS_MODULE)
+!.endif
+
+!___________________________________________________________________
+! WWW Library
+
+!library : $(HEADERS)  wwwlib_$(TCP)($(MODULES))
+library : wwwlib_$(TCP)($(MODULES))
+ 	@ Continue
+
+build_$(TCP).com : descrip.mms
+	$(MMS) /NoAction /From_Sources /Output = Build_$(TCP).com /Macro = ($(TCP)=1)
+
+clean :
+	- Set Protection = (Owner:RWED) *.*;-1
+	- Purge /NoLog /NoConfirm
+	- Delete /NoLog /NoConfirm *.obj;,*.olb;
+
+!___________________________________________________________________
+! Simple Dependencies
+
+
+!HTString.obj :	HTString.c HTString.h tcp.h Version.make HTUtils.h
+!HTAtom.obj :	HTAtom.c HTAtom.h HTUtils.h HTString.h
+!HTChunk.obj :	HTChunk.c HTChunk.h HTUtils.h
+!HTList.obj :	HTList.c HTList.h HTUtils.h
+!HTBTree.obj :	HTBTree.c HTBTree.h HTUtils.h
+!HTMLDTD.obj :	HTMLDTD.c HTMLDTD.h SGML.h
+!HTPlain.obj :	HTPlain.c HTPlain.h HTStream.h
+!HTWriter.obj :	HTWriter.c HTWriter.h HTStream.h
+!HTFWriter.obj :	HTFWriter.c HTFWriter.h HTStream.h
+!HTMLGen.obj :	HTMLGen.c HTMLGen.h HTUtils.h HTMLDTD.h
+!HTAlert.obj :	HTAlert.c HTAlert.h HTUtils.h Version.make
+!HTRules.obj :	HTRules.c HTRules.h HTUtils.h Version.make
+!HTInit.obj :	HTInit.c HTInit.h HTUtils.h HTList.h
+!HTMIME.obj :	HTMIME.c HTMIME.h HTUtils.h HTList.h
+!HTTelnet.obj :	HTTelnet.c HTTelnet.h HTUtils.h
+!HTWAIS.obj :	HTWAIS.c HTWAIS.h HTUtils.h HTList.h
+!HTWSRC.obj :	HTWSRC.c HTWSRC.h HTUtils.h HTList.h
+!HTAccess.obj :	HTAccess.c HTAccess.h HTUtils.h
+!HTAnchor.obj :	HTAnchor.c HTAnchor.h HTUtils.h HTList.h
+!HTFile.obj :	HTFile.c HTFile.h HTUtils.h HTVMSUtils.h
+!HTFormat.obj :	HTFormat.c HTFormat.h HTUtils.h HTML.h SGML.h HTPlain.h HTMLGen.h HTList.h
+!HTFTP.obj :	HTFTP.c HTFTP.h HTUtils.h
+!HTGopher.obj :	HTGopher.c HTGopher.h HTUtils.h HTList.h
+!HTFinger.obj :	HTFinger.c HTFinger.h HTUtils.h HTList.h
+!HTHistory.obj :	HTHistory.c HTHistory.h HTUtils.h HTList.h
+!HTNews.obj :	HTNews.c HTNews.h HTUtils.h HTList.h
+!HTParse.obj :	HTParse.c HTParse.h HTUtils.h
+!HTStyle.obj :	HTStyle.c HTStyle.h HTUtils.h
+!HTTCP.obj :	HTTCP.c HTTCP.h HTUtils.h tcp.h
+!HTTP.obj :	HTTP.c HTTP.h HTUtils.h
+!SGML.obj :	SGML.c SGML.h HTUtils.h
+!HTAABrow.obj :	HTAABrow.c HTUtils.h
+!HTAAFile.obj :	HTAAFile.c HTUtils.h
+!HTAAProt.obj :	HTAAProt.c HTUtils.h
+!HTAAServ.obj :	HTAAServ.c HTUtils.h
+!HTAAUtil.obj :	HTAAUtil.c HTUtils.h
+!HTACL.obj :	HTACL.c HTUtils.h
+!HTGroup.obj :	HTGroup.c HTUtils.h
+!HTLex.obj :	HTLex.c HTUtils.h
+!HTAssoc.obj :	HTAssoc.c HTAssoc.h HTAAUtil.h HTString.h
+!HTPasswd.obj :	HTPasswd.c HTPasswd.h HTUtils.h HTAAUtil.h HTFile.h tcp.h
+!HTAuth.obj :	HTAuth.c HTAuth.h HTUtils.h HTPasswd.h HTAssoc.h HTUU.h
+!HTUU.obj :	HTUU.c HTUU.h HTUtils.h
+!crypt.obj :	crypt.c ufc-crypt.h
+!HTVMSUtils.obj :	HTVMSUtils.c HTVMSUtils.h HTUtils.h
+!crypt_util.obj :	crypt_util.c ufc-crypt.h patchlevel.h
diff --git a/WWW/Library/vms/libmake.com b/WWW/Library/vms/libmake.com
new file mode 100644
index 00000000..75cb23de
--- /dev/null
+++ b/WWW/Library/vms/libmake.com
@@ -0,0 +1,186 @@
+$ v = 'f$verify(0)'
+$!			LIBMAKE.COM
+$!
+$!   Command file to build the WWWLibrary on VMS systems.
+$!
+$!   26-Jul-1995	F.Macrides		macrides@sci.wfeb.edu
+$!	Adding support for GNUC.
+$!   03-May-1995	F.Macrides		macrides@sci.wfeb.edu
+$!	Include /nomember for compilations with DECC.  It's not the
+$!	default on AXP and the code assumes byte alignment.
+$!   07-Dec-1994	F.Macrides		macrides@sci.wfeb.edu
+$!	Updated for DECC/VAX, VAXC/VAX and DECC/AXP
+$!   03-NOV-1994	A.Harper		A.Harper@kcl.ac.uk
+$!	Mods to support SOCKETSHR/NETLIB and add a /DEBUG/NOOPT option
+$!   02-Jun-1994	F.Macrides		macrides@sci.wfeb.edu
+$!	Mods to support TCPWare (To use non-blocking connects, you need
+$!	the DRIVERS_V405B.INC patch from FTP.PROCESS.COM for TCPware for
+$!	OpenVMS Version 4.0-5, or a higher version of TCPWare, which will
+$!	have that bug in the TCPDRIVER fixed.  Otherwise, add NO_IOCTL to
+$!	the /define=(...) list.)
+$!   20-May-1994	Andy Harper		A.Harper@bay.cc.kcl.ac.uk
+$!	Added support for the CMU TCP/IP transport
+$!   13-Dec-1993	F.Macrides		macrides@sci.wfeb.edu
+$!	Mods for conditional compilations with VAXC versus DECC
+$!   10-Dec-1993	F.Macrides		macrides@sci.wfeb.edu
+$!	Initial version, for WWWLibrary v2.14 with Lynx v2.1
+$!
+$ ON CONTROL_Y THEN GOTO CLEANUP
+$ ON ERROR THEN GOTO CLEANUP
+$ agent = 0
+$ extra = ""
+$ IF P1 .EQS. ""
+$ THEN
+$ 	write sys$output "Acceptable TCP/IP agents are"
+$ 	write sys$output " [1] MultiNet (default)"
+$ 	write sys$output " [2] UCX"
+$ 	write sys$output " [3] WIN_TCP"
+$	write sys$output " [4] CMU_TCP"
+$	write sys$output " [5] SOCKETSHR_TCP"
+$	write sys$output " [6] TCPWARE"
+$ 	write sys$output " [7] DECNET"
+$ 	read sys$command/prompt="Agent [1,2,3,4,5,6,7] (RETURN = [1]) " agent
+$ ENDIF
+$ if agent .eq. 1 .or. agent .eqs. "" .or. p1 .eqs. "MULTINET" then -
+    transport = "MULTINET"
+$ if agent .eq. 2 .or. p1 .eqs. "UCX" then transport = "UCX"
+$ if agent .eq. 3 .or. p1 .eqs. "WIN_TCP" then transport = "WIN_TCP"
+$ if agent .eq. 4 .or. p1 .eqs. "CMU_TCP" then transport = "CMU_TCP"
+$ if agent .eq. 5 .or. p1 .eqs. "SOCKETSHR_TCP" then transport = "SOCKETSHR_TCP"
+$ if agent .eq. 6 .or. p1 .eqs. "TCPWARE" then transport = "TCPWARE"
+$ if agent .eq. 7 .or. p1 .eqs. "DECNET" then transport = "DECNET"
+$!
+$ if transport .eqs. "TCPWARE" then extra = ",UCX"
+$!
+$ cc_opts = ""
+$ if p2 .nes. "" then cc_opts = "/DEBUG/NOOPT"
+$!
+$ IF f$trnlnm("VAXCMSG") .eqs. "DECC$MSG" .or. -
+     f$trnlnm("DECC$CC_DEFAULT") .eqs. "/DECC" .or. -
+     f$trnlnm("DECC$CC_DEFAULT") .eqs. "/VAXC"
+$ THEN
+$  v1 = f$verify(1)
+$! DECC:
+$  v1 = 'f$verify(0)'
+$  If transport .eqs. "UCX" .or. transport .eqs. "TCPWARE"
+$  Then
+$  v1 = f$verify(1)
+$!
+$ cc/decc/prefix=all /nomember 'cc_opts'-
+    /warning=(disable=implicitfunc)-
+    /DEFINE=(DEBUG,ACCESS_AUTH,'transport''extra',VC="""2.14""")-
+    /INCLUDE=([-.Implementation]) -
+    [-.Implementation]HTString.c
+$!
+$ cc := cc/decc/prefix=all /nomember 'cc_opts'-
+	  /warning=(disable=implicitfunc)-
+	  /DEFINE=(DEBUG,ACCESS_AUTH,'transport''extra')-
+	  /INCLUDE=([-.Implementation])
+$!
+$  v1 = 'f$verify(0)'
+$  Else
+$  if transport .eqs. "MULTINET" then -
+	extra = ",_DECC_V4_SOURCE,__SOCKET_TYPEDEFS"
+$  v1 = f$verify(1)
+$!
+$ cc/decc/prefix=ansi /nomember 'cc_opts'-
+    /warning=(disable=implicitfunc)-
+    /DEFINE=(DEBUG,ACCESS_AUTH,'transport''extra',VC="""2.14""")-
+    /INCLUDE=([-.Implementation]) -
+    [-.Implementation]HTString.c
+$!
+$ cc := cc/decc/prefix=ansi /nomember 'cc_opts'-
+	  /warning=(disable=implicitfunc)-
+	  /DEFINE=(DEBUG,ACCESS_AUTH,'transport''extra')-
+	  /INCLUDE=([-.Implementation])
+$!
+$  v1 = 'f$verify(0)'
+$  EndIf
+$ ELSE
+$  IF f$search("gnu_cc:[000000]gcclib.olb") .nes. ""
+$  THEN
+$   v1 = f$verify(1)
+$! GNUC:
+$!
+$   gcc/DEFINE=(DEBUG,ACCESS_AUTH,'transport''extra',VC="""2.14""") 'cc_opts'-
+       /INCLUDE=([-.Implementation]) -
+       [-.Implementation]HTString.c
+$!
+$   cc := gcc/DEFINE=(DEBUG,ACCESS_AUTH,'transport''extra') 'cc_opts'-
+	     /INCLUDE=([-.Implementation])
+$!
+$   v1 = 'f$verify(0)'
+$  ELSE
+$   v1 = f$verify(1)
+$! VAXC:
+$!
+$   cc/DEFINE=(DEBUG,ACCESS_AUTH,'transport''extra',VC="""2.14""") 'cc_opts'-
+      /INCLUDE=([-.Implementation]) -
+      [-.Implementation]HTString.c
+$!
+$   cc := cc/DEFINE=(DEBUG,ACCESS_AUTH,'transport''extra') 'cc_opts'-
+	    /INCLUDE=([-.Implementation])
+$!
+$   v1 = 'f$verify(0)'
+$  ENDIF
+$ ENDIF
+$ v1 = f$verify(1)
+$ cc [-.Implementation]HTParse.c
+$ cc [-.Implementation]HTAccess.c
+$ cc [-.Implementation]HTTP.c
+$ cc [-.Implementation]HTFile.c
+$ cc [-.Implementation]HTBTree.c
+$ cc [-.Implementation]HTFTP.c
+$ cc [-.Implementation]HTTCP.c
+$ cc [-.Implementation]SGML.c
+$ cc [-.Implementation]HTMLDTD.c
+$ cc [-.Implementation]HTChunk.c
+$ cc [-.Implementation]HTPlain.c
+$ cc [-.Implementation]HTWriter.c
+$ cc [-.Implementation]HTFWriter.c
+$ cc [-.Implementation]HTMLGen.c
+$ cc [-.Implementation]HTAtom.c
+$ cc [-.Implementation]HTAnchor.c
+$ cc [-.Implementation]HTStyle.c
+$ cc [-.Implementation]HTList.c
+$ cc [-.Implementation]HTAlert.c
+$ cc [-.Implementation]HTRules.c
+$ cc [-.Implementation]HTFormat.c
+$ cc [-.Implementation]HTInit.c
+$ cc [-.Implementation]HTMIME.c
+$ cc [-.Implementation]HTHistory.c
+$ cc [-.Implementation]HTNews.c
+$ cc [-.Implementation]HTGopher.c
+$ cc [-.Implementation]HTTelnet.c
+$ cc [-.Implementation]HTFinger.c
+$ cc [-.Implementation]HTWSRC.c
+$ cc [-.Implementation]HTAAUtil.c
+$ cc [-.Implementation]HTAABrow.c
+$ cc [-.Implementation]HTAAServ.c
+$ cc [-.Implementation]HTAAFile.c
+$ cc [-.Implementation]HTPasswd.c
+$ cc [-.Implementation]HTGroup.c
+$ cc [-.Implementation]HTACL.c
+$ cc [-.Implementation]HTAuth.c
+$ cc [-.Implementation]HTAAProt.c
+$ cc [-.Implementation]HTAssoc.c
+$ cc [-.Implementation]HTLex.c
+$ cc [-.Implementation]HTUU.c
+$ cc [-.Implementation]HTVMSUtils.c
+$ cc [-.Implementation]getpass.c
+$ cc [-.Implementation]getline.c
+$ cc [-.Implementation]crypt.c
+$ cc [-.Implementation]crypt_util.c
+$ cc [-.Implementation]HTWAIS.c
+$ cc [-.Implementation]HTVMS_WaisUI.c
+$ cc [-.Implementation]HTVMS_WaisProt.c
+$!    
+$ If f$search("[-.Implementation]WWWLib_''transport'.olb") .eqs. "" Then -
+    LIBRARY/Create [-.Implementation]WWWLib_'transport'.olb
+$ LIBRARY/Replace [-.Implementation]WWWLib_'transport'.olb *.obj
+$ Delete/nolog/noconf *.obj;*
+$!
+$ v1 = 'f$verify(0)'
+$ CLEANUP:
+$    v1 = f$verify(v)
+$exit
diff --git a/WWW/Makefile b/WWW/Makefile
new file mode 100644
index 00000000..41a90056
--- /dev/null
+++ b/WWW/Makefile
@@ -0,0 +1,9 @@
+#	Make basic WWW distribution
+#
+#  See the README and the documentation on the web.
+#  When you have done BUILD you will have www so you will be able to
+#  read the documentation online.
+#
+all :
+	BUILD
+
diff --git a/WWW/README.txt b/WWW/README.txt
new file mode 100644
index 00000000..ad5d8bee
--- /dev/null
+++ b/WWW/README.txt
@@ -0,0 +1,208 @@
+                                                                        Read Me
+                      WORLDWIDEWEB CERN-DISTRIBUTED CODE
+                                       
+   See the CERN copyright[1] .  This is the README file which you get when you
+   unwrap one of our tar files. These files contain information about
+   hypertext, hypertext systems, and the WorldWideWeb project. If you have
+   taken this with a .tar file, you will have only a subset of the files.
+   
+   THIS FILE IS A VERY ABRIDGED VERSION OF THE INFORMATION AVAILABLE ON THE
+   WEB.   IF IN DOUBT, READ THE WEB DIRECTLY. If you have not got ANY browser
+   installed yet, do this by telnet to info.cern.ch (no username or password).
+   
+   Files from info.cern.ch are also mirrored on ftp.ripe.net.
+   
+Archive Directory structure
+
+   Under /pub/www[2] , besides this README file, you'll find bin[3] , src[4]
+   and doc[5] directories.  The main archives are as follows:
+   
+  bin/xxx/bbbb            Executable binaries of program bbbb for system xxx.
+                         Check what's there before you bother compiling. (Note
+                         HP700/8800 series is "snake")
+                         
+  bin/next/WorldWideWeb_v.vv.tar.Z
+                         The Hypertext Browser/editor for the NeXT -- binary.
+                         
+  src/WWWLibrary_v.vv.tar.Z
+                          The W3 Library. All source, and Makefiles for
+                         selected systems.
+                         
+  src/WWWLineMode_v.vv.tar.Z
+                          The Line mode browser - all source, and Makefiles for
+                         selected systems. Requires the Library[6] .
+                         
+  src/WWWDaemon_v.vv.tar.Z
+                          The HTTP daemon, and WWW-WAIS  gateway programs.
+                         Source.  Requires the Library.
+                         
+  src/WWWMailRobot_v.vv.tar.Z
+                          The Mail Robot.
+                         
+  doc/WWWBook.tar.Z       A snapshot of our internal documentation - we prefer
+                         you to access this on line -- see warnings below.
+                         
+Basic WWW software installation from source
+
+   This applies to the line mode client and the server.  Below, $prod means
+   LineMode or Daemon depending on which you are building.
+   
+  GENERATED DIRECTORY STRUCTURE
+  
+   The tar files are all designed to be unwrapped in the same (this) directory.
+   They create different parts of a common directory tree under that directory.
+   There may be some duplication. They also generate a few files in this
+   directory: README.*, Copyright.*, and some installation instructions (.txt).
+   
+   The directory structure is, for product $prod  and machine $WWW_MACH
+   
+  WWW/$prod/Implementation
+                          Source files for a given product
+                         
+  WWW/$prod/Implementation/CommonMakefile
+                         The machine-independent parts of the Makefile for this
+                         product
+                         
+
+                                                                Read Me (65/66)
+  WWW/$prod/$WWW_MACH/    Area for compiling for a given system
+                         
+  WWW/All/$WWW_MACH/Makefile.include
+                         The machine-dependent parts of the makefile for any
+                         product
+                         
+  WWW/All/Implementation/Makefile.product
+                         A makefile which includes both parts above and so can
+                         be used from any product, any machine.
+                         
+  COMPILATION ON ALREADY SUPPORTED PLATFORMS
+  
+   You must get the WWWLibrary tar file as well as the products you want and
+   unwrap them all from the same directory.
+   
+   You must define the environmant variable WWW_MACH to be the architecure of
+   your machine (sun4, decstation, rs6000, sgi, snake, etc)
+   
+   In directory WWW, type BUILD.
+   
+  COMPILATION ON NEW PLATFORMS
+  
+   If your machine is not on the list:
+   
+      Make up a new subdirectory of that name under WWW/$prod and WWW/All,
+      copying the contents of a basically similar architecture's directory.
+      
+      Check the  WWW/All/$WWW_MACH/Makefile.include for suitable directory and
+      flag definitions.
+      
+      Check the file tcp.h for the system-specific include file coordinates,
+      etc.
+      
+      Send any changes you have to make back to www-request@info.cern.ch for
+      inclusion into future releases.
+      
+      Once you have this set up, type BUILD.
+      
+NeXTStep Browser/Editor
+
+   The browser for the NeXT is those files contained in the application
+   directory WWW/Next/Implementation/WorldWideWeb.app and is compiled. When you
+   install the app, you may want to configure the default page,
+   WorldWideWeb.app/default.html. These must point to some useful information!
+   You should keep it up to date with pointers to info on your site and
+   elsewhere. If you use the CERN home page note there is a link at the bottom
+   to the master copy on our server.   You should set up the address of your
+   local news server with
+   
+                      dwrite WorldWideWeb NewsHost  news
+
+   replacing the last word with the actual address of your news host. See
+   Installation instructions[7] .
+   
+Line Mode browser
+
+   Binaries of this for some systems are available in /pub/www/bin/ . The
+   binaries can be picked up, set executable, and run immediately.
+   
+   If there is no binary, see "Installation from source" above.
+   
+    (See Installation notes[8] ).  Do the same thing (in the same directory) to
+   the WWWLibrary_v.cc.tar.Z file to get the common library.
+   
+
+                                                               Read Me (65/130)
+   You will have an ASCII printable manual in the file
+   WWW/LineMode/Defaults/line-mode-guide.txt which you can print out at this
+   stage. This is a frozen copy of some of the online documentation.
+   
+   Whe you install the browser, you may configure a default page. This is
+   /usr/local/lib/WWW/default.html for the line mode browser. This must point
+   to some useful information! You should keep it up to date with pointers to
+   info on your site and elsewhere. If you use the CERN home page note there is
+   a link at the bottom to the master copy on our server.
+   
+   Some basic documentation on the browser is delivered with the home page in
+   the directory WWW/LineMode/Defaults. A separate tar file of that directory
+   (WWWLineModeDefaults.tar.Z) is available if you just want to update that.
+   
+   The rest of the documentation is in hypertext, and so wil be readable most
+   easily with a browser. We suggest that after installing the browser, you
+   browse through the basic documentation so that you are aware of the options
+   and customisation possibilities for example.
+   
+Server
+
+   The server can be run very simply under the internet  daemon, to export a
+   file directory tree as a browsable hypertext tree.  Binaries are avilable
+   for some platofrms, otherwise follow instructions above for compiling and
+   then go on to " Installing the basic W3 server[9] ".
+   
+XMosaic
+
+   XMosaic is an X11/Motif  W3 browser.
+   
+   The sources and binaries are distributed separately from
+   FTP.NCSA.UIUC.EDU[10] , in  /Web/xmosaic[11] .  Binaries are available for
+   some platforms.  If you have to build from source, check the README in the
+   distribution.
+   
+   The binaries can be picked up, uncompressed, set "executable" and run
+   immediately.
+   
+Viola browser for X11
+
+   Viola is an X11 application for reading global hypertext.  If a binary is
+   available from your machine, in /pub/www/bin/.../viola*, then take that and
+   also the Viola "apps" tar file which contains the scripts you will need.
+   
+   To generate this from source, you will need both the W3 library and the
+   Viola source files.  There is an Imakefile with the viola source directory.
+   You will need to generate the XPA and XPM libraries and the W3 library
+   befere you make viola itself.
+   
+Documentation
+
+   In the /pub/www/doc[12] directory are a number articles, preprints and
+   guides on the web.
+   
+   See the online WWW bibliography[13] for a list of these and other articles,
+   books, etc. and also the list of WWW Manuals[14] available in text and
+   postscript form.
+   
+General
+
+   Your comments will of course be most appreciated, on code, or information on
+   the web which is out of date or misleading. If you write your own hypertext
+   and make it available by anonymous ftp or using a server, tell us and we'll
+   put some pointers to it in ours. Thus spreads the web...
+
+                                                               Read Me (66/195)
+                                                                Tim Berners-Lee
+                                                                               
+                                                           WorldWideWeb project
+                                                                               
+                                              CERN, 1211 Geneva 23, Switzerland
+                                                                               
+          Tel: +41 22 767 3755; Fax: +41 22 767 7155; email: timbl@info.cern.ch
+                                                                               
+