/*
 * Copyright (c) 2003-2011
 * Distributed Systems Software.  All rights reserved.
 * See the file LICENSE for redistribution information.
 */

/*****************************************************************************
 * COPYRIGHT AND PERMISSION NOTICE
 * 
 * Copyright (c) 2001-2003 The Queen in Right of Canada
 * 
 * All rights reserved.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, and/or sell
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, provided that the above copyright notice(s) and this
 * permission notice appear in all copies of the Software and that both the
 * above copyright notice(s) and this permission notice appear in supporting
 * documentation.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE 
 * BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 
 * SOFTWARE.
 * 
 * Except as contained in this notice, the name of a copyright holder shall not
 * be used in advertising or otherwise to promote the sale, use or other
 * dealings in this Software without prior written authorization of the
 * copyright holder.
 ***************************************************************************/

/*
 * Support functions for the Expat parser
 */

#ifndef lint
static const char copyright[] =
"Copyright (c) 2003-2011\n\
Distributed Systems Software.  All rights reserved.";
static const char revid[] =
  "$Id: xml.c 2528 2011-09-23 21:54:05Z brachman $";
#endif

#ifdef DSSLIB
#include "dsslib.h"
#else
#include "local.h"
#endif

static char *log_module_name = "xml";

enum {
  PARSE_XML_MAX_STACK_DEPTH = 10
};

/* XXX Fix for threads */
static void *parse_xml_stack[PARSE_XML_MAX_STACK_DEPTH];
static int parse_xml_index = -1;
static Parse_xml_error parse_xml_error = { NULL, NULL, -1, -1 };
static XML_Parser parse_xml_parser = NULL;
static int parse_xml_complete = 1;

static char *xmlns = NULL;
static char *xmlns_attr_value = NULL;

void
parse_xml_init(char *name, XML_Parser p)
{

  parse_xml_error.name = strdup(name);
  parse_xml_error.mesg = NULL;
  parse_xml_error.line = -1;
  parse_xml_error.pos = -1;
  parse_xml_index = -1;
  parse_xml_parser = p;
  parse_xml_complete = 0;
}

void
parse_xml_end(void)
{

  XML_ParserFree(parse_xml_parser);
  parse_xml_parser = NULL;
}

void
parse_xml_set_complete(void)
{

  parse_xml_complete = 1;
}

int
parse_xml_is_complete(void)
{

  return(parse_xml_complete);
}

void
parse_xml_set_error(char *mesg)
{

  if (parse_xml_parser == NULL)
	return;

  if (mesg != NULL)
	parse_xml_error.mesg = mesg;
  else {
	enum XML_Error code;

	if ((code = XML_GetErrorCode(parse_xml_parser)) != XML_ERROR_NONE)
	  parse_xml_error.mesg = (char *) XML_ErrorString(code);
	else
	  parse_xml_error.mesg = "XML parse error";
  }

  parse_xml_error.line = XML_GetCurrentLineNumber(parse_xml_parser);
  parse_xml_error.pos = XML_GetCurrentColumnNumber(parse_xml_parser);
}

int
parse_xml_is_error(Parse_xml_error *err)
{

  if (parse_xml_parser == NULL) {
	if (parse_xml_error.mesg == NULL)
	  return(0);
	*err = parse_xml_error;
	return(1);
  }

  /*
   * XXX There is a known problem in expat where the lexer's position in
   * the source document isn't maintained properly.
   * Calling XML_GetCurrentLineNumber() regularly seems to be a workaround.
   */
  XML_GetCurrentLineNumber(parse_xml_parser);

  if (err != NULL)
	*err = parse_xml_error;

  return(parse_xml_error.mesg != NULL);
}

Parse_xml_result
parse_xml_push(void *state)
{

  if (parse_xml_error.mesg != NULL || parse_xml_complete)
	return(PARSE_XML_ERROR);

  if (parse_xml_index == (PARSE_XML_MAX_STACK_DEPTH - 1)) {
	parse_xml_error.mesg = "Maximum XML stack depth reached";
	return(PARSE_XML_ERROR);
  }

  parse_xml_stack[++parse_xml_index] = state;
  return(PARSE_XML_OK);
}

