/* expat-module.c
 * S-Lang bindings for the expat library.
 * This was tested with expat versions 1.95.8 and 2.0.
 * 
 * $Id: expat-module.c,v 1.6 2008/06/01 12:02:34 paul Exp paul $
 * 
 * Copyright (c) 2006-2008 Paul Boekholt.
 * Released under the terms of the GNU GPL (version 2 or later).
 */
#include <slang.h>
#include <string.h>
#include <expat.h>

SLANG_MODULE(expat);

#define MODULE_MAJOR_VERSION	0
#define MODULE_MINOR_VERSION	5
#define MODULE_PATCH_LEVEL	0
static char *Module_Version_String = "0.5.0";
#define MODULE_VERSION_NUMBER	\
   (MODULE_MAJOR_VERSION*10000+MODULE_MINOR_VERSION*100+MODULE_PATCH_LEVEL)

/*{{{ expat type */

static int Expat_Type_Id=0;

typedef struct
{
   XML_Parser p;
   SLang_MMT_Type *mmt;		       /* parent MMT */
   SLang_Any_Type *userdata;
   SLang_Name_Type *startelement_callback;
   SLang_Name_Type *endelement_callback;
   SLang_Name_Type *characterdata_callback;
   SLang_Name_Type *default_callback;
   SLang_Name_Type *startnamespacedecl_callback;
   SLang_Name_Type *endnamespacedecl_callback;
}
expat_type;

#define DUMMY_EXPAT_TYPE 255

static void free_expat_type (expat_type *pt)
{
   XML_ParserFree(pt->p);
   SLang_free_anytype(pt->userdata);
   /* SLang_free_function is just a placeholder for SLang 3 */
   SLfree ((char *)pt);
}

static SLang_MMT_Type *allocate_expat_type (XML_Parser p)
{
   expat_type *pt;
   SLang_MMT_Type *mmt;
   
   pt = (expat_type *) SLmalloc (sizeof (expat_type));
   if (pt == NULL)
     return NULL;
   memset ((char *) pt, 0, sizeof (expat_type));
   
   pt->p = p;
   
   if (NULL == (mmt = SLang_create_mmt (Expat_Type_Id, (VOID_STAR) pt)))
     {
	free_expat_type (pt);
	return NULL;
     }
   pt->mmt = mmt;
   return mmt;
}

static void destroy_expat (SLtype type, VOID_STAR f)
{
   (void) type;
   free_expat_type ((expat_type *) f);
}
/*}}}*/
/*{{{ exceptions*/

static int Expat_Error = 0;

static int  XML_No_Memory_Error,
  XML_Syntax_Error,
  XML_No_Elements_Error,
  XML_Invalid_Token_Error,
  XML_Unclosed_Token_Error,
  XML_Partial_Char_Error,
  XML_Tag_Mismatch_Error,
  XML_Duplicate_Attribute_Error,
  XML_Junk_After_Doc_Element_Error,
  XML_Param_Entity_Ref_Error,
  XML_Undefined_Entity_Error,
  XML_Recursive_Entity_Ref_Error,
  XML_Async_Entity_Error,
  XML_Bad_Char_Ref_Error,
  XML_Binary_Entity_Ref_Error,
  XML_Attribute_External_Entity_Ref_Error,
  XML_Misplaced_Xml_Pi_Error,
  XML_Unknown_Encoding_Error,
  XML_Incorrect_Encoding_Error,
  XML_Unclosed_Cdata_Section_Error,
  XML_External_Entity_Handling_Error,
  XML_Not_Standalone_Error,
  XML_Unexpected_State_Error,
  XML_Entity_Declared_In_Pe_Error,
  XML_Feature_Requires_Xml_Dtd_Error,
  XML_Cant_Change_Feature_Once_Parsing_Error,
  XML_Unbound_Prefix_Error,
  XML_Undeclaring_Prefix_Error,
  XML_Incomplete_Pe_Error,
  XML_Xml_Decl_Error,
  XML_Text_Decl_Error,
  XML_Publicid_Error,
  XML_Suspended_Error,
  XML_Not_Suspended_Error,
  XML_Aborted_Error,
  XML_Finished_Error,
  XML_Suspend_Pe_Error;


typedef struct
{
   int error_code;
   int *errcode_ptr;
   char *name;
   char *description;
}
Expat_Exception_Table_Type;

