// igraph_read_graph_graphml [c]
int igraph_read_graph_graphml(igraph_t *graph, FILE *instream, int index) {

#if HAVE_LIBXML == 1
    xmlParserCtxtPtr ctxt;
    xmlDocPtr doc;

    struct igraph_i_graphml_parser_state state;
    int res;
    char buffer[4096];
    igraph_bool_t parsing_successful;
    char* error_message;

    if (index < 0) {
        IGRAPH_ERROR("Graph index must be non-negative", IGRAPH_EINVAL);
    }

    xmlInitParser();
    IGRAPH_CHECK(igraph_i_graphml_parser_state_init(&state, graph, index));
    IGRAPH_FINALLY(igraph_i_graphml_parser_state_destroy, &state);

    /* Create a progressive parser context */
    res = (int) fread(buffer, 1, sizeof(buffer), instream);
    if (res < sizeof(buffer) && !feof(instream)) {
        IGRAPH_ERROR("IO error while reading GraphML data", IGRAPH_PARSEERROR);
    }
    ctxt = xmlCreatePushParserCtxt(&igraph_i_graphml_sax_handler,
                                   &state,
                                   buffer,
                                   res,
                                   NULL);
    /*   ctxt=xmlCreateIOParserCtxt(&igraph_i_graphml_sax_handler, &state, */
    /*               igraph_i_libxml2_read_callback, */
    /*               igraph_i_libxml2_close_callback, */
    /*               instream, XML_CHAR_ENCODING_NONE); */
    if (ctxt == NULL) {
        IGRAPH_ERROR("Can't create progressive parser context", IGRAPH_PARSEERROR);
    }

    /* Set parsing options */
    if (xmlCtxtUseOptions(ctxt,
                          XML_PARSE_NOBLANKS |
                          XML_PARSE_NONET | XML_PARSE_NSCLEAN |
                          XML_PARSE_NOCDATA | XML_PARSE_HUGE
                         )) {
        xmlFreeParserCtxt(ctxt);
        IGRAPH_ERROR("Cannot set options for the parser context", IGRAPH_EINVAL);
    }

    /* Okay, parsing will start now. The parser might do things that eventually
     * trigger the igraph error handler, but we want the parser state to
     * survive whatever happens here. So, we need to pop off
     * igraph_i_graphml_parser_state_destroy() from the stack and temporarily
     * assume responsibility for calling it ourselves until we are back from the
     * parser */
    IGRAPH_FINALLY_CLEAN(1);

    /* Do the parsing */
    while ((res = (int) fread(buffer, 1, sizeof(buffer), instream)) > 0) {
        xmlParseChunk(ctxt, buffer, res, 0);
        if (!state.successful) {
            break;
        }
    }
    xmlParseChunk(ctxt, buffer, res, 1);

    /* Free the context */
    doc = ctxt->myDoc;
    xmlFreeParserCtxt(ctxt);
    if (doc) {
        /* In theory this should not be necessary, but it looks like certain malformed
         * GraphML files leave a partially-parsed doc in memory */
        xmlFreeDoc(doc);
    }

    /* Extract the error message from the parser state (if any), and make a
     * copy so we can safely destroy the parser state before triggering the
     * error */
    parsing_successful = state.successful;
    error_message = parsing_successful || state.error_message == NULL ? NULL : strdup(state.error_message);

    /* Now that we have lifted error_message out of the parser state, we can
     * put the destructor of the parser state back on the FINALLY stack */
    IGRAPH_FINALLY(igraph_i_graphml_parser_state_destroy, &state);

    /* ...and we can also put the error message pointer on the FINALLY stack */
    if (error_message != NULL) {
        IGRAPH_FINALLY(free, error_message);
    }

    /* Trigger the stored error if needed */
    if (!parsing_successful) {
        if (error_message != NULL) {
            IGRAPH_ERROR(error_message, IGRAPH_PARSEERROR);
        } else {
            IGRAPH_ERROR("Malformed GraphML file", IGRAPH_PARSEERROR);
        }
    }

    /* Did we actually manage to reach the graph to be parsed, given its index?
     * If not, that's an error as well. */
    if (state.index >= 0) {
        IGRAPH_ERROR("Graph index was too large", IGRAPH_EINVAL);
    }

    /* Okay, everything seems good. We can now take the parser state and
     * construct our graph from the data gathered during the parsing */
    IGRAPH_CHECK(igraph_i_graphml_parser_state_finish_parsing(&state));

    igraph_i_graphml_parser_state_destroy(&state);
    IGRAPH_FINALLY_CLEAN(1);

    return IGRAPH_SUCCESS;
#else
    IGRAPH_UNUSED(graph);
    IGRAPH_UNUSED(instream);
    IGRAPH_UNUSED(index);

    IGRAPH_ERROR("GraphML support is disabled", IGRAPH_UNIMPLEMENTED);
#endif
}