Parse_xml_result
parse_xml_pop(void **state)
{

  if (parse_xml_error.mesg != NULL || parse_xml_complete)
	return(PARSE_XML_ERROR);

  if (parse_xml_index == -1) {
	parse_xml_error.mesg = "XML stack underrun";
	return(PARSE_XML_ERROR);
  }

  if (state != NULL)
	*state = parse_xml_stack[parse_xml_index];
  parse_xml_index--;

  return(PARSE_XML_OK);
}

Parse_xml_result
parse_xml_top(void **state)
{

  if (parse_xml_error.mesg != NULL || parse_xml_complete)
	return(PARSE_XML_ERROR);
  if (parse_xml_index == -1)
	return(PARSE_XML_EMPTY);

  if (state != NULL)
	*state = parse_xml_stack[parse_xml_index];
  return(PARSE_XML_OK);
}

/*
 * Return non-zero if there's nothing on the parse stack,
 * otherwise return zero.
 */
int
parse_xml_is_empty(void)
{

  return(parse_xml_top(NULL) == PARSE_XML_EMPTY);
}

/*
 * Return non-zero if there's something on the parse stack,
 * otherwise return zero.
 */
int
parse_xml_is_not_empty(void)
{

  return(parse_xml_top(NULL) == PARSE_XML_OK);
}

void
parse_xml_default_handler(void *userData, const XML_Char *s, int len)
{
  const char *p;

  if (parse_xml_is_error(NULL))
    return;

  for (p = s; p < (s + len); p++) {
    if (!isspace((int) *p))
      parse_xml_set_error("Unexpected XML or character data");
  }
}

void
parse_xml_comment_handler(void *userData, const XML_Char *data)
{

  if (parse_xml_is_error(NULL))
    return;

  /* Nothing to do */
}

/*
 * The generic attribute validation function.
 * Check that VALUE is a legitimate attribute value according to specification
 * V.  The specification allows case insensitive attributes if NOCASE is
 * non-zero; in this case, the value will be mapped to the variant that
 * appears in the specification.
 * Return a copy of the attribute value to use, or NULL on error (and
 * set ERRMSG, if non-NULL, to an error message).
 */
char *
parse_xml_attr_validator(Parse_attr_validate *v, const char *name,
						 const char *value, char **errmsg)
{
  int i;

  if (errmsg != NULL)
	*errmsg = NULL;

  if (value == NULL)
	return(NULL);

  for (i = 0; v->values[i] != NULL; i++) {
	if (v->nocase) {
	  if (strcaseeq(value, v->values[i]))
		break;
	}
	else {
	  if (streq(value, v->values[i]))
		break;
	}
  }
	
  if (v->values[i] == NULL)
	return(NULL);

  return(strdup(v->values[i]));
}

static char *attr_binary[] = {
  "yes", "no", NULL
};

Parse_attr_validate parse_xml_attr_validate_binary = {
  parse_xml_attr_validator, attr_binary, 1, NULL
};

/*
 * Check that:
 * 1. All required attributes are present
 * 2. All provided attribute names are correct
 * 3. (optionally) attribute values are valid
 * 4. (optionally) attribute names are to be ignored/discarded
 * 5. No attribute name is given more than once
 *
 * Attribute values are copied; if a validation function is provided for
 * an attribute, it is expected to malloc a copy of the attribute value.
 * If TAB is NULL, then no attributes are permitted by the DTD.
 *
 * Return -1 on error and optionally set ERRMSG to point to an error message,
 * otherwise return the number of (non-ignored) attributes found.
 *
 * Note that the ordering of the attributes is not enforced.
 *
 * XXX This might be enhanced to ignore/discard any attribute names
 * for a given set; e.g., xsi:*, xmlns:*
 */