static const Expat_Exception_Table_Type Expat_Exception_Table [] = 
{
     { XML_ERROR_NO_MEMORY, &XML_No_Memory_Error, "ExpatNoMemoryError", "out of memory"},
     { XML_ERROR_SYNTAX, &XML_Syntax_Error, "ExpatSyntaxError", "syntax error"},
     { XML_ERROR_NO_ELEMENTS, &XML_No_Elements_Error, "ExpatNoElementsError", "no element found"},
     { XML_ERROR_INVALID_TOKEN, &XML_Invalid_Token_Error, "ExpatInvalidTokenError", "not well-formed (invalid token)"},
     { XML_ERROR_UNCLOSED_TOKEN, &XML_Unclosed_Token_Error, "ExpatUnclosedTokenError", "unclosed token"},
     { XML_ERROR_PARTIAL_CHAR, &XML_Partial_Char_Error, "ExpatPartialCharError", "partial character"},
     { XML_ERROR_TAG_MISMATCH, &XML_Tag_Mismatch_Error, "ExpatTagMismatchError", "mismatched tag"},
     { XML_ERROR_DUPLICATE_ATTRIBUTE, &XML_Duplicate_Attribute_Error, "ExpatDuplicateAttributeError", "duplicate attribute"},
     { XML_ERROR_JUNK_AFTER_DOC_ELEMENT, &XML_Junk_After_Doc_Element_Error, "ExpatJunkAfterDocElementError", "junk after document element"},
     { XML_ERROR_PARAM_ENTITY_REF, &XML_Param_Entity_Ref_Error, "ExpatParamEntityRefError", "illegal parameter entity reference"},
     { XML_ERROR_UNDEFINED_ENTITY, &XML_Undefined_Entity_Error, "ExpatUndefinedEntityError", "undefined entity"},
     { XML_ERROR_RECURSIVE_ENTITY_REF, &XML_Recursive_Entity_Ref_Error, "ExpatRecursiveEntityRefError", "recursive entity reference"},
     { XML_ERROR_ASYNC_ENTITY, &XML_Async_Entity_Error, "ExpatAsyncEntityError", "asynchronous entity"},
     { XML_ERROR_BAD_CHAR_REF, &XML_Bad_Char_Ref_Error, "ExpatBadCharRefError", "reference to invalid character number"},
     { XML_ERROR_BINARY_ENTITY_REF, &XML_Binary_Entity_Ref_Error, "ExpatBinaryEntityRefError", "reference to binary entity"},
     { XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF, &XML_Attribute_External_Entity_Ref_Error, "ExpatAttributeExternalEntityRefError", "reference to external entity in attribute"},
     { XML_ERROR_MISPLACED_XML_PI, &XML_Misplaced_Xml_Pi_Error, "ExpatMisplacedXmlPiError", "XML or text declaration not at start of entity"},
     { XML_ERROR_UNKNOWN_ENCODING, &XML_Unknown_Encoding_Error, "ExpatUnknownEncodingError", "unknown encoding"},
     { XML_ERROR_INCORRECT_ENCODING, &XML_Incorrect_Encoding_Error, "ExpatIncorrectEncodingError", "encoding specified in XML declaration is incorrect"},
     { XML_ERROR_UNCLOSED_CDATA_SECTION, &XML_Unclosed_Cdata_Section_Error, "ExpatUnclosedCdataSectionError", "unclosed CDATA section"},
     { XML_ERROR_EXTERNAL_ENTITY_HANDLING, &XML_External_Entity_Handling_Error, "ExpatExternalEntityHandlingError", "error in processing external entity reference"},
     { XML_ERROR_NOT_STANDALONE, &XML_Not_Standalone_Error, "ExpatNotStandaloneError", "document is not standalone"},
     { XML_ERROR_UNEXPECTED_STATE, &XML_Unexpected_State_Error, "ExpatUnexpectedStateError", "unexpected parser state - please send a bug report"},
     { XML_ERROR_ENTITY_DECLARED_IN_PE, &XML_Entity_Declared_In_Pe_Error, "ExpatEntityDeclaredInPeError", "entity declared in parameter entity"},
     { XML_ERROR_FEATURE_REQUIRES_XML_DTD, &XML_Feature_Requires_Xml_Dtd_Error, "ExpatFeatureRequiresXmlDtdError", "requested feature requires XML_DTD support in Expat"},
     { XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING, &XML_Cant_Change_Feature_Once_Parsing_Error, "ExpatCantChangeFeatureOnceParsingError", "cannot change setting once parsing has begun"},
     { XML_ERROR_UNBOUND_PREFIX, &XML_Unbound_Prefix_Error, "ExpatUnboundPrefixError", "unbound prefix"},
     { XML_ERROR_UNDECLARING_PREFIX, &XML_Undeclaring_Prefix_Error, "ExpatUndeclaringPrefixError", "must not undeclare prefix"},
     { XML_ERROR_INCOMPLETE_PE, &XML_Incomplete_Pe_Error, "ExpatIncompletePeError", "incomplete markup in parameter entity"},
     { XML_ERROR_XML_DECL, &XML_Xml_Decl_Error, "ExpatXmlDeclError", "XML declaration not well-formed"},
     { XML_ERROR_TEXT_DECL, &XML_Text_Decl_Error, "ExpatTextDeclError", "text declaration not well-formed"},
     { XML_ERROR_PUBLICID, &XML_Publicid_Error, "ExpatPublicidError", "illegal character(s) in public id"},
     { XML_ERROR_SUSPENDED, &XML_Suspended_Error, "ExpatSuspendedError", "parser suspended"},
     { XML_ERROR_NOT_SUSPENDED, &XML_Not_Suspended_Error, "ExpatNotSuspendedError", "parser not suspended"},
     { XML_ERROR_ABORTED, &XML_Aborted_Error, "ExpatAbortedError", "parsing aborted"},
     { XML_ERROR_FINISHED, &XML_Finished_Error, "ExpatFinishedError", "parsing finished"},
     { XML_ERROR_SUSPEND_PE, &XML_Suspend_Pe_Error, "ExpatSuspendPeError", "cannot suspend in external parameter entity"},
     { XML_ERROR_NONE, 0, 0, 0}
};

