From 354f620a45fcba796a0f71ce20f2132896f415f2 Mon Sep 17 00:00:00 2001 From: James Lan Date: Mon, 27 Oct 2025 14:40:22 -0700 Subject: [PATCH] build: compile option WINPATH to allow processing Windows path on other platforms --- CMakeLists.txt | 7 ++++++- README.md | 1 + catalog.c | 4 ++-- configure.ac | 20 ++++++++++++++++++++ include/libxml/meson.build | 1 + include/libxml/xmlversion.h.in | 7 +++++++ meson.build | 5 +++++ meson_options.txt | 6 ++++++ testparser.c | 8 ++++---- uri.c | 18 +++++++++--------- xmlIO.c | 20 ++++++++++++++------ 11 files changed, 75 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b8de36ce8..163661f81 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,6 +47,11 @@ option(LIBXML2_WITH_TESTS "Build tests" ON) option(LIBXML2_WITH_THREADS "Add multithread support" ON) option(LIBXML2_WITH_TLS "Enable thread-local storage" OFF) option(LIBXML2_WITH_VALID "Add the DTD validation support" ON) +if(WIN32 OR CYGWIN) + option(LIBXML2_WITH_WINPATH "Handle Windows-styled path" ON) +else() + option(LIBXML2_WITH_WINPATH "Handle Windows-styled path" OFF) +endif() option(LIBXML2_WITH_XINCLUDE "Add the XInclude support" ON) option(LIBXML2_WITH_XPATH "Add the XPATH support" ON) @@ -90,7 +95,7 @@ if(LIBXML2_WITH_PYTHON) CACHE PATH "Python bindings install directory") endif() -foreach(VARIABLE IN ITEMS WITH_C14N WITH_CATALOG WITH_DEBUG WITH_HTML WITH_HTTP WITH_ICONV WITH_ICU WITH_ISO8859X WITH_MODULES WITH_OUTPUT WITH_PATTERN WITH_PUSH WITH_READER WITH_REGEXPS WITH_RELAXNG WITH_SAX1 WITH_SCHEMAS WITH_SCHEMATRON WITH_THREADS WITH_THREAD_ALLOC WITH_VALID WITH_WRITER WITH_XINCLUDE WITH_XPATH WITH_XPTR WITH_ZLIB) +foreach(VARIABLE IN ITEMS WITH_C14N WITH_CATALOG WITH_DEBUG WITH_HTML WITH_HTTP WITH_ICONV WITH_ICU WITH_ISO8859X WITH_MODULES WITH_OUTPUT WITH_PATTERN WITH_PUSH WITH_READER WITH_REGEXPS WITH_RELAXNG WITH_SAX1 WITH_SCHEMAS WITH_SCHEMATRON WITH_THREADS WITH_THREAD_ALLOC WITH_VALID WITH_WINPATH WITH_WRITER WITH_XINCLUDE WITH_XPATH WITH_XPTR WITH_ZLIB) if(LIBXML2_${VARIABLE}) set(${VARIABLE} 1) else() diff --git a/README.md b/README.md index 974a0fc29..ae26919b6 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,7 @@ The following options disable or enable code modules and relevant symbols: --with-threads multithreading support (on) --with-thread-alloc per-thread malloc hooks (off) --with-valid DTD validation support (on) + --with-winpath Windows path support (on for Windows) --with-writer xmlWriter serialization interface (on) --with-xinclude XInclude 1.0 support (on) --with-xpath XPath 1.0 support (on) diff --git a/catalog.c b/catalog.c index fa6d77ca1..401dbc14f 100644 --- a/catalog.c +++ b/catalog.c @@ -3202,7 +3202,7 @@ xmlLoadCatalogs(const char *pathss) { const char *cur; const char *paths; xmlChar *path; -#ifdef _WIN32 +#ifdef LIBXML2_WINPATH_ENABLED int i, iLen; #endif @@ -3218,7 +3218,7 @@ xmlLoadCatalogs(const char *pathss) { cur++; path = xmlStrndup((const xmlChar *)paths, cur - paths); if (path != NULL) { -#ifdef _WIN32 +#ifdef LIBXML2_WINPATH_ENABLED iLen = strlen((const char*)path); for(i = 0; i < iLen; i++) { if(path[i] == '\\') { diff --git a/configure.ac b/configure.ac index d8dc4e1b4..d5887018e 100644 --- a/configure.ac +++ b/configure.ac @@ -114,6 +114,8 @@ AC_ARG_WITH(thread-alloc, [ --with-thread-alloc per-thread malloc hooks (off)]) AC_ARG_WITH(valid, [ --with-valid DTD validation support (on)]) +AC_ARG_WITH(winpath, +[ --with-winpath Windows-styled path support (auto)]) AC_ARG_WITH(writer, [ --with-writer xmlWriter serialization interface (on)]) AC_ARG_WITH(xinclude, @@ -400,6 +402,24 @@ fi AC_SUBST(WITH_READER) AM_CONDITIONAL(WITH_READER_SOURCES, test "$WITH_READER" = "1") +if test "$with_winpath" = "yes" ; then + echo Enabling Windows path support + WITH_WINPATH=1 +elif test "$with_winpath" = "no" ; then + WITH_WINPATH=0 +else + case "$host" in + *-*-mingw* | *-*-cygwin* | *-*-msys* ) + echo "Enabling Windows path support (default for Windows)" + WITH_WINPATH=1 + ;; + *) + WITH_WINPATH=0 + ;; + esac +fi +AC_SUBST(WITH_WINPATH) + if test "$with_writer" = "no" ; then echo Disabling the xmlWriter saving interface WITH_WRITER=0 diff --git a/include/libxml/meson.build b/include/libxml/meson.build index c3b9a6026..6fec78106 100644 --- a/include/libxml/meson.build +++ b/include/libxml/meson.build @@ -27,6 +27,7 @@ xmlversion_h.set10('WITH_SCHEMATRON', want_schematron) xmlversion_h.set10('WITH_THREADS', want_threads) xmlversion_h.set10('WITH_THREAD_ALLOC', want_thread_alloc) xmlversion_h.set10('WITH_VALID', want_valid) +xmlversion_h.set10('WITH_WINPATH', want_winpath) xmlversion_h.set10('WITH_WRITER', want_writer) xmlversion_h.set10('WITH_XINCLUDE', want_xinclude) xmlversion_h.set10('WITH_XPATH', want_xpath) diff --git a/include/libxml/xmlversion.h.in b/include/libxml/xmlversion.h.in index 107707bb8..f7ed8a937 100644 --- a/include/libxml/xmlversion.h.in +++ b/include/libxml/xmlversion.h.in @@ -141,6 +141,13 @@ #define LIBXML_SGML_CATALOG_ENABLED #endif +#if @WITH_WINPATH@ +/** + * Whether the Windows path support is configured in + */ +#define LIBXML_WINPATH_ENABLED +#endif + #if @WITH_XPATH@ /** * Whether XPath is configured in diff --git a/meson.build b/meson.build index 12a165fe0..1cd89f09c 100644 --- a/meson.build +++ b/meson.build @@ -118,6 +118,10 @@ want_sax1 = want_minimum ? feature.enabled() : feature.allowed() feature = get_option('threads') want_threads = want_minimum ? feature.enabled() : feature.allowed() +# auto-enabled on Windows +feature = get_option('winpath') +want_winpath = (sys_cygwin or sys_windows) ? feature.allowed() : feature.enabled() + feature = get_option('valid') want_valid = want_minimum ? feature.enabled() : feature.allowed() @@ -650,6 +654,7 @@ summary( 'thread-alloc': want_thread_alloc, 'tls': want_tls, 'valid': want_valid, + 'winpath': want_winpath, 'writer': want_writer, 'xinclude': want_xinclude, 'xpath': want_xpath, diff --git a/meson_options.txt b/meson_options.txt index 9bc02b3c4..445606ec0 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -138,6 +138,12 @@ option('valid', description: 'DTD validation support' ) +option('winpath', + type: 'feature', + value: 'auto', + description: 'Handling Windows-styled path' +) + option('writer', type: 'feature', description: 'xmlWriter serialization interface' diff --git a/testparser.c b/testparser.c index 39b2bd47d..b05db58d3 100644 --- a/testparser.c +++ b/testparser.c @@ -1231,7 +1231,7 @@ testBuildRelativeUri(void) { "/b2/c2", "with%20space/x%20x/y%20y" } -#if defined(_WIN32) || defined(__CYGWIN__) +#if defined(LIBXML_WINPATH_ENABLED) , { "\\a\\b1\\c1", "\\a\\b2\\c2", @@ -1298,7 +1298,7 @@ testBuildRelativeUri(void) { return err; } -#if defined(_WIN32) || defined(__CYGWIN__) +#if defined(LIBXML_WINPATH_ENABLED) static int testWindowsUri(void) { const char *url = "c:/a%20b/file.txt"; @@ -1371,7 +1371,7 @@ testWindowsUri(void) { return err; } -#endif /* WIN32 */ +#endif /* LIBXML_WINPATH_ENABLED */ #if defined(LIBXML_ICONV_ENABLED) || defined(LIBXML_ICU_ENABLED) static int @@ -1584,7 +1584,7 @@ main(void) { err |= testWriterClose(); #endif err |= testBuildRelativeUri(); -#if defined(_WIN32) || defined(__CYGWIN__) +#if defined(LIBXML_WINPATH_ENABLED) err |= testWindowsUri(); #endif #if defined(LIBXML_ICONV_ENABLED) || defined(LIBXML_ICU_ENABLED) diff --git a/uri.c b/uri.c index cfeca1e01..084bd25f0 100644 --- a/uri.c +++ b/uri.c @@ -229,7 +229,7 @@ xmlParse3986Scheme(xmlURIPtr uri, const char **str) { return(1); cur++; -#if defined(_WIN32) || defined(__CYGWIN__) +#if defined(LIBXML_WINPATH_ENABLED) /* * Don't treat Windows drive letters as scheme. */ @@ -580,7 +580,7 @@ xmlParse3986Segment(xmlURIPtr uri, const char **str, char forbid, int empty) } NEXT(cur); -#if defined(_WIN32) || defined(__CYGWIN__) +#if defined(LIBXML_WINPATH_ENABLED) /* * Allow Windows drive letters. */ @@ -1437,7 +1437,7 @@ xmlIsPathSeparator(int c, int isFile) { if (c == '/') return(1); -#if defined(_WIN32) || defined(__CYGWIN__) +#if defined(LIBXML_WINPATH_ENABLED) if (isFile && (c == '\\')) return(1); #endif @@ -1477,7 +1477,7 @@ xmlNormalizePath(char *path, int isFile) { * Collapse multiple separators first. */ while (xmlIsPathSeparator(*cur, isFile)) { -#if defined(_WIN32) || defined(__CYGWIN__) +#if defined(LIBXML_WINPATH_ENABLED) /* Allow two separators at start of path */ if ((isFile) && (out == path + 1)) *out++ = '/'; @@ -1838,7 +1838,7 @@ xmlIsAbsolutePath(const xmlChar *path) { if (xmlIsPathSeparator(c, 1)) return(1); -#if defined(_WIN32) || defined(__CYGWIN__) +#if defined(LIBXML_WINPATH_ENABLED) if ((((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'))) && (path[1] == ':')) @@ -2022,7 +2022,7 @@ xmlBuildURISafe(const xmlChar *URI, const xmlChar *base, xmlChar **valPtr) { return(xmlResolvePath(URI, base, valPtr)); } -#if defined(_WIN32) || defined(__CYGWIN__) +#if defined(LIBXML_WINPATH_ENABLED) /* * Resolve paths with a Windows drive letter as filesystem path * even if base has a scheme. @@ -2368,12 +2368,12 @@ xmlParseUriOrPath(const char *str, xmlURIPtr *out, int *drive) { path = buf; if (xmlIsAbsolutePath(BAD_CAST buf)) { -#if defined(_WIN32) || defined(__CYGWIN__) +#if defined(LIBXML_WINPATH_ENABLED) const char *server = NULL; int isFileScheme = 0; #endif -#if defined(_WIN32) || defined(__CYGWIN__) +#if defined(LIBXML_WINPATH_ENABLED) if (strncmp(buf, "//?/UNC/", 8) == 0) { server = buf + 8; isFileScheme = 1; @@ -2442,7 +2442,7 @@ xmlParseUriOrPath(const char *str, xmlURIPtr *out, int *drive) { xmlNormalizePath(uri->path, /* isFile */ 0); } -#if defined(_WIN32) || defined(__CYGWIN__) +#if defined(LIBXML_WINPATH_ENABLED) if ((uri->path[0] == '/') && (((uri->path[1] >= 'A') && (uri->path[1] <= 'Z')) || ((uri->path[1] >= 'a') && (uri->path[1] <= 'z'))) && diff --git a/xmlIO.c b/xmlIO.c index ee3cc9615..3f9af053c 100644 --- a/xmlIO.c +++ b/xmlIO.c @@ -681,23 +681,31 @@ static int xmlConvertUriToPath(const char *uri, char **out) { const char *escaped; char *unescaped; +#ifdef LIBXML_WINPATH_ENABLED + xmlChar ch; +#endif *out = NULL; if (!xmlStrncasecmp(BAD_CAST uri, BAD_CAST "file://localhost/", 17)) { - escaped = &uri[16]; + escaped = &uri[16]; } else if (!xmlStrncasecmp(BAD_CAST uri, BAD_CAST "file:///", 8)) { - escaped = &uri[7]; + escaped = &uri[7]; } else if (!xmlStrncasecmp(BAD_CAST uri, BAD_CAST "file:/", 6)) { /* lots of generators seems to lazy to read RFC 1738 */ - escaped = &uri[5]; + escaped = &uri[5]; } else { return(1); } -#ifdef _WIN32 +#ifdef LIBXML_WINPATH_ENABLED /* Ignore slash like in file:///C:/file.txt */ - escaped += 1; + if (escaped[0] == '/') { + ch = escaped[1]; + if (((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')) + && escaped[2] == ':') + escaped += 1; + } #endif unescaped = xmlURIUnescapeString(escaped, 0, NULL); @@ -2701,7 +2709,7 @@ xmlParserGetDirectory(const char *filename) { if (filename == NULL) return(NULL); -#if defined(_WIN32) +#if defined(LIBXML_WINPATH_ENABLED) # define IS_XMLPGD_SEP(ch) ((ch=='/')||(ch=='\\')) #else # define IS_XMLPGD_SEP(ch) (ch=='/')