int
parse_xml_attr(Parse_attr_tab *tab, const char **attr, char **errmsg)
{
  int a, i, n, required;

  if (tab == NULL) {
	if (attr != NULL && attr[0] != NULL) {
	  if (errmsg != NULL)
		*errmsg = "No attributes are allowed";
	  return(-1);
	}
	return(0);
  }

  /* Initialize */
  required = 0;
  for (i = 0; tab[i].type != ATTR_END; i++) {
	tab[i].count = 0;
	if (tab[i].type == ATTR_REQUIRED)
	  required = 1;
  }

  /* No attributes - is at least one attribute required? */
  if (attr == NULL) {
	if (required) {
	  if (errmsg != NULL)
		*errmsg = "Required attribute is missing";
	  return(-1);
	}
	return(0);
  }

  /* Examine each attribute, validate, copy its value. */
  n = 0;
  for (a = 0; attr[a] != NULL; a += 2) {
	int have_ns;
	char *uri, *name;

	if (attr[a + 1] == NULL) {
	  if (errmsg != NULL)
		*errmsg = ds_xprintf("No attribute value present: %s", attr[a]);
	  return(-1);
	}

	/*
	 * If the attribute name includes a namespace, we may be ignoring
	 * this attribute.
	 */
	if ((have_ns = parse_xmlns_name(attr[a], &uri, &name)) == -1) {
	  if (errmsg != NULL)
		*errmsg = ds_xprintf("Attribute namespace parse failed: %s", attr[a]);
	  return(-1);
	}

	for (i = 0; tab[i].type != ATTR_END; i++) {
	  if (have_ns && tab[i].type == ATTR_IGNORE_NS && streq(tab[i].name, uri))
		break;

	  if (streq(attr[a], tab[i].name)) {
		if (tab[i].type == ATTR_IGNORE)
		  break;

		if (tab[i].count) {
		  if (errmsg != NULL)
			*errmsg = ds_xprintf("Duplicate attribute: %s", attr[a]);
		  return(-1);
		}
		tab[i].count++;

		if (tab[i].validate != NULL) {
		  char *err, *v;

		  if ((v = (tab[i].validate->validate)(tab[i].validate,
											   attr[a], attr[a + 1],
											   &err)) == NULL) {
			if (errmsg != NULL) {
			  if (err != NULL)
				*errmsg = ds_xprintf("Invalid value for attribute \"%s\": %s",
								   attr[a], err);
			  else
				*errmsg = ds_xprintf("Invalid value for attribute name \"%s\"",
									 attr[a]);
			}
			if (tab[i].ctl == NULL || !tab[i].ctl->warn_on_invalid_syntax)
			  return(-1);
			v = strdup(attr[a + 1]);
		  }
		  *tab[i].value = v;
		}
		else
		  *tab[i].value = strdup(attr[a + 1]);
		n++;
		break;
	  }
	}

	if (tab[i].type == ATTR_END) {
	  if (errmsg != NULL)
		*errmsg = ds_xprintf("Unrecognized attribute name: \"%s\"", attr[a]);
	  return(-1);
	}
  }

  /* Are any required attributes missing? */
  for (i = 0; tab[i].type != ATTR_END; i++) {
	if (tab[i].type == ATTR_REQUIRED && tab[i].count == 0) {
	  if (errmsg != NULL)
		*errmsg = ds_xprintf("Required attribute is missing: %s", tab[i].name);
	  return(-1);
	}
  }

  return(n);
}

/*
 * Initialize the XML namespace to NS.
 * This information is used both when parsing XML and emitting XML wrt
 * the "xmlns" attribute.
 */
void
parse_xml_init_xmlns(char *ns)
{

  if (ns == NULL || *ns == '\0') {
	xmlns = "";
	xmlns_attr_value = "";
  }
  else {
	xmlns = strdup(ns);
	xmlns_attr_value = strdup(ns);
  }
}

char *
parse_xml_xmlns(void)
{

  return(xmlns_attr_value);
}

/*
 * Expat converts a namespace qualified element or attribute name into the form
 * <URI>XMLNS_SEP_CHAR<NAME>
 * For example, given:
 *   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 *   xsi:schemaLocation="... blah ..."
 * and XMLNS_SEP_CHAR defined as '|', Expat will pass us the attribute name
 *   http://www.w3.org/2001/XMLSchema-instance|schemaLocation
 * Given:
 *   xmlns="http://fedroot.com/dacs/v1.4"
 * Expat will convert an unqualified element name to the name with
 * "http://fedroot.com/dacs/v1.4|" prepended.
 *
 * Return 0 if EXPANDED_NAME does not specify a namespace (optionally
 * copying it to NAME), 1 if a namespace is specified (optionally setting URI
 * and NAME), and -1 on error.
 */
int
parse_xmlns_name(const char *expanded_name, char **uri, char **name)
{
  char *p, *nns;

  /* No namespace? */
  if ((p = strchr(expanded_name, (int) XMLNS_SEP_CHAR)) == NULL) {
	if (uri != NULL)
	  *uri = NULL;
	if (name != NULL)
	  *name = strdup(expanded_name);
	return(0);
  }

  if (p == expanded_name || *(p + 1) == '\0') {
	/* No namespace? No name? */
	return(-1);
  }

  nns = strdup(expanded_name);
  nns[p - expanded_name] = '\0';
  if (uri != NULL)
	*uri = nns;
  if (name != NULL)
	*name = nns + (p - expanded_name + 1);
  return(1);
}