static int translate_error (int error_code)
{
   const Expat_Exception_Table_Type *b;
   if (error_code == XML_ERROR_NONE) return 0;
   b = Expat_Exception_Table;
   
   while (b->errcode_ptr != NULL)
     {
	if (b->error_code == error_code)
	  break;
	b++;
     }
   if (b->errcode_ptr == NULL) return Expat_Error;
   else return *(b->errcode_ptr);
}

static int check_error (XML_Parser p, SLang_MMT_Type *mmt)
{
   int error_code;
   int error;
   error_code = XML_GetErrorCode(p);
   if ((error = translate_error(error_code)))
     {
	SLerr_throw(error,  (char *) XML_ErrorString(error_code), Expat_Type_Id, &mmt);
	return 1;
     }
   return 0;
}

/*}}}*/
/*{{{ parser creation */

static void slxml_new(void)
{
   SLang_MMT_Type *mmt;
   XML_Parser p;
   expat_type *parser;
   
   if (NULL == (p = XML_ParserCreate(NULL))
       || (NULL == (mmt = allocate_expat_type (p))))
     {
	SLang_verror (SL_RunTime_Error, "xml_new failed");
	XML_ParserFree(p);
	return;
     }
   parser = SLang_object_from_mmt (mmt);
   XML_SetUserData(p, (void *)parser);
   if (-1 == SLang_push_mmt (mmt))
     {
	SLang_free_mmt (mmt);
	(void) SLang_push_null();
     }
}

static void slxml_new_ns(const char *sep)
{
   SLang_MMT_Type *mmt;
   XML_Parser p;
   expat_type *parser;
   
   if (NULL == (p = XML_ParserCreateNS(NULL, *sep))
       || (NULL == (mmt = allocate_expat_type (p))))
     {
	SLang_verror (SL_RunTime_Error, "xml_new_ns failed");
	XML_ParserFree(p);
	return;
     }
   parser = SLang_object_from_mmt (mmt);
   XML_SetUserData(p, (void *)parser);
   if (-1 == SLang_push_mmt (mmt))
     {
	SLang_free_mmt (mmt);
	(void) SLang_push_null();
     }
}

/*}}}*/
/*{{{ parsing */

static void slxml_parse(const char *s, int *isfinal)
{
   expat_type *parser;
   SLang_MMT_Type *mmt;
   if (NULL == (mmt = SLang_pop_mmt (Expat_Type_Id)))
     {
	return;
     }
   parser = SLang_object_from_mmt (mmt);
   if (XML_STATUS_OK != XML_Parse(parser->p, s, strlen(s), *isfinal))
     {
	check_error(parser->p, mmt);
     }
   SLang_free_mmt (mmt);
}

static void slxml_stop_parser(int *resumable)
{
   expat_type *parser;
   SLang_MMT_Type *mmt;
   if (NULL == (mmt = SLang_pop_mmt (Expat_Type_Id)))
     {
	return;
     }
   parser = SLang_object_from_mmt (mmt);
   if (XML_STATUS_OK != XML_StopParser(parser->p, *resumable))
     {
	check_error(parser->p, mmt);
     }
   SLang_free_mmt (mmt);
}

