/* File: libwww_parse_xml.c
** Author(s): kifer, Yang Yang
** Contact: xsb-contact@cs.sunysb.edu
**
** Copyright (C) The Research Foundation of SUNY, 2000
**
** XSB 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.
**
** XSB 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 XSB; if not, write to the Free Software Foundation,
** Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
** $Id: libwww_parse_xml.c,v 1.14 2001/03/17 05:44:01 kifer Exp $
**
*/
#include "libwww_util.h"
#include "libwww_parse.h"
#include "libwww_parse_xml.h"
/* BOOL, PRIVATE, PUBLIC, etc., are defined in a Libwww header */
/* ------------------------------------------------------------------------- */
/* HTXML STREAM HANDLERS */
/* ------------------------------------------------------------------------- */
PRIVATE void HTXML_setHandlers (XML_Parser me)
{
XML_SetElementHandler(me, xml_beginElement, xml_endElement);
XML_SetCharacterDataHandler(me, xml_addText);
XML_SetProcessingInstructionHandler(me, xml_processingInstruction);
XML_SetUnparsedEntityDeclHandler(me, xml_unparsedEntityDecl);
XML_SetNotationDeclHandler(me, xml_notationDecl);
XML_SetExternalEntityRefHandler(me, xml_externalEntityRef);
XML_SetUnknownEncodingHandler(me, xml_unknownEncoding, NULL);
/* This exists only in expat 1.1. This version doesn't prohibit expansion of
internal entities. Commented until expat 1.1 is included in libwww
XML_SetDefaultHandlerExpand(me, xml_default);
*/
}
void HTXML_newInstance (HTStream * me,
HTRequest * request,
HTFormat target_format,
HTStream * target_stream,
XML_Parser xmlparser,
void * context)
{
USERDATA *userdata = xml_create_userData(xmlparser, request, target_stream);
XML_SetUserData(xmlparser, (void *) userdata);
if (me && xmlparser) HTXML_setHandlers(xmlparser);
}
/* This is the callback that captures start tag events */
PRIVATE void xml_beginElement(void *userdata, /* where we build everything */
const XML_Char *tag, /* tag */
const XML_Char **attributes)
{
USERDATA *userdata_obj = (USERDATA *) userdata;
#ifdef LIBWWW_DEBUG
xsb_dbgmsg("***In xml_beginElement(%s): stackptr=%d tag=%s suppress=%d choose=%d",
RequestID(userdata_obj->request),
userdata_obj->stackptr, tag,
IS_SUPPRESSED_TAG((HKEY)(char *)tag, userdata_obj->request),
IS_SELECTED_TAG((HKEY)(char *)tag, userdata_obj->request)
);
#endif
if (IS_STRIPPED_TAG((HKEY)(char *)tag, userdata_obj->request)) return;
if ((suppressing(userdata_obj)
&& !IS_SELECTED_TAG((HKEY)(char *)tag, userdata_obj->request))
|| (parsing(userdata_obj)
&& IS_SUPPRESSED_TAG((HKEY)(char *)tag, userdata_obj->request))) {
xml_push_suppressed_element(userdata_obj, tag);
return;
}
/* parsing or suppressing & found a selected tag */
if ((parsing(userdata_obj)
&& !IS_SUPPRESSED_TAG((HKEY)(char *)tag, userdata_obj->request))
|| (suppressing(userdata_obj)
&& IS_SELECTED_TAG((HKEY)(char *)tag, userdata_obj->request))) {
xml_push_element(userdata_obj,tag,attributes);
return;
}
}
/* The callback for the end-tag event */
PRIVATE void xml_endElement (void *userdata, const XML_Char *tag)
{
USERDATA *userdata_obj = (USERDATA *) userdata;
#ifdef LIBWWW_DEBUG
xsb_dbgmsg("***In xml_endElement(%s): stackptr=%d, tag=%s",
RequestID(userdata_obj->request),
userdata_obj->stackptr, tag);
#endif
if (IS_STRIPPED_TAG((HKEY)(char *)tag, userdata_obj->request)) return;
/* Expat does checking for tag mismatches, so we don't have to */
if (parsing(userdata_obj))
xml_pop_element(userdata_obj);
else
xml_pop_suppressed_element(userdata_obj);
#ifdef LIBWWW_DEBUG_VERBOSE
if (userdata_obj->stackptr >= 0) {
if (!STACK_TOP(userdata_obj).suppress)
print_prolog_term(STACK_TOP(userdata_obj).elt_term, "elt_term");
}
#endif
return;
}
/* The callback to capture text events */
PRIVATE void xml_addText (void *userdata,
const XML_Char *textbuf,
int len)
{
USERDATA *userdata_obj = (USERDATA *) userdata;
static XSB_StrDefine(pcdata_buf);
int shift = 0;
REQUEST_CONTEXT *context =
(REQUEST_CONTEXT *)HTRequest_context(userdata_obj->request);
#ifdef LIBWWW_DEBUG
xsb_dbgmsg("***In xml_addText (%s)", RequestID(userdata_obj->request));
#endif
if (IS_STRIPPED_TAG((HKEY)"pcdata", userdata_obj->request)) return;
if (suppressing(userdata_obj)) return;
/* strip useless newlines */
if (strncmp(textbuf,"\n", len) == 0) return;
if (!xml_push_element(userdata_obj, "pcdata", NULL))
return;
/* copy textbuf (which isn't null-terminated) into a variable length str */
XSB_StrEnsureSize(&pcdata_buf, len+1);
strncpy(pcdata_buf.string, textbuf, len);
pcdata_buf.length = len;
XSB_StrNullTerminate(&pcdata_buf);
/* if string starts with a newline, skip the newline */
if (strncmp(textbuf,"\n", strlen("\n")) == 0)
shift = strlen("\n");
#ifdef LIBWWW_DEBUG_VERBOSE
xsb_dbgmsg("***In addText: pcdata=%s", pcdata_buf.string+shift);
#endif
/* put the text string into the elt term and then pop it */
if (context->convert2list)
c2p_chars(pcdata_buf.string+shift,
p2p_arg(STACK_TOP(userdata_obj).elt_term,3));
else
c2p_string(pcdata_buf.string+shift,
p2p_arg(STACK_TOP(userdata_obj).elt_term,3));
xml_pop_element(userdata_obj);
return;
}
/* Collect tag's attributes and make them into a list of the form
[attval(attr,val), ...]; bind it to Arg 2 of ELT_TERM */
PRIVATE void collect_xml_attributes (prolog_term elt_term,
const XML_Char **attrs)
{
static XSB_StrDefine(attrname);
prolog_term
prop_list = p2p_arg(elt_term,2),
prop_list_tail = prop_list,
prop_list_head;
c2p_list(prop_list_tail);
while (attrs && *attrs) {
XSB_StrEnsureSize(&attrname, strlen((char *)*attrs));
strcpy_lower(attrname.string, (char *)*attrs);
#ifdef LIBWWW_DEBUG_VERBOSE
xsb_dbgmsg("***attr=%s", attrname.string);
#endif
prop_list_head = p2p_car(prop_list_tail);
c2p_functor("attval",2,prop_list_head);
c2p_string(attrname.string, p2p_arg(prop_list_head,1));
/* get value */
attrs++;
/* if *attrs=NULL, then it is an error: expat will stop */
if (*attrs)
c2p_string((char *)*attrs, p2p_arg(prop_list_head, 2));
prop_list_tail = p2p_cdr(prop_list_tail);
c2p_list(prop_list_tail);
attrs++;
}
/* Terminate the property list */
c2p_nil(prop_list_tail);
return;
}
/* push element onto USERDATA->stack */
PRIVATE int xml_push_element (USERDATA *userdata,
const XML_Char *tag,
const XML_Char **attrs)
{
static XSB_StrDefine(lower_tagname);
prolog_term location;
/* If tag is not valid */
if (tag == NULL) return TRUE;
if (userdata->stackptr < 0)
location = userdata->parsed_term_tail;
else
location = STACK_TOP(userdata).content_list_tail;
userdata->stackptr++;
#ifdef LIBWWW_DEBUG
xsb_dbgmsg("***In xml_push_element(%s): stackptr=%d tag=%s",
RequestID(userdata->request), userdata->stackptr, tag);
#endif
CHECK_STACK_OVERFLOW(userdata);
/* wire the new elt into where it should be in the content list */
STACK_TOP(userdata).elt_term = p2p_car(location);
STACK_TOP(userdata).tag = (XML_Char *)tag; /* cast to discard const
declaration */
STACK_TOP(userdata).suppress = FALSE;
/* lowercase the tag */
XSB_StrEnsureSize(&lower_tagname, strlen(tag)+1);
strcpy_lower(lower_tagname.string, tag);
/* normal tags look like elt(tagname, attrlist, contentlist);
pcdata tags are: elt(pcdata,[],text); */
if (XSB_StrCmp(&lower_tagname, "pcdata")==0)
c2p_functor("elt",3,STACK_TOP(userdata).elt_term);
else /* normal elt */
c2p_functor("elt",3,STACK_TOP(userdata).elt_term);
c2p_string(lower_tagname.string, p2p_arg(STACK_TOP(userdata).elt_term, 1));
collect_xml_attributes(STACK_TOP(userdata).elt_term, attrs);
#ifdef LIBWWW_DEBUG_VERBOSE
xsb_dbgmsg("***elt_name=%s", lower_tagname.string);
print_prolog_term(STACK_TOP(userdata).elt_term, "elt_term");
#endif
/* normal element */
if (XSB_StrCmp(&lower_tagname, "pcdata")!=0) {
STACK_TOP(userdata).content_list_tail =
p2p_arg(STACK_TOP(userdata).elt_term,3);
c2p_list(STACK_TOP(userdata).content_list_tail);
}
return TRUE;
}
/* When done with an elt, close its contents list and pop the stack */
PRIVATE void xml_pop_element(USERDATA *userdata)
{
#ifdef LIBWWW_DEBUG_VERBOSE
xsb_dbgmsg("***In xml_pop_element(%s): stackptr=%d, elt_name=%s",
RequestID(userdata->request),
userdata->stackptr,
STACK_TOP(userdata).tag);
#endif
/* close the property list, for notmal elements */
if (strcasecmp(STACK_TOP(userdata).tag, "pcdata")!=0) {
c2p_nil(STACK_TOP(userdata).content_list_tail);
}
/* insert new list cell into the tail and change content_list_tail to point
to the new tail */
if (userdata->stackptr > 0) {
STACK_PREV(userdata).content_list_tail =
p2p_cdr(STACK_PREV(userdata).content_list_tail);
c2p_list(STACK_PREV(userdata).content_list_tail);
} else {
userdata->parsed_term_tail = p2p_cdr(userdata->parsed_term_tail);
c2p_list(userdata->parsed_term_tail);
}
userdata->stackptr--;
#ifdef LIBWWW_DEBUG_VERBOSE
if (userdata->stackptr >= 0)
print_prolog_term(STACK_TOP(userdata).content_list_tail,
"content_list_tail");
else
print_prolog_term(userdata->parsed_term_tail, "parsed_term_tail");
#endif
return;
}
/* Push tag, but keep only the tag info; don't convert to prolog term */
PRIVATE void xml_push_suppressed_element(USERDATA *userdata,
const XML_Char *tag)
{
/* non-empty tag */
userdata->stackptr++; /* advance ptr, but don't push tag */
STACK_TOP(userdata).tag = (XML_Char *)tag; /* cast to discard const
declaration */
STACK_TOP(userdata).suppress = TRUE;
/* passing content list tail through suppressed elements */
if (userdata->stackptr == 0)
STACK_TOP(userdata).content_list_tail = userdata->parsed_term_tail;
else
STACK_TOP(userdata).content_list_tail =
STACK_PREV(userdata).content_list_tail;
return;
}
PRIVATE void xml_pop_suppressed_element(USERDATA *userdata)
{
/* chain the list tails back through the sequence of suppressed tags */
if (userdata->stackptr > 0) {
STACK_PREV(userdata).content_list_tail = STACK_TOP(userdata).content_list_tail;
} else {
userdata->parsed_term_tail = STACK_TOP(userdata).content_list_tail;
}
userdata->stackptr--;
#ifdef LIBWWW_DEBUG_VERBOSE
xsb_dbgmsg("***In xml_pop_suppressed_element(%s): stackptr=%d",
RequestID(userdata->request), userdata->stackptr);
if (userdata->stackptr >= 0)
print_prolog_term(STACK_TOP(userdata).content_list_tail, "content_list_tail");
else
print_prolog_term(userdata->parsed_term_tail, "parsed_term_tail");
#endif
return;
}
PRIVATE USERDATA *xml_create_userData(XML_Parser parser,
HTRequest *request,
HTStream *target_stream)
{
USERDATA *me = NULL;
#ifdef LIBWWW_DEBUG
xsb_dbgmsg("***Start xml_create_userData: Request %s", RequestID(request));
#endif
if (parser) {
/* make sure that MIME type is appropriate for XML */
if (!verifyMIMEformat(request, XMLPARSE)) {
/*
HTStream * input = HTRequest_inputStream(request);
(*input->isa->abort)(input, NULL);
HTRequest_setInputStream(request,NULL);
HTRequest_kill(request);
return NULL;
*/
xsb_abort("[LIBWWW_REQUEST] Bug: Request type/MIME type mismatch");
}
if ((me = (USERDATA *) HT_CALLOC(1, sizeof(USERDATA))) == NULL)
HT_OUTOFMEM("libwww_parse_xml");
me->delete_method = xml_delete_userData;
me->parser = parser;
me->request = request;
me->target = target_stream;
me->suppress_is_default =
((REQUEST_CONTEXT *)HTRequest_context(request))->suppress_is_default;
me->parsed_term = p2p_new();
c2p_list(me->parsed_term);
me->parsed_term_tail = me->parsed_term;
SETUP_STACK(me);
}
#ifdef LIBWWW_DEBUG
xsb_dbgmsg("***End xml_create_userData: Request %s", RequestID(request));
#endif
/* Hook up userdata to the request context */
((REQUEST_CONTEXT *)HTRequest_context(request))->userdata = (void *)me;
return me;
}
PRIVATE void xml_delete_userData(void *userdata)
{
prolog_term parsed_result, status_term;
USERDATA *me = (USERDATA *)userdata;
HTRequest *request = me->request;
if (me->request) {
parsed_result =
((REQUEST_CONTEXT *)HTRequest_context(request))->request_result;
status_term =
((REQUEST_CONTEXT *)HTRequest_context(request))->status_term;
} else return;
#ifdef LIBWWW_DEBUG
xsb_dbgmsg("***In xml_delete_userData(%s): stackptr=%d",
RequestID(request), me->stackptr);
#endif
/* if the status code says the doc was loaded fine, but stackptr is != -1,
it means the doc is ill-formed */
if (me->stackptr >= 0 && (me->status == HT_LOADED)) {
c2p_int(WWW_DOC_SYNTAX,status_term);
}
/* terminate the parsed prolog terms list */
c2p_nil(me->parsed_term_tail);
/* pass the result to the outside world */
if (is_var(me->parsed_term))
p2p_unify(parsed_result, me->parsed_term);
else
xsb_abort("[LIBWWW_REQUEST] Request %s: Arg 4 (Result) must be unbound variable",
RequestID(request));
if (me->target) FREE_TARGET(me);
if (me->stack) HT_FREE(me->stack);
HT_FREE(me);
#ifdef LIBWWW_DEBUG
xsb_dbgmsg("***Request %s: freed the USERDATA object", RequestID(request));
#endif
return;
}
/* Unused handlers, which might get used later in the development */
PRIVATE void xml_processingInstruction (void * userData,
const XML_Char * target,
const XML_Char * data)
{
return;
}
/*
** This is called for a declaration of an unparsed (NDATA)
** entity. The base argument is whatever was set by XML_SetBase.
** The entityName, systemId and notationName arguments will never be null.
** The other arguments may be.
*/
PRIVATE void xml_unparsedEntityDecl (void * userData,
const XML_Char * entityName,
const XML_Char * base,
const XML_Char * systemId,
const XML_Char * publicId,
const XML_Char * notationName)
{
return;
}
/*
** This is called for a declaration of notation.
** The base argument is whatever was set by XML_SetBase.
** The notationName will never be null. The other arguments can be.
*/
PRIVATE void xml_notationDecl (void * userData,
const XML_Char * notationName,
const XML_Char * base,
const XML_Char * systemId,
const XML_Char * publicId)
{
return;
}
/*
** This is called for a reference to an external parsed general entity. The
** referenced entity is not automatically parsed. The application can parse it
** immediately or later using XML_ExternalEntityParserCreate. The parser
** argument is the parser parsing the entity containing the reference; it can
** be passed as the parser argument to XML_ExternalEntityParserCreate. The
** systemId argument is the system identifier as specified in the entity
** declaration; it will not be null. The base argument is the system
** identifier that should be used as the base for resolving systemId if
** systemId was relative; this is set by XML_SetBase; it may be null. The
** publicId argument is the public identifier as specified in the entity
** declaration, or null if none was specified; the whitespace in the public
** identifier will have been normalized as required by the XML spec. The
** openEntityNames argument is a space-separated list of the names of the
** entities that are open for the parse of this entity (including the name of
** the referenced entity); this can be passed as the openEntityNames argument
** to XML_ExternalEntityParserCreate; openEntityNames is valid only until the
** handler returns, so if the referenced entity is to be parsed later, it must
** be copied. The handler should return 0 if processing should not continue
** because of a fatal error in the handling of the external entity. In this
** case the calling parser will return an XML_ERROR_EXTERNAL_ENTITY_HANDLING
** error. Note that unlike other handlers the first argument is the parser,
** not userData. */
PRIVATE int xml_externalEntityRef (XML_Parser parser,
const XML_Char *openEntityNames,
const XML_Char *base,
const XML_Char *systemId,
const XML_Char *publicId)
{
XML_Parser extParser =
XML_ExternalEntityParserCreate(parser, openEntityNames, 0);
HTAnchor *anchor = NULL;
HTRequest *request = HTRequest_new();
char *uri;
USERDATA *userdata = XML_GetUserData(parser);
HTRequest *parent_request = userdata->request;
char *cwd = HTGetCurrentDirectoryURL();
USERDATA *subuserdata;
uri = HTParse((char *)systemId, cwd, PARSE_ALL);
anchor = HTAnchor_findAddress(uri);
HTRequest_setOutputFormat(request, WWW_SOURCE);
set_subrequest_context(parent_request,request,xml_push_dummy(userdata));
setup_termination_filter(request,xml_entity_termination_handler);
subuserdata = xml_create_userData(extParser, request, NULL);
XML_SetUserData(extParser, (void *) subuserdata);
total_number_of_requests++;
#ifdef LIBWWW_DEBUG
xsb_dbgmsg("***In xml_externalEntityRef(%s): uri=%s", RequestID(request), uri);
#endif
/* libwww breaks when a local file request is issued concurrently with an
existing request to another local file. So, we ignore such subrequests. */
if ((strncmp(uri,"file:/",6) != 0) || !HTRequest_preemptive(parent_request)) {
if (strncmp(uri,"file:/",6) == 0)
HTRequest_setPreemptive(request,YES);
HTLoadAnchor(anchor,request);
} else {
HTRequest_setAnchor(request,anchor);
xml_entity_termination_handler(request,NULL,NULL,WWW_EXTERNAL_ENTITY);
}
HT_FREE(uri);
HT_FREE(cwd);
return TRUE;
}
/*
** This is called for an encoding that is unknown to the parser.
** The encodingHandlerData argument is that which was passed as the
** second argument to XML_SetUnknownEncodingHandler.
** The name argument gives the name of the encoding as specified in
** the encoding declaration.
** If the callback can provide information about the encoding,
** it must fill in the XML_Encoding structure, and return 1.
** Otherwise it must return 0.
** If info does not describe a suitable encoding,
** then the parser will return an XML_UNKNOWN_ENCODING error.
*/
PRIVATE int xml_unknownEncoding (void *encodingHandlerData,
const XML_Char *name,
XML_Encoding *info)
{
return 0;
}
/* Default is commented out so that expat will parse entities.
PRIVATE void xml_default (void * userData, const XML_Char * str, int len)
{
XSB_StrDefine(unparsed);
XSB_StrEnsureSize(&unparsed, len+1);
strncpy(unparsed.string, str, len);
unparsed.length = len;
XSB_StrNullTerminate(&unparsed);
#ifdef LIBWWW_DEBUG
xsb_dbgmsg("***In xml_default: Request: %s: Unparsed: %s",
RequestID(((USERDATA *)userData)->request), unparsed.string);
#endif
return;
}
*/
/* Pushes an open prolog term onto the stack and return that term */
PRIVATE prolog_term xml_push_dummy(USERDATA *userdata)
{
prolog_term location;
if (userdata->stackptr < 0)
location = userdata->parsed_term_tail;
else
location = STACK_TOP(userdata).content_list_tail;
userdata->stackptr++;
#ifdef LIBWWW_DEBUG
xsb_dbgmsg("***In xml_push_dummy(%s): stackptr=%d",
RequestID(userdata->request), userdata->stackptr);
#endif
CHECK_STACK_OVERFLOW(userdata);
/* wire the new elt into where it should be in the content list */
STACK_TOP(userdata).elt_term = location;
STACK_TOP(userdata).tag = "extentity";
/* insert new list cell into the tail and change content_list_tail to point
to the new tail */
if (userdata->stackptr > 0) {
STACK_PREV(userdata).content_list_tail =
p2p_cdr(STACK_PREV(userdata).content_list_tail);
c2p_list(STACK_PREV(userdata).content_list_tail);
} else {
userdata->parsed_term_tail = p2p_cdr(userdata->parsed_term_tail);
c2p_list(userdata->parsed_term_tail);
}
userdata->stackptr--;
#ifdef LIBWWW_DEBUG_VERBOSE
if (userdata->stackptr >= 0)
print_prolog_term(STACK_TOP(userdata).content_list_tail,
"content_list_tail");
else
print_prolog_term(userdata->parsed_term_tail, "parsed_term_tail");
#endif
return location;
}
int xml_entity_termination_handler(HTRequest *request,
HTResponse *response,
void *param,
int status)
{
char *ext_entity_expansion=NULL;
REQUEST_CONTEXT *context = (REQUEST_CONTEXT *)HTRequest_context(request);
USERDATA *userdata = context->userdata;
XML_Parser extParser = userdata->parser;
/* the following conditions are handled by standard libwww filters */
if (context->retry && AUTH_OR_REDIRECTION(status))
return HT_OK; /* this causes other filters to be used */
if (status==HT_LOADED) {
ext_entity_expansion = HTChunk_toCString(context->result_chunk);
#ifdef LIBWWW_DEBUG
xsb_dbgmsg("***In xml_entity_termination_handler(%s): entity=%s",
RequestID(request), ext_entity_expansion);
#endif
XML_Parse(extParser,ext_entity_expansion,strlen(ext_entity_expansion),1);
HT_FREE(ext_entity_expansion);
} else {
prolog_term request_result = p2p_car(context->request_result);
char *uri = HTAnchor_address((HTAnchor *)HTRequest_anchor(request));
#ifdef LIBWWW_DEBUG
xsb_dbgmsg("***In xml_entity_termination_handler(%s): request failed",
RequestID(request));
#endif
c2p_functor("unexpanded_entity",2,request_result);
c2p_string(uri,p2p_arg(request_result,1));
c2p_int(status,p2p_arg(request_result,2));
}
report_asynch_subrequest_status(request, status);
XML_ParserFree(extParser);
if (userdata)
(((USERDATA *)userdata)->delete_method)(userdata);
if (total_number_of_requests > 0)
total_number_of_requests--;
/* when the last request is done, stop the event loop */
if ((total_number_of_requests == 0) && event_loop_runnung) {
HTEventList_stopLoop();
event_loop_runnung = FALSE;
#ifdef LIBWWW_DEBUG
xsb_dbgmsg("***In xml_entity_termination_handler: event loop halted, status=%d, HTNetCount=%d",
status, HTNet_count());
#endif
}
return !HT_OK;
}
void set_xml_conversions()
{
/* Must delete old converter and create new. Apparently something in libwww
releases the atoms used in thes converters, which causes it to crash
in HTStreamStack() on the second call to xmlparse. */
HTPresentation_deleteAll(XML_converter);
XML_converter = HTList_new();
HTConversion_add(XML_converter,"*/*", "www/debug",
HTBlackHoleConverter, 1.0, 0.0, 0.0);
HTConversion_add(XML_converter,"message/rfc822", "*/*",
HTMIMEConvert, 1.0, 0.0, 0.0);
HTConversion_add(XML_converter,"message/x-rfc822-foot", "*/*",
HTMIMEFooter, 1.0, 0.0, 0.0);
HTConversion_add(XML_converter,"message/x-rfc822-head", "*/*",
HTMIMEHeader, 1.0, 0.0, 0.0);
HTConversion_add(XML_converter,"message/x-rfc822-cont", "*/*",
HTMIMEContinue, 1.0, 0.0, 0.0);
HTConversion_add(XML_converter,"message/x-rfc822-upgrade","*/*",
HTMIMEUpgrade, 1.0, 0.0, 0.0);
HTConversion_add(XML_converter,"message/x-rfc822-partial", "*/*",
HTMIMEPartial, 1.0, 0.0, 0.0);
HTConversion_add(XML_converter,"multipart/*", "*/*",
HTBoundary, 1.0, 0.0, 0.0);
HTConversion_add(XML_converter,"text/x-http", "*/*",
HTTPStatus_new, 1.0, 0.0, 0.0);
/* www/xml is invented for servers that don't recognize XML */
HTConversion_add(XML_converter,"text/plain", "www/xml",
HTXML_new, 1.0, 0.0, 0.0);
HTConversion_add(XML_converter,"text/html", "www/xml",
HTXML_new, 1.0, 0.0, 0.0);
HTConversion_add(XML_converter,"www/present", "www/xml",
HTXML_new, 1.0, 0.0, 0.0);
HTConversion_add(XML_converter, "text/xml", "*/*",
HTXML_new, 1.0, 0.0, 0.0);
HTConversion_add(XML_converter, "application/xml", "*/*",
HTXML_new, 1.0, 0.0, 0.0);
}
syntax highlighted by Code2HTML, v. 0.9.1