File:  [Public] / libwww / Library / src / HTFormat.c
Revision 1.89: download - view: text, annotated - select for diffs
Mon Jun 3 19:25:11 1996 UTC (28 years ago) by eric
Branches: MAIN
CVS tags: HEAD
removed HTHashList

/*								     HTFormat.c
**	MANAGE DIFFERENT FILE FORMATS
**
**	(c) COPYRIGHT MIT 1995.
**	Please first read the full copyright statement in the file COPYRIGH.
**	@(#) $Id: HTFormat.c,v 1.89 1996/06/03 19:25:11 eric Exp $
**
** Bugs:
**	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.
**
** HISTORY:
**	8 Jul 94  FM	Insulate free from _free structure element.
**	8 Nov 94  HFN	Changed a lot to make reentrant
*/

/* Library Include files */
#include "sysdep.h"
#include "WWWUtil.h"
#include "HTStream.h"
#include "HTFWrite.h"
#include "HTError.h"
#include "HTFormat.h"					 /* Implemented here */

#define NO_VALUE_FOUND	-1e30		 /* Stream Stack Value if none found */

PRIVATE HTList * HTConversions = NULL;			    /* Content types */
PRIVATE HTList * HTContentCoders = NULL;		   /* Content coders */
PRIVATE HTList * HTTransferCoders = NULL;	  /* Content transfer coders */
PRIVATE HTList * HTCharsets = NULL;
PRIVATE HTList * HTLanguages = NULL;

PRIVATE double HTMaxSecs = 1e10;		/* No effective limit */

struct _HTStream {
    const HTStreamClass *	isa;
};

struct _HTCoding {
    HTEncoding		encoding;
    HTCoder *		encoder;
    HTCoder *		decoder;
    double		quality;
};

/* ------------------------------------------------------------------------- */
/*				BASIC CONVERTERS			     */
/* ------------------------------------------------------------------------- */

PUBLIC HTStream * HTBlackHoleConverter (HTRequest *	request,
					void *		param,
					HTFormat	input_format,
					HTFormat	output_format,
					HTStream *	output_stream)
{
    return HTBlackHole();
}
/*	HTThroughLine
**	-------------
**
** This function is a dummy function that returns the same output stream
** as given as a parameter. Henrik 01/03-94
*/
PUBLIC HTStream* HTThroughLine (HTRequest *	request,
				void *		param,
				HTFormat	input_format,
				HTFormat	output_format,
				HTStream *	output_stream)
{
    return output_stream;
}

/*
**	For all `accept lists' there is a local list and a global list. The
**	local list is a part of the request structure and the global list is
**	internal to the HTFormat module. The global lists can be used when
**	specifying accept lists for ALL requests and the local list can be 
**	used to add specific accept headers to the request.
*/

/* ------------------------------------------------------------------------- */
/* 	  			CONTENT TYPES				     */
/* ------------------------------------------------------------------------- */