static void slxml_resume_parser (void)
{
   expat_type *parser;
   SLang_MMT_Type *mmt;
   if (NULL == (mmt = SLang_pop_mmt (Expat_Type_Id)))
     {
	return;
     }
   parser = SLang_object_from_mmt (mmt);
   if (XML_STATUS_OK != XML_ResumeParser(parser->p))
     {
	check_error(parser->p, mmt);
     }
   SLang_free_mmt (mmt);
}

/*}}}*/
/*{{{ userdata */
static void slxml_setuserdata(void)
{
   SLang_MMT_Type *mmt;
   expat_type *parser;
   SLang_Any_Type *userdata;
   
   (void) SLang_pop_anytype (&userdata);
   
   if (NULL == (mmt = SLang_pop_mmt (Expat_Type_Id)))
     {
	return;
     }
   parser = SLang_object_from_mmt (mmt);
   
   SLang_free_anytype (parser->userdata);
   parser->userdata = NULL;
   parser->userdata = userdata;
}

static void slxml_getuserdata(void)
{
   SLang_MMT_Type *mmt;
   expat_type *parser;
   
   if (NULL == (mmt = SLang_pop_mmt (Expat_Type_Id)))
     {
	SLang_push_null();
	return;
     }
   parser = SLang_object_from_mmt (mmt);
   
   (void) SLang_push_anytype (parser->userdata);
}

/*}}}*/
/*{{{ callbacks */
/*{{{ startelementhandler */
/*{{{ attribute structure */

typedef struct
{
   const char *name;
   const char *value;
}
Attribute_Type;

static SLang_CStruct_Field_Type Attribute_Type_Layout [] =
{
   MAKE_CSTRUCT_FIELD(Attribute_Type, name, "name", SLANG_STRING_TYPE, 0),
   MAKE_CSTRUCT_FIELD(Attribute_Type, value, "value", SLANG_STRING_TYPE, 0),
   SLANG_END_CSTRUCT_TABLE
};

static int push_attributes (const char **attributes)
{
   SLindex_Type size;
   SLang_Array_Type *at;
   SLang_Struct_Type *st;
   int ww;
   const char **attribute = attributes;
   for (size = 0; *attribute; size++)
     attribute +=2;
   
   if (NULL == (at = SLang_create_array (SLANG_STRUCT_TYPE, 0, NULL, &size, 1)))
     return -1;
   
   attribute = attributes;
   
   for (ww = 0; ww < size; ww++)
     {
	/* Is this correct? */
	Attribute_Type si;
	si.name = *attribute;
	attribute++;
	si.value = *attribute;
	attribute++;
	if ((-1 == SLang_push_cstruct ((VOID_STAR)&si, Attribute_Type_Layout))
	    || (-1 == SLang_pop_struct (&st))
	    || (-1 == SLang_set_array_element (at, &ww, &st)))
	  {
	     SLang_free_array (at);
	     return -1;
	  }
	SLang_free_struct(st);
     }
   return SLang_push_array(at,1);
}

/*}}}*/


static void startelementhandler(void *userData, const char *name, const char **atts)
{
   if ((-1 == SLang_start_arg_list ())
       || (-1 == SLang_push_mmt(((expat_type *)userData)->mmt))
       || (-1 == SLang_push_string ((char *)name))
       || (-1 == push_attributes(atts))
       || (-1 == SLang_end_arg_list ()))
     {
	SLang_verror(SL_RunTime_Error, "startelementhandler failed");
	XML_StopParser(((expat_type *)userData)->p, XML_FALSE);
	return;
     }
   (void) SLexecute_function (((expat_type *)userData)->startelement_callback);
   if (SLang_get_error()) {
      XML_StopParser(((expat_type *)userData)->p, XML_FALSE);
   }
}

static int pop_function_or_null(SLang_Name_Type **func)
{
   if (SLang_peek_at_stack () == SLANG_NULL_TYPE)
     {
	(void) SLang_pop_null ();
	*func = NULL;
     }
   else if (NULL == (*func = SLang_pop_function ()))
     return 0;
   return 1;
}

static void slxml_set_start_element_handler (void)
{
   SLang_MMT_Type *mmt;
   expat_type *parser;
   SLang_Name_Type *func;
   
   if (!pop_function_or_null(&func)) return;
   
   if (NULL == (mmt = SLang_pop_mmt (Expat_Type_Id)))
     {
	return;
     }
   
   parser = SLang_object_from_mmt (mmt);
   
   parser->startelement_callback = func;
   XML_SetStartElementHandler(parser->p, (func ? startelementhandler : NULL));
}

/*}}}*/
/*{{{ endelementhandler */

