From e087f6d44e87f489fcb3056e86319ebba4218156 Mon Sep 17 00:00:00 2001 From: "Thomas E. Dickey" Date: Mon, 2 Sep 1996 19:39:24 -0400 Subject: snapshot of project "lynx", label v2_6 --- WWW/BUILD | 42 + WWW/Copyright.txt | 22 + WWW/Library/Implementation/BSDI_Makefile | 413 ++++ WWW/Library/Implementation/CommonMakefile | 376 ++++ WWW/Library/Implementation/HTAABrow.c | 1051 +++++++++ WWW/Library/Implementation/HTAABrow.h | 138 ++ WWW/Library/Implementation/HTAAFile.c | 210 ++ WWW/Library/Implementation/HTAAFile.h | 126 ++ WWW/Library/Implementation/HTAAProt.c | 592 +++++ WWW/Library/Implementation/HTAAProt.h | 231 ++ WWW/Library/Implementation/HTAAServ.c | 686 ++++++ WWW/Library/Implementation/HTAAServ.h | 146 ++ WWW/Library/Implementation/HTAAUtil.c | 640 ++++++ WWW/Library/Implementation/HTAAUtil.h | 361 +++ WWW/Library/Implementation/HTACL.c | 221 ++ WWW/Library/Implementation/HTACL.h | 110 + WWW/Library/Implementation/HTAccess.c | 1199 ++++++++++ WWW/Library/Implementation/HTAccess.h | 306 +++ WWW/Library/Implementation/HTAlert.c | 125 ++ WWW/Library/Implementation/HTAlert.h | 86 + WWW/Library/Implementation/HTAnchor.c | 999 +++++++++ WWW/Library/Implementation/HTAnchor.h | 358 +++ WWW/Library/Implementation/HTAssoc.c | 87 + WWW/Library/Implementation/HTAssoc.h | 44 + WWW/Library/Implementation/HTAtom.c | 169 ++ WWW/Library/Implementation/HTAtom.h | 49 + WWW/Library/Implementation/HTAuth.c | 210 ++ WWW/Library/Implementation/HTAuth.h | 66 + WWW/Library/Implementation/HTBTree.c | 720 ++++++ WWW/Library/Implementation/HTBTree.h | 104 + WWW/Library/Implementation/HTCJK.h | 110 + WWW/Library/Implementation/HTChunk.c | 100 + WWW/Library/Implementation/HTChunk.h | 160 ++ WWW/Library/Implementation/HTFTP.c | 3214 +++++++++++++++++++++++++++ WWW/Library/Implementation/HTFTP.h | 72 + WWW/Library/Implementation/HTFWriter.c | 358 +++ WWW/Library/Implementation/HTFWriter.h | 37 + WWW/Library/Implementation/HTFile.c | 1871 ++++++++++++++++ WWW/Library/Implementation/HTFile.h | 173 ++ WWW/Library/Implementation/HTFinger.c | 438 ++++ WWW/Library/Implementation/HTFinger.h | 24 + WWW/Library/Implementation/HTFormat.c | 836 +++++++ WWW/Library/Implementation/HTFormat.h | 394 ++++ WWW/Library/Implementation/HTGopher.c | 2008 +++++++++++++++++ WWW/Library/Implementation/HTGopher.h | 27 + WWW/Library/Implementation/HTGroup.c | 772 +++++++ WWW/Library/Implementation/HTGroup.h | 189 ++ WWW/Library/Implementation/HTHistory.c | 157 ++ WWW/Library/Implementation/HTHistory.h | 112 + WWW/Library/Implementation/HTInit.c | 176 ++ WWW/Library/Implementation/HTInit.h | 22 + WWW/Library/Implementation/HTLex.c | 142 ++ WWW/Library/Implementation/HTLex.h | 64 + WWW/Library/Implementation/HTList.c | 262 +++ WWW/Library/Implementation/HTList.h | 136 ++ WWW/Library/Implementation/HTMIME.c | 2009 +++++++++++++++++ WWW/Library/Implementation/HTMIME.h | 77 + WWW/Library/Implementation/HTML.h | 79 + WWW/Library/Implementation/HTMLDTD.c | 1122 ++++++++++ WWW/Library/Implementation/HTMLDTD.h | 890 ++++++++ WWW/Library/Implementation/HTMLGen.c | 367 +++ WWW/Library/Implementation/HTMLGen.h | 32 + WWW/Library/Implementation/HTNews.c | 1646 ++++++++++++++ WWW/Library/Implementation/HTNews.h | 35 + WWW/Library/Implementation/HTParse.c | 606 +++++ WWW/Library/Implementation/HTParse.h | 159 ++ WWW/Library/Implementation/HTPasswd.c | 301 +++ WWW/Library/Implementation/HTPasswd.h | 129 ++ WWW/Library/Implementation/HTPlain.c | 311 +++ WWW/Library/Implementation/HTPlain.h | 21 + WWW/Library/Implementation/HTRules.c | 418 ++++ WWW/Library/Implementation/HTRules.h | 165 ++ WWW/Library/Implementation/HTStream.h | 61 + WWW/Library/Implementation/HTString.c | 156 ++ WWW/Library/Implementation/HTString.h | 51 + WWW/Library/Implementation/HTStyle.c | 363 +++ WWW/Library/Implementation/HTStyle.h | 183 ++ WWW/Library/Implementation/HTTCP.c | 955 ++++++++ WWW/Library/Implementation/HTTCP.h | 118 + WWW/Library/Implementation/HTTP.c | 1309 +++++++++++ WWW/Library/Implementation/HTTP.h | 28 + WWW/Library/Implementation/HTTelnet.c | 579 +++++ WWW/Library/Implementation/HTTelnet.h | 24 + WWW/Library/Implementation/HTUU.c | 207 ++ WWW/Library/Implementation/HTUU.h | 29 + WWW/Library/Implementation/HTUtils.h | 311 +++ WWW/Library/Implementation/HTVMSUtils.c | 1220 ++++++++++ WWW/Library/Implementation/HTVMSUtils.h | 116 + WWW/Library/Implementation/HTVMS_WaisProt.c | 2501 +++++++++++++++++++++ WWW/Library/Implementation/HTVMS_WaisProt.h | 376 ++++ WWW/Library/Implementation/HTVMS_WaisUI.c | 2448 ++++++++++++++++++++ WWW/Library/Implementation/HTVMS_WaisUI.h | 674 ++++++ WWW/Library/Implementation/HTWAIS.c | 1103 +++++++++ WWW/Library/Implementation/HTWAIS.h | 44 + WWW/Library/Implementation/HTWSRC.c | 478 ++++ WWW/Library/Implementation/HTWSRC.h | 46 + WWW/Library/Implementation/HTWriter.c | 189 ++ WWW/Library/Implementation/HTWriter.h | 28 + WWW/Library/Implementation/HText.h | 262 +++ WWW/Library/Implementation/LYLeaks.h | 149 ++ WWW/Library/Implementation/LYexit.h | 54 + WWW/Library/Implementation/Makefile | 489 ++++ WWW/Library/Implementation/SGML.c | 2145 ++++++++++++++++++ WWW/Library/Implementation/SGML.h | 202 ++ WWW/Library/Implementation/Version.make | 1 + WWW/Library/Implementation/crypt.c | 129 ++ WWW/Library/Implementation/crypt_util.c | 981 ++++++++ WWW/Library/Implementation/getline.c | 74 + WWW/Library/Implementation/getpass.c | 64 + WWW/Library/Implementation/patchlevel.h | 24 + WWW/Library/Implementation/tcp.h | 524 +++++ WWW/Library/Implementation/ufc-crypt.h | 108 + WWW/Library/apollo_m68k/Makefile | 38 + WWW/Library/clix/Makefile | 30 + WWW/Library/convex/Makefile | 32 + WWW/Library/decstation/Makefile | 23 + WWW/Library/duns/Makefile | 489 ++++ WWW/Library/freebsd/Makefile | 27 + WWW/Library/isc/Makefile | 30 + WWW/Library/mips/Makefile | 29 + WWW/Library/netbsd/Makefile | 29 + WWW/Library/next/Makefile | 40 + WWW/Library/osf/Makefile | 23 + WWW/Library/ptx/Makefile | 29 + WWW/Library/rs6000/Makefile | 29 + WWW/Library/sco/Makefile | 33 + WWW/Library/sgi/Makefile | 30 + WWW/Library/snake/Makefile | 33 + WWW/Library/solaris2/Makefile | 29 + WWW/Library/sun3/Makefile | 29 + WWW/Library/sun4/Makefile | 29 + WWW/Library/svr4/Makefile | 29 + WWW/Library/unix/Makefile | 30 + WWW/Library/unix_x/Makefile | 491 ++++ WWW/Library/vax_ultrix/Makefile | 33 + WWW/Library/vms/COPYING.LIB | 481 ++++ WWW/Library/vms/descrip.mms | 258 +++ WWW/Library/vms/libmake.com | 186 ++ WWW/Makefile | 9 + WWW/README.txt | 208 ++ 140 files changed, 51667 insertions(+) create mode 100644 WWW/BUILD create mode 100644 WWW/Copyright.txt create mode 100644 WWW/Library/Implementation/BSDI_Makefile create mode 100644 WWW/Library/Implementation/CommonMakefile create mode 100644 WWW/Library/Implementation/HTAABrow.c create mode 100644 WWW/Library/Implementation/HTAABrow.h create mode 100644 WWW/Library/Implementation/HTAAFile.c create mode 100644 WWW/Library/Implementation/HTAAFile.h create mode 100644 WWW/Library/Implementation/HTAAProt.c create mode 100644 WWW/Library/Implementation/HTAAProt.h create mode 100644 WWW/Library/Implementation/HTAAServ.c create mode 100644 WWW/Library/Implementation/HTAAServ.h create mode 100644 WWW/Library/Implementation/HTAAUtil.c create mode 100644 WWW/Library/Implementation/HTAAUtil.h create mode 100644 WWW/Library/Implementation/HTACL.c create mode 100644 WWW/Library/Implementation/HTACL.h create mode 100644 WWW/Library/Implementation/HTAccess.c create mode 100644 WWW/Library/Implementation/HTAccess.h create mode 100644 WWW/Library/Implementation/HTAlert.c create mode 100644 WWW/Library/Implementation/HTAlert.h create mode 100644 WWW/Library/Implementation/HTAnchor.c create mode 100644 WWW/Library/Implementation/HTAnchor.h create mode 100644 WWW/Library/Implementation/HTAssoc.c create mode 100644 WWW/Library/Implementation/HTAssoc.h create mode 100644 WWW/Library/Implementation/HTAtom.c create mode 100644 WWW/Library/Implementation/HTAtom.h create mode 100644 WWW/Library/Implementation/HTAuth.c create mode 100644 WWW/Library/Implementation/HTAuth.h create mode 100644 WWW/Library/Implementation/HTBTree.c create mode 100644 WWW/Library/Implementation/HTBTree.h create mode 100644 WWW/Library/Implementation/HTCJK.h create mode 100644 WWW/Library/Implementation/HTChunk.c create mode 100644 WWW/Library/Implementation/HTChunk.h create mode 100644 WWW/Library/Implementation/HTFTP.c create mode 100644 WWW/Library/Implementation/HTFTP.h create mode 100644 WWW/Library/Implementation/HTFWriter.c create mode 100644 WWW/Library/Implementation/HTFWriter.h create mode 100644 WWW/Library/Implementation/HTFile.c create mode 100644 WWW/Library/Implementation/HTFile.h create mode 100644 WWW/Library/Implementation/HTFinger.c create mode 100644 WWW/Library/Implementation/HTFinger.h create mode 100644 WWW/Library/Implementation/HTFormat.c create mode 100644 WWW/Library/Implementation/HTFormat.h create mode 100644 WWW/Library/Implementation/HTGopher.c create mode 100644 WWW/Library/Implementation/HTGopher.h create mode 100644 WWW/Library/Implementation/HTGroup.c create mode 100644 WWW/Library/Implementation/HTGroup.h create mode 100644 WWW/Library/Implementation/HTHistory.c create mode 100644 WWW/Library/Implementation/HTHistory.h create mode 100644 WWW/Library/Implementation/HTInit.c create mode 100644 WWW/Library/Implementation/HTInit.h create mode 100644 WWW/Library/Implementation/HTLex.c create mode 100644 WWW/Library/Implementation/HTLex.h create mode 100644 WWW/Library/Implementation/HTList.c create mode 100644 WWW/Library/Implementation/HTList.h create mode 100644 WWW/Library/Implementation/HTMIME.c create mode 100644 WWW/Library/Implementation/HTMIME.h create mode 100644 WWW/Library/Implementation/HTML.h create mode 100644 WWW/Library/Implementation/HTMLDTD.c create mode 100644 WWW/Library/Implementation/HTMLDTD.h create mode 100644 WWW/Library/Implementation/HTMLGen.c create mode 100644 WWW/Library/Implementation/HTMLGen.h create mode 100644 WWW/Library/Implementation/HTNews.c create mode 100644 WWW/Library/Implementation/HTNews.h create mode 100644 WWW/Library/Implementation/HTParse.c create mode 100644 WWW/Library/Implementation/HTParse.h create mode 100644 WWW/Library/Implementation/HTPasswd.c create mode 100644 WWW/Library/Implementation/HTPasswd.h create mode 100644 WWW/Library/Implementation/HTPlain.c create mode 100644 WWW/Library/Implementation/HTPlain.h create mode 100644 WWW/Library/Implementation/HTRules.c create mode 100644 WWW/Library/Implementation/HTRules.h create mode 100644 WWW/Library/Implementation/HTStream.h create mode 100644 WWW/Library/Implementation/HTString.c create mode 100644 WWW/Library/Implementation/HTString.h create mode 100644 WWW/Library/Implementation/HTStyle.c create mode 100644 WWW/Library/Implementation/HTStyle.h create mode 100644 WWW/Library/Implementation/HTTCP.c create mode 100644 WWW/Library/Implementation/HTTCP.h create mode 100644 WWW/Library/Implementation/HTTP.c create mode 100644 WWW/Library/Implementation/HTTP.h create mode 100644 WWW/Library/Implementation/HTTelnet.c create mode 100644 WWW/Library/Implementation/HTTelnet.h create mode 100644 WWW/Library/Implementation/HTUU.c create mode 100644 WWW/Library/Implementation/HTUU.h create mode 100644 WWW/Library/Implementation/HTUtils.h create mode 100644 WWW/Library/Implementation/HTVMSUtils.c create mode 100644 WWW/Library/Implementation/HTVMSUtils.h create mode 100644 WWW/Library/Implementation/HTVMS_WaisProt.c create mode 100644 WWW/Library/Implementation/HTVMS_WaisProt.h create mode 100644 WWW/Library/Implementation/HTVMS_WaisUI.c create mode 100644 WWW/Library/Implementation/HTVMS_WaisUI.h create mode 100644 WWW/Library/Implementation/HTWAIS.c create mode 100644 WWW/Library/Implementation/HTWAIS.h create mode 100644 WWW/Library/Implementation/HTWSRC.c create mode 100644 WWW/Library/Implementation/HTWSRC.h create mode 100644 WWW/Library/Implementation/HTWriter.c create mode 100644 WWW/Library/Implementation/HTWriter.h create mode 100644 WWW/Library/Implementation/HText.h create mode 100644 WWW/Library/Implementation/LYLeaks.h create mode 100644 WWW/Library/Implementation/LYexit.h create mode 100644 WWW/Library/Implementation/Makefile create mode 100644 WWW/Library/Implementation/SGML.c create mode 100644 WWW/Library/Implementation/SGML.h create mode 100644 WWW/Library/Implementation/Version.make create mode 100644 WWW/Library/Implementation/crypt.c create mode 100644 WWW/Library/Implementation/crypt_util.c create mode 100644 WWW/Library/Implementation/getline.c create mode 100644 WWW/Library/Implementation/getpass.c create mode 100644 WWW/Library/Implementation/patchlevel.h create mode 100644 WWW/Library/Implementation/tcp.h create mode 100644 WWW/Library/Implementation/ufc-crypt.h create mode 100644 WWW/Library/apollo_m68k/Makefile create mode 100644 WWW/Library/clix/Makefile create mode 100644 WWW/Library/convex/Makefile create mode 100644 WWW/Library/decstation/Makefile create mode 100644 WWW/Library/duns/Makefile create mode 100644 WWW/Library/freebsd/Makefile create mode 100644 WWW/Library/isc/Makefile create mode 100644 WWW/Library/mips/Makefile create mode 100644 WWW/Library/netbsd/Makefile create mode 100644 WWW/Library/next/Makefile create mode 100644 WWW/Library/osf/Makefile create mode 100644 WWW/Library/ptx/Makefile create mode 100644 WWW/Library/rs6000/Makefile create mode 100644 WWW/Library/sco/Makefile create mode 100644 WWW/Library/sgi/Makefile create mode 100644 WWW/Library/snake/Makefile create mode 100644 WWW/Library/solaris2/Makefile create mode 100644 WWW/Library/sun3/Makefile create mode 100644 WWW/Library/sun4/Makefile create mode 100644 WWW/Library/svr4/Makefile create mode 100644 WWW/Library/unix/Makefile create mode 100644 WWW/Library/unix_x/Makefile create mode 100644 WWW/Library/vax_ultrix/Makefile create mode 100644 WWW/Library/vms/COPYING.LIB create mode 100644 WWW/Library/vms/descrip.mms create mode 100644 WWW/Library/vms/libmake.com create mode 100644 WWW/Makefile create mode 100644 WWW/README.txt (limited to 'WWW') 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 /* 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 included by HTUtils.h -- FM *//* FILE */ +#include +#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 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 +#ifndef VMS +#include /* Unix password file routine: getpwnam() */ +#include /* 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 included by HTUtils.h -- FM *//* FILE */ +#include /* 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 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 +#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 included by HTUtils.h -- FM *//* FILE */ +#include + +#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 +** 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 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; iname, 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=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 /* 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 +#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 + +#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 included by HTUtils.h -- FM *//* joe@athena, TBL 921019 */ +#include + +#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 +#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 +#endif +#include + +#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 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 +** DD Denis DeLaRoca 310 825-4580 +** LM Lou Montulli +** FM Foteos Macrides +** 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 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 +#endif /* VMS */ + +#ifndef VMS +#ifdef LONG_LIST +#include +#include +#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 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; iisa; /* 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; irep == 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; irep == 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 + +#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