/*	Define a presentation system command for a content-type
**	-------------------------------------------------------
** INPUT:
**	conversions:	The list of conveters and presenters
**	representation:	the MIME-style format name
**	command:	the MAILCAP-style command template
**	quality:	A degradation faction [0..1]
**	maxbytes:	A limit on the length acceptable as input (0 infinite)
**	maxsecs:	A limit on the time user will wait (0 for infinity)
*/
PUBLIC void HTPresentation_add (HTList *	conversions,
				const char *	representation,
				const char *	command,
				const char *	test_command,
				double		quality,
				double		secs, 
				double		secs_per_byte)
{
    HTPresentation * pres;
    if ((pres = (HTPresentation  *) HT_CALLOC(1,sizeof(HTPresentation))) == NULL)
        HT_OUTOFMEM("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->rep = HTAtom_for(representation);
    pres->command = NULL;
    StrAllocCopy(pres->command, command);
    pres->test_command = NULL;
    StrAllocCopy(pres->test_command, test_command);
    HTList_addObject(conversions, pres);
}

PUBLIC void HTPresentation_deleteAll (HTList * list)
{
    if (list) {
	HTList *cur = list;
	HTPresentation *pres;
	while ((pres = (HTPresentation*) HTList_nextObject(cur))) {
	    HT_FREE(pres->command);
	    HT_FREE(pres);
	}
	HTList_delete(list);
    }
}

/*	Define a built-in function for a content-type
**	---------------------------------------------
*/
PUBLIC void HTConversion_add (HTList *		conversions,
			      const char *	representation_in,
			      const char *	representation_out,
			      HTConverter *	converter,
			      double		quality,
			      double		secs, 
			      double		secs_per_byte)
{
    HTPresentation * pres;
    if ((pres = (HTPresentation*) HT_CALLOC(1,sizeof(HTPresentation))) == NULL)
        HT_OUTOFMEM("HTSetPresentation");
    pres->rep = HTAtom_for(representation_in);
    pres->rep_out = HTAtom_for(representation_out);
    pres->converter = converter;
    pres->command = NULL;		/* Fixed */
    pres->test_command = NULL;
    pres->quality = quality;
    pres->secs = secs;
    pres->secs_per_byte = secs_per_byte;
    HTList_addObject(conversions, pres);
}

PUBLIC void HTConversion_deleteAll (HTList * list)
{
    HTPresentation_deleteAll(list);
}

/* ------------------------------------------------------------------------- */
/*		CONTENT ENCODING AND CONTENT TRANSFER ENCODING 		     */
/* ------------------------------------------------------------------------- */

PUBLIC BOOL HTCoding_add (HTList * 	list,
			  const char *	encoding,
			  HTCoder *	encoder,
			  HTCoder *	decoder,
			  double	quality)
{
    if (list && encoding && (encoder || decoder)) {
	HTCoding * me;
	if ((me = (HTCoding *) HT_CALLOC(1, sizeof(HTCoding))) == NULL)
	    HT_OUTOFMEM("HTCoding_add");
	me->encoding = HTAtom_for(encoding);
	me->encoder = encoder;
	me->decoder = decoder;
	me->quality = quality;
	if (CORE_TRACE)
	    HTTrace("Codings..... Adding %s with quality %.2f\n",
		    encoding, quality);
	return HTList_addObject(list, (void *) me);
    }
    if (CORE_TRACE) HTTrace("Codings..... Bad argument\n");
    return NO;
}

PUBLIC void HTCoding_deleteAll (HTList * list)
{
    if (list) {
	HTList * cur = list;
	HTCoding * pres;
	while ((pres = (HTCoding *) HTList_nextObject(cur)))
	    HT_FREE(pres);
	HTList_delete(list);
    }
}

PUBLIC const char * HTCoding_name (HTCoding * me)
{
    return me ? HTAtom_name(me->encoding) : NULL;
}

/* ------------------------------------------------------------------------- */
/* 	  			CONTENT LANGUAGE			     */
/* ------------------------------------------------------------------------- */

PUBLIC void HTLanguage_add (HTList *		list,
			    const char *	lang,
			    double		quality)
{
    HTAcceptNode * node;
    if (!list || !lang || !*lang)  {
	if (CORE_TRACE)
	    HTTrace("Languages... Bad argument\n");
	return;
    }
    if ((node = (HTAcceptNode *) HT_CALLOC(1, sizeof(HTAcceptNode))) == NULL)
        HT_OUTOFMEM("HTAcceptLanguage");

    HTList_addObject(list, (void*)node);
    node->atom = HTAtom_for(lang);
    node->quality = quality;
}

PUBLIC void HTLanguage_deleteAll (HTList * list)
{
    if (list) {
	HTList *cur = list;
	HTAcceptNode *pres;
	while ((pres = (HTAcceptNode *) HTList_nextObject(cur))) {
	    HT_FREE(pres);
	}
	HTList_delete(list);
    }
}

/* ------------------------------------------------------------------------- */
/* 	  			CONTENT CHARSET				     */
/* ------------------------------------------------------------------------- */

PUBLIC void HTCharset_add (HTList *		list,
			   const char *		charset,
			   double		quality)
{
    HTAcceptNode * node;
    if (!list || !charset || !*charset)  {
	if (CORE_TRACE)
	    HTTrace("Charset..... Bad argument\n");
	return;
    }
    if ((node = (HTAcceptNode *) HT_CALLOC(1, sizeof(HTAcceptNode))) == NULL)
        HT_OUTOFMEM("HTAcceptCharsetuage");

    HTList_addObject(list, (void*)node);
    node->atom = HTAtom_for(charset);
    node->quality = quality;
}

PUBLIC void HTCharset_deleteAll (HTList * list)
{
    if (list) {
	HTList *cur = list;
	HTAcceptNode *pres;
	while ((pres = (HTAcceptNode *) HTList_nextObject(cur))) {
	    HT_FREE(pres);
	}
	HTList_delete(list);
    }
}

/* ------------------------------------------------------------------------- */
/* 			GLOBAL LIST OF CONVERTERS ETC.			     */
/* ------------------------------------------------------------------------- */

/*
**	Global Accept Format Types Conversions
**	list can be NULL
*/
PUBLIC void HTFormat_setConversion (HTList * list)
{
    HTConversions = list;
}

PUBLIC HTList * HTFormat_conversion (void)
{
    return HTConversions;
}

/*
**	Global list of Content Encodings
**	list can be NULL
*/
PUBLIC void HTFormat_setContentCoding (HTList *list)
{
    HTContentCoders = list;
}

PUBLIC HTList * HTFormat_contentCoding (void)
{
    return HTContentCoders;
}

/*
**	Global list of Content Transfer Encodings
**	list can be NULL
*/
PUBLIC void HTFormat_setTransferCoding (HTList *list)
{
    HTTransferCoders = list;
}

PUBLIC HTList * HTFormat_transferCoding (void)
{
    return HTTransferCoders;
}

/*
**	Global Accept Languages
**	list can be NULL
*/
PUBLIC void HTFormat_setLanguage (HTList *list)
{
    HTLanguages = list;
}

PUBLIC HTList * HTFormat_language (void)
{
    return HTLanguages;
}

/*
**	Global Accept Charsets
**	list can be NULL
*/
PUBLIC void HTFormat_setCharset (HTList *list)
{
    HTCharsets = list;
}

PUBLIC HTList * HTFormat_charset (void)
{
    return HTCharsets;
}

/*
**	Convenience function to clean up
*/
PUBLIC void HTFormat_deleteAll (void)
{
    if (HTConversions) {
	HTConversion_deleteAll(HTConversions);
	HTConversions = NULL;
    }
    if (HTLanguages) {
	HTLanguage_deleteAll(HTLanguages);
	HTLanguages = NULL;
    }
    if (HTContentCoders) {
	HTCoding_deleteAll(HTContentCoders);
	HTContentCoders = NULL;
    }
    if (HTTransferCoders) {
	HTCoding_deleteAll(HTTransferCoders);
	HTTransferCoders = NULL;
    }
    if (HTCharsets) {
	HTCharset_deleteAll(HTCharsets);
	HTCharsets = NULL;
    }
}

/* ------------------------------------------------------------------------- */
/* 				FORMAT NEGOTIATION			     */
/* ------------------------------------------------------------------------- */

PRIVATE BOOL better_match (HTFormat f, HTFormat g)
{
    const char *p, *q;

    if (f && g  &&  (p = HTAtom_name(f))  &&  (q = HTAtom_name(g))) {
	int i,j;
	for(i=0 ; *p; p++) if (*p == '*') i++;
	for(j=0 ; *q; q++) if (*q == '*') j++;
	if (i < j) return YES;
    }
    return NO;
}


PRIVATE BOOL wild_match (HTAtom * tmplate, HTAtom * actual)
{
    const char *t, *a;
    char *st, *sa;
    BOOL match = NO;

    if (tmplate && actual && (t = HTAtom_name(tmplate))) {
	if (!strcmp(t, "*"))
	    return YES;

	if (strchr(t, '*') &&
	    (a = HTAtom_name(actual)) &&
	    (st = strchr(t, '/')) && (sa = strchr(a,'/'))) {

	    *sa = 0;
	    *st = 0;

	    if ((*(st-1)=='*' &&
		 (*(st+1)=='*' || !strcasecomp(st+1, sa+1))) ||
		(*(st+1)=='*' && !strcasecomp(t,a)))
		match = YES;

	    *sa = '/';
	    *st = '/';
	}    
    }
    return match;
}

/*
 * Added by takada@seraph.ntt.jp (94/04/08)
 */
PRIVATE BOOL lang_match (HTAtom * tmplate, HTAtom * actual)
{
    const char *t, *a;
    char *st, *sa;
    BOOL match = NO;

    if (tmplate && actual &&
	(t = HTAtom_name(tmplate)) && (a = HTAtom_name(actual))) {
	st = strchr(t, '_');
	sa = strchr(a, '_');
	if ((st != NULL) && (sa != NULL)) {
	    if (!strcasecomp(t, a))
	      match = YES;
	    else
	      match = NO;
	}
	else {
	    if (st != NULL) *st = 0;
	    if (sa != NULL) *sa = 0;
	    if (!strcasecomp(t, a))
	      match = YES;
	    else
	      match = NO;
	    if (st != NULL) *st = '_';
	    if (sa != NULL) *sa = '_';
	}
    }
    return match;
}
/* end of addition */



PRIVATE double type_value (HTAtom * content_type, HTList * accepted)
{
    HTList * cur = accepted;
    HTPresentation * pres;
    HTPresentation * wild = NULL;

    if (!content_type || !accepted) return -1;

    while ((pres = (HTPresentation*)HTList_nextObject(cur))) {
	if (pres->rep == content_type)
	    return pres->quality;
	else if (wild_match(pres->rep, content_type))
	    wild = pres;
    }
    if (wild) return wild->quality;
    else return -1;
}


PRIVATE double lang_value (HTAtom * language, HTList * accepted)
{
    HTList * cur = accepted;
    HTAcceptNode * node;
    HTAcceptNode * wild = NULL;

    if (!language || !accepted || HTList_isEmpty(accepted)) {
	return 0.1;
    }

    while ((node = (HTAcceptNode*)HTList_nextObject(cur))) {
	if (node->atom == language) {
	    return node->quality;
	}
	/*
	 * patch by takada@seraph.ntt.jp (94/04/08)
	 * the original line was
	 * else if (wild_match(node->atom, language)) {
	 * and the new line is
	 */
	else if (lang_match(node->atom, language)) {
	    wild = node;
	}
    }

    if (wild) {
	return wild->quality;
    }
    else {
	return 0.1;
    }
}


PRIVATE double encoding_value (HTAtom * encoding, HTList * accepted)
{
    HTList * cur = accepted;
    HTAcceptNode * node;
    HTAcceptNode * wild = NULL;
    const char * e;

    if (!encoding || !accepted || HTList_isEmpty(accepted))
	return 1;

    e = HTAtom_name(encoding);
    if (!strcmp(e, "7bit") || !strcmp(e, "8bit") || !strcmp(e, "binary"))
	return 1;

    while ((node = (HTAcceptNode*)HTList_nextObject(cur))) {
	if (node->atom == encoding)
	    return node->quality;
	else if (wild_match(node->atom, encoding))
	    wild = node;
    }
    if (wild) return wild->quality;
    else return 1;
}


PUBLIC BOOL HTRank (HTList * possibilities,
		    HTList * accepted_content_types,
		    HTList * accepted_languages,
		    HTList * accepted_encodings)
{
    int accepted_cnt = 0;
    HTList * accepted;
    HTList * sorted;
    HTList * cur;
    HTContentDescription * d;

    if (!possibilities) return NO;

    accepted = HTList_new();
    cur = possibilities;
    while ((d = (HTContentDescription*)HTList_nextObject(cur))) {
	double tv = type_value(d->content_type, accepted_content_types);
	double lv = lang_value(d->content_language, accepted_languages);
	double ev = encoding_value(d->content_encoding, accepted_encodings);

	if (tv > 0) {
	    d->quality *= tv * lv * ev;
	    HTList_addObject(accepted, d);
	    accepted_cnt++;
	}
	else {
	    HT_FREE(d->filename);
	    HT_FREE(d);
	}
    }

    if (CORE_TRACE) HTTrace("Ranking.....\n");
    if (CORE_TRACE) HTTrace(
	   "\nRANK QUALITY CONTENT-TYPE         LANGUAGE ENCODING    FILE\n");

    sorted = HTList_new();
    while (accepted_cnt-- > 0) {
	HTContentDescription * worst = NULL;
	cur = accepted;
	while ((d = (HTContentDescription*)HTList_nextObject(cur))) {
	    if (!worst || d->quality < worst->quality)
		worst = d;
	}
	if (worst) {
	    if (CORE_TRACE)
		HTTrace("%d.   %.4f  %-20.20s %-8.8s %-10.10s %s\n",
			accepted_cnt+1,
			worst->quality,
			(worst->content_type
		         ? HTAtom_name(worst->content_type)      : "-"),
			(worst->content_language
		         ? HTAtom_name(worst->content_language)  :"-"),
			(worst->content_encoding
		         ? HTAtom_name(worst->content_encoding)  :"-"),
			(worst->filename
		         ? worst->filename                       :"-"));
	    HTList_removeObject(accepted, (void*)worst);
	    HTList_addObject(sorted, (void*)worst);
	}
    }
    if (CORE_TRACE) HTTrace("\n");
    HTList_delete(accepted);
    HTList_delete(possibilities->next);
    possibilities->next = sorted->next;
    sorted->next = NULL;
    HTList_delete(sorted);

    if (!HTList_isEmpty(possibilities)) return YES;
    else return NO;
}


/*	Create a Content Type 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.
**
**	The star/star format is special, in that if you can take
**	that you can take anything.
*/
PUBLIC HTStream * HTStreamStack (HTFormat	rep_in,
				 HTFormat	rep_out,
				 HTStream *	output_stream,
				 HTRequest *	request,
				 BOOL		guess)
{
    HTList * conversion[2];
    int which_list;
    double best_quality = -1e30;		/* Pretty bad! */
    HTPresentation *pres, *best_match=NULL;
    if (rep_out == WWW_RAW) {
	if (CORE_TRACE) HTTrace("StreamStack. Raw output...\n");
	return output_stream ? output_stream : HTErrorStream();
    }

    if (rep_out == rep_in) {
	if (CORE_TRACE)
	    HTTrace("StreamStack. Identical input/output format (%s)\n",
		     HTAtom_name(rep_out));
	return output_stream ? output_stream : HTErrorStream();
    }
    if (CORE_TRACE) {
	const char *p = HTAtom_name(rep_in);
	const char *q = HTAtom_name(rep_out); 
	HTTrace("StreamStack. Constructing stream stack for %s to %s\n",
		 p ? p : "<NULL>", q ? q : "<NULL>");
    }

    conversion[0] = HTRequest_conversion(request);
    conversion[1] = HTConversions;

    for(which_list = 0; which_list<2; which_list++) {
	HTList * cur = conversion[which_list];
	while ((pres = (HTPresentation*)HTList_nextObject(cur))) {
	    if ((pres->rep==rep_in || wild_match(pres->rep, rep_in)) &&
		(pres->rep_out==rep_out || wild_match(pres->rep_out,rep_out))){
		if (!best_match || better_match(pres->rep, best_match->rep) ||
		    (!better_match(best_match->rep, pres->rep) &&
		     pres->quality > best_quality)) {
#ifdef HAVE_SYSTEM
		    int result=0;
		    if (pres->test_command) {
			result = system(pres->test_command);
			if (CORE_TRACE) 
			    HTTrace("StreamStack. system(%s) returns %d\n", pres->test_command, result);
		    }
		    if (!result) {
			best_match = pres;
			best_quality = pres->quality;
		    }
#else
		    best_match = pres;
		    best_quality = pres->quality;
#endif /* HAVE_SYSTEM */
		}
	    }
	}
    }

    if (best_match) {
 	if (rep_out == WWW_SOURCE && best_match->rep_out != WWW_SOURCE) {
	    if (CORE_TRACE) HTTrace("StreamStack. Source output\n");
	    return output_stream ? output_stream : HTErrorStream();
	}
	return (*best_match->converter)(request, best_match->command,
					rep_in, rep_out, output_stream);
    }
    if (rep_out == WWW_SOURCE) {
	if (CORE_TRACE) HTTrace("StreamStack. Source output\n");
	return output_stream ? output_stream : HTErrorStream();
    }

    if (CORE_TRACE)
	HTTrace("StreamStack. No match found, dumping to local file\n");
    return HTSaveLocally(request, NULL, rep_in, rep_out, output_stream);
}
	

/*		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 double HTStackValue (HTList *	theseConversions,
			    HTFormat	rep_in,
			    HTFormat	rep_out,
			    double	initial_value,
			    long int	length)
{
    int which_list;
    HTList* conversion[2];
    
    if (CORE_TRACE) {
	HTTrace("StackValue.. 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;

    conversion[0] = theseConversions;
    conversion[1] = HTConversions;
    
    for(which_list = 0; which_list<2; which_list++)
     if (conversion[which_list]) {
        HTList * cur = conversion[which_list];
	HTPresentation * pres;
	while ((pres = (HTPresentation*)HTList_nextObject(cur))) {
	    if (pres->rep == rep_in &&
		(pres->rep_out == rep_out || wild_match(pres->rep_out, rep_out))) {
	        double value = initial_value * pres->quality;
		if (HTMaxSecs != 0.0)
		    value = value - (length*pres->secs_per_byte + pres->secs)
			                 /HTMaxSecs;
		return value;
	    }
	}
    }
    return NO_VALUE_FOUND;		/* Really bad */
}