static void endelementhandler(void *userData, const char *name)
{
   if ((-1 == SLang_start_arg_list ())
       || (-1 == SLang_push_mmt(((expat_type *)userData)->mmt))
       || (-1 == SLang_push_string ((char *)name))
       || (-1 == SLang_end_arg_list ()))
     {
	SLang_verror(SL_RunTime_Error, "endelementhandler failed");
	XML_StopParser(((expat_type *)userData)->p, XML_FALSE);
	return;
     }
   (void) SLexecute_function (((expat_type *)userData)->endelement_callback);
   if (SLang_get_error()) {
      XML_StopParser(((expat_type *)userData)->p, XML_FALSE);
   }
}

static void slxml_set_end_element_handler (void)
{
   SLang_MMT_Type *mmt;
   expat_type *parser;
   SLang_Name_Type *func;
   
   if (!pop_function_or_null(&func)) return;
   
   if (NULL == (mmt = SLang_pop_mmt (Expat_Type_Id)))
     {
	return;
     }
   parser = SLang_object_from_mmt (mmt);
   
   parser->endelement_callback = func;
   XML_SetEndElementHandler(parser->p, (func ? endelementhandler : NULL));
}

/*}}}*/
/*{{{ characterdata handler */

static void characterdatahandler(void *userData, const char *s, const int len)
{
   char *str;
   if ((-1 == SLang_start_arg_list ())
       || (NULL == (str = SLang_create_nslstring ((char *)s, len)))
       || (-1 == SLang_push_mmt(((expat_type *)userData)->mmt))
       || (-1 == SLang_push_string(str))
       || (-1 == SLang_end_arg_list ()))
     {
	SLang_verror(SL_RunTime_Error, "characterdatahandler failed");
	XML_StopParser(((expat_type *)userData)->p, XML_FALSE);
	return;
     }
   (void) SLexecute_function (((expat_type *)userData)->characterdata_callback);
   if (SLang_get_error()) {
      XML_StopParser(((expat_type *)userData)->p, XML_FALSE);
   }
   SLang_free_slstring(str);
}

static void slxml_set_character_data_handler (void)
{
   SLang_MMT_Type *mmt;
   expat_type *parser;
   SLang_Name_Type *func;
   
   if (!pop_function_or_null(&func)) return;
   
   if (NULL == (mmt = SLang_pop_mmt (Expat_Type_Id)))
     {
	return;
     }
   
   parser = SLang_object_from_mmt (mmt);
   
   parser->characterdata_callback = func;
   XML_SetCharacterDataHandler(parser->p, (func ? characterdatahandler : NULL));
}

/*}}}*/
/*{{{ startnamespace handler */

static void startnamespacedeclhandler(void *userData, const char *prefix, const char *uri)
{
   if ((-1 == SLang_start_arg_list ())
       || (-1 == SLang_push_mmt(((expat_type *)userData)->mmt))
       || (-1 == SLang_push_string((char *)prefix))
       || (-1 == SLang_push_string((char *)uri))
       || (-1 == SLang_end_arg_list ()))
     {
	SLang_verror(SL_RunTime_Error, "startnamespacedeclhandler failed");
	XML_StopParser(((expat_type *)userData)->p, XML_FALSE);
	return;
     }
   (void) SLexecute_function (((expat_type *)userData)->startnamespacedecl_callback);
   if (SLang_get_error()) {
      XML_StopParser(((expat_type *)userData)->p, XML_FALSE);
   }
}

static void slxml_set_start_namespace_decl_handler (void)
{
   SLang_MMT_Type *mmt;
   expat_type *parser;
   SLang_Name_Type *func;
   
   if (!pop_function_or_null(&func)) return;
   
   if (NULL == (mmt = SLang_pop_mmt (Expat_Type_Id)))
     {
	return;
     }
   
   parser = SLang_object_from_mmt (mmt);
   
   parser->startnamespacedecl_callback = func;
   XML_SetStartNamespaceDeclHandler(parser->p, (func ? startnamespacedeclhandler : NULL));
}

/*}}}*/
/*{{{ endnamespace handler */

static void endnamespacedeclhandler(void *userData, const char *prefix)
{
   if ((-1 == SLang_start_arg_list ())
       || (-1 == SLang_push_mmt(((expat_type *)userData)->mmt))
       || (-1 == (SLang_push_string((char *)prefix)))
       || (-1 == SLang_end_arg_list ()))
     {
	SLang_verror(SL_RunTime_Error, "endnamespacedeclhandler failed");
	XML_StopParser(((expat_type *)userData)->p, XML_FALSE);
	return;
     }
   (void) SLexecute_function (((expat_type *)userData)->endnamespacedecl_callback);
   if (SLang_get_error()) {
      XML_StopParser(((expat_type *)userData)->p, XML_FALSE);
   }
}