typedef enum {
  XML_COMMENT_NODE   = 0,
  XML_ELEMENT_NODE   = 1,
  XML_DATA_NODE      = 2,
  XML_ROOT_NODE      = 3
} Parse_xml_node_type;

typedef struct Parse_xml_node_attr {
  char *name;
  char *value;
} Parse_xml_node_attr;

typedef struct Parse_xml_node {
  Parse_xml_node_type type;
  char *name;
  Dsvec *attrs;
  Ds *value;
  Dsvec *children;
} Parse_xml_node;

typedef struct Parse_xml_attributes {
  char *element_name;
  Parse_attr_tab tab;
} Parse_xml_attributes;

static MAYBE_UNUSED Parse_attr_tab *
lookup_attr_tab(Parse_xml_attributes *tabs, const char *element_name)
{
  int i;

  if (tabs == NULL)
	return(NULL);
  for (i = 0; tabs[i].element_name != NULL; i++) {
	if (streq(tabs[i].element_name, element_name))
	  return(&tabs[i].tab);
  }

  return(NULL);
}

char *
xml_escape_cdata(char *str)
{
  Ds *ds;
  static Strsubst xml_cdata_subst[] = {
	{ "<",  "&lt;" },
	{ "&",  "&amp;" },
	{ NULL, NULL }
  };

  ds = strsubst(str, xml_cdata_subst);

  return(ds_buf(ds));
}

char *
xml_unescape_cdata(char *str)
{
  Ds *ds;
  static Strsubst xml_cdata_subst[] = {
	{ "&lt;",  "<" },
	{ "&amp;", "&" },
	{ NULL,    NULL }
  };

  ds = strsubst(str, xml_cdata_subst);

  return(ds_buf(ds));
}

/*
 * This is the inverse of xml_attribute_value_encode().
 * XXX this only handles a subset of the cases, as general entity and
 * character reference interpolation is not done
 */
char *
xml_attribute_value_decode(char *enc_value)
{
  Ds *ds;
  static Strsubst xml_entity_subst[] = {
	{ "&lt;",  "<" },
	{ "&gt;",  ">" },
	{ "&amp;", "&" },
	{ "&quot;", "\"" },
	{ "&apos;", "'" },
	{ NULL,    NULL }
  };

  ds = strsubst(enc_value, xml_entity_subst);

  return(ds_buf(ds));
}

/*
 * The general case is where an XML attribute value is CDATA.
 * The value string can contain any text, excluding '<' and '&' characters.
 * Also, in the context of an attribute value, the type of quote that delimits
 * the attribute value must be escaped within the string.
 * The '>' character is only a problem in the context of ']]>'.
 *
 * If QUOTE_CHAR is the single quote character or double quote character,
 * that character will be escaped if it appears in VALUE; if it is neither
 * character, then the value is immaterial and neither of those characters
 * will be quoted.
 *
 * Return a properly escaped string.
 */
char *
xml_attribute_value_encode(char *value, int quote_char)
{
  char *p;
  Ds ds;

  for (p = value; *p != '\0'; p++) {
	if (*p == '<' || *p == '>' || *p == '&' || *p == quote_char)
	  break;
  }

  ds_init(&ds);
  ds_copyb(&ds, value, p - value, 0);

  while (*p != '\0') {
	if (*p == '<')
	  ds_append(&ds, "&lt;");
	else if (*p == '>')
	  ds_append(&ds, "&gt;");
	else if (*p == '&')
	  ds_append(&ds, "&amp;");
	else if (*p == quote_char) {
	  if (*p == '\'')
		ds_append(&ds, "&apos;");
	  else if (*p == '"')
		ds_append(&ds, "&quot;");
	  else {
		/* We could reject QUOTE_CHAR and fail instead... */
		ds_appendc(&ds, (int) *p);
	  }
	}
	else
	  ds_appendc(&ds, (int) *p);
	p++;
  }

  ds_appendc(&ds, '\0');

  return(ds_buf(&ds));
}

/*
 * As in Canonical XML (http://www.w3.org/TR/xml-c14n)
 */
