+/*
+ * pg_xml_init --- set up for use of libxml and register an error handler
+ *
+ * This should be called by each function that is about to use libxml
+ * facilities and requires error handling. It initializes libxml with
+ * pg_xml_init_library() and establishes our libxml error handler.
+ *
+ * strictness determines which errors are reported and which are ignored.
+ *
+ * Calls to this function MUST be followed by a PG_TRY block that guarantees
+ * that pg_xml_done() is called during either normal or error exit.
+ *
+ * This is exported for use by contrib/xml2, as well as other code that might
+ * wish to share use of this module's libxml error handler.
+ */
+PgXmlErrorContext *
+pg_xml_init(PgXmlStrictness strictness)
+{
+ PgXmlErrorContext *errcxt;
+ void *new_errcxt;
+
+ /* Do one-time setup if needed */
+ pg_xml_init_library();
+
+ /* Create error handling context structure */
+ errcxt = (PgXmlErrorContext *) palloc(sizeof(PgXmlErrorContext));
+ errcxt->magic = ERRCXT_MAGIC;
+ errcxt->strictness = strictness;
+ errcxt->err_occurred = false;
+ initStringInfo(&errcxt->err_buf);
+
+ /*
+ * Save original error handler and install ours. libxml originally didn't
+ * distinguish between the contexts for generic and for structured error
+ * handlers. If we're using an old libxml version, we must thus save the
+ * generic error context, even though we're using a structured error
+ * handler.
+ */
+ errcxt->saved_errfunc = xmlStructuredError;
+
+#ifdef HAVE_XMLSTRUCTUREDERRORCONTEXT
+ errcxt->saved_errcxt = xmlStructuredErrorContext;
+#else
+ errcxt->saved_errcxt = xmlGenericErrorContext;
+#endif
+
+ xmlSetStructuredErrorFunc((void *) errcxt, xml_errorHandler);
+
+ /*
+ * Verify that xmlSetStructuredErrorFunc set the context variable we
+ * expected it to. If not, the error context pointer we just saved is not
+ * the correct thing to restore, and since that leaves us without a way to
+ * restore the context in pg_xml_done, we must fail.
+ *
+ * The only known situation in which this test fails is if we compile with
+ * headers from a libxml2 that doesn't track the structured error context
+ * separately (< 2.7.4), but at runtime use a version that does, or vice
+ * versa. The libxml2 authors did not treat that change as constituting
+ * an ABI break, so the LIBXML_TEST_VERSION test in pg_xml_init_library
+ * fails to protect us from this.
+ */
+
+#ifdef HAVE_XMLSTRUCTUREDERRORCONTEXT
+ new_errcxt = xmlStructuredErrorContext;
+#else
+ new_errcxt = xmlGenericErrorContext;
+#endif
+
+ if (new_errcxt != (void *) errcxt)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("could not set up XML error handler"),
+ errhint("This probably indicates that the version of libxml2"
+ " being used is not compatible with the libxml2"
+ " header files that PostgreSQL was built with.")));
+
+ /*
+ * Also, install an entity loader to prevent unwanted fetches of external
+ * files and URLs.
+ */
+ errcxt->saved_entityfunc = xmlGetExternalEntityLoader();
+ xmlSetExternalEntityLoader(xmlPgEntityLoader);
+
+ return errcxt;
+}
+
+
+/*
+ * pg_xml_done --- restore previous libxml error handling
+ *
+ * Resets libxml's global error-handling state to what it was before
+ * pg_xml_init() was called.
+ *
+ * This routine verifies that all pending errors have been dealt with
+ * (in assert-enabled builds, anyway).
+ */
+void
+pg_xml_done(PgXmlErrorContext *errcxt, bool isError)
+{
+ void *cur_errcxt;
+
+ /* An assert seems like enough protection here */
+ Assert(errcxt->magic == ERRCXT_MAGIC);
+
+ /*
+ * In a normal exit, there should be no un-handled libxml errors. But we
+ * shouldn't try to enforce this during error recovery, since the longjmp
+ * could have been thrown before xml_ereport had a chance to run.
+ */
+ Assert(!errcxt->err_occurred || isError);
+
+ /*
+ * Check that libxml's global state is correct, warn if not. This is a
+ * real test and not an Assert because it has a higher probability of
+ * happening.
+ */
+#ifdef HAVE_XMLSTRUCTUREDERRORCONTEXT
+ cur_errcxt = xmlStructuredErrorContext;
+#else
+ cur_errcxt = xmlGenericErrorContext;
+#endif
+
+ if (cur_errcxt != (void *) errcxt)
+ elog(WARNING, "libxml error handling state is out of sync with xml.c");
+
+ /* Restore the saved handlers */
+ xmlSetStructuredErrorFunc(errcxt->saved_errcxt, errcxt->saved_errfunc);
+ xmlSetExternalEntityLoader(errcxt->saved_entityfunc);
+
+ /*
+ * Mark the struct as invalid, just in case somebody somehow manages to
+ * call xml_errorHandler or xml_ereport with it.
+ */
+ errcxt->magic = 0;
+
+ /* Release memory */
+ pfree(errcxt->err_buf.data);
+ pfree(errcxt);
+}
+
+
+/*
+ * pg_xml_error_occurred() --- test the error flag
+ */
+bool
+pg_xml_error_occurred(PgXmlErrorContext *errcxt)
+{
+ return errcxt->err_occurred;