static void slxml_set_end_namespace_decl_handler (void)
{
   SLang_MMT_Type *mmt;
   expat_type *parser;
   SLang_Name_Type *func;
   
   if (!pop_function_or_null(&func)) return;
   
   if (NULL == (mmt = SLang_pop_mmt (Expat_Type_Id)))
     {
	return;
     }
   
   parser = SLang_object_from_mmt (mmt);
   
   parser->endnamespacedecl_callback = func;
   XML_SetEndNamespaceDeclHandler(parser->p, (func ? endnamespacedeclhandler : NULL));
}

/*}}}*/
/*{{{ default handler */

static void defaulthandler(void *userData, const char *s, const int len)
{
   char *str;
   if ((-1 == SLang_start_arg_list ())
       || (NULL == (str = SLang_create_nslstring ((char *)s, len)))
       || (-1 == SLang_push_mmt(((expat_type *)userData)->mmt))
       || (-1 == SLang_push_string(str))
       || (-1 == SLang_end_arg_list ()))
     {
	SLang_verror(SL_RunTime_Error, "defaulthandler failed");
	XML_StopParser(((expat_type *)userData)->p, XML_FALSE);
	return;
     }
   (void) SLexecute_function (((expat_type *)userData)->default_callback);
   if (SLang_get_error()) {
      XML_StopParser(((expat_type *)userData)->p, XML_FALSE);
   }
   SLang_free_slstring(str);
}

static void slxml_set_default_handler (void)
{
   SLang_MMT_Type *mmt;
   expat_type *parser;
   SLang_Name_Type *func;
   
   if (!pop_function_or_null(&func)) return;
   
   if (NULL == (mmt = SLang_pop_mmt (Expat_Type_Id)))
     {
	return;
     }
   
   parser = SLang_object_from_mmt (mmt);
   
   parser->default_callback = func;
   XML_SetDefaultHandler(parser->p, (func ? defaulthandler : NULL));
}

/*}}}*/
/*}}}*/
/*{{{ sget */

static SLang_CStruct_Field_Type Status_Type_Layout [] =
{
   MAKE_CSTRUCT_FIELD(XML_ParsingStatus, parsing, "parsing", SLANG_INT_TYPE, 0),
   MAKE_CSTRUCT_FIELD(XML_ParsingStatus, finalBuffer, "finalbuffer", SLANG_INT_TYPE, 0),
   SLANG_END_CSTRUCT_TABLE
};


/*
 * sget function.
 * It's possible to call the handlers as methods of an expat object - e.g.
 * parser.characterdatahandler("hello world");
 * This will give an error if parser.characterdatahandler is NULL.
 */