char *
xml_cdata_canonicalize(char *value)
{
  char *p;
  Ds ds;

  for (p = value; *p != '\0'; p++) {
	if (*p == '<' || *p == '>' || *p == '&' || (*p < 0x20 && *p != '\n'))
	  break;
  }

  ds_init(&ds);
  ds_copyb(&ds, value, p - value, 0);

  while (*p != '\0') {
	if (*p == '<')
	  ds_append(&ds, "&lt;");
	else if (*p == '>')
	  ds_append(&ds, "&gt;");
	else if (*p == '&')
	  ds_append(&ds, "&amp;");
	else if ((int) *p < 0x20 && *p != '\n') {
	  ds_append(&ds, "&#x");
	  ds_append(&ds, strbtohex((unsigned char *) p, 1,
							   STRBTOHEX_UPPER | STRBTOHEX_NOZ));
	  ds_appendc(&ds, (int) ';');
	}
	else
	  ds_appendc(&ds, (int) *p);
	p++;
  }

  ds_appendc(&ds, '\0');

  return(ds_buf(&ds));
}

static MAYBE_UNUSED char *
strcanon(char *str)
{
  char *p;
  Ds ds;

  ds_init(&ds);
  p = str;
  while (*p != '\0') {
	ds_appendc(&ds, (int) *p++);
	while (isspace((int) *(p - 1)) && isspace((int) *p))
	  p++;
  }
  ds_appendc(&ds, (int) '\0');

  return(ds_buf(&ds));
}

#ifdef XML_GENERIC

static void
parse_xml_generic_show_node(Parse_xml_node *node)
{
  unsigned int ui;
  Parse_xml_node_attr **a;

  if (node->type == XML_ROOT_NODE)
	fprintf(stderr, "Root\n");
  else if (node->type == XML_ELEMENT_NODE) {
	fprintf(stderr, "<%s", node->name);
	if (node->attrs != NULL) {
	  a = (Parse_xml_node_attr **) dsvec_base(node->attrs);
	  for (ui = 0; ui < dsvec_len(node->attrs); ui++)
		fprintf(stderr, " %s=\"%s\"", a[ui]->name, a[ui]->value);
	}
	if (dsvec_len(node->children) == 0)
	  fprintf(stderr, "/>");
	else
	  fprintf(stderr, ">");
  }
  else if (node->type == XML_COMMENT_NODE) {
	fprintf(stderr, "<!--%s-->\n", ds_buf(node->value));
  }
  else if (node->type == XML_DATA_NODE) {
	fprintf(stderr, "%s", strcanon(ds_buf(node->value)));
  }

}

static void
parse_xml_generic_show(Parse_xml_node *root)
{
  unsigned int ui;
  Parse_xml_node *n, **c;

  n = root;
  parse_xml_generic_show_node(n);
  c = (Parse_xml_node **) dsvec_base(n->children);
  for (ui = 0; ui < dsvec_len(n->children); ui++)
	parse_xml_generic_show(c[ui]);
  if (n->type == XML_ELEMENT_NODE && dsvec_len(n->children) != 0)
	fprintf(stderr, "</%s>\n", n->name);
}

static void
parse_xml_generic_element_start(void *data, const char *element,
								const char **attr)
{
  int i;
  char *el;
  Parse_xml_node *new_node;
  Parse_xml_node_attr *a;

  if (parse_xmlns_name(element, NULL, &el) == -1) {
	parse_xml_set_error(ds_xprintf("Invalid namespace: %s", element));
	return;
  }

  if (parse_xml_is_error(NULL))
	return;

  new_node = ALLOC(Parse_xml_node);
  new_node->type = XML_ELEMENT_NODE;
  new_node->name = strdup(el);
  if (attr != NULL && attr[0] != NULL) {
	char *errmsg;
	Parse_attr_tab *tab;
	Parse_xml_attributes *tabs;

	tabs = (Parse_xml_attributes *) data;
	if (tabs != NULL && (tab = lookup_attr_tab(tabs, el)) != NULL) {
	  if (parse_xml_attr(tab, attr, &errmsg) == -1) {
		parse_xml_set_error(errmsg);
		return;
	  }
	}

	new_node->attrs = dsvec_init(NULL, sizeof(Parse_xml_node_attr));
	for (i = 0; attr[i] != NULL; i += 2) {
	  a = ALLOC(Parse_xml_node_attr);
	  a->name = strdup(attr[i]);
	  a->value = strdup(attr[i + 1]);
	  dsvec_add_ptr(new_node->attrs, a);
	}
  }
  else
	new_node->attrs = NULL;
  new_node->value = NULL;
  new_node->children = dsvec_init(NULL, sizeof(Parse_xml_node));

  parse_xml_push((void *) new_node);
}

