Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: xml2
Title: Parse XML
Version: 1.4.1
Version: 1.5.0
Authors@R: c(
person("Hadley", "Wickham", role = "aut"),
person("Jim", "Hester", role = "aut"),
Expand Down
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# xml2 1.5.0

* Experimental custom myExternalEntityLoader on libxml2 2.15 and up.

# xml2 1.4.1

* Remove a test that broke with libxml2 2.15
Expand Down
7 changes: 7 additions & 0 deletions R/init.R
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,10 @@ xml_parse_options <- function() {
xml_save_options <- function() {
.Call(xml_save_options_)
}

download_file_callback <- function(url){
tmp <- tempfile()
on.exit(unlink(tmp))
download.file(url, tmp, quiet = TRUE)
readBin(tmp, raw(), file.info(tmp)$size)
}
41 changes: 40 additions & 1 deletion src/xml2_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <libxml/parser.h>
#include <string.h>

static xmlExternalEntityLoader defaultLoader = NULL;

/* * *
* Author: Nick Wellnhofer <[email protected]>
* Date: Tue, 24 Oct 2023 15:02:36 +0200
Expand Down Expand Up @@ -49,12 +51,49 @@ void handleGenericError(void *ctx, const char *fmt, ...){
Rf_error("%s", buffer);
}

#if LIBXML_VERSION >= 21500

xmlParserInput *download_file_callback(const char *url){
SEXP arg = PROTECT(Rf_mkString(url));
SEXP expr = PROTECT(Rf_install("download_file_callback"));
SEXP call = PROTECT(Rf_lang2(expr, arg));
SEXP env = R_FindNamespace(Rf_mkString("xml2"));
int err = 1;
SEXP out = PROTECT(R_tryEvalSilent(call, env, &err));
if(err) return NULL;
xmlParserInputFlags flags = XML_INPUT_BUF_STATIC | XML_INPUT_USE_SYS_CATALOG;
xmlParserInput *buf = xmlNewInputFromMemory(url, RAW(out), Rf_length(out), flags);
//xmlParserInputBuffer *buf = xmlParserInputBufferCreateMem((char*) RAW(out), Rf_length(out), XML_CHAR_ENCODING_UTF8);
UNPROTECT(4);
return buf;
}

static xmlParserInputPtr myExternalEntityLoader(const char *URL, const char *ID, xmlParserCtxtPtr ctxt){
if (URL && (strncmp(URL, "http://", 7) == 0 || strncmp(URL, "https://", 8) == 0)) {
//REprintf("Fetching external resource %s\n", URL);
xmlParserInput *buf = download_file_callback(URL);
if(buf) return buf;
}
// Fallback to default behavior
if (defaultLoader)
return defaultLoader(URL, ID, ctxt);
return NULL;
}

#endif


void init_libxml2_library(void) {
// Check that header and libs are compatible
LIBXML_TEST_VERSION

xmlInitParser();
xmlSetStructuredErrorFunc(NULL, handleStructuredError);
xmlSetGenericErrorFunc(NULL, handleGenericError);
}

// Set custom download callback
#if LIBXML_VERSION >= 21500
defaultLoader = xmlGetExternalEntityLoader();
xmlSetExternalEntityLoader(myExternalEntityLoader);
#endif
}
Loading