+ */ + + /* 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
 text
+			     * It might look better with the name as the
+			     * header and the rest as a 
    with
  • 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
    with the first line as the
    and + * the rest as some form of
    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[] = { + "\nCSO/PH Query Form for $(HOST)\n\n", + "

    CSO/PH Query Form for $(HOST)

    ", + "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.", + "
    ", + "[ | ", + " ]", + "

    ", + "
    Search parameters (* indicates indexed field):", + "
    ", "$(NAMEFLD)
    \n
    $(FDESC)$(FNDX)", + "
    Last: ", + "
    First: ", + "$(QFIELDS)
    $(FDESC)$(FNDX)", + "
    \n$(NEXTFLD)", + "
    ", + "
    \n

    ", + "
    Output format:", + "
    Returned data option:
    ", + "$(RFIELDS) $(FDESC)
    ", + "$(NEXTFLD) ", + "

    \n\n", + (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, "
    \n"); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + } + if (ndx == 0) { + strcpy(buf, + "
    Information/status
    \n"); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + } else { + sprintf(buf, + "
    Entry %d:
    \n", ndx); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + } + prev_ndx = ndx; + } + } else { + sprintf(buf, "
    %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, + "
    %s
    %s\n", + fname, fvalue, fvalue); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + } else { + sprintf(buf, "
    %s
    ", fname); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + i = 0; + buf[i] = '\0'; + l = fvalue; + while (*l) { + if (*l == '<') { + strcat(buf, "<"); + l++; + i += 4; + buf[i] = '\0'; + } else if (*l == '>') { + strcat(buf, ">"); + 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, ")\"")); + strcat(buf, "\">"); + i = strlen(buf); + while (*l && !strchr(" \r\n\t,>)\"", *l)) { + buf[i++] = *l++; + } + buf[i] = '\0'; + strcat(buf, ""); + i += 4; + FREE(href); + } + } + strcat(buf, "\n"); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + } + } else { + sprintf(buf, "
    "); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + i = 0; + buf[i] = '\0'; + l = fvalue; + while (*l) { + if (*l == '<') { + strcat(buf, "<"); + l++; + i += 4; + buf[i] = '\0'; + } else if (*l == '>') { + strcat(buf, ">"); + 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, ")\"")); + strcat(buf, "\">"); + i = strlen(buf); + while (*l && !strchr(" \r\n\t,>)\"", *l)) { + buf[i++] = *l++; + } + buf[i] = '\0'; + strcat(buf, ""); + i += 4; + FREE(href); + } + } + strcat(buf, "\n"); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + } + } else { + sprintf(buf, "
    %s\n", fname ? fname : rcode ); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + } + } + } +end_CSOreport: + if (prev_ndx != -100) { + sprintf(buf, "
    \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, + "\n\nCSO/PH Results on %s\n\n\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
    \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, + "Error: At least one indexed field value must be specified!\n"); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + strcpy(buf, "\n\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, "

    \nCSO/PH command: "); + (*Target->isa->put_block)(Target, buf, strlen(buf)); + (*Target->isa->put_block)(Target, command, clen); + strcpy(buf, "

    \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, "\n\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 +#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 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 */ /* Included via previous headers. - FM */ +/* #include */ /* Included via previous headers. - FM */ + +/* + * MIME decoding routines + * + * Written by S. Ichikawa, + * partially inspired by encdec.c of . + */ +#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; iisa->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; iisa->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__. + 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 +#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; ppreformatted; + 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, "'); + 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, "\n\n
    \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 
    +
    +#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  "
    +**  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  "
    +**  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)\""));
    +				    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
    +    **  			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 
    +#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  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 
    +#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; istyles; 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 
    +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 
    +#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 
    +#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 
    +#include 
    +#include 
    +
    +
    +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 
    +#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. 
    +       *
    +       * 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,"Bad File Request",31) ||
    +       0==strncmp(line_buffer,"Address should begin with",25) ||
    +       0==strncmp(line_buffer,"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, raw.	*/
    +PUBLIC BOOL HTPassHighCtrlNum = FALSE;	/* Pass €-Ÿ 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
    +                                                                               
    +   
    -- 
    cgit 1.4.1-2-gfad0