static void
parse_xml_generic_element_end(void *data, const char *element)
{
  char *el;
  Parse_xml_node *node, *parent, *top;

  if (parse_xmlns_name(element, NULL, &el) == -1) {
	parse_xml_set_error(ds_xprintf("Invalid namespace: %s", element));
	return;
  }

  if (parse_xml_pop((void **) &node) == PARSE_XML_ERROR) {
	parse_xml_set_error("Excess element end node");
	return;
  }

  if (node == NULL) {
	parse_xml_set_error("NULL node?");
	return;
  }

  if (node->type == XML_DATA_NODE) {
	if (parse_xml_pop((void **) &parent) == PARSE_XML_ERROR) {
	  parse_xml_set_error("No parent for data node?");
	  return;
	}
	dsvec_add_ptr(parent->children, node);
	node = parent;
  }

  if (node->type == XML_ELEMENT_NODE) {
	if (parse_xml_top((void **) &top) != PARSE_XML_OK) {
	  parse_xml_set_error("No top node?");
	  return;
	}
	dsvec_add_ptr(top->children, node);
  }
  else if (node->type == XML_COMMENT_NODE) {
  }
  else {
	parse_xml_set_error("Unrecognized node type");
	return;
  }

}

static void
parse_xml_generic_character_data_handler(void *userData, const XML_Char *s,
										 int len)
{
  Parse_xml_node *new_node, *node;

  if (parse_xml_is_error(NULL))
	return;

  if (parse_xml_top((void **) &node) != PARSE_XML_OK) {
	parse_xml_set_error("Unexpected character data");
	return;
  }

  if (node->type == XML_DATA_NODE) {
	/* Concatenate */
	ds_concatn(node->value, (char *) s, len);
  }
  else {
	new_node = ALLOC(Parse_xml_node);
	new_node->type = XML_DATA_NODE;
	new_node->name = NULL;
	new_node->attrs = NULL;
	new_node->value = ds_init(NULL);
	new_node->children = dsvec_init(NULL, sizeof(Parse_xml_node));

	ds_concatn(new_node->value, (char *) s, len);
	parse_xml_push((void *) new_node);
  }
}

static void
parse_xml_generic_comment_handler(void *userData, const XML_Char *s)
{

}

int
parse_xml_generic(char *xml_doc, Parse_xml_attributes **attrs,
				  Parse_xml_node **node)
{
  int st;
  Parse_xml_error err;
  Parse_xml_node *top;
  XML_Parser p = XML_ParserCreateNS(NULL, XMLNS_SEP_CHAR);

  if (p == NULL)
	return(-1);

  parse_xml_init("Generic", p);

  XML_SetElementHandler(p, parse_xml_generic_element_start,
						parse_xml_generic_element_end);
  XML_SetCharacterDataHandler(p, parse_xml_generic_character_data_handler);
  XML_SetCommentHandler(p, parse_xml_generic_comment_handler);

  *node = ALLOC(Parse_xml_node);
  (*node)->type = XML_ROOT_NODE;
  (*node)->name = NULL;
  (*node)->attrs = NULL;
  (*node)->value = NULL;
  (*node)->children = dsvec_init(NULL, sizeof(Parse_xml_node));
  parse_xml_push((void *) *node);

  XML_SetUserData(p, (void *) attrs);

  st = XML_Parse(p, xml_doc, strlen(xml_doc), 1);

  if (parse_xml_is_error(&err) || st == 0) {
	if (err.mesg == NULL) {
	  parse_xml_set_error(NULL);
	  parse_xml_is_error(&err);
	}
    log_msg((LOG_ERROR_LEVEL, "parse_xml_generic: line %d, pos %d",
             err.line, err.pos));
    if (err.mesg != NULL)
      log_msg((LOG_ERROR_LEVEL, "parse_xml_generic: %s", err.mesg));
    parse_xml_end();
    return(-1);
  }

  parse_xml_end();

  if (parse_xml_top((void **) &top) != PARSE_XML_OK)
	return(-1);
  if (top->type != XML_ROOT_NODE)
	parse_xml_set_error("parse_xml_generic: parse failure");

  return(0);
}

