/* constructor */
static VALUE
XMLParser_new(int argc, VALUE* argv, VALUE klass)
{
  XMLParser* parser;
  VALUE obj;
  VALUE arg1;
  VALUE arg2;
  VALUE arg3;
  int count;
  char* encoding = NULL;
#ifdef NEW_EXPAT
  char* nssep = NULL;
#endif
  char* context = NULL;
  XMLParser* rootparser = NULL;
  VALUE parent = Qnil;

  count = rb_scan_args(argc, argv, "03", &arg1, &arg2, &arg3);
  if (count == 1) {
    /* new(encoding) */
    if (TYPE(arg1) != T_NIL) {
      Check_Type(arg1, T_STRING); /* encoding */
      encoding = RSTRING_PTR(arg1);
    }
  }
  else if (count == 2) {
    /* new(encoding, nschar) */
    /* new(parser, context) */
#ifdef NEW_EXPAT
    if (TYPE(arg1) != T_DATA) {
      if (TYPE(arg1) != T_NIL) {
        Check_Type(arg1, T_STRING); /* encoding */
        encoding = RSTRING_PTR(arg1);
      }
      Check_Type(arg2, T_STRING); /* nschar */
      nssep = RSTRING_PTR(arg2);
    }
    else {
#endif
      Check_Type(arg1, T_DATA); /* parser */
      GET_PARSER(arg1, rootparser);
      if (!NIL_P(arg2)) {
        Check_Type(arg2, T_STRING); /* context */
        context = RSTRING_PTR(arg2);
      }
      parent = arg1;
#ifdef NEW_EXPAT
    }
#endif
  }
  else if (count == 3) {
    /* new(parser, context, encoding) */
    Check_Type(arg1, T_DATA); /* parser */
    GET_PARSER(arg1, rootparser);
    if (!NIL_P(arg2)) {
      Check_Type(arg2, T_STRING); /* context */
      context = RSTRING_PTR(arg2);
    }
    Check_Type(arg3, T_STRING); /* encoding */
    encoding = RSTRING_PTR(arg3);
    parent = arg1;
  }

  /* create object */
  obj = Data_Make_Struct(klass, XMLParser,
                         XMLParser_mark, XMLParser_free, parser);
  /* create parser */
  if (rootparser == NULL) {
#ifdef NEW_EXPAT
    if (nssep == NULL)
      parser->parser = XML_ParserCreate(encoding);
    else
      parser->parser = XML_ParserCreateNS(encoding, nssep[0]);
#else
    parser->parser = XML_ParserCreate(encoding);
#endif
    parser->tainted = 0;
    parser->context = NULL;
  }
  else {
    parser->parser = XML_ExternalEntityParserCreate(rootparser->parser,
                                                    context, encoding);
    /* clear all inhrited handlers,
       because handlers should be set in "parse" method  */
    XML_SetElementHandler(parser->parser, NULL, NULL);
    XML_SetCharacterDataHandler(parser->parser, NULL);
    XML_SetProcessingInstructionHandler(parser->parser, NULL);
    XML_SetDefaultHandler(parser->parser, NULL);
    XML_SetUnparsedEntityDeclHandler(parser->parser, NULL);
    XML_SetNotationDeclHandler(parser->parser, NULL);
    XML_SetExternalEntityRefHandler(parser->parser, NULL);
#ifdef NEW_EXPAT
    XML_SetCommentHandler(parser->parser, NULL);
    XML_SetCdataSectionHandler(parser->parser, NULL, NULL);
    XML_SetNamespaceDeclHandler(parser->parser, NULL, NULL);
    XML_SetNotStandaloneHandler(parser->parser, NULL);
#endif
#ifdef HAVE_XML_SETDOCTYPEDECLHANDLER
    XML_SetDoctypeDeclHandler(parser->parser, NULL, NULL);
#endif
#ifdef HAVE_EXPAT_H
    XML_SetElementDeclHandler(parser->parser, NULL);
    XML_SetAttlistDeclHandler(parser->parser, NULL);
    XML_SetXmlDeclHandler(parser->parser, NULL);
    XML_SetEntityDeclHandler(parser->parser, NULL);
#endif
#if 0
    XML_SetExternalParsedEntityDeclHandler(parser->parser, NULL);
    XML_SetInternalParsedEntityDeclHandler(parser->parser, NULL);
#endif
#ifdef HAVE_XML_SETSKIPPEDENTITYHANDLER
    XML_SetSkippedEntityHandler(parser->parser, NULL);
#endif
    if (rootparser->tainted)
      parser->tainted |= 1;
    parser->context = context;
  }
  if (!parser->parser)
    rb_raise(eXMLParserError, "cannot create parser");

  /* setting up internal data */
  XML_SetUserData(parser->parser, (void*)obj);
  parser->iterator = 0;
  parser->defaultCurrent = 0;
#ifdef NEW_EXPAT
  parser->lastAttrs = NULL;
#endif
  parser->parent = parent;
  parser->detectedEncoding = NULL;

  rb_obj_call_init(obj, argc, argv);

  return obj;
}