/*	Create a new coder and insert it into stream chain
**	--------------------------------------------------
**	Creating the content decoding stack is not based on quality factors as
**	we don't have the freedom as with content types. Specify whether you
**	you want encoding or decoding using the BOOL "encode" flag.
*/
PUBLIC HTStream * HTContentCodingStack (HTEncoding	encoding,
					HTStream *	target,
					HTRequest *	request,
					void *		param,
					BOOL		encode)
{
    HTList * coders[2];
    HTStream * top = target;
    HTCoding * pres = NULL;
    int cnt;
    if (!encoding || !request) {
	if (CORE_TRACE) HTTrace("Codings... Nothing applied...\n");
	return target ? target : HTErrorStream();
    }
    coders[0] = HTRequest_encoding(request);
    coders[1] = HTContentCoders;
    if (CORE_TRACE)
	HTTrace("Codings..... Looking for %s\n", HTAtom_name(encoding));
    for (cnt=0; cnt < 2; cnt++) {
	HTList * cur = coders[cnt];
	while ((pres = (HTCoding *) HTList_nextObject(cur))) {
	    if (pres->encoding == encoding) {
		if (CORE_TRACE) HTTrace("Codings..... Found...\n");
		if (encode) {
		    if (pres->encoder)
			top = (*pres->encoder)(request, param, encoding, top);
		    break;
		} else if (pres->decoder) {
		    top = (*pres->decoder)(request, param, encoding, top);
		    break;
		}
	    }
	}
    }
    return top;
}

