about summary refs log tree commit diff stats
path: root/WWW/Library/Implementation
diff options
context:
space:
mode:
Diffstat (limited to 'WWW/Library/Implementation')
-rw-r--r--WWW/Library/Implementation/HTFTP.c5
-rw-r--r--WWW/Library/Implementation/HTFile.c790
-rw-r--r--WWW/Library/Implementation/HTFile.h17
-rw-r--r--WWW/Library/Implementation/HTFormat.c186
-rw-r--r--WWW/Library/Implementation/HTFormat.h23
-rw-r--r--WWW/Library/Implementation/HTMIME.c12
-rw-r--r--WWW/Library/Implementation/HTParse.c58
-rw-r--r--WWW/Library/Implementation/HTTCP.c1
-rw-r--r--WWW/Library/Implementation/HTTP.c36
9 files changed, 680 insertions, 448 deletions
diff --git a/WWW/Library/Implementation/HTFTP.c b/WWW/Library/Implementation/HTFTP.c
index 6770079c..7c41ab10 100644
--- a/WWW/Library/Implementation/HTFTP.c
+++ b/WWW/Library/Implementation/HTFTP.c
@@ -3725,10 +3725,13 @@ int HTFTPLoad(const char *name,
 		case cftGzip:
 		    StrAllocCopy(anchor->content_encoding, "x-gzip");
 		    break;
+		case cftDeflate:
+		    StrAllocCopy(anchor->content_encoding, "x-deflate");
+		    break;
 		case cftBzip2:
 		    StrAllocCopy(anchor->content_encoding, "x-bzip2");
 		    break;
-		default:
+		case cftNone:
 		    break;
 		}
 	    }
diff --git a/WWW/Library/Implementation/HTFile.c b/WWW/Library/Implementation/HTFile.c
index bf10952f..de4c9b6d 100644
--- a/WWW/Library/Implementation/HTFile.c
+++ b/WWW/Library/Implementation/HTFile.c
@@ -812,6 +812,29 @@ const char *HTFileSuffix(HTAtom *rep,
     return "";			/* Dunno */
 }
 
+/*
+ * Trim version from VMS filenames to avoid confusing comparisons.
+ */
+#ifdef VMS
+static const char *VMS_trim_version(const char *filename)
+{
+    const char *result = filename;
+    char *version = strchr(filename, ';');
+
+    if (version != 0) {
+	static char *stripped;
+
+	StrAllocCopy(stripped, filename);
+	stripped[(const char *) version - filename] = '\0';
+	result = (const char *) stripped;
+    }
+    return result;
+}
+#define VMS_DEL_VERSION(name) name = VMS_trim_version(name)
+#else
+#define VMS_DEL_VERSION(name)	/* nothing */
+#endif
+
 /*	Determine file format from file name.
  *	-------------------------------------
  *
@@ -832,9 +855,7 @@ HTFormat HTFileFormat(const char *filename,
     int i;
     int lf;
 
-#ifdef VMS
-    char *semicolon = NULL;
-#endif /* VMS */
+    VMS_DEL_VERSION(filename);
 
     if (pencoding)
 	*pencoding = NULL;
@@ -845,15 +866,6 @@ HTFormat HTFileFormat(const char *filename,
 	    *pencoding = WWW_ENC_8BIT;
 	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();
@@ -873,10 +885,6 @@ HTFormat HTFileFormat(const char *filename,
 	    if (pdesc)
 		*pdesc = suff->desc;
 	    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 */
@@ -894,10 +902,6 @@ HTFormat HTFileFormat(const char *filename,
 			    *pencoding != WWW_ENC_7BIT &&
 			    !IsUnityEnc(suff->encoding))
 			    *pencoding = suff->encoding;
-#ifdef VMS
-			if (semicolon != NULL)
-			    *semicolon = ';';
-#endif /* VMS */
 			return suff->rep;
 		    }
 		}
@@ -915,13 +919,11 @@ HTFormat HTFileFormat(const char *filename,
     /*
      * Set default encoding unless found with suffix already.
      */
-    if (pencoding && !*pencoding)
-	*pencoding = suff->encoding ? suff->encoding
-	    : HTAtom_for("binary");
-#ifdef VMS
-    if (semicolon != NULL)
-	*semicolon = ';';
-#endif /* VMS */
+    if (pencoding && !*pencoding) {
+	*pencoding = (suff->encoding
+		      ? suff->encoding
+		      : HTAtom_for("binary"));
+    }
     return suff->rep ? suff->rep : WWW_BINARY;
 }
 