static int expat_sget (SLtype type, char *name)
{
   SLang_MMT_Type *mmt;
   expat_type *parser;
   int status = 0;
   
   (void) type;
   
   if (NULL == (mmt = SLang_pop_mmt (Expat_Type_Id)))
     return -1;
   parser = SLang_object_from_mmt (mmt);
   
   switch(*name)
     {
      case 'u':
	if (!strcmp (name, "userdata"))
	  {
	     status = SLang_push_anytype (parser->userdata);
	     break;
	  }
      case 's':
	if (!strcmp(name, "startelementhandler"))
	  {
	     status = SLang_push_function (parser->startelement_callback);
	     break;
	  }
	else if (!strcmp(name, "startnamespacedeclhandler"))
	  {
	     status = SLang_push_function (parser->startnamespacedecl_callback);
	     break;
	  }
      case 'e':
	if (!strcmp(name, "endelementhandler"))
	  {
	     status = SLang_push_function (parser->endelement_callback);
	     break;
	  }
	else if (!strcmp(name, "endnamespacedeclhandler"))
	  {
	     status = SLang_push_function (parser->endnamespacedecl_callback);
	     break;
	  }
	else if (!strcmp(name, "errorcode"))
	  {
	     status = SLang_push_integer(translate_error(XML_GetErrorCode(parser->p)));
	     break;
	  }
	else if (!strcmp(name, "errorstring"))
	  {
	     status = SLang_push_string((char *)XML_ErrorString(XML_GetErrorCode(parser->p)));
	     break;
	  }
      case 'c':	
	if (!strcmp(name, "characterdatahandler"))
	  {
	     status = SLang_push_function (parser->characterdata_callback);
	     break;
	  }
	else if (!strcmp(name, "currentbyteindex"))
	  {
#ifdef XML_LARGE_SIZE
	     status = SLang_push_long_long(XML_GetCurrentByteIndex(parser->p));
#else
	     status = SLang_push_long(XML_GetCurrentByteIndex(parser->p));
#endif
	     break;
	  }
	else if (!strcmp(name, "currentline"))
	  {
#ifdef XML_LARGE_SIZE
	     status = SLang_push_ulong_long((unsigned long long)XML_GetCurrentLineNumber(parser->p));
#else
	     status = SLang_push_ulong((unsigned long)XML_GetCurrentLineNumber(parser->p));
#endif
	     break;
	  }
	else if (!strcmp(name, "currentcolumn"))
	  {
#ifdef XML_LARGE_SIZE
	     status = SLang_push_ulong_long((unsigned long long)XML_GetCurrentColumnNumber(parser->p));
#else
	     status = SLang_push_ulong((unsigned long)XML_GetCurrentColumnNumber(parser->p));
#endif
	     break;
	  }
      case 'p':
	  {
	     if (!strcmp(name, "parsingstatus"))
	       {
		  XML_ParsingStatus parsingstatus;
		  XML_GetParsingStatus(parser->p, &parsingstatus);
		  status = SLang_push_cstruct ((VOID_STAR)&parsingstatus, Status_Type_Layout);
		  break;
	       }
	  }
      case 'd':
	if (!strcmp(name, "defaulthandler"))
	  {
	     status = SLang_push_function (parser->default_callback);
	     break;
	  }
      default:
	status = -1;
	SLang_verror (SL_NOT_IMPLEMENTED, "Expat_Type.%s is invalid", name);
     }
   
   SLang_free_mmt (mmt);
   return status;
}

/*}}}*/
/*{{{ sput */
static int expat_sput (SLtype type, char *name)
{
   SLang_MMT_Type *mmt;
   expat_type *parser;
   int status = 0;
   SLang_Name_Type *func;
   
   (void) type;
   
   if (NULL == (mmt = SLang_pop_mmt (Expat_Type_Id)))
     return -1;
   parser = SLang_object_from_mmt (mmt);
   
   switch (*name)
     {
      case 'u':
	if (!strcmp (name, "userdata"))
	  {
	     SLang_Any_Type *userdata;
	     
	     (void) SLang_pop_anytype (&userdata);
	     
	     SLang_free_anytype (parser->userdata);
	     parser->userdata = NULL;
	     parser->userdata = userdata;
	     break;
	  }
      case 's':
	if (!strcmp (name, "startelementhandler"))
	  {
	     if (!pop_function_or_null(&func))  return -1;
	     parser->startelement_callback = func;
	     XML_SetStartElementHandler(parser->p, (func ? startelementhandler : NULL));
	     break;
	  }
	else if (!strcmp (name, "startnamespacedeclhandler"))
	  {
	     if (!pop_function_or_null(&func))  return -1;
	     parser->startnamespacedecl_callback = func;
	     XML_SetStartNamespaceDeclHandler(parser->p, (func ? startnamespacedeclhandler : NULL));
	     break;
	  }
      case 'e':
	if (!strcmp (name, "endelementhandler"))
	  {
	     if (!pop_function_or_null(&func))  return -1;
	     parser->endelement_callback = func;
	     XML_SetEndElementHandler(parser->p, (func ? endelementhandler : NULL));
	     break;
	  }
	else if (!strcmp (name, "endnamespacedeclhandler"))
	  {
	     if (!pop_function_or_null(&func))  return -1;
	     parser->endnamespacedecl_callback = func;
	     XML_SetEndNamespaceDeclHandler(parser->p, (func ? endnamespacedeclhandler : NULL));
	     break;
	  }
      case 'c':
	if (!strcmp (name, "characterdatahandler"))
	  {
	     if (!pop_function_or_null(&func))  return -1;
	     parser->characterdata_callback = func;
	     XML_SetCharacterDataHandler(parser->p, (func ? characterdatahandler : NULL));
	     break;
	  }
      case 'd':
	if (!strcmp (name, "defaulthandler"))
	  {
	     if (!pop_function_or_null(&func))  return -1;
	     parser->default_callback = func;
	     XML_SetDefaultHandler(parser->p, (func ? defaulthandler : NULL));
	     break;
	  }
      default:
	status = -1;
	SLang_verror (SL_NOT_IMPLEMENTED, "Expat_Type.%s is invalid", name);
     }
   SLang_free_mmt (mmt);
   return status;
}


/*}}}*/
/*{{{ intrinsics */