/*
**  Here you can provide a complete list instead of a single token.
**  The list has to filled up in the order the _encodings_ are to be applied
*/
PUBLIC HTStream * HTContentEncodingStack (HTList *	encodings,
					  HTStream *	target,
					  HTRequest *	request,
					  void *	param)
{
    if (encodings) {
	HTList * cur = encodings;
	HTEncoding pres;
	HTStream * top = target;
	while ((pres = (HTEncoding) HTList_nextObject(cur)))
	    top = HTContentCodingStack(pres, top, request, param, YES);
	return top;
    }
    return HTErrorStream();
}

/*
**  Here you can provide a complete list instead of a single token.
**  The list has to be in the order the _encodings_ were applied - that
**  is, the same way that _encodings_ are to be applied. This is all consistent
**  with the order of the Content-Encoding header.
*/
PUBLIC HTStream * HTContentDecodingStack (HTList *	encodings,
					  HTStream *	target,
					  HTRequest *	request,
					  void *	param)
{
    if (encodings) {
	HTEncoding pres;
	int cnt = HTList_count(encodings);
	HTStream * top = target;
	while (cnt > 0) {
	    pres = (HTEncoding) HTList_objectAt(encodings, --cnt);
	    top = HTContentCodingStack(pres, top, request, param, NO);
	}
	return top;
    }
    return HTErrorStream();
}

