Through the 'supersede' module, gettext now uses the 'canonicalize' module. Vaclav Slavik reported in <https://savannah.gnu.org/bugs/?59079>:
"msgfmt ... to unmounted shared folders on Windows via UNC paths (e.g. \\localhost\sharedfolder) fails. I got multiple reports of this pretty quickly, so it's not as obscure an edge case as one could be excused to think. This is because fopen_supersede() uses canonicalize_filename_mode() which doesn't handle this situation correctly. While it has code in place to recognize leading double-slash as being different from single-slash, and knows that this applies to win32, that's it. It then traverses the entire path from the root, stat()ing each path component - but the initial part, \\hostname, is not stat()able, it's not a path; \\hostname\foo is the first real component. Attached patch fixes this to the best of my understanding of how canonicalize_filename_mode() works: it increases `prefix_len` (which is bound to be 0 for \\paths) to cover the hostname component, and copies the hostname part to `dest` as well - causing canonicalization to start at the first real path component and keep the leading \\hostname\ bit constant. " I modified Vaclav's patch to include out-of-bounds handling, and to make prefix_len point to the slash/backslash after the server name (rather than past this slash/backslash). Tested with file names of the form \\localhost\foo -> //localhost\foo \\localhost\.\foo -> //localhost\foo \\localhost\..\foo -> //localhost\foo \\localhost -> //localhost 2020-09-20 Bruno Haible <br...@clisp.org> canonicalize: Add support for UNC file names on native Windows. Reported and initial patch by Vaclav Slavik <vac...@slavik.io> in <https://savannah.gnu.org/bugs/?59079>. * lib/canonicalize.c (canonicalize_filename_mode): For UNC file names, extend the prefix to include the server. diff --git a/lib/canonicalize.c b/lib/canonicalize.c index aa0c3bd..dfdb912 100644 --- a/lib/canonicalize.c +++ b/lib/canonicalize.c @@ -163,8 +163,34 @@ canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode) *dest++ = '/'; if (DOUBLE_SLASH_IS_DISTINCT_ROOT) { - if (ISSLASH (name[1]) && !ISSLASH (name[2]) && !prefix_len) - *dest++ = '/'; + if (prefix_len == 0 /* implies ISSLASH (name[0]) */ + && ISSLASH (name[1]) && !ISSLASH (name[2])) + { + *dest++ = '/'; +#if defined _WIN32 && !defined __CYGWIN__ + /* For UNC file names '\\server\path\to\file', extend the prefix + to include the server: '\\server\'. */ + { + size_t i; + for (i = 2; name[i] != '\0' && !ISSLASH (name[i]); ) + i++; + if (name[i] != '\0' /* implies ISSLASH (name[i]) */ + && i + 1 < rname_limit - rname) + { + prefix_len = i; + memcpy (dest, name + 2, i - 2 + 1); + dest += i - 2 + 1; + } + else + { + /* Either name = '\\server'; this is an invalid file name. + Or name = '\\server\...' and server is more than + PATH_MAX - 4 bytes long. In either case, stop the UNC + processing. */ + } + } +#endif + } *dest = '\0'; } start = name + prefix_len;