static SLang_Intrin_Fun_Type Module_Intrinsics [] =
{
   MAKE_INTRINSIC_0("xml_new", slxml_new, SLANG_VOID_TYPE),
   MAKE_INTRINSIC_1("xml_new_ns", slxml_new_ns, SLANG_VOID_TYPE, SLANG_CHAR_TYPE),
   MAKE_INTRINSIC_SI("xml_parse", slxml_parse, SLANG_VOID_TYPE),
   MAKE_INTRINSIC_I("xml_stop_parser", slxml_stop_parser, SLANG_VOID_TYPE),
   MAKE_INTRINSIC_0("xml_resume_parser", slxml_resume_parser, SLANG_VOID_TYPE),
   MAKE_INTRINSIC_0("xml_set_user_data", slxml_setuserdata, SLANG_VOID_TYPE),
   MAKE_INTRINSIC_0("xml_get_user_data", slxml_getuserdata, SLANG_VOID_TYPE),
   MAKE_INTRINSIC_0("xml_set_start_element_handler", slxml_set_start_element_handler, SLANG_VOID_TYPE),
   MAKE_INTRINSIC_0("xml_set_end_element_handler", slxml_set_end_element_handler, SLANG_VOID_TYPE),
   MAKE_INTRINSIC_0("xml_set_character_data_handler", slxml_set_character_data_handler, SLANG_VOID_TYPE),
   MAKE_INTRINSIC_0("xml_set_default_handler", slxml_set_default_handler, SLANG_VOID_TYPE),
   MAKE_INTRINSIC_0("xml_set_start_namespace_decl_handler", slxml_set_start_namespace_decl_handler, SLANG_VOID_TYPE),
   MAKE_INTRINSIC_0("xml_set_end_namespace_decl_handler", slxml_set_end_namespace_decl_handler, SLANG_VOID_TYPE),
   SLANG_END_INTRIN_FUN_TABLE
};

static SLang_Intrin_Var_Type Module_Variables [] =
{
   MAKE_VARIABLE("_expat_module_version_string", &Module_Version_String, SLANG_STRING_TYPE, 1),
   SLANG_END_INTRIN_VAR_TABLE
};

static SLang_IConstant_Type Module_Constants [] =
{
   MAKE_ICONSTANT("_expat_module_version", MODULE_VERSION_NUMBER),
   MAKE_ICONSTANT("XML_INITIALIZED", XML_INITIALIZED),
   MAKE_ICONSTANT("XML_PARSING", XML_PARSING),
   MAKE_ICONSTANT("XML_FINISHED", XML_FINISHED),
   MAKE_ICONSTANT("XML_SUSPENDED", XML_SUSPENDED),
   SLANG_END_ICONST_TABLE
};

/*}}}*/
/*{{{ register class */

static int register_expat_type (void)
{
   SLang_Class_Type *cl;
   
   if (Expat_Type_Id)
     return 0;
   
   if ((NULL == (cl = SLclass_allocate_class ("Expat_Type")))
       || (-1 == SLclass_set_destroy_function (cl, destroy_expat))
       || (-1 == SLclass_set_sget_function (cl, expat_sget))
       || (-1 == SLclass_set_sput_function(cl, expat_sput))
       || (-1 == SLclass_register_class (cl, SLANG_VOID_TYPE, sizeof (expat_type), SLANG_CLASS_TYPE_MMT)))
     return -1;
   
   Expat_Type_Id = SLclass_get_class_id (cl);
   
   if (Expat_Error == 0)
     {
	const Expat_Exception_Table_Type *b;
	b = Expat_Exception_Table;
	
	if (-1 == (Expat_Error = SLerr_new_exception (SL_RunTime_Error, "ExpatError", "Expat error")))
	  return -1;
	b++;
	while (b->errcode_ptr != NULL)
	  {
	     *b->errcode_ptr = SLerr_new_exception (Expat_Error, b->name, b->description);
	     if (*b->errcode_ptr == -1)
	       return -1;
	     b++;
	  }
     }
   
   return 0;
}

/*}}}*/
/*{{{ init */

int init_expat_module_ns (char *ns_name)
{
   SLang_NameSpace_Type *ns = SLns_create_namespace (ns_name);
   if ((ns == NULL)
       || (-1 == register_expat_type ())
       || (-1 == SLns_add_intrin_fun_table (ns, Module_Intrinsics, NULL))
       || (-1 == SLns_add_intrin_var_table (ns, Module_Variables, NULL))
       || (-1 == SLns_add_iconstant_table (ns, Module_Constants, NULL)))
     return -1;
   
   return 0;
}

/*}}}*/
