/* parse method */
static VALUE
XMLParser_parse(int argc, VALUE* argv, VALUE obj)
{
  XMLParser* parser;
  int ret;
  VALUE str;
  VALUE isFinal;
  int final = 1;
  int count;
  int fromStream = 0;
  ID mid = rb_intern("gets");
  ID linebuf = rb_intern("_linebuf");

  count = rb_scan_args(argc, argv, "02", &str, &isFinal);
  /* If "str" has public "gets" method, it will be considered *stream* */
  if (!rb_obj_is_kind_of(str, rb_cString) &&
      rb_method_boundp(CLASS_OF(str), mid, 1)) {
    fromStream = 1;
  }
  else if (!NIL_P(str)) {
    Check_Type(str, T_STRING);
  }
  if (count >= 2) {
    if (isFinal == Qtrue)
      final = 1;
    else if (isFinal == Qfalse)
      final = 0;
    else
      rb_raise(rb_eTypeError, "not valid value");
  }

  GET_PARSER(obj, parser);

  parser->iterator = rb_block_given_p();

  /* Setup event handlers */
  setup_evnet_handlers(parser, obj);

  /* Parse from stream (probably slightly slow) */
  if (fromStream) {
    VALUE buf;

    if (OBJ_TAINTED(str))
      taintParser(parser);
    do {
      buf = rb_funcall(str, mid, 0);
      if (!NIL_P(buf)) {
        Check_Type(buf, T_STRING);
        if (OBJ_TAINTED(buf))
          taintParser(parser);
        rb_ivar_set(obj, linebuf, buf); /* protect buf from GC (reasonable?)*/
        ret = XML_Parse(parser->parser,
                        RSTRING_PTR(buf), RSTRING_LEN(buf), 0);
      }
      else {
        ret = XML_Parse(parser->parser, NULL, 0, 1);
      }
      if (!ret) {
        int err = XML_GetErrorCode(parser->parser);
        const char* errStr = XML_ErrorString(err);
        rb_raise(eXMLParserError, (char*)errStr);
      }
    } while (!NIL_P(buf));
    return Qnil;
  }

  /* Parse string */
  if (!NIL_P(str)) {
#if defined(HAVE_RUBY_ENCODING_H) && defined(HAVE_XML_PARSERRESET)
    int err;
#endif
    if (OBJ_TAINTED(str))
      taintParser(parser);
    ret = XML_Parse(parser->parser,
                    RSTRING_PTR(str), RSTRING_LEN(str), final);
#if defined(HAVE_RUBY_ENCODING_H) && defined(HAVE_XML_PARSERRESET)
    /* Ruby 1.9.1 Encoding conversion */
    err = XML_GetErrorCode(parser->parser);
    if (final && err == XML_ERROR_UNKNOWN_ENCODING) {
      rb_encoding* enc;
      volatile VALUE encobj;
      volatile VALUE ustr;
      enc = rb_enc_find(parser->detectedEncoding);
      if ((int)ENC_TO_ENCINDEX(enc) != rb_ascii8bit_encindex()) {
        rb_enc_associate(str, enc);
        encobj = rb_enc_from_encoding(enc_xml);
        /* rb_str_encode may raises an exception */
        ustr = rb_str_encode(str, encobj, 0, Qnil);
        if (!NIL_P(ustr)) {
          XML_ParserReset(parser->parser, "utf-8");
          XML_SetUserData(parser->parser, (void*)obj);
          parser->defaultCurrent = 0;
#ifdef NEW_EXPAT
          parser->lastAttrs = NULL;
#endif
          parser->detectedEncoding = NULL;
          setup_evnet_handlers(parser, obj);
          ret = XML_Parse(parser->parser,
                          RSTRING_PTR(ustr), RSTRING_LEN(ustr), final);
        }
      }
    }
#endif
  }
  else
    ret = XML_Parse(parser->parser, NULL, 0, final);
  if (!ret) {
    int err = XML_GetErrorCode(parser->parser);
    const char* errStr = XML_ErrorString(err);
    rb_raise(eXMLParserError, (char*)errStr);
  }

  return Qnil;
}