@@ -1207,6 +1209,8 @@ CompressFileType HTCompressFileType(char *filename,
     size_t len = strlen(filename);
     char *ftype = filename + len;
 
+    VMS_DEL_VERSION(filename);
+
     if ((len > 4)
 	&& !strcasecomp((ftype - 3), "bz2")
 	&& strchr(dots, ftype[-4]) != 0) {
@@ -1217,6 +1221,11 @@ CompressFileType HTCompressFileType(char *filename,
 	       && strchr(dots, ftype[-3]) != 0) {
 	result = cftGzip;
 	ftype -= 3;
+    } else if ((len > 3)
+	       && !strcasecomp((ftype - 2), "zz")
+	       && strchr(dots, ftype[-3]) != 0) {
+	result = cftDeflate;
+	ftype -= 3;
     } else if ((len > 2)
 	       && !strcmp((ftype - 1), "Z")
 	       && strchr(dots, ftype[-2]) != 0) {
@@ -1225,11 +1234,42 @@ CompressFileType HTCompressFileType(char *filename,
     }
 
     *suffix = ftype;
+
     CTRACE((tfp, "HTCompressFileType(%s) returns %d:%s\n",
 	    filename, result, *suffix));
     return result;
 }
 
+/*
+ * Check if the token from "Content-Encoding" corresponds to a compression
+ * type.  RFC 2068 (and cut/paste into RFC 2616) lists these:
+ *	gzip
+ *	compress
+ *	deflate
+ * as well as "identity" (but that does nothing).
+ */
+CompressFileType HTEncodingToCompressType(const char *coding)
+{
+    CompressFileType result = cftNone;
+
+    if (coding == 0) {
+	result = cftNone;
+    } else if (!strcasecomp(coding, "gzip") ||
+	       !strcasecomp(coding, "x-gzip")) {
+	result = cftGzip;
+    } else if (!strcasecomp(coding, "compress") ||
+	       !strcasecomp(coding, "x-compress")) {
+	result = cftCompress;
+    } else if (!strcasecomp(coding, "bzip2") ||
+	       !strcasecomp(coding, "x-bzip2")) {
+	result = cftBzip2;
+    } else if (!strcasecomp(coding, "deflate") ||
+	       !strcasecomp(coding, "x-deflate")) {
+	result = cftDeflate;
+    }
+    return result;
+}
+
 /*	Determine write access to a file.
  *	---------------------------------
  *
@@ -2079,6 +2119,269 @@ int HTStat(const char *filename,
 }
 #endif
 
+#ifdef VMS
+#define FOPEN_MODE(bin) "r", "shr=put", "shr=upd"
+#define DOT_STRING "._-"	/* FIXME: should we check if suffix is after ']' or ':' ? */
+#else
+#define FOPEN_MODE(bin) (bin ? BIN_R : "r")
+#define DOT_STRING "."
+#endif
+
+static int decompressAndParse(HTParentAnchor *anchor,
+			      HTFormat format_out,
+			      HTStream *sink,
+			      char *nodename GCC_UNUSED,
+			      char *filename,
+			      HTAtom *myEncoding,
+			      HTFormat format,
+			      int *statusp)
+{
+    HTAtom *encoding = 0;
+
+#ifdef USE_ZLIB
+    FILE *zzfp = 0;
+    gzFile gzfp = 0;
+#endif /* USE_ZLIB */
+#ifdef USE_BZLIB
+    BZFILE *bzfp = 0;
+#endif /* USE_ZLIB */
+#if defined(USE_ZLIB) || defined(USE_BZLIB)
+    CompressFileType internal_decompress = cftNone;
+    BOOL failed_decompress = NO;
+#endif
+    char *dot = 0;
+    char *localname = filename;
+    int bin;
+    FILE *fp;
+
+#ifdef VMS
+    /*
+     * Assume that the file is in Unix-style syntax if it contains a '/' after
+     * the leading one.  @@
+     */
+    localname = (strchr(localname + 1, '/')
+		 ? HTVMS_name(nodename, localname)
+		 : localname + 1);
+#endif /* VMS */
+
+    bin = HTCompressFileType(filename, ".", &dot) != cftNone;
+    fp = fopen(localname, FOPEN_MODE(bin));
+
+#ifdef VMS
+    /*
+     * If the file wasn't VMS syntax, then perhaps it is Ultrix.
+     */
+    if (!fp) {
+	char *ultrixname = 0;
+
+	CTRACE((tfp, "HTLoadFile: Can't open as %s\n", localname));
+	HTSprintf0(&ultrixname, "%s::\"%s\"", nodename, filename);
+	fp = fopen(ultrixname, FOPEN_MODE(bin));
+	if (!fp) {
+	    CTRACE((tfp, "HTLoadFile: Can't open as %s\n", ultrixname));
+	}
+	FREE(ultrixname);
+    }
+#endif /* VMS */
+    CTRACE((tfp, "HTLoadFile: Opening `%s' gives %p\n", localname, fp));
+    if (fp) {			/* Good! */
+	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 (!IsUnityEnc(myEncoding)) {
+	    /*
+	     * We already know from the call to HTFileFormat that
+	     * this is a compressed file, no need to look at the filename
+	     * again.  - kw
+	     */
+	    CompressFileType method = HTEncodingToCompressType(HTAtom_name(myEncoding));
+
+#define isDOWNLOAD(m) (strcmp(format_out->name, "www/download") && (method == m))
+#ifdef USE_ZLIB
+	    if (isDOWNLOAD(cftGzip)) {
+		fclose(fp);
+		gzfp = gzopen(localname, BIN_R);
+
+		CTRACE((tfp, "HTLoadFile: gzopen of `%s' gives %p\n",
+			localname, gzfp));
+		internal_decompress = cftGzip;
+	    } else if (isDOWNLOAD(cftDeflate)) {
+		zzfp = fp;
+		fp = 0;
+
+		CTRACE((tfp, "HTLoadFile: zzopen of `%s' gives %p\n",
+			localname, zzfp));
+		internal_decompress = cftDeflate;
+	    } else
+#endif /* USE_ZLIB */
+#ifdef USE_BZLIB
+	    if (isDOWNLOAD(cftBzip2)) {
+		fclose(fp);
+		bzfp = BZ2_bzopen(localname, BIN_R);
+
+		CTRACE((tfp, "HTLoadFile: bzopen of `%s' gives %p\n",
+			localname, bzfp));
+		internal_decompress = cftBzip2;
+	    } else
+#endif /* USE_BZLIB */
+	    {
+		StrAllocCopy(anchor->content_type, format->name);
+		StrAllocCopy(anchor->content_encoding, HTAtom_name(myEncoding));
+		format = HTAtom_for("www/compressed");
+	    }
+	} else {
+	    CompressFileType cft = HTCompressFileType(localname, DOT_STRING, &dot);
+
+	    if (cft != cftNone) {
+		char *cp = NULL;
+
+		StrAllocCopy(cp, localname);
+		cp[dot - localname] = '\0';
+		format = HTFileFormat(cp, &encoding, NULL);
+		FREE(cp);
+		format = HTCharsetFormat(format, anchor,
+					 UCLYhndl_HTFile_for_unspec);
+		StrAllocCopy(anchor->content_type, format->name);
+	    }
+
+	    switch (cft) {
+	    case cftCompress:
+		StrAllocCopy(anchor->content_encoding, "x-compress");
+		format = HTAtom_for("www/compressed");
+		break;
+	    case cftDeflate:
+		StrAllocCopy(anchor->content_encoding, "x-deflate");
+#ifdef USE_ZLIB
+		if (strcmp(format_out->name, "www/download") != 0) {
+		    zzfp = fp;
+		    fp = 0;
+
+		    CTRACE((tfp, "HTLoadFile: zzopen of `%s' gives %p\n",
+			    localname, zzfp));
+		    internal_decompress = cftDeflate;
+		}
+#else /* USE_ZLIB */
+		format = HTAtom_for("www/compressed");
+#endif /* USE_ZLIB */
+		break;
+	    case cftGzip:
+		StrAllocCopy(anchor->content_encoding, "x-gzip");
+#ifdef USE_ZLIB
+		if (strcmp(format_out->name, "www/download") != 0) {
+		    fclose(fp);
+		    gzfp = gzopen(localname, BIN_R);
+
+		    CTRACE((tfp, "HTLoadFile: gzopen of `%s' gives %p\n",
+			    localname, gzfp));
+		    internal_decompress = cftGzip;
+		}
+#else /* USE_ZLIB */
+		format = HTAtom_for("www/compressed");
+#endif /* USE_ZLIB */
+		break;
+	    case cftBzip2:
+		StrAllocCopy(anchor->content_encoding, "x-bzip2");
+#ifdef USE_BZLIB
+		if (strcmp(format_out->name, "www/download") != 0) {
+		    fclose(fp);
+		    bzfp = BZ2_bzopen(localname, BIN_R);
+
+		    CTRACE((tfp, "HTLoadFile: bzopen of `%s' gives %p\n",
+			    localname, bzfp));
+		    internal_decompress = cftBzip2;
+		}
+#else /* USE_BZLIB */
+		format = HTAtom_for("www/compressed");
+#endif /* USE_BZLIB */
+		break;
+	    case cftNone:
+		break;
+	    }
+	}
+#if defined(USE_ZLIB) || defined(USE_BZLIB)
+	if (internal_decompress != cftNone) {
+	    switch (internal_decompress) {
+#ifdef USE_ZLIB
+	    case cftDeflate:
+		failed_decompress = (zzfp == 0);
+		break;
+	    case cftCompress:
+	    case cftGzip:
+		failed_decompress = (gzfp == 0);
+		break;
+#endif
+#ifdef USE_BZLIB
+	    case cftBzip2:
+		failed_decompress = (bzfp == 0);
+		break;
+#endif
+	    default:
+		failed_decompress = YES;
+		break;
+	    }
+	    if (failed_decompress) {
+		*statusp = HTLoadError(NULL,
+				       -(HT_ERROR),
+				       FAILED_OPEN_COMPRESSED_FILE);
+	    } else {
+		char *sugfname = NULL;
+
+		if (anchor->SugFname) {
+		    StrAllocCopy(sugfname, anchor->SugFname);
+		} else {
+		    char *anchor_path = HTParse(anchor->address, "",
+						PARSE_PATH + PARSE_PUNCTUATION);
+		    char *lastslash;
+
+		    HTUnEscape(anchor_path);
+		    lastslash = strrchr(anchor_path, '/');
+		    if (lastslash)
+			StrAllocCopy(sugfname, lastslash + 1);
+		    FREE(anchor_path);
+		}
+		FREE(anchor->content_encoding);
+		if (sugfname && *sugfname)
+		    HTCheckFnameForCompression(&sugfname, anchor,
+					       TRUE);
+		if (sugfname && *sugfname)
+		    StrAllocCopy(anchor->SugFname, sugfname);
+		FREE(sugfname);
+#ifdef USE_BZLIB
+		if (bzfp)
+		    *statusp = HTParseBzFile(format, format_out,
+					     anchor,
+					     bzfp, sink);
+#endif
+#ifdef USE_ZLIB
+		if (gzfp)
+		    *statusp = HTParseGzFile(format, format_out,
+					     anchor,
+					     gzfp, sink);
+		else if (zzfp)
+		    *statusp = HTParseZzFile(format, format_out,
+					     anchor,
+					     zzfp, sink);
+#endif
+	    }
+	} else
+#endif /* USE_ZLIB || USE_BZLIB */
+	{
+	    *statusp = HTParseFile(format, format_out, anchor, fp, sink);
+	    fclose(fp);
+	}
+	return TRUE;
+    }				/* If successful open */
+    return FALSE;
+}
+
 /*	Load a document.
  *	----------------
  *
@@ -2102,7 +2405,6 @@ int HTLoadFile(const char *addr,
     HTFormat format;
     char *nodename = NULL;
     char *newname = NULL;	/* Simplified name of file */
-    HTAtom *encoding;		/* @@ not used yet */
     HTAtom *myEncoding = NULL;	/* enc of this file, may be gzip etc. */
     int status = -1;
     char *dot;
@@ -2110,16 +2412,6 @@ int HTLoadFile(const char *addr,
 #ifdef VMS
     struct stat stat_info;
 #endif /* VMS */
-#ifdef USE_ZLIB
-    gzFile gzfp = 0;
-#endif /* USE_ZLIB */
-#ifdef USE_BZLIB
-    BZFILE *bzfp = 0;
-#endif /* USE_ZLIB */
-#if defined(USE_ZLIB) || defined(USE_BZLIB)
-    CompressFileType internal_decompress = cftNone;
-    BOOL failed_decompress = NO;
-#endif
 
     /*
      * Reduce the filename to a basic form (hopefully unique!).
@@ -2231,222 +2523,19 @@ int HTLoadFile(const char *addr,
 	}
     }
 
-    /*
-     * Assume that the file is in Unix-style syntax if it contains a '/' after
-     * the leading one.  @@
-     */
-    {
-	FILE *fp;
-	char *vmsname = strchr(filename + 1, '/') ?
-	HTVMS_name(nodename, filename) : filename + 1;
-
-	fp = fopen(vmsname, "r", "shr=put", "shr=upd");
-
-	/*
-	 * If the file wasn't VMS syntax, then perhaps it is Ultrix.
-	 */
-	if (!fp) {
-	    char *ultrixname = 0;
-
-	    CTRACE((tfp, "HTLoadFile: Can't open as %s\n", vmsname));
-	    HTSprintf0(&ultrixname, "%s::\"%s\"", nodename, filename);
-	    fp = fopen(ultrixname, "r", "shr=put", "shr=upd");
-	    if (!fp) {
-		CTRACE((tfp, "HTLoadFile: Can't open as %s\n",
-			ultrixname));
-	    }
-	    FREE(ultrixname);
-	}
-	if (fp) {
-	    char *semicolon = NULL;
-
-	    if (HTEditable(vmsname)) {
-		HTAtom *put = HTAtom_for("PUT");
-		HTList *methods = HTAnchor_methods(anchor);
-
-		if (HTList_indexOf(methods, put) == (-1)) {
-		    HTList_addObject(methods, put);
-		}
-	    }
-	    /*
-	     * Trim vmsname at semicolon if a version number was included, so
-	     * it doesn't interfere with the check for a compressed file.  - FM
-	     */
-	    if ((semicolon = strchr(vmsname, ';')) != NULL)
-		*semicolon = '\0';
-	    /*
-	     * Fake a Content-Encoding for compressed files.  - FM
-	     */
-	    if (!IsUnityEnc(myEncoding)) {
-		/*
-		 * We already know from the call to HTFileFormat above that
-		 * this is a compressed file, no need to look at the filename
-		 * again.  - kw
-		 */
-#ifdef USE_ZLIB
-		if (strcmp(format_out->name, "www/download") != 0 &&
-		    (!strcmp(HTAtom_name(myEncoding), "gzip") ||
-		     !strcmp(HTAtom_name(myEncoding), "x-gzip"))) {
-		    fclose(fp);
-		    if (semicolon != NULL)
-			*semicolon = ';';
-		    gzfp = gzopen(vmsname, BIN_R);
-
-		    CTRACE((tfp, "HTLoadFile: gzopen of `%s' gives %p\n",
-			    vmsname, (void *) gzfp));
-		    internal_decompress = cftGzip;
-		} else
-#endif /* USE_ZLIB */
-#ifdef USE_BZLIB
-		    if (strcmp(format_out->name, "www/download") != 0 &&
-			(!strcmp(HTAtom_name(myEncoding), "bzip2") ||
-			 !strcmp(HTAtom_name(myEncoding), "x-bzip2"))) {
-		    fclose(fp);
-		    if (semicolon != NULL)
-			*semicolon = ';';
-		    bzfp = BZ2_bzopen(vmsname, BIN_R);
-
-		    CTRACE((tfp, "HTLoadFile: bzopen of `%s' gives %p\n",
-			    vmsname, (void *) bzfp));
-		    use_zread = YES;
-		} else
-#endif /* USE_BZLIB */
-		{
-		    StrAllocCopy(anchor->content_type, format->name);
-		    StrAllocCopy(anchor->content_encoding, HTAtom_name(myEncoding));
-		    format = HTAtom_for("www/compressed");
-		}
-	    } else {
-		/* FIXME: should we check if suffix is after ']' or ':' ? */
-		CompressFileType cft = HTCompressFileType(vmsname, "._-", &dot);
-
-		if (cft != cftNone) {
-		    char *cp = NULL;
-
-		    StrAllocCopy(cp, vmsname);
-		    cp[dot - vmsname] = '\0';
-		    format = HTFileFormat(cp, &encoding, NULL);
-		    FREE(cp);
-		    format = HTCharsetFormat(format, anchor,
-					     UCLYhndl_HTFile_for_unspec);
-		    StrAllocCopy(anchor->content_type, format->name);
-		}
-
-		switch (cft) {
-		case cftCompress:
-		    StrAllocCopy(anchor->content_encoding, "x-compress");
-		    format = HTAtom_for("www/compressed");
-		    break;
-		case cftGzip:
-		    StrAllocCopy(anchor->content_encoding, "x-gzip");
-#ifdef USE_ZLIB
-		    if (strcmp(format_out->name, "www/download") != 0) {
-			fclose(fp);
-			if (semicolon != NULL)
-			    *semicolon = ';';
-			gzfp = gzopen(vmsname, BIN_R);
-
-			CTRACE((tfp, "HTLoadFile: gzopen of `%s' gives %p\n",
-				vmsname, (void *) gzfp));
-			internal_decompress = cftGzip;
-		    }
-#else /* USE_ZLIB */
-		    format = HTAtom_for("www/compressed");
-#endif /* USE_ZLIB */
-		    break;
-		case cftBzip2:
-		    StrAllocCopy(anchor->content_encoding, "x-bzip2");
-#ifdef USE_BZLIB
-		    if (strcmp(format_out->name, "www/download") != 0) {
-			fclose(fp);
-			if (semicolon != NULL)
-			    *semicolon = ';';
-			bzfp = BZ2_bzopen(vmsname, BIN_R);
-
-			CTRACE((tfp, "HTLoadFile: bzopen of `%s' gives %p\n",
-				vmsname, (void *) bzfp));
-			internal_decompress = cfgBzip2;
-		    }
-#else /* USE_BZLIB */
-		    format = HTAtom_for("www/compressed");
-#endif /* USE_BZLIB */
-		    break;
-		case cftNone:
-		    break;
-		}
-	    }
-	    if (semicolon != NULL)
-		*semicolon = ';';
-	    FREE(filename);
-	    FREE(nodename);
-#if defined(USE_ZLIB) || defined(USE_BZLIB)
-	    if (internal_decompress != cftNone) {
-		switch (internal_decompress) {
-#ifdef USE_ZLIB
-		case cftCompress:
-		case cftGzip:
-		    failed_decompress = (gzfp == 0);
-		    break;
-#endif
-#ifdef USE_BZLIB
-		case cftBzip2:
-		    failed_decompress = (bzfp == 0);
-		    break;
-#endif
-		default:
-		    failed_decompress = YES;
-		    break;
-		}
-		if (failed_decompress) {
-		    status = HTLoadError(NULL,
-					 -(HT_ERROR),
-					 FAILED_OPEN_COMPRESSED_FILE);
-		} else {
-		    char *sugfname = NULL;
-
-		    if (anchor->SugFname) {
-			StrAllocCopy(sugfname, anchor->SugFname);
-		    } else {
-			char *anchor_path = HTParse(anchor->address, "",
-						    PARSE_PATH + PARSE_PUNCTUATION);
-			char *lastslash;
-
-			HTUnEscape(anchor_path);
-			lastslash = strrchr(anchor_path, '/');
-			if (lastslash)
-			    StrAllocCopy(sugfname, lastslash + 1);
-			FREE(anchor_path);
-		    }
-		    FREE(anchor->content_encoding);
-		    if (sugfname && *sugfname)
-			HTCheckFnameForCompression(&sugfname, anchor,
-						   TRUE);
-		    if (sugfname && *sugfname)
-			StrAllocCopy(anchor->SugFname, sugfname);
-		    FREE(sugfname);
-#ifdef USE_BZLIB
-		    if (bzfp)
-			status = HTParseBzFile(format, format_out,
-					       anchor,
-					       bzfp, sink);
-#endif
-#ifdef USE_ZLIB
-		    if (gzfp)
-			status = HTParseGzFile(format, format_out,
-					       anchor,
-					       gzfp, sink);
-#endif
-		}
-	    } else
-#endif /* USE_ZLIB || USE_BZLIB */
-	    {
-		status = HTParseFile(format, format_out, anchor, fp, sink);
-		fclose(fp);
-	    }
-	    return status;
-	}			/* If successful open */
+    if (decompressAndParse(anchor,
+			   format_out,
+			   sink,
+			   nodename,
+			   filename,
+			   myEncoding,
+			   format,
+			   &status)) {
+	FREE(nodename);
 	FREE(filename);
+	return status;
     }
+    FREE(filename);
 
 #else /* not VMS: */
 
@@ -2540,6 +2629,9 @@ int HTLoadFile(const char *addr,
 			    case cftGzip:
 				atomname = "application/x-gzip";
 				break;
+			    case cftDeflate:
+				atomname = "application/x-deflate";
+				break;
 			    case cftBzip2:
 				atomname = "application/x-bzip2";
 				break;
@@ -2678,184 +2770,19 @@ int HTLoadFile(const char *addr,
 /* End of directory reading section
 */
 #endif /* HAVE_READDIR */
-	{
-	    int bin = HTCompressFileType(localname, ".", &dot) != cftNone;
-	    FILE *fp = fopen(localname, (bin ? BIN_R : "r"));
-
-	    CTRACE((tfp, "HTLoadFile: Opening `%s' gives %p\n",
-		    localname, (void *) fp));
-	    if (fp) {		/* Good! */
-		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 (!IsUnityEnc(myEncoding)) {
-		    /*
-		     * We already know from the call to HTFileFormat above that
-		     * this is a compressed file, no need to look at the
-		     * filename again.  - kw
-		     */
-#ifdef USE_ZLIB
-		    if (strcmp(format_out->name, "www/download") != 0 &&
-			(!strcmp(HTAtom_name(myEncoding), "gzip") ||
-			 !strcmp(HTAtom_name(myEncoding), "x-gzip"))) {
-			fclose(fp);
-			gzfp = gzopen(localname, BIN_R);
-
-			CTRACE((tfp, "HTLoadFile: gzopen of `%s' gives %p\n",
-				localname, (void *) gzfp));
-			internal_decompress = cftGzip;
-		    } else
-#endif /* USE_ZLIB */
-#ifdef USE_BZLIB
-			if (strcmp(format_out->name, "www/download") != 0 &&
-			    (!strcmp(HTAtom_name(myEncoding), "bzip2") ||
-			     !strcmp(HTAtom_name(myEncoding), "x-bzip2"))) {
-			fclose(fp);
-			bzfp = BZ2_bzopen(localname, BIN_R);
-
-			CTRACE((tfp, "HTLoadFile: bzopen of `%s' gives %p\n",
-				localname, (void *) bzfp));
-			internal_decompress = cftBzip2;
-		    } else
-#endif /* USE_BZLIB */
-		    {
-			StrAllocCopy(anchor->content_type, format->name);
-			StrAllocCopy(anchor->content_encoding, HTAtom_name(myEncoding));
-			format = HTAtom_for("www/compressed");
-		    }
-		} else {
-		    CompressFileType cft = HTCompressFileType(localname, ".", &dot);
-
-		    if (cft != cftNone) {
-			char *cp = NULL;
-
-			StrAllocCopy(cp, localname);
-			cp[dot - localname] = '\0';
-			format = HTFileFormat(cp, &encoding, NULL);
-			FREE(cp);
-			format = HTCharsetFormat(format, anchor,
-						 UCLYhndl_HTFile_for_unspec);
-			StrAllocCopy(anchor->content_type, format->name);
-		    }
-
-		    switch (cft) {
-		    case cftCompress:
-			StrAllocCopy(anchor->content_encoding, "x-compress");
-			format = HTAtom_for("www/compressed");
-			break;
-		    case cftGzip:
-			StrAllocCopy(anchor->content_encoding, "x-gzip");
-#ifdef USE_ZLIB
-			if (strcmp(format_out->name, "www/download") != 0) {
-			    fclose(fp);
-			    gzfp = gzopen(localname, BIN_R);
-
-			    CTRACE((tfp,
-				    "HTLoadFile: gzopen of `%s' gives %p\n",
-				    localname, (void *) gzfp));
-			    internal_decompress = cftGzip;
-			}
-#else /* USE_ZLIB */
-			format = HTAtom_for("www/compressed");
-#endif /* USE_ZLIB */
-			break;
-		    case cftBzip2:
-			StrAllocCopy(anchor->content_encoding, "x-bzip2");
-#ifdef USE_BZLIB
-			if (strcmp(format_out->name, "www/download") != 0) {
-			    fclose(fp);
-			    bzfp = BZ2_bzopen(localname, BIN_R);
-
-			    CTRACE((tfp,
-				    "HTLoadFile: bzopen of `%s' gives %p\n",
-				    localname, (void *) bzfp));
-			    internal_decompress = cftBzip2;
-			}
-#else /* USE_BZLIB */
-			format = HTAtom_for("www/compressed");
-#endif /* USE_BZLIB */
-			break;
-		    case cftNone:
-			break;
-		    }
-		}
-		FREE(localname);
-		FREE(nodename);
-#if defined(USE_ZLIB) || defined(USE_BZLIB)
-		if (internal_decompress != cftNone) {
-		    switch (internal_decompress) {
-#ifdef USE_ZLIB
-		    case cftGzip:
-			failed_decompress = (gzfp == 0);
-			break;
-#endif
-#ifdef USE_BZLIB
-		    case cftBzip2:
-			failed_decompress = (bzfp == 0);
-			break;
-#endif
-		    default:
-			failed_decompress = YES;
-			break;
-		    }
-		    if (failed_decompress) {
-			status = HTLoadError(NULL,
-					     -(HT_ERROR),
-					     FAILED_OPEN_COMPRESSED_FILE);
-		    } else {
-			char *sugfname = NULL;
-
-			if (anchor->SugFname) {
-			    StrAllocCopy(sugfname, anchor->SugFname);
-			} else {
-			    char *anchor_path = HTParse(anchor->address, "",
-							PARSE_PATH + PARSE_PUNCTUATION);
-			    char *lastslash;
-
-			    HTUnEscape(anchor_path);
-			    lastslash = strrchr(anchor_path, '/');
-			    if (lastslash)
-				StrAllocCopy(sugfname, lastslash + 1);
-			    FREE(anchor_path);
-			}
-			FREE(anchor->content_encoding);
-			if (sugfname && *sugfname)
-			    HTCheckFnameForCompression(&sugfname, anchor,
-						       TRUE);
-			if (sugfname && *sugfname)
-			    StrAllocCopy(anchor->SugFname, sugfname);
-			FREE(sugfname);
-#ifdef USE_BZLIB
-			if (bzfp)
-			    status = HTParseBzFile(format, format_out,
-						   anchor,
-						   bzfp, sink);
-#endif
-#ifdef USE_ZLIB
-			if (gzfp)
-			    status = HTParseGzFile(format, format_out,
-						   anchor,
-						   gzfp, sink);
-#endif
-		    }
-		} else
-#endif /* USE_ZLIB */
-		{
-		    status = HTParseFile(format, format_out, anchor, fp, sink);
-		    fclose(fp);
-		}
-		return status;
-	    }			/* If successful open */
+	if (decompressAndParse(anchor,
+			       format_out,
+			       sink,
+			       nodename,
+			       localname,
+			       myEncoding,
+			       format,
+			       &status)) {
+	    FREE(nodename);
 	    FREE(localname);
-	}			/* scope of fp */
+	    return status;
+	}
+	FREE(localname);
     }				/* local unix file system */
 #endif /* !NO_UNIX_IO */
 #endif /* VMS */
@@ -2966,6 +2893,11 @@ void HTInitProgramPaths(void)
 	    path = GZIP_PATH;
 	    break;
 #endif
+#ifdef INFLATE_PATH
+	case ppINFLATE:
+	    path = INFLATE_PATH;
+	    break;
+#endif
 #ifdef INSTALL_PATH
 	case ppINSTALL:
 	    path = INSTALL_PATH;
diff --git a/WWW/Library/Implementation/HTFile.h b/WWW/Library/Implementation/HTFile.h
index 8c522b64..0c69bb42 100644
--- a/WWW/Library/Implementation/HTFile.h
+++ b/WWW/Library/Implementation/HTFile.h
@@ -192,20 +192,34 @@ extern void LYGetFileInfo(const char *filename,
 extern float HTFileValue(const char *filename);
 
 /*
- *  Determine compression type from file name, by looking at its suffix.
+ *  Known compression types.
  */
 typedef enum {
     cftNone
     ,cftCompress
     ,cftGzip
     ,cftBzip2
+    ,cftDeflate
 } CompressFileType;
 
+/*
+ *  Determine compression type from file name, by looking at its suffix.
+ */
 extern CompressFileType HTCompressFileType(char *filename,
 					   char *dots,
 					   char **suffix);
 
 /*
+ *  Determine compression type from the content-type.
+ */
+extern CompressFileType HTContentToCompressType(const char *encoding);
+
+/*
+ *  Determine compression type from the content-encoding.
+ */
+extern CompressFileType HTEncodingToCompressType(const char *encoding);
+
+/*
  *  Determine write access to a file.
  *
  *  ON EXIT,
@@ -276,6 +290,7 @@ typedef enum {
     ,ppCOPY
     ,ppCSWING
     ,ppGZIP
+    ,ppINFLATE
     ,ppINSTALL
     ,ppMKDIR
     ,ppMV
diff --git a/WWW/Library/Implementation/HTFormat.c b/WWW/Library/Implementation/HTFormat.c
index fa9bc045..bdcdef17 100644
--- a/WWW/Library/Implementation/HTFormat.c
+++ b/WWW/Library/Implementation/HTFormat.c
@@ -436,7 +436,7 @@ static HTPresentation *HTFindPresentation(HTFormat rep_in,
 		    if (failsMailcap(pres, anchor))
 			continue;
 		    last_default_match = pres;
-		/* otherwise use the first one */
+		    /* otherwise use the first one */
 		}
 	    }
 	}
@@ -1084,6 +1084,122 @@ static int HTGzFileCopy(gzFile gzfp, HTStream *sink)
     HTFinishDisplayPartial();
     return rv;
 }
+
+/*	Push data from a deflate file pointer down a stream
+ *	-------------------------------------
+ *
+ *  This routine is responsible for creating and PRESENTING any
+ *  graphic (or other) objects described by the file.  The code is
+ *  loosely based on the inflate.c file from w3m.
+ *
+ *
+ *  State of file and target stream on entry:
+ *		      FILE (zzfp) assumed open (should have deflated content),
+ *		      target (sink) assumed valid.
+ *
+ *  Return values:
+ *	HT_INTERRUPTED  Interruption after some data read.
+ *	HT_PARTIAL_CONTENT	Error after some data read.
+ *	-1		Error before any data read.
+ *	HT_LOADED	Normal end of file indication on reading.
+ *
+ *  State of file and target stream on return:
+ *	always		zzfp still open, target stream still valid.
+ */
+static int HTZzFileCopy(FILE *zzfp, HTStream *sink)
+{
+    static char dummy_head[1 + 1] =
+    {
+	0x8 + 0x7 * 0x10,
+	(((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF,
+    };
+
+    z_stream s;
+    HTStreamClass targetClass;
+    int bytes;
+    int rv = HT_OK;
+    char output_buffer[INPUT_BUFFER_SIZE];
+    int status;
+    int flush;
+    int retry = 0;
+    int len = 0;
+
+    /*  Push the data down the stream
+     */
+    targetClass = *(sink->isa);	/* Copy pointers to procedures */
+
+    s.zalloc = Z_NULL;
+    s.zfree = Z_NULL;
+    s.opaque = Z_NULL;
+    status = inflateInit(&s);
+    if (status != Z_OK) {
+	CTRACE((tfp, "HTZzFileCopy inflateInit() %s\n", zError(status)));
+	exit(1);
+    }
+    s.avail_in = 0;
+    s.next_out = (Bytef *) output_buffer;
+    s.avail_out = sizeof(output_buffer);
+    flush = Z_NO_FLUSH;
+
+    /*  read and inflate deflate'd file, and push binary down sink
+     */
+    HTReadProgress(bytes = 0, 0);
+    for (;;) {
+	if (s.avail_in == 0) {
+	    s.next_in = (Bytef *) input_buffer;
+	    len = s.avail_in = fread(input_buffer, 1, INPUT_BUFFER_SIZE, zzfp);
+	}
+	status = inflate(&s, flush);
+	if (status == Z_STREAM_END || status == Z_BUF_ERROR) {
+	    len = sizeof(output_buffer) - s.avail_out;
+	    if (len > 0) {
+		(*targetClass.put_block) (sink, output_buffer, len);
+		bytes += len;
+		HTReadProgress(bytes, -1);
+		HTDisplayPartial();
+	    }
+	    rv = HT_LOADED;
+	    break;
+	} else if (status == Z_DATA_ERROR && !retry++) {
+	    status = inflateReset(&s);
+	    if (status != Z_OK) {
+		CTRACE((tfp, "HTZzFileCopy inflateReset() %s\n", zError(status)));
+		rv = bytes ? HT_PARTIAL_CONTENT : -1;
+		break;
+	    }
+	    s.next_in = (Bytef *) dummy_head;
+	    s.avail_in = sizeof(dummy_head);
+	    status = inflate(&s, flush);
+	    s.next_in = (Bytef *) input_buffer;
+	    s.avail_in = len;
+	    continue;
+	} else if (status != Z_OK) {
+	    CTRACE((tfp, "HTZzFileCopy inflate() %s\n", zError(status)));
+	    rv = bytes ? HT_PARTIAL_CONTENT : -1;
+	    break;
+	} else if (s.avail_out == 0) {
+	    len = sizeof(output_buffer);
+	    s.next_out = (Bytef *) output_buffer;
+	    s.avail_out = sizeof(output_buffer);
+
+	    (*targetClass.put_block) (sink, output_buffer, len);
+	    bytes += len;
+	    HTReadProgress(bytes, -1);
+	    HTDisplayPartial();
+
+	    if (HTCheckForInterrupt()) {
+		_HTProgress(TRANSFER_INTERRUPTED);
+		rv = bytes ? HT_INTERRUPTED : -1;
+		break;
+	    }
+	}
+	retry = 1;
+    }				/* next bufferload */
+
+    inflateEnd(&s);
+    HTFinishDisplayPartial();
+    return rv;
+}
 #endif /* USE_ZLIB */
 
 #ifdef USE_BZLIB
@@ -1480,6 +1596,74 @@ int HTParseGzFile(HTFormat rep_in,
     else
 	return HT_LOADED;
 }
+
+/*	HTParseZzFile
+ *
+ *  State of file and target stream on entry:
+ *			FILE (zzfp) assumed open,
+ *			target (sink) usually NULL (will call stream stack).
+ *
+ *  Return values:
+ *	-501		Stream stack failed (cannot present or convert).
+ *	-1		Download cancelled.
+ *	HT_NO_DATA	Error before any data read.
+ *	HT_PARTIAL_CONTENT	Interruption or error after some data read.
+ *	HT_LOADED	Normal end of file indication on reading.
+ *
+ *  State of file and target stream on return:
+ *	always		zzfp closed; target freed, aborted, or NULL.
+ */
+int HTParseZzFile(HTFormat rep_in,
+		  HTFormat format_out,
+		  HTParentAnchor *anchor,
+		  FILE *zzfp,
+		  HTStream *sink)
+{
+    HTStream *stream;
+    HTStreamClass targetClass;
+    int rv;
+
+    stream = HTStreamStack(rep_in, format_out, sink, anchor);
+
+    if (!stream) {
+	char *buffer = 0;
+
+	fclose(zzfp);
+	if (LYCancelDownload) {
+	    LYCancelDownload = FALSE;
+	    return -1;
+	}
+	HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O,
+		   HTAtom_name(rep_in), HTAtom_name(format_out));
+	CTRACE((tfp, "HTFormat(in HTParseGzFile): %s\n", buffer));
+	rv = HTLoadError(sink, 501, buffer);
+	FREE(buffer);
+	return rv;
+    }
+
+    /*
+     * 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 */
+    rv = HTZzFileCopy(zzfp, stream);
+    if (rv == -1 || rv == HT_INTERRUPTED) {
+	(*targetClass._abort) (stream, NULL);
+    } else {
+	(*targetClass._free) (stream);
+    }
+
+    fclose(zzfp);
+    if (rv == -1)
+	return HT_NO_DATA;
+    else if (rv == HT_INTERRUPTED || (rv > 0 && rv != HT_LOADED))
+	return HT_PARTIAL_CONTENT;
+    else
+	return HT_LOADED;
+}
 #endif /* USE_ZLIB */
 
 #ifdef USE_BZLIB
diff --git a/WWW/Library/Implementation/HTFormat.h b/WWW/Library/Implementation/HTFormat.h
index ec51e7d6..ad3df250 100644
--- a/WWW/Library/Implementation/HTFormat.h
+++ b/WWW/Library/Implementation/HTFormat.h
@@ -218,9 +218,13 @@ typedef enum {
 typedef enum {
     encodingNONE = 0
     ,encodingGZIP = 1
-    ,encodingCOMPRESS = 2
-    ,encodingBZIP2 = 4
-    ,encodingALL = encodingGZIP + encodingCOMPRESS + encodingBZIP2
+    ,encodingDEFLATE = 2
+    ,encodingCOMPRESS = 4
+    ,encodingBZIP2 = 8
+    ,encodingALL = (encodingGZIP
+		    + encodingDEFLATE
+		    + encodingCOMPRESS
+		    + encodingBZIP2)
 } AcceptEncoding;
 
 /*
@@ -482,6 +486,19 @@ extern int HTParseGzFile(HTFormat format_in,
 			 gzFile gzfp,
 			 HTStream *sink);
 
+/*
+HTParseZzFile: Parse a deflate'd File through a file pointer
+
+   This routine is called by protocols modules to load an object.  uses
+   HTStreamStack and HTZzFileCopy.  Returns HT_LOADED if successful, can also
+   return HT_PARTIAL_CONTENT, HT_NO_DATA, or other <0 for failure.
+ */
+extern int HTParseZzFile(HTFormat format_in,
+			 HTFormat format_out,
+			 HTParentAnchor *anchor,
+			 FILE *zzfp,
+			 HTStream *sink);
+
 #endif /* USE_ZLIB */
 
 #ifdef USE_BZLIB
diff --git a/WWW/Library/Implementation/HTMIME.c b/WWW/Library/Implementation/HTMIME.c
index efa54e4e..49791955 100644
--- a/WWW/Library/Implementation/HTMIME.c
+++ b/WWW/Library/Implementation/HTMIME.c
@@ -15,6 +15,7 @@
 #include <HTMIME.h>		/* Implemented here */
 #include <HTTP.h>		/* for redirecting_url */
 #include <HTAlert.h>
+#include <HTFile.h>
 #include <HTCJK.h>
 #include <UCMap.h>
 #include <UCDefs.h>
@@ -168,14 +169,17 @@ void HTMIME_TrimDoubleQuotes(char *value)
 	value[i] = cp[(i + 1)];
 }
 
+/*
+ * Check if the token from "Content-Encoding" corresponds to a compression
+ * type.
+ */
 static BOOL content_is_compressed(HTStream *me)
 {
     char *encoding = me->anchor->content_encoding;
+    BOOL result = (HTEncodingToCompressType(encoding) != cftNone);
 
-    return encoding != 0
-	&& strcmp(encoding, "8bit") != 0
-	&& strcmp(encoding, "7bit") != 0
-	&& strcmp(encoding, "binary") != 0;
+    CTRACE((tfp, "content is%s compressed\n", result ? "" : " NOT"));
+    return result;
 }
 
 /*
diff --git a/WWW/Library/Implementation/HTParse.c b/WWW/Library/Implementation/HTParse.c
index e894b55c..70a21573 100644
--- a/WWW/Library/Implementation/HTParse.c
+++ b/WWW/Library/Implementation/HTParse.c
@@ -29,6 +29,24 @@ struct struct_parts {
     char *anchor;
 };
 
+#if 0				/* for debugging */
+static void show_parts(const char *name, struct struct_parts *parts, int line)
+{
+    if (TRACE) {
+	CTRACE((tfp, "struct_parts(%s) %s@%d\n", name, __FILE__, line));
+	CTRACE((tfp, "   access   '%s'\n", NONNULL(parts->access)));
+	CTRACE((tfp, "   host     '%s'\n", NONNULL(parts->host)));
+	CTRACE((tfp, "   absolute '%s'\n", NONNULL(parts->absolute)));
+	CTRACE((tfp, "   relative '%s'\n", NONNULL(parts->relative)));
+	CTRACE((tfp, "   search   '%s'\n", NONNULL(parts->search)));
+	CTRACE((tfp, "   anchor   '%s'\n", NONNULL(parts->anchor)));
+    }
+}
+#define SHOW_PARTS(name) show_parts(#name, &name, __LINE__)
+#else
+#define SHOW_PARTS(name)	/* nothing */
+#endif
+
 /*	Strip white space off a string.				HTStrip()
  *	-------------------------------
  *
@@ -163,6 +181,16 @@ static void scan(char *name,
 #define LYalloca_free(x)   free(x)
 #endif
 
+static char *strchr_or_end(char *string, int ch)
+{
+    char *result = strchr(string, ch);
+
+    if (result == 0) {
+	result = string + strlen(string);
+    }
+    return result;
+}
+
 /*	Parse a Name relative to another name.			HTParse()
  *	--------------------------------------
  *
@@ -187,7 +215,7 @@ char *HTParse(const char *aName,
     int len, len1, len2;
     char *name = NULL;
     char *rel = NULL;
-    char *p;
+    char *p, *q;
     char *acc_method;
     struct struct_parts given, related;
 
@@ -236,6 +264,7 @@ char *HTParse(const char *aName,
      * Cut up the string into URL fields.
      */
     scan(name, &given);
+    SHOW_PARTS(given);
 
     /*
      * Now related string.
@@ -254,6 +283,7 @@ char *HTParse(const char *aName,
 	memcpy(rel, relatedName, len2);
 	scan(rel, &related);
     }
+    SHOW_PARTS(related);
 
     /*
      * Handle the scheme (access) field.
@@ -450,12 +480,24 @@ char *HTParse(const char *aName,
 	    *tail++ = '/';
 	    strcpy(tail, related.absolute);
 	    if (given.relative) {
-		p = strchr(tail, '?');	/* Search part? */
-		if (p == NULL)
-		    p = (tail + strlen(tail) - 1);
-		for (; *p != '/'; p--) ;	/* last / */
-		p[1] = '\0';	/* Remove filename */
-		strcat(p, given.relative);	/* Add given one */
+		/* RFC 1808 part 4 step 5 (if URL path is empty) */
+		/* a) if given has params, add/replace that */
+		if (given.relative[0] == ';') {
+		    strcpy(strchr_or_end(tail, ';'), given.relative);
+		}
+		/* b) if given has query, add/replace that */
+		else if (given.relative[0] == '?') {
+		    strcpy(strchr_or_end(tail, '?'), given.relative);
+		}
+		/* otherwise fall through to RFC 1808 part 4 step 6 */
+		else {
+		    p = strchr(tail, '?');	/* Search part? */
+		    if (p == NULL)
+			p = (tail + strlen(tail) - 1);
+		    for (; *p != '/'; p--) ;	/* last / */
+		    p[1] = '\0';	/* Remove filename */
+		    strcat(p, given.relative);	/* Add given one */
+		}
 		HTSimplify(result);
 	    }
 	    CTRACE((tfp, "HTParse: (Related-ABS)\n"));
@@ -536,7 +578,7 @@ char *HTParse(const char *aName,
 	default:
 	    CTRACE((tfp, "HTParse:      encode:`%s'\n", result));
 	    do {
-		char *q = p + strlen(p) + 2;
+		q = p + strlen(p) + 2;
 
 		while (q != p + 1) {
 		    q[0] = q[-2];
diff --git a/WWW/Library/Implementation/HTTCP.c b/WWW/Library/Implementation/HTTCP.c
index b5574926..84aeb412 100644
--- a/WWW/Library/Implementation/HTTCP.c
+++ b/WWW/Library/Implementation/HTTCP.c
@@ -1390,6 +1390,7 @@ static LYNX_ADDRINFO *HTGetAddrInfo(const char *str,
 	res = NULL;
     }
 
+    free(s);
     return res;
 }
 #endif /* INET6 */
diff --git a/WWW/Library/Implementation/HTTP.c b/WWW/Library/Implementation/HTTP.c
index ab12eb2b..7307ebb1 100644
--- a/WWW/Library/Implementation/HTTP.c
+++ b/WWW/Library/Implementation/HTTP.c
@@ -353,6 +353,40 @@ static void strip_userid(char *host)
     }
 }
 
+/*
+ * Check if the user's options specified to use the given encoding.  Normally
+ * all encodings with compiled-in support are specified (encodingALL).
+ */
+static BOOL acceptEncoding (int code)
+{
+    BOOL result = FALSE;
+    if ((code & LYAcceptEncoding) != 0) {
+	const char *program = 0;
+	switch (code) {
+	case encodingGZIP:
+	    program = HTGetProgramPath(ppGZIP);
+	    break;
+	case encodingDEFLATE:
+	    program = HTGetProgramPath(ppINFLATE);
+	    break;
+	case encodingCOMPRESS:
+	    program = HTGetProgramPath(ppCOMPRESS);
+	    break;
+	case encodingBZIP2:
+	    program = HTGetProgramPath(ppBZIP2);
+	    break;
+	default:
+	    break;
+	}
+	/*
+	 * FIXME:  if lynx did not rely upon external programs to decompress
+	 * files for external viewers, this check could be relaxed.
+	 */
+	result = (program != 0);
+    }
+    return result;
+}
+
 /*		Load Document from HTTP Server			HTLoadHTTP()
  *		==============================
  *
@@ -745,7 +779,7 @@ static int HTLoadHTTP(const char *arg,
 	    int j, k;
 
 	    for (j = 1; j < encodingALL; j <<= 1) {
-		if ((j & LYAcceptEncoding) != 0) {
+		if (acceptEncoding(j)) {
 		    for (k = 0; tbl_preferred_encoding[k].name != 0; ++k) {
 			if (tbl_preferred_encoding[k].value == j) {
 			    if (list != 0)