int
parse_xml_generic_test(char *xml, int file)
{
  char *buf;
  Ds *ds;
  Parse_xml_node *node;

  if (xml == NULL) {
	ds = ds_getf(stdin);
	buf = ds_buf(ds);
  }
  else if (file) {
	if (load_file(xml, &buf, NULL) != 0)
	  return(-1);
  }
  else
	buf = xml;

  parse_xml_generic(buf, NULL, &node);
  parse_xml_generic_show(node);

  return(0);
}
#endif

#ifdef NOTDEF
static void
parse_dtd_xmldoctype(void *userData, const XML_Char *doctypeName,
					 const XML_Char *sysid, const XML_Char *pubid,
					 int has_internal_subset)
{
}

static void
parse_dtd_xmldecl(void *userData, const XML_Char *version,
				  const XML_Char *encoding, int standalone)
{
}

static void
parse_dtd_eldecl(void *userData, const XML_Char *name,
				 XML_Content *model)
{
}

static void
parse_dtd_attdecl(void *userData, const XML_Char *elname,
				  const XML_Char *attname, const XML_Char *att_type,
				  const XML_Char *dflt, int isrequired)
{
}

static void
parse_dtd_entdecl(void *userData, const XML_Char *entityName,
				  int is_parameter_entity, const XML_Char *value,
				  int value_length, const XML_Char *base,
				  const XML_Char *systemId, const XML_Char *publicId,
				  const XML_Char *notationName)
{
}

static void
parse_dtd_upentdecl(void *userData, const XML_Char *entityName, 
					const XML_Char *base, const XML_Char *systemId,
					const XML_Char *publicId, const XML_Char *notationName)
{
}

static void
parse_dtd_default_handler(void *userData, const XML_Char *s, int len)
{
}

static void
parse_dtd_comment_handler(void *userData, const XML_Char *data)
{
}

int
parse_dtd(char *dtd_doc)
{
  int st;
  Parse_xml_error err;
  Parse_xml_node *top;
  XML_Parser p = XML_ParserCreateNS(NULL, XMLNS_SEP_CHAR);

  if (p == NULL)
	return(-1);

  parse_xml_init("DTD", p);

  XML_SetParamEntityParsing(p, XML_PARAM_ENTITY_PARSING_NEVER);

  XML_SetStartDoctypeDeclHandler(p, parse_dtd_xmldoctype);
  XML_SetXmlDeclHandler(p, parse_dtd_xmldecl);
  XML_SetElementDeclHandler(p, parse_dtd_eldecl);
  XML_SetAttlistDeclHandler(p, parse_dtd_attdecl);
  XML_SetEntityDeclHandler(p, parse_dtd_entdecl);
  XML_SetUnparsedEntityDeclHandler(p, parse_dtd_upentdecl);

  XML_SetDefaultHandler(p, parse_dtd_default_handler);
  XML_SetCommentHandler(p, parse_dtd_comment_handler);

  st = XML_Parse(p, dtd_doc, strlen(dtd_doc), 1);

  if (parse_xml_is_error(&err) || st == 0) {
	if (err.mesg == NULL) {
	  parse_xml_set_error(NULL);
	  parse_xml_is_error(&err);
	}
    log_msg((LOG_ERROR_LEVEL, "parse_dtd: line %d, pos %d",
             err.line, err.pos));
    if (err.mesg != NULL)
      log_msg((LOG_ERROR_LEVEL, "parse_dtd_generic: %s", err.mesg));
    parse_xml_end();
    return(-1);
  }

  parse_xml_end();

  if (parse_xml_top((void **) &top) != PARSE_XML_OK)
	return(-1);
  if (top->type != XML_ROOT_NODE)
	parse_xml_set_error("parse_dtd_generic: parse failure");

  return(0);
}

int
parse_dtd_test(char *dtd, int file)
{
  char *buf;
  Ds *ds;

  if (dtd == NULL) {
	ds = ds_getf(stdin);
	buf = ds_buf(ds);
  }
  else if (file) {
	if (load_file(dtd, &buf, NULL) != 0)
	  return(-1);
  }
  else
	buf = dtd;

  parse_dtd(buf);

  return(0);

}
#endif