/*	Create a new transfer coder and insert it into stream chain
**	-----------------------------------------------------------
**	Creating the content decoding stack is not based on quality factors as
**	we don't have the freedom as with content types. Specify whether you
**	you want encoding or decoding using the BOOL "encode" flag.
*/
PUBLIC HTStream * HTTransferCodingStack (HTEncoding	encoding,
					 HTStream *	target,
					 HTRequest *	request,
					 void *		param,
					 BOOL		encode)
{
    HTList * coders[2];
    HTStream * top = target;
    HTCoding * pres = NULL;
    int cnt;
    if (!encoding || !request) {
	if (CORE_TRACE) HTTrace("C-T-E..... Nothing applied...\n");
	return target ? target : HTErrorStream();
    }
    coders[0] = HTRequest_transfer(request);
    coders[1] = HTTransferCoders;
    if (CORE_TRACE)
	HTTrace("C-T-E....... Looking for %s\n", HTAtom_name(encoding));
    for (cnt=0; cnt < 2; cnt++) {
	HTList * cur = coders[cnt];
	while ((pres = (HTCoding *) HTList_nextObject(cur))) {
	    if (pres->encoding == encoding) {
		if (CORE_TRACE) HTTrace("C-T-E....... Found...\n");
		if (encode) {
		    if (pres->encoder)
			top = (*pres->encoder)(request, param, encoding, top);
		    break;
		} else if (pres->decoder) {
		    top = (*pres->decoder)(request, param, encoding, top);
		    break;
		}
	    }
	}
    }
    return top;
}


Webmaster